patch 9.2.0820: GUI: hidden popup image is displayed and not erased
Commit:
https://github.com/vim/vim/commit/011108797f86d1eaebdbe107b5e9c222662636c3
Author: Hirohito Higashi <
h.eas...@gmail.com>
Date: Mon Jul 20 17:50:06 2026 +0000
patch 9.2.0820: GUI: hidden popup image is displayed and not erased
Problem: In the GUI a popup image is drawn even when the popup is
hidden, and hiding a popup leaves part of the image on the
screen (Bakudankun).
Solution: Skip hidden popups when repainting images after the cursor is
undrawn or the window is exposed. Redraw the area the image
covered when the popup is hidden or closed, also for the GDI
backend (Hirohito Higashi).
fixes: #20796
closes: #20798
Co-Authored-By: Claude Opus 4.8 (1M context) <
nor...@anthropic.com>
Signed-off-by: Hirohito Higashi <
h.eas...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/popupwin.c b/src/popupwin.c
index cd3fe80da..0acb7453d 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -120,11 +120,20 @@ static void redraw_overlapped_opacity_popups(int winrow, int wincol,
#ifdef FEAT_IMAGE_KITTY
static void popup_image_clear_kitty(win_T *wp);
#endif
+// GDI and cairo paint the image straight into the window, so the area has to
+// be redrawn when the popup goes away. GTK4 keeps a list of images to render
+// instead, there gui_gtk4_remove_image() does the job.
+#ifdef FEAT_IMAGE_GDI
+# define POPUP_IMAGE_CLEAR_GUI
+#endif
#if defined(FEAT_GUI_GTK) && defined(FEAT_IMAGE_CAIRO)
# if !GTK_CHECK_VERSION(4,0,0)
-static void popup_image_clear_cairo(win_T *wp);
+# define POPUP_IMAGE_CLEAR_GUI
# endif
#endif
+#ifdef POPUP_IMAGE_CLEAR_GUI
+static void popup_image_clear_gui(win_T *wp);
+#endif
#ifdef FEAT_IMAGE
static bool popup_image_composites_frames(void);
#endif
@@ -4314,10 +4323,8 @@ popup_hide(win_T *wp)
wp->w_popup_flags |= POPF_HIDDEN;
-#if defined(FEAT_GUI_GTK) && defined(FEAT_IMAGE_CAIRO)
-# if !GTK_CHECK_VERSION(4,0,0)
- popup_image_clear_cairo(wp);
-# endif
+#ifdef POPUP_IMAGE_CLEAR_GUI
+ popup_image_clear_gui(wp);
#endif
// Do not decrement b_nwindows, we still reference the buffer.
@@ -4523,10 +4530,8 @@ popup_free(win_T *wp)
if (gui.in_use)
gui_gtk4_remove_image(wp);
#endif
-#if defined(FEAT_GUI_GTK) && defined(FEAT_IMAGE_CAIRO)
-# if !GTK_CHECK_VERSION(4,0,0)
- popup_image_clear_cairo(wp);
-# endif
+#ifdef POPUP_IMAGE_CLEAR_GUI
+ popup_image_clear_gui(wp);
#endif
sign_undefine_by_name(popup_get_sign_name(wp), FALSE);
wp->w_buffer->b_locked = FALSE;
@@ -7162,10 +7167,12 @@ popup_image_clear_kitty(win_T *wp)
}
# endif
-# if defined(FEAT_GUI_GTK) && defined(FEAT_IMAGE_CAIRO)
-# if !GTK_CHECK_VERSION(4,0,0)
+# ifdef POPUP_IMAGE_CLEAR_GUI
+/*
+ * Redraw the area the image of "wp" was drawn in, the popup goes away.
+ */
static void
-popup_image_clear_cairo(win_T *wp)
+popup_image_clear_gui(win_T *wp)
{
if (!gui.in_use
|| wp->w_popup_image_emit_cells_w <= 0
@@ -7181,7 +7188,6 @@ popup_image_clear_cairo(win_T *wp)
wp->w_popup_image_emit_cells_w = 0;
wp->w_popup_image_emit_cells_h = 0;
}
-# endif
# endif
# if defined(FEAT_IMAGE_SIXEL) || defined(FEAT_IMAGE_KITTY)
@@ -7245,7 +7251,10 @@ popup_maybe_emit_image_rect(
int img_left, img_top, img_right, img_bottom;
int src_x, src_y, draw_w, draw_h;
- if (!gui.in_use || wp->w_popup_image_data == NULL
+ // This is called for every popup, also the hidden ones: the cursor undraw
+ // and WM_PAINT paths do not go through find_next_popup().
+ if (!gui.in_use || (wp->w_popup_flags & POPF_HIDDEN)
+ || wp->w_popup_image_data == NULL
|| wp->w_popup_image_w <= 0 || wp->w_popup_image_h <= 0)
return;
diff --git a/src/version.c b/src/version.c
index ae605f243..4031cd24c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -759,6 +759,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 820,
/**/
819,
/**/