all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: martin rudalics <rudalics@gmx.at>
Cc: emacs-devel <emacs-devel@gnu.org>
Subject: Fix `window-configuration-change-hook' and `window-size-change-functions'
Date: Mon, 22 Feb 2016 13:59:54 +0100	[thread overview]
Message-ID: <56CB064A.9060101@gmx.at> (raw)
In-Reply-To: <83k2skhhz1.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 3224 bytes --]

 From archived bug#21333: 25.0.50; window-size-change-functions not called after mini-window resize:

  > If anything, IMO we should _reduce_ the number of unrelated events
  > that trigger a call to these functions.  For example, currently any
  > command that reads from the minibuffer will trigger it, because when
  > read-from-minibuffer exits, it restores the window configuration by
  > calling set-window-configuration, which is documented to trigger these
  > functions.  That just doesn't make any sense to me, since most reads
  > from the minibuffer don't resize any windows!

I plan to install in the next days the attached patch which tries to fix
this and also some other issues discussed in this thread.  Please have a
look.

martin


Fix `window-configuration-change-hook' and `window-size-change-functions'

(1) Run `window-configuration-change-hook' if and only if a window was
deleted/created or shows another buffer.

(2) Run `window-size-change-functions' if and only if at least one
window changed its size (in a few cases `window-size-change-functions'
will also run when no window changed its size).

(3) Provide two functions `window-old-pixel-height' and
`window-old-pixel-width' that allow to easily detect which window
changed size.

* src/frame.h (struct frame): New boolean member
window_configuration_changed.
(FRAME_WINDOW_SIZES_CHANGED): Remove macro.
(FRAME_WINDOW_CONFIGURATION_CHANGED): New macro.
* src/frame.c (adjust_frame_size): Don't run
`window-configuration-change-hook'.
* src/window.h (struct window): New fields old_pixel_width and
old_pixel_height.
(WINDOW_INTERNAL_P): New macro.
* src/window.c (Fwindow_old_pixel_width)
(Fwindow_old_pixel_height): New functions.
(Fdelete_other_windows_internal, Fwindow_resize_apply)
(resize_frame_windows, Fsplit_window_internal)
(Fdelete_window_internal, grow_mini_window)
(shrink_mini_window, Fresize_mini_window_internal): Don't call
FRAME_WINDOW_SIZES_CHANGED.
(window_size_changed, window_set_old_pixel_sizes)
(run_window_size_change_functions): New functions.
(make_window): Initialize old_pixel_width and old_pixel_height
fields.
(Fdelete_window_internal): Don't call
run_window_configuration_change_hook.
(struct saved_window): Add old_pixel_height and old_pixel_width
fields.
(Fset_window_configuration): Try to identify window configuration
changes correctly so run_window_configuration_change_hook and
run_window_size_change_functions run only if configuration and size
really changed.
(save_window_save): Set the old_pixel_height and
old_pixel_width fields.
(Vwindow_size_change_functions): Move definiton from xdisp.c.
* src/xdisp.c (prepare_menu_bars, redisplay_internal): Call
run_window_size_change_functions.
(Vwindow_size_change_functions): Move definition to window.c.
* src/xfns.c (x_set_menu_bar_lines): Don't call
run_window_configuration_change_hook.
* doc/lispref/windows.texi (Window Sizes): Document new
functions `window-old-pixel-height' and `window-old-pixel-width'.
(Window Configurations): Mention that this may trigger
execution of `window-size-change-functions' although no window
changed size.
(Window Hooks): Update descriptions of `window-size-change-functions'
and `window-configuration-change-hook'.


[-- Attachment #2: window-hooks.diff --]
[-- Type: text/plain, Size: 58722 bytes --]

diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index f61f08a..eac3bd6 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -545,6 +545,12 @@ Window Sizes
 children.
 @end defun

+@defun window-old-pixel-height &optional Lisp_Object &optional window
+This function returns the height of window @var{window} in pixels at the
+time @code{window-size-change-functions} was run for the last time on
+@var{window}'s frame (@pxref{Window Hooks}).
+@end defun
+
 @cindex window pixel width
 @cindex pixel width of a window
 @cindex total pixel width of a window
@@ -559,6 +565,12 @@ Window Sizes
 the screen areas spanned by its children.
 @end defun

+@defun window-old-pixel-width &optional Lisp_Object &optional window
+This function returns the width of window @var{window} in pixels at the
+time @code{window-size-change-functions} was run for the last time on
+@var{window}'s frame (@pxref{Window Hooks}).
+@end defun
+
 @cindex full-width window
 @cindex full-height window
   The following functions can be used to determine whether a given
@@ -4087,11 +4099,11 @@ Window Configurations
 The argument @var{configuration} must be a value that was previously
 returned by @code{current-window-configuration}.  The configuration is
 restored in the frame from which @var{configuration} was made, whether
-that frame is selected or not.  This always counts as a window size
-change and triggers execution of the @code{window-size-change-functions}
-(@pxref{Window Hooks}), because @code{set-window-configuration} doesn't
-know how to tell whether the new configuration actually differs from the
-old one.
+that frame is selected or not.  In some rare cases this may trigger
+execution of the @code{window-size-change-functions} (@pxref{Window
+Hooks}) even if the size of windows did not change at all.  The
+@code{window-configuration-change-hook} functions will be called if and
+only if at least one window was added to or deleted from the frame.

 If the frame from which @var{configuration} was saved is dead, all this
 function does is restore the three variables @code{window-min-height},
@@ -4378,33 +4390,37 @@ Window Hooks
 @end defvar

 @defvar window-size-change-functions
-This variable holds a list of functions to be called if the size of
-any window changes for any reason.  The functions are called at the
-beginning of a redisplay cycle, and just once for each frame on which
-size changes have occurred.
-
-Each function receives the frame as its sole argument.  There is no
-direct way to find out which windows on that frame have changed size, or
-precisely how.  However, if a size-change function records, at each
-call, the existing windows and their sizes, it can also compare the
-present sizes and the previous sizes.
-
-Creating or deleting windows counts as a size change, and therefore
-causes these functions to be called.  Changing the frame size also
-counts, because it changes the sizes of the existing windows.
+This variable holds a list of functions to be called if the size of any
+window changes for any reason.  The functions are called once per
+redisplay, and once for each frame on which size changes have occurred.
+
+Each function receives the frame as its sole argument.  To find out
+whether a specific window has changed size, compare the return values of
+@code{window-old-pixel-width} and @code{window-pixel-width} respectively
+@code{window-old-pixel-height} and @code{window-pixel-height} for that
+window (@pxref{Window Sizes}).
+
+These function are usually only called when at least one window was
+added or has changed size since the last time this hook was run for the
+associated frame.  In some rare cases this hook also runs when a window
+that was added intermittently has been deleted afterwards.  In these
+cases none of the windows on the frame will appear to have changed its
+size.

 You may use @code{save-selected-window} in these functions
 (@pxref{Selecting Windows}).  However, do not use
 @code{save-window-excursion} (@pxref{Window Configurations}); exiting
-that macro counts as a size change, which would cause these functions
-to be called over and over.
+that macro counts as a size change, which would cause these functions to
+be called again.
 @end defvar

 @defvar window-configuration-change-hook
-A normal hook that is run every time you change the window configuration
-of an existing frame.  This includes splitting or deleting windows,
-changing the sizes of windows, or displaying a different buffer in a
-window.
+A normal hook that is run every time the window configuration of a frame
+changes.  Window configuration changes include splitting and deleting
+windows and the display of a different buffer in a window.  Resizing the
+frame or individual windows do not count as configuration changes.  Use
+@code{window-size-change-functions}, see above, when you want to track
+size changes that are not caused by the deletion or creation of windows.

 The buffer-local part of this hook is run once for each window on the
 affected frame, with the relevant window selected and its buffer
diff --git a/lisp/window.el b/lisp/window.el
index e4669c1..1c679ae 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -3136,10 +3136,10 @@ window--sanitize-window-sizes
     (walk-window-tree
      (lambda (window)
        (let  ((delta (- (window-min-size window horizontal nil t)
-			(window-size window horizontal t))))
-	 (when (> delta 0)
-	   (if (window-resizable-p window delta horizontal nil t)
-	       (window-resize window delta horizontal nil t)
+                        (window-size window horizontal t))))
+         (when (> delta 0)
+           (if (window-resizable-p window delta horizontal nil t)
+               (window-resize window delta horizontal nil t)
 	     (setq value nil))))))
     value))

diff --git a/src/frame.c b/src/frame.c
index 8c86afe..df473ae 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -591,8 +591,6 @@ adjust_frame_size (struct frame *f, int new_width, int new_height, int inhibit,
 		  || new_pixel_height != old_pixel_height);

   unblock_input ();
-
-  run_window_configuration_change_hook (f);
 }

 /* Allocate basically initialized frame.  */
diff --git a/src/frame.h b/src/frame.h
index 71dab4b..d9424ab 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -288,8 +288,9 @@ struct frame
      cleared.  */
   bool_bf explicit_name : 1;

-  /* True if size of some window on this frame has changed.  */
-  bool_bf window_sizes_changed : 1;
+  /* True if configuration of windows on this frame has changed since
+     last call of run_window_size_change_functions.  */
+  bool_bf window_configuration_changed : 1;

   /* True if the mouse has moved on this display device
      since the last time we checked.  */
@@ -828,10 +829,10 @@ default_pixels_per_inch_y (void)
    are frozen on frame F.  */
 #define FRAME_WINDOWS_FROZEN(f) (f)->frozen_window_starts

-/* True if a size change has been requested for frame F
-   but not yet really put into effect.  This can be true temporarily
-   when an X event comes in at a bad time.  */
-#define FRAME_WINDOW_SIZES_CHANGED(f) (f)->window_sizes_changed
+/* True if the frame's window configuration has changed since last call
+   of run_window_size_change_functions.  */
+#define FRAME_WINDOW_CONFIGURATION_CHANGED(f)	\
+  ((f)->window_configuration_changed)

 /* The minibuffer window of frame F, if it has one; otherwise nil.  */
 #define FRAME_MINIBUF_WINDOW(f) f->minibuffer_window
diff --git a/src/window.c b/src/window.c
index e1a30ee..7267a25 100644
--- a/src/window.c
+++ b/src/window.c
@@ -720,6 +720,30 @@ the height of the screen areas spanned by its children.  */)
   return make_number (decode_valid_window (window)->pixel_height);
 }

+DEFUN ("window-old-pixel-width", Fwindow_old_pixel_width, Swindow_old_pixel_width, 0, 1, 0,
+       doc: /* Return the old width of window WINDOW in pixels.
+WINDOW must be a valid window and defaults to the selected one.
+
+The return value is the pixel width of WINDOW at the last time
+`window-size-change-functions' was run.  It's zero if WINDOW was made
+after that.  */)
+  (Lisp_Object window)
+{
+  return make_number (decode_valid_window (window)->old_pixel_width);
+}
+
+DEFUN ("window-old-pixel-height", Fwindow_old_pixel_height, Swindow_old_pixel_height, 0, 1, 0,
+       doc: /* Return the old height of window WINDOW in pixels.
+WINDOW must be a valid window and defaults to the selected one.
+
+The return value is the pixel height of WINDOW at the last time
+`window-size-change-functions' was run.  It's zero if WINDOW was made
+after that.  */)
+  (Lisp_Object window)
+{
+  return make_number (decode_valid_window (window)->old_pixel_height);
+}
+
 DEFUN ("window-total-height", Fwindow_total_height, Swindow_total_height, 0, 2, 0,
        doc: /* Return the height of window WINDOW in lines.
 WINDOW must be a valid window and defaults to the selected one.
@@ -2879,6 +2903,7 @@ window-start value is reasonable when this function is called.  */)
   Lisp_Object sibling, pwindow, swindow IF_LINT (= Qnil), delta;
   ptrdiff_t startpos IF_LINT (= 0), startbyte IF_LINT (= 0);
   int top IF_LINT (= 0), new_top;
