Mercurial > emacs
changeset 314:36a905212ceb
Initial revision
author | Jim Blandy <jimb@redhat.com> |
---|---|
date | Wed, 03 Jul 1991 12:10:07 +0000 |
parents | ac18f34e3e33 |
children | faf70884106b |
files | src/dispnew.c |
diffstat | 1 files changed, 2145 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dispnew.c Wed Jul 03 12:10:07 1991 +0000 @@ -0,0 +1,2145 @@ +/* Updating of data structures for redisplay. + Copyright (C) 1985, 1986, 1987, 1988, 1990 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include <signal.h> + +#include "config.h" +#include <stdio.h> +#include <ctype.h> + +#ifdef NEED_TIME_H +#include <time.h> +#else /* not NEED_TIME_H */ +#ifdef HAVE_TIMEVAL +#include <sys/time.h> +#endif /* HAVE_TIMEVAL */ +#endif /* not NEED_TIME_H */ + +#ifdef HAVE_TERMIO +#include <termio.h> +#ifdef TCOUTQ +#undef TIOCOUTQ +#define TIOCOUTQ TCOUTQ +#endif /* TCOUTQ defined */ +#include <fcntl.h> +#else +#ifndef VMS +#include <sys/ioctl.h> +#endif /* not VMS */ +#endif /* not HAVE_TERMIO */ + +/* Allow m- file to inhibit use of FIONREAD. */ +#ifdef BROKEN_FIONREAD +#undef FIONREAD +#endif + +/* Interupt input is not used if there is no FIONREAD. */ +#ifndef FIONREAD +#undef SIGIO +#endif + +#undef NULL + +#include "termchar.h" +#include "termopts.h" +#include "cm.h" +#include "lisp.h" +#include "dispextern.h" +#include "buffer.h" +#include "screen.h" +#include "window.h" +#include "commands.h" +#include "disptab.h" +#include "indent.h" + +#ifdef HAVE_X_WINDOWS +#include "xterm.h" +#endif /* HAVE_X_WINDOWS */ + +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define min(a, b) ((a) < (b) ? (a) : (b)) + +#ifndef PENDING_OUTPUT_COUNT +/* Get number of chars of output now in the buffer of a stdio stream. + This ought to be built in in stdio, but it isn't. + Some s- files override this because their stdio internals differ. */ +#define PENDING_OUTPUT_COUNT(FILE) ((FILE)->_ptr - (FILE)->_base) +#endif + +/* Nonzero means do not assume anything about current + contents of actual terminal screen */ + +int screen_garbaged; + +/* Nonzero means last display completed. Zero means it was preempted. */ + +int display_completed; + +/* Lisp variable visible-bell; enables use of screen-flash + instead of audible bell. */ + +int visible_bell; + +/* Invert the color of the whole screen, at a low level. */ + +int inverse_video; + +/* Line speed of the terminal. */ + +int baud_rate; + +/* nil or a symbol naming the window system under which emacs is + running ('x is the only current possibility). */ + +Lisp_Object Vwindow_system; + +/* Version number of X windows: 10, 11 or nil. */ +Lisp_Object Vwindow_system_version; + +/* Vector of glyph definitions. Indexed by glyph number, + the contents are a string which is how to output the glyph. + + If Vglyph_table is nil, a glyph is output by using its low 8 bits + as a character code. */ + +Lisp_Object Vglyph_table; + +/* Display table to use for vectors that don't specify their own. */ + +Lisp_Object Vstandard_display_table; + +/* Nonzero means reading single-character input with prompt + so put cursor on minibuffer after the prompt. */ + +int cursor_in_echo_area; + +/* The currently selected screen. + In a single-screen version, this variable always remains 0. */ + +SCREEN_PTR selected_screen; + +/* In a single-screen version, the information that would otherwise + exist inside a `struct screen' lives in the following variables instead. */ + +#ifndef MULTI_SCREEN + +/* Desired terminal cursor position (to show position of point), + origin zero */ + +int cursX, cursY; + +/* Description of current screen contents */ + +struct screen_glyphs *current_glyphs; + +/* Description of desired screen contents */ + +struct screen_glyphs *desired_glyphs; + +#endif /* not MULTI_SCREEN */ + +/* This is a vector, made larger whenever it isn't large enough, + which is used inside `update_screen' to hold the old contents + of the SCREEN_PHYS_LINES of the screen being updated. */ +struct screen_glyphs **ophys_lines; +/* Length of vector currently allocated. */ +int ophys_lines_length; + +FILE *termscript; /* Stdio stream being used for copy of all output. */ + +struct cm Wcm; /* Structure for info on cursor positioning */ + +extern short ospeed; /* Output speed (from sg_ospeed) */ + +int in_display; /* 1 if in redisplay: can't handle SIGWINCH now. */ + +int delayed_size_change; /* 1 means SIGWINCH happened when not safe. */ +int delayed_screen_height; /* Remembered new screen height. */ +int delayed_screen_width; /* Remembered new screen width. */ + +#ifdef MULTI_SCREEN + +DEFUN ("redraw-screen", Fredraw_screen, Sredraw_screen, 1, 1, 0, + "Clear screen SCREEN and output again what is supposed to appear on it.") + (screen) + Lisp_Object screen; +{ + SCREEN_PTR s; + + CHECK_SCREEN (screen, 0); + s = XSCREEN (screen); + update_begin (s); + /* set_terminal_modes (); */ + clear_screen (); + update_end (s); + fflush (stdout); + clear_screen_records (s); + windows_or_buffers_changed++; + /* Mark all windows as INaccurate, + so that every window will have its redisplay done. */ + mark_window_display_accurate (SCREEN_ROOT_WINDOW (s), 0); + s->garbaged = 0; + return Qnil; +} + +DEFUN ("redraw-display", Fredraw_display, Sredraw_display, 0, 0, "", + "Redraw all screens marked as having their images garbled.") + () +{ + Lisp_Object screen, tail; + + for (tail = Vscreen_list; CONSP (tail); tail = XCONS (tail)->cdr) + { + screen = XCONS (tail)->car; + if (XSCREEN (screen)->garbaged && XSCREEN (screen)->visible) + Fredraw_screen (screen); + } + return Qnil; +} + +redraw_screen (s) + SCREEN_PTR s; +{ + Lisp_Object screen; + XSET (screen, Lisp_Screen, s); + Fredraw_screen (screen); +} + +#else /* not MULTI_SCREEN */ + +DEFUN ("redraw-display", Fredraw_display, Sredraw_display, 0, 0, 0, + "Clear screen and output again what is supposed to appear on it.") + () +{ + update_begin (0); + set_terminal_modes (); + clear_screen (); + update_end (0); + fflush (stdout); + clear_screen_records (0); + windows_or_buffers_changed++; + /* Mark all windows as INaccurate, + so that every window will have its redisplay done. */ + mark_window_display_accurate (XWINDOW (minibuf_window)->prev, 0); + return Qnil; +} + +#endif /* not MULTI_SCREEN */ + +static struct screen_glyphs * +make_screen_glyphs (screen, empty) + register SCREEN_PTR screen; + int empty; +{ + register int i; + register width = SCREEN_WIDTH (screen); + register height = SCREEN_HEIGHT (screen); + register struct screen_glyphs *new = + (struct screen_glyphs *) xmalloc (sizeof (struct screen_glyphs)); + + SET_GLYPHS_SCREEN (new, screen); + new->height = height; + new->width = width; + new->used = (int *) xmalloc (height * sizeof (int)); + new->glyphs = (GLYPH **) xmalloc (height * sizeof (GLYPH *)); + new->highlight = (char *) xmalloc (height * sizeof (char)); + new->enable = (char *) xmalloc (height * sizeof (char)); + bzero (new->enable, height * sizeof (char)); + new->bufp = (int *) xmalloc (height * sizeof (int)); + +#ifdef HAVE_X_WINDOWS + if (SCREEN_IS_X (screen)) + { + new->nruns = (int *) xmalloc (height * sizeof (int)); + new->face_list + = (struct run **) xmalloc (height * sizeof (struct run *)); + new->top_left_x = (short *) xmalloc (height * sizeof (short)); + new->top_left_y = (short *) xmalloc (height * sizeof (short)); + new->pix_width = (short *) xmalloc (height * sizeof (short)); + new->pix_height = (short *) xmalloc (height * sizeof (short)); + } +#endif + + if (empty) + { + /* Make the buffer used by decode_mode_spec. This buffer is also + used as temporary storage when updating the screen. See scroll.c. */ + unsigned int total_glyphs = (width + 2) * sizeof (GLYPH); + + new->total_contents = (GLYPH *) xmalloc (total_glyphs); + bzero (new->total_contents, total_glyphs); + } + else + { + unsigned int total_glyphs = height * (width + 2) * sizeof (GLYPH); + + new->total_contents = (GLYPH *) xmalloc (total_glyphs); + bzero (new->total_contents, total_glyphs); + for (i = 0; i < height; i++) + new->glyphs[i] = new->total_contents + i * (width + 2) + 1; + } + + return new; +} + +static void +free_screen_glyphs (screen, glyphs) + SCREEN_PTR screen; + struct screen_glyphs *glyphs; +{ + if (glyphs->total_contents) + free (glyphs->total_contents); + + free (glyphs->used); + free (glyphs->glyphs); + free (glyphs->highlight); + free (glyphs->enable); + free (glyphs->bufp); + +#ifdef HAVE_X_WINDOWS + if (SCREEN_IS_X (screen)) + { + free (glyphs->nruns); + free (glyphs->face_list); + free (glyphs->top_left_x); + free (glyphs->top_left_y); + free (glyphs->pix_width); + free (glyphs->pix_height); + } +#endif + + free (glyphs); +} + +static void +remake_screen_glyphs (screen) + SCREEN_PTR screen; +{ + if (SCREEN_CURRENT_GLYPHS (screen)) + free_screen_glyphs (screen, SCREEN_CURRENT_GLYPHS (screen)); + if (SCREEN_DESIRED_GLYPHS (screen)) + free_screen_glyphs (screen, SCREEN_DESIRED_GLYPHS (screen)); + if (SCREEN_TEMP_GLYPHS (screen)) + free_screen_glyphs (screen, SCREEN_TEMP_GLYPHS (screen)); + + if (SCREEN_MESSAGE_BUF (screen)) + SCREEN_MESSAGE_BUF (screen) + = (char *) xrealloc (SCREEN_MESSAGE_BUF (screen), + SCREEN_WIDTH (screen) + 1); + else + SCREEN_MESSAGE_BUF (screen) + = (char *) xmalloc (SCREEN_WIDTH (screen) + 1); + + SCREEN_CURRENT_GLYPHS (screen) = make_screen_glyphs (screen, 0); + SCREEN_DESIRED_GLYPHS (screen) = make_screen_glyphs (screen, 0); + SCREEN_TEMP_GLYPHS (screen) = make_screen_glyphs (screen, 1); + SET_SCREEN_GARBAGED (screen); +} + +/* Return the hash code of contents of line VPOS in screen-matrix M. */ + +static int +line_hash_code (m, vpos) + register struct screen_glyphs *m; + int vpos; +{ + register GLYPH *body, *end; + register int h = 0; + + if (!m->enable[vpos]) + return 0; + + /* Give all lighlighted lines the same hash code + so as to encourage scrolling to leave them in place. */ + if (m->highlight[vpos]) + return -1; + + body = m->glyphs[vpos]; + + if (must_write_spaces) + while (1) + { + GLYPH g = *body++; + + if (g == 0) + break; + h = (((h << 4) + (h >> 24)) & 0x0fffffff) + g - SPACEGLYPH; + } + else + while (1) + { + GLYPH g = *body++; + + if (g == 0) + break; + h = (((h << 4) + (h >> 24)) & 0x0fffffff) + g; + } + + if (h) + return h; + return 1; +} + +/* Return number of characters in line in M at vpos VPOS, + except don't count leading and trailing spaces + unless the terminal requires those to be explicitly output. */ + +static unsigned int +line_draw_cost (m, vpos) + struct screen_glyphs *m; + int vpos; +{ + register GLYPH *beg = m->glyphs[vpos]; + register GLYPH *end = m->glyphs[vpos] + m->used[vpos]; + register int i; + register int tlen = GLYPH_TABLE_LENGTH; + register Lisp_Object *tbase = GLYPH_TABLE_BASE; + + /* Ignore trailing and leading spaces if we can. */ + if (!must_write_spaces) + { + while ((end != beg) && (*end == SPACEGLYPH)) + --end; + if (end == beg) + return (0); /* All blank line. */ + + while (*beg == SPACEGLYPH) + ++beg; + } + + /* If we don't have a glyph-table, each glyph is one character, + so return the number of glyphs. */ + if (tbase == 0) + return end - beg; + + /* Otherwise, scan the glyphs and accumulate their total size in I. */ + i = 0; + while ((beg <= end) && *beg) + { + register GLYPH g = *beg++; + + if (GLYPH_SIMPLE_P (tbase, tlen, g)) + i += 1; + else + i += GLYPH_LENGTH (tbase, g); + } + return i; +} + +/* The functions on this page are the interface from xdisp.c to redisplay. + + The only other interface into redisplay is through setting + SCREEN_CURSOR_X (screen) and SCREEN_CURSOR_Y (screen) + and SET_SCREEN_GARBAGED (screen). */ + +/* cancel_line eliminates any request to display a line at position `vpos' */ + +cancel_line (vpos, screen) + int vpos; + register SCREEN_PTR screen; +{ + SCREEN_DESIRED_GLYPHS (screen)->enable[vpos] = 0; +} + +clear_screen_records (screen) + register SCREEN_PTR screen; +{ + bzero (SCREEN_CURRENT_GLYPHS (screen)->enable, SCREEN_HEIGHT (screen)); +} + +/* Prepare to display on line VPOS starting at HPOS within it. */ + +void +get_display_line (screen, vpos, hpos) + register SCREEN_PTR screen; + int vpos; + register int hpos; +{ + register struct screen_glyphs *glyphs; + register struct screen_glyphs *desired_glyphs = SCREEN_DESIRED_GLYPHS (screen); + register GLYPH *p; + + if (vpos < 0 || (! SCREEN_VISIBLE_P (screen))) + abort (); + + if ((desired_glyphs->enable[vpos]) && desired_glyphs->used[vpos] > hpos) + abort (); + + if (! desired_glyphs->enable[vpos]) + { + desired_glyphs->used[vpos] = 0; + desired_glyphs->highlight[vpos] = 0; + desired_glyphs->enable[vpos] = 1; + } + + if (hpos > desired_glyphs->used[vpos]) + { + GLYPH *g = desired_glyphs->glyphs[vpos] + desired_glyphs->used[vpos]; + GLYPH *end = desired_glyphs->glyphs[vpos] + hpos; + + desired_glyphs->used[vpos] = hpos; + while (g != end) + *g++ = SPACEGLYPH; + } +} + +/* Like bcopy except never gets confused by overlap. */ + +void +safe_bcopy (from, to, size) + char *from, *to; + int size; +{ + register char *endf; + register char *endt; + + if (size == 0) + return; + + /* If destination is higher in memory, and overlaps source zone, + copy from the end. */ + if (from < to && from + size > to) + { + endf = from + size; + endt = to + size; + + /* If TO - FROM is large, then we should break the copy into + nonoverlapping chunks of TO - FROM bytes each. However, if + TO - FROM is small, then the bcopy function call overhead + makes this not worth it. The crossover point could be about + anywhere. Since I don't think the obvious copy loop is ever + too bad, I'm trying to err in its favor. */ + if (to - from < 64) + { + do + *--endt = *--endf; + while (endf != from); + } + else + { + /* Since the overlap is always less than SIZE, we can always + safely do this loop once. */ + while (endt > to) + { + endt -= (to - from); + endf -= (to - from); + + bcopy (endf, endt, to - from); + } + + /* If TO - FROM wasn't a multiple of SIZE, there will be a + little left over. The amount left over is + (endt + (to - from)) - to, which is endt - from. */ + bcopy (from, to, endt - from); + } + } + else + bcopy (from, to, size); +} + +#if 0 +void +safe_bcopy (from, to, size) + char *from, *to; + int size; +{ + register char *endf; + register char *endt; + + if (size == 0) + return; + + /* If destination is higher in memory, and overlaps source zone, + copy from the end. */ + if (from < to && from + size > to) + { + endf = from + size; + endt = to + size; + + do + *--endt = *--endf; + while (endf != from); + + return; + } + + bcopy (from, to, size); +} +#endif + +/* Rotate a vector of SIZE bytes, by DISTANCE bytes. + DISTANCE may be negative. */ + +static void +rotate_vector (vector, size, distance) + char *vector; + int size; + int distance; +{ + char *temp = (char *) alloca (size); + + if (distance < 0) + distance += size; + + bcopy (vector, temp + distance, size - distance); + bcopy (vector + size - distance, temp, distance); + bcopy (temp, vector, size); +} + +/* Scroll lines from vpos FROM up to but not including vpos END + down by AMOUNT lines (AMOUNT may be negative). + Returns nonzero if done, zero if terminal cannot scroll them. */ + +int +scroll_screen_lines (screen, from, end, amount) + register SCREEN_PTR screen; + int from, end, amount; +{ + register int i; + register struct screen_glyphs *current_screen + = SCREEN_CURRENT_GLYPHS (screen); + + if (!line_ins_del_ok) + return 0; + + if (amount == 0) + return 1; + + if (amount > 0) + { + update_begin (screen); + set_terminal_window (end + amount); + if (!scroll_region_ok) + ins_del_lines (end, -amount); + ins_del_lines (from, amount); + set_terminal_window (0); + + rotate_vector (current_screen->glyphs + from, + sizeof (GLYPH *) * (end + amount - from), + amount * sizeof (GLYPH *)); + + safe_bcopy (current_screen->used + from, + current_screen->used + from + amount, + (end - from) * sizeof current_screen->used[0]); + + safe_bcopy (current_screen->highlight + from, + current_screen->highlight + from + amount, + (end - from) * sizeof current_screen->highlight[0]); + + safe_bcopy (current_screen->enable + from, + current_screen->enable + from + amount, + (end - from) * sizeof current_screen->enable[0]); + + /* Mark the lines made empty by scrolling as enabled, empty and + normal video. */ + bzero (current_screen->used + from, + amount * sizeof current_screen->used[0]); + bzero (current_screen->highlight + from, + amount * sizeof current_screen->highlight[0]); + for (i = from; i < from + amount; i++) + { + current_screen->glyphs[i][0] = '\0'; + current_screen->enable[i] = 1; + } + + safe_bcopy (current_screen->bufp + from, + current_screen->bufp + from + amount, + (end - from) * sizeof current_screen->bufp[0]); + +#ifdef HAVE_X_WINDOWS + if (SCREEN_IS_X (screen)) + { + safe_bcopy (current_screen->nruns + from, + current_screen->nruns + from + amount, + (end - from) * sizeof current_screen->nruns[0]); + + safe_bcopy (current_screen->face_list + from, + current_screen->face_list + from + amount, + (end - from) * sizeof current_screen->face_list[0]); + + safe_bcopy (current_screen->top_left_x + from, + current_screen->top_left_x + from + amount, + (end - from) * sizeof current_screen->top_left_x[0]); + + safe_bcopy (current_screen->top_left_y + from, + current_screen->top_left_y + from + amount, + (end - from) * sizeof current_screen->top_left_y[0]); + + safe_bcopy (current_screen->pix_width + from, + current_screen->pix_width + from + amount, + (end - from) * sizeof current_screen->pix_width[0]); + + safe_bcopy (current_screen->pix_height + from, + current_screen->pix_height + from + amount, + (end - from) * sizeof current_screen->pix_height[0]); + } +#endif /* HAVE_X_WINDOWS */ + + update_end (screen); + } + if (amount < 0) + { + update_begin (screen); + set_terminal_window (end); + ins_del_lines (from + amount, amount); + if (!scroll_region_ok) + ins_del_lines (end + amount, -amount); + set_terminal_window (0); + + rotate_vector (current_screen->glyphs + from + amount, + sizeof (GLYPH *) * (end - from - amount), + amount * sizeof (GLYPH *)); + + safe_bcopy (current_screen->used + from, + current_screen->used + from + amount, + (end - from) * sizeof current_screen->used[0]); + + safe_bcopy (current_screen->highlight + from, + current_screen->highlight + from + amount, + (end - from) * sizeof current_screen->highlight[0]); + + safe_bcopy (current_screen->enable + from, + current_screen->enable + from + amount, + (end - from) * sizeof current_screen->enable[0]); + + /* Mark the lines made empty by scrolling as enabled, empty and + normal video. */ + bzero (current_screen->used + end + amount, + - amount * sizeof current_screen->used[0]); + bzero (current_screen->highlight + end + amount, + - amount * sizeof current_screen->highlight[0]); + for (i = end + amount; i < end; i++) + { + current_screen->glyphs[i][0] = '\0'; + current_screen->enable[i] = 1; + } + + safe_bcopy (current_screen->bufp + from, + current_screen->bufp + from + amount, + (end - from) * sizeof current_screen->bufp[0]); + +#ifdef HAVE_X_WINDOWS + if (SCREEN_IS_X (screen)) + { + safe_bcopy (current_screen->nruns + from, + current_screen->nruns + from + amount, + (end - from) * sizeof current_screen->nruns[0]); + + safe_bcopy (current_screen->face_list + from, + current_screen->face_list + from + amount, + (end - from) * sizeof current_screen->face_list[0]); + + safe_bcopy (current_screen->top_left_x + from, + current_screen->top_left_x + from + amount, + (end - from) * sizeof current_screen->top_left_x[0]); + + safe_bcopy (current_screen->top_left_y + from, + current_screen->top_left_y + from + amount, + (end - from) * sizeof current_screen->top_left_y[0]); + + safe_bcopy (current_screen->pix_width + from, + current_screen->pix_width + from + amount, + (end - from) * sizeof current_screen->pix_width[0]); + + safe_bcopy (current_screen->pix_height + from, + current_screen->pix_height + from + amount, + (end - from) * sizeof current_screen->pix_height[0]); + } +#endif /* HAVE_X_WINDOWS */ + + update_end (screen); + } + return 1; +} + +/* After updating a window W that isn't the full screen wide, + copy all the columns that W does not occupy + into the SCREEN_DESIRED_GLYPHS (screen) from the SCREEN_PHYS_GLYPHS (screen) + so that update_screen will not change those columns. */ + +preserve_other_columns (w) + struct window *w; +{ + register int vpos; + register struct screen_glyphs *current_screen, *desired_screen; + register SCREEN_PTR screen = XSCREEN (w->screen); + int start = XFASTINT (w->left); + int end = XFASTINT (w->left) + XFASTINT (w->width); + int bot = XFASTINT (w->top) + XFASTINT (w->height); + + current_screen = SCREEN_CURRENT_GLYPHS (screen); + desired_screen = SCREEN_DESIRED_GLYPHS (screen); + + for (vpos = XFASTINT (w->top); vpos < bot; vpos++) + { + if (current_screen->enable[vpos] && desired_screen->enable[vpos]) + { + if (start > 0) + { + int len; + + bcopy (current_screen->glyphs[vpos], + desired_screen->glyphs[vpos], start); + len = min (start, current_screen->used[vpos]); + if (desired_screen->used[vpos] < len) + desired_screen->used[vpos] = len; + } + if (current_screen->used[vpos] > end + && desired_screen->used[vpos] < current_screen->used[vpos]) + { + while (desired_screen->used[vpos] < end) + desired_screen->glyphs[vpos][desired_screen->used[vpos]++] + = SPACEGLYPH; + bcopy (current_screen->glyphs[vpos] + end, + desired_screen->glyphs[vpos] + end, + current_screen->used[vpos] - end); + desired_screen->used[vpos] = current_screen->used[vpos]; + } + } + } +} + +#if 0 + +/* If window w does not need to be updated and isn't the full screen wide, + copy all the columns that w does occupy + into the SCREEN_DESIRED_LINES (screen) from the SCREEN_PHYS_LINES (screen) + so that update_screen will not change those columns. + + Have not been able to figure out how to use this correctly. */ + +preserve_my_columns (w) + struct window *w; +{ + register int vpos, fin; + register struct screen_glyphs *l1, *l2; + register SCREEN_PTR screen = XSCREEN (w->screen); + int start = XFASTINT (w->left); + int end = XFASTINT (w->left) + XFASTINT (w->width); + int bot = XFASTINT (w->top) + XFASTINT (w->height); + + for (vpos = XFASTINT (w->top); vpos < bot; vpos++) + { + if ((l1 = SCREEN_DESIRED_GLYPHS (screen)->glyphs[vpos + 1]) + && (l2 = SCREEN_PHYS_GLYPHS (screen)->glyphs[vpos + 1])) + { + if (l2->length > start && l1->length < l2->length) + { + fin = l2->length; + if (fin > end) fin = end; + while (l1->length < start) + l1->body[l1->length++] = ' '; + bcopy (l2->body + start, l1->body + start, fin - start); + l1->length = fin; + } + } + } +} + +#endif + +/* On discovering that the redisplay for a window was no good, + cancel the columns of that window, so that when the window is + displayed over again get_display_line will not complain. */ + +cancel_my_columns (w) + struct window *w; +{ + register int vpos; + register SCREEN_PTR screen = XSCREEN (w->screen); + register struct screen_glyphs *desired_glyphs = screen->desired_glyphs; + register int start = XFASTINT (w->left); + register int bot = XFASTINT (w->top) + XFASTINT (w->height); + + for (vpos = XFASTINT (w->top); vpos < bot; vpos++) + if (desired_glyphs->enable[vpos] + && desired_glyphs->used[vpos] >= start) + desired_glyphs->used[vpos] = start; +} + +/* These functions try to perform directly and immediately on the screen + the necessary output for one change in the buffer. + They may return 0 meaning nothing was done if anything is difficult, + or 1 meaning the output was performed properly. + They assume that the screen was up to date before the buffer + change being displayed. THey make various other assumptions too; + see command_loop_1 where these are called. */ + +int +direct_output_for_insert (g) + int g; +{ + register SCREEN_PTR screen = selected_screen; + register struct screen_glyphs *current_screen + = SCREEN_CURRENT_GLYPHS (screen); + +#ifndef COMPILER_REGISTER_BUG + register +#endif /* COMPILER_REGISTER_BUG */ + struct window *w = XWINDOW (selected_window); +#ifndef COMPILER_REGISTER_BUG + register +#endif /* COMPILER_REGISTER_BUG */ + int hpos = SCREEN_CURSOR_X (screen); +#ifndef COMPILER_REGISTER_BUG + register +#endif /* COMPILER_REGISTER_BUG */ + int vpos = SCREEN_CURSOR_Y (screen); + + /* Give up if about to continue line */ + if (hpos - XFASTINT (w->left) + 1 + 1 >= XFASTINT (w->width) + + /* Avoid losing if cursor is in invisible text off left margin */ + || (XINT (w->hscroll) && hpos == XFASTINT (w->left)) + + /* Give up if cursor outside window (in minibuf, probably) */ + || SCREEN_CURSOR_Y (screen) < XFASTINT (w->top) + || SCREEN_CURSOR_Y (screen) >= XFASTINT (w->top) + XFASTINT (w->height) + + /* Give up if cursor not really at SCREEN_CURSOR_X, SCREEN_CURSOR_Y */ + || !display_completed + + /* Give up if buffer appears in two places. */ + || buffer_shared > 1 + + /* Give up if w is minibuffer and a message is being displayed there */ + || (MINI_WINDOW_P (w) && echo_area_glyphs)) + return 0; + + current_screen->glyphs[vpos][hpos] = g; + unchanged_modified = MODIFF; + beg_unchanged = GPT - BEG; + XFASTINT (w->last_point) = point; + XFASTINT (w->last_point_x) = hpos; + XFASTINT (w->last_modified) = MODIFF; + + reassert_line_highlight (0, vpos); + write_glyphs (¤t_screen->glyphs[vpos][hpos], 1); + fflush (stdout); + ++SCREEN_CURSOR_X (screen); + if (hpos == current_screen->used[vpos]) + { + current_screen->used[vpos] = hpos + 1; + current_screen->glyphs[vpos][hpos + 1] = 0; + } + + return 1; +} + +int +direct_output_forward_char (n) + int n; +{ + register SCREEN_PTR screen = selected_screen; + register struct window *w = XWINDOW (selected_window); + + /* Avoid losing if cursor is in invisible text off left margin */ + if (XINT (w->hscroll) && SCREEN_CURSOR_X (screen) == XFASTINT (w->left)) + return 0; + + SCREEN_CURSOR_X (screen) += n; + XFASTINT (w->last_point_x) = SCREEN_CURSOR_X (screen); + XFASTINT (w->last_point) = point; + cursor_to (SCREEN_CURSOR_Y (screen), SCREEN_CURSOR_X (screen)); + fflush (stdout); + return 1; +} + +static void update_line (); + +/* Update screen S based on the data in SCREEN_DESIRED_GLYPHS. + Value is nonzero if redisplay stopped due to pending input. + FORCE nonzero means do not stop for pending input. */ + +int +update_screen (s, force, inhibit_hairy_id) + SCREEN_PTR s; + int force; + int inhibit_hairy_id; +{ + register struct screen_glyphs *current_screen = SCREEN_CURRENT_GLYPHS (s); + register struct screen_glyphs *desired_screen = SCREEN_DESIRED_GLYPHS (s); + register int i; + int pause; + int preempt_count = baud_rate / 2400 + 1; + extern input_pending; +#ifdef HAVE_X_WINDOWS + register int downto, leftmost; +#endif + + if (SCREEN_HEIGHT (s) == 0) abort (); /* Some bug zeros some core */ + + detect_input_pending (); + if (input_pending && !force) + { + pause = 1; + goto do_pause; + } + + update_begin (s); + + if (!line_ins_del_ok) + inhibit_hairy_id = 1; + + /* Don't compute for i/d line if just want cursor motion. */ + for (i = 0; i < SCREEN_HEIGHT (s); i++) + if (desired_screen->enable[i]) + break; + + /* Try doing i/d line, if not yet inhibited. */ + if (!inhibit_hairy_id && i < SCREEN_HEIGHT (s)) + force |= scrolling (s); + + /* Update the individual lines as needed. Do bottom line first. */ + + if (desired_screen->enable[SCREEN_HEIGHT (s) - 1]) + update_line (s, SCREEN_HEIGHT (s) - 1); + +#ifdef HAVE_X_WINDOWS + if (SCREEN_IS_X (s)) + { + leftmost = downto = s->display.x->internal_border_width; + if (desired_screen->enable[0]) + { + current_screen->top_left_x[SCREEN_HEIGHT (s) - 1] = leftmost; + current_screen->top_left_y[SCREEN_HEIGHT (s) - 1] + = PIXEL_HEIGHT (s) - s->display.x->internal_border_width + - LINE_HEIGHT(s, SCREEN_HEIGHT (s) - 1); + current_screen->top_left_x[0] = leftmost; + current_screen->top_left_y[0] = downto; + } + } +#endif /* HAVE_X_WINDOWS */ + + /* Now update the rest of the lines. */ + for (i = 0; i < SCREEN_HEIGHT (s) - 1 && (force || !input_pending); i++) + { + if (desired_screen->enable[i]) + { + if (SCREEN_IS_TERMCAP (s)) + { + /* Flush out every so many lines. + Also flush out if likely to have more than 1k buffered + otherwise. I'm told that some telnet connections get + really screwed by more than 1k output at once. */ + int outq = PENDING_OUTPUT_COUNT (stdout); + if (outq > 900 + || (outq > 20 && ((i - 1) % preempt_count == 0))) + { + fflush (stdout); + if (preempt_count == 1) + { +#ifdef TIOCOUTQ + if (ioctl (0, TIOCOUTQ, &outq) < 0) + /* Probably not a tty. Ignore the error and reset + * the outq count. */ + outq = PENDING_OUTPUT_COUNT (stdout); +#endif + outq *= 10; + sleep (outq / baud_rate); + } + } + if ((i - 1) % preempt_count == 0) + detect_input_pending (); + } + + update_line (s, i); +#ifdef HAVE_X_WINDOWS + if (SCREEN_IS_X (s)) + { + current_screen->top_left_y[i] = downto; + current_screen->top_left_x[i] = leftmost; + } +#endif /* HAVE_X_WINDOWS */ + } + + if (SCREEN_IS_X (s)) + downto += LINE_HEIGHT(s, i); + } + pause = (i < SCREEN_HEIGHT (s) - 1) ? i : 0; + + /* Now just clean up termcap drivers and set cursor, etc. */ + if (!pause) + { + + if (s == selected_screen && cursor_in_echo_area < 0) + cursor_to (SCREEN_HEIGHT (s) - 1, 0); + else if (s == selected_screen && cursor_in_echo_area + && !desired_screen->used[SCREEN_HEIGHT (s) - 1]) + cursor_to (SCREEN_HEIGHT (s), 0); + else if (cursor_in_echo_area) + cursor_to (SCREEN_HEIGHT (s) - 1, + min (SCREEN_WIDTH (s) - 1, + desired_screen->used[SCREEN_HEIGHT (s) - 1])); + else + cursor_to (SCREEN_CURSOR_Y (s), max (min (SCREEN_CURSOR_X (s), + SCREEN_WIDTH (s) - 1), 0)); + } + + update_end (s); + + if (termscript) + fflush (termscript); + fflush (stdout); + + /* Here if output is preempted because input is detected. */ + do_pause: + + if (SCREEN_HEIGHT (s) == 0) abort (); /* Some bug zeros some core */ + display_completed = !pause; + + bzero (desired_screen->enable, SCREEN_HEIGHT (s)); + return pause; +} + +/* Called when about to quit, to check for doing so + at an improper time. */ + +void +quit_error_check () +{ + if (SCREEN_DESIRED_GLYPHS (selected_screen) == 0) + return; + if (SCREEN_DESIRED_GLYPHS (selected_screen)->enable[0]) + abort (); + if (SCREEN_DESIRED_GLYPHS (selected_screen)->enable[SCREEN_HEIGHT (selected_screen) - 1]) + abort (); +} + +/* Decide what insert/delete line to do, and do it */ + +extern void scrolling_1 (); + +scrolling (screen) + SCREEN_PTR screen; +{ + int unchanged_at_top, unchanged_at_bottom; + int window_size; + int changed_lines; + int *old_hash = (int *) alloca (SCREEN_HEIGHT (screen) * sizeof (int)); + int *new_hash = (int *) alloca (SCREEN_HEIGHT (screen) * sizeof (int)); + int *draw_cost = (int *) alloca (SCREEN_HEIGHT (screen) * sizeof (int)); + register int i; + int free_at_end_vpos = SCREEN_HEIGHT (screen); + register struct screen_glyphs *current_screen = SCREEN_CURRENT_GLYPHS (screen); + register struct screen_glyphs *desired_screen = SCREEN_DESIRED_GLYPHS (screen); + + /* Compute hash codes of all the lines. + Also calculate number of changed lines, + number of unchanged lines at the beginning, + and number of unchanged lines at the end. */ + + changed_lines = 0; + unchanged_at_top = 0; + unchanged_at_bottom = SCREEN_HEIGHT (screen); + for (i = 0; i < SCREEN_HEIGHT (screen); i++) + { + /* Give up on this scrolling if some old lines are not enabled. */ + if (!current_screen->enable[i]) + return 0; + old_hash[i] = line_hash_code (current_screen, i); + if (! desired_screen->enable[i]) + new_hash[i] = old_hash[i]; + else + new_hash[i] = line_hash_code (desired_screen, i); + + if (old_hash[i] != new_hash[i]) + { + changed_lines++; + unchanged_at_bottom = SCREEN_HEIGHT (screen) - i - 1; + } + else if (i == unchanged_at_top) + unchanged_at_top++; + draw_cost[i] = line_draw_cost (desired_screen, i); + } + + /* If changed lines are few, don't allow preemption, don't scroll. */ + if (changed_lines < baud_rate / 2400 + || unchanged_at_bottom == SCREEN_HEIGHT (screen)) + return 1; + + window_size = (SCREEN_HEIGHT (screen) - unchanged_at_top + - unchanged_at_bottom); + + if (scroll_region_ok) + free_at_end_vpos -= unchanged_at_bottom; + else if (memory_below_screen) + free_at_end_vpos = -1; + + /* If large window, fast terminal and few lines in common between + current screen and desired screen, don't bother with i/d calc. */ + if (window_size >= 18 && baud_rate > 2400 + && (window_size >= + 10 * scrolling_max_lines_saved (unchanged_at_top, + SCREEN_HEIGHT (screen) - unchanged_at_bottom, + old_hash, new_hash, draw_cost))) + return 0; + + scrolling_1 (screen, window_size, unchanged_at_top, unchanged_at_bottom, + draw_cost + unchanged_at_top - 1, + old_hash + unchanged_at_top - 1, + new_hash + unchanged_at_top - 1, + free_at_end_vpos - unchanged_at_top); + + return 0; +} + +/* Return the offset in its buffer of the character at location col, line + in the given window. */ +int +buffer_posn_from_coords (window, col, line) + struct window *window; + int col, line; +{ + int window_left = XFASTINT (window->left); + + /* The actual width of the window is window->width less one for the + \ which ends wrapped lines, and less one if it's not the + rightmost window. */ + int window_width = (XFASTINT (window->width) - 1 + - (XFASTINT (window->width) + window_left + != SCREEN_WIDTH (XSCREEN (window->screen)))); + + /* The screen's list of buffer positions of line starts. */ + int *bufp = SCREEN_CURRENT_GLYPHS (XSCREEN (window->screen))->bufp; + + /* Since compute_motion will only operate on the current buffer, + we need to save the old one and restore it when we're done. */ + struct buffer *old_current_buffer = current_buffer; + int posn; + + current_buffer = XBUFFER (window->buffer); + + { + /* compute_motion will find the buffer position corresponding to a + screen position, given a buffer position to start at and its + screen position, by scanning from the start to the goal. In + order to make this faster, we need to choose a starting buffer + position with a known screen position as close to the goal as + possible. + + The bufp array in the screen_glyphs structure gives the buffer + position of the first character on each screen line. This + would be a perfect starting location, except that there's no + way to know if this character really starts flush with the + beginning of the line or if it is being continued from the + previous line; characters like ?\M-x display as \370 and can + wrap off the end of one line onto the next. + + So what we do is start on the target line, and scan upwards + until we find a screen line that starts right after a newline + in the buffer, or at the top of the window; both of these + assure us that the character at bufp starts flush with the + beginning of the line. */ + int i; + + /* Only works for the leftmost window on a line. bufp is useless + for the others. */ + if (window_left == 0) + { + for (i = line; i > XFASTINT (window->top); i--) + if (FETCH_CHAR (bufp[i]-1) == '\n') + break; + } + + posn + = compute_motion (bufp[i], i, window_left, + ZV, col, line, + window_width, XINT (window->hscroll), 0) + ->bufpos; + } + + current_buffer = old_current_buffer; + + return posn; +} + +static int +count_blanks (r) + register GLYPH *r; +{ + register GLYPH *p = r; + while (*r++ == SPACEGLYPH); + return r - p - 1; +} + +static int +count_match (str1, str2) + GLYPH *str1, *str2; +{ + register GLYPH *p1 = str1; + register GLYPH *p2 = str2; + while (*p1++ == *p2++); + return p1 - str1 - 1; +} + +/* Char insertion/deletion cost vector, from term.c */ +extern int *char_ins_del_vector; + +#define char_ins_del_cost(s) (&char_ins_del_vector[SCREEN_HEIGHT((s))]) + +static void +update_line (screen, vpos) + register SCREEN_PTR screen; + int vpos; +{ + register GLYPH *obody, *nbody, *op1, *op2, *np1, *temp; + int tem; + int osp, nsp, begmatch, endmatch, olen, nlen; + int save; + register struct screen_glyphs *current_screen + = SCREEN_CURRENT_GLYPHS (screen); + register struct screen_glyphs *desired_screen + = SCREEN_DESIRED_GLYPHS (screen); + + if (desired_screen->highlight[vpos] + != (current_screen->enable[vpos] && current_screen->highlight[vpos])) + { + change_line_highlight (desired_screen->highlight[vpos], vpos, + (current_screen->enable[vpos] ? + current_screen->used[vpos] : 0)); + current_screen->enable[vpos] = 0; + } + else + reassert_line_highlight (desired_screen->highlight[vpos], vpos); + + if (! current_screen->enable[vpos]) + { + olen = 0; + } + else + { + obody = current_screen->glyphs[vpos]; + olen = current_screen->used[vpos]; + if (! current_screen->highlight[vpos]) + { + if (!must_write_spaces) + while (obody[olen - 1] == SPACEGLYPH && olen > 0) + olen--; + } + else + { + /* For an inverse-video line, remember we gave it + spaces all the way to the screen edge + so that the reverse video extends all the way across. */ + + while (olen < SCREEN_WIDTH (screen) - 1) + obody[olen++] = SPACEGLYPH; + } + } + + /* One way or another, this will enable the line being updated. */ + current_screen->enable[vpos] = 1; + current_screen->used[vpos] = desired_screen->used[vpos]; + current_screen->highlight[vpos] = desired_screen->highlight[vpos]; + current_screen->bufp[vpos] = desired_screen->bufp[vpos]; + +#ifdef HAVE_X_WINDOWS + if (SCREEN_IS_X (screen)) + { + current_screen->pix_width[vpos] + = current_screen->used[vpos] + * FONT_WIDTH (screen->display.x->font); + current_screen->pix_height[vpos] + = FONT_HEIGHT (screen->display.x->font); + } +#endif /* HAVE_X_WINDOWS */ + + if (!desired_screen->enable[vpos]) + { + nlen = 0; + goto just_erase; + } + + nbody = desired_screen->glyphs[vpos]; + nlen = desired_screen->used[vpos]; + + /* Pretend trailing spaces are not there at all, + unless for one reason or another we must write all spaces. */ + if (! desired_screen->highlight[vpos]) + { + if (!must_write_spaces) + /* We know that the previous character byte contains 0. */ + while (nbody[nlen - 1] == SPACEGLYPH) + nlen--; + } + else + { + /* For an inverse-video line, give it extra trailing spaces + all the way to the screen edge + so that the reverse video extends all the way across. */ + + while (nlen < SCREEN_WIDTH (screen) - 1) + nbody[nlen++] = SPACEGLYPH; + } + + /* If there's no i/d char, quickly do the best we can without it. */ + if (!char_ins_del_ok) + { + int i,j; + + for (i = 0; i < nlen; i++) + { + if (i >= olen || nbody[i] != obody[i]) /* A non-matching char. */ + { + cursor_to (vpos, i); + for (j = 1; (i + j < nlen && + (i + j >= olen || nbody[i+j] != obody[i+j])); + j++); + + /* Output this run of non-matching chars. */ + write_glyphs (nbody + i, j); + i += j - 1; + + /* Now find the next non-match. */ + } + } + + /* Clear the rest of the line, or the non-clear part of it. */ + if (olen > nlen) + { + cursor_to (vpos, nlen); + clear_end_of_line (olen); + } + + /* Exchange contents between current_screen and new_screen. */ + temp = desired_screen->glyphs[vpos]; + desired_screen->glyphs[vpos] = current_screen->glyphs[vpos]; + current_screen->glyphs[vpos] = temp; + + return; + } + + if (!olen) + { + nsp = (must_write_spaces || desired_screen->highlight[vpos]) + ? 0 : count_blanks (nbody); + if (nlen > nsp) + { + cursor_to (vpos, nsp); + write_glyphs (nbody + nsp, nlen - nsp); + } + + /* Exchange contents between current_screen and new_screen. */ + temp = desired_screen->glyphs[vpos]; + desired_screen->glyphs[vpos] = current_screen->glyphs[vpos]; + current_screen->glyphs[vpos] = temp; + + return; + } + + obody[olen] = 1; + save = nbody[nlen]; + nbody[nlen] = 0; + + /* Compute number of leading blanks in old and new contents. */ + osp = count_blanks (obody); + if (!desired_screen->highlight[vpos]) + nsp = count_blanks (nbody); + else + nsp = 0; + + /* Compute number of matching chars starting with first nonblank. */ + begmatch = count_match (obody + osp, nbody + nsp); + + /* Spaces in new match implicit space past the end of old. */ + /* A bug causing this to be a no-op was fixed in 18.29. */ + if (!must_write_spaces && osp + begmatch == olen) + { + np1 = nbody + nsp; + while (np1[begmatch] == SPACEGLYPH) + begmatch++; + } + + /* Avoid doing insert/delete char + just cause number of leading spaces differs + when the following text does not match. */ + if (begmatch == 0 && osp != nsp) + osp = nsp = min (osp, nsp); + + /* Find matching characters at end of line */ + op1 = obody + olen; + np1 = nbody + nlen; + op2 = op1 + begmatch - min (olen - osp, nlen - nsp); + while (op1 > op2 && op1[-1] == np1[-1]) + { + op1--; + np1--; + } + endmatch = obody + olen - op1; + + /* Put correct value back in nbody[nlen]. + This is important because direct_output_for_insert + can write into the line at a later point. + If this screws up the zero at the end of the line, re-establish it. */ + nbody[nlen] = save; + obody[olen] = 0; + + /* tem gets the distance to insert or delete. + endmatch is how many characters we save by doing so. + Is it worth it? */ + + tem = (nlen - nsp) - (olen - osp); + if (endmatch && tem + && (!char_ins_del_ok || endmatch <= char_ins_del_cost (screen)[tem])) + endmatch = 0; + + /* nsp - osp is the distance to insert or delete. + If that is nonzero, begmatch is known to be nonzero also. + begmatch + endmatch is how much we save by doing the ins/del. + Is it worth it? */ + + if (nsp != osp + && (!char_ins_del_ok + || begmatch + endmatch <= char_ins_del_cost (screen)[nsp - osp])) + { + begmatch = 0; + endmatch = 0; + osp = nsp = min (osp, nsp); + } + + /* Now go through the line, inserting, writing and + deleting as appropriate. */ + + if (osp > nsp) + { + cursor_to (vpos, nsp); + delete_glyphs (osp - nsp); + } + else if (nsp > osp) + { + /* If going to delete chars later in line + and insert earlier in the line, + must delete first to avoid losing data in the insert */ + if (endmatch && nlen < olen + nsp - osp) + { + cursor_to (vpos, nlen - endmatch + osp - nsp); + delete_glyphs (olen + nsp - osp - nlen); + olen = nlen - (nsp - osp); + } + cursor_to (vpos, osp); + insert_glyphs ((char *)0, nsp - osp); + } + olen += nsp - osp; + + tem = nsp + begmatch + endmatch; + if (nlen != tem || olen != tem) + { + cursor_to (vpos, nsp + begmatch); + if (!endmatch || nlen == olen) + { + /* If new text being written reaches right margin, + there is no need to do clear-to-eol at the end. + (and it would not be safe, since cursor is not + going to be "at the margin" after the text is done) */ + if (nlen == SCREEN_WIDTH (screen)) + olen = 0; + write_glyphs (nbody + nsp + begmatch, nlen - tem); + +#ifdef obsolete + +/* the following code loses disastrously if tem == nlen. + Rather than trying to fix that case, I am trying the simpler + solution found above. */ + + /* If the text reaches to the right margin, + it will lose one way or another (depending on AutoWrap) + to clear to end of line after outputting all the text. + So pause with one character to go and clear the line then. */ + if (nlen == SCREEN_WIDTH (screen) && fast_clear_end_of_line && olen > nlen) + { + /* endmatch must be zero, and tem must equal nsp + begmatch */ + write_glyphs (nbody + tem, nlen - tem - 1); + clear_end_of_line (olen); + olen = 0; /* Don't let it be cleared again later */ + write_glyphs (nbody + nlen - 1, 1); + } + else + write_glyphs (nbody + nsp + begmatch, nlen - tem); +#endif /* OBSOLETE */ + + } + else if (nlen > olen) + { + write_glyphs (nbody + nsp + begmatch, olen - tem); + insert_glyphs (nbody + nsp + begmatch + olen - tem, nlen - olen); + olen = nlen; + } + else if (olen > nlen) + { + write_glyphs (nbody + nsp + begmatch, nlen - tem); + delete_glyphs (olen - nlen); + olen = nlen; + } + } + + just_erase: + /* If any unerased characters remain after the new line, erase them. */ + if (olen > nlen) + { + cursor_to (vpos, nlen); + clear_end_of_line (olen); + } + + /* Exchange contents between current_screen and new_screen. */ + temp = desired_screen->glyphs[vpos]; + desired_screen->glyphs[vpos] = current_screen->glyphs[vpos]; + current_screen->glyphs[vpos] = temp; +} + +DEFUN ("open-termscript", Fopen_termscript, Sopen_termscript, + 1, 1, "FOpen termscript file: ", + "Start writing all terminal output to FILE as well as the terminal.\n\ +FILE = nil means just close any termscript file currently open.") + (file) + Lisp_Object file; +{ + if (termscript != 0) fclose (termscript); + termscript = 0; + + if (! NULL (file)) + { + file = Fexpand_file_name (file, Qnil); + termscript = fopen (XSTRING (file)->data, "w"); + if (termscript == 0) + report_file_error ("Opening termscript", Fcons (file, Qnil)); + } + return Qnil; +} + + +#ifdef SIGWINCH +window_change_signal () +{ + int width, height; + extern int errno; + int old_errno = errno; + + get_screen_size (&width, &height); + + /* The screen size change obviously applies to a termcap-controlled + screen. Find such a screen in the list, and assume it's the only + one (since the redisplay code always writes to stdout, not a + FILE * specified in the screen structure). Record the new size, + but don't reallocate the data structures now. Let that be done + later outside of the signal handler. */ + + { + Lisp_Object tail; + + for (tail = Vscreen_list; CONSP (tail); tail = XCONS (tail)->cdr) + { + SCREEN_PTR s = XSCREEN (XCONS (tail)->car); + + if (s->output_method == output_termcap) + { + ++in_display; + change_screen_size (s, height, width, 0); + --in_display; + break; + } + } + } + + signal (SIGWINCH, window_change_signal); + errno = old_errno; +} +#endif /* SIGWINCH */ + + +/* Do any change in screen size that was requested by a signal. */ + +do_pending_window_change () +{ + /* If window_change_signal should have run before, run it now. */ + while (delayed_size_change) + { + Lisp_Object tail; + + delayed_size_change = 0; + + for (tail = Vscreen_list; CONSP (tail); tail = XCONS (tail)->cdr) + { + SCREEN_PTR s = XSCREEN (XCONS (tail)->car); + int height = SCREEN_NEW_HEIGHT (s); + int width = SCREEN_NEW_WIDTH (s); + + SCREEN_NEW_HEIGHT (s) = 0; + SCREEN_NEW_WIDTH (s) = 0; + + if (height != 0) + change_screen_size (s, height, width, 0); + } + } +} + + +/* Change the screen height and/or width. Values may be given as zero to + indicate no change is to take place. */ + +change_screen_size (screen, newlength, newwidth, pretend) + register SCREEN_PTR screen; + register int newlength, newwidth, pretend; +{ + /* If we can't deal with the change now, queue it for later. */ + if (in_display) + { + SCREEN_NEW_HEIGHT (screen) = newlength; + SCREEN_NEW_WIDTH (screen) = newwidth; + delayed_size_change = 1; + return; + } + + /* This size-change overrides any pending one for this screen. */ + SCREEN_NEW_HEIGHT (screen) = 0; + SCREEN_NEW_WIDTH (screen) = 0; + + if ((newlength == 0 || newlength == SCREEN_HEIGHT (screen)) + && (newwidth == 0 || newwidth == SCREEN_WIDTH (screen))) + return; + + if (newlength && newlength != SCREEN_HEIGHT (screen)) + { + if (XSCREEN (WINDOW_SCREEN (XWINDOW (SCREEN_MINIBUF_WINDOW (screen)))) + == screen + && ! EQ (SCREEN_MINIBUF_WINDOW (screen), + SCREEN_ROOT_WINDOW (screen))) + { + /* Screen has both root and minibuffer. */ + set_window_height (SCREEN_ROOT_WINDOW (screen), + newlength - 1, 0); + XFASTINT (XWINDOW (SCREEN_MINIBUF_WINDOW (screen))->top) + = newlength - 1; + set_window_height (SCREEN_MINIBUF_WINDOW (screen), 1, 0); + } + else + /* Screen has just one top-level window. */ + set_window_height (SCREEN_ROOT_WINDOW (screen), newlength, 0); + + if (SCREEN_IS_TERMCAP (screen) == output_termcap && !pretend) + ScreenRows = newlength; + +#if 0 + if (screen->output_method == output_termcap) + { + screen_height = newlength; + if (!pretend) + ScreenRows = newlength; + } +#endif + } + + if (newwidth && newwidth != SCREEN_WIDTH (screen)) + { + set_window_width (SCREEN_ROOT_WINDOW (screen), newwidth, 0); + if (XSCREEN (WINDOW_SCREEN (XWINDOW (SCREEN_MINIBUF_WINDOW (screen)))) + == screen) + set_window_width (SCREEN_MINIBUF_WINDOW (screen), newwidth, 0); + SCREEN_WIDTH (screen) = newwidth; + + if (SCREEN_IS_TERMCAP (screen) && !pretend) + ScreenCols = newwidth; +#if 0 + if (screen->output_method == output_termcap) + { + screen_width = newwidth; + if (!pretend) + ScreenCols = newwidth; + } +#endif + } + + if (newlength) + SCREEN_HEIGHT (screen) = newlength; + + remake_screen_glyphs (screen); + calculate_costs (screen); +} + +DEFUN ("send-string-to-terminal", Fsend_string_to_terminal, + Ssend_string_to_terminal, 1, 1, 0, + "Send STRING to the terminal without alteration.\n\ +Control characters in STRING will have terminal-dependent effects.") + (str) + Lisp_Object str; +{ + CHECK_STRING (str, 0); + fwrite (XSTRING (str)->data, 1, XSTRING (str)->size, stdout); + fflush (stdout); + if (termscript) + { + fwrite (XSTRING (str)->data, 1, XSTRING (str)->size, termscript); + fflush (termscript); + } + return Qnil; +} + +DEFUN ("ding", Fding, Sding, 0, 1, 0, + "Beep, or flash the screen.\n\ +Also, unless an argument is given,\n\ +terminate any keyboard macro currently executing.") + (arg) + Lisp_Object arg; +{ + if (!NULL (arg)) + { + ring_bell (); + fflush (stdout); + } + else + bitch_at_user (); + + return Qnil; +} + +bitch_at_user () +{ + if (noninteractive) + putchar (07); + else if (!INTERACTIVE) /* Stop executing a keyboard macro. */ + error ("Keyboard macro terminated by a command ringing the bell"); + else + ring_bell (); + fflush (stdout); +} + +DEFUN ("sleep-for", Fsleep_for, Ssleep_for, 1, 2, 0, + "Pause, without updating display, for ARG seconds.\n\ +Optional second arg non-nil means ARG is measured in milliseconds.\n\ +\(Not all operating systems support milliseconds.)") + (n, millisec) + Lisp_Object n, millisec; +{ +#ifndef subprocesses +#ifdef HAVE_TIMEVAL + struct timeval timeout, end_time, garbage1; +#endif /* HAVE_TIMEVAL */ +#endif /* no subprocesses */ + int usec = 0; + int sec; + + CHECK_NUMBER (n, 0); + sec = XINT (n); + if (sec <= 0) + return Qnil; + + if (!NULL (millisec)) + { +#ifndef HAVE_TIMEVAL + error ("millisecond sit-for not supported on %s", SYSTEM_TYPE); +#else + usec = sec % 1000 * 1000; + sec /= 1000; +#endif + } + +#ifdef subprocesses + wait_reading_process_input (sec, usec, 0, 0); +#else /* No subprocesses */ + immediate_quit = 1; + QUIT; + +#ifdef VMS + sys_sleep (sec); +#else /* not VMS */ +/* The reason this is done this way + (rather than defined (H_S) && defined (H_T)) + is because the VMS preprocessor doesn't grok `defined' */ +#ifdef HAVE_SELECT +#ifdef HAVE_TIMEVAL + gettimeofday (&end_time, &garbage1); + end_time.tv_sec += sec; + end_time.tv_usec += usec; + if (end_time.tv_usec >= 1000000) + end_time.tv_sec++, end_time.tv_usec -= 1000000; + + while (1) + { + gettimeofday (&timeout, &garbage1); + timeout.tv_sec = end_time.tv_sec - timeout.tv_sec; + timeout.tv_usec = end_time.tv_usec - timeout.tv_usec; + if (timeout.tv_usec < 0) + timeout.tv_usec += 1000000, timeout.tv_sec--; + if (timeout.tv_sec < 0) + break; + if (!select (1, 0, 0, 0, &timeout)) + break; + } +#else /* not HAVE_TIMEVAL */ + /* Is it safe to quit out of `sleep'? I'm afraid to trust it. */ + sleep (sec); +#endif /* HAVE_TIMEVAL */ +#else /* not HAVE_SELECT */ + sleep (sec); +#endif /* HAVE_SELECT */ +#endif /* not VMS */ + + immediate_quit = 0; +#endif /* no subprocesses */ + + return Qnil; +} + +DEFUN ("sit-for", Fsit_for, Ssit_for, 1, 3, 0, + "Perform redisplay, then wait for ARG seconds or until input is available.\n\ +Optional second arg non-nil means ARG counts in milliseconds.\n\ +Optional third arg non-nil means don't redisplay, just wait for input.\n\ +Redisplay is preempted as always if input arrives, and does not happen\n\ +if input is available before it starts.\n\ +Value is t if waited the full time with no input arriving.") + (n, millisec, nodisp) + Lisp_Object n, millisec, nodisp; +{ +#ifndef subprocesses +#ifdef HAVE_TIMEVAL + struct timeval timeout; +#else + int timeout_sec; +#endif + int waitchannels; +#endif /* no subprocesses */ + int usec = 0; + int sec; + + CHECK_NUMBER (n, 0); + + if (detect_input_pending ()) + return Qnil; + + if (EQ (nodisp, Qnil)) + redisplay_preserve_echo_area (); + + sec = XINT (n); + if (sec <= 0) + return Qt; + + if (!NULL (millisec)) + { +#ifndef HAVE_TIMEVAL + error ("millisecond sleep-for not supported on %s", SYSTEM_TYPE); +#else + usec = sec % 1000 * 1000; + sec /= 1000; +#endif + } + +#ifdef subprocesses +#ifdef SIGIO + gobble_input (); +#endif /* SIGIO */ + wait_reading_process_input (sec, usec, 1, 1); +#else /* no subprocesses */ + immediate_quit = 1; + QUIT; + + waitchannels = 1; +#ifdef VMS + input_wait_timeout (XINT (n)); +#else /* not VMS */ +#ifndef HAVE_TIMEVAL + timeout_sec = sec; + select (1, &waitchannels, 0, 0, &timeout_sec); +#else /* HAVE_TIMEVAL */ + timeout.tv_sec = sec; + timeout.tv_usec = usec; + select (1, &waitchannels, 0, 0, &timeout); +#endif /* HAVE_TIMEVAL */ +#endif /* not VMS */ + + immediate_quit = 0; +#endif /* no subprocesses */ + + return detect_input_pending () ? Qnil : Qt; +} + +DEFUN ("sleep-for-millisecs", Fsleep_for_millisecs, Ssleep_for_millisecs, + 1, 1, 0, + "Pause, without updating display, for ARG milliseconds.") + (n) + Lisp_Object n; +{ +#ifndef HAVE_TIMEVAL + error ("sleep-for-millisecs not supported on %s", SYSTEM_TYPE); +#else + CHECK_NUMBER (n, 0); + wait_reading_process_input (XINT (n) / 1000, XINT (n) % 1000 * 1000, + 0, 0); + return Qnil; +#endif /* HAVE_TIMEVAL */ +} + +char *terminal_type; + +/* Initialization done when Emacs fork is started, before doing stty. */ +/* Determine terminal type and set terminal_driver */ +/* Then invoke its decoding routine to set up variables + in the terminal package */ + +init_display () +{ +#ifdef HAVE_X_WINDOWS + extern int display_arg; +#endif + + meta_key = 0; + inverse_video = 0; + cursor_in_echo_area = 0; + terminal_type = (char *) 0; + + /* If the DISPLAY environment variable is set, try to use X, and + die with an error message if that doesn't work. */ + + /* Check if we're using a window system here before trying to + initialize the terminal. If we check the terminal first, + + If someone has indicated that they want + to use a window system, we shouldn't bother initializing the + terminal. This is especially important when the terminal is so + dumb that emacs gives up before and doesn't bother using the window + system. */ + +#ifdef HAVE_X_WINDOWS + if (!inhibit_window_system && (display_arg || egetenv ("DISPLAY"))) + { + Vwindow_system = intern ("x"); +#ifdef HAVE_X11 + Vwindow_system_version = make_number (11); +#else + Vwindow_system_version = make_number (10); +#endif + return; + } +#endif /* HAVE_X_WINDOWS */ + + /* If no window system has been specified, try to use the terminal. */ + if (! isatty (0)) + { + fprintf (stderr, "emacs: standard input is not a tty\n"); + exit (1); + } + + /* Look at the TERM variable */ + terminal_type = (char *) getenv ("TERM"); + if (!terminal_type) + { +#ifdef VMS + fprintf (stderr, "Please specify your terminal type.\n\ +For types defined in VMS, use set term /device=TYPE.\n\ +For types not defined in VMS, use define emacs_term \"TYPE\".\n\ +\(The quotation marks are necessary since terminal types are lower case.)\n"); +#else + fprintf (stderr, "Please set the environment variable TERM; see tset(1).\n"); +#endif + exit (1); + } + +#ifdef VMS + /* VMS DCL tends to upcase things, so downcase term type. + Hardly any uppercase letters in terminal types; should be none. */ + { + char *new = (char *) xmalloc (strlen (terminal_type) + 1); + char *p; + + strcpy (new, terminal_type); + + for (p = new; *p; p++) + if (isupper (*p)) + *p = tolower (*p); + + terminal_type = new; + } +#endif + + term_init (terminal_type); + + remake_screen_glyphs (selected_screen); + calculate_costs (selected_screen); + + /* X and Y coordinates of the cursor between updates. */ + SCREEN_CURSOR_X (selected_screen) = 0; + SCREEN_CURSOR_Y (selected_screen) = 0; + +#ifdef SIGWINCH +#ifndef CANNOT_DUMP + if (initialized) +#endif /* CANNOT_DUMP */ + signal (SIGWINCH, window_change_signal); +#endif /* SIGWINCH */ +} + +syms_of_display () +{ +#ifdef MULTI_SCREEN + defsubr (&Sredraw_screen); +#endif + defsubr (&Sredraw_display); + defsubr (&Sopen_termscript); + defsubr (&Sding); + defsubr (&Ssit_for); + defsubr (&Ssleep_for); + defsubr (&Ssend_string_to_terminal); + + DEFVAR_INT ("baud-rate", &baud_rate, + "The output baud rate of the terminal.\n\ +On most systems, changing this value will affect the amount of padding\n\ +and the other strategic decisions made during redisplay."); + DEFVAR_BOOL ("inverse-video", &inverse_video, + "*Non-nil means invert the entire screen display.\n\ +This means everything is in inverse video which otherwise would not be."); + DEFVAR_BOOL ("visible-bell", &visible_bell, + "*Non-nil means try to flash the screen to represent a bell."); + DEFVAR_BOOL ("no-redraw-on-reenter", &no_redraw_on_reenter, + "*Non-nil means no need to redraw entire screen after suspending.\n\ +A non-nil value is useful if the terminal can automatically preserve\n\ +Emacs's screen display when you reenter Emacs.\n\ +It is up to you to set this variable if your terminal can do that."); + DEFVAR_LISP ("window-system", &Vwindow_system, + "A symbol naming the window-system under which Emacs is running\n\ +\(such as `x'), or nil if emacs is running on an ordinary terminal."); + DEFVAR_LISP ("window-system-version", &Vwindow_system_version, + "The version number of the window system in use.\n\ +For X windows, this is 10 or 11."); + DEFVAR_BOOL ("cursor-in-echo-area", &cursor_in_echo_area, + "Non-nil means put cursor in minibuffer, at end of any message there."); + DEFVAR_LISP ("glyph-table", &Vglyph_table, + "Table defining how to output a glyph code to the screen.\n\ +If not nil, this is a vector indexed by glyph code to define the glyph.\n\ +Each element can be:\n\ + integer: a glyph code which this glyph is an alias for.\n\ + string: output this glyph using that string (not impl. in X windows).\n\ + nil: this glyph mod 256 is char code to output,\n\ + and this glyph / 256 is face code for X windows (see `x-set-face')."); + Vglyph_table = Qnil; + + DEFVAR_LISP ("standard-display-table", &Vstandard_display_table, + "Display table to use for buffers that specify none.\n\ +See `buffer-display-table' for more information."); + Vstandard_display_table = Qnil; + + /* Initialize `window-system', unless init_display already decided it. */ +#ifdef CANNOT_DUMP + if (noninteractive) +#endif + { + Vwindow_system = Qnil; + Vwindow_system_version = Qnil; + } +} +