From f85892e39ef5cace7b7a91985c844786e5b6a56b Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 27 May 2026 20:12:19 +0000 Subject: [PATCH] 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 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 111 ++++++++++++++++++++++++++++++++++--------------- src/version.c | 2 + 2 files changed, 80 insertions(+), 33 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index 343b4f62da..2b09ce1ce8 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 de7154c646..80f6a620b4 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, /**/