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

From: Eli Zaretskii <[email-masked]>
Date: Fri, 28 Aug 2015 13:02:01 +0300

> Date: Thu, 27 Aug 2015 20:49:17 +0000
> From: Pip Cet <[email-masked]>
> Cc: [email-masked], [email-masked]
> 
>     > The call to grow/shrink_mini_window only recomputes data
>     > about the windows for the next redisplay cycle.
>     >
>     > No. It computes data about the windows for the cycle that's currently
>     > happening, that has already called prepare_menu_bars and will most likely
>     not
>     > do so again.
>     
>     That's exactly what I said, just in other words.
> 
> I'm sorry, I totally misread that, then.

I don't think you've misread, no.  See below.

> I think of redisplay cycles as beginning when redisplay() is called
> and ending when it returns, so in my terminology the "next"
> redisplay cycle is the one that will happen after control briefly
> (or not so briefly, see below) returns to the command loop and
> redisplay() is called again.

That's my interpretation as well.

There's some misunderstanding here, but I'm not sure what it is.  We
keep saying the same things about the higher levels, and for me it's
clear that our argument is about lower-level details, not about the
high-level overall picture.  But you for some reason think we disagree
on that higher level.

Upon re-reading the thread, it's possible that the misunderstanding is
due to the following factors:

 . it's not always clear whether we are talking about what would
   happen _after_ resize_mini_window is changed to raise the frame's
   "windows changed" flag, or about the current code where it doesn't;

 . both window-size-change-functions and pre-redisplay-function is
   being discussed, although it should be clear their purpose is
   different, and in particular pre-redisplay-function cannot be moved
   from where it currently lives, at least not significantly: it must
   run _before_ the bulk of the redisplay code runs, or else it will
   fail to fulfill its contract; and

 . you introduce issues into the discussion that are not directly
   related to it, which just muddies the waters, for example:

>     > Note that grow_mini_window is called by redisplay_internal, via
>     > resize_mini_window, not just by display_echo_area.
>     
>     They are both called _after_ prepare_menu_bars.
> 
> Except when they're not, I've just seen this backtrace:
> 
> #0 grow_mini_window (w=0x1265ef0, delta=210, pixelwise=true) at window.c:4488
> #1 0x0000000000452c01 in resize_mini_window (w=0x1265ef0, exact_p=true) at
> xdisp.c:10833
> #2 0x0000000000426fb9 in do_switch_frame (frame=54632789, track=1,
> for_deletion=0, norecord=43920) at frame.c:1163
> #3 0x000000000042718d in Fselect_frame (frame=54632789, norecord=43920) at
> frame.c:1225
> #4 0x0000000000496566 in select_window (window=54633277, norecord=43920,
> inhibit_point_swap=false) at window.c:499
> #5 0x00000000004967de in Fselect_window (window=54633277, norecord=43920) at
> window.c:575
> #6 0x0000000000453fe7 in x_consider_frame_title (frame=54632789) at
> xdisp.c:11487
> #7 0x0000000000454437 in prepare_menu_bars () at xdisp.c:11595

This code path is realized when Emacs needs to perform a thorough
redisplay, which happens relatively rarely.  How does adding this
complication to the already troubled discussion help to reach an
agreement and move on?  IMO, we should first agree on what happens in
99% of cases and on solutions to that, and then see if the remaining
1% needs any minor corrections.  By contrast, if we keep adding more
and more marginal cases, we are just expanding the tree of issues that
need to be discussed without control and without any hope to advance
any time soon.

