]> code.delx.au - gnu-emacs/blobdiff - src/xdisp.c
Merge branch 'emacs-25-merge'
[gnu-emacs] / src / xdisp.c
index bc76384a4f996f69720259d25497b395906ee3d7..37dc6047e58cb0b358f84525fbe405a7eb8de1fb 100644 (file)
@@ -292,7 +292,9 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "lisp.h"
 #include "atimer.h"
+#include "composite.h"
 #include "keyboard.h"
+#include "systime.h"
 #include "frame.h"
 #include "window.h"
 #include "termchar.h"
@@ -303,13 +305,11 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #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"
@@ -434,22 +434,58 @@ static Lisp_Object Vmessage_stack;
 
 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.  */
@@ -584,6 +620,17 @@ bset_update_mode_line (struct buffer *b)
   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
@@ -10686,6 +10733,9 @@ display_echo_area (struct window *w)
      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
@@ -11505,9 +11555,10 @@ x_consider_frame_title (Lisp_Object frame)
 {
   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;
@@ -12310,6 +12361,7 @@ PIXELWISE non-nil means return the height of the tool bar in pixels.  */)
 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))
@@ -13336,9 +13388,6 @@ redisplay_internal (void)
   /* 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
@@ -13490,6 +13539,32 @@ redisplay_internal (void)
     {
       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;
 
@@ -13506,6 +13581,27 @@ redisplay_internal (void)
           && (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;
@@ -13558,7 +13654,6 @@ redisplay_internal (void)
       && !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
@@ -13754,29 +13849,14 @@ redisplay_internal (void)
 #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 ();
 
@@ -13790,15 +13870,13 @@ redisplay_internal (void)
              && !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)
@@ -13842,6 +13920,20 @@ redisplay_internal (void)
                        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.  */
@@ -13895,9 +13987,23 @@ redisplay_internal (void)
       /* 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.  */
@@ -15438,7 +15544,6 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
       && !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
@@ -16196,9 +16301,33 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       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))
@@ -16615,6 +16744,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   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
@@ -16625,7 +16755,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       || 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
@@ -16665,9 +16795,15 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
      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,
@@ -16692,8 +16828,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
                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
@@ -16701,11 +16836,11 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
         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))
@@ -16811,7 +16946,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 
  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))
     {
@@ -16846,6 +16982,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
            ignore_mouse_drag_p = true;
 #endif
         }
+      x_consider_frame_title (w->frame);
 #endif
     }
 
@@ -17049,7 +17186,6 @@ try_window_reusing_current_matrix (struct window *w)
       /* 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;
 
@@ -17689,7 +17825,7 @@ row_containing_pos (struct window *w, ptrdiff_t charpos,
   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;
@@ -17827,7 +17963,7 @@ try_window_id (struct window *w)
     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
@@ -31076,11 +31212,13 @@ the buffer when it becomes large.  */);
   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,
@@ -31421,6 +31559,10 @@ display table takes effect; in this case, Emacs does not consult
   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;
 }