diff --git a/src/video/wayland/SDL_waylandclipboard.c b/src/video/wayland/SDL_waylandclipboard.c index 81f8f8c3c5..9e786afd22 100644 --- a/src/video/wayland/SDL_waylandclipboard.c +++ b/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) { diff --git a/src/video/wayland/SDL_waylanddatamanager.c b/src/video/wayland/SDL_waylanddatamanager.c index 5137c3bef1..49b973f971 100644 --- a/src/video/wayland/SDL_waylanddatamanager.c +++ b/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; } } diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 14031d8e4c..761aae86fd 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/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)); diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index 17a9ba24fa..74c8ed2363 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/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;