]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0529: GTK4: clipboard returns empty after a foreign app takes the selection v9.2.0529
authorYasuhiro Matsumoto <mattn.jp@gmail.com>
Sun, 24 May 2026 17:29:04 +0000 (17:29 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 24 May 2026 17:29:04 +0000 (17:29 +0000)
Problem:  GTK4: clipboard read returns empty after a foreign app takes
          the selection (lilydjwg, after v9.2.0501)
Solution: Skip the set_content call unless gdk_clipboard_is_local()
          still says we own the clipboard (Yasuhiro Matsumoto).

clipboard_changed_cb calls clip_lose_selection() which cascades into
clip_mch_lose_selection(), and that unconditionally called
gdk_clipboard_set_content(clipboard, NULL).  When the signal fires
because *another* app took the selection, this re-claims ownership
with NULL content, so the next gdk_clipboard_read_text_async() returns
empty and the user sees "Nothing in register *".

fixes:  #20256
closes: #20261

Co-Authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/gui_gtk4.c
src/version.c

index a9c9de7caf661d0725b32db623c1880c63c7cdaf..c12256790e8cd3a45e9a55a8434c3ba6abb09fd5 100644 (file)
@@ -285,6 +285,7 @@ static void drawarea_unrealize_cb(GtkWidget *widget, gpointer data);
 static void drawarea_resize_cb(GtkDrawingArea *area, int width, int height, gpointer data);
 static void drawarea_scale_factor_cb(GObject *object, GParamSpec *pspec, gpointer data);
 static cairo_surface_t *create_backing_surface(int width, int height);
+static void clipboard_changed_cb(GdkClipboard *clipboard, gpointer user_data);
 
 /*
  * Parse the GUI related command-line arguments.  Any arguments used are
@@ -589,6 +590,19 @@ gui_mch_init(void)
     // Create a blank (invisible) cursor for hiding the mouse pointer.
     gui.blank_pointer = gdk_cursor_new_from_name("none", NULL);
 
+    {
+       GdkDisplay   *display = gtk_widget_get_display(gui.mainwin);
+       GdkClipboard *primary = gdk_display_get_primary_clipboard(display);
+       GdkClipboard *board = gdk_display_get_clipboard(display);
+
+       if (primary != NULL)
+           g_signal_connect(primary, "changed",
+                   G_CALLBACK(clipboard_changed_cb), &clip_star);
+       if (board != NULL)
+           g_signal_connect(board, "changed",
+                   G_CALLBACK(clipboard_changed_cb), &clip_plus);
+    }
+
     return OK;
 }
 
@@ -3130,6 +3144,8 @@ clip_mch_request_selection(Clipboard_T *cbd)
        g_main_context_iteration(NULL, TRUE);
 }
 
+static int in_clipboard_set = FALSE;
+
 /*
  * Send the current selection to the clipboard.
  */
@@ -3174,7 +3190,9 @@ clip_mch_set_selection(Clipboard_T *cbd)
        {
            mch_memmove(nul_str, str, len);
            nul_str[len] = NUL;
+           in_clipboard_set = TRUE;
            gdk_clipboard_set_text(clipboard, (const char *)nul_str);
+           in_clipboard_set = FALSE;
            vim_free(nul_str);
        }
     }
@@ -3182,6 +3200,18 @@ clip_mch_set_selection(Clipboard_T *cbd)
     vim_free(str);
 }
 
+    static void
+clipboard_changed_cb(GdkClipboard *clipboard, gpointer user_data)
+{
+    Clipboard_T *cbd = (Clipboard_T *)user_data;
+
+    if (in_clipboard_set)
+       return;
+    if (gdk_clipboard_is_local(clipboard))
+       return;
+    clip_lose_selection(cbd);
+}
+
 /*
  * Own the selection.  In GTK4, ownership is implicit when content is set
  * on the clipboard.  Return OK to indicate we can own it.
@@ -3205,8 +3235,12 @@ clip_mch_lose_selection(Clipboard_T *cbd)
     if (clipboard == NULL)
        return;
 
-    // Setting NULL content provider releases ownership.
-    gdk_clipboard_set_content(clipboard, NULL);
+    // Only release ownership if we still own it.  Otherwise we would
+    // clobber another application's clipboard content with NULL, which
+    // happens when this is called from clipboard_changed_cb after a
+    // foreign app took the selection.
+    if (gdk_clipboard_is_local(clipboard))
+       gdk_clipboard_set_content(clipboard, NULL);
 }
 
 // Balloon eval - use GTK4 tooltip
index 19dbebbdb4ba3d34cd593b0d99b993878d8e118c..ca5b3ec12ff28582be9c3f980623982e1aa03da4 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    529,
 /**/
     528,
 /**/