diff options
author | Stefan Monnier <monnier@iro.umontreal.ca> | 2022-09-25 16:15:16 -0400 |
---|---|---|
committer | Stefan Monnier <monnier@iro.umontreal.ca> | 2022-09-25 16:15:16 -0400 |
commit | 650c20f1ca4e07591a727e1cfcc74b3363d15985 (patch) | |
tree | 85d11f6437cde22f410c25e0e5f71a3131ebd07d /src/dispnew.c | |
parent | 8869332684c2302b5ba1ead4568bbc7ba1c0183e (diff) | |
parent | 4b85ae6a24380fb67a3315eaec9233f17a872473 (diff) | |
download | emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.tar.gz emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.tar.bz2 emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.zip |
Merge 'master' into noverlay
Diffstat (limited to 'src/dispnew.c')
-rw-r--r-- | src/dispnew.c | 971 |
1 files changed, 748 insertions, 223 deletions
diff --git a/src/dispnew.c b/src/dispnew.c index ad59704a168..b786f0f1ba4 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -1,6 +1,6 @@ /* Updating of data structures for redisplay. -Copyright (C) 1985-1988, 1993-1995, 1997-2017 Free Software Foundation, +Copyright (C) 1985-1988, 1993-1995, 1997-2022 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -41,6 +41,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include "systime.h" #include "tparam.h" #include "xwidget.h" +#include "pdumper.h" #ifdef HAVE_WINDOW_SYSTEM #include TERM_HEADER @@ -66,7 +67,7 @@ struct dim /* Function prototypes. */ -static void update_frame_line (struct frame *, int); +static void update_frame_line (struct frame *, int, bool); static int required_matrix_height (struct window *); static int required_matrix_width (struct window *); static void increment_row_positions (struct glyph_row *, ptrdiff_t, ptrdiff_t); @@ -78,7 +79,7 @@ static void adjust_decode_mode_spec_buffer (struct frame *); static void fill_up_glyph_row_with_spaces (struct glyph_row *); static void clear_window_matrices (struct window *, bool); static void fill_up_glyph_row_area_with_spaces (struct glyph_row *, int); -static int scrolling_window (struct window *, bool); +static int scrolling_window (struct window *, int); static bool update_window_line (struct window *, int, bool *); static void mirror_make_current (struct window *, int); #ifdef GLYPH_DEBUG @@ -88,7 +89,7 @@ static void check_matrix_pointers (struct glyph_matrix *, static void mirror_line_dance (struct window *, int, int, int *, char *); static bool update_window_tree (struct window *, bool); static bool update_window (struct window *, bool); -static bool update_frame_1 (struct frame *, bool, bool, bool); +static bool update_frame_1 (struct frame *, bool, bool, bool, bool); static bool scrolling (struct frame *); static void set_window_cursor_after_update (struct window *); static void adjust_frame_glyphs_for_window_redisplay (struct frame *); @@ -155,7 +156,7 @@ static int history_idx; /* A tick that's incremented each time something is added to the history. */ -static uprintmax_t history_tick; +static uintmax_t history_tick; /* Add to the redisplay history how window W has been displayed. MSG is a trace containing the information how W's glyph matrix @@ -174,7 +175,7 @@ add_window_display_history (struct window *w, const char *msg, bool paused_p) ++history_idx; snprintf (buf, sizeof redisplay_history[0].trace, - "%"pMu": window %p (%s)%s\n%s", + "%"PRIuMAX": window %p (%s)%s\n%s", history_tick++, ptr, ((BUFFERP (w->contents) @@ -201,7 +202,7 @@ add_frame_display_history (struct frame *f, bool paused_p) buf = redisplay_history[history_idx].trace; ++history_idx; - sprintf (buf, "%"pMu": update frame %p%s", + sprintf (buf, "%"PRIuMAX": update frame %p%s", history_tick++, ptr, paused_p ? " ***paused***" : ""); } @@ -233,9 +234,7 @@ DEFUN ("dump-redisplay-history", Fdump_redisplay_history, #endif /* GLYPH_DEBUG */ -#if (defined PROFILING \ - && (defined __FreeBSD__ || defined GNU_LINUX || defined __MINGW32__) \ - && !HAVE___EXECUTABLE_START) +#if defined PROFILING && !HAVE___EXECUTABLE_START /* This function comes first in the Emacs executable and is used only to estimate the text start for profiling. */ void @@ -366,6 +365,8 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y int i; int new_rows; bool marginal_areas_changed_p = 0; + bool tab_line_changed_p = 0; + bool tab_line_p = 0; bool header_line_changed_p = 0; bool header_line_p = 0; int left = -1, right = -1; @@ -377,9 +378,13 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y { window_box (w, ANY_AREA, 0, 0, &window_width, &window_height); + tab_line_p = window_wants_tab_line (w); + tab_line_changed_p = tab_line_p != matrix->tab_line_p; + header_line_p = window_wants_header_line (w); header_line_changed_p = header_line_p != matrix->header_line_p; } + matrix->tab_line_p = tab_line_p; matrix->header_line_p = header_line_p; /* If POOL is null, MATRIX is a window matrix for window-based redisplay. @@ -397,6 +402,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y if (!marginal_areas_changed_p && !XFRAME (w->frame)->fonts_changed + && !tab_line_changed_p && !header_line_changed_p && matrix->window_pixel_left == WINDOW_LEFT_PIXEL_EDGE (w) && matrix->window_pixel_top == WINDOW_TOP_PIXEL_EDGE (w) @@ -448,7 +454,11 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y if (w == NULL || (row == matrix->rows + dim.height - 1 && window_wants_mode_line (w)) - || (row == matrix->rows && matrix->header_line_p)) + || (row == matrix->rows && matrix->tab_line_p) + || (row == matrix->rows + && !matrix->tab_line_p && matrix->header_line_p) + || (row == (matrix->rows + 1) + && matrix->tab_line_p && matrix->header_line_p)) { row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA]; @@ -463,6 +473,11 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y = row->glyphs[LEFT_MARGIN_AREA] + left; row->glyphs[RIGHT_MARGIN_AREA] = row->glyphs[TEXT_AREA] + dim.width - left - right; + /* Leave room for a border glyph. */ + if (!FRAME_WINDOW_P (XFRAME (w->frame)) + && !WINDOW_RIGHTMOST_P (w) + && right > 0) + row->glyphs[RIGHT_MARGIN_AREA] -= 1; row->glyphs[LAST_AREA] = row->glyphs[LEFT_MARGIN_AREA] + dim.width; } @@ -478,6 +493,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y Allocate glyph memory from the heap. */ if (dim.width > matrix->matrix_w || new_rows + || tab_line_changed_p || header_line_changed_p || marginal_areas_changed_p) { @@ -493,7 +509,11 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y /* The mode line, if displayed, never has marginal areas. */ if ((row == matrix->rows + dim.height - 1 && !(w && window_wants_mode_line (w))) - || (row == matrix->rows && matrix->header_line_p)) + || (row == matrix->rows && matrix->tab_line_p) + || (row == matrix->rows + && !matrix->tab_line_p && matrix->header_line_p) + || (row == (matrix->rows + 1) + && matrix->tab_line_p && matrix->header_line_p)) { row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA]; @@ -518,6 +538,14 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y eassert (left >= 0 && right >= 0); matrix->left_margin_glyphs = left; matrix->right_margin_glyphs = right; + + /* If we are resizing a window, make sure the previous mode-line + row of the window's current matrix is no longer marked as such. */ + if (w && matrix == w->current_matrix + && matrix->nrows > 0 + && dim.height != matrix->nrows + && matrix->nrows <= matrix->rows_allocated) + MATRIX_MODE_LINE_ROW (matrix)->mode_line_p = false; } /* Number of rows to be used by MATRIX. */ @@ -539,6 +567,7 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y upper window). Invalidate all rows that are no longer part of the window. */ if (!marginal_areas_changed_p + && !tab_line_changed_p && !header_line_changed_p && new_rows == 0 && dim.width == matrix->matrix_w @@ -728,7 +757,7 @@ shift_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int start, in eassert (start >= 0 && start < matrix->nrows); eassert (end >= 0 && end <= matrix->nrows); - min_y = WINDOW_HEADER_LINE_HEIGHT (w); + min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (w); for (; start < end; ++start) @@ -767,7 +796,13 @@ clear_current_matrices (register struct frame *f) clear_glyph_matrix (XWINDOW (f->menu_bar_window)->current_matrix); #endif -#if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS) +#if defined (HAVE_WINDOW_SYSTEM) + /* Clear the matrix of the tab-bar window, if any. */ + if (WINDOWP (f->tab_bar_window)) + clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix); +#endif + +#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* Clear the matrix of the tool-bar window, if any. */ if (WINDOWP (f->tool_bar_window)) clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix); @@ -792,7 +827,12 @@ clear_desired_matrices (register struct frame *f) clear_glyph_matrix (XWINDOW (f->menu_bar_window)->desired_matrix); #endif -#if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS) +#if defined (HAVE_WINDOW_SYSTEM) + if (WINDOWP (f->tab_bar_window)) + clear_glyph_matrix (XWINDOW (f->tab_bar_window)->desired_matrix); +#endif + +#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) if (WINDOWP (f->tool_bar_window)) clear_glyph_matrix (XWINDOW (f->tool_bar_window)->desired_matrix); #endif @@ -845,7 +885,7 @@ clear_glyph_row (struct glyph_row *row) enum { off = offsetof (struct glyph_row, used) }; /* Zero everything except pointers in `glyphs'. */ - memset (row->used, 0, sizeof *row - off); + memset ((char *) row + off, 0, sizeof *row - off); } @@ -857,7 +897,7 @@ blank_row (struct window *w, struct glyph_row *row, int y) { int min_y, max_y; - min_y = WINDOW_HEADER_LINE_HEIGHT (w); + min_y = WINDOW_TAB_LINE_HEIGHT (w) + WINDOW_HEADER_LINE_HEIGHT (w); max_y = WINDOW_BOX_HEIGHT_NO_MODE_LINE (w); clear_glyph_row (row); @@ -994,7 +1034,7 @@ copy_row_except_pointers (struct glyph_row *to, struct glyph_row *from) { enum { off = offsetof (struct glyph_row, x) }; - memcpy (&to->x, &from->x, sizeof *to - off); + memcpy ((char *) to + off, (char *) from + off, sizeof *to - off); } @@ -1062,7 +1102,7 @@ find_glyph_row_slice (struct glyph_matrix *window_matrix, call to this function really clears it. In addition, this function makes sure the marginal areas of ROW are in sync with the window's display margins. MODE_LINE_P non-zero means we are preparing a - glyph row for header line or mode line. */ + glyph row for tab/header line or mode line. */ void prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p) @@ -1077,11 +1117,11 @@ prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p) } if (mode_line_p) { - /* Mode and header lines, if displayed, never have marginal + /* Mode and header/tab lines, if displayed, never have marginal areas. If we are called with MODE_LINE_P non-zero, we are - displaying the mode/header line in this window, and so the + displaying the mode/header/tab line in this window, and so the marginal areas of this glyph row should be eliminated. This - is needed when the mode/header line is switched on in a + is needed when the mode/header/tab line is switched on in a window that has display margins. */ if (w->left_margin_cols > 0) row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA]; @@ -1099,13 +1139,20 @@ prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p) /* Make sure the marginal areas of this row are in sync with what the window wants, when the row actually displays text - and not header/mode line. */ + and not tab/header/mode line. */ if (w->left_margin_cols > 0 && (left != row->glyphs[TEXT_AREA] - row->glyphs[LEFT_MARGIN_AREA])) row->glyphs[TEXT_AREA] = row->glyphs[LEFT_MARGIN_AREA] + left; if (w->right_margin_cols > 0 && (right != row->glyphs[LAST_AREA] - row->glyphs[RIGHT_MARGIN_AREA])) - row->glyphs[RIGHT_MARGIN_AREA] = row->glyphs[LAST_AREA] - right; + { + row->glyphs[RIGHT_MARGIN_AREA] = row->glyphs[LAST_AREA] - right; + /* Leave room for a border glyph. */ + if (!FRAME_WINDOW_P (XFRAME (w->frame)) + && !WINDOW_RIGHTMOST_P (w) + && right > 0) + row->glyphs[RIGHT_MARGIN_AREA] -= 1; + } } } @@ -1281,7 +1328,7 @@ row_equal_p (struct glyph_row *a, struct glyph_row *b, bool mouse_face_p) with zeros. If GLYPH_DEBUG and ENABLE_CHECKING are in effect, the global variable glyph_pool_count is incremented for each pool allocated. */ -static struct glyph_pool * +static struct glyph_pool * ATTRIBUTE_MALLOC new_glyph_pool (void) { struct glyph_pool *result = xzalloc (sizeof *result); @@ -1699,7 +1746,7 @@ required_matrix_height (struct window *w) if (FRAME_WINDOW_P (f)) { - /* https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00194.html */ + /* https://lists.gnu.org/r/emacs-devel/2015-11/msg00194.html */ int ch_height = max (FRAME_SMALLEST_FONT_HEIGHT (f), 1); int window_pixel_height = window_box_height (w) + eabs (w->vscroll); @@ -1708,8 +1755,8 @@ required_matrix_height (struct window *w) /* One partially visible line at the top and bottom of the window. */ + 2 - /* 2 for header and mode line. */ - + 2); + /* 3 for tab, header and mode line. */ + + 3); } #endif /* HAVE_WINDOW_SYSTEM */ @@ -1726,7 +1773,7 @@ required_matrix_width (struct window *w) struct frame *f = XFRAME (w->frame); if (FRAME_WINDOW_P (f)) { - /* https://lists.gnu.org/archive/html/emacs-devel/2015-11/msg00194.html */ + /* https://lists.gnu.org/r/emacs-devel/2015-11/msg00194.html */ int ch_width = max (FRAME_SMALLEST_CHAR_WIDTH (f), 1); /* Compute number of glyphs needed in a glyph row. */ @@ -1790,12 +1837,23 @@ adjust_frame_glyphs (struct frame *f) if (FRAME_WINDOW_P (f)) adjust_frame_glyphs_for_window_redisplay (f); else - adjust_frame_glyphs_for_frame_redisplay (f); + { + adjust_frame_glyphs_for_frame_redisplay (f); + eassert (FRAME_INITIAL_P (f) + || noninteractive + || !initialized + || (f->current_matrix + && f->current_matrix->nrows > 0 + && f->current_matrix->rows + && f->desired_matrix + && f->desired_matrix->nrows > 0 + && f->desired_matrix->rows)); + } /* Don't forget the buffer for decode_mode_spec. */ adjust_decode_mode_spec_buffer (f); - f->glyphs_initialized_p = 1; + f->glyphs_initialized_p = true; unblock_input (); } @@ -1862,6 +1920,7 @@ fake_current_matrices (Lisp_Object window) - r->used[LEFT_MARGIN_AREA] - r->used[RIGHT_MARGIN_AREA]); r->mode_line_p = 0; + r->tab_line_p = 0; } } } @@ -2022,7 +2081,19 @@ adjust_frame_glyphs_for_frame_redisplay (struct frame *f) to the frame width (from CHANGE_FRAME_SIZE_1). */ if (matrix_dim.width != FRAME_TOTAL_COLS (f) || matrix_dim.height != FRAME_TOTAL_LINES (f)) - return; + { + /* We have reallocated the frame's glyph pools, but didn't + update the glyph pointers in the frame's glyph matrices + to use the reallocated pools (that happens below, in the + call to adjust_glyph_matrix). Set the frame's garbaged + flag, so that when we are called again from + redisplay_internal, we don't erroneously call + save_current_matrix, because it will use the wrong glyph + pointers, and will most probably crash. */ + if (!FRAME_WINDOW_P (f) && pool_changed_p) + SET_FRAME_GARBAGED (f); + return; + } eassert (matrix_dim.width == FRAME_TOTAL_COLS (f) && matrix_dim.height == FRAME_TOTAL_LINES (f)); @@ -2059,6 +2130,19 @@ adjust_frame_glyphs_for_frame_redisplay (struct frame *f) SET_FRAME_GARBAGED (f); } } + else if (!FRAME_INITIAL_P (f) && !noninteractive && initialized) + { + if (!f->desired_matrix->nrows || !f->desired_matrix->rows) + { + adjust_glyph_matrix (NULL, f->desired_matrix, 0, 0, matrix_dim); + SET_FRAME_GARBAGED (f); + } + if (!f->current_matrix->nrows || !f->current_matrix->rows) + { + adjust_glyph_matrix (NULL, f->current_matrix, 0, 0, matrix_dim); + SET_FRAME_GARBAGED (f); + } + } } @@ -2106,7 +2190,39 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f) } #endif -#if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS) +#if defined (HAVE_WINDOW_SYSTEM) + { + /* Allocate/ reallocate matrices of the tab bar window. If we + don't have a tab bar window yet, make one. */ + struct window *w; + if (NILP (f->tab_bar_window)) + { + Lisp_Object frame; + fset_tab_bar_window (f, make_window ()); + w = XWINDOW (f->tab_bar_window); + XSETFRAME (frame, f); + wset_frame (w, frame); + w->pseudo_window_p = 1; + } + else + w = XWINDOW (f->tab_bar_window); + + w->pixel_left = 0; + w->left_col = 0; + w->pixel_top = FRAME_MENU_BAR_HEIGHT (f) + + (!NILP (Vtab_bar_position) ? FRAME_TOOL_BAR_HEIGHT (f) : 0); + w->top_line = FRAME_MENU_BAR_LINES (f) + + (!NILP (Vtab_bar_position) ? FRAME_TOOL_BAR_LINES (f) : 0); + w->total_cols = FRAME_TOTAL_COLS (f); + w->pixel_width = (FRAME_PIXEL_WIDTH (f) + - 2 * FRAME_INTERNAL_BORDER_WIDTH (f)); + w->total_lines = FRAME_TAB_BAR_LINES (f); + w->pixel_height = FRAME_TAB_BAR_HEIGHT (f); + allocate_matrices_for_window_redisplay (w); + } +#endif + +#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) { /* Allocate/ reallocate matrices of the tool bar window. If we don't have a tool bar window yet, make one. */ @@ -2125,8 +2241,10 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f) w->pixel_left = 0; w->left_col = 0; - w->pixel_top = FRAME_MENU_BAR_HEIGHT (f); - w->top_line = FRAME_MENU_BAR_LINES (f); + w->pixel_top = FRAME_MENU_BAR_HEIGHT (f) + + (NILP (Vtab_bar_position) ? FRAME_TAB_BAR_HEIGHT (f) : 0); + w->top_line = FRAME_MENU_BAR_LINES (f) + + (NILP (Vtab_bar_position) ? FRAME_TAB_BAR_LINES (f) : 0); w->total_cols = FRAME_TOTAL_COLS (f); w->pixel_width = (FRAME_PIXEL_WIDTH (f) - 2 * FRAME_INTERNAL_BORDER_WIDTH (f)); @@ -2169,7 +2287,7 @@ free_glyphs (struct frame *f) /* Block interrupt input so that we don't get surprised by an X event while we're in an inconsistent state. */ block_input (); - f->glyphs_initialized_p = 0; + f->glyphs_initialized_p = false; /* Release window sub-matrices. */ if (!NILP (f->root_window)) @@ -2188,7 +2306,19 @@ free_glyphs (struct frame *f) } #endif -#if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS) +#if defined (HAVE_WINDOW_SYSTEM) + /* Free the tab bar window and its glyph matrices. */ + if (!NILP (f->tab_bar_window)) + { + struct window *w = XWINDOW (f->tab_bar_window); + free_glyph_matrix (w->desired_matrix); + free_glyph_matrix (w->current_matrix); + w->desired_matrix = w->current_matrix = NULL; + fset_tab_bar_window (f, Qnil); + } +#endif + +#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* Free the tool bar window and its glyph matrices. */ if (!NILP (f->tool_bar_window)) { @@ -2464,11 +2594,15 @@ build_frame_matrix_from_leaf_window (struct glyph_matrix *frame_matrix, struct w the corresponding frame row to be updated. */ frame_row->enabled_p = true; - /* Maybe insert a vertical border between horizontally adjacent + /* Maybe insert a vertical border between horizontally adjacent windows. */ - if (GLYPH_CHAR (right_border_glyph) != 0) + if (GLYPH_CHAR (right_border_glyph) != 0) { - struct glyph *border = window_row->glyphs[LAST_AREA] - 1; + struct glyph *border = window_row->glyphs[LAST_AREA] - 1; + /* It's a subtle bug if we are overwriting some non-char + glyph with the vertical border glyph. */ + eassert (border->type == CHAR_GLYPH); + border->type = CHAR_GLYPH; SET_CHAR_GLYPH_FROM_GLYPH (*border, right_border_glyph); } @@ -2509,8 +2643,7 @@ spec_glyph_lookup_face (struct window *w, GLYPH *glyph) /* Convert the glyph's specified face to a realized (cache) face. */ if (lface_id > 0) { - int face_id = merge_faces (XFRAME (w->frame), - Qt, lface_id, DEFAULT_FACE_ID); + int face_id = merge_faces (w, Qt, lface_id, DEFAULT_FACE_ID); SET_GLYPH_FACE (*glyph, face_id); } } @@ -2599,12 +2732,25 @@ set_frame_matrix_frame (struct frame *f) operations in window matrices of frame_matrix_frame. */ static void -make_current (struct glyph_matrix *desired_matrix, struct glyph_matrix *current_matrix, int row) +make_current (struct glyph_matrix *desired_matrix, + struct glyph_matrix *current_matrix, int row) { struct glyph_row *current_row = MATRIX_ROW (current_matrix, row); struct glyph_row *desired_row = MATRIX_ROW (desired_matrix, row); bool mouse_face_p = current_row->mouse_face_p; + /* If we aborted redisplay of this window, a row in the desired + matrix might not have its hash computed. But update_window + relies on each row having its correct hash, so do it here if + needed. */ + if (!desired_row->hash + /* A glyph row that is not completely empty is unlikely to have + a zero hash value. */ + && !(!desired_row->used[0] + && !desired_row->used[1] + && !desired_row->used[2])) + desired_row->hash = row_hash (desired_row); + /* Do current_row = desired_row. This exchanges glyph pointers between both rows, and does a structure assignment otherwise. */ assign_row (current_row, desired_row); @@ -3083,7 +3229,30 @@ update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p) update_window (XWINDOW (f->menu_bar_window), true); #endif -#if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS) +#if defined (HAVE_WINDOW_SYSTEM) + /* Update the tab-bar window, if present. */ + if (WINDOWP (f->tab_bar_window)) + { + struct window *w = XWINDOW (f->tab_bar_window); + + /* Update tab-bar window. */ + if (w->must_be_updated_p) + { + Lisp_Object tem; + + update_window (w, true); + w->must_be_updated_p = false; + + /* Swap tab-bar strings. We swap because we want to + reuse strings. */ + tem = f->current_tab_bar_string; + fset_current_tab_bar_string (f, f->desired_tab_bar_string); + fset_desired_tab_bar_string (f, tem); + } + } +#endif + +#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) /* Update the tool-bar window, if present. */ if (WINDOWP (f->tool_bar_window)) { @@ -3120,16 +3289,23 @@ update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p) build_frame_matrix (f); /* Update the display. */ - update_begin (f); - paused_p = update_frame_1 (f, force_p, inhibit_hairy_id_p, 1); - update_end (f); + if (FRAME_INITIAL_P (f)) + /* No actual display to update so the "update" is a nop and + obviously isn't interrupted by pending input. */ + paused_p = false; + else + { + update_begin (f); + paused_p = update_frame_1 (f, force_p, inhibit_hairy_id_p, 1, false); + update_end (f); + } if (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) { if (FRAME_TTY (f)->termscript) - fflush_unlocked (FRAME_TTY (f)->termscript); + fflush (FRAME_TTY (f)->termscript); if (FRAME_TERMCAP_P (f)) - fflush_unlocked (FRAME_TTY (f)->output); + fflush (FRAME_TTY (f)->output); } /* Check window matrices for lost pointers. */ @@ -3174,7 +3350,7 @@ update_frame_with_menu (struct frame *f, int row, int col) cursor_at_point_p = !(row >= 0 && col >= 0); /* Force update_frame_1 not to stop due to pending input, and not try scrolling. */ - paused_p = update_frame_1 (f, 1, 1, cursor_at_point_p); + paused_p = update_frame_1 (f, 1, 1, cursor_at_point_p, true); /* ROW and COL tell us where in the menu to position the cursor, so that screen readers know the active region on the screen. */ if (!cursor_at_point_p) @@ -3182,8 +3358,8 @@ update_frame_with_menu (struct frame *f, int row, int col) update_end (f); if (FRAME_TTY (f)->termscript) - fflush_unlocked (FRAME_TTY (f)->termscript); - fflush_unlocked (FRAME_TTY (f)->output); + fflush (FRAME_TTY (f)->termscript); + fflush (FRAME_TTY (f)->output); /* Check window matrices for lost pointers. */ #if GLYPH_DEBUG #if 0 @@ -3200,6 +3376,53 @@ update_frame_with_menu (struct frame *f, int row, int col) display_completed = !paused_p; } +/* Update the mouse position for a frame F. This handles both + updating the display for mouse-face properties and updating the + help echo text. + + Returns the number of events generated. */ +int +update_mouse_position (struct frame *f, int x, int y) +{ + previous_help_echo_string = help_echo_string; + help_echo_string = Qnil; + + note_mouse_highlight (f, x, y); + + /* If the contents of the global variable help_echo_string + has changed, generate a HELP_EVENT. */ + if (!NILP (help_echo_string) + || !NILP (previous_help_echo_string)) + { + Lisp_Object frame; + XSETFRAME (frame, f); + + gen_help_event (help_echo_string, frame, help_echo_window, + help_echo_object, help_echo_pos); + return 1; + } + + return 0; +} + +DEFUN ("display--update-for-mouse-movement", Fdisplay__update_for_mouse_movement, + Sdisplay__update_for_mouse_movement, 2, 2, 0, + doc: /* Handle mouse movement detected by Lisp code. + +This function should be called when Lisp code detects the mouse has +moved, even if `track-mouse' is nil. This handles updates that do not +rely on input events such as updating display for mouse-face +properties or updating the help echo text. */) + (Lisp_Object mouse_x, Lisp_Object mouse_y) +{ + CHECK_FIXNUM (mouse_x); + CHECK_FIXNUM (mouse_y); + + update_mouse_position (SELECTED_FRAME (), XFIXNUM (mouse_x), + XFIXNUM (mouse_y)); + return Qnil; +} + /************************************************************************ Window-based updates @@ -3390,8 +3613,10 @@ update_window (struct window *w, bool force_p) { struct glyph_matrix *desired_matrix = w->desired_matrix; bool paused_p; - int preempt_count = baud_rate / 2400 + 1; + int preempt_count = clip_to_bounds (1, baud_rate / 2400 + 1, INT_MAX); +#ifdef HAVE_WINDOW_SYSTEM struct redisplay_interface *rif = FRAME_RIF (XFRAME (WINDOW_FRAME (w))); +#endif #ifdef GLYPH_DEBUG /* Check that W's frame doesn't have glyph matrices. */ eassert (FRAME_WINDOW_P (XFRAME (WINDOW_FRAME (w)))); @@ -3401,22 +3626,36 @@ update_window (struct window *w, bool force_p) if (!force_p) detect_input_pending_ignore_squeezables (); - /* If forced to complete the update, or if no input is pending, do - the update. */ - if (force_p || !input_pending || !NILP (do_mouse_tracking)) + /* If forced to complete the update, no input is pending, or we are + tracking the mouse, do the update. */ + if (force_p || !input_pending || !NILP (track_mouse)) { struct glyph_row *row, *end; struct glyph_row *mode_line_row; + struct glyph_row *tab_line_row; struct glyph_row *header_line_row; int yb; bool changed_p = 0, mouse_face_overwritten_p = 0; int n_updated = 0; + bool invisible_rows_marked = false; - rif->update_window_begin_hook (w); +#ifdef HAVE_WINDOW_SYSTEM + gui_update_window_begin (w); +#endif yb = window_text_bottom_y (w); row = MATRIX_ROW (desired_matrix, 0); end = MATRIX_MODE_LINE_ROW (desired_matrix); + /* Take note of the tab line, if there is one. We will + update it below, after updating all of the window's lines. */ + if (row->mode_line_p && row->tab_line_p) + { + tab_line_row = row; + ++row; + } + else + tab_line_row = NULL; + /* Take note of the header line, if there is one. We will update it below, after updating all of the window's lines. */ if (row->mode_line_p) @@ -3446,7 +3685,8 @@ update_window (struct window *w, bool force_p) /* 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); + int rc = scrolling_window (w, (tab_line_row != NULL ? 1 : 0) + + (header_line_row != NULL ? 1 : 0)); if (rc < 0) { /* All rows were found to be equal. */ @@ -3489,22 +3729,54 @@ update_window (struct window *w, bool force_p) tempted to optimize redisplay based on lines displayed in the first redisplay. */ if (MATRIX_ROW_BOTTOM_Y (row) >= yb) - for (i = vpos + 1; i < w->current_matrix->nrows - 1; ++i) - SET_MATRIX_ROW_ENABLED_P (w->current_matrix, i, false); + { + for (i = vpos + 1; i < w->current_matrix->nrows - 1; ++i) + SET_MATRIX_ROW_ENABLED_P (w->current_matrix, i, false); + invisible_rows_marked = true; + } } /* Was display preempted? */ paused_p = row < end; + if (!paused_p && !invisible_rows_marked) + { + /* If we didn't mark the invisible rows in the current + matrix as invalid above, do that now. This can happen if + scrolling_window updates the last visible rows of the + current matrix, in which case the above loop doesn't get + to examine the last visible row. */ + int i; + for (i = 0; i < w->current_matrix->nrows - 1; ++i) + { + struct glyph_row *current_row = MATRIX_ROW (w->current_matrix, i); + if (current_row->enabled_p + && MATRIX_ROW_BOTTOM_Y (current_row) >= yb) + { + for (++i ; i < w->current_matrix->nrows - 1; ++i) + SET_MATRIX_ROW_ENABLED_P (w->current_matrix, i, false); + } + } + } + set_cursor: + /* Update the tab line after scrolling because a new tab + line would otherwise overwrite lines at the top of the window + that can be scrolled. */ + if (tab_line_row && tab_line_row->enabled_p) + { + tab_line_row->y = 0; + update_window_line (w, 0, &mouse_face_overwritten_p); + } + /* Update the header line after scrolling because a new header line would otherwise overwrite lines at the top of the window that can be scrolled. */ if (header_line_row && header_line_row->enabled_p) { - header_line_row->y = 0; - update_window_line (w, 0, &mouse_face_overwritten_p); + header_line_row->y = tab_line_row ? CURRENT_TAB_LINE_HEIGHT (w) : 0; + update_window_line (w, tab_line_row ? 1 : 0, &mouse_face_overwritten_p); } /* Fix the appearance of overlapping/overlapped rows. */ @@ -3534,13 +3806,17 @@ update_window (struct window *w, bool force_p) #ifdef HAVE_WINDOW_SYSTEM update_window_fringes (w, 0); -#endif /* 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 W->output_cursor doesn't contain the cursor location. */ - rif->update_window_end_hook (w, !paused_p, mouse_face_overwritten_p); + gui_update_window_end (w, !paused_p, mouse_face_overwritten_p); +#endif + /* If the update wasn't interrupted, this window has been + completely updated. */ + if (!paused_p) + w->must_be_updated_p = false; } else paused_p = 1; @@ -3556,6 +3832,92 @@ update_window (struct window *w, bool force_p) return paused_p; } +#ifdef HAVE_WINDOW_SYSTEM + +/* Start update of window W. */ + +void +gui_update_window_begin (struct window *w) +{ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f); + + block_input (); + + if (FRAME_RIF (f)->update_window_begin_hook) + FRAME_RIF (f)->update_window_begin_hook (w); + + w->output_cursor = w->cursor; + + if (f == hlinfo->mouse_face_mouse_frame) + { + /* Don't do highlighting for mouse motion during the update. */ + hlinfo->mouse_face_defer = true; + + /* If the frame needs to be redrawn, simply forget about any + prior mouse highlighting. */ + if (FRAME_GARBAGED_P (f)) + hlinfo->mouse_face_window = Qnil; + } + + unblock_input (); +} + +/* End update of window W. + + Draw vertical borders between horizontally adjacent windows, and + display W's cursor if CURSOR_ON_P is non-zero. + + MOUSE_FACE_OVERWRITTEN_P non-zero means that some row containing + glyphs in mouse-face were overwritten. In that case we have to + make sure that the mouse-highlight is properly redrawn. */ +void +gui_update_window_end (struct window *w, bool cursor_on_p, + bool mouse_face_overwritten_p) +{ + struct frame *f = XFRAME (WINDOW_FRAME (w)); + + /* Pseudo windows don't have cursors, so don't display them here. */ + if (!w->pseudo_window_p) + { + block_input (); + + if (cursor_on_p) + display_and_set_cursor (w, true, + w->output_cursor.hpos, w->output_cursor.vpos, + w->output_cursor.x, w->output_cursor.y); + + if (cursor_in_mouse_face_p (w) && cursor_on_p) + mouse_face_overwritten_p = 1; + + if (draw_window_fringes (w, true)) + { + if (WINDOW_RIGHT_DIVIDER_WIDTH (w)) + gui_draw_right_divider (w); + else + gui_draw_vertical_border (w); + } + unblock_input (); + } + + /* If a row with mouse-face was overwritten, arrange for + frame_up_to_date_hook to redisplay the mouse highlight. */ + if (mouse_face_overwritten_p) + { + Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f); + + hlinfo->mouse_face_beg_row = hlinfo->mouse_face_beg_col = -1; + hlinfo->mouse_face_end_row = hlinfo->mouse_face_end_col = -1; + hlinfo->mouse_face_window = Qnil; + } + + if (FRAME_RIF (f)->update_window_end_hook) + FRAME_RIF (f)->update_window_end_hook (w, + cursor_on_p, + mouse_face_overwritten_p); +} + +#endif /* HAVE_WINDOW_SYSTEM */ /* Update the display of area AREA in window W, row number VPOS. AREA can be either LEFT_MARGIN_AREA or RIGHT_MARGIN_AREA. */ @@ -3582,7 +3944,8 @@ update_marginal_area (struct window *w, struct glyph_row *updated_row, Value is true if display has changed. */ static bool -update_text_area (struct window *w, struct glyph_row *updated_row, int vpos) +update_text_area (struct window *w, struct glyph_row *updated_row, int vpos, + bool *partial_p) { struct glyph_row *current_row = MATRIX_ROW (w->current_matrix, vpos); struct glyph_row *desired_row = MATRIX_ROW (w->desired_matrix, vpos); @@ -3603,9 +3966,13 @@ update_text_area (struct window *w, struct glyph_row *updated_row, int vpos) However, it causes excessive flickering when mouse is moved across the mode line. Luckily, turning it off for the mode line doesn't seem to hurt anything. -- cyd. - But it is still needed for the header line. -- kfs. */ + But it is still needed for the header line. -- kfs. + The header line vpos is 1 if a tab line is enabled. (18th + Apr 2022) */ || (current_row->mouse_face_p - && !(current_row->mode_line_p && vpos > 0)) + && !(current_row->mode_line_p + && (vpos > (w->current_matrix->tab_line_p + && w->current_matrix->header_line_p)))) || current_row->x != desired_row->x) { output_cursor_to (w, vpos, 0, desired_row->y, desired_row->x); @@ -3684,6 +4051,13 @@ update_text_area (struct window *w, struct glyph_row *updated_row, int vpos) { x += desired_glyph->pixel_width; ++desired_glyph, ++current_glyph, ++i; + + /* Say that only a partial update was performed of + the current row (i.e. not all the glyphs were + drawn). This is used to preserve the stipple_p + flag of the current row inside + update_window_line. */ + *partial_p = true; } /* Consider the case that the current row contains "xxx @@ -3755,9 +4129,15 @@ update_text_area (struct window *w, struct glyph_row *updated_row, int vpos) rif->write_glyphs (w, updated_row, start, TEXT_AREA, i - start_hpos); changed_p = 1; + *partial_p = true; } } + /* This means we will draw from the start, so no partial update + is being performed. */ + if (!i) + *partial_p = false; + /* Write the rest. */ if (i < desired_row->used[TEXT_AREA]) { @@ -3830,7 +4210,9 @@ update_window_line (struct window *w, int vpos, bool *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); struct redisplay_interface *rif = FRAME_RIF (XFRAME (WINDOW_FRAME (w))); - bool changed_p = 0; + + /* partial_p is true if not all of desired_row was drawn. */ + bool changed_p = 0, partial_p = 0, was_stipple; /* A row can be completely invisible in case a desired matrix was built with a vscroll and then make_cursor_line_fully_visible shifts @@ -3854,7 +4236,7 @@ update_window_line (struct window *w, int vpos, bool *mouse_face_overwritten_p) } /* Update the display of the text area. */ - if (update_text_area (w, desired_row, vpos)) + if (update_text_area (w, desired_row, vpos, &partial_p)) { changed_p = 1; if (current_row->mouse_face_p) @@ -3883,7 +4265,17 @@ update_window_line (struct window *w, int vpos, bool *mouse_face_overwritten_p) } /* Update current_row from desired_row. */ + was_stipple = current_row->stipple_p; make_current (w->desired_matrix, w->current_matrix, vpos); + + /* If only a partial update was performed, any stipple already + displayed in MATRIX_ROW (w->current_matrix, vpos) might still be + there, so don't hurry to clear that flag if it's not in + desired_row. */ + + if (partial_p && was_stipple) + current_row->stipple_p = true; + return changed_p; } @@ -3905,11 +4297,11 @@ set_window_cursor_after_update (struct window *w) /* If we are showing a message instead of the mini-buffer, show the cursor for the message instead. */ && XWINDOW (minibuf_window) == w - && EQ (minibuf_window, echo_area_window) + && BASE_EQ (minibuf_window, echo_area_window) /* These cases apply only to the frame that contains the active mini-buffer window. */ && FRAME_HAS_MINIBUF_P (f) - && EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window)) + && BASE_EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window)) { cx = cy = vpos = hpos = 0; @@ -4063,7 +4455,6 @@ add_row_entry (struct glyph_row *row) return entry; } - /* Try to reuse part of the current display of W by scrolling lines. HEADER_LINE_P means W has a header line. @@ -4091,7 +4482,7 @@ add_row_entry (struct glyph_row *row) 1 if we did scroll. */ static int -scrolling_window (struct window *w, bool header_line_p) +scrolling_window (struct window *w, int tab_line_p) { struct glyph_matrix *desired_matrix = w->desired_matrix; struct glyph_matrix *current_matrix = w->current_matrix; @@ -4104,11 +4495,19 @@ scrolling_window (struct window *w, bool header_line_p) struct redisplay_interface *rif = FRAME_RIF (XFRAME (WINDOW_FRAME (w))); /* Skip over rows equal at the start. */ - for (i = header_line_p; i < current_matrix->nrows - 1; ++i) + for (i = tab_line_p; i < current_matrix->nrows - 1; ++i) { struct glyph_row *d = MATRIX_ROW (desired_matrix, i); struct glyph_row *c = MATRIX_ROW (current_matrix, i); + /* If there is a row with a stipple currently on the glass, give + up. Stipples look different depending on where on the + display they are drawn, so scrolling the display will produce + incorrect results. */ + + if (c->stipple_p) + return 0; + if (c->enabled_p && d->enabled_p && !d->redraw_fringe_bitmaps_p @@ -4124,8 +4523,11 @@ scrolling_window (struct window *w, bool header_line_p) break; } -#ifdef HAVE_XWIDGETS - /* Currently this seems needed to detect xwidget movement reliably. */ + /* Can't scroll the display of w32 GUI frames when position of point + is indicated by the system caret, because scrolling the display + will then "copy" the pixels used by the caret. */ +#ifdef HAVE_NTGUI + if (w32_use_visible_system_caret) return 0; #endif @@ -4135,6 +4537,16 @@ scrolling_window (struct window *w, bool header_line_p) first_old = first_new = i; + while (i < current_matrix->nrows - 1) + { + /* If there is a stipple after the first change, give up as + well. */ + if (MATRIX_ROW (current_matrix, i)->stipple_p) + return 0; + + ++i; + } + /* Set last_new to the index + 1 of the row that reaches the bottom boundary in the desired matrix. Give up if we find a disabled row before we reach the bottom boundary. */ @@ -4474,23 +4886,20 @@ scrolling_window (struct window *w, bool header_line_p) static bool update_frame_1 (struct frame *f, bool force_p, bool inhibit_id_p, - bool set_cursor_p) + bool set_cursor_p, bool updating_menu_p) { /* Frame matrices to work on. */ struct glyph_matrix *current_matrix = f->current_matrix; struct glyph_matrix *desired_matrix = f->desired_matrix; int i; bool pause_p; - int preempt_count = baud_rate / 2400 + 1; + int preempt_count = clip_to_bounds (1, baud_rate / 2400 + 1, INT_MAX); eassert (current_matrix && desired_matrix); if (baud_rate != FRAME_COST_BAUD_RATE (f)) calculate_costs (f); - if (preempt_count <= 0) - preempt_count = 1; - if (!force_p && detect_input_pending_ignore_squeezables ()) { pause_p = 1; @@ -4513,14 +4922,16 @@ update_frame_1 (struct frame *f, bool force_p, bool inhibit_id_p, /* Update the individual lines as needed. Do bottom line first. */ if (MATRIX_ROW_ENABLED_P (desired_matrix, desired_matrix->nrows - 1)) - update_frame_line (f, desired_matrix->nrows - 1); + update_frame_line (f, desired_matrix->nrows - 1, updating_menu_p); /* Now update the rest of the lines. */ for (i = 0; i < desired_matrix->nrows - 1 && (force_p || !input_pending); i++) { if (MATRIX_ROW_ENABLED_P (desired_matrix, i)) { - if (FRAME_TERMCAP_P (f)) + /* Note that output_buffer_size being 0 means that we want the + old default behavior of flushing output every now and then. */ + if (FRAME_TERMCAP_P (f) && FRAME_TTY (f)->output_buffer_size == 0) { /* Flush out every so many lines. Also flush out if likely to have more than 1k buffered @@ -4532,14 +4943,14 @@ update_frame_1 (struct frame *f, bool force_p, bool inhibit_id_p, ptrdiff_t outq = __fpending (display_output); if (outq > 900 || (outq > 20 && ((i - 1) % preempt_count == 0))) - fflush_unlocked (display_output); + fflush (display_output); } } if (!force_p && (i - 1) % preempt_count == 0) detect_input_pending_ignore_squeezables (); - update_frame_line (f, i); + update_frame_line (f, i, updating_menu_p); } } @@ -4552,13 +4963,13 @@ update_frame_1 (struct frame *f, bool force_p, bool inhibit_id_p, /* If we are showing a message instead of the mini-buffer, show the cursor for the message instead of for the (now hidden) mini-buffer contents. */ - || (EQ (minibuf_window, selected_window) - && EQ (minibuf_window, echo_area_window) + || (BASE_EQ (minibuf_window, selected_window) + && BASE_EQ (minibuf_window, echo_area_window) && !NILP (echo_area_buffer[0]))) /* These cases apply only to the frame that contains the active mini-buffer window. */ && FRAME_HAS_MINIBUF_P (f) - && EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window)) + && BASE_EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window)) { int top = WINDOW_TOP_EDGE_LINE (XWINDOW (FRAME_MINIBUF_WINDOW (f))); int col; @@ -4652,7 +5063,6 @@ scrolling (struct frame *frame) unsigned *new_hash = old_hash + height; int *draw_cost = (int *) (new_hash + height); int *old_draw_cost = draw_cost + height; - eassert (current_matrix); /* Compute hash codes of all the lines. Also calculate number of @@ -4674,8 +5084,7 @@ scrolling (struct frame *frame) { /* This line cannot be redrawn, so don't let scrolling mess it. */ new_hash[i] = old_hash[i]; -#define INFINITY 1000000 /* Taken from scroll.c */ - draw_cost[i] = INFINITY; + draw_cost[i] = SCROLL_INFINITY; } else { @@ -4775,7 +5184,7 @@ count_match (struct glyph *str1, struct glyph *end1, struct glyph *str2, struct /* Perform a frame-based update on line VPOS in frame FRAME. */ static void -update_frame_line (struct frame *f, int vpos) +update_frame_line (struct frame *f, int vpos, bool updating_menu_p) { struct glyph *obody, *nbody, *op1, *op2, *np1, *nend; int tem; @@ -4814,6 +5223,12 @@ update_frame_line (struct frame *f, int vpos) current_row->enabled_p = true; current_row->used[TEXT_AREA] = desired_row->used[TEXT_AREA]; + /* For some reason, cursor is sometimes moved behind our back when a + frame with a TTY menu is redrawn. Homing the cursor as below + fixes that. */ + if (updating_menu_p) + cursor_to (f, 0, 0); + /* If desired line is empty, just clear the line. */ if (!desired_row->enabled_p) { @@ -5086,13 +5501,15 @@ update_frame_line (struct frame *f, int vpos) ***********************************************************************/ /* Determine what's under window-relative pixel position (*X, *Y). - Return the OBJECT (string or buffer) that's there. + Return the object (string or buffer) that's there. Return in *POS the position in that object. Adjust *X and *Y to character positions. + If an image is shown at the specified position, return + in *OBJECT its image-spec. Return in *DX and *DY the pixel coordinates of the click, - relative to the top left corner of OBJECT, or relative to + relative to the top left corner of object, or relative to the top left corner of the character glyph at (*X, *Y) - if OBJECT is nil. + if the object at (*X, *Y) is nil. Return WIDTH and HEIGHT of the object at (*X, *Y), or zero if the coordinates point to an empty area of the display. */ @@ -5143,6 +5560,29 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p include the hscroll. */ to_x += it.first_visible_x; + /* If we are hscrolling only the current line, and Y is at the line + containing point, augment TO_X with the hscroll amount of the + current line. */ + if (it.line_wrap == TRUNCATE + && EQ (automatic_hscrolling, Qcurrent_line) && IT_CHARPOS (it) < PT) + { + struct it it2 = it; + void *it2data = bidi_shelve_cache (); + it2.last_visible_x = 1000000; + /* If the line at Y shows point, the call below to + move_it_in_display_line will succeed in reaching point. */ + move_it_in_display_line (&it2, PT, -1, MOVE_TO_POS); + if (IT_CHARPOS (it2) >= PT) + { + to_x += (w->hscroll - w->min_hscroll) * FRAME_COLUMN_WIDTH (it.f); + /* We need to pretend the window is hscrolled, so that + move_it_in_display_line below will DTRT with TO_X. */ + it.first_visible_x += w->hscroll * FRAME_COLUMN_WIDTH (it.f); + it.last_visible_x += w->hscroll * FRAME_COLUMN_WIDTH (it.f); + } + bidi_unshelve_cache (it2data, 0); + } + /* Now move horizontally in the row to the glyph under *X. Second argument is ZV to prevent move_it_in_display_line from matching based on buffer positions. */ @@ -5179,6 +5619,11 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p #ifdef HAVE_WINDOW_SYSTEM if (it.what == IT_IMAGE) { + /* Note that this ignores images that are fringe bitmaps, + because their image ID is zero, and so IMAGE_OPT_FROM_ID will + return NULL. This is okay, since fringe bitmaps are not + displayed in the text area, and so are never the object we + are interested in. */ img = IMAGE_OPT_FROM_ID (it.f, it.image_id); if (img && !NILP (img->spec)) *object = img->spec; @@ -5189,7 +5634,8 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p start position, i.e. it excludes the header-line row, but MATRIX_ROW includes the header-line row. Adjust for a possible header-line row. */ - it_vpos = it.vpos + window_wants_header_line (w); + it_vpos = it.vpos + window_wants_header_line (w) + + window_wants_tab_line (w); if (it_vpos < w->current_matrix->nrows && (row = MATRIX_ROW (w->current_matrix, it_vpos), row->enabled_p)) @@ -5253,6 +5699,8 @@ mode_line_string (struct window *w, enum window_part part, if (part == ON_MODE_LINE) row = MATRIX_MODE_LINE_ROW (w->current_matrix); + else if (part == ON_TAB_LINE) + row = MATRIX_TAB_LINE_ROW (w->current_matrix); else row = MATRIX_HEADER_LINE_ROW (w->current_matrix); y0 = *y - row->y; @@ -5413,31 +5861,34 @@ handle_window_change_signal (int sig) termcap-controlled terminal, but we can't decide which. Therefore, we resize the frames corresponding to each tty. */ - for (tty = tty_list; tty; tty = tty->next) { + for (tty = tty_list; tty; tty = tty->next) + { + if (! tty->term_initted) + continue; - if (! tty->term_initted) - continue; + /* Suspended tty frames have tty->input == NULL avoid trying to + use it. */ + if (!tty->input) + continue; - /* Suspended tty frames have tty->input == NULL avoid trying to - use it. */ - if (!tty->input) - continue; + get_tty_size (fileno (tty->input), &width, &height); - get_tty_size (fileno (tty->input), &width, &height); + if (width > 5 && height > 2) + { + Lisp_Object tail, frame; - if (width > 5 && height > 2) { - Lisp_Object tail, frame; + FOR_EACH_FRAME (tail, frame) + { + struct frame *f = XFRAME (frame); - FOR_EACH_FRAME (tail, frame) - if (FRAME_TERMCAP_P (XFRAME (frame)) && FRAME_TTY (XFRAME (frame)) == tty) - /* Record the new sizes, but don't reallocate the data - structures now. Let that be done later outside of the - signal handler. */ - change_frame_size (XFRAME (frame), width, - height - FRAME_MENU_BAR_LINES (XFRAME (frame)), - 0, 1, 0, 0); + if (FRAME_TERMCAP_P (f) && FRAME_TTY (f) == tty) + /* Record the new sizes, but don't reallocate the data + structures now. Let that be done later outside of the + signal handler. */ + change_frame_size (f, width, height, false, true, false); + } + } } - } } static void @@ -5455,7 +5906,6 @@ deliver_window_change_signal (int sig) void do_pending_window_change (bool safe) { - /* If window change signal handler should have run before, run it now. */ if (redisplaying_p && !safe) return; @@ -5463,15 +5913,20 @@ do_pending_window_change (bool safe) { Lisp_Object tail, frame; - delayed_size_change = 0; + delayed_size_change = false; FOR_EACH_FRAME (tail, frame) { struct frame *f = XFRAME (frame); - if (f->new_height != 0 || f->new_width != 0) + /* Negative new_width or new_height values mean no change is + required (a native size can never drop below zero). If + new_size_p is not set, this means the size change was + requested by adjust_frame_size but has not been honored by + the window manager yet. */ + if (f->new_size_p && (f->new_height >= 0 || f->new_width >= 0)) change_frame_size (f, f->new_width, f->new_height, - 0, 0, safe, f->new_pixelwise); + false, false, safe); } } } @@ -5479,47 +5934,46 @@ do_pending_window_change (bool safe) static void change_frame_size_1 (struct frame *f, int new_width, int new_height, - bool pretend, bool delay, bool safe, bool pixelwise) + bool pretend, bool delay, bool safe) { - /* If we can't deal with the change now, queue it for later. */ if (delay || (redisplaying_p && !safe)) { + if (CONSP (frame_size_history) + && ((new_width != f->new_width + || new_height != f->new_height + || new_width != FRAME_PIXEL_WIDTH (f) + || new_height != FRAME_PIXEL_HEIGHT (f)))) + frame_size_history_extra + (f, build_string ("change_frame_size_1, delayed"), + FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), + new_width, new_height, f->new_width, f->new_height); + + /* We can't deal with the change now, queue it for later. */ f->new_width = new_width; f->new_height = new_height; - f->new_pixelwise = pixelwise; - delayed_size_change = 1; + f->new_size_p = true; + delayed_size_change = true; } else { - /* This size-change overrides any pending one for this frame. */ - f->new_height = 0; - f->new_width = 0; - f->new_pixelwise = 0; - - /* If an argument is zero, set it to the current value. */ - if (pixelwise) - { - new_width = (new_width <= 0) ? FRAME_TEXT_WIDTH (f) : new_width; - new_height = (new_height <= 0) ? FRAME_TEXT_HEIGHT (f) : new_height; - } - else - { - new_width = (((new_width <= 0) ? FRAME_COLS (f) : new_width) - * FRAME_COLUMN_WIDTH (f)); - new_height = (((new_height <= 0) ? FRAME_LINES (f) : new_height) - * FRAME_LINE_HEIGHT (f)); - } - - /* Adjust frame size but make sure x_set_window_size does not - get called. */ - adjust_frame_size (f, new_width, new_height, 5, pretend, - Qchange_frame_size); + /* Storing -1 in the new_width/new_height slots means that no size + change is pending. Native sizes are always non-negative. + Reset the new_size_p slot as well. */ + f->new_height = -1; + f->new_width = -1; + f->new_size_p = false; + /* adjust_frame_size wants its arguments in terms of text_width + and text_height, so convert them here. For pathologically + small frames, the resulting values may be negative though. */ + adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, new_width), + FRAME_PIXEL_TO_TEXT_HEIGHT (f, new_height), 5, + pretend, Qchange_frame_size); } } -/* Change text height/width of frame F. Values may be given as zero to - indicate that no change is needed. +/* Change native height/width of frame F to NEW_WIDTH/NEW_HEIGHT pixels. + Values may be given as -1 to indicate that no change is needed. If DELAY, assume we're being called from a signal handler, and queue the change for later - perhaps the next redisplay. Since this tries @@ -5529,7 +5983,7 @@ change_frame_size_1 (struct frame *f, int new_width, int new_height, change frame sizes while a redisplay is in progress. */ void change_frame_size (struct frame *f, int new_width, int new_height, - bool pretend, bool delay, bool safe, bool pixelwise) + bool pretend, bool delay, bool safe) { Lisp_Object tail, frame; @@ -5539,13 +5993,12 @@ change_frame_size (struct frame *f, int new_width, int new_height, size affects all frames. Termcap now supports multiple ttys. */ FOR_EACH_FRAME (tail, frame) - if (! FRAME_WINDOW_P (XFRAME (frame))) + if (!FRAME_WINDOW_P (XFRAME (frame))) change_frame_size_1 (XFRAME (frame), new_width, new_height, - pretend, delay, safe, pixelwise); + pretend, delay, safe); } else - change_frame_size_1 (f, new_width, new_height, pretend, delay, safe, - pixelwise); + change_frame_size_1 (f, new_width, new_height, pretend, delay, safe); } /*********************************************************************** @@ -5616,13 +6069,17 @@ when TERMINAL is nil. */) if (tty->termscript) { - fwrite_unlocked (SDATA (string), 1, SBYTES (string), tty->termscript); - fflush_unlocked (tty->termscript); + fwrite (SDATA (string), 1, SBYTES (string), tty->termscript); + fflush (tty->termscript); } out = tty->output; } - fwrite_unlocked (SDATA (string), 1, SBYTES (string), out); - fflush_unlocked (out); + /* STRING might be very long, in which case fwrite could be + interrupted by SIGIO. So we temporarily block SIGIO. */ + unrequest_sigio (); + fwrite (SDATA (string), 1, SBYTES (string), out); + fflush (out); + request_sigio (); unblock_input (); return Qnil; } @@ -5637,7 +6094,7 @@ terminate any keyboard macro currently executing. */) if (!NILP (arg)) { if (noninteractive) - putchar_unlocked (07); + putchar (07); else ring_bell (XFRAME (selected_frame)); } @@ -5651,7 +6108,7 @@ void bitch_at_user (void) { if (noninteractive) - putchar_unlocked (07); + putchar (07); else if (!INTERACTIVE) /* Stop executing a keyboard macro. */ { const char *msg @@ -5680,8 +6137,8 @@ additional wait period, in milliseconds; this is for backwards compatibility. if (!NILP (milliseconds)) { - CHECK_NUMBER (milliseconds); - duration += XINT (milliseconds) / 1000.0; + CHECK_FIXNUM (milliseconds); + duration += XFIXNUM (milliseconds) / 1000.0; } if (duration > 0) @@ -5711,7 +6168,14 @@ additional wait period, in milliseconds; this is for backwards compatibility. READING is true if reading input. If DISPLAY_OPTION is >0 display process output while waiting. If DISPLAY_OPTION is >1 perform an initial redisplay before waiting. -*/ + + Returns a boolean Qt if we waited the full time and returns Qnil if the + wait was interrupted by incoming process output or keyboard events. + + FIXME: When `wait_reading_process_output` returns early because of + process output, instead of returning nil we should loop and wait some + more (i.e. until either there's pending input events or the timeout + expired). */ Lisp_Object sit_for (Lisp_Object timeout, bool reading, int display_option) @@ -5719,6 +6183,8 @@ sit_for (Lisp_Object timeout, bool reading, int display_option) intmax_t sec; int nsec; bool do_display = display_option > 0; + bool curbuf_eq_winbuf + = (current_buffer == XBUFFER (XWINDOW (selected_window)->contents)); swallow_events (do_display); @@ -5731,9 +6197,18 @@ sit_for (Lisp_Object timeout, bool reading, int display_option) if (INTEGERP (timeout)) { - sec = XINT (timeout); - if (sec <= 0) - return Qt; + if (integer_to_intmax (timeout, &sec)) + { + if (sec <= 0) + return Qt; + sec = min (sec, WAIT_READING_MAX); + } + else + { + if (NILP (Fnatnump (timeout))) + return Qt; + sec = WAIT_READING_MAX; + } nsec = 0; } else if (FLOATP (timeout)) @@ -5757,14 +6232,22 @@ sit_for (Lisp_Object timeout, bool reading, int display_option) wrong_type_argument (Qnumberp, timeout); -#ifdef USABLE_SIGIO +#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL) gobble_input (); #endif - wait_reading_process_output (sec, nsec, reading ? -1 : 1, do_display, - Qnil, NULL, 0); + int nbytes + = wait_reading_process_output (sec, nsec, reading ? -1 : 1, do_display, + Qnil, NULL, 0); - return detect_input_pending () ? Qnil : Qt; + if (reading && curbuf_eq_winbuf) + /* Timers and process filters/sentinels may have changed the selected + window (e.g. in response to a connection from emacsclient), in which + case we should follow it (unless we weren't in the selected-window's + buffer to start with). */ + set_buffer_internal (XBUFFER (XWINDOW (selected_window)->contents)); + + return (nbytes > 0 || detect_input_pending ()) ? Qnil : Qt; } @@ -5779,20 +6262,17 @@ Return t if redisplay was performed, nil if redisplay was preempted immediately by pending input. */) (Lisp_Object force) { - ptrdiff_t count; - swallow_events (true); if ((detect_input_pending_run_timers (1) && NILP (force) && !redisplay_dont_pause) || !NILP (Vexecuting_kbd_macro)) return Qnil; - count = SPECPDL_INDEX (); + specpdl_ref count = SPECPDL_INDEX (); if (!NILP (force) && !redisplay_dont_pause) specbind (Qredisplay_dont_pause, Qt); redisplay_preserve_echo_area (2); - unbind_to (count, Qnil); - return Qt; + return unbind_to (count, Qt); } @@ -5841,7 +6321,7 @@ pass nil for VARIABLE. */) { if (idx == ASIZE (state)) goto changed; - if (!EQ (AREF (state, idx++), frame)) + if (!BASE_EQ (AREF (state, idx++), frame)) goto changed; if (idx == ASIZE (state)) goto changed; @@ -5856,7 +6336,7 @@ pass nil for VARIABLE. */) continue; if (idx == ASIZE (state)) goto changed; - if (!EQ (AREF (state, idx++), buf)) + if (!BASE_EQ (AREF (state, idx++), buf)) goto changed; if (idx == ASIZE (state)) goto changed; @@ -5889,7 +6369,7 @@ pass nil for VARIABLE. */) || n + 20 < ASIZE (state) / 2) /* Add 20 extra so we grow it less often. */ { - state = Fmake_vector (make_number (n + 20), Qlambda); + state = make_vector (n + 20, Qlambda); if (! NILP (variable)) Fset (variable, state); else @@ -5936,12 +6416,24 @@ pass nil for VARIABLE. */) Initialization ***********************************************************************/ +static void +init_faces_initial (void) +{ + /* For the initial frame, we don't have any way of knowing what + are the foreground and background colors of the terminal. */ + struct frame *sf = SELECTED_FRAME (); + + FRAME_FOREGROUND_PIXEL (sf) = FACE_TTY_DEFAULT_FG_COLOR; + FRAME_BACKGROUND_PIXEL (sf) = FACE_TTY_DEFAULT_BG_COLOR; + call0 (intern ("tty-set-up-initial-frame-faces")); +} + /* 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. */ -void -init_display (void) +static void +init_display_interactive (void) { char *terminal_type; @@ -5961,9 +6453,7 @@ init_display (void) with. Otherwise newly opened tty frames will not resize automatically. */ #ifdef SIGWINCH -#ifndef CANNOT_DUMP - if (initialized) -#endif /* CANNOT_DUMP */ + if (!will_dump_p ()) { struct sigaction action; emacs_sigaction_init (&action, deliver_window_change_signal); @@ -5973,10 +6463,21 @@ init_display (void) /* If running as a daemon, no need to initialize any frames/terminal, except on Windows, where we at least want to initialize it. */ -#ifndef WINDOWSNT if (IS_DAEMON) + { + /* Pdump'ed Emacs doesn't record the initial frame from temacs, + so the non-basic faces realized for that frame in temacs + aren't in emacs. This causes errors when users try to + customize those faces in their init file. The call to + init_faces_initial will realize these faces now. (Non-daemon + Emacs does this either near the end of this function or when + the GUI frame is created.) */ + if (dumped_with_pdumper_p ()) + init_faces_initial (); +#ifndef WINDOWSNT return; #endif + } /* If the user wants to use a window system, we shouldn't bother initializing the terminal. This is especially important when the @@ -5984,7 +6485,8 @@ init_display (void) using the window system. If the DISPLAY environment variable is set and nonempty, - try to use X, and die with an error message if that doesn't work. */ + try to use X, and if that fails output a line to stderr + reporting that -nw will be simulated. */ #ifdef HAVE_X_WINDOWS if (! inhibit_window_system && ! display_arg) @@ -6004,9 +6506,6 @@ init_display (void) if (!inhibit_window_system && display_arg) { Vinitial_window_system = Qx; -#ifdef HAVE_X11 - Vwindow_system_version = make_number (11); -#endif #ifdef USE_NCURSES /* In some versions of ncurses, tputs crashes if we have not called tgetent. @@ -6021,20 +6520,30 @@ init_display (void) if (!inhibit_window_system) { Vinitial_window_system = Qw32; - Vwindow_system_version = make_number (1); return; } #endif /* HAVE_NTGUI */ #ifdef HAVE_NS - if (!inhibit_window_system -#ifndef CANNOT_DUMP - && initialized -#endif - ) + if (!inhibit_window_system && !will_dump_p ()) { Vinitial_window_system = Qns; - Vwindow_system_version = make_number (10); + return; + } +#endif + +#ifdef HAVE_PGTK + if (!inhibit_window_system && !will_dump_p ()) + { + Vinitial_window_system = Qpgtk; + return; + } +#endif + +#ifdef HAVE_HAIKU + if (!inhibit_window_system && !will_dump_p ()) + { + Vinitial_window_system = Qhaiku; return; } #endif @@ -6050,12 +6559,14 @@ init_display (void) #endif if (!terminal_type) { + char const *msg + = "Please set the environment variable TERM; see 'tset'.\n"; #ifdef HAVE_WINDOW_SYSTEM if (! inhibit_window_system) - fprintf (stderr, "Please set the environment variable DISPLAY or TERM (see 'tset').\n"); - else + msg = ("Please set the environment variable DISPLAY or TERM; " + "see 'tset'.\n"); #endif /* HAVE_WINDOW_SYSTEM */ - fprintf (stderr, "Please set the environment variable TERM; see 'tset'.\n"); + fputs (msg, stderr); exit (1); } @@ -6076,6 +6587,7 @@ init_display (void) t->reference_count++; #ifdef MSDOS + f->output_data.tty = &the_only_tty_output; f->output_data.tty->display_info = &the_only_display_info; #else if (f->output_method == output_termcap) @@ -6084,8 +6596,8 @@ init_display (void) t->display_info.tty->top_frame = selected_frame; change_frame_size (XFRAME (selected_frame), FrameCols (t->display_info.tty), - FrameRows (t->display_info.tty) - - FRAME_MENU_BAR_LINES (f), 0, 0, 1, 0); + FrameRows (t->display_info.tty), + false, false, true); /* Delete the initial terminal. */ if (--initial_terminal->reference_count == 0 @@ -6119,22 +6631,23 @@ init_display (void) calculate_costs (XFRAME (selected_frame)); - /* Set up faces of the initial terminal frame of a dumped Emacs. */ - if (initialized - && !noninteractive - && NILP (Vinitial_window_system)) - { - /* For the initial frame, we don't have any way of knowing what - are the foreground and background colors of the terminal. */ - struct frame *sf = SELECTED_FRAME (); + /* Set up faces of the initial terminal frame. */ + if (initialized && !noninteractive && NILP (Vinitial_window_system)) + init_faces_initial (); +} - FRAME_FOREGROUND_PIXEL (sf) = FACE_TTY_DEFAULT_FG_COLOR; - FRAME_BACKGROUND_PIXEL (sf) = FACE_TTY_DEFAULT_BG_COLOR; - call0 (intern ("tty-set-up-initial-frame-faces")); +void +init_display (void) +{ + if (noninteractive) + { + if (dumped_with_pdumper_p ()) + init_faces_initial (); } + else + init_display_interactive (); } - /*********************************************************************** Blinking cursor @@ -6169,11 +6682,14 @@ WINDOW nil or omitted means report on the selected window. */) Initialization ***********************************************************************/ +static void syms_of_display_for_pdumper (void); + void syms_of_display (void) { defsubr (&Sredraw_frame); defsubr (&Sredraw_display); + defsubr (&Sdisplay__update_for_mouse_movement); defsubr (&Sframe_or_buffer_changed_p); defsubr (&Sopen_termscript); defsubr (&Sding); @@ -6187,7 +6703,7 @@ syms_of_display (void) defsubr (&Sdump_redisplay_history); #endif - frame_and_buffer_state = Fmake_vector (make_number (20), Qlambda); + frame_and_buffer_state = make_vector (20, Qlambda); staticpro (&frame_and_buffer_state); /* This is the "purpose" slot of a display table. */ @@ -6223,6 +6739,8 @@ The value is a symbol: `w32' for an Emacs frame that is a window on MS-Windows display, `ns' for an Emacs frame on a GNUstep or Macintosh Cocoa display, `pc' for a direct-write MS-DOS frame. + `pgtk' for an Emacs frame using pure GTK facilities. + `haiku' for an Emacs frame running in Haiku. Use of this variable as a boolean is deprecated. Instead, use `display-graphic-p' or any of the other `display-*-p' @@ -6236,15 +6754,13 @@ The value is a symbol: `w32' for an Emacs frame that is a window on MS-Windows display, `ns' for an Emacs frame on a GNUstep or Macintosh Cocoa display, `pc' for a direct-write MS-DOS frame. + `pgtk' for an Emacs frame using pure GTK facilities. + `haiku' for an Emacs frame running in Haiku. Use of this variable as a boolean is deprecated. Instead, use `display-graphic-p' or any of the other `display-*-p' predicates which report frame's specific UI-related capabilities. */); - DEFVAR_LISP ("window-system-version", Vwindow_system_version, - doc: /* The version number of the window system in use. -For X windows, this is 11. */); - DEFVAR_BOOL ("cursor-in-echo-area", cursor_in_echo_area, doc: /* Non-nil means put cursor in minibuffer, at end of any message there. */); @@ -6276,11 +6792,20 @@ See `buffer-display-table' for more information. */); beginning of the next redisplay). */ redisplay_dont_pause = true; -#ifdef CANNOT_DUMP - if (noninteractive) -#endif - { - Vinitial_window_system = Qnil; - Vwindow_system_version = Qnil; - } + DEFVAR_LISP ("x-show-tooltip-timeout", Vx_show_tooltip_timeout, + doc: /* The default timeout (in seconds) for `x-show-tip'. */); + Vx_show_tooltip_timeout = make_fixnum (5); + + DEFVAR_LISP ("tab-bar-position", Vtab_bar_position, + doc: /* Specify on which side from the tool bar the tab bar shall be. +Possible values are t (below the tool bar), nil (above the tool bar). +This option affects only builds where the tool bar is not external. */); + + pdumper_do_now_and_after_load (syms_of_display_for_pdumper); +} + +static void +syms_of_display_for_pdumper (void) +{ + Vinitial_window_system = Qnil; } |