+  bool resize_failed = false;

   w = decode_valid_window (window);
   XSETWINDOW (window, w);
@@ -2978,8 +3003,6 @@ window-start value is reasonable when this function is called.  */)

   fset_redisplay (f);
   Vwindow_list = Qnil;
-  FRAME_WINDOW_SIZES_CHANGED (f) = true;
-  bool resize_failed = false;

   if (!WINDOW_LEAF_P (w))
     {
@@ -3229,6 +3252,76 @@ If WINDOW is omitted or nil, it defaults to the selected window.  */)
   return Qnil;
 }

+
+/* Compare old and present pixel sizes of windows in tree rooted at W.
+   Return true iff any of these windows differs in size.  */
+
+static bool
+window_size_changed (struct window *w)
+{
+  if (w->pixel_width != w->old_pixel_width
+      || w->pixel_height != w->old_pixel_height)
+    return true;
+
+  if (WINDOW_INTERNAL_P (w))
+    {
+      w = XWINDOW (w->contents);
+      while (w)
+	{
+	  if (window_size_changed (w))
+	    return true;
+
+	  w = NILP (w->next) ? 0 : XWINDOW (w->next);
+	}
+    }
+
+  return false;
+}
+
+/* Set old pixel sizes of windows in tree rooted at W to their present
+   pixel sizes.  */
+
+static void
+window_set_old_pixel_sizes (struct window *w)
+{
+  w->old_pixel_width = w->pixel_width;
+  w->old_pixel_height = w->pixel_height;
+
+  if (WINDOW_INTERNAL_P (w))
+    {
+      w = XWINDOW (w->contents);
+      while (w)
+	{
+	  window_set_old_pixel_sizes (w);
+	  w = NILP (w->next) ? 0 : XWINDOW (w->next);
+	}
+    }
+}
+
+
+void
+run_window_size_change_functions (Lisp_Object frame)
+{
+  struct frame *f = XFRAME (frame);
+  struct window *r = XWINDOW (FRAME_ROOT_WINDOW (f));
+  Lisp_Object functions = Vwindow_size_change_functions;
+
+  if (FRAME_WINDOW_CONFIGURATION_CHANGED (f) ||
+      window_size_changed (r))
+    {
+      while (CONSP (functions))
+	{
+	  if (!EQ (XCAR (functions), Qt))
+	    call1 (XCAR (functions), frame);
+	  functions = XCDR (functions);
+	}
+
+      window_set_old_pixel_sizes (r);
+      FRAME_WINDOW_CONFIGURATION_CHANGED (f) = false;
+    }
+}
+
+
 /* Make WINDOW display BUFFER.  RUN_HOOKS_P means it's allowed
    to run hooks.  See make_frame for a case where it's not allowed.
    KEEP_MARGINS_P means that the current margins, fringes, and
@@ -3263,15 +3356,9 @@ set_window_buffer (Lisp_Object window, Lisp_Object buffer,

   if (!(keep_margins_p && samebuf))
     { /* If we're not actually changing the buffer, don't reset hscroll
-	 and vscroll.  This case happens for example when called from
-	 change_frame_size_1, where we use a dummy call to
-	 Fset_window_buffer on the frame's selected window (and no
-	 other) just in order to run window-configuration-change-hook
-	 (no longer true since change_frame_size_1 directly calls
-	 run_window_configuration_change_hook).  Resetting hscroll and
-	 vscroll here is problematic for things like image-mode and
-	 doc-view-mode since it resets the image's position whenever we
-	 resize the frame.  */
+	 and vscroll.  Resetting hscroll and vscroll here is problematic
+	 for things like image-mode and doc-view-mode since it resets
+	 the image's position whenever we resize the frame.  */
       w->hscroll = w->min_hscroll = w->hscroll_whole = 0;
       w->suspend_auto_hscroll = false;
       w->vscroll = 0;
@@ -3283,10 +3370,8 @@ set_window_buffer (Lisp_Object window, Lisp_Object buffer,
       w->start_at_line_beg = false;
       w->force_start = false;
     }
