patch 9.2.0544: GTK4: window blank after a resize or drag
Commit:
https://github.com/vim/vim/commit/f85892e39ef5cace7b7a91985c844786e5b6a56b
Author: Yasuhiro Matsumoto <[email protected]>
Date: Wed May 27 20:12:19 2026 +0000
patch 9.2.0544: GTK4: window blank after a resize or drag
Problem: GTK4: window blank after a resize or drag
(Steven A. Falco)
Solution: In drawarea_resize_cb() keep the backing surface in sync with
the drawing area, preserving the old contents. Stop touching
the surface in gui_mch_set_text_area_pos().
Debounce gui_resize_shell() so it runs once the drag stream
settles and update_screen() can paint (Yasuhiro Matsumoto).
related: #20307
closes: #20327
Co-Authored-by: Claude <[email protected]>
Signed-off-by: Yasuhiro Matsumoto <[email protected]>
Signed-off-by: Christian Brabandt <[email protected]>
diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c
index 343b4f62d..2b09ce1ce 100644
--- a/src/gui_gtk4.c
+++ b/src/gui_gtk4.c
@@ -1912,39 +1912,94 @@ drawarea_unrealize_cb(GtkWidget *widget UNUSED,
gpointer data UNUSED)
}
}
+// Debounced resize: drawarea_resize_cb only resizes the backing surface
+// (preserving old content) and (re)arms a short timeout. The actual
+// gui_resize_shell() runs from drawarea_resize_apply_cb once the user has
+// stopped dragging for ~100 ms, by which time no input is pending and
+// update_screen() will not bail in screenclear()'s wake.
+static guint drawarea_resize_timeout_id = 0;
+static int drawarea_resize_pending_w = 0;
+static int drawarea_resize_pending_h = 0;
+
+ static gboolean
+drawarea_resize_apply_cb(gpointer data UNUSED)
+{
+ int width = drawarea_resize_pending_w;
+ int height = drawarea_resize_pending_h;
+
+ drawarea_resize_timeout_id = 0;
+
+ if (width <= 0 || height <= 0)
+ return G_SOURCE_REMOVE;
+ if (updating_screen)
+ {
+ drawarea_resize_timeout_id = g_timeout_add(50,
+ drawarea_resize_apply_cb, NULL);
+ return G_SOURCE_REMOVE;
+ }
+
+ gui.force_redraw = TRUE;
+ gui_resize_shell(width, height);
+ if (gui.in_use)
+ redraw_all_later(UPD_CLEAR);
+ return G_SOURCE_REMOVE;
+}
+
static void
drawarea_resize_cb(GtkDrawingArea *area UNUSED, int width, int height,
gpointer data UNUSED)
{
cairo_t *cr;
- int scale = get_drawarea_scale();
+ cairo_surface_t *old_surface;
+ int scale = get_drawarea_scale();
if (width <= 0 || height <= 0)
return;
+ drawarea_resize_pending_w = width;
+ drawarea_resize_pending_h = height;
+
+ // Keep the backing surface in sync with the drawing area so GTK keeps
+ // showing the previous frame. Re-creating it preserves the old
+ // contents.
if (gui.surface != NULL)
{
int sw = cairo_image_surface_get_width(gui.surface) / scale;
int sh = cairo_image_surface_get_height(gui.surface) / scale;
-
- if (sw == width && sh == height)
- return;
-
- cairo_surface_destroy(gui.surface);
+ if (sw != width || sh != height)
+ {
+ old_surface = gui.surface;
+ gui.surface = create_backing_surface(width, height);
+ if (gui.surface != NULL)
+ {
+ cr = cairo_create(gui.surface);
+ set_cairo_source_from_pixel(cr, gui.back_pixel);
+ cairo_paint(cr);
+ cairo_set_source_surface(cr, old_surface, 0, 0);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ }
+ cairo_surface_destroy(old_surface);
+ }
+ }
+ else
+ {
+ gui.surface = create_backing_surface(width, height);
+ if (gui.surface != NULL)
+ {
+ cr = cairo_create(gui.surface);
+ set_cairo_source_from_pixel(cr, gui.back_pixel);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ }
}
- // Create a fresh surface filled with the background color.
- // Do not copy old surface content: gui_resize_shell() will trigger
- // a full redraw, and stale content (e.g. intro screen text) would
- // otherwise remain as ghost artifacts.
- gui.surface = create_backing_surface(width, height);
- cr = cairo_create(gui.surface);
- set_cairo_source_from_pixel(cr, gui.back_pixel);
- cairo_paint(cr);
- cairo_destroy(cr);
-
- // Notify Vim about the new size - this will cause a full redraw
- gui_resize_shell(width, height);
+ // Debounce: (re)arm the apply timeout, so gui_resize_shell() only
+ // runs once the resize stream settles.
+ if (drawarea_resize_timeout_id != 0)
+ g_source_remove(drawarea_resize_timeout_id);
+ drawarea_resize_timeout_id = g_timeout_add(100,
+ drawarea_resize_apply_cb, NULL);
}
static void
@@ -4003,21 +4058,11 @@ gui_mch_set_text_area_pos(int x, int y, int w, int h)
// form_size_allocate which gives drawarea the formwin's full size.
gui_gtk_form_move(GTK_FORM(gui.formwin), gui.drawarea, x, y);
- // Update surface to match new text area size
- if (w > 0 && h > 0)
- {
- int scale = get_drawarea_scale();
-
- if (gui.surface != NULL)
- {
- int sw = cairo_image_surface_get_width(gui.surface) / scale;
- int sh = cairo_image_surface_get_height(gui.surface) / scale;
- if (sw == w && sh == h)
- return;
- cairo_surface_destroy(gui.surface);
- }
- gui.surface = create_backing_surface(w, h);
- }
+ // Surface sizing is owned by drawarea_resize_cb; don't recreate it
+ // here. Recreating on every text-area change wiped any preserved
+ // content whenever a sub-cell resize shifted the cell grid, and
+ // update_screen() may bail (char_avail()) during a drag and leave
+ // the fresh surface blank.
}
/*
diff --git a/src/version.c b/src/version.c
index de7154c64..80f6a620b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 544,
/**/
543,
/**/
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/vim_dev/E1wSKtA-009jnZ-83%40256bit.org.