SDL: wayland: Generalize the self data read function

From 717cd3f76663f4e56380229b99a40f68cdec5420 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Wed, 14 Oct 2026 12:04:05 -0400
Subject: [PATCH] wayland: Generalize the self data read function

---
 src/video/wayland/SDL_waylanddatamanager.c | 88 ++++++++++++++--------
 src/video/wayland/SDL_waylanddatamanager.h |  2 +
 2 files changed, 59 insertions(+), 31 deletions(-)

diff --git a/src/video/wayland/SDL_waylanddatamanager.c b/src/video/wayland/SDL_waylanddatamanager.c
index 8ab84715ce89f..339546716e225 100644
--- a/src/video/wayland/SDL_waylanddatamanager.c
+++ b/src/video/wayland/SDL_waylanddatamanager.c
@@ -458,39 +458,10 @@ static void offer_source_done_handler(void *data, struct wl_callback *callback,
     }
 }
 
-static struct wl_callback_listener offer_source_listener = {
+static const struct wl_callback_listener offer_source_listener = {
     offer_source_done_handler
 };
 
-static void DataOfferCheckSource(SDL_WaylandDataOffer *offer, const char *mime_type)
-{
-    int pipefd[2];
-
-    if (!offer) {
-        return;
-    }
-    SDL_WaylandDataDevice *data_device = offer->data_device;
-
-    if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == 0) {
-        if (offer->callback) {
-            wl_callback_destroy(offer->callback);
-        }
-        if (offer->read_fd >= 0) {
-            close(offer->read_fd);
-        }
-
-        offer->read_fd = pipefd[0];
-
-        wl_data_offer_receive(offer->offer, mime_type, pipefd[1]);
-        close(pipefd[1]);
-
-        offer->callback = wl_display_sync(offer->data_device->seat->display->display);
-        wl_callback_add_listener(offer->callback, &offer_source_listener, offer);
-
-        WAYLAND_wl_display_flush(data_device->seat->display->display);
-    }
-}
-
 static void UpdateSeatOffers(SDL_WaylandDataDevice *data_device)
 {
     SDL_WaylandSeat *offer_seat = data_device->seat;
@@ -528,7 +499,7 @@ static void SelectionOfferNotifyFromMIMEs(SDL_WaylandDataDevice *data_device, bo
         wl_array_for_each(item, &offer->mimes) {
             // If origin metadata is found, queue a check and wait for confirmation that this offer isn't recursive.
             if (check_origin && SDL_strcmp(*item, SDL_DATA_ORIGIN_MIME) == 0) {
-                DataOfferCheckSource(offer, *item);
+                Wayland_DataOfferRequestDataFromSelf(offer, *item, &offer_source_listener, offer);
                 return;
             }
 
@@ -578,6 +549,61 @@ void Wayland_DataDeviceSetSelectionOffer(SDL_WaylandDataDevice *data_device, SDL
     }
 }
 
+void *Wayland_DataOfferGetDataFromSelf(SDL_WaylandDataOffer *offer, size_t *length)
+{
+    if (!offer) {
+        SDL_SetError("Invalid data offer");
+        return NULL;
+    }
+
+    if (offer->read_fd < 0) {
+        SDL_SetError("Pipe closed");
+        return NULL;
+    }
+
+    // The timeout is 0, as the data will already be in the pipe if it is from this process.
+    void *buffer = NULL;
+    while (ReadPipe(offer->read_fd, &buffer, length, 0) > 0) {
+    }
+
+    return buffer;
+}
+
+bool Wayland_DataOfferRequestDataFromSelf(SDL_WaylandDataOffer *offer, const char *mime_type, const struct wl_callback_listener *completion_listener, void *userdata)
+{
+    int pipefd[2];
+
+    if (!offer) {
+        SDL_SetError("Invalid data offer");
+        return false;
+    }
+
+    SDL_WaylandDataDevice *data_device = offer->data_device;
+    if (offer->read_fd >= 0) {
+        close(offer->read_fd);
+        offer->read_fd = -1;
+    }
+    if (offer->callback) {
+        wl_callback_destroy(offer->callback);
+        offer->callback = NULL;
+    }
+
+    if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) == 0) {
+        offer->read_fd = pipefd[0];
+        wl_data_offer_receive(offer->offer, mime_type, pipefd[1]);
+        close(pipefd[1]);
+
+        offer->callback = wl_display_sync(data_device->seat->display->display);
+        wl_callback_add_listener(offer->callback, completion_listener, userdata);
+
+        WAYLAND_wl_display_flush(data_device->seat->display->display);
+    } else {
+        return SDL_SetError("Could not create pipe");
+    }
+
+    return true;
+}
+
 void *Wayland_DataOfferReceive(SDL_WaylandDataOffer *offer, const char *mime_type, size_t *length, bool extended_timeout)
 {
     const Sint64 timeout = extended_timeout ? EXTENDED_PIPE_TIMEOUT_NS : DEFAULT_PIPE_TIMEOUT_NS;
diff --git a/src/video/wayland/SDL_waylanddatamanager.h b/src/video/wayland/SDL_waylanddatamanager.h
index 1f4597906b2c7..46a9a16b54544 100644
--- a/src/video/wayland/SDL_waylanddatamanager.h
+++ b/src/video/wayland/SDL_waylanddatamanager.h
@@ -118,6 +118,8 @@ extern void Wayland_DataSourceDestroy(SDL_WaylandDataSource *source);
 extern void Wayland_PrimarySelectionSourceDestroy(SDL_WaylandPrimarySelectionSource *source);
 
 // Wayland Data / Primary Selection Offer - (Receiving)
+extern bool Wayland_DataOfferRequestDataFromSelf(SDL_WaylandDataOffer *offer, const char *mime_type, const struct wl_callback_listener *completion_listener, void *userdata);
+extern void *Wayland_DataOfferGetDataFromSelf(SDL_WaylandDataOffer *offer, size_t *length);
 extern void *Wayland_DataOfferReceive(SDL_WaylandDataOffer *offer, const char *mime_type, size_t *length, bool extended_timeout);
 extern void *Wayland_PrimarySelectionOfferReceive(SDL_WaylandPrimarySelectionOffer *offer, const char *mime_type, size_t *length);
 extern bool Wayland_DataOfferHasMIME(SDL_WaylandDataOffer *offer, const char *mime_type);