Try to resize or resize-and-move child frames in one update

This pertains to X11 toolkit builds.  Other ports (including
PGTK) seem to have mostly atomic window updates already.

* src/xterm.c (x_set_window_size_1): Resize the Xt widget eagerly,
so the next redraw is not clipped.  Update the "desired" Cairo
surface dimensions.  Skip waiting for next XEvent.  Do all that
for child frames only.  Update old comments (bug#80662).
(x_set_window_size_and_position_1): Same.  Also clear the widget's
cached position coordinates (we don't keep them up to date).
(x_set_window_size, x_set_window_size_and_position): Skip
redrawing the border on child frames, it will happen during
redisplay anyway.

* src/gtkutil.c (xg_frame_set_size_and_position): On child
frames, resize the GTK widget hierarchy immediately.  Update the
"desired" Cairo surface dimensions.  Skip waiting for events.

* src/widget.c (EmacsFrameResize):
When resize should be a no-op, exit early (minor optimization).
(EmacsFrameExpose): Redraw the border on the last Expose event.

* src/xdisp.c (clear_garbaged_frames): Don't redraw borders here.

* src/xfns.c (x_window): Undo the previous change in bit_gravity
in the no-toolkit build.  StaticGravity works the best for it
thanks to no nesting in window configuration.
This commit is contained in:
Dmitry Gutov 2026-05-01 02:19:40 +03:00
parent da4ab3d738
commit a6a3b32208
5 changed files with 74 additions and 30 deletions

View file

@ -1421,6 +1421,16 @@ xg_frame_set_size_and_position (struct frame *f, int width, int height)
#ifndef HAVE_PGTK
gdk_window_move_resize (gwin, x, y, outer_width, outer_height);
if (FRAME_PARENT_FRAME (f))
{
/* Resize all inner widgets and Cairo surface right away so the
next redisplay drawing isn't clipped to the old size. */
GtkAllocation alloc = {0, 0, outer_width, outer_height};
gtk_widget_size_allocate (FRAME_GTK_OUTER_WIDGET (f), &alloc);
#ifdef USE_CAIRO
x_cr_update_surface_desired_size (f, width, height);
#endif
}
#else
if (FRAME_GTK_OUTER_WIDGET (f))
gdk_window_move_resize (gwin, x, y, outer_width, outer_height);
@ -1432,7 +1442,11 @@ xg_frame_set_size_and_position (struct frame *f, int width, int height)
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
if (FRAME_VISIBLE_P (f))
/* For child frames, don't wait for events — that would flush the X
buffer and might show outdated contents in the frame. Same for
invisible frames: this way is faster and x_make_frame_visible will
wait for event anyway. */
if (FRAME_VISIBLE_P (f) && !FRAME_PARENT_FRAME (f))
{
/* Must call this to flush out events */
(void)gtk_events_pending ();

View file

@ -428,6 +428,10 @@ EmacsFrameResize (Widget widget)
ew->core.width, ew->core.height,
f->new_width, f->new_height);
if (FRAME_PIXEL_WIDTH (f) == ew->core.width
&& FRAME_PIXEL_HEIGHT (f) == ew->core.height)
return;
change_frame_size (f, ew->core.width, ew->core.height,
false, true, false);
@ -495,6 +499,8 @@ EmacsFrameExpose (Widget widget, XEvent *event, Region region)
expose_frame (f, event->xexpose.x, event->xexpose.y,
event->xexpose.width, event->xexpose.height);
if (event->xexpose.count == 0)
x_clear_under_internal_border (f);
flush_frame (f);
}

View file

@ -13661,11 +13661,6 @@ clear_garbaged_frames (void)
if (is_tty_frame (f))
current_matrices_cleared = true;
#ifdef HAVE_WINDOW_SYSTEM
if (FRAME_WINDOW_P (f)
&& FRAME_RIF (f)->clear_under_internal_border)
FRAME_RIF (f)->clear_under_internal_border (f);
#endif
fset_redisplay (f);
f->garbaged = false;
f->resized_p = false;

View file

@ -4483,7 +4483,7 @@ x_window (struct frame *f)
attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f);
attributes.border_pixel = f->output_data.x->border_pixel;
attributes.bit_gravity = NorthWestGravity;
attributes.bit_gravity = StaticGravity;
attributes.backing_store = NotUseful;
attributes.save_under = True;
attributes.event_mask = STANDARD_EVENT_SET;

View file