-  /* Maybe we could move this into the `if' but it's not obviously safe and
-     I doubt it's worth the trouble.  */
-  wset_redisplay (w);

+  wset_redisplay (w);
   wset_update_mode_line (w);

   /* We must select BUFFER to run the window-scroll-functions and to look up
@@ -3314,7 +3399,7 @@ set_window_buffer (Lisp_Object window, Lisp_Object buffer,

   if (run_hooks_p)
     {
-      if (! NILP (Vwindow_scroll_functions))
+      if (!NILP (Vwindow_scroll_functions))
 	run_hook_with_args_2 (Qwindow_scroll_functions, window,
 			      Fmarker_position (w->start));
       if (!samebuf)
@@ -3559,6 +3644,8 @@ make_window (void)
   w->phys_cursor_width = -1;
 #endif
   w->sequence_number = ++sequence_number;
+  w->old_pixel_width = 0;
+  w->old_pixel_height = 0;
   w->scroll_bar_width = -1;
   w->scroll_bar_height = -1;
   w->column_number_displayed = -1;
@@ -3922,7 +4009,6 @@ be applied on the Elisp level.  */)
   window_resize_apply (r, horflag);

   fset_redisplay (f);
-  FRAME_WINDOW_SIZES_CHANGED (f) = true;

   adjust_frame_glyphs (f);
   unblock_input ();
@@ -4087,7 +4173,6 @@ resize_frame_windows (struct frame *f, int size, bool horflag, bool pixelwise)
 	}
     }

-  FRAME_WINDOW_SIZES_CHANGED (f) = true;
   fset_redisplay (f);
 }

@@ -4214,7 +4299,6 @@ set correctly.  See the code of `split-window' for how this is done.  */)
     p = XWINDOW (o->parent);

   fset_redisplay (f);
-  FRAME_WINDOW_SIZES_CHANGED (f) = true;
   new = make_window ();
   n = XWINDOW (new);
   wset_frame (n, frame);
@@ -4383,7 +4467,6 @@ Signal an error when WINDOW is the only window on its frame.  */)

       fset_redisplay (f);
       Vwindow_list = Qnil;
-      FRAME_WINDOW_SIZES_CHANGED (f) = true;

       wset_next (w, Qnil);  /* Don't delete w->next too.  */
       free_window_matrices (w);
@@ -4451,9 +4534,6 @@ Signal an error when WINDOW is the only window on its frame.  */)
 	}
       else
 	unblock_input ();
-
-      /* Must be run by the caller:
-	 run_window_configuration_change_hook (f);  */
     }
   else
     /* We failed: Relink WINDOW into window tree.  */
@@ -4527,7 +4607,6 @@ grow_mini_window (struct window *w, int delta, bool pixelwise)
 	  /* Enforce full redisplay of the frame.  */
 	  /* FIXME: Shouldn't window--resize-root-window-vertically do it?  */
 	  fset_redisplay (f);
-	  FRAME_WINDOW_SIZES_CHANGED (f) = true;
 	  adjust_frame_glyphs (f);
 	  unblock_input ();
 	}
@@ -4567,7 +4646,6 @@ shrink_mini_window (struct window *w, bool pixelwise)
 	  /* Enforce full redisplay of the frame.  */
 	  /* FIXME: Shouldn't window--resize-root-window-vertically do it?  */
 	  fset_redisplay (f);
-	  FRAME_WINDOW_SIZES_CHANGED (f) = true;
 	  adjust_frame_glyphs (f);
 	  unblock_input ();
 	}
@@ -4610,7 +4688,6 @@ DEFUN ("resize-mini-window-internal", Fresize_mini_window_internal, Sresize_mini
       w->top_line = r->top_line + r->total_lines;

       fset_redisplay (f);
-      FRAME_WINDOW_SIZES_CHANGED (f) = true;
       adjust_frame_glyphs (f);
       unblock_input ();
       return Qt;
@@ -5948,6 +6025,7 @@ struct saved_window

   Lisp_Object window, buffer, start, pointm, old_pointm;
   Lisp_Object pixel_left, pixel_top, pixel_height, pixel_width;
+  Lisp_Object old_pixel_height, old_pixel_width;
   Lisp_Object left_col, top_line, total_cols, total_lines;
   Lisp_Object normal_cols, normal_lines;
   Lisp_Object hscroll, min_hscroll, hscroll_whole, suspend_auto_hscroll;
@@ -6063,6 +6141,12 @@ the return value is nil.  Otherwise the value is t.  */)
       struct window *root_window;
       struct window **leaf_windows;
       ptrdiff_t i, k, n_leaf_windows;
+      /* Records whether a window has been added or removed wrt the
+	 original configuration.  */
+      bool window_changed = false;
+      /* Records whether a window has changed its buffer wrt the
+	 original configuration.  */
+      bool buffer_changed = false;

       /* Don't do this within the main loop below: This may call Lisp
 	 code and is thus potentially unsafe while input is blocked.  */
@@ -6071,12 +6155,18 @@ the return value is nil.  Otherwise the value is t.  */)
 	  p = SAVED_WINDOW_N (saved_windows, k);
 	  window = p->window;
 	  w = XWINDOW (window);
+
+	  if (NILP (w->contents))
+	    /* A dead window that will be resurrected, the window
+	       configuration will change.  */
+	    window_changed = true;
+
 	  if (BUFFERP (w->contents)
 	      && !EQ (w->contents, p->buffer)
 	      && BUFFER_LIVE_P (XBUFFER (p->buffer)))
 	    /* If a window we restore gets another buffer, record the
 	       window's old buffer.  */
-	    call1 (Qrecord_window_buffer, window);
+	      call1 (Qrecord_window_buffer, window);
 	}

       /* Disallow x_set_window_size, temporarily.  */
@@ -6100,7 +6190,6 @@ the return value is nil.  Otherwise the value is t.  */)
 	}

       fset_redisplay (f);
-      FRAME_WINDOW_SIZES_CHANGED (f) = true;

       /* Problem: Freeing all matrices and later allocating them again
 	 is a serious redisplay flickering problem.  What we would
@@ -6156,6 +6245,8 @@ the return value is nil.  Otherwise the value is t.  */)
 	  w->pixel_top = XFASTINT (p->pixel_top);
 	  w->pixel_width = XFASTINT (p->pixel_width);
 	  w->pixel_height = XFASTINT (p->pixel_height);
+	  w->old_pixel_width = XFASTINT (p->old_pixel_width);
+	  w->old_pixel_height = XFASTINT (p->old_pixel_height);
 	  w->left_col = XFASTINT (p->left_col);
 	  w->top_line = XFASTINT (p->top_line);
 	  w->total_cols = XFASTINT (p->total_cols);
@@ -6203,6 +6294,9 @@ the return value is nil.  Otherwise the value is t.  */)
 	  if (BUFFERP (p->buffer) && BUFFER_LIVE_P (XBUFFER (p->buffer)))
 	    /* If saved buffer is alive, install it.  */
 	    {
+	      if (!EQ (w->contents, p->buffer))
+		/* Record buffer configuration change.  */
+		buffer_changed = true;
 	      wset_buffer (w, p->buffer);
 	      w->start_at_line_beg = !NILP (p->start_at_line_beg);
 	      set_marker_restricted (w->start, p->start, w->contents);
@@ -6236,6 +6330,8 @@ the return value is nil.  Otherwise the value is t.  */)
 	  else if (!NILP (w->start))
 	    /* Leaf window has no live buffer, get one.  */
 	    {
+	      /* Record buffer configuration change.  */
+	      buffer_changed = true;
 	      /* Get the buffer via other_buffer_safely in order to
 		 avoid showing an unimportant buffer and, if necessary, to
 		 recreate *scratch* in the course (part of Juanma's bs-show
@@ -6283,7 +6379,10 @@ the return value is nil.  Otherwise the value is t.  */)
       /* Now, free glyph matrices in windows that were not reused.  */
       for (i = 0; i < n_leaf_windows; i++)
 	if (NILP (leaf_windows[i]->contents))
-	  free_window_matrices (leaf_windows[i]);
+	  {
+	    free_window_matrices (leaf_windows[i]);
+	    window_changed = true;
+	  }

       /* Allow x_set_window_size again and apply frame size changes if
 	 needed.  */
@@ -6303,7 +6402,8 @@ the return value is nil.  Otherwise the value is t.  */)

       /* Record the selected window's buffer here.  The window should
 	 already be the selected one from the call above.  */
-      select_window (data->current_window, Qnil, false);
+      if (WINDOW_LIVE_P (data->current_window))
+	select_window (data->current_window, Qnil, false);

       /* Fselect_window will have made f the selected frame, so we
 	 reselect the proper frame here.  Fhandle_switch_frame will change the
@@ -6313,7 +6413,32 @@ the return value is nil.  Otherwise the value is t.  */)
       if (FRAME_LIVE_P (XFRAME (data->selected_frame)))
 	do_switch_frame (data->selected_frame, 0, 0, Qnil);

-      run_window_configuration_change_hook (f);
+      if (window_changed)
+	/* At least one window has been added or removed.  Run
+	   `window-configuration-change-hook' and make sure
+	   `window-size-change-functions' get run later.
+
+	   We have to do this in order to capture the following
+	   scenario: Suppose our frame contains two live windows W1 and
+	   W2 and ‘set-window-configuration’ replaces them by two
+	   windows W3 and W4 that were dead the last time
+	   run_window_size_change_functions was run.  If W3 and W4 have
+	   the same values for their old and new pixel sizes but these
+	   values differ from those of W1 and W2, the sizes of our
+	   frame's two live windows changed but window_size_changed has
+	   no means to detect that fact.
+
+	   Obviously, this will get us false positives, for example,
+	   when we restore the original configuration with W1 and W2
+	   before run_window_size_change_functions gets called.  */
+	{
+	  run_window_configuration_change_hook (f);
+	  FRAME_WINDOW_CONFIGURATION_CHANGED (f) = true;
+	}
+      else if (buffer_changed)
+	/* At least one window has changed its buffer.  Run
+	   `window-configuration-change-hook' only.  */
+	run_window_configuration_change_hook (f);
     }

   if (!NILP (new_current_buffer))
