/* Updating of data structures for redisplay.
- Copyright (C) 1985, 86, 87, 88, 93, 94, 95, 97, 98, 1999
+ Copyright (C) 1985, 86, 87, 88, 93, 94, 95, 97, 98, 1999, 2000, 2001
Free Software Foundation, Inc.
This file is part of GNU Emacs.
#include "cm.h"
#include "buffer.h"
#include "charset.h"
+#include "keyboard.h"
#include "frame.h"
#include "window.h"
#include "commands.h"
#include "intervals.h"
#include "blockinput.h"
#include "process.h"
-#include "keyboard.h"
/* I don't know why DEC Alpha OSF1 fail to compile this file if we
include the following file. */
#include "w32term.h"
#endif /* HAVE_NTGUI */
+#ifdef macintosh
+#include "macterm.h"
+#endif /* macintosh */
+
/* Include systime.h after xterm.h to avoid double inclusion of time.h. */
#include "systime.h"
#endif
#endif /* not __GNU_LIBRARY__ */
+#if defined(HAVE_TERM_H) && defined (LINUX) && defined (HAVE_LIBNCURSES)
+#include <term.h> /* for tgetent */
+#endif
\f
/* Structure to pass dimensions around. Used for character bounding
boxes, glyph matrix dimensions and alike. */
\f
/* Function prototypes. */
+static struct glyph_matrix *save_current_matrix P_ ((struct frame *));
+static void restore_current_matrix P_ ((struct frame *, struct glyph_matrix *));
+static void fake_current_matrices P_ ((Lisp_Object));
static void redraw_overlapping_rows P_ ((struct window *, int));
static void redraw_overlapped_rows P_ ((struct window *, int));
static int count_blanks P_ ((struct glyph *, int));
static void adjust_glyph_matrix P_ ((struct window *, struct glyph_matrix *,
int, int, struct dim));
static void change_frame_size_1 P_ ((struct frame *, int, int, int, int, int));
-static void swap_glyphs_in_rows P_ ((struct glyph_row *, struct glyph_row *));
static void swap_glyph_pointers P_ ((struct glyph_row *, struct glyph_row *));
+#if GLYPH_DEBUG
static int glyph_row_slice_p P_ ((struct glyph_row *, struct glyph_row *));
+#endif
static void fill_up_frame_row_with_spaces P_ ((struct glyph_row *, int));
static void build_frame_matrix_from_window_tree P_ ((struct glyph_matrix *,
struct window *));
static void clear_window_matrices P_ ((struct window *, int));
static void fill_up_glyph_row_area_with_spaces P_ ((struct glyph_row *, int));
static int scrolling_window P_ ((struct window *, int));
-static int update_window_line P_ ((struct window *, int));
+static int update_window_line P_ ((struct window *, int, int *));
static void update_marginal_area P_ ((struct window *, int, int));
static int update_text_area P_ ((struct window *, int));
static void make_current P_ ((struct glyph_matrix *, struct glyph_matrix *,
static int update_frame_1 P_ ((struct frame *, int, int));
static void set_window_cursor_after_update P_ ((struct window *));
static int row_equal_p P_ ((struct window *, struct glyph_row *,
- struct glyph_row *));
+ struct glyph_row *, int));
static void adjust_frame_glyphs_for_window_redisplay P_ ((struct frame *));
static void adjust_frame_glyphs_for_frame_redisplay P_ ((struct frame *));
static void reverse_rows P_ ((struct glyph_matrix *, int, int));
static void sync_window_with_frame_matrix_rows P_ ((struct window *));
struct window *frame_row_to_window P_ ((struct window *, int));
-
\f
/* Non-zero means don't pause redisplay for pending input. (This is
for debugging and for a future implementation of EDT-like
int cursor_in_echo_area;
-Lisp_Object Qdisplay_table;
+Lisp_Object Qdisplay_table, Qredisplay_dont_pause;
\f
/* The currently selected frame. In a single-frame version, this
#define WINDOW_TO_FRAME_VPOS(W, VPOS) window_to_frame_vpos ((W), (VPOS))
#define WINDOW_TO_FRAME_HPOS(W, HPOS) window_to_frame_hpos ((W), (HPOS))
+/* One element of the ring buffer containing redisplay history
+ information. */
+
+struct redisplay_history
+{
+ char trace[512 + 100];
+};
+
+/* The size of the history buffer. */
+
+#define REDISPLAY_HISTORY_SIZE 30
+
+/* The redisplay history buffer. */
+
+static struct redisplay_history redisplay_history[REDISPLAY_HISTORY_SIZE];
+
+/* Next free entry in redisplay_history. */
+
+static int history_idx;
+
+/* A tick that's incremented each time something is added to the
+ history. */
+
+static unsigned history_tick;
+
+static void add_frame_display_history P_ ((struct frame *, int));
+static void add_window_display_history P_ ((struct window *, char *, int));
+
+
+/* Add to the redisplay history how window W has been displayed.
+ MSG is a trace containing the information how W's glyph matrix
+ has been contructed. PAUSED_P non-zero means that the update
+ has been interrupted for pending input. */
+
+static void
+add_window_display_history (w, msg, paused_p)
+ struct window *w;
+ char *msg;
+ int paused_p;
+{
+ char *buf;
+
+ if (history_idx >= REDISPLAY_HISTORY_SIZE)
+ history_idx = 0;
+ buf = redisplay_history[history_idx].trace;
+ ++history_idx;
+
+ sprintf (buf, "%d: window %p (`%s')%s\n",
+ history_tick++,
+ w,
+ ((BUFFERP (w->buffer)
+ && STRINGP (XBUFFER (w->buffer)->name))
+ ? (char *) XSTRING (XBUFFER (w->buffer)->name)->data
+ : "???"),
+ paused_p ? " ***paused***" : "");
+ strcat (buf, msg);
+}
+
+
+/* Add to the redisplay history that frame F has been displayed.
+ PAUSED_P non-zero means that the update has been interrupted for
+ pending input. */
+
+static void
+add_frame_display_history (f, paused_p)
+ struct frame *f;
+ int paused_p;
+{
+ char *buf;
+
+ if (history_idx >= REDISPLAY_HISTORY_SIZE)
+ history_idx = 0;
+ buf = redisplay_history[history_idx].trace;
+ ++history_idx;
+
+ sprintf (buf, "%d: update frame %p%s",
+ history_tick++,
+ f, paused_p ? " ***paused***" : "");
+}
+
+
+DEFUN ("dump-redisplay-history", Fdump_redisplay_history,
+ Sdump_redisplay_history, 0, 0, "",
+ "Dump redisplay history to stderr.")
+ ()
+{
+ int i;
+
+ for (i = history_idx - 1; i != history_idx; --i)
+ {
+ if (i < 0)
+ i = REDISPLAY_HISTORY_SIZE - 1;
+ fprintf (stderr, "%s\n", redisplay_history[i].trace);
+ }
+
+ return Qnil;
+}
+
+
#else /* GLYPH_DEBUG == 0 */
#define WINDOW_TO_FRAME_VPOS(W, VPOS) ((VPOS) + XFASTINT ((W)->top))
int header_line_changed_p = 0;
int header_line_p = 0;
int left = -1, right = -1;
- int window_x, window_y, window_width, window_height;
+ int window_x, window_y, window_width = -1, window_height;
/* See if W had a top line that has disappeared now, or vice versa. */
if (w)
if (!marginal_areas_changed_p
&& !fonts_changed_p
&& !header_line_changed_p
+ && matrix->window_left_x == XFASTINT (w->left)
&& matrix->window_top_y == XFASTINT (w->top)
&& matrix->window_height == window_height
&& matrix->window_vscroll == w->vscroll
/* Number of rows to be used by MATRIX. */
matrix->nrows = dim.height;
+ xassert (matrix->nrows >= 0);
- /* Mark rows in a current matrix of a window as not having valid
- contents. It's important to not do this for desired matrices.
- When Emacs starts, it may already be building desired matrices
- when this function runs. */
- if (w && matrix == w->current_matrix)
+ if (w)
{
- /* Optimize the case that only the height has changed (C-x 2,
- upper window). Invalidate all rows that are no longer part
- of the window. */
- if (!marginal_areas_changed_p
- && matrix->window_top_y == XFASTINT (w->top)
- && matrix->window_width == window_width)
+ if (matrix == w->current_matrix)
{
- i = 0;
- while (matrix->rows[i].enabled_p
- && (MATRIX_ROW_BOTTOM_Y (matrix->rows + i)
- < matrix->window_height))
- ++i;
-
- /* Window end is invalid, if inside of the rows that
- are invalidated. */
- if (INTEGERP (w->window_end_vpos)
- && XFASTINT (w->window_end_vpos) >= i)
- w->window_end_valid = Qnil;
+ /* Mark rows in a current matrix of a window as not having
+ valid contents. It's important to not do this for
+ desired matrices. When Emacs starts, it may already be
+ building desired matrices when this function runs. */
+ if (window_width < 0)
+ window_width = window_box_width (w, -1);
+
+ /* Optimize the case that only the height has changed (C-x 2,
+ upper window). Invalidate all rows that are no longer part
+ of the window. */
+ if (!marginal_areas_changed_p
+ && !header_line_changed_p
+ && new_rows == 0
+ && dim.width == matrix->matrix_w
+ && matrix->window_left_x == XFASTINT (w->left)
+ && matrix->window_top_y == XFASTINT (w->top)
+ && matrix->window_width == window_width)
+ {
+ /* Find the last row in the window. */
+ for (i = 0; i < matrix->nrows && matrix->rows[i].enabled_p; ++i)
+ if (MATRIX_ROW_BOTTOM_Y (matrix->rows + i) >= window_height)
+ {
+ ++i;
+ break;
+ }
+
+ /* Window end is invalid, if inside of the rows that
+ are invalidated below. */
+ if (INTEGERP (w->window_end_vpos)
+ && XFASTINT (w->window_end_vpos) >= i)
+ w->window_end_valid = Qnil;
- while (i < matrix->nrows)
- matrix->rows[i++].enabled_p = 0;
+ while (i < matrix->nrows)
+ matrix->rows[i++].enabled_p = 0;
+ }
+ else
+ {
+ for (i = 0; i < matrix->nrows; ++i)
+ matrix->rows[i].enabled_p = 0;
+ }
}
- else
+ else if (matrix == w->desired_matrix)
{
+ /* Rows in desired matrices always have to be cleared;
+ redisplay expects this is the case when it runs, so it
+ had better be the case when we adjust matrices between
+ redisplays. */
for (i = 0; i < matrix->nrows; ++i)
matrix->rows[i].enabled_p = 0;
}
}
+
/* Remember last values to be able to optimize frame redraws. */
matrix->matrix_x = x;
was last adjusted. This is used to optimize redisplay above. */
if (w)
{
+ matrix->window_left_x = XFASTINT (w->left);
matrix->window_top_y = XFASTINT (w->top);
matrix->window_height = window_height;
matrix->window_width = window_width;
DELTA_BYTES. */
void
-increment_glyph_matrix_buffer_positions (matrix, start, end, delta,
- delta_bytes)
+increment_matrix_positions (matrix, start, end, delta, delta_bytes)
struct glyph_matrix *matrix;
int start, end, delta, delta_bytes;
{
xassert (start <= end);
for (; start < end; ++start)
- increment_glyph_row_buffer_positions (matrix->rows + start,
- delta, delta_bytes);
+ increment_row_positions (matrix->rows + start, delta, delta_bytes);
}
struct glyph_row *row = &matrix->rows[start];
row->y += dy;
+ row->visible_height = row->height;
if (row->y < min_y)
- row->visible_height = row->height - (min_y - row->y);
- else if (row->y + row->height > max_y)
- row->visible_height = row->height - (row->y + row->height - max_y);
- else
- row->visible_height = row->height;
+ row->visible_height -= min_y - row->y;
+ if (row->y + row->height > max_y)
+ row->visible_height -= row->y + row->height - max_y;
}
}
changes in the glyph_row structure, i.e. addition or removal of
structure members. */
+static struct glyph_row null_row;
+
void
clear_glyph_row (row)
struct glyph_row *row;
{
struct glyph *p[1 + LAST_AREA];
- static struct glyph_row null_row;
/* Save pointers. */
p[LEFT_MARGIN_AREA] = row->glyphs[LEFT_MARGIN_AREA];
row->glyphs[TEXT_AREA] = p[TEXT_AREA];
row->glyphs[RIGHT_MARGIN_AREA] = p[RIGHT_MARGIN_AREA];
row->glyphs[LAST_AREA] = p[LAST_AREA];
+
+#if 0 /* At some point, some bit-fields of struct glyph were not set,
+ which made glyphs unequal when compared with GLYPH_EQUAL_P.
+ Redisplay outputs such glyphs, and flickering effects were
+ the result. This also depended on the contents of memory
+ returned by xmalloc. If flickering happens again, activate
+ the code below If the flickering is gone with that, chances
+ are that the flickering has the same reason as here. */
+ bzero (p[0], (char *) p[LAST_AREA] - (char *) p[0]);
+#endif
}
row->y = y;
row->ascent = row->phys_ascent = 0;
row->height = row->phys_height = CANON_Y_UNIT (XFRAME (w->frame));
-
+ row->visible_height = row->height;
+
if (row->y < min_y)
- row->visible_height = row->height - (min_y - row->y);
- else if (row->y + row->height > max_y)
- row->visible_height = row->height - (row->y + row->height - max_y);
- else
- row->visible_height = row->height;
+ row->visible_height -= min_y - row->y;
+ if (row->y + row->height > max_y)
+ row->visible_height -= row->y + row->height - max_y;
row->enabled_p = 1;
}
ends. */
void
-increment_glyph_row_buffer_positions (row, delta, delta_bytes)
+increment_row_positions (row, delta, delta_bytes)
struct glyph_row *row;
int delta, delta_bytes;
{
}
+#if 0
/* Swap glyphs between two glyph rows A and B. This exchanges glyph
contents, i.e. glyph structure contents are exchanged between A and
B without changing glyph pointers in A and B. */
}
}
+#endif /* 0 */
/* Exchange pointers to glyph memory between glyph rows A and B. */
from->used[area] * sizeof (struct glyph));
/* Increment buffer positions in TO by DELTA. */
- increment_glyph_row_buffer_positions (to, delta, delta_bytes);
+ increment_row_positions (to, delta, delta_bytes);
}
is non-zero if the glyph memory of WINDOW_ROW is part of the glyph
memory of FRAME_ROW. */
+#if GLYPH_DEBUG
+
static int
glyph_row_slice_p (window_row, frame_row)
struct glyph_row *window_row, *frame_row;
&& window_glyph_start < frame_glyph_end);
}
+#endif /* GLYPH_DEBUG */
+
+#if 0
/* Find the row in the window glyph matrix WINDOW_MATRIX being a slice
of ROW in the frame matrix FRAME_MATRIX. Value is null if no row
return i < window_matrix->nrows ? window_matrix->rows + i : 0;
}
+#endif /* 0 */
/* Prepare ROW for display. Desired rows are cleared lazily,
i.e. they are only marked as to be cleared by setting their
if (!must_write_spaces)
{
/* Skip from the end over trailing spaces. */
- while (end != beg && CHAR_GLYPH_SPACE_P (*end))
+ while (end > beg && CHAR_GLYPH_SPACE_P (*(end - 1)))
--end;
/* All blank line. */
/* Test two glyph rows A and B for equality. Value is non-zero if A
and B have equal contents. W is the window to which the glyphs
rows A and B belong. It is needed here to test for partial row
- visibility. */
+ visibility. MOUSE_FACE_P non-zero means compare the mouse_face_p
+ flags of A and B, too. */
static INLINE int
-row_equal_p (w, a, b)
+row_equal_p (w, a, b, mouse_face_p)
struct window *w;
struct glyph_row *a, *b;
+ int mouse_face_p;
{
if (a == b)
return 1;
struct glyph *a_glyph, *b_glyph, *a_end;
int area;
+ if (mouse_face_p && a->mouse_face_p != b->mouse_face_p)
+ return 0;
+
/* Compare glyphs. */
for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area)
{
/* Width and height MUST be chosen so that there are no
holes in the frame matrix. */
- dim.width = w->width;
- dim.height = w->height;
+ dim.width = XINT (w->width);
+ dim.height = XINT (w->height);
/* Will matrix be re-allocated? */
if (x != w->desired_matrix->matrix_x
}
+/* In the window tree with root W, build current matrices of leaf
+ windows from the frame's current matrix. */
+
+static void
+fake_current_matrices (window)
+ Lisp_Object window;
+{
+ struct window *w;
+
+ for (; !NILP (window); window = w->next)
+ {
+ w = XWINDOW (window);
+
+ if (!NILP (w->hchild))
+ fake_current_matrices (w->hchild);
+ else if (!NILP (w->vchild))
+ fake_current_matrices (w->vchild);
+ else
+ {
+ int i;
+ struct frame *f = XFRAME (w->frame);
+ struct glyph_matrix *m = w->current_matrix;
+ struct glyph_matrix *fm = f->current_matrix;
+
+ xassert (m->matrix_h == XFASTINT (w->height));
+ xassert (m->matrix_w == XFASTINT (w->width));
+
+ for (i = 0; i < m->matrix_h; ++i)
+ {
+ struct glyph_row *r = m->rows + i;
+ struct glyph_row *fr = fm->rows + i + XFASTINT (w->top);
+
+ xassert (r->glyphs[TEXT_AREA] >= fr->glyphs[TEXT_AREA]
+ && r->glyphs[LAST_AREA] <= fr->glyphs[LAST_AREA]);
+
+ r->enabled_p = fr->enabled_p;
+ if (r->enabled_p)
+ {
+ r->used[LEFT_MARGIN_AREA] = m->left_margin_glyphs;
+ r->used[RIGHT_MARGIN_AREA] = m->right_margin_glyphs;
+ r->used[TEXT_AREA] = (m->matrix_w
+ - r->used[LEFT_MARGIN_AREA]
+ - r->used[RIGHT_MARGIN_AREA]);
+ r->mode_line_p = 0;
+ r->inverse_p = fr->inverse_p;
+ }
+ }
+ }
+ }
+}
+
+
+/* Save away the contents of frame F's current frame matrix. Value is
+ a glyph matrix holding the contents of F's current frame matrix. '*/
+
+static struct glyph_matrix *
+save_current_matrix (f)
+ struct frame *f;
+{
+ int i;
+ struct glyph_matrix *saved;
+
+ saved = (struct glyph_matrix *) xmalloc (sizeof *saved);
+ bzero (saved, sizeof *saved);
+ saved->nrows = f->current_matrix->nrows;
+ saved->rows = (struct glyph_row *) xmalloc (saved->nrows
+ * sizeof *saved->rows);
+ bzero (saved->rows, saved->nrows * sizeof *saved->rows);
+
+ for (i = 0; i < saved->nrows; ++i)
+ {
+ struct glyph_row *from = f->current_matrix->rows + i;
+ struct glyph_row *to = saved->rows + i;
+ size_t nbytes = from->used[TEXT_AREA] * sizeof (struct glyph);
+ to->glyphs[TEXT_AREA] = (struct glyph *) xmalloc (nbytes);
+ bcopy (from->glyphs[TEXT_AREA], to->glyphs[TEXT_AREA], nbytes);
+ to->used[TEXT_AREA] = from->used[TEXT_AREA];
+ }
+
+ return saved;
+}
+
+
+/* Restore the contents of frame F's current frame matrix from SAVED,
+ and free memory associated with SAVED. */
+
+static void
+restore_current_matrix (f, saved)
+ struct frame *f;
+ struct glyph_matrix *saved;
+{
+ int i;
+
+ for (i = 0; i < saved->nrows; ++i)
+ {
+ struct glyph_row *from = saved->rows + i;
+ struct glyph_row *to = f->current_matrix->rows + i;
+ size_t nbytes = from->used[TEXT_AREA] * sizeof (struct glyph);
+ bcopy (from->glyphs[TEXT_AREA], to->glyphs[TEXT_AREA], nbytes);
+ to->used[TEXT_AREA] = from->used[TEXT_AREA];
+ xfree (from->glyphs[TEXT_AREA]);
+ }
+
+ xfree (saved->rows);
+ xfree (saved);
+}
+
+
+
/* Allocate/reallocate glyph matrices of a single frame F for
frame-based redisplay. */
f->desired_matrix = new_glyph_matrix (f->desired_pool);
f->current_matrix = new_glyph_matrix (f->current_pool);
}
-
+
/* Compute window glyph matrices. (This takes the mini-buffer
window into account). The result is the size of the frame glyph
matrix needed. The variable window_change_flags is set to a bit
xassert (matrix_dim.width == FRAME_WIDTH (f)
&& matrix_dim.height == FRAME_HEIGHT (f));
- /* Resize frame matrices. */
- adjust_glyph_matrix (NULL, f->desired_matrix, 0, 0, matrix_dim);
- adjust_glyph_matrix (NULL, f->current_matrix, 0, 0, matrix_dim);
-
- /* Since location and size of sub-matrices within the pool may
- have changed, and current matrices don't have meaningful
- contents anymore, mark the frame garbaged. */
- SET_FRAME_GARBAGED (f);
+ /* Pointers to glyph memory in glyph rows are exchanged during
+ the update phase of redisplay, which means in general that a
+ frame's current matrix consists of pointers into both the
+ desired and current glyph pool of the frame. Adjusting a
+ matrix sets the frame matrix up so that pointers are all into
+ the same pool. If we want to preserve glyph contents of the
+ current matrix over a call to adjust_glyph_matrix, we must
+ make a copy of the current glyphs, and restore the current
+ matrix' contents from that copy. */
+ if (display_completed
+ && !FRAME_GARBAGED_P (f)
+ && matrix_dim.width == f->current_matrix->matrix_w
+ && matrix_dim.height == f->current_matrix->matrix_h)
+ {
+ struct glyph_matrix *copy = save_current_matrix (f);
+ adjust_glyph_matrix (NULL, f->desired_matrix, 0, 0, matrix_dim);
+ adjust_glyph_matrix (NULL, f->current_matrix, 0, 0, matrix_dim);
+ restore_current_matrix (f, copy);
+ fake_current_matrices (FRAME_ROOT_WINDOW (f));
+ }
+ else
+ {
+ adjust_glyph_matrix (NULL, f->desired_matrix, 0, 0, matrix_dim);
+ adjust_glyph_matrix (NULL, f->current_matrix, 0, 0, matrix_dim);
+ SET_FRAME_GARBAGED (f);
+ }
}
}
{
struct glyph_row *frame_row = frame_matrix->rows + frame_y;
struct glyph_row *window_row = window_matrix->rows + window_y;
+ int current_row_p = window_matrix == w->current_matrix;
/* Fill up the frame row with spaces up to the left margin of the
window row. */
/* Fill up areas in the window matrix row with spaces. */
fill_up_glyph_row_with_spaces (window_row);
+
+ /* If only part of W's desired matrix has been built, and
+ window_row wasn't displayed, use the corresponding current
+ row instead. */
+ if (window_matrix == w->desired_matrix
+ && !window_row->enabled_p)
+ {
+ window_row = w->current_matrix->rows + window_y;
+ current_row_p = 1;
+ }
- if (window_matrix == w->current_matrix)
+ if (current_row_p)
{
- /* We have to copy W's current matrix. Copy window
- row to frame row. */
+ /* Copy window row to frame row. */
bcopy (window_row->glyphs[0],
frame_row->glyphs[TEXT_AREA] + window_matrix->matrix_x,
window_matrix->matrix_w * sizeof (struct glyph));
}
else
{
- /* Copy W's desired matrix. */
-
+ xassert (window_row->enabled_p);
+
+ /* Only when a desired row has been displayed, we want
+ the corresponding frame row to be updated. */
+ frame_row->enabled_p = 1;
+
/* Maybe insert a vertical border between horizontally adjacent
windows. */
if (right_border_glyph)
SET_CHAR_GLYPH_FROM_GLYPH (*border, right_border_glyph);
}
-#if 0 /* This shouldn't be necessary. Let's check it. */
- /* Due to hooks installed, it normally doesn't happen that
- window rows and frame rows of the same matrix are out of
- sync, i.e. have a different understanding of where to
- find glyphs for the row. The following is a safety-belt
- that doesn't cost much and makes absolutely sure that
- window and frame matrices are in sync. */
- if (!glyph_row_slice_p (window_row, frame_row))
- {
- /* Find the row in the window being a slice. There
- should exist one from program logic. */
- struct glyph_row *slice_row
- = find_glyph_row_slice (window_matrix, frame_matrix, frame_y);
- xassert (slice_row != 0);
-
- /* Exchange glyphs between both window rows. */
- swap_glyphs_in_rows (window_row, slice_row);
-
- /* Exchange pointers between both rows. */
- swap_glyph_pointers (window_row, slice_row);
- }
-#endif
-
/* Window row window_y must be a slice of frame row
frame_y. */
xassert (glyph_row_slice_p (window_row, frame_row));
#if GLYPH_DEBUG
strcpy (w->current_matrix->method, w->desired_matrix->method);
+ add_window_display_history (w, w->current_matrix->method, 0);
#endif
}
frame_row->used[TEXT_AREA]
= window_matrix->matrix_x + window_matrix->matrix_w;
- /* Or in flags. */
- frame_row->enabled_p |= window_row->enabled_p;
+ /* Or in other flags. */
frame_row->inverse_p |= window_row->inverse_p;
/* Next row. */
{
struct glyph_row *current_row = MATRIX_ROW (current_matrix, row);
struct glyph_row *desired_row = MATRIX_ROW (desired_matrix, row);
+ int mouse_face_p = current_row->mouse_face_p;
/* Do current_row = desired_row. This exchanges glyph pointers
between both rows, and does a structure assignment otherwise. */
/* Enable current_row to mark it as valid. */
current_row->enabled_p = 1;
+ current_row->mouse_face_p = mouse_face_p;
/* If we are called on frame matrices, perform analogous operations
for window matrices. */
|| !display_completed
/* Give up if buffer appears in two places. */
|| buffer_shared > 1
- /* Give up if w is mini-buffer and a message is being displayed there */
- || (MINI_WINDOW_P (w) && !NILP (echo_area_buffer[0]))
+ /* Give up if currently displaying a message instead of the
+ minibuffer contents. */
+ || (EQ (selected_window, minibuf_window)
+ && EQ (minibuf_window, echo_area_window))
/* Give up for hscrolled mini-buffer because display of the prompt
is handled specially there (see display_line). */
|| (MINI_WINDOW_P (w) && XFASTINT (w->hscroll))
|| g == '\r'
/* Give up if unable to display the cursor in the window. */
|| w->cursor.vpos < 0
+ /* Give up if we are showing a message or just cleared the message
+ because we might need to resize the echo area window. */
+ || !NILP (echo_area_buffer[0])
+ || !NILP (echo_area_buffer[1])
|| (glyph_row = MATRIX_ROW (w->current_matrix, w->cursor.vpos),
/* Can't do it in a continued line because continuation
lines would change. */
|| (!window_redisplay_p && !WINDOW_FULL_WIDTH_P (w)))
return 0;
+ /* If we can't insert glyphs, we can use this method only
+ at the end of a line. */
+ if (!char_ins_del_ok)
+ if (PT != ZV && FETCH_BYTE (PT_BYTE) != '\n')
+ return 0;
+
/* Set up a display iterator structure for W. Glyphs will be
produced in scratch_glyph_row. Current position is W's cursor
position. */
clear_glyph_row (&scratch_glyph_row);
SET_TEXT_POS (pos, PT, PT_BYTE);
- DEC_TEXT_POS (pos);
+ DEC_TEXT_POS (pos, !NILP (current_buffer->enable_multibyte_characters));
init_iterator (&it, w, CHARPOS (pos), BYTEPOS (pos), &scratch_glyph_row,
DEFAULT_FACE_ID);
glyph_row = MATRIX_ROW (w->current_matrix, w->cursor.vpos);
+ if (glyph_row->mouse_face_p)
+ return 0;
/* Give up if highlighting trailing whitespace and we have trailing
whitespace in glyph_row. We would have to remove the trailing
it.current_y = w->cursor.y;
it.end_charpos = PT;
it.stop_charpos = min (PT, it.stop_charpos);
+ it.stop_charpos = max (IT_CHARPOS (it), it.stop_charpos);
/* More than one display element may be returned for PT - 1 if
(i) it's a control character which is translated into `\003' or
delta += 1;
delta_bytes += it.len;
- set_iterator_to_next (&it);
+ set_iterator_to_next (&it, 1);
}
/* Give up if we hit the right edge of the window. We would have
{
if (it2.c == '\t')
return 0;
- set_iterator_to_next (&it2);
+ set_iterator_to_next (&it2, 1);
}
/* Number of new glyphs produced. */
}
/* Adjust positions in lines following the one we are in. */
- increment_glyph_matrix_buffer_positions (w->current_matrix,
- w->cursor.vpos + 1,
- w->current_matrix->nrows,
- delta, delta_bytes);
+ increment_matrix_positions (w->current_matrix,
+ w->cursor.vpos + 1,
+ w->current_matrix->nrows,
+ delta, delta_bytes);
glyph_row->contains_overlapping_glyphs_p
|= it.glyph_row->contains_overlapping_glyphs_p;
implemented for X frames. The implementation uses updated_window
and updated_row. */
updated_row = glyph_row;
+ updated_area = TEXT_AREA;
update_begin (f);
if (rif)
{
rif->update_window_begin_hook (w);
- if (glyphs == end - n)
+ if (glyphs == end - n
+ /* In front of a space added by append_space. */
+ || (glyphs == end - n - 1
+ && (end - n)->charpos <= 0))
rif->write_glyphs (glyphs, n);
else
rif->insert_glyphs (glyphs, n);
}
if (rif)
- rif->update_window_end_hook (w, 1);
+ rif->update_window_end_hook (w, 1, 0);
update_end (f);
updated_row = NULL;
fflush (stdout);
struct glyph_row *row;
/* Give up if point moved out of or into a composition. */
- if (check_point_in_composition (current_buffer, w->last_point,
+ if (check_point_in_composition (current_buffer, XINT (w->last_point),
current_buffer, PT))
return 0;
if (!NILP (echo_area_buffer[0]) || !NILP (echo_area_buffer[1]))
return 0;
+ /* Give up if currently displaying a message instead of the
+ minibuffer contents. */
+ if (XWINDOW (minibuf_window) == w
+ && EQ (minibuf_window, echo_area_window))
+ return 0;
+
/* Give up if we don't know where the cursor is. */
if (w->cursor.vpos < 0)
return 0;
row = MATRIX_ROW (w->current_matrix, w->cursor.vpos);
+ /* Give up if PT is outside of the last known cursor row. */
if (PT <= MATRIX_ROW_START_BYTEPOS (row)
|| PT >= MATRIX_ROW_END_BYTEPOS (row))
return 0;
set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0);
+
w->last_cursor = w->cursor;
XSETFASTINT (w->last_point, PT);
tem = f->current_tool_bar_string;
f->current_tool_bar_string = f->desired_tool_bar_string;
f->desired_tool_bar_string = tem;
- f->n_current_tool_bar_items = f->n_desired_tool_bar_items;
-
- /* Swap tool-bar items. We swap because we want to
- reuse vectors. */
- tem = f->current_tool_bar_items;
- f->current_tool_bar_items = f->desired_tool_bar_items;
- f->desired_tool_bar_items = tem;
}
}
/* Update windows. */
paused_p = update_window_tree (root_window, force_p);
update_end (f);
- display_completed = !paused_p;
-
- /* The flush is a performance bottleneck under X. */
-#if 0
+
+#if 0 /* This flush is a performance bottleneck under X,
+ and it doesn't seem to be necessary anyway. */
rif->flush_display (f);
#endif
}
frame matrix we operate. */
set_frame_matrix_frame (f);
- /* Build F's desired matrix from window matrices. For windows
- whose must_be_updated_p flag is set, desired matrices are
- made part of the desired frame matrix. For other windows,
- the current matrix is copied. */
+ /* Build F's desired matrix from window matrices. */
build_frame_matrix (f);
- /* Do the update on the frame desired matrix. */
+ /* Update the display */
+ update_begin (f);
paused_p = update_frame_1 (f, force_p, inhibit_hairy_id_p);
-
+ update_end (f);
+
+ if (termscript)
+ fflush (termscript);
+ fflush (stdout);
+
/* Check window matrices for lost pointers. */
- IF_DEBUG (check_window_matrix_pointers (root_window));
+#if GLYPH_DEBUG
+ check_window_matrix_pointers (root_window);
+ add_frame_display_history (f, paused_p);
+#endif
}
/* Reset flags indicating that a window should be updated. */
set_window_update_flags (root_window, 0);
+
+ display_completed = !paused_p;
return paused_p;
}
struct window *w;
int yb;
{
- int i, bottom_y;
- struct glyph_row *row;
+ int i;
/* If rows overlapping others have been changed, the rows being
overlapped have to be redrawn. This won't draw lines that have
current rows is 0. */
for (i = 0; i < w->current_matrix->nrows; ++i)
{
- row = w->current_matrix->rows + i;
+ struct glyph_row *row = w->current_matrix->rows + i;
if (!row->enabled_p)
break;
row->overlapped_p = 0;
}
- bottom_y = MATRIX_ROW_BOTTOM_Y (row);
- if (bottom_y >= yb)
+ if (MATRIX_ROW_BOTTOM_Y (row) >= yb)
break;
}
}
}
+#ifdef GLYPH_DEBUG
+
+/* Check that no row in the current matrix of window W is enabled
+ which is below what's displayed in the window. */
+
+void
+check_current_matrix_flags (w)
+ struct window *w;
+{
+ int last_seen_p = 0;
+ int i, yb = window_text_bottom_y (w);
+
+ for (i = 0; i < w->current_matrix->nrows - 1; ++i)
+ {
+ struct glyph_row *row = MATRIX_ROW (w->current_matrix, i);
+ if (!last_seen_p && MATRIX_ROW_BOTTOM_Y (row) >= yb)
+ last_seen_p = 1;
+ else if (last_seen_p && row->enabled_p)
+ abort ();
+ }
+}
+
+#endif /* GLYPH_DEBUG */
+
+
/* Update display of window W. FORCE_P non-zero means that we should
not stop when detecting pending input. */
int paused_p;
int preempt_count = baud_rate / 2400 + 1;
extern int input_pending;
+ extern Lisp_Object do_mouse_tracking;
#if GLYPH_DEBUG
struct frame *f = XFRAME (WINDOW_FRAME (w));
extern struct frame *updating_frame;
/* If forced to complete the update, or if no input is pending, do
the update. */
- if (force_p || !input_pending)
+ if (force_p || !input_pending || !NILP (do_mouse_tracking))
{
struct glyph_row *row, *end;
struct glyph_row *mode_line_row;
- struct glyph_row *header_line_row = NULL;
- int yb, changed_p = 0;
+ struct glyph_row *header_line_row;
+ int yb, changed_p = 0, mouse_face_overwritten_p = 0, n_updated;
rif->update_window_begin_hook (w);
yb = window_text_bottom_y (w);
Adjust y-positions of other rows by the top line height. */
row = desired_matrix->rows;
end = row + desired_matrix->nrows - 1;
+
if (row->mode_line_p)
- header_line_row = row++;
+ {
+ header_line_row = row;
+ ++row;
+ }
+ else
+ header_line_row = NULL;
/* Update the mode line, if necessary. */
mode_line_row = MATRIX_MODE_LINE_ROW (desired_matrix);
{
mode_line_row->y = yb;
update_window_line (w, MATRIX_ROW_VPOS (mode_line_row,
- desired_matrix));
+ desired_matrix),
+ &mouse_face_overwritten_p);
changed_p = 1;
}
while (row < end && !row->enabled_p)
++row;
- /* Try reusing part of the display by inserting/deleting lines. */
+ /* Try reusing part of the display by copying. */
if (row < end && !desired_matrix->no_scrolling_p)
{
int rc = scrolling_window (w, header_line_row != NULL);
goto set_cursor;
}
else if (rc > 0)
+ /* We've scrolled the display. */
force_p = 1;
changed_p = 1;
}
if (header_line_row && header_line_row->enabled_p)
{
header_line_row->y = 0;
- update_window_line (w, 0);
+ update_window_line (w, 0, &mouse_face_overwritten_p);
changed_p = 1;
}
/* Update the rest of the lines. */
- for (; row < end && (force_p || !input_pending); ++row)
- if (row->enabled_p
- /* A row can be completely invisible in case a desired
- matrix was built with a vscroll and then
- make_cursor_line_fully_visible shifts the matrix. */
- && row->visible_height > 0)
+ for (n_updated = 0; row < end && (force_p || !input_pending); ++row)
+ if (row->enabled_p)
{
int vpos = MATRIX_ROW_VPOS (row, desired_matrix);
int i;
detect_input_pending. If it's done too often,
scrolling large windows with repeated scroll-up
commands will too quickly pause redisplay. */
- if (!force_p && vpos % preempt_count == 0)
+ if (!force_p && ++n_updated % preempt_count == 0)
detect_input_pending ();
- changed_p |= update_window_line (w, vpos);
+ changed_p |= update_window_line (w, vpos,
+ &mouse_face_overwritten_p);
/* Mark all rows below the last visible one in the current
matrix as invalid. This is necessary because of
set_cursor:
/* Fix the appearance of overlapping(overlapped rows. */
- if (rif->fix_overlapping_area
- && !w->pseudo_window_p
- && changed_p
- && !paused_p)
- {
- redraw_overlapped_rows (w, yb);
- redraw_overlapping_rows (w, yb);
- }
-
if (!paused_p && !w->pseudo_window_p)
{
+ if (changed_p && rif->fix_overlapping_area)
+ {
+ redraw_overlapped_rows (w, yb);
+ redraw_overlapping_rows (w, yb);
+ }
+
/* Make cursor visible at cursor position of W. */
set_window_cursor_after_update (w);
-#if 0
- /* Check that current matrix invariants are satisfied. This
- is for debugging only. See the comment around
- check_matrix_invariants. */
+#if 0 /* Check that current matrix invariants are satisfied. This is
+ for debugging only. See the comment of check_matrix_invariants. */
IF_DEBUG (check_matrix_invariants (w));
#endif
}
strcpy (w->current_matrix->method, w->desired_matrix->method);
#endif
- /* End of update of window W. */
- rif->update_window_end_hook (w, 1);
-
+ /* End the update of window W. Don't set the cursor if we
+ paused updating the display because in this case,
+ set_window_cursor_after_update hasn't been called, and
+ output_cursor doesn't contain the cursor location. */
+ rif->update_window_end_hook (w, !paused_p, mouse_face_overwritten_p);
}
else
paused_p = 1;
+#if GLYPH_DEBUG
+ /* check_current_matrix_flags (w); */
+ add_window_display_history (w, w->current_matrix->method, paused_p);
+#endif
+
clear_glyph_matrix (desired_matrix);
return paused_p;
|| desired_row->phys_height != current_row->phys_height
|| desired_row->visible_height != current_row->visible_height
|| current_row->overlapped_p
+ || current_row->mouse_face_p
|| current_row->x != desired_row->x)
{
rif->cursor_to (vpos, 0, desired_row->y, desired_row->x);
int stop, i, x;
struct glyph *current_glyph = current_row->glyphs[TEXT_AREA];
struct glyph *desired_glyph = desired_row->glyphs[TEXT_AREA];
+ int overlapping_glyphs_p = current_row->contains_overlapping_glyphs_p;
+ int desired_stop_pos = desired_row->used[TEXT_AREA];
/* If the desired row extends its face to the text area end,
make sure we write at least one glyph, so that the face
extension actually takes place. */
- int desired_stop_pos = (desired_row->used[TEXT_AREA]
- - (MATRIX_ROW_EXTENDS_FACE_P (desired_row)
- ? 1 : 0));
+ if (MATRIX_ROW_EXTENDS_FACE_P (desired_row))
+ --desired_stop_pos;
stop = min (current_row->used[TEXT_AREA], desired_stop_pos);
i = 0;
x = desired_row->x;
-
+
+ /* Loop over glyphs that current and desired row may have
+ in common. */
while (i < stop)
{
+ int can_skip_p = 1;
+
/* Skip over glyphs that both rows have in common. These
- don't have to be written. */
- while (i < stop
- && GLYPH_EQUAL_P (desired_glyph, current_glyph))
- {
- x += desired_glyph->pixel_width;
- ++desired_glyph, ++current_glyph, ++i;
- }
-
- /* Consider the case that the current row contains "xxx ppp
- ggg" in italic Courier font, and the desired row is "xxx
- ggg". The character `p' has lbearing, `g' has not. The
- loop above will stop in front of the first `p' in the
- current row. If we would start writing glyphs there, we
- wouldn't erase the lbearing of the `p'. The rest of the
- lbearing problem is then taken care of by x_draw_glyphs. */
- if (current_row->contains_overlapping_glyphs_p
- && i > 0
- && i < current_row->used[TEXT_AREA]
- && current_row->used[TEXT_AREA] != desired_row->used[TEXT_AREA])
+ don't have to be written. We can't skip if the last
+ current glyph overlaps the glyph to its right. For
+ example, consider a current row of `if ' with the `f' in
+ Courier bold so that it overlaps the ` ' to its right.
+ If the desired row is ` ', we would skip over the space
+ after the `if' and there would remain a pixel from the
+ `f' on the screen. */
+ if (overlapping_glyphs_p && i > 0)
{
+ struct glyph *glyph = ¤t_row->glyphs[TEXT_AREA][i - 1];
int left, right;
- rif->get_glyph_overhangs (current_glyph, XFRAME (w->frame),
+
+ rif->get_glyph_overhangs (glyph, XFRAME (w->frame),
&left, &right);
- while (left > 0 && i > 0)
+ can_skip_p = right == 0;
+ }
+
+ if (can_skip_p)
+ {
+ while (i < stop
+ && GLYPH_EQUAL_P (desired_glyph, current_glyph))
+ {
+ x += desired_glyph->pixel_width;
+ ++desired_glyph, ++current_glyph, ++i;
+ }
+
+ /* Consider the case that the current row contains "xxx
+ ppp ggg" in italic Courier font, and the desired row
+ is "xxx ggg". The character `p' has lbearing, `g'
+ has not. The loop above will stop in front of the
+ first `p' in the current row. If we would start
+ writing glyphs there, we wouldn't erase the lbearing
+ of the `p'. The rest of the lbearing problem is then
+ taken care of by x_draw_glyphs. */
+ if (overlapping_glyphs_p
+ && i > 0
+ && i < current_row->used[TEXT_AREA]
+ && (current_row->used[TEXT_AREA]
+ != desired_row->used[TEXT_AREA]))
{
- --i, --desired_glyph, --current_glyph;
- x -= desired_glyph->pixel_width;
- left -= desired_glyph->pixel_width;
+ int left, right;
+
+ rif->get_glyph_overhangs (current_glyph, XFRAME (w->frame),
+ &left, &right);
+ while (left > 0 && i > 0)
+ {
+ --i, --desired_glyph, --current_glyph;
+ x -= desired_glyph->pixel_width;
+ left -= desired_glyph->pixel_width;
+ }
}
}
int start_x = x, start_hpos = i;
struct glyph *start = desired_glyph;
int current_x = x;
-
+ int skip_first_p = !can_skip_p;
+
/* Find the next glyph that's equal again. */
while (i < stop
- && !GLYPH_EQUAL_P (desired_glyph, current_glyph)
+ && (skip_first_p
+ || !GLYPH_EQUAL_P (desired_glyph, current_glyph))
&& x == current_x)
{
x += desired_glyph->pixel_width;
current_x += current_glyph->pixel_width;
++desired_glyph, ++current_glyph, ++i;
+ skip_first_p = 0;
}
if (i == start_hpos || x != current_x)
desired_glyph = start;
break;
}
-
+
rif->cursor_to (vpos, start_hpos, desired_row->y, start_x);
rif->write_glyphs (start, i - start_hpos);
changed_p = 1;
changed. */
static int
-update_window_line (w, vpos)
+update_window_line (w, vpos, mouse_face_overwritten_p)
struct window *w;
- int vpos;
+ int vpos, *mouse_face_overwritten_p;
{
struct glyph_row *current_row = MATRIX_ROW (w->current_matrix, vpos);
struct glyph_row *desired_row = MATRIX_ROW (w->desired_matrix, vpos);
int changed_p = 0;
- xassert (desired_row->enabled_p);
-
/* Set the row being updated. This is important to let xterm.c
know what line height values are in effect. */
updated_row = desired_row;
- /* Update display of the left margin area, if there is one. */
- if (!desired_row->full_width_p
- && !NILP (w->left_margin_width))
+ /* A row can be completely invisible in case a desired matrix was
+ built with a vscroll and then make_cursor_line_fully_visible shifts
+ the matrix. Make sure to make such rows current anyway, since
+ we need the correct y-position, for example, in the current matrix. */
+ if (desired_row->mode_line_p
+ || desired_row->visible_height > 0)
{
- update_marginal_area (w, LEFT_MARGIN_AREA, vpos);
- changed_p = 1;
- }
-
- /* Update the display of the text area. */
- changed_p |= update_text_area (w, vpos);
-
- /* Update display of the right margin area, if there is one. */
- if (!desired_row->full_width_p
- && !NILP (w->right_margin_width))
- {
- changed_p = 1;
- update_marginal_area (w, RIGHT_MARGIN_AREA, vpos);
+ xassert (desired_row->enabled_p);
+
+ /* Update display of the left margin area, if there is one. */
+ if (!desired_row->full_width_p
+ && !NILP (w->left_margin_width))
+ {
+ changed_p = 1;
+ update_marginal_area (w, LEFT_MARGIN_AREA, vpos);
+ }
+
+ /* Update the display of the text area. */
+ if (update_text_area (w, vpos))
+ {
+ changed_p = 1;
+ if (current_row->mouse_face_p)
+ *mouse_face_overwritten_p = 1;
+ }
+
+ /* Update display of the right margin area, if there is one. */
+ if (!desired_row->full_width_p
+ && !NILP (w->right_margin_width))
+ {
+ changed_p = 1;
+ update_marginal_area (w, RIGHT_MARGIN_AREA, vpos);
+ }
+
+ /* Draw truncation marks etc. */
+ if (!current_row->enabled_p
+ || desired_row->y != current_row->y
+ || desired_row->visible_height != current_row->visible_height
+ || desired_row->overlay_arrow_p != current_row->overlay_arrow_p
+ || desired_row->truncated_on_left_p != current_row->truncated_on_left_p
+ || desired_row->truncated_on_right_p != current_row->truncated_on_right_p
+ || desired_row->continued_p != current_row->continued_p
+ || desired_row->mode_line_p != current_row->mode_line_p
+ || (desired_row->indicate_empty_line_p
+ != current_row->indicate_empty_line_p)
+ || (MATRIX_ROW_CONTINUATION_LINE_P (desired_row)
+ != MATRIX_ROW_CONTINUATION_LINE_P (current_row)))
+ rif->after_update_window_line_hook (desired_row);
}
- /* Draw truncation marks etc. */
- if (!current_row->enabled_p
- || desired_row->y != current_row->y
- || desired_row->visible_height != current_row->visible_height
- || desired_row->overlay_arrow_p != current_row->overlay_arrow_p
- || desired_row->truncated_on_left_p != current_row->truncated_on_left_p
- || desired_row->truncated_on_right_p != current_row->truncated_on_right_p
- || desired_row->continued_p != current_row->continued_p
- || desired_row->mode_line_p != current_row->mode_line_p
- || (desired_row->indicate_empty_line_p
- != current_row->indicate_empty_line_p)
- || (MATRIX_ROW_CONTINUATION_LINE_P (desired_row)
- != MATRIX_ROW_CONTINUATION_LINE_P (current_row)))
- rif->after_update_window_line_hook (desired_row);
-
/* Update current_row from desired_row. */
make_current (w->desired_matrix, w->current_matrix, vpos);
updated_row = NULL;
int yb = window_text_bottom_y (w);
last_row = NULL;
- for (row = MATRIX_ROW (w->current_matrix, 0);
- row->enabled_p;
- ++row)
+ row = w->current_matrix->rows;
+ while (row->enabled_p
+ && (last_row == NULL
+ || MATRIX_ROW_BOTTOM_Y (row) <= yb))
{
if (row->used[TEXT_AREA]
&& row->glyphs[TEXT_AREA][0].charpos >= 0)
last_row = row;
-
- if (MATRIX_ROW_BOTTOM_Y (row) >= yb)
- break;
+ ++row;
}
if (last_row)
{
- struct glyph *start = row->glyphs[TEXT_AREA];
- struct glyph *last = start + row->used[TEXT_AREA] - 1;
+ struct glyph *start = last_row->glyphs[TEXT_AREA];
+ struct glyph *last = start + last_row->used[TEXT_AREA] - 1;
while (last > start && last->charpos < 0)
--last;
}
+/* Set WINDOW->must_be_updated_p to ON_P for all windows in the window
+ tree rooted at W. */
+
+void
+set_window_update_flags (w, on_p)
+ struct window *w;
+ int on_p;
+{
+ while (w)
+ {
+ if (!NILP (w->hchild))
+ set_window_update_flags (XWINDOW (w->hchild), on_p);
+ else if (!NILP (w->vchild))
+ set_window_update_flags (XWINDOW (w->vchild), on_p);
+ else
+ w->must_be_updated_p = on_p;
+
+ w = NILP (w->next) ? 0 : XWINDOW (w->next);
+ }
+}
+
+
+\f
+/***********************************************************************
+ Window-Based Scrolling
+ ***********************************************************************/
+
+/* Structure describing rows in scrolling_window. */
+
+struct row_entry
+{
+ /* Number of occurrences of this row in desired and current matrix. */
+ int old_uses, new_uses;
+
+ /* Vpos of row in new matrix. */
+ int new_line_number;
+
+ /* Bucket index of this row_entry in the hash table row_table. */
+ int bucket;
+
+ /* The row described by this entry. */
+ struct glyph_row *row;
+
+ /* Hash collision chain. */
+ struct row_entry *next;
+};
+
+/* A pool to allocate row_entry structures from, and the size of the
+ pool. The pool is reallocated in scrolling_window when we find
+ that we need a larger one. */
+
+static struct row_entry *row_entry_pool;
+static int row_entry_pool_size;
+
+/* Index of next free entry in row_entry_pool. */
+
+static int row_entry_idx;
+
+/* The hash table used during scrolling, and the table's size. This
+ table is used to quickly identify equal rows in the desired and
+ current matrix. */
+
+static struct row_entry **row_table;
+static int row_table_size;
+
+/* Vectors of pointers to row_entry structures belonging to the
+ current and desired matrix, and the size of the vectors. */
+
+static struct row_entry **old_lines, **new_lines;
+static int old_lines_size, new_lines_size;
+
+/* A pool to allocate run structures from, and its size. */
+
+static struct run *run_pool;
+static int runs_size;
+
+/* A vector of runs of lines found during scrolling. */
+
+static struct run **runs;
+
+static struct row_entry *add_row_entry P_ ((struct window *,
+ struct glyph_row *));
+
+
+/* Add glyph row ROW to the scrolling hash table during the scrolling
+ of window W. */
+
+static INLINE struct row_entry *
+add_row_entry (w, row)
+ struct window *w;
+ struct glyph_row *row;
+{
+ struct row_entry *entry;
+ int i = row->hash % row_table_size;
+
+ entry = row_table[i];
+ while (entry && !row_equal_p (w, entry->row, row, 1))
+ entry = entry->next;
+
+ if (entry == NULL)
+ {
+ entry = row_entry_pool + row_entry_idx++;
+ entry->row = row;
+ entry->old_uses = entry->new_uses = 0;
+ entry->new_line_number = 0;
+ entry->bucket = i;
+ entry->next = row_table[i];
+ row_table[i] = entry;
+ }
+
+ return entry;
+}
+
+
/* Try to reuse part of the current display of W by scrolling lines.
HEADER_LINE_P non-zero means W has a top mode line.
struct window *w;
int header_line_p;
{
- struct symbol
- {
- /* Number of occurrences of this line in old and new matrix. */
- short old_uses, new_uses;
-
- /* Vpos of line in new matrix. */
- short new_line_number;
-
- /* The line itself. */
- struct glyph_row *row;
-
- /* Hash collision chain. */
- struct symbol *next;
- };
-
- int SYMBOL_TABLE_SIZE = 101;
- struct symbol **table;
- struct symbol **old_line_syms, **new_line_syms;
- int i, j, first_old, first_new, last_old, last_new;
- struct symbol *sym;
- struct run **runs;
- int nruns;
struct glyph_matrix *desired_matrix = w->desired_matrix;
struct glyph_matrix *current_matrix = w->current_matrix;
int yb = window_text_bottom_y (w);
+ int i, j, first_old, first_new, last_old, last_new;
+ int nruns, nbytes, n, run_idx;
+ struct row_entry *entry;
/* Skip over rows equal at the start. */
- i = header_line_p ? 1 : 0;
- while (i < current_matrix->nrows - 1
- && MATRIX_ROW_ENABLED_P (current_matrix, i)
- && MATRIX_ROW_ENABLED_P (desired_matrix, i)
- && MATRIX_ROW_BOTTOM_Y (MATRIX_ROW (desired_matrix, i)) < yb
- && MATRIX_ROW_BOTTOM_Y (MATRIX_ROW (current_matrix, i)) < yb
- && row_equal_p (w,
- MATRIX_ROW (desired_matrix, i),
- MATRIX_ROW (current_matrix, i)))
+ for (i = header_line_p ? 1 : 0; i < current_matrix->nrows - 1; ++i)
{
- assign_row (MATRIX_ROW (current_matrix, i),
- MATRIX_ROW (desired_matrix, i));
- MATRIX_ROW (desired_matrix, i)->enabled_p = 0;
- ++i;
+ struct glyph_row *d = MATRIX_ROW (desired_matrix, i);
+ struct glyph_row *c = MATRIX_ROW (current_matrix, i);
+
+ if (c->enabled_p
+ && d->enabled_p
+ && c->y == d->y
+ && MATRIX_ROW_BOTTOM_Y (c) <= yb
+ && MATRIX_ROW_BOTTOM_Y (d) <= yb
+ && row_equal_p (w, c, d, 1))
+ {
+ assign_row (c, d);
+ d->enabled_p = 0;
+ }
+ else
+ break;
}
/* Give up if some rows in the desired matrix are not enabled. */
i = first_new + 1;
while (i < desired_matrix->nrows - 1
&& MATRIX_ROW (desired_matrix, i)->enabled_p
- && MATRIX_ROW_BOTTOM_Y (MATRIX_ROW (desired_matrix, i)) < yb)
+ && MATRIX_ROW_BOTTOM_Y (MATRIX_ROW (desired_matrix, i)) <= yb)
++i;
if (!MATRIX_ROW (desired_matrix, i)->enabled_p)
we plan to reuse part of the display even if other parts are
disabled. */
i = first_old + 1;
- while (i < current_matrix->nrows - 1
- && MATRIX_ROW_BOTTOM_Y (MATRIX_ROW (current_matrix, i)) < yb)
- ++i;
+ while (i < current_matrix->nrows - 1)
+ {
+ int bottom = MATRIX_ROW_BOTTOM_Y (MATRIX_ROW (current_matrix, i));
+ if (bottom <= yb)
+ ++i;
+ if (bottom >= yb)
+ break;
+ }
+
last_old = i;
/* Skip over rows equal at the bottom. */
== MATRIX_ROW (desired_matrix, j - 1)->y)
&& row_equal_p (w,
MATRIX_ROW (desired_matrix, i - 1),
- MATRIX_ROW (current_matrix, j - 1)))
+ MATRIX_ROW (current_matrix, j - 1), 1))
--i, --j;
last_new = i;
last_old = j;
if (last_new == first_new)
return 0;
- /* Allocate a hash table in which all rows will be inserted. */
- table = (struct symbol **) alloca (SYMBOL_TABLE_SIZE * sizeof *table);
- bzero (table, SYMBOL_TABLE_SIZE * sizeof *table);
-
- /* For each row in the current matrix, record the symbol belonging
- to the row in OLD_LINE_SYMS. */
- old_line_syms = (struct symbol **) alloca (current_matrix->nrows
- * sizeof *old_line_syms);
- new_line_syms = (struct symbol **) alloca (desired_matrix->nrows
- * sizeof *new_line_syms);
-
-#define ADDSYM(ROW) \
- do \
- { \
- struct glyph_row *row_ = (ROW); \
- int i_ = row_->hash % SYMBOL_TABLE_SIZE; \
- sym = table[i_]; \
- while (sym && !row_equal_p (w, sym->row, row_)) \
- sym = sym->next; \
- if (sym == NULL) \
- { \
- sym = (struct symbol *) alloca (sizeof *sym); \
- sym->row = row_; \
- sym->old_uses = sym->new_uses = 0; \
- sym->next = table[i_]; \
- table[i_] = sym; \
- } \
- } \
- while (0)
-
- /* Add current rows to the symbol table. */
+ /* Reallocate vectors, tables etc. if necessary. */
+
+ if (current_matrix->nrows > old_lines_size)
+ {
+ old_lines_size = current_matrix->nrows;
+ nbytes = old_lines_size * sizeof *old_lines;
+ old_lines = (struct row_entry **) xrealloc (old_lines, nbytes);
+ }
+
+ if (desired_matrix->nrows > new_lines_size)
+ {
+ new_lines_size = desired_matrix->nrows;
+ nbytes = new_lines_size * sizeof *new_lines;
+ new_lines = (struct row_entry **) xrealloc (new_lines, nbytes);
+ }
+
+ n = desired_matrix->nrows + current_matrix->nrows;
+ if (3 * n > row_table_size)
+ {
+ row_table_size = next_almost_prime (3 * n);
+ nbytes = row_table_size * sizeof *row_table;
+ row_table = (struct row_entry **) xrealloc (row_table, nbytes);
+ bzero (row_table, nbytes);
+ }
+
+ if (n > row_entry_pool_size)
+ {
+ row_entry_pool_size = n;
+ nbytes = row_entry_pool_size * sizeof *row_entry_pool;
+ row_entry_pool = (struct row_entry *) xrealloc (row_entry_pool, nbytes);
+ }
+
+ if (desired_matrix->nrows > runs_size)
+ {
+ runs_size = desired_matrix->nrows;
+ nbytes = runs_size * sizeof *runs;
+ runs = (struct run **) xrealloc (runs, nbytes);
+ nbytes = runs_size * sizeof *run_pool;
+ run_pool = (struct run *) xrealloc (run_pool, nbytes);
+ }
+
+ nruns = run_idx = 0;
+ row_entry_idx = 0;
+
+ /* Add rows from the current and desired matrix to the hash table
+ row_hash_table to be able to find equal ones quickly. */
+
for (i = first_old; i < last_old; ++i)
{
if (MATRIX_ROW (current_matrix, i)->enabled_p)
{
- ADDSYM (MATRIX_ROW (current_matrix, i));
- old_line_syms[i] = sym;
- ++sym->old_uses;
+ entry = add_row_entry (w, MATRIX_ROW (current_matrix, i));
+ old_lines[i] = entry;
+ ++entry->old_uses;
}
else
- old_line_syms[i] = NULL;
+ old_lines[i] = NULL;
}
- /* Add desired rows to the symbol table. */
for (i = first_new; i < last_new; ++i)
{
xassert (MATRIX_ROW_ENABLED_P (desired_matrix, i));
- ADDSYM (MATRIX_ROW (desired_matrix, i));
- ++sym->new_uses;
- new_line_syms[i] = sym;
- sym->new_line_number = i;
+ entry = add_row_entry (w, MATRIX_ROW (desired_matrix, i));
+ ++entry->new_uses;
+ entry->new_line_number = i;
+ new_lines[i] = entry;
}
-#undef ADDSYM
-
- /* Record in runs which moves were found, ordered by pixel
- height of copied areas. */
- nruns = 0;
- runs = (struct run **) alloca (desired_matrix->nrows * sizeof *runs);
-
/* Identify moves based on lines that are unique and equal
in both matrices. */
for (i = first_old; i < last_old;)
- if (old_line_syms[i]
- && old_line_syms[i]->old_uses == 1
- && old_line_syms[i]->new_uses == 1)
+ if (old_lines[i]
+ && old_lines[i]->old_uses == 1
+ && old_lines[i]->new_uses == 1)
{
int j, k;
- int new_line = old_line_syms[i]->new_line_number;
- struct run *run = (struct run *) alloca (sizeof *run);
+ int new_line = old_lines[i]->new_line_number;
+ struct run *run = run_pool + run_idx++;
/* Record move. */
run->current_vpos = i;
k = new_line - 1;
while (j > first_old
&& k > first_new
- && old_line_syms[j] == new_line_syms[k])
+ && old_lines[j] == new_lines[k])
{
int h = MATRIX_ROW (current_matrix, j)->height;
--run->current_vpos;
k = new_line + 1;
while (j < last_old
&& k < last_new
- && old_line_syms[j] == new_line_syms[k])
+ && old_lines[j] == new_lines[k])
{
int h = MATRIX_ROW (current_matrix, j)->height;
++run->nrows;
case. */
for (j = 0; j < nruns && runs[j]->height > run->height; ++j)
;
- for (k = nruns; k >= j; --k)
+ for (k = nruns; k > j; --k)
runs[k] = runs[k - 1];
runs[j] = run;
++nruns;
{
struct glyph_row *from, *to;
int to_overlapped_p;
-
+
to = MATRIX_ROW (current_matrix, r->desired_vpos + j);
- to_overlapped_p = to->overlapped_p;
from = MATRIX_ROW (desired_matrix, r->desired_vpos + j);
+ to_overlapped_p = to->overlapped_p;
assign_row (to, from);
to->enabled_p = 1, from->enabled_p = 0;
to->overlapped_p = to_overlapped_p;
}
}
+ /* Clear the hash table, for the next time. */
+ for (i = 0; i < row_entry_idx; ++i)
+ row_table[row_entry_pool[i].bucket] = NULL;
+
/* Value is non-zero to indicate that we scrolled the display. */
return 1;
}
-/* Set WINDOW->must_be_updated_p TO ON_P for all windows WINDOW in the
- window tree rooted at W. */
-
-void
-set_window_update_flags (w, on_p)
- struct window *w;
- int on_p;
-{
- while (w)
- {
- if (!NILP (w->hchild))
- set_window_update_flags (XWINDOW (w->hchild), on_p);
- else if (!NILP (w->vchild))
- set_window_update_flags (XWINDOW (w->vchild), on_p);
- else
- w->must_be_updated_p = on_p;
-
- w = NILP (w->next) ? 0 : XWINDOW (w->next);
- }
-}
-
-
\f
/************************************************************************
Frame-Based Updates
if (preempt_count <= 0)
preempt_count = 1;
- detect_input_pending ();
- if (input_pending && !force_p)
+ if (redisplay_dont_pause)
+ force_p = 1;
+ else if (!force_p && detect_input_pending ())
{
pause = 1;
goto do_pause;
}
- update_begin (f);
-
/* If we cannot insert/delete lines, it's no use trying it. */
if (!line_ins_del_ok)
inhibit_id_p = 1;
/* We have only one cursor on terminal frames. Use it to
display the cursor of the selected window. */
struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f));
- if (w->cursor.vpos >= 0)
+ if (w->cursor.vpos >= 0
+ /* The cursor vpos may be temporarily out of bounds
+ in the following situation: There is one window,
+ with the cursor in the lower half of it. The window
+ is split, and a message causes a redisplay before
+ a new cursor position has been computed. */
+ && w->cursor.vpos < XFASTINT (w->height))
{
int x = WINDOW_TO_FRAME_HPOS (w, w->cursor.hpos);
int y = WINDOW_TO_FRAME_VPOS (w, w->cursor.vpos);
}
}
- update_end (f);
-
- if (termscript)
- fflush (termscript);
- fflush (stdout);
-
do_pause:
- display_completed = !pause;
clear_desired_matrices (f);
return pause;
}
int len;
{
int i;
-
+
for (i = 0; i < len; ++i)
if (!CHAR_GLYPH_SPACE_P (r[i]))
break;
/* Perform a frame-based update on line VPOS in frame FRAME. */
static void
-update_frame_line (frame, vpos)
- register struct frame *frame;
+update_frame_line (f, vpos)
+ struct frame *f;
int vpos;
{
struct glyph *obody, *nbody, *op1, *op2, *np1, *nend;
int tem;
int osp, nsp, begmatch, endmatch, olen, nlen;
- struct glyph_matrix *current_matrix = frame->current_matrix;
- struct glyph_matrix *desired_matrix = frame->desired_matrix;
+ struct glyph_matrix *current_matrix = f->current_matrix;
+ struct glyph_matrix *desired_matrix = f->desired_matrix;
struct glyph_row *current_row = MATRIX_ROW (current_matrix, vpos);
struct glyph_row *desired_row = MATRIX_ROW (desired_matrix, vpos);
int must_write_whole_line_p;
+ int write_spaces_p = must_write_spaces;
+ int colored_spaces_p = (FACE_FROM_ID (f, DEFAULT_FACE_ID)->background
+ != FACE_TTY_DEFAULT_BG_COLOR);
+
+ if (colored_spaces_p)
+ write_spaces_p = 1;
if (desired_row->inverse_p
!= (current_row->enabled_p && current_row->inverse_p))
obody = MATRIX_ROW_GLYPH_START (current_matrix, vpos);
olen = current_row->used[TEXT_AREA];
- if (! current_row->inverse_p)
+ if (!current_row->inverse_p)
{
/* Ignore trailing spaces, if we can. */
- if (!must_write_spaces)
+ if (!write_spaces_p)
while (olen > 0 && CHAR_GLYPH_SPACE_P (obody[olen-1]))
olen--;
}
/* For an inverse-video line, make sure it's filled with
spaces all the way to the frame edge so that the reverse
video extends all the way across. */
- while (olen < FRAME_WIDTH (frame) - 1)
+ while (olen < FRAME_WIDTH (f) - 1)
obody[olen++] = space_glyph;
}
}
if (must_write_whole_line_p)
{
/* Ignore spaces at the end, if we can. */
- if (!must_write_spaces)
+ if (!write_spaces_p)
while (nlen > 0 && CHAR_GLYPH_SPACE_P (nbody[nlen - 1]))
--nlen;
/* Don't call clear_end_of_line if we already wrote the whole
line. The cursor will not be at the right margin in that
case but in the line below. */
- if (nlen < FRAME_WINDOW_WIDTH (frame))
+ if (nlen < FRAME_WINDOW_WIDTH (f))
{
cursor_to (vpos, nlen);
- clear_end_of_line (FRAME_WINDOW_WIDTH (frame));
+ clear_end_of_line (FRAME_WINDOW_WIDTH (f));
}
-
+ else
+ /* Make sure we are in the right row, otherwise cursor movement
+ with cmgoto might use `ch' in the wrong row. */
+ cursor_to (vpos, 0);
+
make_current (desired_matrix, current_matrix, vpos);
return;
}
unless for one reason or another we must write all spaces. */
if (!desired_row->inverse_p)
{
- if (!must_write_spaces)
+ if (!write_spaces_p)
while (nlen > 0 && CHAR_GLYPH_SPACE_P (nbody[nlen - 1]))
nlen--;
}
/* For an inverse-video line, give it extra trailing spaces all
the way to the frame edge so that the reverse video extends
all the way across. */
- while (nlen < FRAME_WIDTH (frame) - 1)
+ while (nlen < FRAME_WIDTH (f) - 1)
nbody[nlen++] = space_glyph;
}
{
/* If current line is blank, skip over initial spaces, if
possible, and write the rest. */
- if (must_write_spaces || desired_row->inverse_p)
+ if (write_spaces_p || desired_row->inverse_p)
nsp = 0;
else
nsp = count_blanks (nbody, nlen);
/* Compute number of leading blanks in old and new contents. */
osp = count_blanks (obody, olen);
- nsp = desired_row->inverse_p ? 0 : count_blanks (nbody, nlen);
+ nsp = (desired_row->inverse_p || colored_spaces_p
+ ? 0
+ : count_blanks (nbody, nlen));
/* Compute number of matching chars starting with first non-blank. */
begmatch = count_match (obody + osp, obody + olen,
/* 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)
+ if (!write_spaces_p && osp + begmatch == olen)
{
np1 = nbody + nsp;
while (np1 + begmatch < nend && CHAR_GLYPH_SPACE_P (np1[begmatch]))
tem = (nlen - nsp) - (olen - osp);
if (endmatch && tem
- && (!char_ins_del_ok || endmatch <= char_ins_del_cost (frame)[tem]))
+ && (!char_ins_del_ok || endmatch <= char_ins_del_cost (f)[tem]))
endmatch = 0;
/* nsp - osp is the distance to insert or delete.
if (nsp != osp
&& (!char_ins_del_ok
- || begmatch + endmatch <= char_ins_del_cost (frame)[nsp - osp]))
+ || begmatch + endmatch <= char_ins_del_cost (f)[nsp - osp]))
{
begmatch = 0;
endmatch = 0;
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 == FRAME_WINDOW_WIDTH (frame))
+ /* If new text being written reaches right margin, there is
+ no need to do clear-to-eol at the end of this function
+ (and it would not be safe, since cursor is not going to
+ be "at the margin" after the text is done). */
+ if (nlen == FRAME_WINDOW_WIDTH (f))
olen = 0;
- write_glyphs (nbody + nsp + begmatch, nlen - tem);
+
+ /* Function write_glyphs is prepared to do nothing
+ if passed a length <= 0. Check it here to avoid
+ unnecessary cursor movement. */
+ if (nlen - tem > 0)
+ {
+ cursor_to (vpos, nsp + begmatch);
+ write_glyphs (nbody + nsp + begmatch, nlen - tem);
+ }
}
else if (nlen > olen)
{
int out = olen - tem; /* Columns to be overwritten originally. */
int del;
+ cursor_to (vpos, nsp + begmatch);
+
/* Calculate columns we can actually overwrite. */
- while (CHAR_GLYPH_PADDING_P (nbody[nsp + begmatch + out])) out--;
+ while (CHAR_GLYPH_PADDING_P (nbody[nsp + begmatch + out]))
+ out--;
write_glyphs (nbody + nsp + begmatch, out);
+
/* If we left columns to be overwritten, we must delete them. */
del = olen - tem - out;
- if (del > 0) delete_glyphs (del);
+ if (del > 0)
+ delete_glyphs (del);
+
/* At last, we insert columns not yet written out. */
insert_glyphs (nbody + nsp + begmatch + out, nlen - olen + del);
olen = nlen;
}
else if (olen > nlen)
{
+ cursor_to (vpos, nsp + begmatch);
write_glyphs (nbody + nsp + begmatch, nlen - tem);
delete_glyphs (olen - nlen);
olen = nlen;
X/Y Position -> Buffer Position
***********************************************************************/
-/* Return the character position of the character at window relative
- pixel position (*X, *Y). *X and *Y are adjusted to character
- boundaries. */
+/* Determine what's under window-relative pixel position (*X, *Y).
+ Return in *OBJECT the object (string or buffer) that's there.
+ Return in *POS the position in that object. Adjust *X and *Y
+ to character boundaries. */
-int
-buffer_posn_from_coords (w, x, y)
+void
+buffer_posn_from_coords (w, x, y, object, pos)
struct window *w;
int *x, *y;
+ Lisp_Object *object;
+ struct display_pos *pos;
{
struct it it;
struct buffer *old_current_buffer = current_buffer;
*x = it.current_x - it.first_visible_x + left_area_width;
*y = it.current_y;
current_buffer = old_current_buffer;
- return IT_CHARPOS (it);
+
+ *object = STRINGP (it.string) ? it.string : w->buffer;
+ *pos = it.current;
}
Lisp_Object
mode_line_string (w, x, y, mode_line_p, charpos)
struct window *w;
- int x, y;
+ int x, y, mode_line_p;
int *charpos;
{
struct glyph_row *row;
int signalnum; /* some compilers complain in signal calls. */
{
int width, height;
+#ifndef USE_CRT_DLL
extern int errno;
+#endif
int old_errno = errno;
get_frame_size (&width, &height);
return Qnil;
if (initial_display)
- redisplay_preserve_echo_area ();
+ redisplay_preserve_echo_area (2);
if (sec == 0 && usec == 0)
return Qt;
}
#endif /* HAVE_NTGUI */
+#ifdef macintosh
+ if (!inhibit_window_system)
+ {
+ Vwindow_system = intern ("mac");
+ Vwindow_system_version = make_number (1);
+ adjust_frame_glyphs_initially ();
+ return;
+ }
+#endif /* macintosh */
+
/* If no window system has been specified, try to use the terminal. */
if (! isatty (0))
{
defsubr (&Sinternal_show_cursor);
defsubr (&Sinternal_show_cursor_p);
+#if GLYPH_DEBUG
+ defsubr (&Sdump_redisplay_history);
+#endif
+
frame_and_buffer_state = Fmake_vector (make_number (20), Qlambda);
staticpro (&frame_and_buffer_state);
Qdisplay_table = intern ("display-table");
staticpro (&Qdisplay_table);
+ Qredisplay_dont_pause = intern ("redisplay-dont-pause");
+ staticpro (&Qredisplay_dont_pause);
DEFVAR_INT ("baud-rate", &baud_rate,
"*The output baud rate of the terminal.\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 `face-id').");
+ nil: this glyph mod 524288 is the code of a character to output,\n\
+ and this glyph / 524288 is the face number (see `face-id') to use\n\
+ while outputting it.");
Vglyph_table = Qnil;
DEFVAR_LISP ("standard-display-table", &Vstandard_display_table,