SDL: wayland: Don't send nil selections when changing clipboard data

From dfcbd16b4c7b1666eaa2e45b51a0ab9657ae3fed Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Mon, 27 Jul 2026 11:54:10 -0400
Subject: [PATCH] wayland: Don't send nil selections when changing clipboard
 data

Sending a nil selection followed by immediately destroying the selection offer is redundant, and avoiding intermediate nil offers helps some clipboard managers avoid potential race conditions.
---
 src/video/wayland/SDL_waylandclipboard.c   |  8 +++--
 src/video/wayland/SDL_waylanddatamanager.c | 35 +++-------------------
 src/video/wayland/SDL_waylanddatamanager.h |  2 --
 src/video/wayland/SDL_waylandevents.c      |  4 ++-
 4 files changed, 13 insertions(+), 36 deletions(-)

diff --git a/src/video/wayland/SDL_waylandclipboard.c b/src/video/wayland/SDL_waylandclipboard.c
index a8409bc6f64bb..81f8f8c3c54a6 100644
--- a/src/video/wayland/SDL_waylandclipboard.c
+++ b/src/video/wayland/SDL_waylandclipboard.c
@@ -53,7 +53,9 @@ bool Wayland_SetClipboardData(SDL_VideoDevice *_this)
                 Wayland_DataSourceDestroy(source);
             }
         } else {
-            result = Wayland_DataDeviceClearSelection(data_device);
+            Wayland_DataSourceDestroy(data_device->selection_source);
+            data_device->selection_source = NULL;
+            result = true;
         }
     }
 
@@ -133,7 +135,9 @@ bool Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
                 Wayland_PrimarySelectionSourceDestroy(source);
             }
         } else {
-            result = Wayland_PrimarySelectionDeviceClearSelection(primary_selection_device);
+            Wayland_PrimarySelectionSourceDestroy(seat->primary_selection_device->selection_source);
+            seat->primary_selection_device->selection_source = NULL;
+            result = true;
         }
     } else {
         result = SDL_SetError("Primary selection not supported");
diff --git a/src/video/wayland/SDL_waylanddatamanager.c b/src/video/wayland/SDL_waylanddatamanager.c
index 1a293247fb572..5137c3bef1707 100644
--- a/src/video/wayland/SDL_waylanddatamanager.c
+++ b/src/video/wayland/SDL_waylanddatamanager.c
@@ -620,35 +620,6 @@ void Wayland_PrimarySelectionOfferDestroy(SDL_WaylandPrimarySelectionOffer *offe
     }
 }
 
-bool Wayland_DataDeviceClearSelection(SDL_WaylandDataDevice *data_device)
-{
-    if (!data_device || !data_device->data_device) {
-        return SDL_SetError("Invalid Data Device");
-    }
-
-    if (data_device->selection_source) {
-        wl_data_device_set_selection(data_device->data_device, NULL, data_device->seat->last_implicit_grab_serial);
-        Wayland_DataSourceDestroy(data_device->selection_source);
-        data_device->selection_source = NULL;
-    }
-    return true;
-}
-
-bool Wayland_PrimarySelectionDeviceClearSelection(SDL_WaylandPrimarySelectionDevice *primary_selection_device)
-{
-    if (!primary_selection_device || !primary_selection_device->primary_selection_device) {
-        return SDL_SetError("Invalid Primary Selection Device");
-    }
-
-    if (primary_selection_device->selection_source) {
-        zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
-                                                      NULL, primary_selection_device->seat->last_implicit_grab_serial);
-        Wayland_PrimarySelectionSourceDestroy(primary_selection_device->selection_source);
-        primary_selection_device->selection_source = NULL;
-    }
-    return true;
-}
-
 bool Wayland_DataDeviceSetSelection(SDL_WaylandDataDevice *data_device, SDL_WaylandDataSource *source, const char **mime_types, size_t mime_count)
 {
     if (!data_device) {
@@ -678,7 +649,8 @@ bool Wayland_DataDeviceSetSelection(SDL_WaylandDataDevice *data_device, SDL_Wayl
         source->data_device = data_device;
 
     } else {
-        Wayland_DataDeviceClearSelection(data_device);
+        Wayland_DataSourceDestroy(data_device->selection_source);
+        data_device->selection_source = NULL;
         return SDL_SetError("No mime data");
     }
 
@@ -714,7 +686,8 @@ bool Wayland_PrimarySelectionDeviceSetSelection(SDL_WaylandPrimarySelectionDevic
         primary_selection_device->selection_source = source;
         source->primary_selection_device = primary_selection_device;
     } else {
-        Wayland_PrimarySelectionDeviceClearSelection(primary_selection_device);
+        Wayland_PrimarySelectionSourceDestroy(primary_selection_device->selection_source);
+        primary_selection_device->selection_source = NULL;
         return SDL_SetError("No mime data");
     }
 
diff --git a/src/video/wayland/SDL_waylanddatamanager.h b/src/video/wayland/SDL_waylanddatamanager.h
index 382d046ac1de2..0a9d330a77cdf 100644
--- a/src/video/wayland/SDL_waylanddatamanager.h
+++ b/src/video/wayland/SDL_waylanddatamanager.h
@@ -139,8 +139,6 @@ extern void Wayland_DataOfferDestroy(SDL_WaylandDataOffer *offer);
 extern void Wayland_PrimarySelectionOfferDestroy(SDL_WaylandPrimarySelectionOffer *offer);
 
 // Clipboard / Primary Selection
-extern bool Wayland_DataDeviceClearSelection(SDL_WaylandDataDevice *device);
-extern bool Wayland_PrimarySelectionDeviceClearSelection(SDL_WaylandPrimarySelectionDevice *device);
 extern bool Wayland_DataDeviceSetSelection(SDL_WaylandDataDevice *device, SDL_WaylandDataSource *source, const char **mime_types, size_t mime_count);
 extern bool Wayland_PrimarySelectionDeviceSetSelection(SDL_WaylandPrimarySelectionDevice *device, SDL_WaylandPrimarySelectionSource *source, const char *const *mime_types, size_t mime_count);
 extern void Wayland_DataDeviceSetSerial(SDL_WaylandDataDevice *device, uint32_t serial);
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index e4de4351f951f..14031d8e4c8d7 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -3698,10 +3698,12 @@ void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool shutting_down)
     SDL_free(seat->name);
 
     if (seat->data_device) {
-        Wayland_DataDeviceClearSelection(seat->data_device);
         if (seat->data_device->selection_offer) {
             Wayland_DataOfferDestroy(seat->data_device->selection_offer);
         }
+        if (seat->data_device->selection_source) {
+            Wayland_DataSourceDestroy(seat->data_device->selection_source);
+        }
         if (seat->data_device->drag_offer) {
             Wayland_DataOfferDestroy(seat->data_device->drag_offer);
         }