@@ -6464,6 +6589,8 @@ save_window_save (Lisp_Object window, struct Lisp_Vector *vector, ptrdiff_t i)
       p->pixel_top = make_number (w->pixel_top);
       p->pixel_width = make_number (w->pixel_width);
       p->pixel_height = make_number (w->pixel_height);
+      p->old_pixel_width = make_number (w->old_pixel_width);
+      p->old_pixel_height = make_number (w->old_pixel_height);
       p->left_col = make_number (w->left_col);
       p->top_line = make_number (w->top_line);
       p->total_cols = make_number (w->total_cols);
@@ -7246,6 +7373,16 @@ selected; while the global part is run only once for the modified frame,
 with the relevant frame selected.  */);
   Vwindow_configuration_change_hook = Qnil;

+  DEFVAR_LISP ("window-size-change-functions", Vwindow_size_change_functions,
+    doc: /* Functions called during redisplay, if window sizes have changed.
+The value should be a list of functions that take one 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 ("recenter-redisplay", Vrecenter_redisplay,
 	       doc: /* Non-nil means `recenter' redraws entire frame.
 If this option is non-nil, then the `recenter' command with a nil
@@ -7374,6 +7511,8 @@ displayed after a scrolling operation to be somewhat inaccurate.  */);
   defsubr (&Swindow_use_time);
   defsubr (&Swindow_pixel_width);
   defsubr (&Swindow_pixel_height);
+  defsubr (&Swindow_old_pixel_width);
+  defsubr (&Swindow_old_pixel_height);
   defsubr (&Swindow_total_width);
   defsubr (&Swindow_total_height);
   defsubr (&Swindow_normal_size);
diff --git a/src/window.h b/src/window.h
index c29207d..dea1d19 100644
--- a/src/window.h
+++ b/src/window.h
@@ -214,6 +214,11 @@ struct window
     int pixel_width;
     int pixel_height;

+    /* The pixel sizes of the window at the last time
+       `window-size-change-functions' was run.  */
+    int old_pixel_width;
+    int old_pixel_height;
+
     /* The size of the window.  */
     int total_cols;
     int total_lines;
@@ -499,15 +504,17 @@ wset_next_buffers (struct window *w, Lisp_Object val)
 #define WINDOW_LEAF_P(W) \
   (BUFFERP ((W)->contents))

-/* True if W is a member of horizontal combination.  */
+/* Non-nil if W is internal.  */
+#define WINDOW_INTERNAL_P(W) \
+  (WINDOWP ((W)->contents))

+/* True if W is a member of horizontal combination.  */
 #define WINDOW_HORIZONTAL_COMBINATION_P(W) \
-  (WINDOWP ((W)->contents) && (W)->horizontal)
+  (WINDOW_INTERNAL_P (W) && (W)->horizontal)

 /* True if W is a member of vertical combination.  */
-
 #define WINDOW_VERTICAL_COMBINATION_P(W) \
-  (WINDOWP ((W)->contents) && !(W)->horizontal)
+  (WINDOW_INTERNAL_P (W) && !(W)->horizontal)

 /* WINDOW's XFRAME.  */
 #define WINDOW_XFRAME(W) (XFRAME (WINDOW_FRAME ((W))))
@@ -1014,6 +1021,7 @@ extern void shrink_mini_window (struct window *, bool);
 extern int window_relative_x_coord (struct window *, enum window_part, int);

 void run_window_configuration_change_hook (struct frame *f);
+void run_window_size_change_functions (Lisp_Object);

 /* Make WINDOW display BUFFER.  RUN_HOOKS_P means it's allowed
    to run hooks.  See make_frame for a case where it's not allowed.  */
diff --git a/src/xdisp.c b/src/xdisp.c
index fed5879..4330f10 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -11786,24 +11786,7 @@ prepare_menu_bars (void)
 	      && !XBUFFER (w->contents)->text->redisplay)
 	    continue;

-	  /* If a window on this frame changed size, report that to
-	     the user and clear the size-change flag.  */
-	  if (FRAME_WINDOW_SIZES_CHANGED (f))
-	    {
-	      Lisp_Object functions;
-
-	      /* Clear flag first in case we get an error below.  */
-	      FRAME_WINDOW_SIZES_CHANGED (f) = false;
-	      functions = Vwindow_size_change_functions;
-
-	      while (CONSP (functions))
-		{
-		  if (!EQ (XCAR (functions), Qt))
-		    call1 (XCAR (functions), frame);
-		  functions = XCDR (functions);
-		}
-	    }
-
+	  run_window_size_change_functions (frame);
 	  menu_bar_hooks_run = update_menu_bar (f, false, menu_bar_hooks_run);
 #ifdef HAVE_WINDOW_SYSTEM
 	  update_tool_bar (f, false);
@@ -13599,24 +13582,12 @@ redisplay_internal (void)
 	 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))
+      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);
-	    }
-
+	  run_window_size_change_functions (selected_frame);
 	  unbind_to (count1, Qnil);
 	}

@@ -13638,22 +13609,10 @@ redisplay_internal (void)
     {
       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);
-	    }
-
+	  run_window_size_change_functions (selected_frame);
 	  unbind_to (count1, Qnil);
 	}

@@ -31447,16 +31406,6 @@ If nil, disable message logging.  If t, log messages but don't truncate
 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 during redisplay, if window sizes have changed.
-The value should be a list of functions that take one 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,
     doc: /* List of functions to call before redisplaying a window with scrolling.
 Each function is called with two arguments, the window and its new
diff --git a/src/xfns.c b/src/xfns.c
index 20ac627..2a50a5a 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -1313,7 +1313,6 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
     }
 #endif /* not USE_X_TOOLKIT && not USE_GTK */
   adjust_frame_glyphs (f);
