summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/lispref/windows.texi314
-rw-r--r--etc/NEWS27
-rw-r--r--lisp/erc/erc-track.el5
-rw-r--r--lisp/frame.el11
-rw-r--r--lisp/net/rcirc.el8
-rw-r--r--lisp/window.el30
-rw-r--r--src/frame.c25
-rw-r--r--src/frame.h46
-rw-r--r--src/window.c767
-rw-r--r--src/window.h27
-rw-r--r--src/xdisp.c72
11 files changed, 949 insertions, 383 deletions
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index a0853180fb4..6b5aa66a955 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -568,12 +568,6 @@ its pixel height is the pixel height of the screen areas spanned by its
children.
@end defun
-@defun window-pixel-height-before-size-change &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
@@ -588,12 +582,6 @@ If @var{window} is an internal window, its pixel width is the width of
the screen areas spanned by its children.
@end defun
-@defun window-pixel-width-before-size-change &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
@@ -5705,10 +5693,6 @@ prevent the code in @var{forms} from opening new windows, because new
windows might be opened in other frames (@pxref{Choosing Window}), and
@code{save-window-excursion} only saves and restores the window
configuration on the current frame.
-
-Do not use this macro in @code{window-size-change-functions}; exiting
-the macro triggers execution of @code{window-size-change-functions},
-leading to an endless loop.
@end defmac
@defun window-configuration-p object
@@ -5827,10 +5811,10 @@ This function sets @var{window}'s value of @var{parameter} to
is the selected window.
@end defun
-By default, the functions that save and restore window configurations or the
-states of windows (@pxref{Window Configurations}) do not care about
-window parameters. This means that when you change the value of a
-parameter within the body of a @code{save-window-excursion}, the
+By default, the functions that save and restore window configurations
+or the states of windows (@pxref{Window Configurations}) do not care
+about window parameters. This means that when you change the value of
+a parameter within the body of a @code{save-window-excursion}, the
previous value is not restored when that macro exits. It also means
that when you restore via @code{window-state-put} a window state saved
earlier by @code{window-state-get}, all cloned windows have their
@@ -6019,27 +6003,26 @@ applications. It might be replaced by an improved solution in future
versions of Emacs.
@end table
+
@node Window Hooks
@section Hooks for Window Scrolling and Changes
@cindex hooks for window operations
-This section describes how a Lisp program can take action whenever a
-window displays a different part of its buffer or a different buffer.
-There are three actions that can change this: scrolling the window,
-switching buffers in the window, and changing the size of the window.
-The first two actions run @code{window-scroll-functions}; the last runs
-@code{window-size-change-functions}.
+This section describes how Lisp programs can take action after a
+window has been scrolled or other window modifications occurred. We
+first consider the case where a window shows a different part of its
+buffer.
@defvar window-scroll-functions
This variable holds a list of functions that Emacs should call before
-redisplaying a window with scrolling. Displaying a different buffer in
-the window also runs these functions.
+redisplaying a window with scrolling. Displaying a different buffer
+in a window and making a new window also call these functions.
-This variable is not a normal hook, because each function is called with
-two arguments: the window, and its new display-start position. At the
-time of the call, the display-start position of the window argument is
-already set to its new value, and the buffer to be displayed in the
-window is already set as the current buffer.
+This variable is not a normal hook, because each function is called
+with two arguments: the window, and its new display-start position.
+At the time of the call, the display-start position of the argument
+window is already set to its new value, and the buffer to be displayed
+in the window is set as the current buffer.
These functions must take care when using @code{window-end}
(@pxref{Window Start and End}); if you need an up-to-date value, you
@@ -6050,63 +6033,226 @@ is scrolled. It's not designed for that, and such use probably won't
work.
@end defvar
-@defun run-window-scroll-functions &optional window
-This function calls @code{window-scroll-functions} for the specified
-@var{window}, which defaults to the selected window.
-@end defun
+In addition, you can use @code{jit-lock-register} to register a Font
+Lock fontification function, which will be called whenever parts of a
+buffer are (re)fontified because a window was scrolled or its size
+changed. @xref{Other Font Lock Variables}.
+
+@cindex window change functions
+ The remainder of this section covers four hooks that are called at
+the end of redisplay provided a significant, non-scrolling change of a
+window has been detected. For simplicity, these hooks and the
+functions they call will be collectively referred to as @dfn{window
+change functions}.
+
+@cindex window buffer change
+The first of these hooks is run after a @dfn{window buffer change} is
+detected, which means that a window was created, deleted or assigned
+another buffer.
+
+@defvar window-buffer-change-functions
+This variable specifies functions called at the end of redisplay when
+window buffers have changed. The value should be a list of functions
+that take one argument.
+
+Functions specified buffer-locally are called for any window showing
+the corresponding buffer if that window has been created or assigned
+that buffer since the last time window change functions were run. In
+this case the window is passed as argument.
+
+Functions specified by the default value are called for a frame if at
+least one window on that frame has been added, deleted or assigned
+another buffer since the last time window change functions were run.
+In this case the frame is passed as argument.
+@end defvar
+
+@cindex window size change
+The second of these hooks is run after a @dfn{window size change} has
+been detected which means that a window was created, assigned another
+buffer, or changed its total size or that of its text area.
@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 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-pixel-width-before-size-change} and
-@code{window-pixel-width} respectively
-@code{window-pixel-height-before-size-change} and
-@code{window-pixel-height} for that window (@pxref{Window Sizes}).
-
-The buffer-local value of this hook is run once for the buffer and the
-frame in question, provided at least one window showing the buffer on
-that frame has changed its size. As it still receives the frame as
-its sole argument, any function called on a buffer-local basis will be
-oblivious to which window(s) showing the buffer changed its (their)
-size and has to check out these windows by using the method described
-in the previous paragraph.
-
-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.
+This variable specifies functions called at the end of redisplay when
+a window size change occurred. The value should be a list of
+functions that take one argument.
+
+Functions specified buffer-locally are called for any window showing
+the corresponding buffer if that window has been added or assigned
+another buffer, total or body size since the last time window change
+functions were run. In this case the window is passed as argument.
+
+Functions specified by the default value are called for a frame if at
+least one window on that frame has been added or assigned another
+buffer, total or body size since the last time window change functions
+were run. In this case the frame is passed as argument.
+@end defvar
+
+@cindex window selection change
+The third of these hooks is run after a @dfn{window selection change}
+has selected another window since the last redisplay.
+
+@defvar window-selection-change-functions
+This variable specifies functions called at the end of redisplay when
+the selected window or a frame's selected window has changed. The
+value should be a list of functions that take one argument.
+
+Functions specified buffer-locally are called for any window showing
+the corresponding buffer if that window has been selected or
+deselected (among all windows or among all windows on its frame) since
+the last time window change functions were run. In this case the
+window is passed as argument.
+
+Functions specified by the default value are called for a frame if
+that frame has been selected or deselected or the frame's selected
+window has changed since the last time window change functions were
+run. In this case the frame is passed as argument.
@end defvar
+@cindex window configuration change
+The fourth of these hooks is run when a @dfn{window configuration
+change} has been detected which means that either the buffer or the
+size of a window changed.
+
@defvar window-configuration-change-hook
-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.
-
-The hook can be also used for tracking changes of window sizes. It
-is, however, not run when the size of a frame changes or automatic
-resizing of a minibuffer window (@pxref{Minibuffer Windows}) changes
-the size of another window. As a rule, adding a function to
-@code{window-size-change-functions}, see above, is the recommended way
-for reliably tracking size changes of any window.
-
-The buffer-local value of this hook is run once for each window on the
-affected frame, with the relevant window selected and its buffer
-current. The global value of this hook is run once for the modified
-frame, with that frame selected.
+This variable specifies functions called at the end of redisplay when
+either the buffer or the size of a window has changed. The value
+should be a list of functions that take no argument.
+
+Functions specified buffer-locally are called for any window showing
+the corresponding buffer if at least one window on that frame has been
+added, deleted or assigned another buffer, total or body size since
+the last time window change functions were run. Each call is
+performed with the window showing the buffer temporarily selected and
+its buffer current.
+
+Functions specified by the default value are called for each frame if
+at least one window on that frame has been added, deleted or assigned
+another buffer, total or body size since the last time window change
+functions were run. Each call is performed with the frame temporarily
+selected and the selected window's buffer current.
@end defvar
-@defun run-window-configuration-change-hook &optional frame
-This function runs @code{window-configuration-change-hook} for the
-specified @var{frame}, which defaults to the selected frame.
+Window change functions are called at the end of redisplay for each
+frame as follows: First, any buffer-local window buffer change
+function, window size change function and selected window change
+functions are called in this order. Next, the default values for
+these functions are called in the same order. Then any buffer-local
+window configuration change functions are called followed by functions
+specified by the default value of those functions.
+
+ Window change functions are run for a specific frame only if a
+corresponding change was registered for that frame earlier. Such
+changes include the creation or deletion of a window or the assignment
+of another buffer or size to a window. Note that even when such a
+change has been registered, this does not mean that any of the hooks
+described above is run. If, for example, a change was registered
+within the scope of a window excursion (@pxref{Window
+Configurations}), this will trigger a call of window change functions
+only if that excursion still persists at the time change functions are
+run. If it is exited earlier, hooks will be run only if registered by
+a change outside the scope of that excursion.
+
+ While window change functions are run, the functions described next
+can be called to get more insight into what has changed for a specific
+window or frame since the last redisplay. All these functions take a
+live window as single, optional argument, defaulting to the selected
+window.
+
+@defun window-old-buffer &optional window
+This function returns the buffer shown in @var{window} at the last
+time window change functions were run for @var{window}'s frame. If it
+returns @code{nil}, @var{window} has been created after that. If it
+returns @code{t}, @var{window} was not shown at that time but has been
+restored from a previously saved window configuration afterwards.
+Otherwise, the return value is the buffer shown by @code{window} at
+that time.
@end defun
- In addition, you can use @code{jit-lock-register} to register a Font
-Lock fontification function, which will be called whenever parts of a
-buffer are (re)fontified because a window was scrolled or its size
-changed. @xref{Other Font Lock Variables}.
+@defun window-old-pixel-width &optional window
+This function returns the total pixel width of @var{window} the
+last time window change functions found @code{window} live on its
+frame. It is zero if @code{window} was created after that.
+@end defun
+
+@defun window-old-pixel-height &optional window
+This function returns the total pixel height of @var{window} the last
+time window change functions found @code{window} live on its frame.
+It is zero if @code{window} was created after that.
+@end defun
+
+@defun window-old-body-pixel-width &optional window
+This function returns the pixel width of @var{window}'s text area the
+last time window change functions found @code{window} live on its
+frame. It is zero if @code{window} was created after that.
+@end defun
+
+@defun window-old-body-pixel-height &optional window
+This function returns the pixel height of @var{window}'s text area the
+last time window change functions found @code{window} live on its
+frame. It is zero if @code{window} was created after that.
+@end defun
+
+In order to find out which window or frame was selected the last time
+window change functions were run, the following functions can be used:
+
+@defun frame-old-selected-window &optional frame
+This function returns the selected window of @var{frame} at the last
+time window change functions were run. If omitted or @code{nil}
+@var{frame} defaults to the selected frame.
+@end defun
+
+@defun old-selected-window
+This function returns the selected window at the last time window
+change functions were run.
+@end defun
+
+@defun old-selected-frame
+This function returns the selected frame at the last time window
+change functions were run.
+@end defun
+
+Note that window change functions provide no information about which
+windows have been deleted since the last time they were run. If
+necessary, an application should remember any window showing a
+specific buffer in a local variable of that buffer and update it in a
+function run by the default value of
+@code{window-buffer-change-functions} or
+@code{window-configuration-change-hook} (the only hooks triggered by
+the deletion of windows).
+
+ The following caveats should be considered when adding a function
+to window change functions:
+
+@itemize @bullet
+@item
+Some operations will not trigger a call of window change functions.
+These include showing another buffer in a minibuffer window or any
+change of a tooltip window.
+
+@item
+Window change functions should not create or delete windows or change
+the buffer, size or selection status of any window because there is no
+guarantee that the information about such a change will be propagated
+to other window change functions. If at all, any such change should
+be executed only by the last function listed by the default value of
+@code{window-configuration-change-hook}.
+
+@item
+Macros like @code{save-window-excursion}, @code{with-selected-window}
+or @code{with-current-buffer} can be used when running window change
+functions.
+
+@item
+Running window change functions does not save and restore match data.
+Unless running @code{window-configuration-change-hook} it does not
+save or restore the selected window or frame or the current buffer
+either.
+
+@item
+Any redisplay triggering the run of window change functions may be
+aborted. If the abort occurs before window change functions have run
+to their completion, they will be run again with the previous values,
+that is, as if redisplay had not been performed. If aborted later,
+they will be run with the new values, that is, as if redisplay had
+been actually performed.
+@end itemize
diff --git a/etc/NEWS b/etc/NEWS
index a96ec781b4e..bb214f26c5b 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1283,9 +1283,30 @@ displaying the same buffer. See the node "(elisp) Face Remapping"
of the Emacs Lisp Reference manual for more detail.
+++
-** Special handling of buffer-local 'window-size-change-functions'.
-A buffer-local value of this hook is now run only if at least one
-window showing the buffer has changed its size.
+** Window change functions have been redesigned completely.
+Hooks reacting to window changes run now only when redisplay detects
+that a change has actually occurred. The four hooks provided are:
+'window-buffer-change-functions' (run after window buffers have
+changed), 'window-size-change-functions' (run after a window was
+assigned a new buffer or size), 'window-configuration-change-hook'
+(like the former but run also when a window was deleted) and
+'window-selection-change-functions' (run when the selected window
+changed). 'window-scroll-functions' are unaffected by these changes.
+
+In addition, a number of functions now allow the caller to detect what
+has changed since last redisplay: 'window-old-buffer' returns for any
+window the buffer it showed at that time. ‘old-selected-window’ and
+'old-selected-frame' return the window and frame that were selected
+during last redisplay. 'window-old-pixel-width' (renamed from
+'window-pixel-width-before-size-change'), 'window-old-pixel-height'
+(renamed from 'window-pixel-height-before-size-change'),
+'window-old-body-pixel-width' and 'window-old-body-pixel-height'
+return the total and body sizes of any window during last redisplay.
+
+One consequence of these changes is that all window change functions
+run now after functions run by 'post-command-hook'. See the section
+"(elisp) Window Hooks" in the Elisp manual for a detailed explanation
+of the new behavior.
+++
** New buffer display action alist entry 'dedicated'.
diff --git a/lisp/erc/erc-track.el b/lisp/erc/erc-track.el
index c966cf86270..f42bd648725 100644
--- a/lisp/erc/erc-track.el
+++ b/lisp/erc/erc-track.el
@@ -640,7 +640,7 @@ only consider active buffers visible.")
(unless (minibuffer-window-active-p (minibuffer-window))
;; delay this until command has finished to make sure window is
;; actually visible before clearing activity
- (add-hook 'post-command-hook 'erc-modified-channels-update)))
+ (erc-modified-channels-update)))
(defvar erc-modified-channels-update-inside nil
"Variable to prevent running `erc-modified-channels-update' multiple
@@ -669,8 +669,7 @@ ARGS are ignored."
(erc-modified-channels-remove-buffer buffer))))
erc-modified-channels-alist)
(when removed-channel
- (erc-modified-channels-display)))
- (remove-hook 'post-command-hook 'erc-modified-channels-update)))
+ (erc-modified-channels-display)))))
(defvar erc-track-mouse-face (if (featurep 'xemacs)
'modeline-mousable
diff --git a/lisp/frame.el b/lisp/frame.el
index 8341ba1707f..dc81302939e 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1745,20 +1745,17 @@ for FRAME."
(let* ((frame (window-normalize-frame frame))
(root (frame-root-window frame))
(mini (minibuffer-window frame))
- (mini-height-before-size-change 0)
+ (mini-old-height 0)
(mini-height 0))
;; FRAME's minibuffer window counts iff it's on FRAME and FRAME is
;; not a minibuffer-only frame.
(when (and (eq (window-frame mini) frame) (not (eq mini root)))
- (setq mini-height-before-size-change
- (window-pixel-height-before-size-change mini))
+ (setq mini-old-height (window-old-pixel-height mini))
(setq mini-height (window-pixel-height mini)))
;; Return non-nil when either the width of the root or the sum of
;; the heights of root and minibuffer window changed.
- (or (/= (window-pixel-width-before-size-change root)
- (window-pixel-width root))
- (/= (+ (window-pixel-height-before-size-change root)
- mini-height-before-size-change)
+ (or (/= (window-old-pixel-width root) (window-pixel-width root))
+ (/= (+ (window-old-pixel-height root) mini-old-height)
(+ (window-pixel-height root) mini-height)))))
;;;; Frame/display capabilities.
diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el
index 863554604e8..47681ccfe79 100644
--- a/lisp/net/rcirc.el
+++ b/lisp/net/rcirc.el
@@ -2064,9 +2064,7 @@ activity. Only run if the buffer is not visible and
(defvar rcirc-visible-buffers nil)
(defun rcirc-window-configuration-change ()
(unless (minibuffer-window-active-p (minibuffer-window))
- ;; delay this until command has finished to make sure window is
- ;; actually visible before clearing activity
- (add-hook 'post-command-hook 'rcirc-window-configuration-change-1)))
+ (rcirc-window-configuration-change-1)))
(defun rcirc-window-configuration-change-1 ()
;; clear activity and overlay arrows
@@ -2090,9 +2088,7 @@ activity. Only run if the buffer is not visible and
rcirc-activity)))
;; update the mode-line string
(unless (equal old-activity rcirc-activity)
- (rcirc-update-activity-string)))
-
- (remove-hook 'post-command-hook 'rcirc-window-configuration-change-1))
+ (rcirc-update-activity-string))))
;;; buffer name abbreviation
diff --git a/lisp/window.el b/lisp/window.el
index 751263c9259..424d0525f4e 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -2043,6 +2043,8 @@ doc-string of `window-resizable'."
;; Aliases of functions defined in window.c.
(defalias 'window-height 'window-total-height)
(defalias 'window-width 'window-body-width)
+(defalias 'window-pixel-width-before-size-change 'window-old-pixel-width)
+(defalias 'window-pixel-height-before-size-change 'window-old-pixel-height)
(defun window-full-height-p (&optional window)
"Return t if WINDOW is as high as its containing frame.
@@ -2759,8 +2761,7 @@ as small) as possible, but don't signal an error."
;; The following routine catches the case where we want to resize
;; a minibuffer-only frame.
(when (resize-mini-window-internal window)
- (window--pixel-to-total frame)
- (run-window-configuration-change-hook frame))))))
+ (window--pixel-to-total frame))))))
(defun window--resize-apply-p (frame &optional horizontal)
"Return t when a window on FRAME shall be resized vertically.
@@ -2858,9 +2859,7 @@ instead."
(window--resize-siblings window delta horizontal ignore))
(when (window--resize-apply-p frame horizontal)
(if (window-resize-apply frame horizontal)
- (progn
- (window--pixel-to-total frame horizontal)
- (run-window-configuration-change-hook frame))
+ (window--pixel-to-total frame horizontal)
(error "Failed to apply resizing %s" window))))
(t
(error "Cannot resize window %s" window)))))
@@ -3579,9 +3578,7 @@ move it as far as possible in the desired direction."
;; Don't report an error in the standard case.
(when (window--resize-apply-p frame horizontal)
(if (window-resize-apply frame horizontal)
- (progn
- (window--pixel-to-total frame horizontal)
- (run-window-configuration-change-hook frame))
+ (window--pixel-to-total frame horizontal)
;; But do report an error if applying the changes fails.
(error "Failed adjusting window %s" window))))))))
@@ -4112,7 +4109,6 @@ that is its frame's root window."
;; `delete-window-internal' has selected a window that should
;; not be selected, fix this here.
(other-window -1 frame))
- (run-window-configuration-change-hook frame)
(window--check frame)
;; Always return nil.
nil))))
@@ -4198,7 +4194,6 @@ any window whose `no-delete-other-windows' parameter is non-nil."
;; If WINDOW is the main window of its frame do nothing.
(unless (eq window main)
(delete-other-windows-internal window main)
- (run-window-configuration-change-hook frame)
(window--check frame))
;; Always return nil.
nil)))
@@ -5186,7 +5181,6 @@ frame. The selected window is not changed by this function."
(unless size
(window--sanitize-window-sizes horizontal))
- (run-window-configuration-change-hook frame)
(run-window-scroll-functions new)
(window--check frame)
;; Always return the new window.
@@ -5417,15 +5411,13 @@ window."
(balance-windows-1 window)
(when (window--resize-apply-p frame)
(window-resize-apply frame)
- (window--pixel-to-total frame)
- (run-window-configuration-change-hook frame))
+ (window--pixel-to-total frame))
;; Balance horizontally.
(window--resize-reset (window-frame window) t)
(balance-windows-1 window t)
(when (window--resize-apply-p frame t)
(window-resize-apply frame t)
- (window--pixel-to-total frame t)
- (run-window-configuration-change-hook frame))))
+ (window--pixel-to-total frame t))))
(defun window-fixed-size-p (&optional window direction)
"Return t if WINDOW cannot be resized in DIRECTION.
@@ -9441,15 +9433,7 @@ displaying that processes's buffer."
(when size
(set-process-window-size process (cdr size) (car size))))))))))
-;; Remove the following call in Emacs 27, running
-;; 'window-size-change-functions' should suffice.
(add-hook 'window-configuration-change-hook 'window--adjust-process-windows)
-
-;; Catch any size changes not handled by
-;; 'window-configuration-change-hook' (Bug#32720, "another issue" in
-;; Bug#33230).
-(add-hook 'window-size-change-functions (lambda (_frame)
- (window--adjust-process-windows)))
;; Some of these are in tutorial--default-keys, so update that if you
;; change these.
diff --git a/src/frame.c b/src/frame.c
index ca6704a44c0..6d93abd09bf 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -55,9 +55,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#endif
/* The currently selected frame. */
-
Lisp_Object selected_frame;
+/* The selected frame the last time window change functions were run. */
+Lisp_Object old_selected_frame;
+
/* A frame which is not just a mini-buffer, or NULL if there are no such
frames. This is usually the most recent such frame that was selected. */
@@ -855,7 +857,8 @@ make_frame (bool mini_p)
f->ns_transparent_titlebar = false;
#endif
#endif
-
+ /* This one should never be zero. */
+ f->change_stamp = 1;
root_window = make_window ();
rw = XWINDOW (root_window);
if (mini_p)
@@ -1451,7 +1454,8 @@ This function returns FRAME, or nil if FRAME has been deleted. */)
return do_switch_frame (frame, 1, 0, norecord);
}
-DEFUN ("handle-switch-frame", Fhandle_switch_frame, Shandle_switch_frame, 1, 1, "^e",
+DEFUN ("handle-switch-frame", Fhandle_switch_frame,
+ Shandle_switch_frame, 1, 1, "^e",
doc: /* Handle a switch-frame event EVENT.
Switch-frame events are usually bound to this function.
A switch-frame event is an event Emacs sends itself to
@@ -1471,6 +1475,18 @@ DEFUN ("selected-frame", Fselected_frame, Sselected_frame, 0, 0, 0,
{
return selected_frame;
}
+
+DEFUN ("old-selected-frame", Fold_selected_frame,
+ Sold_selected_frame, 0, 0, 0,
+ doc: /* Return the old selected FRAME.
+FRAME must be a live frame and defaults to the selected one.
+
+The return value is the frame selected the last time window change
+functions were run. */)
+ (void)
+{
+ return old_selected_frame;
+}
DEFUN ("frame-list", Fframe_list, Sframe_list,
0, 0, 0,
@@ -6098,9 +6114,10 @@ iconify the top level frame instead. */);
defsubr (&Swindow_system);
defsubr (&Sframe_windows_min_size);
defsubr (&Smake_terminal_frame);
- defsubr (&Shandle_switch_frame);
defsubr (&Sselect_frame);
+ defsubr (&Shandle_switch_frame);
defsubr (&Sselected_frame);
+ defsubr (&Sold_selected_frame);
defsubr (&Sframe_list);
defsubr (&Sframe_parent);
defsubr (&Sframe_ancestor_p);
diff --git a/src/frame.h b/src/frame.h
index b7059027fbe..ab3efdfa926 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -125,6 +125,10 @@ struct frame
The selected window of the selected frame is Emacs's selected window. */
Lisp_Object selected_window;
+ /* This frame's selected window when run_window_change_functions was
+ called the last time on this frame. */
+ Lisp_Object old_selected_window;
+
/* This frame's minibuffer window.
Most frames have their own minibuffer windows,
but only the selected frame's minibuffer window
@@ -321,9 +325,14 @@ struct frame
cleared. */
bool_bf explicit_name : 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 at least one window on this frame changed since the last
+ call of run_window_change_functions. Changes are either "state
+ changes" (a window has been created, deleted or got assigned
+ another buffer) or "size changes" (the total or body size of a
+ window changed). run_window_change_functions exits early unless
+ either this flag is true or a window selection happened on this
+ frame. */
+ bool_bf window_change : 1;
/* True if the mouse has moved on this display device
since the last time we checked. */
@@ -406,6 +415,20 @@ struct frame
/* Bitfield area ends here. */
+ /* This frame's change stamp, set the last time window change
+ functions were run for this frame. Should never be 0 because
+ that's the change stamp of a new window. A window was not on a
+ frame the last run_window_change_functions was called on it if
+ it's change stamp differs from that of its frame. */
+ int change_stamp;
+
+ /* This frame's number of windows, set the last time window change
+ functions were run for this frame. Should never be 0 even for
+ minibuffer-only frames. If no window has been added, this allows
+ to detect whether a window was deleted on this frame since the
+ last time run_window_change_functions was called on it. */
+ ptrdiff_t number_of_windows;
+
/* Number of lines (rounded up) of tool bar. REMOVE THIS */
int tool_bar_lines;
@@ -662,6 +685,11 @@ fset_selected_window (struct frame *f, Lisp_Object val)
f->selected_window = val;
}
INLINE void
+fset_old_selected_window (struct frame *f, Lisp_Object val)
+{
+ f->old_selected_window = val;
+}
+INLINE void
fset_title (struct frame *f, Lisp_Object val)
{
f->title = val;
@@ -908,10 +936,9 @@ default_pixels_per_inch_y (void)
are frozen on frame F. */
#define FRAME_WINDOWS_FROZEN(f) (f)->frozen_window_starts
-/* 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
+/* True if at least one window changed on frame F since the last time
+ window change functions were run on F. */
+#define FRAME_WINDOW_CHANGE(f) (f)->window_change
/* The minibuffer window of frame F, if it has one; otherwise nil. */
#define FRAME_MINIBUF_WINDOW(f) f->minibuffer_window
@@ -919,8 +946,10 @@ default_pixels_per_inch_y (void)
/* The root window of the window tree of frame F. */
#define FRAME_ROOT_WINDOW(f) f->root_window
-/* The currently selected window of the window tree of frame F. */
+/* The currently selected window of frame F. */
#define FRAME_SELECTED_WINDOW(f) f->selected_window
+/* The old selected window of frame F. */
+#define FRAME_OLD_SELECTED_WINDOW(f) f->old_selected_window
#define FRAME_INSERT_COST(f) (f)->insert_line_cost
#define FRAME_DELETE_COST(f) (f)->delete_line_cost
@@ -1215,6 +1244,7 @@ SET_FRAME_VISIBLE (struct frame *f, int v)
(f)->iconified = (eassert (0 <= (i) && (i) <= 1), (i))
extern Lisp_Object selected_frame;
+extern Lisp_Object old_selected_frame;
#if ! (defined USE_GTK || defined HAVE_NS)
extern int frame_default_tool_bar_height;
diff --git a/src/window.c b/src/window.c
index 72185f9340a..0fc4f622995 100644
--- a/src/window.c
+++ b/src/window.c
@@ -77,6 +77,11 @@ static void apply_window_adjustment (struct window *);
FRAME_SELECTED_WINDOW (selected_frame). */
Lisp_Object selected_window;
+/* The value of selected_window at the last time window change
+ functions were run. This is always the same as
+ FRAME_OLD_SELECTED_WINDOW (old_selected_frame). */
+Lisp_Object old_selected_window;
+
/* A list of all windows for use by next_window and Fwindow_list.
Functions creating or deleting windows should invalidate this cache
by setting it to nil. */
@@ -304,6 +309,12 @@ wset_buffer (struct window *w, Lisp_Object val)
adjust_window_count (w, 1);
}
+static void
+wset_old_buffer (struct window *w, Lisp_Object val)
+{
+ w->old_buffer = val;
+}
+
DEFUN ("windowp", Fwindowp, Swindowp, 1, 1, 0,
doc: /* Return t if OBJECT is a window and nil otherwise. */)
(Lisp_Object object)
@@ -428,6 +439,22 @@ return the selected window of that frame. */)
return window;
}
+DEFUN ("frame-old-selected-window", Fframe_old_selected_window,
+ Sframe_old_selected_window, 0, 1, 0,
+ doc: /* Return old selected window of FRAME.
+FRAME must be a live frame and defaults to the selected one.
+
+The return value is the window selected on FRAME the last time window
+change functions were run for FRAME. */)
+ (Lisp_Object frame)
+{
+ if (NILP (frame))
+ frame = selected_frame;
+ CHECK_LIVE_FRAME (frame);
+
+ return XFRAME (frame)->old_selected_window;
+}
+
DEFUN ("set-frame-selected-window", Fset_frame_selected_window,
Sset_frame_selected_window, 2, 3, 0,
doc: /* Set selected window of FRAME to WINDOW.
@@ -465,6 +492,16 @@ selected windows appears and to which many commands apply. */)
return selected_window;
}
+DEFUN ("old-selected-window", Fold_selected_window,
+ Sold_selected_window, 0, 0, 0,
+ doc: /* Return the old selected window.
+The return value is the window selected the last time window change
+functions were run. */)
+ (void)
+{
+ return old_selected_window;
+}
+
EMACS_INT window_select_count;
/* If select_window is called with inhibit_point_swap true it will
@@ -597,9 +634,33 @@ Return nil for an internal window or a deleted window. */)
(Lisp_Object window)
{
struct window *w = decode_any_window (window);
+
return WINDOW_LEAF_P (w) ? w->contents : Qnil;
}
+DEFUN ("window-old-buffer", Fwindow_old_buffer, Swindow_old_buffer, 0, 1, 0,
+ doc: /* Return the old buffer displayed by WINDOW.
+WINDOW must be a live window and defaults to the selected one.
+
+The return value is the buffer shown in WINDOW at the last time window
+change functions were run. It is nil if WINDOW was created after
+that. It is t if WINDOW has been restored from a window configuration
+after that. */)
+ (Lisp_Object window)
+{
+ struct window *w = decode_live_window (window);
+
+ return (NILP (w->old_buffer)
+ /* A new window. */
+ ? Qnil
+ : (w->change_stamp != WINDOW_XFRAME (w)->change_stamp)
+ /* A window restored from a configuration. */
+ ? Qt
+ /* A window that was live the last time seen by window
+ change functions. */
+ : w->old_buffer);
+}
+
DEFUN ("window-parent", Fwindow_parent, Swindow_parent, 0, 1, 0,
doc: /* Return the parent window of window WINDOW.
WINDOW must be a valid window and defaults to the selected one.
@@ -723,34 +784,32 @@ the height of the screen areas spanned by its children. */)
return make_fixnum (decode_valid_window (window)->pixel_height);
}
-DEFUN ("window-pixel-width-before-size-change",
- Fwindow_pixel_width_before_size_change,
- Swindow_pixel_width_before_size_change, 0, 1, 0,
- doc: /* Return pixel width of window WINDOW before last size changes.
+DEFUN ("window-old-pixel-width", Fwindow_old_pixel_width,
+ Swindow_old_pixel_width, 0, 1, 0,
+ doc: /* Return old total pixel width of WINDOW.
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. */)
+The return value is the total pixel width of WINDOW after the last
+time window change functions found WINDOW live on its frame. It is
+zero if WINDOW was created after that. */)
(Lisp_Object window)
{
return (make_fixnum
- (decode_valid_window (window)->pixel_width_before_size_change));
+ (decode_valid_window (window)->old_pixel_width));
}
-DEFUN ("window-pixel-height-before-size-change",
- Fwindow_pixel_height_before_size_change,
- Swindow_pixel_height_before_size_change, 0, 1, 0,
- doc: /* Return pixel height of window WINDOW before last size changes.
+DEFUN ("window-old-pixel-height", Fwindow_old_pixel_height,
+ Swindow_old_pixel_height, 0, 1, 0,
+ doc: /* Return old total pixel height of WINDOW.
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. */)
+The return value is the total pixel height of WINDOW after the last
+time window change functions found WINDOW live on its frame. It is
+zero if WINDOW was created after that. */)
(Lisp_Object window)
{
return (make_fixnum
- (decode_valid_window (window)->pixel_height_before_size_change));
+ (decode_valid_window (window)->old_pixel_height));
}
DEFUN ("window-total-height", Fwindow_total_height, Swindow_total_height, 0, 2, 0,
@@ -984,6 +1043,26 @@ window_body_width (struct window *w, bool pixelwise)
0);
}
+DEFUN ("window-body-width", Fwindow_body_width, Swindow_body_width, 0, 2, 0,
+ doc: /* Return the width of WINDOW's text area.
+WINDOW must be a live window and defaults to the selected one. Optional
+argument PIXELWISE non-nil means return the width in pixels. The return
+value does not include any vertical dividers, fringes or marginal areas,
+or scroll bars.
+
+If PIXELWISE is nil, return the largest integer smaller than WINDOW's
+pixel width divided by the character width of WINDOW's frame. This
+means that if a column at the right of the text area is only partially
+visible, that column is not counted.
+
+Note that the returned value includes the column reserved for the
+continuation glyph. */)
+ (Lisp_Object window, Lisp_Object pixelwise)
+{
+ return make_fixnum (window_body_width (decode_live_window (window),
+ !NILP (pixelwise)));
+}
+
DEFUN ("window-body-height", Fwindow_body_height, Swindow_body_height, 0, 2, 0,
doc: /* Return the height of WINDOW's text area.
WINDOW must be a live window and defaults to the selected one. Optional
@@ -1001,24 +1080,34 @@ visible, that line is not counted. */)
!NILP (pixelwise)));
}
-DEFUN ("window-body-width", Fwindow_body_width, Swindow_body_width, 0, 2, 0,
- doc: /* Return the width of WINDOW's text area.
-WINDOW must be a live window and defaults to the selected one. Optional
-argument PIXELWISE non-nil means return the width in pixels. The return
-value does not include any vertical dividers, fringes or marginal areas,
-or scroll bars.
+DEFUN ("window-old-body-pixel-width",
+ Fwindow_old_body_pixel_width,
+ Swindow_old_body_pixel_width, 0, 1, 0,
+ doc: /* Return old width of WINDOW's text area in pixels.
+WINDOW must be a live window and defaults to the selected one.
-If PIXELWISE is nil, return the largest integer smaller than WINDOW's
-pixel width divided by the character width of WINDOW's frame. This
-means that if a column at the right of the text area is only partially
-visible, that column is not counted.
+The return value is the pixel width of WINDOW's text area after the
+last time window change functions found WINDOW live on its frame. It
+is zero if WINDOW was created after that. */)
+ (Lisp_Object window)
+{
+ return (make_fixnum
+ (decode_live_window (window)->old_body_pixel_width));
+}
-Note that the returned value includes the column reserved for the
-continuation glyph. */)
- (Lisp_Object window, Lisp_Object pixelwise)
+DEFUN ("window-old-body-pixel-height",
+ Fwindow_old_body_pixel_height,
+ Swindow_old_body_pixel_height, 0, 1, 0,
+ doc: /* Return old height of WINDOW's text area in pixels.
+WINDOW must be a live window and defaults to the selected one.
+
+The return value is the pixel height of WINDOW's text area after the
+last time window change functions found WINDOW live on its frame. It
+is zero if WINDOW was created after that. */)
+ (Lisp_Object window)
{
- return make_fixnum (window_body_width (decode_live_window (window),
- !NILP (pixelwise)));
+ return (make_fixnum
+ (decode_live_window (window)->old_body_pixel_height));
}
DEFUN ("window-mode-line-height", Fwindow_mode_line_height,
@@ -3264,7 +3353,7 @@ window-start value is reasonable when this function is called. */)
adjust_frame_glyphs (f);
unblock_input ();
- run_window_configuration_change_hook (f);
+ FRAME_WINDOW_CHANGE (f) = true;
return Qnil;
}
@@ -3318,6 +3407,15 @@ select_frame_norecord (Lisp_Object frame)
Fselect_frame (frame, Qt);
}
+/**
+ * run_window_configuration_change_hook:
+ *
+ * Run any functions on 'window-configuration-change-hook' for the
+ * frame specified by F. The buffer-local values are run with the
+ * window showing the buffer selected. The default value is run with
+ * the frame specified by F selected. All functions are called with
+ * the selected window's buffer current.
+ */
static void
run_window_configuration_change_hook (struct frame *f)
{
@@ -3371,7 +3469,10 @@ run_window_configuration_change_hook (struct frame *f)
DEFUN ("run-window-configuration-change-hook", Frun_window_configuration_change_hook,
Srun_window_configuration_change_hook, 0, 1, 0,
doc: /* Run `window-configuration-change-hook' for FRAME.
-If FRAME is omitted or nil, it defaults to the selected frame. */)
+If FRAME is omitted or nil, it defaults to the selected frame.
+
+This function should not be needed any more and will be therefore
+considered obsolete. */)
(Lisp_Object frame)
{
run_window_configuration_change_hook (decode_live_frame (frame));
@@ -3381,130 +3482,381 @@ If FRAME is omitted or nil, it defaults to the selected frame. */)
DEFUN ("run-window-scroll-functions", Frun_window_scroll_functions,
Srun_window_scroll_functions, 0, 1, 0,
doc: /* Run `window-scroll-functions' for WINDOW.
-If WINDOW is omitted or nil, it defaults to the selected window. */)
+If WINDOW is omitted or nil, it defaults to the selected window.
+
+This function is curently only called by 'split-window' for the new
+window after it has established the size of the new window. */)
(Lisp_Object window)
{
- if (! NILP (Vwindow_scroll_functions))
+ struct window *w = decode_live_window (window);
+ ptrdiff_t count = SPECPDL_INDEX ();
+
+ record_unwind_current_buffer ();
+ Fset_buffer (w->contents);
+ if (!NILP (Vwindow_scroll_functions))
run_hook_with_args_2 (Qwindow_scroll_functions, window,
- Fmarker_position (decode_live_window (window)->start));
+ Fmarker_position (w->start));
+ unbind_to (count, Qnil);
+
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)
+/**
+ * window_sub_list:
+ *
+ * Return list of live windows constructed by traversing any window
+ * sub-tree rooted at WINDOW in preorder followed by right siblings of
+ * WINDOW. Called from outside with second argument WINDOWS nil. The
+ * returned list is in reverse order.
+ */
+static Lisp_Object
+window_sub_list (Lisp_Object window, Lisp_Object windows)
{
- if (w->pixel_width != w->pixel_width_before_size_change
- || w->pixel_height != w->pixel_height_before_size_change)
- return true;
- if (WINDOW_INTERNAL_P (w))
+ struct window *w = XWINDOW (window);
+
+ while (w)
{
- w = XWINDOW (w->contents);
- while (w)
- {
- if (window_size_changed (w))
- return true;
+ if (WINDOW_INTERNAL_P (w))
+ windows = window_sub_list (w->contents, windows);
+ else
+ windows = Fcons (window, windows);
- w = NILP (w->next) ? 0 : XWINDOW (w->next);
- }
+ window = w->next;
+ w = NILP (window) ? 0 : XWINDOW (window);
}
- return false;
+ return windows;
}
-/* Set before size change pixel sizes of windows in tree rooted at W to
- their present pixel sizes. */
-static void
-window_set_before_size_change_sizes (struct window *w)
+/**
+ * window_change_record_windows:
+ *
+ * Record changes for all live windows found by traversing any window
+ * sub-tree rooted at WINDOW in preorder followed by any right
+ * siblings of WINDOW. This sets the old buffer, old pixel and old
+ * body pixel sizes of each live window found to the respective
+ * current values. It also sets the change stamp of each window found
+ * to STAMP. Return the number of live windows found.
+ *
+ * When not called by itself recursively, WINDOW is its frame's root
+ * window, STAMP is the current change stamp of WINDOW's frame and
+ * NUMBER is 0.
+ */
+static ptrdiff_t
+window_change_record_windows (Lisp_Object window, int stamp, ptrdiff_t number)
{
- w->pixel_width_before_size_change = w->pixel_width;
- w->pixel_height_before_size_change = w->pixel_height;
+ struct window *w = XWINDOW (window);
- if (WINDOW_INTERNAL_P (w))
+ while (w)
{
- w = XWINDOW (w->contents);
- while (w)
+ if (WINDOW_INTERNAL_P (w))
+ number = window_change_record_windows (w->contents, stamp, number);
+ else
{
- window_set_before_size_change_sizes (w);
- w = NILP (w->next) ? 0 : XWINDOW (w->next);
+ number += 1;
+ w->change_stamp = stamp;
+ wset_old_buffer (w, w->contents);
+ w->old_pixel_width = w->pixel_width;
+ w->old_pixel_height = w->pixel_height;
+ w->old_body_pixel_width = window_body_width (w, true);
+ w->old_body_pixel_height = window_body_height (w, true);
}
+
+ w = NILP (w->next) ? 0 : XWINDOW (w->next);
}
+
+ return number;
}
-void
-run_window_size_change_functions (Lisp_Object frame)
+/**
+ * window_change_record_frame:
+ *
+ * Record changes for FRAME. This records FRAME's selected window,
+ * updates FRAME's change stamp, records the states of all live
+ * windows of FRAME via window_change_record_windows and resets
+ * FRAME's window_change flag.
+ */
+static void
+window_change_record_frame (Lisp_Object frame)
{
struct frame *f = XFRAME (frame);
- struct window *r = XWINDOW (FRAME_ROOT_WINDOW (f));
- if (NILP (Vrun_hooks)
- || !(f->can_x_set_window_size)
- || !(f->after_make_frame))
- return;
+ /* Record selected window. */
+ fset_old_selected_window (f, FRAME_SELECTED_WINDOW (f));
- if (FRAME_WINDOW_CONFIGURATION_CHANGED (f)
- /* Here we implicitly exclude the possibility that the height of
- FRAME and its minibuffer window both change leaving the height
- of FRAME's root window alone. */
- || window_size_changed (r))
- {
- Lisp_Object globals = Fdefault_value (Qwindow_size_change_functions);
- Lisp_Object windows = Fwindow_list (frame, Qlambda, Qnil);
- /* The buffers for which the local hook was already run. */
- Lisp_Object buffers = Qnil;
+ /* Bump up FRAME's change stamp. If this wraps, make it 1 to avoid
+ that a new window (whose change stamp is always set to 0) gets
+ reported as "existing before". */
+ f->change_stamp += 1;
+ if (f->change_stamp == 0)
+ f->change_stamp = 1;
- for (; CONSP (windows); windows = XCDR (windows))
- {
- Lisp_Object window = XCAR (windows);
- Lisp_Object buffer = Fwindow_buffer (window);
-
- /* Run a buffer-local value only once for that buffer and
- only if at least one window showing that buffer on FRAME
- actually changed its size. Note that the function is run
- with FRAME as its argument and as such oblivious to the
- window checked below. */
- if (window_size_changed (XWINDOW (window))
- && !NILP (Flocal_variable_p (Qwindow_size_change_functions, buffer))
- && NILP (Fmemq (buffer, buffers)))
- {
- Lisp_Object locals
- = Fbuffer_local_value (Qwindow_size_change_functions, buffer);
+ /* Bump up the change stamps of all live windows on this frame so
+ the next call of this function can tell whether any of them
+ "existed before" and record state for each of these windows. */
+ f->number_of_windows
+ = window_change_record_windows (f->root_window, f->change_stamp, 0);
- while (CONSP (locals))
- {
- if (!EQ (XCAR (locals), Qt))
- safe_call1 (XCAR (locals), frame);
- locals = XCDR (locals);
- }
+ /* Reset our flag. */
+ FRAME_WINDOW_CHANGE (f) = false;
+}
- buffers = Fcons (buffer, buffers);
- }
- }
- while (CONSP (globals))
+/**
+ * window_change_record:
+ *
+ * Record selected window in old_selected_window and selected frame in
+ * old_selected_frame.
+ */
+static void
+window_change_record (void)
+{
+ /* Strictly spoken we don't need old_selected_window at all - its
+ value is the old selected window of old_selected_frame. */
+ old_selected_window = selected_window;
+ old_selected_frame = selected_frame;
+}
+
+
+/**
+ * run_window_change_functions_1:
+ *
+ * Run window change functions specified by SYMBOL with argument
+ * WINDOW_OR_FRAME. If BUFFER is nil, WINDOW_OR_FRAME specifies a
+ * frame. In this case, run the default value of SYMBOL. Otherwise,
+ * WINDOW_OR_FRAME denotes a window showing BUFFER. In this case, run
+ * the buffer local value of SYMBOL in BUFFER, if any.
+ */
+static void
+run_window_change_functions_1 (Lisp_Object symbol, Lisp_Object buffer,
+ Lisp_Object window_or_frame)
+{
+ Lisp_Object funs = Qnil;
+
+ if (NILP (buffer))
+ funs = Fdefault_value (symbol);
+ else if (!NILP (Fassoc (symbol, BVAR (XBUFFER (buffer), local_var_alist),
+ Qnil)))
+ /* Don't run global value buffer-locally. */
+ funs = buffer_local_value (symbol, buffer);
+
+ while (CONSP (funs))
+ {
+ if (!EQ (XCAR (funs), Qt))
+ safe_call1 (XCAR (funs), window_or_frame);
+ funs = XCDR (funs);
+ }
+}
+
+
+/**
+ * run_window_change_functions:
+ *
+ * Run window change functions for each live frame. This function
+ * must be called from a "safe" position in redisplay_internal.
+ *
+ * Do not run any functions for a frame whose window_change flag is
+ * nil and where no window selection happened since the last time this
+ * function was called. Also, skip any tooltip frame.
+ *
+ * The change functions run are, in this order:
+ *
+ * 'window-buffer-change-functions' which are run for a window that
+ * changed its buffer or that was not shown the last time window
+ * change functions were run. The default value is also run when a
+ * window was deleted since the last time window change functions were
+ * run.
+ *
+ * `window-size-change-functions' run for a window that changed its
+ * body or total size, a window that changed its buffer or a window
+ * that was not shown the last time window change functions were run.
+ *
+ * `window-selected-change-functions' run for a window that was
+ * (de-)selected since the last time window change functions were run.
+ *
+ * A buffer-local value of these functions is run if and only if the
+ * window for which the functions are run, currently shows the buffer.
+ * Each call gets one argument - the window showing the buffer. This
+ * means that the buffer-local value of these functions may be called
+ * as many times at the buffer is shown on the frame.
+ *
+ * The default value of these functions is called only after all
+ * buffer-local values for all of these functions have been run. Each
+ * such call receives one argument - the frame for which this function
+ * is run.
+ *
+ * After the three change functions cited above have been run in the
+ * indicated way, functions on 'window-configuration-change-hook' are
+ * run. A buffer-local value is run if a window shows that buffer and
+ * has either changed its buffer or its body or total size or did not
+ * appear on this frame since the last time window change functions
+ * were run. The functions are called without argument and the
+ * buffer's window selected. The default value is run without
+ * argument and the frame for which the function is run selected.
+ *
+ * This function does not save and restore match data. Any functions
+ * it calls are responsible for doing that themselves.
+ */
+void
+run_window_change_functions (void)
+{
+ Lisp_Object tail, frame;
+ bool selected_frame_change = !EQ (selected_frame, old_selected_frame);
+ ptrdiff_t count_outer = SPECPDL_INDEX ();
+
+ record_unwind_protect_void (window_change_record);
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ struct frame *f = XFRAME (frame);
+ Lisp_Object root = FRAME_ROOT_WINDOW (f);
+ bool frame_window_change = FRAME_WINDOW_CHANGE (f);
+ bool window_buffer_change, window_size_change;
+ bool frame_buffer_change = false, frame_size_change = false;
+ bool frame_selected_change
+ = (selected_frame_change
+ && (EQ (frame, old_selected_frame)
+ || EQ (frame, selected_frame)));
+ bool frame_selected_window_change
+ = !EQ (FRAME_OLD_SELECTED_WINDOW (f), FRAME_SELECTED_WINDOW (f));
+ bool window_deleted = false;
+ Lisp_Object windows;
+ ptrdiff_t number_of_windows;
+ ptrdiff_t count_inner = SPECPDL_INDEX ();
+
+ if (!f->can_x_set_window_size
+ || !f->after_make_frame
+ || FRAME_TOOLTIP_P (f)
+ || !(frame_window_change
+ || frame_selected_change
+ || frame_selected_window_change))
+ /* Either we cannot run hooks for this frame yet or no window
+ change has been reported for this frame since the last time
+ we ran window change functions on it. */
+ continue;
+
+ /* Analyze windows and run buffer locals hooks in pre-order. */
+ windows = Fnreverse (window_sub_list (root, Qnil));
+ number_of_windows = 0;
+
+ record_unwind_protect (window_change_record_frame, frame);
+
+ /* The following loop collects all data needed to tell whether
+ the default value of a hook shall be run and runs any buffer
+ local hooks right away. */
+ for (; CONSP (windows); windows = XCDR (windows))
{
- if (!EQ (XCAR (globals), Qt))
- safe_call1 (XCAR (globals), frame);
- globals = XCDR (globals);
+ Lisp_Object window = XCAR (windows);
+ struct window *w = XWINDOW (window);
+ Lisp_Object buffer = WINDOW_BUFFER (w);
+
+ /* Count this window even if it has been deleted while
+ running a hook. */
+ number_of_windows += 1;
+
+ if (!WINDOW_LIVE_P (window))
+ continue;
+
+ /* A "buffer change" means either the window's buffer
+ changed or the window was not part of this frame the last
+ time window change functions were run for it. */
+ window_buffer_change =
+ (frame_window_change
+ && (!EQ (buffer, w->old_buffer)
+ || w->change_stamp != f->change_stamp));
+ /* A "size change" means either a buffer change or that the
+ total or body size of the window has changed.
+
+ Note: A buffer change implies a size change because either
+ this window didn't show the buffer before or this window
+ didn't show the buffer the last time the window change
+ functions were run. In either case, an application
+ tracing size changes in a buffer-locally fashion might
+ want to be informed about that change. */
+ window_size_change =
+ (frame_window_change
+ && (window_buffer_change
+ || w->pixel_width != w->old_pixel_width
+ || w->pixel_height != w->old_pixel_height
+ || window_body_width (w, true) != w->old_body_pixel_width
+ || window_body_height (w, true) != w->old_body_pixel_height));
+
+ /* The following two are needed when running the default
+ values for this frame below. */
+ frame_buffer_change = frame_buffer_change || window_buffer_change;
+ frame_size_change = frame_size_change || window_size_change;
+
+ if (window_buffer_change)
+ run_window_change_functions_1
+ (Qwindow_buffer_change_functions, buffer, window);
+
+ if (window_size_change && WINDOW_LIVE_P (window))
+ run_window_change_functions_1
+ (Qwindow_size_change_functions, buffer, window);
+
+ /* This window's selection has changed when it it was
+ (de-)selected as its frame's or the globally selected
+ window. */
+ if (((frame_selected_change
+ && (EQ (window, old_selected_window)
+ || EQ (window, selected_window)))
+ || (frame_selected_window_change
+ && (EQ (window, FRAME_OLD_SELECTED_WINDOW (f))
+ || EQ (window, FRAME_SELECTED_WINDOW (f)))))
+ && WINDOW_LIVE_P (window))
+ run_window_change_functions_1
+ (Qwindow_selection_change_functions, buffer, window);
}
- window_set_before_size_change_sizes (r);
+ /* When the number of windows on a frame has decreased, at least
+ one window of that frame was deleted. In that case, we want
+ to run the default buffer and configuration change hooks. The
+ default size change hook is not necessarily run in that case,
+ but usually will be unless the deletion was "compensated" by
+ a reduction of the frame size or an increase of a minibuffer
+ window size. */
+ window_deleted = number_of_windows < f->number_of_windows;
+ /* A frame changed buffers when one of its windows has changed
+ its buffer or at least one window was deleted. */
+ if ((frame_buffer_change || window_deleted) && FRAME_LIVE_P (f))
+ run_window_change_functions_1
+ (Qwindow_buffer_change_functions, Qnil, frame);
+
+ /* A size change occurred when at least one of the frame's
+ windows has changed size. */
+ if (frame_size_change && FRAME_LIVE_P (f))
+ run_window_change_functions_1
+ (Qwindow_size_change_functions, Qnil, frame);
+
+ /* A frame has changed its window selection when its selected
+ window has changed or when it was (de-)selected. */
+ if ((frame_selected_change || frame_selected_window_change)
+ && FRAME_LIVE_P (f))
+ run_window_change_functions_1
+ (Qwindow_selection_change_functions, Qnil, frame);
+
+ /* A frame's configuration changed when one of its windows has
+ changed buffer or size or at least one window was deleted. */
+ if ((frame_size_change || window_deleted) && FRAME_LIVE_P (f))
+ /* This will run any buffer local window configuration change
+ hook as well. */
+ run_window_configuration_change_hook (f);
- if (FRAME_HAS_MINIBUF_P (f) && !FRAME_MINIBUF_ONLY_P (f))
- /* Record size of FRAME's minibuffer window too. */
- window_set_before_size_change_sizes
- (XWINDOW (FRAME_MINIBUF_WINDOW (f)));
+ if (!FRAME_LIVE_P (f))
+ continue;
- FRAME_WINDOW_CONFIGURATION_CHANGED (f) = false;
+ /* Record changes (via window_change_record_frame) for this
+ frame, even when an unhandled error occurred. */
+ unbind_to (count_inner, Qnil);
}
-}
+ /* Record selected window and frame. */
+ unbind_to (count_outer, Qnil);
+}
/* 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.
@@ -3581,14 +3933,18 @@ set_window_buffer (Lisp_Object window, Lisp_Object buffer,
apply_window_adjustment (w);
}
- if (run_hooks_p)
- {
- if (!NILP (Vwindow_scroll_functions))
- run_hook_with_args_2 (Qwindow_scroll_functions, window,
- Fmarker_position (w->start));
- if (!samebuf)
- run_window_configuration_change_hook (XFRAME (WINDOW_FRAME (w)));
- }
+ if (run_hooks_p && !NILP (Vwindow_scroll_functions))
+ run_hook_with_args_2 (Qwindow_scroll_functions, window,
+ Fmarker_position (w->start));
+
+ /* Ensure that window change functions are run later if the buffer
+ differs and the window is neither a mini nor a pseudo window.
+
+ Note: Running window change functions for the minibuffer is noisy
+ and was generally suppressed in the past. Is there any reason we
+ should run them? */
+ if (!samebuf && !MINI_WINDOW_P (w) && !WINDOW_PSEUDO_P (w))
+ FRAME_WINDOW_CHANGE (XFRAME (w->frame)) = true;
unbind_to (count, Qnil);
}
@@ -3828,8 +4184,6 @@ make_window (void)
w->phys_cursor_width = -1;
#endif
w->sequence_number = ++sequence_number;
- w->pixel_width_before_size_change = 0;
- w->pixel_height_before_size_change = 0;
w->scroll_bar_width = -1;
w->scroll_bar_height = -1;
w->column_number_displayed = -1;
@@ -4095,6 +4449,9 @@ window_resize_apply (struct window *w, bool horflag)
else
/* Bug#15957. */
w->window_end_valid = false;
+
+ if (!WINDOW_PSEUDO_P (w))
+ FRAME_WINDOW_CHANGE (WINDOW_XFRAME (w)) = true;
}
@@ -4559,17 +4916,11 @@ set correctly. See the code of `split-window' for how this is done. */)
block_input ();
window_resize_apply (p, horflag);
adjust_frame_glyphs (f);
- /* Set buffer of NEW to buffer of reference window. Don't run
- any hooks. */
- set_window_buffer (new, r->contents, false, true);
+ /* Set buffer of NEW to buffer of reference window. */
+ set_window_buffer (new, r->contents, true, true);
+ FRAME_WINDOW_CHANGE (f) = true;
unblock_input ();
- /* Maybe we should run the scroll functions in Elisp (which already
- runs the configuration change hook). */
- if (! NILP (Vwindow_scroll_functions))
- run_hook_with_args_2 (Qwindow_scroll_functions, new,
- Fmarker_position (n->start));
- /* Return NEW. */
return new;
}
@@ -4720,6 +5071,8 @@ Signal an error when WINDOW is the only window on its frame. */)
}
else
unblock_input ();
+
+ FRAME_WINDOW_CHANGE (f) = true;
}
else
/* We failed: Relink WINDOW into window tree. */
@@ -6310,7 +6663,6 @@ struct saved_window
Lisp_Object window, buffer, start, pointm, old_pointm;
Lisp_Object pixel_left, pixel_top, pixel_height, pixel_width;
- Lisp_Object pixel_height_before_size_change, pixel_width_before_size_change;
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;
@@ -6426,12 +6778,6 @@ 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. */
@@ -6441,11 +6787,6 @@ the return value is nil. Otherwise the value is t. */)
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)))
@@ -6530,10 +6871,6 @@ the return value is nil. Otherwise the value is t. */)
w->pixel_top = XFIXNAT (p->pixel_top);
w->pixel_width = XFIXNAT (p->pixel_width);
w->pixel_height = XFIXNAT (p->pixel_height);
- w->pixel_width_before_size_change
- = XFIXNAT (p->pixel_width_before_size_change);
- w->pixel_height_before_size_change
- = XFIXNAT (p->pixel_height_before_size_change);
w->left_col = XFIXNAT (p->left_col);
w->top_line = XFIXNAT (p->top_line);
w->total_cols = XFIXNAT (p->total_cols);
@@ -6581,9 +6918,6 @@ 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);
@@ -6617,8 +6951,6 @@ 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
@@ -6666,10 +6998,7 @@ 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]);
- window_changed = true;
- }
+ free_window_matrices (leaf_windows[i]);
/* Allow x_set_window_size again and apply frame size changes if
needed. */
@@ -6699,35 +7028,10 @@ the return value is nil. Otherwise the value is t. */)
selected window. */
if (FRAME_LIVE_P (XFRAME (data->selected_frame)))
do_switch_frame (data->selected_frame, 0, 0, Qnil);
-
- 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);
}
+ FRAME_WINDOW_CHANGE (f) = true;
+
if (!NILP (new_current_buffer))
{
Fset_buffer (new_current_buffer);
@@ -6889,10 +7193,6 @@ save_window_save (Lisp_Object window, struct Lisp_Vector *vector, ptrdiff_t i)
p->pixel_top = make_fixnum (w->pixel_top);
p->pixel_width = make_fixnum (w->pixel_width);
p->pixel_height = make_fixnum (w->pixel_height);
- p->pixel_width_before_size_change
- = make_fixnum (w->pixel_width_before_size_change);
- p->pixel_height_before_size_change
- = make_fixnum (w->pixel_height_before_size_change);
p->left_col = make_fixnum (w->left_col);
p->top_line = make_fixnum (w->top_line);
p->total_cols = make_fixnum (w->total_cols);
@@ -7581,9 +7881,9 @@ init_window_once (void)
{
struct frame *f = make_initial_frame ();
XSETFRAME (selected_frame, f);
- Vterminal_frame = selected_frame;
+ old_selected_frame = Vterminal_frame = selected_frame;
minibuf_window = f->minibuffer_window;
- selected_window = f->selected_window;
+ old_selected_window = selected_window = f->selected_window;
}
void
@@ -7604,6 +7904,8 @@ syms_of_window (void)
DEFSYM (Qwindow_configuration_change_hook, "window-configuration-change-hook");
DEFSYM (Qwindow_size_change_functions, "window-size-change-functions");
+ DEFSYM (Qwindow_buffer_change_functions, "window-buffer-change-functions");
+ DEFSYM (Qwindow_selection_change_functions, "window-selection-change-functions");
DEFSYM (Qwindowp, "windowp");
DEFSYM (Qwindow_configuration_p, "window-configuration-p");
DEFSYM (Qwindow_live_p, "window-live-p");
@@ -7688,24 +7990,66 @@ on their symbols to be controlled by this variable. */);
Vwindow_point_insertion_type = Qnil;
DEFSYM (Qwindow_point_insertion_type, "window-point-insertion-type");
- DEFVAR_LISP ("window-configuration-change-hook",
- Vwindow_configuration_change_hook,
- doc: /* Functions to call when window configuration changes.
-The buffer-local value is run once per window, with the relevant window
-selected; while the global value is run only once for the modified frame,
-with the relevant frame selected. */);
- Vwindow_configuration_change_hook = Qnil;
+ DEFVAR_LISP ("window-buffer-change-functions", Vwindow_buffer_change_functions,
+ doc: /* Functions called during redisplay when window buffers have changed.
+The value should be a list of functions that take one argument.
+
+Functions specified buffer-locally are called for each window showing
+the corresponding buffer if and only if that window has been added or
+changed its buffer since the last redisplay. In this case the window
+is passed as argument.
+
+Functions specified by the default value are called for each frame if
+at least one window on that frame has been added, deleted or changed
+its buffer since the last redisplay. In this case the frame is passed
+as argument. */);
+ Vwindow_buffer_change_functions = Qnil;
DEFVAR_LISP ("window-size-change-functions", Vwindow_size_change_functions,
- doc: /* Functions called during redisplay, if window sizes have changed.
+ doc: /* Functions called during redisplay when 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. */);
+
+Functions specified buffer-locally are called for each window showing
+the corresponding buffer if and only if that window has been added or
+changed its buffer or its total or body size since the last redisplay.
+In this case the window is passed as argument.
+
+Functions specified by the default value are called for each frame if
+at least one window on that frame has been added or changed its buffer
+or its total or body size since the last redisplay. In this case the
+frame is passed as argument. */);
Vwindow_size_change_functions = Qnil;
+ DEFVAR_LISP ("window-selection-change-functions", Vwindow_selection_change_functions,
+ doc: /* Functions called during redisplay when the selected window has changed.
+The value should be a list of functions that take one argument.
+
+Functions specified buffer-locally are called for each window showing
+the corresponding buffer if and only if that window has been selected
+or deselected since the last redisplay. In this case the window is
+passed as argument.
+
+Functions specified by the default value are called for each frame if
+the frame's selected window has changed since the last redisplay. In
+this case the frame is passed as argument. */);
+ Vwindow_selection_change_functions = Qnil;
+
+ DEFVAR_LISP ("window-configuration-change-hook", Vwindow_configuration_change_hook,
+ doc: /* Functions called during redisplay when window configuration has changed.
+The value should be a list of functions that take no argument.
+
+Functions specified buffer-locally are called for each window showing
+the corresponding buffer if at least one window on that frame has been
+added, deleted or changed its buffer or its total or body size since
+the last redisplay. Each call is performed with the window showing
+the buffer temporarily selected.
+
+Functions specified by the default value are called for each frame if
+at least one window on that frame has been added, deleted or changed
+its buffer or its total or body size since the last redisplay. Each
+call is performed with the frame temporarily selected. */);
+ Vwindow_configuration_change_hook = 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
@@ -7817,6 +8161,7 @@ displayed after a scrolling operation to be somewhat inaccurate. */);
Vfast_but_imprecise_scrolling = false;
defsubr (&Sselected_window);
+ defsubr (&Sold_selected_window);
defsubr (&Sminibuffer_window);
defsubr (&Swindow_minibuffer_p);
defsubr (&Swindowp);
@@ -7826,10 +8171,12 @@ displayed after a scrolling operation to be somewhat inaccurate. */);
defsubr (&Sframe_root_window);
defsubr (&Sframe_first_window);
defsubr (&Sframe_selected_window);
+ defsubr (&Sframe_old_selected_window);
defsubr (&Sset_frame_selected_window);
defsubr (&Spos_visible_in_window_p);
defsubr (&Swindow_line_height);
defsubr (&Swindow_buffer);
+ defsubr (&Swindow_old_buffer);
defsubr (&Swindow_parent);
defsubr (&Swindow_top_child);
defsubr (&Swindow_left_child);
@@ -7840,8 +8187,10 @@ displayed after a scrolling operation to be somewhat inaccurate. */);
defsubr (&Swindow_use_time);
defsubr (&Swindow_pixel_width);
defsubr (&Swindow_pixel_height);
- defsubr (&Swindow_pixel_width_before_size_change);
- defsubr (&Swindow_pixel_height_before_size_change);
+ defsubr (&Swindow_old_pixel_width);
+ defsubr (&Swindow_old_pixel_height);
+ defsubr (&Swindow_old_body_pixel_width);
+ defsubr (&Swindow_old_body_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 ee6ec3bb19a..9c4aea85ea6 100644
--- a/src/window.h
+++ b/src/window.h
@@ -142,6 +142,11 @@ struct window
as well. */
Lisp_Object contents;
+ /* The old buffer of this window, set to this window's buffer by
+ run_window_change_functions every time it sees this window.
+ Unused for internal windows. */
+ Lisp_Object old_buffer;
+
/* A marker pointing to where in the text to start displaying.
BIDI Note: This is the _logical-order_ start, i.e. the smallest
buffer position visible in the window, not necessarily the
@@ -229,6 +234,14 @@ struct window
/* Unique number of window assigned when it was created. */
EMACS_INT sequence_number;
+ /* The change stamp of this window. Set to 0 when the window is
+ created, it is set to its frame's change stamp every time
+ run_window_change_functions is run on that frame with this
+ window live. It is left alone when the window exists only
+ within a window configuration. Not useful for internal
+ windows. */
+ int change_stamp;
+
/* The upper left corner pixel coordinates of this window, as
integers relative to upper left corner of frame = 0, 0. */
int pixel_left;
@@ -243,10 +256,13 @@ 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 pixel_width_before_size_change;
- int pixel_height_before_size_change;
+ /* The pixel and pixel body sizes of the window at the last time
+ run_window_change_functions was run with this window live. Not
+ useful for internal windows. */
+ int old_pixel_width;
+ int old_pixel_height;
+ int old_body_pixel_width;
+ int old_body_pixel_height;
/* The size of the window. */
int total_cols;
@@ -1023,6 +1039,7 @@ wset_next_buffers (struct window *w, Lisp_Object val)
This value is always the same as FRAME_SELECTED_WINDOW (selected_frame). */
extern Lisp_Object selected_window;
+extern Lisp_Object old_selected_window;
/* This is a time stamp for window selection, so we can find the least
recently used window. Its only users are Fselect_window,
@@ -1051,7 +1068,7 @@ extern void grow_mini_window (struct window *, int, bool);
extern void shrink_mini_window (struct window *, bool);
extern int window_relative_x_coord (struct window *, enum window_part, int);
-void run_window_size_change_functions (Lisp_Object);
+void run_window_change_functions (void);
/* 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 7725570ced3..86495078fb6 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -2786,6 +2786,7 @@ init_iterator (struct it *it, struct window *w,
struct glyph_row *row, enum face_id base_face_id)
{
enum face_id remapped_base_face_id = base_face_id;
+ int body_width = 0, body_height = 0;
/* Some precondition checks. */
eassert (w != NULL && it != NULL);
@@ -2962,7 +2963,7 @@ init_iterator (struct it *it, struct window *w,
{
/* Mode lines, menu bar in terminal frames. */
it->first_visible_x = 0;
- it->last_visible_x = WINDOW_PIXEL_WIDTH (w);
+ it->last_visible_x = body_width = WINDOW_PIXEL_WIDTH (w);
}
else
{
@@ -2982,8 +2983,12 @@ init_iterator (struct it *it, struct window *w,
else
it->first_visible_x =
window_hscroll_limited (w, it->f) * FRAME_COLUMN_WIDTH (it->f);
- it->last_visible_x = (it->first_visible_x
- + window_box_width (w, TEXT_AREA));
+
+ body_width = window_box_width (w, TEXT_AREA);
+ if (!w->pseudo_window_p && !MINI_WINDOW_P (w)
+ && body_width != w->old_body_pixel_width)
+ FRAME_WINDOW_CHANGE (it->f) = true;
+ it->last_visible_x = it->first_visible_x + body_width;
/* If we truncate lines, leave room for the truncation glyph(s) at
the right margin. Otherwise, leave room for the continuation
@@ -2997,7 +3002,8 @@ init_iterator (struct it *it, struct window *w,
}
it->header_line_p = window_wants_header_line (w);
- it->current_y = WINDOW_HEADER_LINE_HEIGHT (w) + w->vscroll;
+ body_height = WINDOW_HEADER_LINE_HEIGHT (w);
+ it->current_y = body_height + w->vscroll;
}
/* Leave room for a border glyph. */
@@ -3006,6 +3012,10 @@ init_iterator (struct it *it, struct window *w,
it->last_visible_x -= 1;
it->last_visible_y = window_text_bottom_y (w);
+ body_height += it->last_visible_y;
+ if (!w->pseudo_window_p && !MINI_WINDOW_P (w)
+ && body_height != w->old_body_pixel_height)
+ FRAME_WINDOW_CHANGE (it->f) = true;
/* For mode lines and alike, arrange for the first glyph having a
left box line if the face specifies a box. */
@@ -12200,8 +12210,6 @@ prepare_menu_bars (void)
&& !XBUFFER (w->contents)->text->redisplay)
continue;
- run_window_size_change_functions (frame);
-
if (FRAME_PARENT_FRAME (f))
continue;
@@ -14119,20 +14127,6 @@ 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)
- {
- ptrdiff_t count1 = SPECPDL_INDEX ();
-
- record_unwind_save_match_data ();
- run_window_size_change_functions (selected_frame);
- unbind_to (count1, Qnil);
- }
-
if (message_cleared_p)
update_miniwindow_p = true;
@@ -14149,15 +14143,6 @@ redisplay_internal (void)
&& (current_buffer->clip_changed || window_outdated (w))
&& resize_mini_window (w, false))
{
- if (sf->redisplay)
- {
- ptrdiff_t count1 = SPECPDL_INDEX ();
-
- record_unwind_save_match_data ();
- run_window_size_change_functions (selected_frame);
- 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;
@@ -14347,7 +14332,19 @@ redisplay_internal (void)
&& (w = XWINDOW (selected_window)) != sw)
goto retry;
- /* We used to always goto end_of_redisplay here, but this
+ if (!NILP (Vrun_hooks))
+ {
+ run_window_change_functions ();
+
+ /* If windows or buffers changed or selected_window
+ changed, redisplay again. */
+ if ((windows_or_buffers_changed)
+ || (WINDOWP (selected_window)
+ && (w = XWINDOW (selected_window)) != sw))
+ goto retry;
+ }
+
+ /* We used to always goto end_of_redisplay here, but this
isn't enough if we have a blinking cursor. */
if (w->cursor_off_p == w->last_cursor_off_p)
goto end_of_redisplay;
@@ -14706,9 +14703,22 @@ redisplay_internal (void)
/* If we just did a pending size change, or have additional
visible frames, or selected_window changed, redisplay again. */
if ((windows_or_buffers_changed && !pending)
- || (WINDOWP (selected_window) && (w = XWINDOW (selected_window)) != sw))
+ || (WINDOWP (selected_window)
+ && (w = XWINDOW (selected_window)) != sw))
goto retry;
+ if (!NILP (Vrun_hooks))
+ {
+ run_window_change_functions ();
+
+ /* If windows or buffers changed or selected_window changed,
+ redisplay again. */
+ if ((windows_or_buffers_changed)
+ || (WINDOWP (selected_window)
+ && (w = XWINDOW (selected_window)) != sw))
+ goto retry;
+ }
+
/* Clear the face and image caches.
We used to do this only if consider_all_windows_p. But the cache