#include "lisp.h"
#include "atimer.h"
+#include "composite.h"
#include "keyboard.h"
+#include "systime.h"
#include "frame.h"
#include "window.h"
#include "termchar.h"
#include "indent.h"
#include "commands.h"
#include "keymap.h"
-#include "macros.h"
#include "disptab.h"
#include "termhooks.h"
#include "termopts.h"
#include "intervals.h"
#include "coding.h"
-#include "process.h"
#include "region-cache.h"
#include "font.h"
#include "fontset.h"
static bool message_enable_multibyte;
-/* Nonzero if we should redraw the mode lines on the next redisplay.
- If it has value REDISPLAY_SOME, then only redisplay the mode lines where
- the `redisplay' bit has been set. Otherwise, redisplay all mode lines
- (the number used is then only used to track down the cause for this
- full-redisplay). */
+/* At each redisplay cycle, we should refresh everything there is to refresh.
+ To do that efficiently, we use many optimizations that try to make sure we
+ don't waste too much time updating things that haven't changed.
+ The coarsest such optimization is that, in the most common cases, we only
+ look at the selected-window.
+
+ To know whether other windows should be considered for redisplay, we use the
+ variable windows_or_buffers_changed: as long as it is 0, it means that we
+ have not noticed anything that should require updating anything else than
+ the selected-window. If it is set to REDISPLAY_SOME, it means that since
+ last redisplay, some changes have been made which could impact other
+ windows. To know which ones need redisplay, every buffer, window, and frame
+ has a `redisplay' bit, which (if true) means that this object needs to be
+ redisplayed. If windows_or_buffers_changed is 0, we know there's no point
+ looking for those `redisplay' bits (actually, there might be some such bits
+ set, but then only on objects which aren't displayed anyway).
+
+ OTOH if it's non-zero we wil have to loop through all windows and then check
+ the `redisplay' bit of the corresponding window, frame, and buffer, in order
+ to decide whether that window needs attention or not. Note that we can't
+ just look at the frame's redisplay bit to decide that the whole frame can be
+ skipped, since even if the frame's redisplay bit is unset, some of its
+ windows's redisplay bits may be set.
+
+ Mostly for historical reasons, windows_or_buffers_changed can also take
+ other non-zero values. In that case, the precise value doesn't matter (it
+ encodes the cause of the setting but is only used for debugging purposes),
+ and what it means is that we shouldn't pay attention to any `redisplay' bits
+ and we should simply try and redisplay every window out there. */
-int update_mode_lines;
+int windows_or_buffers_changed;
-/* Nonzero if window sizes or contents other than selected-window have changed
- since last redisplay that finished.
- If it has value REDISPLAY_SOME, then only redisplay the windows where
- the `redisplay' bit has been set. Otherwise, redisplay all windows
- (the number used is then only used to track down the cause for this
- full-redisplay). */
+/* Nonzero if we should redraw the mode lines on the next redisplay.
+ Similarly to `windows_or_buffers_changed', If it has value REDISPLAY_SOME,
+ then only redisplay the mode lines in those buffers/windows/frames where the
+ `redisplay' bit has been set.
+ For any other value, redisplay all mode lines (the number used is then only
+ used to track down the cause for this full-redisplay).
+
+ Since the frame title uses the same %-constructs as the mode line
+ (except %c and %l), if this variable is non-zero, we also consider
+ redisplaying the title of each frame, see x_consider_frame_title.
+
+ The `redisplay' bits are the same as those used for
+ windows_or_buffers_changed, and setting windows_or_buffers_changed also
+ causes recomputation of the mode lines of all those windows. IOW this
+ variable only has an effect if windows_or_buffers_changed is zero, in which
+ case we should only need to redisplay the mode-line of those objects with
+ a `redisplay' bit set but not the window's text content (tho we may still
+ need to refresh the text content of the selected-window). */
-int windows_or_buffers_changed;
+int update_mode_lines;
/* True after display_mode_line if %l was used and it displayed a
line number. */
b->text->redisplay = true;
}
+void
+maybe_set_redisplay (Lisp_Object symbol)
+{
+ if (HASH_TABLE_P (Vredisplay__variables)
+ && hash_lookup (XHASH_TABLE (Vredisplay__variables), symbol, NULL) >= 0)
+ {
+ bset_update_mode_line (current_buffer);
+ current_buffer->prevent_redisplay_optimizations_p = true;
+ }
+}
+
#ifdef GLYPH_DEBUG
/* True means print traces of redisplay if compiled with
reset the echo_area_buffer in question to nil at the end because
with_echo_area_buffer will sets it to an empty buffer. */
bool i = display_last_displayed_message_p;
+ /* According to the C99, C11 and C++11 standards, the integral value
+ of a "bool" is always 0 or 1, so this array access is safe here,
+ if oddly typed. */
no_message_p = NILP (echo_area_buffer[i]);
window_height_changed_p
{
struct frame *f = XFRAME (frame);
- if (FRAME_WINDOW_P (f)
- || FRAME_MINIBUF_ONLY_P (f)
- || f->explicit_name)
+ if ((FRAME_WINDOW_P (f)
+ || FRAME_MINIBUF_ONLY_P (f)
+ || f->explicit_name)
+ && NILP (Fframe_parameter (frame, Qtooltip)))
{
/* Do we have more than one visible frame on this X display? */
Lisp_Object tail, other_frame, fmt;
static bool
redisplay_tool_bar (struct frame *f)
{
+ f->tool_bar_redisplayed = true;
#if defined (USE_GTK) || defined (HAVE_NS)
if (FRAME_EXTERNAL_TOOL_BAR (f))
/* True means redisplay has to redisplay the miniwindow. */
bool update_miniwindow_p = false;
- /* True means we need to redraw frames whose 'redisplay' bit is set. */
- bool consider_some_frames_p = false;
-
TRACE ((stderr, "redisplay_internal %d\n", redisplaying_p));
/* No redisplay if running in batch mode or frame is not yet fully
{
echo_area_display (false);
+ /* If echo_area_display resizes the mini-window, the redisplay and
+ window_sizes_changed flags of the selected frame are set, but
+ it's too late for the hooks in window-size-change-functions,
+ which have been examined already in prepare_menu_bars. So in
+ that case we call the hooks here only for the selected frame. */
+ if (sf->redisplay && FRAME_WINDOW_SIZES_CHANGED (sf))
+ {
+ Lisp_Object functions;
+ ptrdiff_t count1 = SPECPDL_INDEX ();
+
+ record_unwind_save_match_data ();
+
+ /* Clear flag first in case we get an error below. */
+ FRAME_WINDOW_SIZES_CHANGED (sf) = false;
+ functions = Vwindow_size_change_functions;
+
+ while (CONSP (functions))
+ {
+ if (!EQ (XCAR (functions), Qt))
+ call1 (XCAR (functions), selected_frame);
+ functions = XCDR (functions);
+ }
+
+ unbind_to (count1, Qnil);
+ }
+
if (message_cleared_p)
update_miniwindow_p = true;
&& (current_buffer->clip_changed || window_outdated (w))
&& resize_mini_window (w, false))
{
+ if (sf->redisplay)
+ {
+ Lisp_Object functions;
+ ptrdiff_t count1 = SPECPDL_INDEX ();
+
+ record_unwind_save_match_data ();
+
+ /* Clear flag first in case we get an error below. */
+ FRAME_WINDOW_SIZES_CHANGED (sf) = false;
+ functions = Vwindow_size_change_functions;
+
+ while (CONSP (functions))
+ {
+ if (!EQ (XCAR (functions), Qt))
+ call1 (XCAR (functions), selected_frame);
+ functions = XCDR (functions);
+ }
+
+ unbind_to (count1, Qnil);
+ }
+
/* Resized active mini-window to fit the size of what it is
showing if its contents might have changed. */
must_finish = true;
&& !FRAME_OBSCURED_P (XFRAME (w->frame))
&& !XFRAME (w->frame)->cursor_type_changed
&& !XFRAME (w->frame)->face_change
- && !XFRAME (w->frame)->redisplay
/* Make sure recorded data applies to current buffer, etc. */
&& this_line_buffer == current_buffer
&& match_p
#endif
/* Build desired matrices, and update the display. If
- consider_all_windows_p, do it for all windows on all frames. If
- a frame's 'redisplay' flag is set, do it for all windows on each
- such frame. Otherwise do it for selected_window, only. */
+ consider_all_windows_p, do it for all windows on all frames that
+ require redisplay, as specified by their 'redisplay' flag.
+ Otherwise do it for selected_window, only. */
- if (!consider_all_windows_p)
+ if (consider_all_windows_p)
{
FOR_EACH_FRAME (tail, frame)
- {
- if (XFRAME (frame)->redisplay && XFRAME (frame) != sf)
- {
- consider_some_frames_p = true;
- break;
- }
- }
- }
-
- if (consider_all_windows_p || consider_some_frames_p)
- {
- FOR_EACH_FRAME (tail, frame)
- {
- if (XFRAME (frame)->redisplay || consider_all_windows_p)
- XFRAME (frame)->updated_p = false;
- }
+ XFRAME (frame)->updated_p = false;
propagate_buffer_redisplay ();
&& !EQ (FRAME_TTY (f)->top_frame, frame))
continue;
- if (!consider_all_windows_p && !f->redisplay)
- continue;
-
retry_frame:
if (FRAME_WINDOW_P (f) || FRAME_TERMCAP_P (f) || f == sf)
{
bool gcscrollbars
/* Only GC scrollbars when we redisplay the whole frame. */
= f->redisplay || !REDISPLAY_SOME_P ();
+ bool f_redisplay_flag = f->redisplay;
/* Mark all the scroll bars to be removed; we'll redeem
the ones we want when we redisplay their windows. */
if (gcscrollbars && FRAME_TERMINAL (f)->condemn_scroll_bars_hook)
goto retry_frame;
}
+ /* If the frame's redisplay flag was not set before
+ we went about redisplaying its windows, but it is
+ set now, that means we employed some redisplay
+ optimizations inside redisplay_windows, and
+ bypassed producing some screen lines. But if
+ f->redisplay is now set, it might mean the old
+ faces are no longer valid (e.g., if redisplaying
+ some window called some Lisp which defined a new
+ face or redefined an existing face), so trying to
+ use them in update_frame will segfault.
+ Therefore, we must redisplay this frame. */
+ if (!f_redisplay_flag && f->redisplay)
+ goto retry_frame;
+
/* Prevent various kinds of signals during display
update. stdio is not robust about handling
signals, which can cause an apparent I/O error. */
/* Compare desired and current matrices, perform output. */
update:
- /* If fonts changed, display again. */
- if (sf->fonts_changed)
- goto retry;
+ /* If fonts changed, display again. Likewise if redisplay_window_1
+ above caused some change (e.g., a change in faces) that requires
+ considering the entire frame again. */
+ if (sf->fonts_changed || sf->redisplay)
+ {
+ if (sf->redisplay)
+ {
+ /* Set this to force a more thorough redisplay.
+ Otherwise, we might immediately loop back to the
+ above "else-if" clause (since all the conditions that
+ led here might still be true), and we will then
+ infloop, because the selected-frame's redisplay flag
+ is not (and cannot be) reset. */
+ windows_or_buffers_changed = 50;
+ }
+ goto retry;
+ }
/* Prevent freeing of realized faces, since desired matrices are
pending that reference the faces we computed and cached. */
&& !update_mode_lines
&& !windows_or_buffers_changed
&& !f->cursor_type_changed
- && !f->redisplay
&& NILP (Vshow_trailing_whitespace)
/* This code is not used for mini-buffer for the sake of the case
of redisplaying to replace an echo area message; since in
if (w->cursor.vpos < 0)
{
/* If point does not appear, try to move point so it does
- appear. The desired matrix has been built above, so we
- can use it here. */
- new_vpos = window_box_height (w) / 2;
+ appear. The desired matrix has been built above, so we
+ can use it here. First see if point is in invisible
+ text, and if so, move it to the first visible buffer
+ position past that. */
+ struct glyph_row *r = NULL;
+ Lisp_Object invprop =
+ get_char_property_and_overlay (make_number (PT), Qinvisible,
+ Qnil, NULL);
+
+ if (TEXT_PROP_MEANS_INVISIBLE (invprop) != 0)
+ {
+ ptrdiff_t alt_pt;
+ Lisp_Object invprop_end =
+ Fnext_single_char_property_change (make_number (PT), Qinvisible,
+ Qnil, Qnil);
+
+ if (NATNUMP (invprop_end))
+ alt_pt = XFASTINT (invprop_end);
+ else
+ alt_pt = ZV;
+ r = row_containing_pos (w, alt_pt, w->desired_matrix->rows,
+ NULL, 0);
+ }
+ if (r)
+ new_vpos = MATRIX_ROW_BOTTOM_Y (r);
+ else /* Give up and just move to the middle of the window. */
+ new_vpos = window_box_height (w) / 2;
}
if (!cursor_row_fully_visible_p (w, false, false))
startp = run_window_scroll_functions (window, it.current.pos);
/* Redisplay the window. */
+ bool use_desired_matrix = false;
if (!current_matrix_up_to_date_p
|| windows_or_buffers_changed
|| f->cursor_type_changed
|| MINI_WINDOW_P (w)
|| !(used_current_matrix_p
= try_window_reusing_current_matrix (w)))
- try_window (window, startp, 0);
+ use_desired_matrix = (try_window (window, startp, 0) == 1);
/* If new fonts have been loaded (due to fontsets), give up. We
have to start a new redisplay since we need to re-adjust glyph
and similar ones. */
if (w->cursor.vpos < 0)
{
+ /* Prefer the desired matrix to the current matrix, if possible,
+ in the fallback calculations below. This is because using
+ the current matrix might completely goof, e.g. if its first
+ row is after point. */
+ struct glyph_matrix *matrix =
+ use_desired_matrix ? w->desired_matrix : w->current_matrix;
/* First, try locating the proper glyph row for PT. */
struct glyph_row *row =
- row_containing_pos (w, PT, w->current_matrix->rows, NULL, 0);
+ row_containing_pos (w, PT, matrix->rows, NULL, 0);
/* Sometimes point is at the beginning of invisible text that is
before the 1st character displayed in the row. In that case,
alt_pos = XFASTINT (invis_end);
else
alt_pos = ZV;
- row = row_containing_pos (w, alt_pos, w->current_matrix->rows,
- NULL, 0);
+ row = row_containing_pos (w, alt_pos, matrix->rows, NULL, 0);
}
}
/* Finally, fall back on the first row of the window after the
displaying the cursor at all. */
if (!row)
{
- row = w->current_matrix->rows;
+ row = matrix->rows;
if (row->mode_line_p)
++row;
}
- set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0);
+ set_cursor_from_row (w, row, matrix, 0, 0, 0, 0);
}
if (!cursor_row_fully_visible_p (w, false, false))
finish_menu_bars:
- /* When we reach a frame's selected window, redo the frame's menu bar. */
+ /* When we reach a frame's selected window, redo the frame's menu
+ bar and the frame's title. */
if (update_mode_line
&& EQ (FRAME_SELECTED_WINDOW (f), window))
{
ignore_mouse_drag_p = true;
#endif
}
+ x_consider_frame_title (w->frame);
#endif
}
/* Don't try to reuse the display if windows have been split
or such. */
|| windows_or_buffers_changed
- || f->redisplay
|| f->cursor_type_changed)
return false;
while (true)
{
/* Give up if we have gone too far. */
- if (end && row >= end)
+ if ((end && row >= end) || !row->enabled_p)
return NULL;
/* This formerly returned if they were equal.
I think that both quantities are of a "last plus one" type;
GIVE_UP (1);
/* This flag is used to prevent redisplay optimizations. */
- if (windows_or_buffers_changed || f->cursor_type_changed || f->redisplay)
+ if (windows_or_buffers_changed || f->cursor_type_changed)
GIVE_UP (2);
/* This function's optimizations cannot be used if overlays have
Vmessage_log_max = make_number (1000);
DEFVAR_LISP ("window-size-change-functions", Vwindow_size_change_functions,
- doc: /* Functions called before redisplay, if window sizes have changed.
+ doc: /* Functions called during redisplay, if window sizes have changed.
The value should be a list of functions that take one argument.
-Just before redisplay, for each frame, if any of its windows have changed
-size since the last redisplay, or have been split or deleted,
-all the functions in the list are called, with the frame as argument. */);
+During the first part of redisplay, for each frame, if any of its windows
+have changed size since the last redisplay, or have been split or deleted,
+all the functions in the list are called, with the frame as argument.
+If redisplay decides to resize the minibuffer window, it calls these
+functions on behalf of that as well. */);
Vwindow_size_change_functions = Qnil;
DEFVAR_LISP ("window-scroll-functions", Vwindow_scroll_functions,
DEFVAR_LISP ("redisplay--mode-lines-cause", Vredisplay__mode_lines_cause,
doc: /* */);
Vredisplay__mode_lines_cause = Fmake_hash_table (0, NULL);
+
+ DEFVAR_LISP ("redisplay--variables", Vredisplay__variables,
+ doc: /* A hash-table of variables changing which triggers a thorough redisplay. */);
+ Vredisplay__variables = Qnil;
}