-  run_window_configuration_change_hook (f);
 }



c:\emacs-git\quick>c:/Programme/MinGW/msys/1.0/bin/bash.exe --login -i -c  "cd /c/emacs-git/quick/ ; git status"
c:/Programme/MinGW/msys/1.0/bin/bash.exe --login -i -c  "cd /c/emacs-git/quick/ ; git status"
On branch master
Your branch is up-to-date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   doc/lispref/windows.texi
	modified:   lisp/window.el
	modified:   src/frame.c
	modified:   src/frame.h
	modified:   src/window.c
	modified:   src/window.h
	modified:   src/xdisp.c
	modified:   src/xfns.c

no changes added to commit (use "git add" and/or "git commit -a")

c:\emacs-git\quick>c:/Programme/MinGW/msys/1.0/bin/bash.exe --login -i -c  "cd /c/emacs-git/quick/ ; git diff"
c:/Programme/MinGW/msys/1.0/bin/bash.exe --login -i -c  "cd /c/emacs-git/quick/ ; git diff"
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index f61f08a..eac3bd6 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -545,6 +545,12 @@ Window Sizes
 children.
 @end defun

+@defun window-old-pixel-height &optional Lisp_Object &optional window
+This function returns the height of window @var{window} in pixels at the
+time @code{window-size-change-functions} was run for the last time on
+@var{window}'s frame (@pxref{Window Hooks}).
+@end defun
+
 @cindex window pixel width
 @cindex pixel width of a window
 @cindex total pixel width of a window
@@ -559,6 +565,12 @@ Window Sizes
 the screen areas spanned by its children.
 @end defun

+@defun window-old-pixel-width &optional Lisp_Object &optional window
+This function returns the width of window @var{window} in pixels at the
+time @code{window-size-change-functions} was run for the last time on
+@var{window}'s frame (@pxref{Window Hooks}).
+@end defun
+
 @cindex full-width window
 @cindex full-height window
   The following functions can be used to determine whether a given
@@ -4087,11 +4099,11 @@ Window Configurations
 The argument @var{configuration} must be a value that was previously
 returned by @code{current-window-configuration}.  The configuration is
 restored in the frame from which @var{configuration} was made, whether
-that frame is selected or not.  This always counts as a window size
-change and triggers execution of the @code{window-size-change-functions}
-(@pxref{Window Hooks}), because @code{set-window-configuration} doesn't
-know how to tell whether the new configuration actually differs from the
-old one.
+that frame is selected or not.  In some rare cases this may trigger
+execution of the @code{window-size-change-functions} (@pxref{Window
+Hooks}) even if the size of windows did not change at all.  The
+@code{window-configuration-change-hook} functions will be called if and
+only if at least one window was added to or deleted from the frame.

 If the frame from which @var{configuration} was saved is dead, all this
 function does is restore the three variables @code{window-min-height},
@@ -4378,33 +4390,37 @@ Window Hooks
 @end defvar

 @defvar window-size-change-functions
-This variable holds a list of functions to be called if the size of
-any window changes for any reason.  The functions are called at the
-beginning of a redisplay cycle, and just once for each frame on which
-size changes have occurred.
-
-Each function receives the frame as its sole argument.  There is no
-direct way to find out which windows on that frame have changed size, or
-precisely how.  However, if a size-change function records, at each
-call, the existing windows and their sizes, it can also compare the
-present sizes and the previous sizes.
-
-Creating or deleting windows counts as a size change, and therefore
-causes these functions to be called.  Changing the frame size also
-counts, because it changes the sizes of the existing windows.
+This variable holds a list of functions to be called if the size of any
+window changes for any reason.  The functions are called once per
+redisplay, and once for each frame on which size changes have occurred.
+
+Each function receives the frame as its sole argument.  To find out
+whether a specific window has changed size, compare the return values of
+@code{window-old-pixel-width} and @code{window-pixel-width} respectively
+@code{window-old-pixel-height} and @code{window-pixel-height} for that
+window (@pxref{Window Sizes}).
+
+These function are usually only called when at least one window was
+added or has changed size since the last time this hook was run for the
+associated frame.  In some rare cases this hook also runs when a window
+that was added intermittently has been deleted afterwards.  In these
+cases none of the windows on the frame will appear to have changed its
+size.

 You may use @code{save-selected-window} in these functions
 (@pxref{Selecting Windows}).  However, do not use
 @code{save-window-excursion} (@pxref{Window Configurations}); exiting
-that macro counts as a size change, which would cause these functions
-to be called over and over.
+that macro counts as a size change, which would cause these functions to
+be called again.
 @end defvar

 @defvar window-configuration-change-hook
-A normal hook that is run every time you change the window configuration
-of an existing frame.  This includes splitting or deleting windows,
-changing the sizes of windows, or displaying a different buffer in a
-window.
+A normal hook that is run every time the window configuration of a frame
+changes.  Window configuration changes include splitting and deleting
+windows and the display of a different buffer in a window.  Resizing the
+frame or individual windows do not count as configuration changes.  Use
+@code{window-size-change-functions}, see above, when you want to track
+size changes that are not caused by the deletion or creation of windows.

 The buffer-local part of this hook is run once for each window on the
 affected frame, with the relevant window selected and its buffer
diff --git a/src/frame.c b/src/frame.c
index 8c86afe..df473ae 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -591,8 +591,6 @@ adjust_frame_size (struct frame *f, int new_width, int new_height, int inhibit,
 		  || new_pixel_height != old_pixel_height);

   unblock_input ();
-
-  run_window_configuration_change_hook (f);
 }

 /* Allocate basically initialized frame.  */
diff --git a/src/frame.h b/src/frame.h
index 71dab4b..d9424ab 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -288,8 +288,9 @@ struct frame
      cleared.  */
   bool_bf explicit_name : 1;

-  /* True if size of some window on this frame has changed.  */
-  bool_bf window_sizes_changed : 1;
+  /* True if configuration of windows on this frame has changed since
+     last call of run_window_size_change_functions.  */
+  bool_bf window_configuration_changed : 1;

   /* True if the mouse has moved on this display device
      since the last time we checked.  */
@@ -828,10 +829,10 @@ default_pixels_per_inch_y (void)
    are frozen on frame F.  */
 #define FRAME_WINDOWS_FROZEN(f) (f)->frozen_window_starts

-/* True if a size change has been requested for frame F
-   but not yet really put into effect.  This can be true temporarily
-   when an X event comes in at a bad time.  */
-#define FRAME_WINDOW_SIZES_CHANGED(f) (f)->window_sizes_changed
+/* True if the frame's window configuration has changed since last call
+   of run_window_size_change_functions.  */
+#define FRAME_WINDOW_CONFIGURATION_CHANGED(f)	\
+  ((f)->window_configuration_changed)

 /* The minibuffer window of frame F, if it has one; otherwise nil.  */
 #define FRAME_MINIBUF_WINDOW(f) f->minibuffer_window
diff --git a/src/window.c b/src/window.c
index e1a30ee..5b03a24 100644
--- a/src/window.c
+++ b/src/window.c
@@ -720,6 +720,30 @@ the height of the screen areas spanned by its children.  */)
   return make_number (decode_valid_window (window)->pixel_height);
 }

