Просмотр исходного кода

wayland: Clear references to the clipboard when setting new data

Ensure that references to the clipboard being held by other seats are cleared when new data is being set, or some seats may hold references to invalid data and callbacks.
Frank Praznik 1 месяц назад
Родитель
Сommit
331e10c2a3

+ 23 - 6
src/video/wayland/SDL_waylandclipboard.c

@@ -39,9 +39,26 @@ bool Wayland_SetClipboardData(SDL_VideoDevice *_this)
         seat = wl_container_of(video_data->seat_list.next, seat, link);
     }
 
-    video_data->last_incoming_data_offer_seat = seat;
+    video_data->current_data_offer_seat = seat;
 
     if (seat && seat->data_device) {
+        /* Clear references to the clipboard held by other seats, as they are about to become invalid.
+         * For the target seat, the new source data is set before clearing the old source, as some
+         * clipboard managers prefer this behavior.
+         */
+        SDL_WaylandSeat *s;
+        wl_list_for_each (s, &video_data->seat_list, link) {
+            if (s->data_device) {
+                if (s != seat) {
+                    Wayland_DataSourceDestroy(s->data_device->selection_source);
+                    s->data_device->selection_source = NULL;
+                }
+
+                Wayland_DataOfferDestroy(s->data_device->selection_offer);
+                s->data_device->selection_offer = NULL;
+            }
+        }
+
         SDL_WaylandDataDevice *data_device = seat->data_device;
 
         if (_this->clipboard_callback && _this->clipboard_mime_types) {
@@ -65,7 +82,7 @@ bool Wayland_SetClipboardData(SDL_VideoDevice *_this)
 void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length)
 {
     SDL_VideoData *video_data = _this->internal;
-    SDL_WaylandSeat *seat = video_data->last_incoming_data_offer_seat;
+    SDL_WaylandSeat *seat = video_data->current_data_offer_seat;
     void *buffer = NULL;
 
     if (seat && seat->data_device) {
@@ -83,7 +100,7 @@ void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, si
 bool Wayland_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
 {
     SDL_VideoData *video_data = _this->internal;
-    SDL_WaylandSeat *seat = video_data->last_incoming_data_offer_seat;
+    SDL_WaylandSeat *seat = video_data->current_data_offer_seat;
     bool result = false;
 
     if (seat && seat->data_device) {
@@ -122,7 +139,7 @@ bool Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
         seat = wl_container_of(video_data->seat_list.next, seat, link);
     }
 
-    video_data->last_incoming_primary_selection_seat = seat;
+    video_data->current_primary_selection_seat = seat;
 
     if (seat && seat->primary_selection_device) {
         SDL_WaylandPrimarySelectionDevice *primary_selection_device = seat->primary_selection_device;
@@ -148,7 +165,7 @@ bool Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
 char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this)
 {
     SDL_VideoData *video_data = _this->internal;
-    SDL_WaylandSeat *seat = video_data->last_incoming_primary_selection_seat;
+    SDL_WaylandSeat *seat = video_data->current_primary_selection_seat;
     char *text = NULL;
     size_t length = 0;
 
@@ -176,7 +193,7 @@ char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this)
 bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this)
 {
     SDL_VideoData *video_data = _this->internal;
-    SDL_WaylandSeat *seat = video_data->last_incoming_primary_selection_seat;
+    SDL_WaylandSeat *seat = video_data->current_primary_selection_seat;
     bool result = false;
 
     if (seat && seat->primary_selection_device) {

+ 32 - 8
src/video/wayland/SDL_waylanddatamanager.c

@@ -446,6 +446,29 @@ static void DataOfferCheckSource(SDL_WaylandDataOffer *offer, const char *mime_t
     }
 }
 
+static void SetCurrentClipboardOffer(SDL_WaylandDataOffer *offer)
+{
+    SDL_WaylandSeat *offer_seat = offer->data_device->seat;
+    SDL_VideoData *video_data = offer_seat->display;
+
+    // Clear any existing references to the existing clipboard data before replacing the current offer.
+    SDL_WaylandSeat *s;
+    wl_list_for_each (s, &video_data->seat_list, link) {
+        if (s->data_device) {
+            Wayland_DataSourceDestroy(s->data_device->selection_source);
+            s->data_device->selection_source = NULL;
+
+            // Don't clear the offer that is about to be set.
+            if (s != offer_seat) {
+                Wayland_DataOfferDestroy(s->data_device->selection_offer);
+                s->data_device->selection_offer = NULL;
+            }
+        }
+    }
+
+    video_data->current_data_offer_seat = offer_seat;
+}
+
 void Wayland_DataOfferNotifyFromMIMEs(SDL_WaylandDataOffer *offer, bool check_origin)
 {
     int nformats = 0;
@@ -494,6 +517,7 @@ void Wayland_DataOfferNotifyFromMIMEs(SDL_WaylandDataOffer *offer, bool check_or
         new_mime_types[nformats] = NULL;
     }
 
+    SetCurrentClipboardOffer(offer);
     SDL_SendClipboardUpdate(false, new_mime_types, nformats);
 }
 
@@ -697,26 +721,26 @@ bool Wayland_PrimarySelectionDeviceSetSelection(SDL_WaylandPrimarySelectionDevic
 void Wayland_DataDeviceSetSerial(SDL_WaylandDataDevice *data_device, uint32_t serial)
 {
     if (data_device) {
-        data_device->selection_serial = serial;
-
-        // If there was no serial and there is a pending selection set it now.
+        // If there was no serial and there is a pending selection, set it now.
         if (data_device->selection_serial == 0 && data_device->selection_source) {
-            wl_data_device_set_selection(data_device->data_device, data_device->selection_source->source, data_device->selection_serial);
+            wl_data_device_set_selection(data_device->data_device, data_device->selection_source->source, serial);
         }
+
+        data_device->selection_serial = serial;
     }
 }
 
 void Wayland_PrimarySelectionDeviceSetSerial(SDL_WaylandPrimarySelectionDevice *primary_selection_device, uint32_t serial)
 {
     if (primary_selection_device) {
-        primary_selection_device->selection_serial = serial;
-
-        // If there was no serial and there is a pending selection set it now.
+        // If there was no serial and there is a pending selection, set it now.
         if (primary_selection_device->selection_serial == 0 && primary_selection_device->selection_source) {
             zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
                                                           primary_selection_device->selection_source->source,
-                                                          primary_selection_device->selection_serial);
+                                                          serial);
         }
+
+        primary_selection_device->selection_serial = serial;
     }
 }
 

+ 1 - 2
src/video/wayland/SDL_waylandevents.c

@@ -2749,7 +2749,6 @@ static void data_device_handle_data_offer(void *data, struct wl_data_device *wl_
     SDL_WaylandDataOffer *data_offer = SDL_calloc(1, sizeof(*data_offer));
     if (data_offer) {
         SDL_WaylandDataDevice *data_device = (SDL_WaylandDataDevice *)data;
-        data_device->seat->display->last_incoming_data_offer_seat = data_device->seat;
         data_offer->offer = id;
         data_offer->data_device = data_device;
         data_offer->read_fd = -1;
@@ -3054,7 +3053,7 @@ static void primary_selection_device_handle_offer(void *data, struct zwp_primary
     SDL_WaylandPrimarySelectionOffer *primary_selection_offer = SDL_calloc(1, sizeof(*primary_selection_offer));
     if (primary_selection_offer) {
         SDL_WaylandPrimarySelectionDevice *primary_selection_device = (SDL_WaylandPrimarySelectionDevice *)data;
-        primary_selection_device->seat->display->last_incoming_primary_selection_seat = primary_selection_device->seat;
+        primary_selection_device->seat->display->current_primary_selection_seat = primary_selection_device->seat;
         primary_selection_offer->offer = id;
         primary_selection_offer->primary_selection_device = primary_selection_device;
         WAYLAND_wl_list_init(&(primary_selection_offer->mimes));

+ 2 - 2
src/video/wayland/SDL_waylandvideo.h

@@ -96,8 +96,8 @@ struct SDL_VideoData
 
     struct wl_list seat_list;
     struct SDL_WaylandSeat *last_implicit_grab_seat;
-    struct SDL_WaylandSeat *last_incoming_data_offer_seat;
-    struct SDL_WaylandSeat *last_incoming_primary_selection_seat;
+    struct SDL_WaylandSeat *current_data_offer_seat;
+    struct SDL_WaylandSeat *current_primary_selection_seat;
     Wayland_EventThreadContext *event_thread_context;
 
     SDL_DisplayData **output_list;