SDL: Only call clipboard callbacks with mime types they expect (d6021)

From d6021b7d896cc2b3680a72b14ede46135fed67b6 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 9 Oct 2025 11:58:40 -0700
Subject: [PATCH] Only call clipboard callbacks with mime types they expect

Also clarified that returning NULL from a callback sends zero length data to the receiver, which should be able to handle that.

Fixes https://github.com/libsdl-org/SDL/issues/9586

(cherry picked from commit 23e3cbec20cf24b6f0644bfaedc0992d1ef922a9)
---
 include/SDL3/SDL_clipboard.h | 6 +-----
 src/video/SDL_clipboard.c    | 7 +++++++
 test/testclipboard.c         | 1 +
 3 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/include/SDL3/SDL_clipboard.h b/include/SDL3/SDL_clipboard.h
index 3ca56a48af90c..6f6fe6c5441b0 100644
--- a/include/SDL3/SDL_clipboard.h
+++ b/include/SDL3/SDL_clipboard.h
@@ -198,11 +198,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_HasPrimarySelectionText(void);
  * \param mime_type the requested mime-type.
  * \param size a pointer filled in with the length of the returned data.
  * \returns a pointer to the data for the provided mime-type. Returning NULL
- *          or setting the length to 0 will cause no data to be sent to the
- *          "receiver". It is up to the receiver to handle this. Essentially
- *          returning no data is more or less undefined behavior and may cause
- *          breakage in receiving applications. The returned data will not be
- *          freed, so it needs to be retained and dealt with internally.
+ *          or setting the length to 0 will cause zero length data to be sent to the "receiver", which should be able to handle this. The returned data will not be freed, so it needs to be retained and dealt with internally.
  *
  * \since This function is available since SDL 3.2.0.
  *
diff --git a/src/video/SDL_clipboard.c b/src/video/SDL_clipboard.c
index 9d8c27d451867..a7653f3b45cd6 100644
--- a/src/video/SDL_clipboard.c
+++ b/src/video/SDL_clipboard.c
@@ -173,6 +173,11 @@ void *SDL_GetInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type
     void *data = NULL;
 
     if (_this->clipboard_callback) {
+        if (!SDL_HasInternalClipboardData(_this, mime_type)) {
+            // The callback hasn't advertised that it can handle this mime type
+            return NULL;
+        }
+
         const void *provided_data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, size);
         if (provided_data) {
             // Make a copy of it for the caller and guarantee null termination
@@ -180,6 +185,8 @@ void *SDL_GetInternalClipboardData(SDL_VideoDevice *_this, const char *mime_type
             if (data) {
                 SDL_memcpy(data, provided_data, *size);
                 SDL_memset((Uint8 *)data + *size, 0, sizeof(Uint32));
+            } else {
+                *size = 0;
             }
         }
     }
diff --git a/test/testclipboard.c b/test/testclipboard.c
index 9dad02be79c52..9b7215146c274 100644
--- a/test/testclipboard.c
+++ b/test/testclipboard.c
@@ -34,6 +34,7 @@ static const void *ClipboardDataCallback(void *userdata, const char *mime_type,
         *size = icon_bmp_len;
         return icon_bmp;
     } else {
+        SDL_Log("Called with unexpected mime type: %s", mime_type);
         return NULL;
     }
 }