+DEFUN ("window-old-pixel-width", Fwindow_old_pixel_width, Swindow_old_pixel_width, 0, 1, 0,
+       doc: /* Return the old width of window WINDOW in pixels.
+WINDOW must be a valid window and defaults to the selected one.
+
+The return value is the pixel width of WINDOW at the last time
+`window-size-change-functions' was run.  It's zero if WINDOW was made
+after that.  */)
+  (Lisp_Object window)
+{
+  return make_number (decode_valid_window (window)->old_pixel_width);
+}
+
+DEFUN ("window-old-pixel-height", Fwindow_old_pixel_height, Swindow_old_pixel_height, 0, 1, 0,
+       doc: /* Return the old height of window WINDOW in pixels.
+WINDOW must be a valid window and defaults to the selected one.
+
+The return value is the pixel height of WINDOW at the last time
+`window-size-change-functions' was run.  It's zero if WINDOW was made
+after that.  */)
+  (Lisp_Object window)
+{
+  return make_number (decode_valid_window (window)->old_pixel_height);
+}
+
 DEFUN ("window-total-height", Fwindow_total_height, Swindow_total_height, 0, 2, 0,
        doc: /* Return the height of window WINDOW in lines.
 WINDOW must be a valid window and defaults to the selected one.
@@ -2879,6 +2903,7 @@ window-start value is reasonable when this function is called.  */)
   Lisp_Object sibling, pwindow, swindow IF_LINT (= Qnil), delta;
   ptrdiff_t startpos IF_LINT (= 0), startbyte IF_LINT (= 0);
   int top IF_LINT (= 0), new_top;
+  bool resize_failed = false;

   w = decode_valid_window (window);
   XSETWINDOW (window, w);
@@ -2978,8 +3003,6 @@ window-start value is reasonable when this function is called.  */)

   fset_redisplay (f);
   Vwindow_list = Qnil;
-  FRAME_WINDOW_SIZES_CHANGED (f) = true;
-  bool resize_failed = false;

   if (!WINDOW_LEAF_P (w))
     {
@@ -3229,6 +3252,76 @@ If WINDOW is omitted or nil, it defaults to the selected window.  */)
   return Qnil;
 }

+
+/* Compare old and present pixel sizes of windows in tree rooted at W.
+   Return true iff any of these windows differs in size.  */
+
+static bool
+window_size_changed (struct window *w)
+{
+  if (w->pixel_width != w->old_pixel_width
+      || w->pixel_height != w->old_pixel_height)
+    return true;
+
+  if (WINDOW_INTERNAL_P (w))
+    {
+      w = XWINDOW (w->contents);
+      while (w)
+	{
+	  if (window_size_changed (w))
+	    return true;
+
+	  w = NILP (w->next) ? 0 : XWINDOW (w->next);
+	}
+    }
+
+  return false;
+}
+
+/* Set old pixel sizes of windows in tree rooted at W to their present
+   pixel sizes.  */
+
+static void
+window_set_old_pixel_sizes (struct window *w)
+{
+  w->old_pixel_width = w->pixel_width;
+  w->old_pixel_height = w->pixel_height;
+
+  if (WINDOW_INTERNAL_P (w))
+    {
+      w = XWINDOW (w->contents);
+      while (w)
+	{
+	  window_set_old_pixel_sizes (w);
+	  w = NILP (w->next) ? 0 : XWINDOW (w->next);
+	}
+    }
+}
+
+
+void
+run_window_size_change_functions (Lisp_Object frame)
+{
+  struct frame *f = XFRAME (frame);
+  struct window *r = XWINDOW (FRAME_ROOT_WINDOW (f));
+  Lisp_Object functions = Vwindow_size_change_functions;
+
+  if (FRAME_WINDOW_CONFIGURATION_CHANGED (f) ||
+      window_size_changed (r))
+    {
+      while (CONSP (functions))
+	{
+	  if (!EQ (XCAR (functions), Qt))
+	    call1 (XCAR (functions), frame);
+	  functions = XCDR (functions);
+	}
+
+      window_set_old_pixel_sizes (r);
+      FRAME_WINDOW_CONFIGURATION_CHANGED (f) = false;
+    }
+}
+
+
 /* Make WINDOW display BUFFER.  RUN_HOOKS_P means it's allowed
    to run hooks.  See make_frame for a case where it's not allowed.
    KEEP_MARGINS_P means that the current margins, fringes, and
@@ -3263,15 +3356,9 @@ set_window_buffer (Lisp_Object window, Lisp_Object buffer,

   if (!(keep_margins_p && samebuf))
     { /* If we're not actually changing the buffer, don't reset hscroll
-	 and vscroll.  This case happens for example when called from
-	 change_frame_size_1, where we use a dummy call to
-	 Fset_window_buffer on the frame's selected window (and no
-	 other) just in order to run window-configuration-change-hook
-	 (no longer true since change_frame_size_1 directly calls
-	 run_window_configuration_change_hook).  Resetting hscroll and
-	 vscroll here is problematic for things like image-mode and
-	 doc-view-mode since it resets the image's position whenever we
-	 resize the frame.  */
+	 and vscroll.  Resetting hscroll and vscroll here is problematic
+	 for things like image-mode and doc-view-mode since it resets
+	 the image's position whenever we resize the frame.  */
       w->hscroll = w->min_hscroll = w->hscroll_whole = 0;
       w->suspend_auto_hscroll = false;
       w->vscroll = 0;
@@ -3283,10 +3370,8 @@ set_window_buffer (Lisp_Object window, Lisp_Object buffer,
       w->start_at_line_beg = false;
       w->force_start = false;
     }
-  /* Maybe we could move this into the `if' but it's not obviously safe and
-     I doubt it's worth the trouble.  */
-  wset_redisplay (w);

+  wset_redisplay (w);
   wset_update_mode_line (w);

   /* We must select BUFFER to run the window-scroll-functions and to look up
@@ -3314,7 +3399,7 @@ set_window_buffer (Lisp_Object window, Lisp_Object buffer,

   if (run_hooks_p)
     {
-      if (! NILP (Vwindow_scroll_functions))
+      if (!NILP (Vwindow_scroll_functions))
 	run_hook_with_args_2 (Qwindow_scroll_functions, window,
 			      Fmarker_position (w->start));
       if (!samebuf)
@@ -3559,6 +3644,8 @@ make_window (void)
   w->phys_cursor_width = -1;
 #endif
   w->sequence_number = ++sequence_number;
+  w->old_pixel_width = 0;
+  w->old_pixel_height = 0;
   w->scroll_bar_width = -1;
   w->scroll_bar_height = -1;
   w->column_number_displayed = -1;
@@ -3922,7 +4009,6 @@ be applied on the Elisp level.  */)
   window_resize_apply (r, horflag);

   fset_redisplay (f);
-  FRAME_WINDOW_SIZES_CHANGED (f) = true;

   adjust_frame_glyphs (f);
   unblock_input ();
@@ -4087,7 +4173,6 @@ resize_frame_windows (struct frame *f, int size, bool horflag, bool pixelwise)
 	}
     }

-  FRAME_WINDOW_SIZES_CHANGED (f) = true;
   fset_redisplay (f);
 }

