1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-04-11 16:18:06 -05:00

Try to ensure term_size() after win_resize_request().

When the terminal asks its TermWin for a resize, the resize operation
happens asynchronously (or can do), and sooner or later, the terminal
will see a term_size() telling it the resize has actually taken
effect.

If the resize _doesn't_ take effect for any reason - e.g. because the
window is maximised, or because the X11 window manager is a tiling one
which will refuse requests to change the window size anyway - then the
terminal never got any explicit notification of refusal to resize. Now
it should, in as many cases as I can arrange.

One obvious case of this is the early exit I recently added to
gtkwin_request_resize() when the window is known to be in a maximised
or tiled state preventing a resize: in that situation, when our own
code knows we're not even attempting the resize, we also queue a
toplevel callback to tell the terminal so.

The more interesting case is when the request is refused for a reason
GTK _didn't_ know in advance, e.g. because the user is running an X11
tiling window manager such as i3, which generally refuses windows'
resize requests. In X11, if a window manager refuses an attempt to
change the window's size via ConfigureWindow, ICCCM says it should
respond by sending a synthetic ConfigureNotify event restating the
same size. Such no-op configure events do reach the "configure_event"
handler in a GTK program, but they weren't previously getting as far
as term_size(), because the call to term_size() was triggered from the
GTK "size_allocate" event on the GtkDrawingArea inside the window (via
drawing_area_setup()), so GTK would detect that nothing had changed.

Now we queue a last-ditch toplevel callback which ensures that if the
configure event doesn't also cause a size_allocate and a call to
drawing_area_setup(), then we cause one of our own once the dust has
settled. And drawing_area_setup(), in turn, now unconditionally calls
term_size() even if the size is the same as it was last time, instead
of taking an early exit. (It still does take an early exit to avoid
unnecessary faffing with Cairo surfaces etc, but _after_ term_size()).

This won't be 100% reliable, because it's the window manager's
responsibility to send those synthetic ConfigureNotify events, and a
window manager is a fallible process which could get into a stuck
state. But it covers all the cases I know of that _can_ be sensibly
covered - so now, when terminal.c asks the front end to resize the
window, it ought to find out in a timely manner whether or not that
has happened, in _almost_ all cases.
This commit is contained in:
Simon Tatham 2021-12-19 10:21:11 +00:00
parent be0cea7130
commit 19b12ee56c

View File

@ -89,6 +89,7 @@ struct GtkFrontend {
gboolean sbar_visible;
gboolean drawing_area_got_size, drawing_area_realised;
gboolean drawing_area_setup_needed;
bool drawing_area_setup_called;
GtkBox *hbox;
GtkAdjustment *sbar_adjust;
GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2,
@ -700,7 +701,6 @@ static void draw_backing_rect(GtkFrontend *inst);
static void drawing_area_setup(GtkFrontend *inst, int width, int height)
{
int w, h, new_scale;
bool need_size = false;
/*
* See if the terminal size has changed.
@ -716,11 +716,7 @@ static void drawing_area_setup(GtkFrontend *inst, int width, int height)
conf_set_int(inst->conf, CONF_width, inst->width);
conf_set_int(inst->conf, CONF_height, inst->height);
/*
* We'll need to tell terminal.c about the resize below.
*/
need_size = true;
/*
* And we must refresh the window's backing image.
* We must refresh the window's backing image.
*/
inst->drawing_area_setup_needed = true;
}
@ -742,10 +738,23 @@ static void drawing_area_setup(GtkFrontend *inst, int width, int height)
inst->drawing_area_setup_needed = true;
/*
* This event might be spurious; some GTK setups have been known
* to call it when nothing at all has changed. Check if we have
* any reason to proceed.
* GTK will sometimes send us configure events when nothing about
* the window size has actually changed. In some situations this
* can happen quite often, so it's a worthwhile optimisation to
* detect that situation and avoid the expensive reinitialisation
* of the backing surface / image, and so on.
*
* However, we must still communicate to the terminal that we
* received a resize event, because sometimes a trivial resize
* event (to the same size we already were) is a signal from the
* window system that a _nontrivial_ resize we recently asked for
* has failed to happen.
*/
inst->drawing_area_setup_called = true;
if (inst->term)
term_size(inst->term, h, w, conf_get_int(inst->conf, CONF_savelines));
if (!inst->drawing_area_setup_needed)
return;
@ -776,10 +785,6 @@ static void drawing_area_setup(GtkFrontend *inst, int width, int height)
draw_backing_rect(inst);
if (need_size && inst->term) {
term_size(inst->term, h, w, conf_get_int(inst->conf, CONF_savelines));
}
if (inst->term)
term_invalidate(inst->term);
@ -806,6 +811,14 @@ static void drawing_area_setup_simple(GtkFrontend *inst)
drawing_area_setup(inst, alloc.width, alloc.height);
}
static void drawing_area_setup_cb(void *vctx)
{
GtkFrontend *inst = (GtkFrontend *)vctx;
if (!inst->drawing_area_setup_called)
drawing_area_setup_simple(inst);
}
static void area_realised(GtkWidget *widget, GtkFrontend *inst)
{
inst->drawing_area_realised = true;
@ -844,6 +857,10 @@ static gboolean window_configured(
term_notify_window_pos(inst->term, event->x, event->y);
term_notify_window_size_pixels(
inst->term, event->width, event->height);
if (inst->drawing_area_realised && inst->drawing_area_got_size) {
inst->drawing_area_setup_called = false;
queue_toplevel_callback(drawing_area_setup_cb, inst);
}
}
return false;
}
@ -2450,6 +2467,14 @@ static void compute_whole_window_size(GtkFrontend *inst,
int *wpix, int *hpix);
#endif
static void gtkwin_deny_term_resize(void *vctx)
{
GtkFrontend *inst = (GtkFrontend *)vctx;
if (inst->term)
term_size(inst->term, inst->term->rows, inst->term->cols,
inst->term->savelines);
}
static void gtkwin_request_resize(TermWin *tw, int w, int h)
{
GtkFrontend *inst = container_of(tw, GtkFrontend, termwin);
@ -2494,6 +2519,7 @@ static void gtkwin_request_resize(TermWin *tw, int w, int h)
GDK_WINDOW_STATE_LEFT_TILED |
#endif
0)) {
queue_toplevel_callback(gtkwin_deny_term_resize, inst);
return;
}
}