From b7a241973a99b8928aec5cdc2de8e193ca8d9ac7 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Tue, 13 Jan 2026 12:37:37 -0500
Subject: [PATCH] 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 2a0d04613ce75b91d4c62809a187037947c665f7)
---
src/video/wayland/SDL_waylandclipboard.c | 2 +-
src/video/wayland/SDL_waylanddatamanager.c | 26 +++++++++++++---------
src/video/wayland/SDL_waylanddatamanager.h | 3 ++-
src/video/wayland/SDL_waylandevents.c | 5 ++---
4 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/src/video/wayland/SDL_waylandclipboard.c b/src/video/wayland/SDL_waylandclipboard.c
index 71242b0ca0177..9e0dc79312821 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 b500687dbc303..63b6b359d3a99 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 18733142243d2..d7a863221b502 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 da038f5524db6..bf2810a2e53e9 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;