@@ -4214,7 +4299,6 @@ set correctly.  See the code of `split-window' for how this is done.  */)
     p = XWINDOW (o->parent);

   fset_redisplay (f);
-  FRAME_WINDOW_SIZES_CHANGED (f) = true;
   new = make_window ();
   n = XWINDOW (new);
   wset_frame (n, frame);
@@ -4383,7 +4467,6 @@ Signal an error when WINDOW is the only window on its frame.  */)

       fset_redisplay (f);
       Vwindow_list = Qnil;
-      FRAME_WINDOW_SIZES_CHANGED (f) = true;

       wset_next (w, Qnil);  /* Don't delete w->next too.  */
       free_window_matrices (w);
@@ -4451,9 +4534,6 @@ Signal an error when WINDOW is the only window on its frame.  */)
 	}
       else
 	unblock_input ();
-
-      /* Must be run by the caller:
-	 run_window_configuration_change_hook (f);  */
     }
   else
     /* We failed: Relink WINDOW into window tree.  */
@@ -4527,7 +4607,6 @@ grow_mini_window (struct window *w, int delta, bool pixelwise)
 	  /* Enforce full redisplay of the frame.  */
 	  /* FIXME: Shouldn't window--resize-root-window-vertically do it?  */
 	  fset_redisplay (f);
-	  FRAME_WINDOW_SIZES_CHANGED (f) = true;
 	  adjust_frame_glyphs (f);
 	  unblock_input ();
 	}
@@ -4567,7 +4646,6 @@ shrink_mini_window (struct window *w, bool pixelwise)
 	  /* Enforce full redisplay of the frame.  */
 	  /* FIXME: Shouldn't window--resize-root-window-vertically do it?  */
 	  fset_redisplay (f);
-	  FRAME_WINDOW_SIZES_CHANGED (f) = true;
 	  adjust_frame_glyphs (f);
 	  unblock_input ();
 	}
@@ -4610,7 +4688,6 @@ DEFUN ("resize-mini-window-internal", Fresize_mini_window_internal, Sresize_mini
       w->top_line = r->top_line + r->total_lines;

       fset_redisplay (f);
-      FRAME_WINDOW_SIZES_CHANGED (f) = true;
       adjust_frame_glyphs (f);
       unblock_input ();
       return Qt;
@@ -5948,6 +6025,7 @@ struct saved_window

   Lisp_Object window, buffer, start, pointm, old_pointm;
   Lisp_Object pixel_left, pixel_top, pixel_height, pixel_width;
+  Lisp_Object old_pixel_height, old_pixel_width;
   Lisp_Object left_col, top_line, total_cols, total_lines;
   Lisp_Object normal_cols, normal_lines;
   Lisp_Object hscroll, min_hscroll, hscroll_whole, suspend_auto_hscroll;
@@ -6063,6 +6141,12 @@ the return value is nil.  Otherwise the value is t.  */)
       struct window *root_window;
       struct window **leaf_windows;
       ptrdiff_t i, k, n_leaf_windows;
+      /* Records whether a window has been added or removed wrt the
+	 original configuration.  */
+      bool window_changed = false;
+      /* Records whether a window has changed its buffer wrt the
+	 original configuration.  */
+      bool buffer_changed = false;

       /* Don't do this within the main loop below: This may call Lisp
 	 code and is thus potentially unsafe while input is blocked.  */
@@ -6071,6 +6155,12 @@ the return value is nil.  Otherwise the value is t.  */)
 	  p = SAVED_WINDOW_N (saved_windows, k);
 	  window = p->window;
 	  w = XWINDOW (window);
+
+	  if (NILP (w->contents))
+	    /* A dead window that will be resurrected, the window
+	       configuration will change.  */
+	    window_changed = true;
+
 	  if (BUFFERP (w->contents)
 	      && !EQ (w->contents, p->buffer)
 	      && BUFFER_LIVE_P (XBUFFER (p->buffer)))
@@ -6100,7 +6190,6 @@ the return value is nil.  Otherwise the value is t.  */)
 	}

       fset_redisplay (f);
-      FRAME_WINDOW_SIZES_CHANGED (f) = true;

       /* Problem: Freeing all matrices and later allocating them again
 	 is a serious redisplay flickering problem.  What we would
@@ -6156,6 +6245,8 @@ the return value is nil.  Otherwise the value is t.  */)
 	  w->pixel_top = XFASTINT (p->pixel_top);
 	  w->pixel_width = XFASTINT (p->pixel_width);
 	  w->pixel_height = XFASTINT (p->pixel_height);
+	  w->old_pixel_width = XFASTINT (p->old_pixel_width);
+	  w->old_pixel_height = XFASTINT (p->old_pixel_height);
 	  w->left_col = XFASTINT (p->left_col);
 	  w->top_line = XFASTINT (p->top_line);
 	  w->total_cols = XFASTINT (p->total_cols);
@@ -6203,6 +6294,9 @@ the return value is nil.  Otherwise the value is t.  */)
 	  if (BUFFERP (p->buffer) && BUFFER_LIVE_P (XBUFFER (p->buffer)))
 	    /* If saved buffer is alive, install it.  */
 	    {
+	      if (!EQ (w->contents, p->buffer))
+		/* Record buffer configuration change.  */
+		buffer_changed = true;
 	      wset_buffer (w, p->buffer);
 	      w->start_at_line_beg = !NILP (p->start_at_line_beg);
 	      set_marker_restricted (w->start, p->start, w->contents);
@@ -6236,6 +6330,8 @@ the return value is nil.  Otherwise the value is t.  */)
 	  else if (!NILP (w->start))
 	    /* Leaf window has no live buffer, get one.  */
 	    {
+	      /* Record buffer configuration change.  */
+	      buffer_changed = true;
 	      /* Get the buffer via other_buffer_safely in order to
 		 avoid showing an unimportant buffer and, if necessary, to
 		 recreate *scratch* in the course (part of Juanma's bs-show
@@ -6283,7 +6379,10 @@ the return value is nil.  Otherwise the value is t.  */)
       /* Now, free glyph matrices in windows that were not reused.  */
       for (i = 0; i < n_leaf_windows; i++)
 	if (NILP (leaf_windows[i]->contents))
-	  free_window_matrices (leaf_windows[i]);
+	  {
+	    free_window_matrices (leaf_windows[i]);
+	    window_changed = true;
+	  }

       /* Allow x_set_window_size again and apply frame size changes if
 	 needed.  */
@@ -6303,7 +6402,8 @@ the return value is nil.  Otherwise the value is t.  */)

       /* Record the selected window's buffer here.  The window should
 	 already be the selected one from the call above.  */
-      select_window (data->current_window, Qnil, false);
+      if (WINDOW_LIVE_P (data->current_window))
+	select_window (data->current_window, Qnil, false);

       /* Fselect_window will have made f the selected frame, so we
 	 reselect the proper frame here.  Fhandle_switch_frame will change the
@@ -6313,7 +6413,32 @@ the return value is nil.  Otherwise the value is t.  */)
       if (FRAME_LIVE_P (XFRAME (data->selected_frame)))
 	do_switch_frame (data->selected_frame, 0, 0, Qnil);

-      run_window_configuration_change_hook (f);
+      if (window_changed)
+	/* At least one window has been added or removed.  Run
+	   `window-configuration-change-hook' and make sure
+	   `window-size-change-functions' get run later.
+
+	   We have to do this in order to capture the following
+	   scenario: Suppose our frame contains two live windows W1 and
+	   W2 and ‘set-window-configuration’ replaces them by two
+	   windows W3 and W4 that were dead the last time
+	   run_window_size_change_functions was run.  If W3 and W4 have
+	   the same values for their old and new pixel sizes but these
+	   values differ from those of W1 and W2, the sizes of our
+	   frame's two live windows changed but window_size_changed has
+	   no means to detect that fact.
+
+	   Obviously, this will get us false positives, for example,
+	   when we restore the original configuration with W1 and W2
+	   before run_window_size_change_functions gets called.  */
+	{
+	  run_window_configuration_change_hook (f);
+	  FRAME_WINDOW_CONFIGURATION_CHANGED (f) = true;
+	}
+      else if (buffer_changed)
+	/* At least one window has changed its buffer.  Run
+	   `window-configuration-change-hook' only.  */
+	run_window_configuration_change_hook (f);
     }

   if (!NILP (new_current_buffer))
@@ -6464,6 +6589,8 @@ save_window_save (Lisp_Object window, struct Lisp_Vector *vector, ptrdiff_t i)
       p->pixel_top = make_number (w->pixel_top);
       p->pixel_width = make_number (w->pixel_width);
       p->pixel_height = make_number (w->pixel_height);
+      p->old_pixel_width = make_number (w->old_pixel_width);
+      p->old_pixel_height = make_number (w->old_pixel_height);
       p->left_col = make_number (w->left_col);
       p->top_line = make_number (w->top_line);
       p->total_cols = make_number (w->total_cols);
@@ -7246,6 +7373,16 @@ selected; while the global part is run only once for the modified frame,
 with the relevant frame selected.  */);
   Vwindow_configuration_change_hook = Qnil;

+  DEFVAR_LISP ("window-size-change-functions", Vwindow_size_change_functions,
+    doc: /* Functions called during redisplay, if window sizes have changed.
+The value should be a list of functions that take one 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 ("recenter-redisplay", Vrecenter_redisplay,
 	       doc: /* Non-nil means `recenter' redraws entire frame.
 If this option is non-nil, then the `recenter' command with a nil
@@ -7374,6 +7511,8 @@ displayed after a scrolling operation to be somewhat inaccurate.  */);
   defsubr (&Swindow_use_time);
   defsubr (&Swindow_pixel_width);
   defsubr (&Swindow_pixel_height);
