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
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
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},
@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-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}).
+
+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
return make_number (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.
+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)->pixel_width_before_size_change));
+}
+
+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.
+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)->pixel_height_before_size_change));
+}
+
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.
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);
fset_redisplay (f);
Vwindow_list = Qnil;
- FRAME_WINDOW_SIZES_CHANGED (f) = true;
- bool resize_failed = false;
if (!WINDOW_LEAF_P (w))
{
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->pixel_width_before_size_change
+ || w->pixel_height != w->pixel_height_before_size_change)
+ 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 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)
+{
+ w->pixel_width_before_size_change = w->pixel_width;
+ w->pixel_height_before_size_change = w->pixel_height;
+
+ if (WINDOW_INTERNAL_P (w))
+ {
+ w = XWINDOW (w->contents);
+ while (w)
+ {
+ window_set_before_size_change_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_before_size_change_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
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;
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
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)
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;
window_resize_apply (r, horflag);
fset_redisplay (f);
- FRAME_WINDOW_SIZES_CHANGED (f) = true;
adjust_frame_glyphs (f);
unblock_input ();
}
}
- FRAME_WINDOW_SIZES_CHANGED (f) = true;
fset_redisplay (f);
}
p = XWINDOW (o->parent);
fset_redisplay (f);
- FRAME_WINDOW_SIZES_CHANGED (f) = true;
new = make_window ();
n = XWINDOW (new);
wset_frame (n, 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);
}
else
unblock_input ();
-
- /* Must be run by the caller:
- run_window_configuration_change_hook (f); */
}
else
/* We failed: Relink WINDOW into window tree. */
/* 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 ();
}
/* 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 ();
}
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;
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;
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. */
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)))
}
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
w->pixel_top = XFASTINT (p->pixel_top);
w->pixel_width = XFASTINT (p->pixel_width);
w->pixel_height = XFASTINT (p->pixel_height);
+ w->pixel_width_before_size_change
+ = XFASTINT (p->pixel_width_before_size_change);
+ w->pixel_height_before_size_change
+ = XFASTINT (p->pixel_height_before_size_change);
w->left_col = XFASTINT (p->left_col);
w->top_line = XFASTINT (p->top_line);
w->total_cols = XFASTINT (p->total_cols);
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);
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
/* 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. */
/* 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
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))
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->pixel_width_before_size_change
+ = make_number (w->pixel_width_before_size_change);
+ p->pixel_height_before_size_change
+ = make_number (w->pixel_height_before_size_change);
p->left_col = make_number (w->left_col);
p->top_line = make_number (w->top_line);
p->total_cols = make_number (w->total_cols);
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
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_total_width);
defsubr (&Swindow_total_height);
defsubr (&Swindow_normal_size);
&& !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);
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);
}
{
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);
}
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