diff options
Diffstat (limited to 'src/dispnew.c')
-rw-r--r-- | src/dispnew.c | 1671 |
1 files changed, 1671 insertions, 0 deletions
diff --git a/src/dispnew.c b/src/dispnew.c new file mode 100644 index 00000000000..38cef720952 --- /dev/null +++ b/src/dispnew.c @@ -0,0 +1,1671 @@ +/* Newly written part of redisplay code. + 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. */ + + +/* This must precede sys/signal.h on certain machines. */ +#include <sys/types.h> +#include <signal.h> + +#include "config.h" +#include <stdio.h> +#include <errno.h> + +#ifdef HAVE_TIMEVAL +#ifdef HPUX +#include <time.h> +#else +#include <sys/time.h> +#endif +#endif + +#ifdef HAVE_TERMIO +#include <termio.h> +#ifdef TCOUTQ +#undef TIOCOUTQ +#define TIOCOUTQ TCOUTQ +#include <fcntl.h> +#endif /* TCOUTQ defined */ +#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 + +/* We are unable to use interrupts if FIONREAD is not available, + so flush SIGIO so we won't try. */ +#ifndef FIONREAD +#ifdef SIGIO +#undef SIGIO +#endif +#endif + +#undef NULL + +#include "termchar.h" +#include "termopts.h" +#include "cm.h" +#include "dispextern.h" +#include "lisp.h" +#include "buffer.h" +#include "window.h" +#include "commands.h" + +#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. */ +#ifdef __GNU_LIBRARY__ +#define PENDING_OUTPUT_COUNT(FILE) ((FILE)->__bufp - (FILE)->__buffer) +#else +#define PENDING_OUTPUT_COUNT(FILE) ((FILE)->_ptr - (FILE)->_base) +#endif +#endif /* No PENDING_OUTPUT_COUNT */ + +/* Nonzero means do not assume anything about current + contents of actual terminal screen */ + +int screen_garbaged; + +/* Desired terminal cursor position (to show position of point), + origin zero */ + +int cursor_hpos, cursor_vpos; + +/* Nonzero means last display completed and cursor is really at + cursor_hpos, cursor_vpos. 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 window system, or nil if no window system. */ + +Lisp_Object Vwindow_system_version; + +/* Nonzero means reading single-character input with prompt + so put cursor on minibuffer after the prompt. */ + +int cursor_in_echo_area; + +/* Nonzero means finish redisplay regardless of available input. + This is used with X windows to avoid a weird timing-dependent bug. */ + +int force_redisplay; + +/* Description of actual screen contents. */ + +struct matrix *current_screen; + +/* Description of desired screen contents. */ + +struct matrix *new_screen; + +/* Buffer sometimes used to hold partial screen contents. */ + +struct matrix *temp_screen; + +/* Stdio stream being used for copy of all terminal output. */ + +FILE *termscript; + +/* Structure for info on cursor positioning */ + +struct cm Wcm; + +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. */ + +/* This buffer records the history of display preemption. */ + +struct preempt +{ + /* Number of keyboard characters read so far at preempt. */ + int keyboard_char_count; + /* Vertical position at which preemption occurred. */ + int vpos; +}; + +#define N_PREEMPTIONS 50 + +/* Circular buffer recording recent display preemptions. */ +struct preempt preemptions[N_PREEMPTIONS]; + +/* Index of next element in preemptions. */ +int preemption_index; + +/* Set these variables in the debugger to force a display preemption. */ +int debug_preemption_vpos = -1; +int debug_preemption_char_count = -1; + +extern int num_input_chars; + +/* Free and reallocate current_screen and new_screen. */ + +struct matrix *make_screen_structure (); + +remake_screen_structures () +{ + int i; + + if (current_screen) + free_screen_structure (current_screen); + if (new_screen) + free_screen_structure (new_screen); + if (temp_screen) + free_screen_structure (temp_screen); + + current_screen = make_screen_structure (0); + new_screen = make_screen_structure (0); + temp_screen = make_screen_structure (1); + + if (message_buf) + { + /* The echo_area_contents, used by error and message, may be pointing at + the string we are replacing. If so, update it. */ + int repair_echo_area_contents = echo_area_contents == message_buf; + + message_buf = (char *) xrealloc (message_buf, screen_width + 1); + + if (repair_echo_area_contents) echo_area_contents = message_buf; + } + else + message_buf = (char *) xmalloc (screen_width + 1); + + /* This may fix a problem of occasionally displaying garbage in the echo area + after a resize in X Windows. */ + for (i = 0; i < screen_width; i++) + message_buf[i] = ' '; + message_buf[screen_width] = 0; +} + +struct matrix * +make_screen_structure (empty) + int empty; +{ + int i; + struct matrix *new = (struct matrix *) xmalloc (sizeof (struct matrix)); + + new->height = screen_height; + new->width = screen_width; + new->highlight = (char *) xmalloc (screen_height); + new->enable = (char *) xmalloc (screen_height); + new->contents = (unsigned char **) xmalloc (screen_height * sizeof (char *)); + new->used = (int *) xmalloc (screen_height * sizeof (int)); + if (empty) + { + /* Make the buffer used by decode_mode_spec. */ + new->total_contents = (unsigned char *) xmalloc (screen_width + 2); + for (i = 0; i < screen_width; i++) + new->total_contents[i] = ' '; + new->total_contents[screen_width] = 0; + } + else + { + /* Add 2 to leave extra bytes at beginning and end of each line. */ + new->total_contents = (unsigned char *) xmalloc (screen_height * (screen_width + 2)); + for (i = 0; i < screen_height; i++) + { + int j; + + new->contents[i] = new->total_contents + i * (screen_width + 2) + 1; + new->contents[i][screen_width] = 0; + new->contents[i][-1] = 0; + for (j = 0; j < screen_width; j++) + new->contents[i][j] = ' '; + } + } + bzero (new->enable, screen_height); + return new; +} + +free_screen_structure (matrix) + struct matrix *matrix; +{ + if (matrix->total_contents) + free (matrix->total_contents); + free (matrix->contents); + free (matrix->highlight); + free (matrix->enable); + free (matrix->used); + free (matrix); +} + +/* Return the hash code of contents of line VPOS of screen-matrix M. */ + +int +line_hash_code (m, vpos) + struct matrix *m; + int vpos; +{ + register unsigned char *body; + register int h = 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->contents[vpos]; + + if (must_write_spaces) + { + while (1) + { + int c = *body++; + if (c == 0) + break; + h = (((h << 4) + (h >> 24)) & 0x0fffffff) + c - ' '; + } + } + else + { + while (1) + { + int c = *body++; + if (c == 0) + break; + h = (((h << 4) + (h >> 24)) & 0x0fffffff) + c; + } + } + 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. */ + +int +line_draw_cost (m, vpos) + struct matrix *m; + int vpos; +{ + register unsigned char *body; + register int i; + + if (must_write_spaces) + return m->used[vpos]; + + body = m->contents[vpos]; + for (i = m->used[vpos]; i > 0 && body[i - 2] == ' '; i--); + + i -= count_blanks (body); + return max (i, 0); +} + +/* The functions on this page are the interface from xdisp.c to redisplay. + + The only other interface into redisplay is through setting + cursor_hpos and cursor_vpos (in xdisp.c) and setting screen_garbaged. */ + +/* cancel_line eliminates any request to display a line at position `vpos' */ + +cancel_line (vpos) + int vpos; +{ + new_screen->enable[vpos] = 0; +} + +clear_screen_records () +{ + int i; + + bzero (current_screen->enable, screen_height); +} + +/* Get ready to display on line `vpos' + and set it up for outputting starting at `hpos' within it. + Return the text string where that line is stored. */ + +unsigned char * +get_display_line (vpos, hpos) + int vpos; + register int hpos; +{ + if (new_screen->enable[vpos] && new_screen->used[vpos] > hpos) + abort (); + if (! new_screen->enable[vpos]) + { + new_screen->used[vpos] = 0; + new_screen->highlight[vpos] = 0; + new_screen->enable[vpos] = 1; + } + + if (hpos > new_screen->used[vpos]) + { + unsigned char *p = new_screen->contents[vpos] + new_screen->used[vpos]; + unsigned char *end = new_screen->contents[vpos] + hpos; + new_screen->used[vpos] = hpos; + while (p != end) + *p++ = ' '; + } + + return new_screen->contents[vpos]; +} + +/* 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 (from, end, amount) + int from, end, amount; +{ + register int i; + + if (!line_ins_del_ok) + return 0; + + if (amount == 0) + return 1; + if (amount > 0) + { + 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->contents + from, + sizeof (char *) * (end + amount - from), + amount * sizeof (char *)); + 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->contents[i][0] = '\0'; + current_screen->enable[i] = 1; + } + } + if (amount < 0) + { + 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->contents + from + amount, + sizeof (char *) * (end - from - amount), + (end - from) * sizeof (char *)); + 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->contents[i][0] = '\0'; + current_screen->enable[i] = 1; + } + } + return 1; +} + +/* Rotate a vector of SIZE bytes, by DISTANCE bytes. + DISTANCE may be negative. */ + +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); +} + +/* Like bcopy except never gets confused by overlap. */ + +safe_bcopy (from, to, size) + char *from, *to; + int size; +{ + register char *endf; + register char *endt; + + if (size == 0) + return; + if (from > to) + { + /* If destination is lower in memory, we can go from the beginning. */ + endf = from + size; + while (from != endf) + *to++ = *from++; + return; + } + + /* If destination is higher in memory, we can go backwards from the end. */ + endf = from + size; + endt = to + size; + + do + *--endt = *--endf; + while (endf != from); +} + +/* After updating a window w that isn't the full screen wide, + copy all the columns that w does not occupy + from current_screen to new_screen, + so that update_screen will not change those columns. */ + +preserve_other_columns (w) + struct window *w; +{ + register int vpos; + 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 (current_screen->enable[vpos] && new_screen->enable[vpos]) + { + if (start > 0) + { + int len; + + bcopy (current_screen->contents[vpos], + new_screen->contents[vpos], start); + len = min (start, current_screen->used[vpos]); + if (new_screen->used[vpos] < len) + new_screen->used[vpos] = len; + } + if (current_screen->used[vpos] > end + && new_screen->used[vpos] < current_screen->used[vpos]) + { + while (new_screen->used[vpos] < end) + new_screen->contents[vpos][new_screen->used[vpos]++] = ' '; + bcopy (current_screen->contents[vpos] + end, + new_screen->contents[vpos] + end, + current_screen->used[vpos] - end); + new_screen->used[vpos] = current_screen->used[vpos]; + } + } + } +} + +/* 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 int start = XFASTINT (w->left); + register int bot = XFASTINT (w->top) + XFASTINT (w->height); + + for (vpos = XFASTINT (w->top); vpos < bot; vpos++) + if (new_screen->enable[vpos] && new_screen->used[vpos] >= start) + new_screen->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 (c) + int c; +{ +#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 = cursor_hpos; +#ifndef COMPILER_REGISTER_BUG + register +#endif COMPILER_REGISTER_BUG + int vpos = cursor_vpos; + + /* 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) */ + || cursor_vpos < XFASTINT (w->top) + || cursor_vpos >= XFASTINT (w->top) + XFASTINT (w->height) + + /* Give up if cursor not really at cursor_hpos, cursor_vpos */ + || !display_completed + + /* Give up if w is minibuffer and a message is being displayed there */ + || (EQ (selected_window, minibuf_window) && echo_area_contents)) + return 0; + + current_screen->contents[vpos][hpos] = c; + unchanged_modified = MODIFF; + beg_unchanged = GPT - BEG; + XFASTINT (w->last_point) = point; + XFASTINT (w->last_point_x) = cursor_hpos; + XFASTINT (w->last_modified) = MODIFF; + + reassert_line_highlight (0, cursor_vpos); + output_chars (¤t_screen->contents[vpos][hpos], 1); + fflush (stdout); + ++cursor_hpos; + if (hpos == current_screen->used[vpos]) + { + current_screen->used[vpos] = hpos + 1; + current_screen->contents[vpos][hpos + 1] = 0; + } + return 1; +} + +int +direct_output_forward_char (n) + int n; +{ + register struct window *w = XWINDOW (selected_window); + + /* Avoid losing if cursor is in invisible text off left margin */ + if (XINT (w->hscroll) && cursor_hpos == XFASTINT (w->left)) + return 0; + /* Don't lose if we are in the truncated text at the end. */ + if (cursor_hpos >= XFASTINT (w->left) + XFASTINT (w->width) - 1) + return 0; + /* Don't move past the window edges. */ + if (cursor_hpos + n >= XFASTINT (w->left) + XFASTINT (w->width) - 1) + return 0; + if (cursor_hpos + n <= XFASTINT (w->left)) + return 0; + + cursor_hpos += n; + XFASTINT (w->last_point_x) = cursor_hpos; + XFASTINT (w->last_point) = point; + move_cursor (cursor_vpos, cursor_hpos); + fflush (stdout); + return 1; +} + +/* Update the actual terminal screen based on the data in new_screen. + Value is nonzero if redisplay stopped due to pending input. + FORCE nonzero means do not stop for pending input. */ + +update_screen (force, inhibit_hairy_id) + int force; + int inhibit_hairy_id; +{ + register struct display_line **p; + register struct display_line *l, *lnew; + register int i; + int pause; + int preempt_count = baud_rate / 2400 + 1; + extern input_pending; + + if (screen_height == 0) abort (); /* Some bug zeros some core */ + + if (force_redisplay) + force = 1; + + if (!force) + detect_input_pending (); + if (!force + && ((num_input_chars == debug_preemption_char_count + && debug_preemption_vpos == screen_height - 1) + || input_pending)) + { + pause = screen_height; + goto do_pause; + } + + update_begin (); + + 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; i++) + if (new_screen->enable) + break; + + /* Try doing i/d line, if not yet inhibited. */ + if (!inhibit_hairy_id && i < screen_height) + force |= scrolling (); + + /* Update the individual lines as needed. Do bottom line first. */ + + if (new_screen->enable[screen_height - 1]) + update_line (screen_height - 1); + for (i = 0; i < screen_height - 1 && (force || !input_pending); i++) + { + if (!force && num_input_chars == debug_preemption_char_count + && debug_preemption_vpos == i) + break; + if (new_screen->enable[i]) + { + /* Flush out every so many lines. + Also flush out if likely to have more than 1k buffered + otherwise. I'm told that 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 && !force) + detect_input_pending (); + /* Now update this line. */ + update_line (i); + } + } + pause = (i < screen_height - 1) ? i + 1 : 0; + + /* Now just clean up termcap drivers and set cursor, etc. */ + if (!pause) + { + if (cursor_in_echo_area < 0) + move_cursor (screen_height - 1, 0); + else if (cursor_in_echo_area > 0 + && !current_screen->enable[screen_height - 1]) + move_cursor (screen_height - 1, 0); + else if (cursor_in_echo_area) + move_cursor (screen_height - 1, + min (screen_width - 1, + current_screen->used[screen_height - 1])); + else + move_cursor (cursor_vpos, max (min (cursor_hpos, screen_width - 1), 0)); + } + + update_end (); + + if (termscript) + fflush (termscript); + fflush (stdout); + + /* Here if output is preempted because input is detected. */ + do_pause: + + if (screen_height == 0) abort (); /* Some bug zeros some core */ + display_completed = !pause; + if (pause) + { + preemptions[preemption_index].vpos = pause - 1; + preemptions[preemption_index].keyboard_char_count = num_input_chars; + preemption_index++; + if (preemption_index == N_PREEMPTIONS) + preemption_index = 0; + } + + bzero (new_screen->enable, screen_height); + return pause; +} + +/* Called when about to quit, to check for doing so + at an improper time. */ + +void +quit_error_check () +{ + if (new_screen == 0) + return; + if (new_screen->enable[0]) + abort (); + if (new_screen->enable[screen_height - 1]) + abort (); +} + +/* Decide what insert/delete line to do, and do it */ + +scrolling () +{ + int unchanged_at_top, unchanged_at_bottom; + int window_size; + int changed_lines; + int *old_hash = (int *) alloca (screen_height * sizeof (int)); + int *new_hash = (int *) alloca (screen_height * sizeof (int)); + int *draw_cost = (int *) alloca (screen_height * sizeof (int)); + register int i; + int free_at_end_vpos = screen_height; + + /* 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; + for (i = 0; i < screen_height; 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 (!new_screen->enable[i]) + new_hash[i] = old_hash[i]; + else + new_hash[i] = line_hash_code (new_screen, i); + if (old_hash[i] != new_hash[i]) + { + changed_lines++; + unchanged_at_bottom = screen_height - i - 1; + } + else if (i == unchanged_at_top) + unchanged_at_top++; + /* If line is not changing, its redraw cost is infinite, + since we can't redraw it. */ + if (!new_screen->enable[i]) + draw_cost[i] = INFINITY; + else + draw_cost[i] = line_draw_cost (new_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) + return 1; + + window_size = screen_height - 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 new_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 - unchanged_at_bottom, + old_hash, new_hash, draw_cost))) + return 0; + + scrolling_1 (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; +} + +update_line (vpos) + int vpos; +{ + register unsigned char *obody, *nbody, *op1, *op2, *np1; + int tem; + int osp, nsp, begmatch, endmatch, olen, nlen; + int save; + unsigned char *temp; + + /* Check for highlighting change. */ + if (new_screen->highlight[vpos] + != (current_screen->enable[vpos] && current_screen->highlight[vpos])) + { + change_line_highlight (new_screen->highlight[vpos], vpos, + (current_screen->enable[vpos] + ? current_screen->used[vpos] : 0)); + current_screen->enable[vpos] = 0; + } + else + reassert_line_highlight (new_screen->highlight[vpos], vpos); + + /* ??? */ + if (! current_screen->enable[vpos]) + { + olen = 0; + } + else + { + obody = current_screen->contents[vpos]; + olen = current_screen->used[vpos]; + + /* Check for bugs that clobber obody[-1]. + Such bugs might well clobber more than that, + so we need to find them, not try to ignore them. */ + if (obody[-1] != 0) + abort (); + + if (! current_screen->highlight[vpos]) + { + /* Note obody[-1] is always 0. */ + if (!must_write_spaces) + while (obody[olen - 1] == ' ') + 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 - 1) + obody[olen++] = ' '; + } + } + + /* One way or another, this will enable the line being updated. */ + current_screen->enable[vpos] = 1; + current_screen->used[vpos] = new_screen->used[vpos]; +#if !defined (ALLIANT) || defined (ALLIANT_2800) + current_screen->highlight[vpos] = new_screen->highlight[vpos]; +#else + { + /* Work around for compiler bug in cc on FX/80 which causes + "dispnew.c", line 896: compiler error: no table entry for op OREG. */ + char tmp; + tmp = new_screen->highlight[vpos]; + current_screen->highlight[vpos] = tmp; + } +#endif + + if (!new_screen->enable[vpos]) + { + nlen = 0; + goto just_erase; + } + + nbody = new_screen->contents[vpos]; + nlen = new_screen->used[vpos]; + + /* Pretend trailing spaces are not there at all, + unless for one reason or another we must write all spaces. */ + /* We know that the previous character byte contains 0. */ + if (! new_screen->highlight[vpos]) + { + if (!must_write_spaces) + while (nlen > 0 && nbody[nlen - 1] == ' ') + nlen--; + if (nlen == 0) + goto just_erase; + } + 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 - 1) + nbody[nlen++] = ' '; + } + + /* 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]) + { + /* We found a non-matching char. */ + move_cursor (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. */ + output_chars (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) + { + move_cursor (vpos, nlen); + clear_end_of_line (olen); + } + + /* Exchange contents between current_screen and new_screen. */ + temp = new_screen->contents[vpos]; + new_screen->contents[vpos] = current_screen->contents[vpos]; + current_screen->contents[vpos] = temp; + return; + } + + if (!olen) + { + nsp = (must_write_spaces || new_screen->highlight[vpos]) + ? 0 : count_blanks (nbody); + if (nlen > nsp) + { + move_cursor (vpos, nsp); + output_chars (nbody + nsp, nlen - nsp); + } + + /* Exchange contents between current_screen and new_screen. */ + temp = new_screen->contents[vpos]; + new_screen->contents[vpos] = current_screen->contents[vpos]; + current_screen->contents[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 (!new_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] == ' ') + 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. */ + nbody[nlen] = save; + /* Likewise, make sure that the null after the usable space in obody + always remains a null. */ + 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 && endmatch <= DCICcost[tem]) + endmatch = 0; + + /* nsp - osp is the distance to insert or delete. + begmatch + endmatch is how much we save by doing so. + Is it worth it? */ + + if (begmatch + endmatch > 0 && nsp != osp + && begmatch + endmatch <= DCICcost[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) + { + move_cursor (vpos, nsp); + delete_chars (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) + { + move_cursor (vpos, nlen - endmatch + osp - nsp); + delete_chars (olen + nsp - osp - nlen); + olen = nlen - (nsp - osp); + } + move_cursor (vpos, osp); + insert_chars ((char *)0, nsp - osp); + } + olen += nsp - osp; + + tem = nsp + begmatch + endmatch; + if (nlen != tem || olen != tem) + { + move_cursor (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) + olen = 0; + output_chars (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 && fast_clear_end_of_line && olen > nlen) + { + /* endmatch must be zero, and tem must equal nsp + begmatch */ + output_chars (nbody + tem, nlen - tem - 1); + clear_end_of_line (olen); + olen = 0; /* Don't let it be cleared again later */ + output_chars (nbody + nlen - 1, 1); + } + else + output_chars (nbody + nsp + begmatch, nlen - tem); +#endif + } + else if (nlen > olen) + { + output_chars (nbody + nsp + begmatch, olen - tem); + insert_chars (nbody + nsp + begmatch + olen - tem, nlen - olen); + olen = nlen; + } + else if (olen > nlen) + { + output_chars (nbody + nsp + begmatch, nlen - tem); + delete_chars (olen - nlen); + olen = nlen; + } + } + + just_erase: + /* If any unerased characters remain after the new line, erase them. */ + if (olen > nlen) + { + move_cursor (vpos, nlen); + clear_end_of_line (olen); + } + + /* Exchange contents between current_screen and new_screen. */ + temp = new_screen->contents[vpos]; + new_screen->contents[vpos] = current_screen->contents[vpos]; + current_screen->contents[vpos] = temp; +} + +count_blanks (str) + char *str; +{ + register char *p = str; + while (*str++ == ' '); + return str - p - 1; +} + +count_match (str1, str2) + char *str1, *str2; +{ + register char *p1 = str1; + register char *p2 = str2; + while (*p1++ == *p2++); + return p1 - str1 - 1; +} + +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; +} + +DEFUN ("set-screen-height", Fset_screen_height, Sset_screen_height, 1, 2, 0, + "Tell redisplay that the screen has LINES lines.\n\ +Optional second arg non-nil means that redisplay should use LINES lines\n\ +but that the idea of the actual height of the screen should not be changed.") + (n, pretend) + Lisp_Object n, pretend; +{ + CHECK_NUMBER (n, 0); + change_screen_size (XINT (n), 0, !NULL (pretend), 0, 0); + return Qnil; +} + +DEFUN ("set-screen-width", Fset_screen_width, Sset_screen_width, 1, 2, 0, + "Tell redisplay that the screen has COLS columns.\n\ +Optional second arg non-nil means that redisplay should use COLS columns\n\ +but that the idea of the actual width of the screen should not be changed.") + (n, pretend) + Lisp_Object n, pretend; +{ + CHECK_NUMBER (n, 0); + change_screen_size (0, XINT (n), !NULL (pretend), 0, 0); + return Qnil; +} + +DEFUN ("screen-height", Fscreen_height, Sscreen_height, 0, 0, 0, + "Return number of lines on screen available for display.") + () +{ + return make_number (screen_height); +} + +DEFUN ("screen-width", Fscreen_width, Sscreen_width, 0, 0, 0, + "Return number of columns on screen available for display.") + () +{ + return make_number (screen_width); +} + +#ifdef SIGWINCH +window_change_signal () +{ + int width, height; + extern int errno; + int old_errno = errno; + + get_screen_size (&width, &height); + /* Record the new size, but don't reallocate the data structures now. + Let that be done later outside of the signal handler. */ + change_screen_size (height, width, 0, 1, 0); + 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 change_screen_size should have run before, run it now. */ + while (delayed_size_change) + { + int newwidth = delayed_screen_width; + int newheight = delayed_screen_height; + delayed_size_change = 0; + change_screen_size_1 (newheight, newwidth, 0, 0); + } +} + +/* Change the screen height and/or width. Values may be given as zero to + indicate no change is to take place. + + PRETEND is normally 0; 1 means change used-size only but don't + change the size used for calculations; -1 means don't redisplay. + + If DELAYED, don't change the screen size now; just record the new + size and do the actual change at a more convenient time. The + SIGWINCH handler and the X Windows ConfigureNotify code use this, + because they can both be called at inconvenient times. + + FORCE means finish redisplay now regardless of pending input. + This is effective only is DELAYED is not set. */ + +change_screen_size (newlength, newwidth, pretend, delayed, force) + register int newlength, newwidth, pretend, delayed, force; +{ + /* Don't queue a size change if we won't really do anything. */ + if ((newlength == 0 || newlength == screen_height) + && (newwidth == 0 || newwidth == screen_width)) + return; + /* If we can't deal with the change now, queue it for later. */ + if (delayed) + { + delayed_screen_width = newwidth; + delayed_screen_height = newlength; + delayed_size_change = 1; + } + else + { + delayed_size_change = 0; + change_screen_size_1 (newlength, newwidth, pretend, force); + } +} + +change_screen_size_1 (newlength, newwidth, pretend, force) + register int newlength, newwidth, pretend, force; +{ + if ((newlength == 0 || newlength == screen_height) + && (newwidth == 0 || newwidth == screen_width)) + return; + if (newlength && newlength != screen_height) + { + set_window_height (XWINDOW (minibuf_window)->prev, newlength - 1, 0); + XFASTINT (XWINDOW (minibuf_window)->top) = newlength - 1; + set_window_height (minibuf_window, 1, 0); + screen_height = newlength; + if (pretend <= 0) + ScreenRows = newlength; + set_terminal_window (0); + } + if (newwidth && newwidth != screen_width) + { + set_window_width (XWINDOW (minibuf_window)->prev, newwidth, 0); + set_window_width (minibuf_window, newwidth, 0); + screen_width = newwidth; + if (pretend <= 0) + ScreenCols = newwidth; + } + remake_screen_structures (); + screen_garbaged = 1; + calculate_costs (); + force_redisplay += force; + if (pretend >= 0) + redisplay_preserve_echo_area (); + force_redisplay -= force; +} + +DEFUN ("baud-rate", Fbaud_rate, Sbaud_rate, 0, 0, 0, + "Return the output baud rate of the terminal.") + () +{ + Lisp_Object temp; + XSET (temp, Lisp_Int, baud_rate); + return temp; +} + +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\ +Terminates any keyboard macro currently executing unless an argument\n\ +is given.") + (arg) + Lisp_Object arg; +{ + if (!NULL (arg)) + { + if (noninteractive) + putchar (07); + else + ring_bell (); + fflush (stdout); + } + else + bell (); + return Qnil; +} + +bell () +{ + if (noninteractive) + putchar (07); + else if (!FROM_KBD) /* 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, 1, 0, + "Pause, without updating display, for ARG seconds.") + (n) + Lisp_Object n; +{ + register int t; +#ifndef subprocesses +#ifdef HAVE_TIMEVAL + struct timeval timeout, end_time, garbage1; +#endif /* HAVE_TIMEVAL */ +#endif /* no subprocesses */ + + CHECK_NUMBER (n, 0); + t = XINT (n); + if (t <= 0) + return Qnil; + +#ifdef subprocesses + wait_reading_process_input (t, 0, 0); +#else /* No subprocesses */ + immediate_quit = 1; + QUIT; + +#ifdef VMS + sys_sleep (t); +#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 += t; + + while (1) + { + gettimeofday (&timeout, &garbage1); + + /* In effect, timeout = end_time - timeout. + Break if result would be negative. */ + if (timeval_subtract (&timeout, end_time, timeout)) + 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 (t); +#endif /* HAVE_TIMEVAL */ +#else /* not HAVE_SELECT */ + sleep (t); +#endif /* HAVE_SELECT */ +#endif /* not VMS */ + + immediate_quit = 0; +#endif /* no subprocesses */ + return Qnil; +} + +#ifdef HAVE_TIMEVAL + +/* Subtract the `struct timeval' values X and Y, + storing the result in RESULT. + Return 1 if the difference is negative, otherwise 0. */ + +int +timeval_subtract (result, x, y) + struct timeval *result, x, y; +{ + /* Perform the carry for the later subtraction by updating y. + This is safer because on some systems + the tv_sec member is unsigned. */ + if (x.tv_usec < y.tv_usec) + { + int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1; + y.tv_usec -= 1000000 * nsec; + y.tv_sec += nsec; + } + if (x.tv_usec - y.tv_usec > 1000000) + { + int nsec = (y.tv_usec - x.tv_usec) / 1000000; + y.tv_usec += 1000000 * nsec; + y.tv_sec -= nsec; + } + + /* Compute the time remaining to wait. tv_usec is certainly positive. */ + result->tv_sec = x.tv_sec - y.tv_sec; + result->tv_usec = x.tv_usec - y.tv_usec; + + /* Return indication of whether the result should be considered negative. */ + return x.tv_sec < y.tv_sec; +} +#endif /* HAVE_TIMEVAL */ + +DEFUN ("sit-for", Fsit_for, Ssit_for, 1, 2, 0, + "Perform redisplay, then wait for ARG seconds or until input is available.\n\ +Optional second arg non-nil means don't redisplay.\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, nodisp) + Lisp_Object n, nodisp; +{ +#ifndef subprocesses +#ifdef HAVE_TIMEVAL + struct timeval timeout; +#else + int timeout_sec; +#endif + int waitchannels; +#endif /* no subprocesses */ + + CHECK_NUMBER (n, 0); + + if (detect_input_pending ()) + return Qnil; + + if (EQ (nodisp, Qnil)) + redisplay_preserve_echo_area (); + if (XINT (n) > 0) + { +#ifdef subprocesses +#ifdef SIGIO + gobble_input (); +#endif /* SIGIO */ + wait_reading_process_input (XINT (n), 1, 1); +#else /* not subprocesses */ + immediate_quit = 1; + QUIT; + + waitchannels = 1; +#ifdef VMS + input_wait_timeout (XINT (n)); +#else /* not VMS */ +#ifndef HAVE_TIMEVAL + timeout_sec = XINT (n); + select (1, &waitchannels, 0, 0, &timeout_sec); +#else /* HAVE_TIMEVAL */ + timeout.tv_sec = XINT (n); + timeout.tv_usec = 0; + select (1, &waitchannels, 0, 0, &timeout); +#endif /* HAVE_TIMEVAL */ +#endif /* not VMS */ + + immediate_quit = 0; +#endif /* not subprocesses */ + } + return detect_input_pending () ? Qnil : Qt; +} + +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 Lisp_Object Vxterm; + Vxterm = Qnil; +#endif + + Vwindow_system = Qnil; + meta_key = 0; + inverse_video = 0; + cursor_in_echo_area = 0; + terminal_type = (char *) 0; + + if (!inhibit_window_system) + { +#ifdef HAVE_X_WINDOWS + extern char *alternate_display; + char *disp = (char *) egetenv ("DISPLAY"); + + /* Note KSH likes to provide an empty string as an envvar value. */ + if (alternate_display || (disp && *disp)) + { + x_term_init (); + Vxterm = Qt; + Vwindow_system = intern ("x"); +#ifdef X11 + Vwindow_system_version = make_number (11); +#else + Vwindow_system_version = make_number (10); +#endif + goto term_init_done; + } +#endif /* HAVE_X_WINDOWS */ + ; + } + /* Record we aren't using a window system. */ + inhibit_window_system = 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); + } + term_init (terminal_type); + + term_init_done: + remake_screen_structures (); + calculate_costs (); + +#ifdef SIGWINCH +#ifndef CANNOT_DUMP + if (initialized) +#endif /* CANNOT_DUMP */ + if (inhibit_window_system) + signal (SIGWINCH, window_change_signal); +#endif /* SIGWINCH */ +} + +syms_of_display () +{ + defsubr (&Sopen_termscript); + defsubr (&Sding); + defsubr (&Ssit_for); + defsubr (&Sscreen_height); + defsubr (&Sscreen_width); + defsubr (&Sset_screen_height); + defsubr (&Sset_screen_width); + defsubr (&Ssleep_for); + defsubr (&Sbaud_rate); + defsubr (&Ssend_string_to_terminal); + + DEFVAR_BOOL ("inverse-video", &inverse_video, + "*Non-nil means use inverse-video."); + DEFVAR_BOOL ("visible-bell", &visible_bell, + "*Non-nil means try to flash the screen to represent a bell.\n\ +Note: for X windows, you must use x-set-bell instead."); + DEFVAR_BOOL ("no-redraw-on-reenter", &no_redraw_on_reenter, + "*Non-nil means no need to redraw entire screen after suspending.\n\ +It is up to you to set this variable to inform Emacs."); + 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, + "Version number of the window system Emacs is running under."); + DEFVAR_BOOL ("cursor-in-echo-area", &cursor_in_echo_area, + "Non-nil means put cursor in minibuffer after any message displayed there."); + + /* Initialize `window-system', unless init_display already decided it. */ +#ifdef CANNOT_DUMP + if (noninteractive) +#endif + { + Vwindow_system_version = Qnil; + Vwindow_system = Qnil; + } +} |