Procházet zdrojové kódy

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)
Frank Praznik před 1 měsícem
rodič
revize
b7a241973a

+ 1 - 1
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);
         }
     }
 

+ 15 - 11
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]);
     }

+ 2 - 1
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);

+ 2 - 3
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;