1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Windows: reinstate redraws during interactive scrollbar drag.

I just discovered that they weren't happening, and the reason why is
thoroughly annoying. Details are in the long comment I've added to the
WM_VSCROLL handler in WndProc, but the short version is that when you
interactively drag the terminal window's scrollbar, a subsidiary
message loop is launched by DefWndProc, causing all our timer events
to go missing until the user lets go of the scrollbar again. So we
have to manually update the terminal window on scroll events, because
the normal system is out of action.

I assume this changed behaviour round about the big rework of terminal
updating in February. Good job I spotted it just _before_ 0.75, and
not just after!
This commit is contained in:
Simon Tatham 2021-04-24 19:43:43 +01:00
parent 8c7685c65d
commit f69cf86a61

View File

@ -2178,6 +2178,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
static bool ignore_clip = false;
static bool fullscr_on_max = false;
static bool processed_resize = false;
static bool in_scrollbar_loop = false;
static UINT last_mousemove = 0;
int resize_action;
@ -2232,6 +2233,21 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
case WM_COMMAND:
case WM_SYSCOMMAND:
switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
case SC_VSCROLL:
case SC_HSCROLL:
if (message == WM_SYSCOMMAND) {
/* As per the long comment in WM_VSCROLL handler: give
* this message the default handling, which starts a
* subsidiary message loop, but set a flag so that
* when we're re-entered from that loop, scroll events
* within an interactive scrollbar-drag can be handled
* differently. */
in_scrollbar_loop = true;
LRESULT result = DefWindowProcW(hwnd, message, wParam, lParam);
in_scrollbar_loop = false;
return result;
}
break;
case IDM_SHOWLOG:
showeventlog(hwnd);
break;
@ -3144,6 +3160,54 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
if (GetScrollInfo(hwnd, SB_VERT, &si) == 0)
si.nTrackPos = HIWORD(wParam);
term_scroll(term, 1, si.nTrackPos);
if (in_scrollbar_loop) {
/*
* Allow window updates to happen during interactive
* scroll.
*
* When the user takes hold of our window's scrollbar
* and wobbles it interactively back and forth, the
* first thing that happens is that this window
* procedure receives WM_SYSCOMMAND / SC_VSCROLL. [1]
* The default handler for that window message starts
* a subsidiary message loop, which continues to run
* until the user lets go of the scrollbar again. All
* WM_VSCROLL / SB_THUMBTRACK messages are generated
* by the handlers within that subsidiary message
* loop.
*
* So, during that time, _our_ message loop is not
* running, which means toplevel callbacks and timers
* and so forth are not happening, which means that
* when we redraw the window and set a timer to clear
* the cooldown flag 20ms later, that timer never
* fires, and we aren't able to keep redrawing the
* window.
*
* The 'obvious' answer would be to seize that
* SYSCOMMAND ourselves and inhibit the default
* handler, so that our message loop carries on
* running. But that would mean we'd have to
* reimplement the whole of the scrollbar handler!
*
* So instead we apply a bodge: set a static variable
* that indicates that we're _in_ that sub-loop, and
* if so, decide it's OK to manually call
* term_update() proper, bypassing the timer and
* cooldown and rate-limiting systems completely,
* whenever we see an SB_THUMBTRACK. This shouldn't
* cause a rate overload, because we're only doing it
* once per UI event!
*
* [1] Actually, there's an extra oddity where
* SC_HSCROLL and SC_VSCROLL have their documented
* values the wrong way round. Many people on the
* Internet have noticed this, e.g.
* https://stackoverflow.com/q/55528397
*/
term_update(term);
}
break;
}
}