diff --git a/src/video/wayland/SDL_waylandclipboard.c b/src/video/wayland/SDL_waylandclipboard.c index 71242b0ca0..9e0dc79312 100644 --- a/src/video/wayland/SDL_waylandclipboard.c +++ b/src/video/wayland/SDL_waylandclipboard.c @@ -72,7 +72,7 @@ void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, si if (data_device->selection_source) { buffer = SDL_GetInternalClipboardData(_this, mime_type, length); } else if (Wayland_data_offer_has_mime(data_device->selection_offer, mime_type)) { - buffer = Wayland_data_offer_receive(data_device->selection_offer, mime_type, length); + buffer = Wayland_data_offer_receive(data_device->selection_offer, mime_type, length, true); } } diff --git a/src/video/wayland/SDL_waylanddatamanager.c b/src/video/wayland/SDL_waylanddatamanager.c index b500687dbc..63b6b359d3 100644 --- a/src/video/wayland/SDL_waylanddatamanager.c +++ b/src/video/wayland/SDL_waylanddatamanager.c @@ -37,10 +37,14 @@ #include "SDL_waylanddatamanager.h" #include "primary-selection-unstable-v1-client-protocol.h" -/* FIXME: This is arbitrary, but we want this to be less than a frame because - * any longer can potentially spin an infinite loop of PumpEvents (!) +/* This is arbitrary, but reading while polling should block for less than a frame, to + * prevent hanging while pumping events. + * + * When querying the clipboard data directly, a larger value is needed to avoid timing + * out if the source needs to process or transfer a large amount of data. */ -#define PIPE_TIMEOUT_NS SDL_MS_TO_NS(14) +#define DEFAULT_PIPE_TIMEOUT_NS SDL_MS_TO_NS(14) +#define EXTENDED_PIPE_TIMEOUT_NS SDL_MS_TO_NS(5000) /* sigtimedwait() is an optional part of POSIX.1-2001, and OpenBSD doesn't implement it. * Based on https://comp.unix.programmer.narkive.com/rEDH0sPT/sigtimedwait-implementation @@ -94,7 +98,7 @@ static ssize_t write_pipe(int fd, const void *buffer, size_t total_length, size_ sigset_t old_sig_set; struct timespec zerotime = { 0 }; - ready = SDL_IOReady(fd, SDL_IOR_WRITE, PIPE_TIMEOUT_NS); + ready = SDL_IOReady(fd, SDL_IOR_WRITE, DEFAULT_PIPE_TIMEOUT_NS); sigemptyset(&sig_set); sigaddset(&sig_set, SIGPIPE); @@ -130,7 +134,7 @@ static ssize_t write_pipe(int fd, const void *buffer, size_t total_length, size_ return bytes_written; } -static ssize_t read_pipe(int fd, void **buffer, size_t *total_length) +static ssize_t read_pipe(int fd, void **buffer, size_t *total_length, Sint64 timeout_ns) { int ready = 0; void *output_buffer = NULL; @@ -139,7 +143,7 @@ static ssize_t read_pipe(int fd, void **buffer, size_t *total_length) ssize_t bytes_read = 0; size_t pos = 0; - ready = SDL_IOReady(fd, SDL_IOR_READ, PIPE_TIMEOUT_NS); + ready = SDL_IOReady(fd, SDL_IOR_READ, timeout_ns); if (ready == 0) { bytes_read = SDL_SetError("Pipe timeout"); @@ -404,7 +408,7 @@ static void offer_source_done_handler(void *data, struct wl_callback *callback, wl_callback_destroy(offer->callback); offer->callback = NULL; - while (read_pipe(offer->read_fd, (void **)&id, &length) > 0) { + while (read_pipe(offer->read_fd, (void **)&id, &length, DEFAULT_PIPE_TIMEOUT_NS) > 0) { } close(offer->read_fd); offer->read_fd = -1; @@ -499,10 +503,10 @@ void Wayland_data_offer_notify_from_mimes(SDL_WaylandDataOffer *offer, bool chec SDL_SendClipboardUpdate(false, new_mime_types, nformats); } -void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer, - const char *mime_type, size_t *length) +void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer, const char *mime_type, size_t *length, bool extended_timeout) { SDL_WaylandDataDevice *data_device = NULL; + const Sint64 timeout = extended_timeout ? EXTENDED_PIPE_TIMEOUT_NS : DEFAULT_PIPE_TIMEOUT_NS; int pipefd[2]; void *buffer = NULL; @@ -523,7 +527,7 @@ void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer, WAYLAND_wl_display_flush(data_device->seat->display->display); - while (read_pipe(pipefd[0], &buffer, length) > 0) { + while (read_pipe(pipefd[0], &buffer, length, timeout) > 0) { } close(pipefd[0]); } @@ -557,7 +561,7 @@ void *Wayland_primary_selection_offer_receive(SDL_WaylandPrimarySelectionOffer * WAYLAND_wl_display_flush(primary_selection_device->seat->display->display); - while (read_pipe(pipefd[0], &buffer, length) > 0) { + while (read_pipe(pipefd[0], &buffer, length, EXTENDED_PIPE_TIMEOUT_NS) > 0) { } close(pipefd[0]); } diff --git a/src/video/wayland/SDL_waylanddatamanager.h b/src/video/wayland/SDL_waylanddatamanager.h index 1873314224..d7a863221b 100644 --- a/src/video/wayland/SDL_waylanddatamanager.h +++ b/src/video/wayland/SDL_waylanddatamanager.h @@ -140,7 +140,8 @@ extern void Wayland_primary_selection_source_destroy(SDL_WaylandPrimarySelection // Wayland Data / Primary Selection Offer - (Receiving) extern void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer, const char *mime_type, - size_t *length); + size_t *length, + bool extended_timeout); extern void *Wayland_primary_selection_offer_receive(SDL_WaylandPrimarySelectionOffer *offer, const char *mime_type, size_t *length); diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index da038f5524..bf2810a2e5 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -2900,8 +2900,7 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d bool drop_handled = false; #ifdef SDL_USE_LIBDBUS if (Wayland_data_offer_has_mime(data_device->drag_offer, FILE_PORTAL_MIME)) { - void *buffer = Wayland_data_offer_receive(data_device->drag_offer, - FILE_PORTAL_MIME, &length); + void *buffer = Wayland_data_offer_receive(data_device->drag_offer, FILE_PORTAL_MIME, &length, false); if (buffer) { SDL_DBusContext *dbus = SDL_DBus_GetContext(); if (dbus) { @@ -2927,7 +2926,7 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d * non paths that are not visible to the application */ if (!drop_handled) { - void *buffer = Wayland_data_offer_receive(data_device->drag_offer, data_device->mime_type, &length); + void *buffer = Wayland_data_offer_receive(data_device->drag_offer, data_device->mime_type, &length, false); if (data_device->has_mime_file) { if (buffer) { char *saveptr = NULL;