wayland: Increase the read timeout when reading from SDL_GetClipboardData()

The default timeout value of 14ms is ideal when querying clipboard data while polling events, to prevent excessive lag if the source takes a long time to respond, however, when reading from SDL_GetClipboardData(), the timeout can be too short if a large amount of data must be processed or transferred. SDL_GetClipboardData() is not called while polling events, so using a longer read timeout to greatly increase the chance of success is acceptable.

Use a 5 second timeout when reading from SDL_GetClipboardData() and GetPrimarySelectionText() to greatly increase the chances of a successful read, even if the requested format requires heavy processing.

(cherry picked from commit 2a0d04613c)
This commit is contained in:
Frank Praznik
2026-01-13 12:37:37 -05:00
parent b8aff4a3ed
commit b7a241973a
4 changed files with 20 additions and 16 deletions

View File

@@ -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);
}
}

View File

@@ -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]);
}

View File

@@ -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);

View File

@@ -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;