>     So if
>     resize_mini_window, however it is called, sets the flag that windows
>     has been resized, only the next redisplay cycle will notice that and
>     call the window-size-change-functions.
> 
> But here you're using "next redisplay cycle" to mean the one that has not been
> started (redisplay() hasn't been called yet), not the one we're currently in.

No, I mean the next one.  Because the frame's "windows changed" flag
is tested before the flag is set (if it will be set by
resize_mini_window).

> I had assumed your message referred to the state of Emacs as of HEAD, not with
> my proposed changes included.

It was.

>     > The call order is that redisplay_internal calls prepare_menu_bars, then
>     calls
>     > grow_mini_window, then performs the frame update. It doesn't go back to
>     calling
>     > prepare_menu_bars, but it does call update_frame, and that actually does
>     its
>     > job.
>     
>     Yes, and that is not what you want because?...
> 
> Because the next call to redisplay() might not be until five seconds later.
> 
>     > When that next cycle comes, it will first call pre-redisplay-function
>     >
>     > Yes. With a nil argument. I don't fully understand why.
>     >
>     > and window-size-change-functions
>     >
>     > No. Miniwindow resizes do not set the WINDOW_SIZES_CHANGED flag even if
>     they
>     > resize other windows.
>     
>     I was talking about the situation after you proposed changes, which
>     will cause the flag to be set (AFAIU).
>     
> 
> That wasn't clear to me. If you're asking "will you shut up already if the
> changes you've already proposed go in", the answer is yes.
> 
> So, in the hopes of finally clarifying matters:
> 
> I want two things. 
> Wish #1: a way of knowing "when" windows are resized or moved, even
> temporarily. Whether that "when" is "just before" or "just after" doesn't
> really matter, but it should be one of those.

This wish needs to be described in more detail.  Resizing of a window
is done in several distinct steps:

 . some function (resize_mini_window, if we are talking about the
   mini-window) decides whether a window should be resized, and if so,
   computes its start position;

 . redisplay_window, called by redisplay_internal, computes the
   "desired" contents of the window on screen;

 . update_window, called by update_frame, actually delivers the needed
   changes to the glass

I hope it is clear that, depending on what exactly the hook function
wants to do with the info about resizing, it could or should be called
in different parts of the code, which are involved in these steps.
And please keep in mind that the first 2 steps mentioned above are not
in a single place in the code.  For example, redisplay_window attempts
several optimizations, each one of which might eventually succeed; or
all of them might fail, indicating to redisplay_internal that a
thorough redisplay of the entire frame is needed.

> Wish #2: a way of knowing, in advance, that a window is going to be resized or
> moved, even temporarily, before any of that hits the X server. That would
> eliminate the (small) window left by #1 when the X server has been updated but
> redisplay hasn't been called again.

See above: this seems to indicate that your wishlist hook needs to be
called somewhere inside update_window.  But where exactly, and whether
indeed update_window is the right place, depends on what would you
like to do with the resizing information.  E.g., if you want to do
something that will require another redisplay, then update_window is
not the right place.

In addition, it is not clear whether your hook will care if the window
actually didn't change at all.  That is something update_window
decides on a screen line by screen line basis, as it goes about its
job.  The result can only be known at the end, and it's quite possible
that we will have to add code to collect the results of individual
screen lines (I didn't check if we already do something like that).

Also, you never actually explained, AFAIR, why it is so important for
you to be called "just before the change hits the X server".

And finally, I hope you will agree now that pre-redisplay-function is
not the right vehicle for these 2 wishes, because your wishes cannot
be granted before redisplay did most of its job regarding the window
in question.

> Wish #1, assuming something like my proposed changes go in and this bug is
> closed, has been granted; there's a race condition, but I might just have to
> live with that.

Do you still think it has been granted?  At the very best, you will be
able to obtain part of the information about the window being resized;
the rest of it, which redisplay still needs to figure out, will not
yet be available.  For example, if the window being resized is the
mini-window, the buffer position where the window above the
mini-window ends will not yet be known.

> Wish #2 eliminates the race condition.

Maybe, but it also limits what you can usefully do from such a hook.

>     > No. The sequence is redisplay_internal, then prepare_menu_bars, then
>     > grow_mini_window, then update_frame.
>     
>     But grow_mini_window only recomputes the start of the window, it does
>     not redisplay it. The next cycle will.
> 
> The one I call "the current cycle"?

I meant the one we both call "the next".  On further thought, it's
possible that the current cycle will do that, at least in some
situations.  It all depends on how redisplay was entered and which
windows/buffers have their 'redisplay' flags set.

>     The function update_frame only reflects on the glass what its
>     redisplay cycle computed to be the desired display. If redisplay
>     didn't recompute the window contents, update_frame will change
>     nothing.
> 
> That's not what I seeing running x_sync manually.

I don't understand what that means, but you will see in update_frame
and its subroutines code that compares the current and the desired
contents of the displayed windows.  That code only redraws the
portions that actually changed.

When redisplay decides nothing needs to be recomputed, the current and
the desired contents will be identical.

> 1. user input
> 2. redisplay called

That is inaccurate.  Redisplay is called after Emacs returns to its
main loop and finds that no input is available.  Emacs returns to the
main loop after processing some command, which could be triggered by
user input, or by input from a subprocess, or by some timer that
became ripe, or by input from D-bus, or from any number of additional
input sources.

> 3. prepare_menu_bars called, calls hooks. Hooks think nothing has changed.
> 4. grow_mini_window called, calculates new window size

You omitted the main part of redisplay here, entered via
redisplay_windows (to potentially redisplay all windows on a frame) or
redisplay_window_1 (to redisplay only the selected window).  That is
where data required by update_frame is computed.

> 5. update_frame is called, writes the new, enlarged mini-window to the glass
> 6. control returns to command loop
> 7. redisplay is called again
> 8. prepare_menu_bars called, calls hooks. Hooks notice mini-window has resized.
> 
> Right?

Given what I wrote above, I think you will understand when I answer
"yes and no".  For starters, when you say "hooks" which one(s) do you
have in mind?

> But there's no guarantee that there won't be intermediate commands executed
> between steps 6 and 7. In fact (progn (message "long message") (sleep 10.0))
> will make step 8 happen ten seconds after the changed sizes have been written
> to the glass.
> 
> Wish #2 would mean swapping steps 3 and 4.

For which hook?  You cannot do that for pre-redisplay-function,
because it _must_ run before redisplay.  (And "swap" is inaccurate,
see my explanations above.)