+  defsubr (&Swindow_old_pixel_width);
+  defsubr (&Swindow_old_pixel_height);
   defsubr (&Swindow_total_width);
   defsubr (&Swindow_total_height);
   defsubr (&Swindow_normal_size);
diff --git a/src/window.h b/src/window.h
index c29207d..dea1d19 100644
--- a/src/window.h
+++ b/src/window.h
@@ -214,6 +214,11 @@ struct window
     int pixel_width;
     int pixel_height;

+    /* The pixel sizes of the window at the last time
+       `window-size-change-functions' was run.  */
+    int old_pixel_width;
+    int old_pixel_height;
+
     /* The size of the window.  */
     int total_cols;
     int total_lines;
@@ -499,15 +504,17 @@ wset_next_buffers (struct window *w, Lisp_Object val)
 #define WINDOW_LEAF_P(W) \
   (BUFFERP ((W)->contents))

-/* True if W is a member of horizontal combination.  */
+/* Non-nil if W is internal.  */
+#define WINDOW_INTERNAL_P(W) \
+  (WINDOWP ((W)->contents))

+/* True if W is a member of horizontal combination.  */
 #define WINDOW_HORIZONTAL_COMBINATION_P(W) \
-  (WINDOWP ((W)->contents) && (W)->horizontal)
+  (WINDOW_INTERNAL_P (W) && (W)->horizontal)

 /* True if W is a member of vertical combination.  */
-
 #define WINDOW_VERTICAL_COMBINATION_P(W) \
-  (WINDOWP ((W)->contents) && !(W)->horizontal)
+  (WINDOW_INTERNAL_P (W) && !(W)->horizontal)

 /* WINDOW's XFRAME.  */
 #define WINDOW_XFRAME(W) (XFRAME (WINDOW_FRAME ((W))))
@@ -1014,6 +1021,7 @@ extern void shrink_mini_window (struct window *, bool);
 extern int window_relative_x_coord (struct window *, enum window_part, int);

 void run_window_configuration_change_hook (struct frame *f);
+void run_window_size_change_functions (Lisp_Object);

 /* Make WINDOW display BUFFER.  RUN_HOOKS_P means it's allowed
    to run hooks.  See make_frame for a case where it's not allowed.  */
diff --git a/src/xdisp.c b/src/xdisp.c
index fed5879..4330f10 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -11786,24 +11786,7 @@ prepare_menu_bars (void)
 	      && !XBUFFER (w->contents)->text->redisplay)
 	    continue;

-	  /* If a window on this frame changed size, report that to
-	     the user and clear the size-change flag.  */
-	  if (FRAME_WINDOW_SIZES_CHANGED (f))
-	    {
-	      Lisp_Object functions;
-
-	      /* Clear flag first in case we get an error below.  */
-	      FRAME_WINDOW_SIZES_CHANGED (f) = false;
-	      functions = Vwindow_size_change_functions;
-
-	      while (CONSP (functions))
-		{
-		  if (!EQ (XCAR (functions), Qt))
-		    call1 (XCAR (functions), frame);
-		  functions = XCDR (functions);
-		}
-	    }
-
+	  run_window_size_change_functions (frame);
 	  menu_bar_hooks_run = update_menu_bar (f, false, menu_bar_hooks_run);
 #ifdef HAVE_WINDOW_SYSTEM
 	  update_tool_bar (f, false);
@@ -13599,24 +13582,12 @@ redisplay_internal (void)
 	 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))
+      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);
-	    }
-
+	  run_window_size_change_functions (selected_frame);
 	  unbind_to (count1, Qnil);
 	}

@@ -13638,22 +13609,10 @@ redisplay_internal (void)
     {
       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);
-	    }
-
+	  run_window_size_change_functions (selected_frame);
 	  unbind_to (count1, Qnil);
 	}

@@ -31447,16 +31406,6 @@ If nil, disable message logging.  If t, log messages but don't truncate
 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 during redisplay, if window sizes have changed.
-The value should be a list of functions that take one 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,
     doc: /* List of functions to call before redisplaying a window with scrolling.
 Each function is called with two arguments, the window and its new
diff --git a/src/xfns.c b/src/xfns.c
index 20ac627..2a50a5a 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -1313,7 +1313,6 @@ x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
     }
 #endif /* not USE_X_TOOLKIT && not USE_GTK */
   adjust_frame_glyphs (f);
-  run_window_configuration_change_hook (f);
 }




  parent reply	other threads:[~2016-02-22 12:59 UTC|newest]

Thread overview: 57+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-08-23 22:06 bug#21333: 25.0.50; window-size-change-functions not called after mini-window resize Pip Cet
2015-08-24  8:18 ` martin rudalics
2015-08-24 11:08   ` Pip Cet
2015-08-24 12:41     ` martin rudalics
2015-08-24 14:35 ` Eli Zaretskii
2015-08-24 18:06   ` martin rudalics
2015-08-24 18:30     ` Eli Zaretskii
2015-08-25  7:25       ` martin rudalics
2015-08-25 10:34         ` Pip Cet
2015-08-25 15:19           ` Eli Zaretskii
2015-08-26  7:08           ` martin rudalics
2015-08-25 15:11         ` Eli Zaretskii
2015-08-26  7:09           ` martin rudalics
2015-08-26 15:29             ` Eli Zaretskii
2015-08-27  7:57               ` martin rudalics
2015-08-27 15:29                 ` Eli Zaretskii
2015-08-27 17:05                   ` Pip Cet
2015-08-27 17:59                     ` martin rudalics
2015-08-27 18:04                       ` Pip Cet
2015-08-28  8:03                         ` martin rudalics
2015-08-28  8:19                           ` Pip Cet
2015-08-28  8:45                             ` Pip Cet
2015-08-27 18:35                     ` Eli Zaretskii
2015-08-27 17:58                   ` martin rudalics
2015-08-24 18:13   ` Pip Cet
2015-08-24 19:03     ` Eli Zaretskii
2015-08-25  7:25       ` martin rudalics
2015-08-25 15:12         ` Eli Zaretskii
2015-08-26  7:09           ` martin rudalics
2015-08-26 10:07             ` Pip Cet
2015-08-26 13:01               ` martin rudalics
2015-08-26 16:00                 ` Pip Cet
2015-08-27  7:59                   ` martin rudalics
2015-08-27 15:25                     ` Eli Zaretskii
2015-08-27 16:35                       ` Pip Cet
2015-08-27 17:59                         ` martin rudalics
2015-08-27 18:57                         ` Eli Zaretskii
2015-08-27 20:49                           ` Pip Cet
2015-08-28 10:02                             ` Eli Zaretskii
2015-08-28 12:34                               ` Pip Cet
2015-08-28 13:13                                 ` Eli Zaretskii
2015-08-28 13:26                                   ` Pip Cet
2015-08-26 15:36               ` Eli Zaretskii
2015-08-27  7:58                 ` martin rudalics
2015-08-27 15:24                   ` Eli Zaretskii
2015-08-27 17:58                     ` martin rudalics
2015-08-27 18:39                       ` Eli Zaretskii
2015-08-27 19:00                         ` Eli Zaretskii
2015-08-28  8:04                           ` martin rudalics
2015-08-28  8:47                             ` Eli Zaretskii
2015-08-28 10:51                               ` martin rudalics
2015-08-28 12:46                                 ` Eli Zaretskii
2015-08-28 13:05                                   ` martin rudalics
2015-08-26 15:32             ` Eli Zaretskii
2015-08-27  7:57               ` martin rudalics
2016-02-22 12:59   ` martin rudalics [this message]
2016-02-23 11:31     ` Fix `window-configuration-change-hook' and `window-size-change-functions' martin rudalics

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=56CB064A.9060101@gmx.at \
    --to=rudalics@gmx.at \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.