@ -28484,6 +28484,19 @@ x_set_window_size_1 (struct frame *f, bool change_gravity,
f->win_gravity = NorthWestGravity;
x_wm_set_size_hint (f, 0, false);
#ifdef USE_X_TOOLKIT
if (FRAME_PARENT_FRAME (f) && f->output_data.x->widget)
{
/* Resize all inner widgets and Cairo surface right away so the
next redisplay drawing isn't clipped to the old size. */
XtResizeWidget (f->output_data.x->widget,
width, height + FRAME_MENUBAR_HEIGHT (f), 0);
#ifdef USE_CAIRO
x_cr_update_surface_desired_size (f, width, height);
#endif
}
else
#endif
XResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
width, height + FRAME_MENUBAR_HEIGHT (f));
@ -28499,30 +28512,25 @@ x_set_window_size_1 (struct frame *f, bool change_gravity,
if (!NILP (Vx_lax_frame_positioning))
return;
/* Now, strictly speaking, we can't be sure that this is accurate,
/* Now, strictly speaking, we can't be sure that this is final,
but the window manager will get around to dealing with the size
change request eventually, and we'll hear how it went when the
ConfigureNotify event gets here.
We could just not bother storing any of this information here,
and let the ConfigureNotify event set everything up, but that
might be kind of confusing to the Lisp code, since size changes
wouldn't be reported in the frame parameters until some random
point in the future when the ConfigureNotify event arrives.
Pass true for DELAY since we can't run Lisp code inside of
a BLOCK_INPUT. */
/* But the ConfigureNotify may in fact never arrive, and then this is
not right if the frame is visible. Instead wait (with timeout)
for the ConfigureNotify. */
if (FRAME_VISIBLE_P (f))
We could just let the ConfigureNotify update everything, but
waiting creates an implicit X flush which might flicker with
outdated contents in the frame. For child frames, the window
manager is not a concern and it's better to finish quickly. */
if (FRAME_VISIBLE_P (f) && !FRAME_PARENT_FRAME (f))
{
/* The event may create delayed size change (delayed because we
can't run Lisp code inside of a BLOCK_INPUT) which will be
applied right after by do_pending_window_change. */
x_wait_for_event (f, ConfigureNotify);
if (CONSP (frame_size_history))
frame_size_history_extra
(f, build_string ("x_set_window_size_1, visible"),
(f, build_string ("x_set_window_size_1, waited for event"),
FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
f->new_width, f->new_height);
}
@ -28530,7 +28538,7 @@ x_set_window_size_1 (struct frame *f, bool change_gravity,
{
if (CONSP (frame_size_history))
frame_size_history_extra
(f, build_string ("x_set_window_size_1, invisible"),
(f, build_string ("x_set_window_size_1, not waited for event"),
FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
f->new_width, f->new_height);
@ -28561,7 +28569,8 @@ x_set_window_size (struct frame *f, bool change_gravity,
x_set_window_size_1 (f, change_gravity, width, height);
#else /* not USE_GTK */
x_set_window_size_1 (f, change_gravity, width, height);
x_clear_under_internal_border (f);
if (!FRAME_PARENT_FRAME (f))
x_clear_under_internal_border (f);
#endif /* not USE_GTK */
/* If cursor was outside the new size, mark it as off. */
@ -28586,16 +28595,35 @@ x_set_window_size_and_position_1 (struct frame *f, int width, int height)
x_wm_set_size_hint (f, 0, false);
XMoveResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
x, y, width, height + FRAME_MENUBAR_HEIGHT (f));
#ifdef USE_X_TOOLKIT
if (FRAME_PARENT_FRAME (f) && f->output_data.x->widget)
{
/* Clear widget's position coordinates because it only sends
changed values with its XConfigureWindow command. And these
are likely outdated because XtDispatchEvent does not save them.
The alternative would be to always use XtMoveWidget instead of
XMoveWindow. */
f->output_data.x->widget->core.x = -1;
f->output_data.x->widget->core.y = -1;
/* Resize all inner widgets and Cairo surface right away so the
next redisplay drawing isn't clipped to the old size. */
XtConfigureWidget (f->output_data.x->widget,
x, y, width, height + FRAME_MENUBAR_HEIGHT (f), 0);
#ifdef USE_CAIRO
x_cr_update_surface_desired_size (f, width, height);
#endif
}
else
#endif
XMoveResizeWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
x, y, width, height + FRAME_MENUBAR_HEIGHT (f));
SET_FRAME_GARBAGED (f);
if (FRAME_VISIBLE_P (f))
/* Same as x_set_window_size_1. */
if (FRAME_VISIBLE_P (f) && !FRAME_PARENT_FRAME (f))
x_wait_for_event (f, ConfigureNotify);
else
/* Call adjust_frame_size right away as with GTK. It might be
tempting to clear out f->new_width and f->new_height here. */
adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, width),
FRAME_PIXEL_TO_TEXT_HEIGHT (f, height),
5, 0, Qx_set_window_size_1);
@ -28615,7 +28643,8 @@ x_set_window_size_and_position (struct frame *f, int width, int height)
x_set_window_size_and_position_1 (f, width, height);
#endif /* USE_GTK */
x_clear_under_internal_border (f);
if (!FRAME_PARENT_FRAME (f))
x_clear_under_internal_border (f);
/* If cursor was outside the new size, mark it as off. */
mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));