Procházet zdrojové kódy

Clipboard data API revamp

The clipboard data API is now supported on all platforms, at least for internal use.
Sam Lantinga před 1 rokem
rodič
revize
35876da3c4

+ 2 - 1
VisualC-GDK/SDL/SDL.vcxproj

@@ -511,6 +511,7 @@
     <ClInclude Include="..\..\src\video\SDL_blit_auto.h" />
     <ClInclude Include="..\..\src\video\SDL_blit_copy.h" />
     <ClInclude Include="..\..\src\video\SDL_blit_slow.h" />
+    <ClInclude Include="..\..\src\video\SDL_clipboard_c.h" />
     <ClInclude Include="..\..\src\video\SDL_egl_c.h" />
     <ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
     <ClInclude Include="..\..\src\video\SDL_rect_c.h" />
@@ -785,4 +786,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>

+ 4 - 1
VisualC-GDK/SDL/SDL.vcxproj.filters

@@ -573,6 +573,9 @@
     <ClInclude Include="..\..\src\video\SDL_blit_slow.h">
       <Filter>video</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\video\SDL_clipboard_c.h">
+      <Filter>video</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\video\SDL_pixels_c.h">
       <Filter>video</Filter>
     </ClInclude>
@@ -1386,4 +1389,4 @@
   <ItemGroup>
     <ResourceCompile Include="..\..\src\core\windows\version.rc" />
   </ItemGroup>
-</Project>
+</Project>

+ 1 - 0
VisualC-WinRT/SDL-UWP.vcxproj

@@ -169,6 +169,7 @@
     <ClInclude Include="..\src\video\SDL_blit_auto.h" />
     <ClInclude Include="..\src\video\SDL_blit_copy.h" />
     <ClInclude Include="..\src\video\SDL_blit_slow.h" />
+    <ClInclude Include="..\src\video\SDL_clipboard_c.h" />
     <ClInclude Include="..\src\video\SDL_egl_c.h" />
     <ClInclude Include="..\src\video\SDL_pixels_c.h" />
     <ClInclude Include="..\src\video\SDL_rect_c.h" />

+ 3 - 0
VisualC-WinRT/SDL-UWP.vcxproj.filters

@@ -366,6 +366,9 @@
     <ClInclude Include="..\src\video\SDL_blit_slow.h">
       <Filter>Source Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\video\SDL_clipboard_c.h">
+      <Filter>video</Filter>
+    </ClInclude>
     <ClInclude Include="..\src\video\SDL_egl_c.h">
       <Filter>Source Files</Filter>
     </ClInclude>

+ 1 - 0
VisualC/SDL/SDL.vcxproj

@@ -433,6 +433,7 @@
     <ClInclude Include="..\..\src\video\SDL_blit_auto.h" />
     <ClInclude Include="..\..\src\video\SDL_blit_copy.h" />
     <ClInclude Include="..\..\src\video\SDL_blit_slow.h" />
+    <ClInclude Include="..\..\src\video\SDL_clipboard_c.h" />
     <ClInclude Include="..\..\src\video\SDL_egl_c.h" />
     <ClInclude Include="..\..\src\video\SDL_pixels_c.h" />
     <ClInclude Include="..\..\src\video\SDL_rect_c.h" />

+ 3 - 0
VisualC/SDL/SDL.vcxproj.filters

@@ -564,6 +564,9 @@
     <ClInclude Include="..\..\src\video\SDL_blit_slow.h">
       <Filter>video</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\video\SDL_clipboard_c.h">
+      <Filter>video</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\src\video\SDL_pixels_c.h">
       <Filter>video</Filter>
     </ClInclude>

+ 28 - 21
include/SDL3/SDL_clipboard.h

@@ -133,9 +133,12 @@ extern DECLSPEC SDL_bool SDLCALL SDL_HasPrimarySelectionText(void);
  * Callback function that will be called when data for the specified mime-type
  * is requested by the OS.
  *
- * \param size      The length of the returned data
- * \param mime_type The requested mime-type
+ * The callback function is called with NULL as the mime_type when the clipboard
+ * is cleared or new data is set. The clipboard is automatically cleared in SDL_Quit().
+ *
  * \param userdata  A pointer to provided user data
+ * \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 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
@@ -147,7 +150,18 @@ extern DECLSPEC SDL_bool SDLCALL SDL_HasPrimarySelectionText(void);
  *
  * \sa SDL_SetClipboardData
  */
-typedef void *(SDLCALL *SDL_ClipboardDataCallback)(size_t *size, const char *mime_type, void *userdata);
+typedef const void *(SDLCALL *SDL_ClipboardDataCallback)(void *userdata, const char *mime_type, size_t *size);
+
+/**
+ * Callback function that will be called when the clipboard is cleared, or new data is set.
+ *
+ * \param userdata A pointer to provided user data
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_SetClipboardData
+ */
+typedef void (SDLCALL *SDL_ClipboardCleanupCallback)(void *userdata);
 
 /**
  * Offer clipboard data to the OS
@@ -157,46 +171,39 @@ typedef void *(SDLCALL *SDL_ClipboardDataCallback)(size_t *size, const char *mim
  * data the callback function will be called allowing it to generate and
  * respond with the data for the requested mime-type.
  *
- * The userdata submitted to this function needs to be freed manually. The
- * following scenarios need to be handled:
- *
- * - When the programs clipboard is replaced (cancelled)
- *   SDL_EVENT_CLIPBOARD_CANCELLED
- * - Before calling SDL_Quit()
- *
  * \param callback A function pointer to the function that provides the
  *                 clipboard data
- * \param mime_count The number of mime-types in the mime_types list
+ * \param cleanup A function pointer to the function that cleans up the
+ *                 clipboard data
+ * \param userdata An opaque pointer that will be forwarded to the callbacks
  * \param mime_types A list of mime-types that are being offered
- * \param userdata An opaque pointer that will be forwarded to the callback
+ * \param num_mime_types The number of mime-types in the mime_types list
  * \returns 0 on success or a negative error code on failure; call
  *          SDL_GetError() for more information.
  *
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_ClipboardDataCallback
- * \sa SDL_GetClipboardUserdata
  * \sa SDL_SetClipboardData
  * \sa SDL_GetClipboardData
  * \sa SDL_HasClipboardData
  */
-extern DECLSPEC int SDLCALL SDL_SetClipboardData(SDL_ClipboardDataCallback callback, size_t mime_count,
-                                                 const char **mime_types, void *userdata);
+extern DECLSPEC int SDLCALL SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types);
 
 /**
- * Retrieve previously set userdata if any.
- *
- * \returns a pointer to the data or NULL if no data exists
+ * Clear the clipboard data
  *
  * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_SetClipboardData
  */
-extern DECLSPEC void *SDLCALL SDL_GetClipboardUserdata(void);
+extern DECLSPEC int SDLCALL SDL_ClearClipboardData();
 
 /**
  * Get the data from clipboard for a given mime type
  *
- * \param length Length of the data
  * \param mime_type The mime type to read from the clipboard
+ * \param size      A pointer filled in with the length of the returned data
  * \returns the retrieved data buffer or NULL on failure; call SDL_GetError()
  *          for more information. Caller must call SDL_free() on the returned
  *          pointer when done with it.
@@ -205,7 +212,7 @@ extern DECLSPEC void *SDLCALL SDL_GetClipboardUserdata(void);
  *
  * \sa SDL_SetClipboardData
  */
-extern DECLSPEC void *SDLCALL SDL_GetClipboardData(size_t *length, const char *mime_type);
+extern DECLSPEC void *SDLCALL SDL_GetClipboardData(const char *mime_type, size_t *size);
 
 /**
  * Query whether there is data in the clipboard for the provided mime type

+ 2 - 2
include/SDL3/SDL_events.h

@@ -540,9 +540,9 @@ typedef struct SDL_DropEvent
  *
  * \sa SDL_SetClipboardData
  */
-typedef struct SDL_ClipboardCancelled
+typedef struct SDL_ClipboardEvent
 {
-    Uint32 type;        /**< ::SDL_EVENT_CLIPBOARD_CANCELLED or ::SDL_EVENT_CLIPBOARD_UPDATE */
+    Uint32 type;        /**< ::SDL_EVENT_CLIPBOARD_UPDATE or ::SDL_EVENT_CLIPBOARD_CANCELLED */
     Uint64 timestamp;   /**< In nanoseconds, populated using SDL_GetTicksNS() */
     void *userdata;     /**< User data if any has been set. NULL for ::SDL_EVENT_CLIPBOARD_UPDATE */
 } SDL_ClipboardEvent;

+ 1 - 1
src/dynapi/SDL_dynapi.sym

@@ -162,7 +162,6 @@ SDL3_0.0.0 {
     SDL_GetCPUCount;
     SDL_GetClipboardData;
     SDL_GetClipboardText;
-    SDL_GetClipboardUserdata;
     SDL_GetClosestFullscreenDisplayMode;
     SDL_GetCurrentAudioDriver;
     SDL_GetCurrentDisplayMode;
@@ -869,6 +868,7 @@ SDL3_0.0.0 {
     SDL_wcsncmp;
     SDL_wcsstr;
     SDL_wcstol;
+    SDL_ClearClipboardData;
     # extra symbols go here (don't modify this line)
   local: *;
 };

+ 1 - 1
src/dynapi/SDL_dynapi_overrides.h

@@ -186,7 +186,6 @@
 #define SDL_GetCPUCount SDL_GetCPUCount_REAL
 #define SDL_GetClipboardData SDL_GetClipboardData_REAL
 #define SDL_GetClipboardText SDL_GetClipboardText_REAL
-#define SDL_GetClipboardUserdata SDL_GetClipboardUserdata_REAL
 #define SDL_GetClosestFullscreenDisplayMode SDL_GetClosestFullscreenDisplayMode_REAL
 #define SDL_GetCurrentAudioDriver SDL_GetCurrentAudioDriver_REAL
 #define SDL_GetCurrentDisplayMode SDL_GetCurrentDisplayMode_REAL
@@ -895,3 +894,4 @@
 #define SDL_wcstol SDL_wcstol_REAL
 
 /* New API symbols are added at the end */
+#define SDL_ClearClipboardData SDL_ClearClipboardData_REAL

+ 3 - 3
src/dynapi/SDL_dynapi_procs.h

@@ -259,9 +259,8 @@ SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioForma
 SDL_DYNAPI_PROC(char*,SDL_GetBasePath,(void),(),return)
 SDL_DYNAPI_PROC(int,SDL_GetCPUCacheLineSize,(void),(),return)
 SDL_DYNAPI_PROC(int,SDL_GetCPUCount,(void),(),return)
-SDL_DYNAPI_PROC(void*,SDL_GetClipboardData,(size_t *a, const char *b),(a,b),return)
+SDL_DYNAPI_PROC(void*,SDL_GetClipboardData,(const char *a, size_t *b),(a,b),return)
 SDL_DYNAPI_PROC(char*,SDL_GetClipboardText,(void),(),return)
-SDL_DYNAPI_PROC(void*,SDL_GetClipboardUserdata,(void),(),return)
 SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetClosestFullscreenDisplayMode,(SDL_DisplayID a, int b, int c, float d, SDL_bool e),(a,b,c,d,e),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetCurrentAudioDriver,(void),(),return)
 SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetCurrentDisplayMode,(SDL_DisplayID a),(a),return)
@@ -666,7 +665,7 @@ SDL_DYNAPI_PROC(int,SDL_SendGamepadEffect,(SDL_Gamepad *a, const void *b, int c)
 SDL_DYNAPI_PROC(int,SDL_SendJoystickEffect,(SDL_Joystick *a, const void *b, int c),(a,b,c),return)
 SDL_DYNAPI_PROC(void,SDL_SetAssertionHandler,(SDL_AssertionHandler a, void *b),(a,b),)
 SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat b, int c, int d, SDL_AudioFormat e, int f, int g),(a,b,c,d,e,f,g),return)
-SDL_DYNAPI_PROC(int,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, size_t b, const char **c, void *d),(a,b,c,d),return)
+SDL_DYNAPI_PROC(int,SDL_SetClipboardData,(SDL_ClipboardDataCallback a, SDL_ClipboardCleanupCallback b, void *c, const char **d, size_t e),(a,b,c,d,e),return)
 SDL_DYNAPI_PROC(int,SDL_SetClipboardText,(const char *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_SetCursor,(SDL_Cursor *a),(a),return)
 SDL_DYNAPI_PROC(void,SDL_SetEventEnabled,(Uint32 a, SDL_bool b),(a,b),)
@@ -940,3 +939,4 @@ SDL_DYNAPI_PROC(wchar_t*,SDL_wcsstr,(const wchar_t *a, const wchar_t *b),(a,b),r
 SDL_DYNAPI_PROC(long,SDL_wcstol,(const wchar_t *a, wchar_t **b, int c),(a,b,c),return)
 
 /* New API symbols are added at the end */
+SDL_DYNAPI_PROC(int,SDL_ClearClipboardData,(void),(),return)

+ 0 - 1
src/events/SDL_clipboardevents_c.h

@@ -24,7 +24,6 @@
 #define SDL_clipboardevents_c_h_
 
 extern int SDL_SendClipboardUpdate(void);
-
 extern int SDL_SendClipboardCancelled(void *userdata);
 
 #endif /* SDL_clipboardevents_c_h_ */

+ 2 - 0
src/events/SDL_events.c

@@ -210,6 +210,8 @@ static void SDL_LogEvent(const SDL_Event *event)
         break;
         SDL_EVENT_CASE(SDL_EVENT_CLIPBOARD_UPDATE)
         break;
+        SDL_EVENT_CASE(SDL_EVENT_CLIPBOARD_CANCELLED)
+        break;
         SDL_EVENT_CASE(SDL_EVENT_RENDER_TARGETS_RESET)
         break;
         SDL_EVENT_CASE(SDL_EVENT_RENDER_DEVICE_RESET)

+ 132 - 14
src/test/SDL_test_common.c

@@ -1757,6 +1757,9 @@ static void SDLTest_PrintEvent(SDL_Event *event)
     case SDL_EVENT_CLIPBOARD_UPDATE:
         SDL_Log("SDL EVENT: Clipboard updated");
         break;
+    case SDL_EVENT_CLIPBOARD_CANCELLED:
+        SDL_Log("SDL EVENT: Clipboard ownership canceled");
+        break;
 
     case SDL_EVENT_FINGER_MOTION:
         SDL_Log("SDL EVENT: Finger: motion touch=%ld, finger=%ld, x=%f, y=%f, dx=%f, dy=%f, pressure=%f",
@@ -1824,10 +1827,69 @@ static void SDLTest_PrintEvent(SDL_Event *event)
     }
 }
 
-static void SDLTest_ScreenShot(SDL_Renderer *renderer)
+#define SCREENSHOT_FILE "screenshot.bmp"
+
+typedef struct
+{
+    void *image;
+    size_t size;
+} SDLTest_ClipboardData;
+
+static void SDLTest_ScreenShotClipboardCleanup(void *context)
+{
+    SDLTest_ClipboardData *data = (SDLTest_ClipboardData *)context;
+
+    SDL_Log("Cleaning up screenshot image data\n");
+
+    if (data->image) {
+        SDL_free(data->image);
+    }
+    SDL_free(data);
+}
+
+static const void *SDLTest_ScreenShotClipboardProvider(void *context, const char *mime_type, size_t *size)
+{
+    SDLTest_ClipboardData *data = (SDLTest_ClipboardData *)context;
+
+    SDL_Log("Providing screenshot image data to clipboard!\n");
+
+    if (!data->image) {
+        SDL_RWops *file;
+
+        file = SDL_RWFromFile(SCREENSHOT_FILE, "r");
+        if (file) {
+            size_t length = (size_t)SDL_RWsize(file);
+            void *image = SDL_malloc(length);
+            if (image) {
+                if (SDL_RWread(file, image, length) != length) {
+                    SDL_Log("Couldn't read %s: %s\n", SCREENSHOT_FILE, SDL_GetError());
+                    SDL_free(image);
+                    image = NULL;
+                }
+            }
+            SDL_RWclose(file);
+
+            if (image) {
+                data->image = image;
+                data->size = length;
+            }
+        } else {
+            SDL_Log("Couldn't load %s: %s\n", SCREENSHOT_FILE, SDL_GetError());
+        }
+    }
+
+    *size = data->size;
+    return data->image;
+}
+
+static void SDLTest_CopyScreenShot(SDL_Renderer *renderer)
 {
     SDL_Rect viewport;
     SDL_Surface *surface;
+    const char *image_formats[] = {
+        "image/bmp"
+    };
+    SDLTest_ClipboardData *clipboard_data;
 
     if (renderer == NULL) {
         return;
@@ -1849,11 +1911,50 @@ static void SDLTest_ScreenShot(SDL_Renderer *renderer)
         return;
     }
 
-    if (SDL_SaveBMP(surface, "screenshot.bmp") < 0) {
-        SDL_Log("Couldn't save screenshot.bmp: %s\n", SDL_GetError());
+    if (SDL_SaveBMP(surface, SCREENSHOT_FILE) < 0) {
+        SDL_Log("Couldn't save %s: %s\n", SCREENSHOT_FILE, SDL_GetError());
         SDL_free(surface);
         return;
     }
+    SDL_free(surface);
+
+    clipboard_data = (SDLTest_ClipboardData *)SDL_calloc(1, sizeof(*clipboard_data));
+    if (!clipboard_data) {
+        SDL_Log("Couldn't allocate clipboard data\n");
+        return;
+    }
+    SDL_SetClipboardData(SDLTest_ScreenShotClipboardProvider, SDLTest_ScreenShotClipboardCleanup, clipboard_data, image_formats, SDL_arraysize(image_formats));
+    SDL_Log("Saved screenshot to %s and clipboard\n", SCREENSHOT_FILE);
+}
+
+static void SDLTest_PasteScreenShot(void)
+{
+    const char *image_formats[] = {
+        "image/bmp",
+        "image/png",
+        "image/tiff",
+    };
+    size_t i;
+
+    for (i = 0; i < SDL_arraysize(image_formats); ++i) {
+        size_t size;
+        void *data = SDL_GetClipboardData(image_formats[i], &size);
+        if (data) {
+            char filename[16];
+            SDL_RWops *file;
+
+            SDL_snprintf(filename, sizeof(filename), "clipboard.%s", image_formats[i] + 6);
+            file = SDL_RWFromFile(filename, "w");
+            if (file) {
+                SDL_Log("Writing clipboard image to %s", filename);
+                SDL_RWwrite(file, data, size);
+                SDL_RWclose(file);
+            }
+            SDL_free(data);
+            return;
+        }
+    }
+    SDL_Log("No supported screenshot data in the clipboard");
 }
 
 static void FullscreenTo(SDLTest_CommonState *state, int index, int windowId)
@@ -1974,7 +2075,7 @@ void SDLTest_CommonEvent(SDLTest_CommonState *state, SDL_Event *event, int *done
             if (window) {
                 for (i = 0; i < state->num_windows; ++i) {
                     if (window == state->windows[i]) {
-                        SDLTest_ScreenShot(state->renderers[i]);
+                        SDLTest_CopyScreenShot(state->renderers[i]);
                     }
                 }
             }
@@ -2080,12 +2181,24 @@ void SDLTest_CommonEvent(SDLTest_CommonState *state, SDL_Event *event, int *done
                 }
             }
             break;
-
         case SDLK_c:
             if (withControl) {
-                /* Ctrl-C copy awesome text! */
-                SDL_SetClipboardText("SDL rocks!\nYou know it!");
-                SDL_Log("Copied text to clipboard\n");
+                if (withShift) {
+                    /* Ctrl-Shift-C copy screenshot! */
+                    SDL_Window *window = SDL_GetWindowFromID(event->key.windowID);
+                    if (window) {
+                        for (i = 0; i < state->num_windows; ++i) {
+                            if (window == state->windows[i]) {
+                                SDLTest_CopyScreenShot(state->renderers[i]);
+                            }
+                        }
+                    }
+                } else {
+                    /* Ctrl-C copy awesome text! */
+                    SDL_SetClipboardText("SDL rocks!\nYou know it!");
+                    SDL_Log("Copied text to clipboard\n");
+                }
+                break;
             }
             if (withAlt) {
                 /* Alt-C toggle a render clip rectangle */
@@ -2118,14 +2231,19 @@ void SDLTest_CommonEvent(SDLTest_CommonState *state, SDL_Event *event, int *done
             break;
         case SDLK_v:
             if (withControl) {
-                /* Ctrl-V paste awesome text! */
-                char *text = SDL_GetClipboardText();
-                if (*text) {
-                    SDL_Log("Clipboard: %s\n", text);
+                if (withShift) {
+                    /* Ctrl-Shift-V paste screenshot! */
+                    SDLTest_PasteScreenShot();
                 } else {
-                    SDL_Log("Clipboard is empty\n");
+                    /* Ctrl-V paste awesome text! */
+                    char *text = SDL_GetClipboardText();
+                    if (*text) {
+                        SDL_Log("Clipboard: %s\n", text);
+                    } else {
+                        SDL_Log("Clipboard is empty\n");
+                    }
+                    SDL_free(text);
                 }
-                SDL_free(text);
             }
             break;
         case SDLK_f:

+ 146 - 34
src/video/SDL_clipboard.c

@@ -20,21 +20,103 @@
 */
 #include "SDL_internal.h"
 
+#include "SDL_clipboard_c.h"
 #include "SDL_sysvideo.h"
+#include "../events/SDL_clipboardevents_c.h"
 
-int SDL_SetClipboardData(SDL_ClipboardDataCallback callback, size_t mime_count, const char **mime_types, void *userdata)
+
+void SDL_CancelClipboardData(Uint32 sequence)
+{
+    SDL_VideoDevice *_this = SDL_GetVideoDevice();
+    size_t i;
+
+    if (sequence != _this->clipboard_sequence) {
+        /* This clipboard data was already canceled */
+        return;
+    }
+
+    SDL_SendClipboardCancelled(_this->clipboard_userdata);
+
+    if (_this->clipboard_cleanup) {
+        _this->clipboard_cleanup(_this->clipboard_userdata);
+    }
+
+    if (_this->clipboard_mime_types) {
+        for (i = 0; i < _this->num_clipboard_mime_types; ++i) {
+            SDL_free(_this->clipboard_mime_types[i]);
+        }
+        SDL_free(_this->clipboard_mime_types);
+        _this->clipboard_mime_types = NULL;
+        _this->num_clipboard_mime_types = 0;
+    }
+
+    _this->clipboard_callback = NULL;
+    _this->clipboard_cleanup = NULL;
+    _this->clipboard_userdata = NULL;
+}
+
+int SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types)
 {
     SDL_VideoDevice *_this = SDL_GetVideoDevice();
+    size_t i;
 
     if (_this == NULL) {
         return SDL_SetError("Video subsystem must be initialized to set clipboard text");
     }
 
+    /* Parameter validation */
+    if (!((callback && mime_types && num_mime_types > 0) ||
+          (!callback && !mime_types && num_mime_types == 0))) {
+        return SDL_SetError("Invalid parameters");
+    }
+
+    if (!callback && !_this->clipboard_callback) {
+        /* Nothing to do, don't modify the system clipboard */
+        return 0;
+    }
+
+    SDL_CancelClipboardData(_this->clipboard_sequence);
+
+    ++_this->clipboard_sequence;
+    if (!_this->clipboard_sequence) {
+        _this->clipboard_sequence = 1;
+    }
+    _this->clipboard_callback = callback;
+    _this->clipboard_cleanup = cleanup;
+    _this->clipboard_userdata = userdata;
+
+    if (mime_types && num_mime_types > 0) {
+        size_t num_allocated = 0;
+
+        _this->clipboard_mime_types = (char **)SDL_malloc(num_mime_types * sizeof(char *));
+        if (_this->clipboard_mime_types) {
+            for (i = 0; i < num_mime_types; ++i) {
+                _this->clipboard_mime_types[i] = SDL_strdup(mime_types[i]);
+                if (_this->clipboard_mime_types[i]) {
+                    ++num_allocated;
+                }
+            }
+        }
+        if (num_allocated < num_mime_types) {
+            SDL_ClearClipboardData();
+            return SDL_OutOfMemory();
+        }
+        _this->num_clipboard_mime_types = num_mime_types;
+    }
+
     if (_this->SetClipboardData) {
-        return _this->SetClipboardData(_this, callback, mime_count, mime_types, userdata);
-    } else {
-        return SDL_Unsupported();
+        if (_this->SetClipboardData(_this) < 0) {
+            return -1;
+        }
     }
+
+    SDL_SendClipboardUpdate();
+    return 0;
+}
+
+int SDL_ClearClipboardData(void)
+{
+    return SDL_SetClipboardData(NULL, NULL, NULL, NULL, 0);
 }
 
 int SDL_SetClipboardText(const char *text)
@@ -49,12 +131,16 @@ int SDL_SetClipboardText(const char *text)
         text = "";
     }
     if (_this->SetClipboardText) {
-        return _this->SetClipboardText(_this, text);
+        if (_this->SetClipboardText(_this, text) < 0) {
+            return -1;
+        }
     } else {
         SDL_free(_this->clipboard_text);
         _this->clipboard_text = SDL_strdup(text);
-        return 0;
     }
+
+    SDL_SendClipboardUpdate();
+    return 0;
 }
 
 int SDL_SetPrimarySelectionText(const char *text)
@@ -69,27 +155,55 @@ int SDL_SetPrimarySelectionText(const char *text)
         text = "";
     }
     if (_this->SetPrimarySelectionText) {
-        return _this->SetPrimarySelectionText(_this, text);
+        if (_this->SetPrimarySelectionText(_this, text) < 0) {
+            return -1;
+        }
     } else {
         SDL_free(_this->primary_selection_text);
         _this->primary_selection_text = SDL_strdup(text);
-        return 0;
     }
+
+    SDL_SendClipboardUpdate();
+    return 0;
 }
 
-void *SDL_GetClipboardData(size_t *length, const char *mime_type)
+void *SDL_GetClipboardData(const char *mime_type, size_t *size)
 {
     SDL_VideoDevice *_this = SDL_GetVideoDevice();
+    void *data = NULL;
+
     if (_this == NULL) {
         SDL_SetError("Video subsystem must be initialized to get clipboard data");
         return NULL;
     }
 
-    if (_this->GetClipboardData) {
-        return _this->GetClipboardData(_this, length, mime_type);
-    } else {
+    if (!mime_type) {
+        SDL_InvalidParamError("mime_type");
         return NULL;
     }
+    if (!size) {
+        SDL_InvalidParamError("size");
+        return NULL;
+    }
+
+    if (_this->GetClipboardData) {
+        data = _this->GetClipboardData(_this, mime_type, size);
+    } else if (_this->clipboard_callback) {
+        const void *provided_data = _this->clipboard_callback(_this->clipboard_userdata, mime_type, size);
+        if (provided_data) {
+            /* Make a copy of it for the caller */
+            data = SDL_malloc(*size);
+            if (data) {
+                SDL_memcpy(data, provided_data, *size);
+            } else {
+                SDL_OutOfMemory();
+            }
+        }
+    }
+    if (!data) {
+        *size = 0;
+    }
+    return data;
 }
 
 char *SDL_GetClipboardText(void)
@@ -135,15 +249,28 @@ char *SDL_GetPrimarySelectionText(void)
 SDL_bool SDL_HasClipboardData(const char *mime_type)
 {
     SDL_VideoDevice *_this = SDL_GetVideoDevice();
+    size_t i;
+
     if (_this == NULL) {
         SDL_SetError("Video subsystem must be initialized to check clipboard data");
         return SDL_FALSE;
     }
 
+    if (!mime_type) {
+        SDL_InvalidParamError("mime_type");
+        return SDL_FALSE;
+    }
+
     if (_this->HasClipboardData) {
         return _this->HasClipboardData(_this, mime_type);
+    } else {
+        for (i = 0; i < _this->num_clipboard_mime_types; ++i) {
+            if (SDL_strcmp(mime_type, _this->clipboard_mime_types[i]) == 0) {
+                return SDL_TRUE;
+            }
+        }
+        return SDL_FALSE;
     }
-    return SDL_FALSE;
 }
 
 SDL_bool SDL_HasClipboardText(void)
@@ -177,26 +304,11 @@ SDL_bool SDL_HasPrimarySelectionText(void)
 
     if (_this->HasPrimarySelectionText) {
         return _this->HasPrimarySelectionText(_this);
+    } else {
+        if (_this->primary_selection_text && _this->primary_selection_text[0] != '\0') {
+            return SDL_TRUE;
+        } else {
+            return SDL_FALSE;
+        }
     }
-
-    if (_this->primary_selection_text && _this->primary_selection_text[0] != '\0') {
-        return SDL_TRUE;
-    }
-
-    return SDL_FALSE;
-}
-
-void *SDL_GetClipboardUserdata(void)
-{
-    SDL_VideoDevice *_this = SDL_GetVideoDevice();
-
-    if (_this == NULL) {
-        SDL_SetError("Video subsystem must be initialized to check clipboard userdata");
-        return NULL;
-    }
-
-    if (_this->GetClipboardUserdata) {
-        return _this->GetClipboardUserdata(_this);
-    }
-    return NULL;
 }

+ 29 - 0
src/video/SDL_clipboard_c.h

@@ -0,0 +1,29 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifndef SDL_clipboard_c_h_
+#define SDL_clipboard_c_h_
+
+/* Cancel the clipboard data callback, called internally for cleanup */
+extern void SDL_CancelClipboardData(Uint32 sequence);
+
+#endif /* SDL_clipboard_c_h_ */

+ 8 - 4
src/video/SDL_sysvideo.h

@@ -338,11 +338,9 @@ struct SDL_VideoDevice
     int (*SetPrimarySelectionText)(SDL_VideoDevice *_this, const char *text);
     char *(*GetPrimarySelectionText)(SDL_VideoDevice *_this);
     SDL_bool (*HasPrimarySelectionText)(SDL_VideoDevice *_this);
-    int (*SetClipboardData)(SDL_VideoDevice *_this, SDL_ClipboardDataCallback callback, size_t mime_count,
-                            const char **mime_types, void *userdata);
-    void *(*GetClipboardData)(SDL_VideoDevice *_this, size_t *len, const char *mime_type);
+    int (*SetClipboardData)(SDL_VideoDevice *_this);
+    void *(*GetClipboardData)(SDL_VideoDevice *_this, const char *mime_type, size_t *size);
     SDL_bool (*HasClipboardData)(SDL_VideoDevice *_this, const char *mime_type);
-    void *(*GetClipboardUserdata)(SDL_VideoDevice *_this);
 
     /* MessageBox */
     int (*ShowMessageBox)(SDL_VideoDevice *_this, const SDL_MessageBoxData *messageboxdata, int *buttonid);
@@ -367,6 +365,12 @@ struct SDL_VideoDevice
     SDL_Window *grabbed_window;
     Uint8 window_magic;
     SDL_WindowID next_object_id;
+    Uint32 clipboard_sequence;
+    SDL_ClipboardDataCallback clipboard_callback;
+    SDL_ClipboardCleanupCallback clipboard_cleanup;
+    void *clipboard_userdata;
+    char **clipboard_mime_types;
+    size_t num_clipboard_mime_types;
     char *clipboard_text;
     char *primary_selection_text;
     SDL_bool setting_display_mode;

+ 3 - 0
src/video/SDL_video.c

@@ -3695,6 +3695,9 @@ void SDL_VideoQuit(void)
         return;
     }
 
+    /* Make sure we don't try to serve clipboard data after this */
+    SDL_ClearClipboardData();
+
     /* Halt event processing before doing anything else */
     SDL_QuitTouch();
     SDL_QuitMouse();

+ 2 - 4
src/video/cocoa/SDL_cocoaclipboard.h

@@ -30,10 +30,8 @@ extern int Cocoa_SetClipboardText(SDL_VideoDevice *_this, const char *text);
 extern char *Cocoa_GetClipboardText(SDL_VideoDevice *_this);
 extern SDL_bool Cocoa_HasClipboardText(SDL_VideoDevice *_this);
 extern void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data);
-extern void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, size_t *len, const char *mime_type);
+extern int Cocoa_SetClipboardData(SDL_VideoDevice *_this);
+extern void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size);
 extern SDL_bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type);
-extern int Cocoa_SetClipboardData(SDL_VideoDevice *_this, SDL_ClipboardDataCallback callback, size_t mime_count,
-                                  const char **mime_types, void *userdata);
-
 
 #endif /* SDL_cocoaclipboard_h_ */

+ 50 - 47
src/video/cocoa/SDL_cocoaclipboard.m

@@ -57,10 +57,10 @@ provideDataForType:(NSPasteboardType)type
     @autoreleasepool {
         size_t size = 0;
         CFStringRef mimeType;
-        void *callbackData;
+        const void *callbackData;
         NSData *data;
         mimeType = UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)type, kUTTagClassMIMEType);
-        callbackData = m_callback(&size, [(__bridge NSString *)mimeType UTF8String], m_userdata);
+        callbackData = m_callback(m_userdata, [(__bridge NSString *)mimeType UTF8String], &size);
         CFRelease(mimeType);
         if (callbackData == NULL || size == 0) {
             return;
@@ -149,33 +149,71 @@ void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data)
     }
 }
 
-void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, size_t *len, const char *mime_type)
+int Cocoa_SetClipboardData(SDL_VideoDevice *_this)
 {
     @autoreleasepool {
         NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
-        NSData *itemData;
-        void *data;
-        *len = 0;
+        NSPasteboardItem *newItem = [NSPasteboardItem new];
+        NSMutableArray *utiTypes = [NSMutableArray new];
+        Cocoa_PasteboardDataProvider *provider = [[Cocoa_PasteboardDataProvider alloc] initWith: _this->clipboard_callback userData: _this->clipboard_userdata];
+        BOOL itemResult = FALSE;
+        BOOL writeResult = FALSE;
+        SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->driverdata;
+
+        for (int i = 0; i < _this->num_clipboard_mime_types; i++) {
+            CFStringRef mimeType = CFStringCreateWithCString(NULL, _this->clipboard_mime_types[i], kCFStringEncodingUTF8);
+            CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
+            CFRelease(mimeType);
+
+            [utiTypes addObject: (__bridge NSString *)utiType];
+            CFRelease(utiType);
+        }
+        itemResult = [newItem setDataProvider: provider forTypes: utiTypes];
+        if (itemResult == FALSE) {
+            return SDL_SetError("Unable to set clipboard item data");
+        }
+
+        [pasteboard clearContents];
+        writeResult = [pasteboard writeObjects: @[newItem]];
+        if (writeResult == FALSE) {
+            return SDL_SetError("Unable to set clipboard data");
+        }
+        data.clipboard_count = [pasteboard changeCount];
+    }
+    return 0;
+}
+
+void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *size)
+{
+    @autoreleasepool {
+        NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+        void *data = NULL;
+        *size = 0;
         for (NSPasteboardItem *item in [pasteboard pasteboardItems]) {
+            NSData *itemData;
             CFStringRef mimeType = CFStringCreateWithCString(NULL, mime_type, kCFStringEncodingUTF8);
             CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
             CFRelease(mimeType);
             itemData = [item dataForType: (__bridge NSString *)utiType];
             CFRelease(utiType);
             if (itemData != nil) {
-                *len = (size_t)[itemData length];
-                data = SDL_malloc(*len);
-                [itemData getBytes: data length: *len];
-                return data;
+                NSUInteger length = [itemData length];
+                *size = (size_t)length;
+                data = SDL_malloc(*size);
+                if (data) {
+                    [itemData getBytes: data length: length];
+                } else {
+                    SDL_OutOfMemory();
+                }
+                break;
             }
         }
-        return nil;
+        return data;
     }
 }
 
 SDL_bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
 {
-
     SDL_bool result = SDL_FALSE;
     @autoreleasepool {
         NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
@@ -191,39 +229,4 @@ SDL_bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
 
 }
 
-int Cocoa_SetClipboardData(SDL_VideoDevice *_this, SDL_ClipboardDataCallback callback, size_t mime_count,
-                           const char **mime_types, void *userdata)
-{
-    @autoreleasepool {
-        NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
-        NSPasteboardItem *newItem = [NSPasteboardItem new];
-        NSMutableArray *utiTypes = [NSMutableArray new];
-        Cocoa_PasteboardDataProvider *provider = [[Cocoa_PasteboardDataProvider alloc] initWith: callback userData: userdata];
-        BOOL itemResult = FALSE;
-        BOOL writeResult = FALSE;
-        SDL_CocoaVideoData *data = (__bridge SDL_CocoaVideoData *)_this->driverdata;
-
-        for (int i = 0; i < mime_count; i++) {
-            CFStringRef mimeType = CFStringCreateWithCString(NULL, mime_types[i], kCFStringEncodingUTF8);
-            CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
-            CFRelease(mimeType);
-
-            [utiTypes addObject: (__bridge NSString *)utiType];
-            CFRelease(utiType);
-        }
-        itemResult = [newItem setDataProvider: provider forTypes: utiTypes];
-        if (itemResult == FALSE) {
-            return SDL_SetError("Unable to set clipboard item data");
-        }
-
-        [pasteboard clearContents];
-        writeResult = [pasteboard writeObjects: @[newItem]];
-        if (writeResult == FALSE) {
-            return SDL_SetError("Unable to set clipboard data");
-        }
-        data.clipboard_count = [pasteboard changeCount];
-    }
-    return 0;
-}
-
 #endif /* SDL_VIDEO_DRIVER_COCOA */

+ 1 - 1
src/video/cocoa/SDL_cocoavideo.m

@@ -175,9 +175,9 @@ static SDL_VideoDevice *Cocoa_CreateDevice(void)
         device->GetClipboardText = Cocoa_GetClipboardText;
         device->HasClipboardText = Cocoa_HasClipboardText;
 
+        device->SetClipboardData = Cocoa_SetClipboardData;
         device->GetClipboardData = Cocoa_GetClipboardData;
         device->HasClipboardData = Cocoa_HasClipboardData;
-        device->SetClipboardData = Cocoa_SetClipboardData;
 
         device->free = Cocoa_DeleteDevice;
 

+ 32 - 61
src/video/wayland/SDL_waylandclipboard.c

@@ -27,8 +27,7 @@
 #include "SDL_waylandevents_c.h"
 #include "SDL_waylandclipboard.h"
 
-int Wayland_SetClipboardData(SDL_VideoDevice *_this, SDL_ClipboardDataCallback callback, size_t mime_count, const char **mime_types,
-                             void *userdata)
+int Wayland_SetClipboardData(SDL_VideoDevice *_this)
 {
     SDL_VideoData *video_data = NULL;
     SDL_WaylandDataDevice *data_device = NULL;
@@ -39,11 +38,11 @@ int Wayland_SetClipboardData(SDL_VideoDevice *_this, SDL_ClipboardDataCallback c
     if (video_data->input != NULL && video_data->input->data_device != NULL) {
         data_device = video_data->input->data_device;
 
-        if (callback && mime_types) {
+        if (_this->clipboard_callback && _this->clipboard_mime_types) {
             SDL_WaylandDataSource *source = Wayland_data_source_create(_this);
-            Wayland_data_source_set_callback(source, callback, userdata, SDL_FALSE);
+            Wayland_data_source_set_callback(source, _this->clipboard_callback, _this->clipboard_userdata, _this->clipboard_sequence);
 
-            status = Wayland_data_device_set_selection(data_device, source, mime_count, mime_types);
+            status = Wayland_data_device_set_selection(data_device, source, (const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types);
             if (status != 0) {
                 Wayland_data_source_destroy(source);
             }
@@ -64,29 +63,22 @@ static const char *text_mime_types[TEXT_MIME_TYPES_LEN] = {
     "STRING",
 };
 
-static void *Wayland_ClipboardTextCallback(size_t *length, const char *mime_type, void *userdata)
+static const void *Wayland_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *length)
 {
-    void *data = NULL;
-    SDL_bool valid_mime_type = SDL_FALSE;
-    *length = 0;
+    const void *data = NULL;
 
-    if (userdata == NULL) {
-        return data;
-    }
+    *length = 0;
 
-    for (size_t i = 0; i < TEXT_MIME_TYPES_LEN; ++i) {
-        if (SDL_strcmp(mime_type, text_mime_types[i]) == 0) {
-            valid_mime_type = SDL_TRUE;
-            break;
+    if (userdata) {
+        for (size_t i = 0; i < TEXT_MIME_TYPES_LEN; ++i) {
+            if (SDL_strcmp(mime_type, text_mime_types[i]) == 0) {
+                char *text = userdata;
+                *length = SDL_strlen(text);
+                data = userdata;
+                break;
+            }
         }
     }
-
-    if (valid_mime_type) {
-        char *text = userdata;
-        *length = SDL_strlen(text);
-        data = userdata;
-    }
-
     return data;
 }
 
@@ -106,9 +98,9 @@ int Wayland_SetClipboardText(SDL_VideoDevice *_this, const char *text)
 
             if (text[0] != '\0') {
                 SDL_WaylandDataSource *source = Wayland_data_source_create(_this);
-                Wayland_data_source_set_callback(source, Wayland_ClipboardTextCallback, SDL_strdup(text), SDL_TRUE);
+                Wayland_data_source_set_callback(source, Wayland_ClipboardTextCallback, SDL_strdup(text), 0);
 
-                status = Wayland_data_device_set_selection(data_device, source, TEXT_MIME_TYPES_LEN, text_mime_types);
+                status = Wayland_data_device_set_selection(data_device, source, text_mime_types, TEXT_MIME_TYPES_LEN);
                 if (status != 0) {
                     Wayland_data_source_destroy(source);
                 }
@@ -140,8 +132,8 @@ int Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
 
                 status = Wayland_primary_selection_device_set_selection(primary_selection_device,
                                                                         source,
-                                                                        TEXT_MIME_TYPES_LEN,
-                                                                        text_mime_types);
+                                                                        text_mime_types,
+                                                                        TEXT_MIME_TYPES_LEN);
                 if (status != 0) {
                     Wayland_primary_selection_source_destroy(source);
                 }
@@ -154,7 +146,7 @@ int Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
     return status;
 }
 
-void *Wayland_GetClipboardData(SDL_VideoDevice *_this, size_t *length, const char *mime_type)
+void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length)
 {
     SDL_VideoData *video_data = NULL;
     SDL_WaylandDataDevice *data_device = NULL;
@@ -164,13 +156,10 @@ void *Wayland_GetClipboardData(SDL_VideoDevice *_this, size_t *length, const cha
     video_data = _this->driverdata;
     if (video_data->input != NULL && video_data->input->data_device != NULL) {
         data_device = video_data->input->data_device;
-        if (data_device->selection_source != NULL) {
-            buffer = Wayland_data_source_get_data(data_device->selection_source,
-                    length, mime_type, SDL_FALSE);
-        } else if (Wayland_data_offer_has_mime(
-                    data_device->selection_offer, mime_type)) {
-            buffer = Wayland_data_offer_receive(data_device->selection_offer,
-                    length, mime_type, SDL_FALSE);
+        if (data_device->selection_source && data_device->selection_source->userdata.sequence != 0) {
+            buffer = Wayland_data_source_get_data(data_device->selection_source, mime_type, length, SDL_FALSE);
+        } 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, SDL_FALSE);
         }
     }
 
@@ -191,12 +180,12 @@ char *Wayland_GetClipboardText(SDL_VideoDevice *_this)
         video_data = _this->driverdata;
         if (video_data->input != NULL && video_data->input->data_device != NULL) {
             data_device = video_data->input->data_device;
-            if (data_device->selection_source != NULL) {
-                text = Wayland_data_source_get_data(data_device->selection_source, &length, TEXT_MIME, SDL_TRUE);
+            if (data_device->selection_source && data_device->selection_source->userdata.sequence == 0) {
+                text = Wayland_data_source_get_data(data_device->selection_source, TEXT_MIME, &length, SDL_TRUE);
             } else if (Wayland_data_offer_has_mime(
                         data_device->selection_offer, TEXT_MIME)) {
                 text = Wayland_data_offer_receive(data_device->selection_offer,
-                                                  &length, TEXT_MIME, SDL_TRUE);
+                                                  TEXT_MIME, &length, SDL_TRUE);
             }
         }
     }
@@ -223,11 +212,11 @@ char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this)
         if (video_data->input != NULL && video_data->input->primary_selection_device != NULL) {
             primary_selection_device = video_data->input->primary_selection_device;
             if (primary_selection_device->selection_source != NULL) {
-                text = Wayland_primary_selection_source_get_data(primary_selection_device->selection_source, &length, TEXT_MIME, SDL_TRUE);
+                text = Wayland_primary_selection_source_get_data(primary_selection_device->selection_source, TEXT_MIME, &length, SDL_TRUE);
             } else if (Wayland_primary_selection_offer_has_mime(
                         primary_selection_device->selection_offer, TEXT_MIME)) {
                 text = Wayland_primary_selection_offer_receive(primary_selection_device->selection_offer,
-                                                               &length, TEXT_MIME, SDL_TRUE);
+                                                               TEXT_MIME, &length, SDL_TRUE);
             }
         }
     }
@@ -250,8 +239,8 @@ static SDL_bool HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
         data_device = video_data->input->data_device;
         if (data_device->selection_source != NULL) {
             size_t length = 0;
-            char *buffer = Wayland_data_source_get_data(data_device->selection_source, &length, mime_type, SDL_TRUE);
-            result = buffer != NULL;
+            char *buffer = Wayland_data_source_get_data(data_device->selection_source, mime_type, &length, SDL_TRUE);
+            result = (buffer ? SDL_TRUE : SDL_FALSE);
             SDL_free(buffer);
         } else {
             result = Wayland_data_offer_has_mime(data_device->selection_offer, mime_type);
@@ -285,7 +274,7 @@ SDL_bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this)
             if (primary_selection_device->selection_source != NULL) {
                 size_t length = 0;
                 char *buffer = Wayland_primary_selection_source_get_data(primary_selection_device->selection_source,
-                                                                         &length, TEXT_MIME, SDL_TRUE);
+                                                                         TEXT_MIME, &length, SDL_TRUE);
                 result = buffer != NULL;
                 SDL_free(buffer);
             } else {
@@ -297,22 +286,4 @@ SDL_bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this)
     return result;
 }
 
-void *Wayland_GetClipboardUserdata(SDL_VideoDevice *_this)
-{
-    SDL_VideoData *video_data = NULL;
-    SDL_WaylandDataDevice *data_device = NULL;
-    void *data = NULL;
-
-    video_data = _this->driverdata;
-    if (video_data->input != NULL && video_data->input->data_device != NULL) {
-        data_device = video_data->input->data_device;
-        if (data_device->selection_source != NULL
-                && data_device->selection_source->userdata.internal == SDL_FALSE) {
-            data = data_device->selection_source->userdata.data;
-        }
-    }
-
-    return data;
-}
-
 #endif /* SDL_VIDEO_DRIVER_WAYLAND */

+ 2 - 4
src/video/wayland/SDL_waylandclipboard.h

@@ -23,9 +23,8 @@
 #ifndef SDL_waylandclipboard_h_
 #define SDL_waylandclipboard_h_
 
-extern int Wayland_SetClipboardData(SDL_VideoDevice *_this, SDL_ClipboardDataCallback callback, size_t mime_count,
-                                    const char **mime_types, void *userdata);
-extern void *Wayland_GetClipboardData(SDL_VideoDevice *_this, size_t *length, const char *mime_type);
+extern int Wayland_SetClipboardData(SDL_VideoDevice *_this);
+extern void *Wayland_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length);
 extern SDL_bool Wayland_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type);
 extern int Wayland_SetClipboardText(SDL_VideoDevice *_this, const char *text);
 extern char *Wayland_GetClipboardText(SDL_VideoDevice *_this);
@@ -33,6 +32,5 @@ extern SDL_bool Wayland_HasClipboardText(SDL_VideoDevice *_this);
 extern int Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text);
 extern char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this);
 extern SDL_bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this);
-extern void *Wayland_GetClipboardUserdata(SDL_VideoDevice *_this);
 
 #endif /* SDL_waylandclipboard_h_ */

+ 28 - 27
src/video/wayland/SDL_waylanddatamanager.c

@@ -30,6 +30,7 @@
 
 #include "../../core/unix/SDL_poll.h"
 #include "../../events/SDL_events_c.h"
+#include "../SDL_clipboard_c.h"
 
 #include "SDL_waylandvideo.h"
 #include "SDL_waylanddatamanager.h"
@@ -218,7 +219,7 @@ static void mime_data_list_free(struct wl_list *list)
     }
 }
 
-static size_t Wayland_send_data(void *data, size_t length, int fd)
+static size_t Wayland_send_data(const void *data, size_t length, int fd)
 {
     size_t result = 0;
 
@@ -234,11 +235,11 @@ static size_t Wayland_send_data(void *data, size_t length, int fd)
 
 ssize_t Wayland_data_source_send(SDL_WaylandDataSource *source, const char *mime_type, int fd)
 {
-    void *data = NULL;
+    const void *data = NULL;
     size_t length = 0;
 
     if (source->callback) {
-        data = source->callback(&length, mime_type, source->userdata.data);
+        data = source->callback(source->userdata.data, mime_type, &length);
     }
 
     return Wayland_send_data(data, length, fd);
@@ -246,11 +247,11 @@ ssize_t Wayland_data_source_send(SDL_WaylandDataSource *source, const char *mime
 
 ssize_t Wayland_primary_selection_source_send(SDL_WaylandPrimarySelectionSource *source, const char *mime_type, int fd)
 {
-    void *data = NULL;
+    const void *data = NULL;
     size_t length = 0;
 
     if (source->callback) {
-        data = source->callback(&length, mime_type, source->userdata.data);
+        data = source->callback(source->userdata.data, mime_type, &length);
     }
 
     return Wayland_send_data(data, length, fd);
@@ -259,12 +260,12 @@ ssize_t Wayland_primary_selection_source_send(SDL_WaylandPrimarySelectionSource
 void Wayland_data_source_set_callback(SDL_WaylandDataSource *source,
                                       SDL_ClipboardDataCallback callback,
                                       void *userdata,
-                                      SDL_bool internal)
+                                      Uint32 sequence)
 {
     if (source != NULL) {
-    source->callback = callback;
-    source->userdata.internal = internal;
-    source->userdata.data = userdata;
+        source->callback = callback;
+        source->userdata.sequence = sequence;
+        source->userdata.data = userdata;
     }
 }
 
@@ -276,12 +277,12 @@ int Wayland_primary_selection_source_set_callback(SDL_WaylandPrimarySelectionSou
         return SDL_InvalidParamError("source");
     }
     source->callback = callback;
-    source->userdata.internal = SDL_TRUE;
+    source->userdata.sequence = 0;
     source->userdata.data = userdata;
     return 0;
 }
 
-static void *Wayland_clone_data_buffer(void *buffer, size_t *len, SDL_bool null_terminate)
+static void *Wayland_clone_data_buffer(const void *buffer, size_t *len, SDL_bool null_terminate)
 {
     void *clone = NULL;
     if (*len > 0 && buffer != NULL) {
@@ -307,17 +308,17 @@ static void *Wayland_clone_data_buffer(void *buffer, size_t *len, SDL_bool null_
 }
 
 void *Wayland_data_source_get_data(SDL_WaylandDataSource *source,
-                                   size_t *length, const char *mime_type,
+                                   const char *mime_type, size_t *length,
                                    SDL_bool null_terminate)
 {
     void *buffer = NULL;
-    void *internal_buffer;
+    const void *internal_buffer;
     *length = 0;
 
     if (source == NULL) {
         SDL_SetError("Invalid data source");
     } else if (source->callback != NULL) {
-        internal_buffer = source->callback(length, mime_type, source->userdata.data);
+        internal_buffer = source->callback(source->userdata.data, mime_type, length);
         buffer = Wayland_clone_data_buffer(internal_buffer, length, null_terminate);
     }
 
@@ -325,17 +326,17 @@ void *Wayland_data_source_get_data(SDL_WaylandDataSource *source,
 }
 
 void *Wayland_primary_selection_source_get_data(SDL_WaylandPrimarySelectionSource *source,
-                                                size_t *length, const char *mime_type,
+                                                const char *mime_type, size_t *length,
                                                 SDL_bool null_terminate)
 {
     void *buffer = NULL;
-    void *internal_buffer;
+    const void *internal_buffer;
     *length = 0;
 
     if (source == NULL) {
         SDL_SetError("Invalid primary selection source");
     } else if (source->callback) {
-        internal_buffer = source->callback(length, mime_type, source->userdata.data);
+        internal_buffer = source->callback(source->userdata.data, mime_type, length);
         buffer = Wayland_clone_data_buffer(internal_buffer, length, null_terminate);
     }
 
@@ -350,10 +351,10 @@ void Wayland_data_source_destroy(SDL_WaylandDataSource *source)
             data_device->selection_source = NULL;
         }
         wl_data_source_destroy(source->source);
-        if (source->userdata.internal == SDL_TRUE) {
-            SDL_free(source->userdata.data);
+        if (source->userdata.sequence) {
+            SDL_CancelClipboardData(source->userdata.sequence);
         } else {
-            SDL_SendClipboardCancelled(source->userdata.data);
+            SDL_free(source->userdata.data);
         }
         SDL_free(source);
     }
@@ -367,7 +368,7 @@ void Wayland_primary_selection_source_destroy(SDL_WaylandPrimarySelectionSource
             primary_selection_device->selection_source = NULL;
         }
         zwp_primary_selection_source_v1_destroy(source->source);
-        if (source->userdata.internal == SDL_TRUE) {
+        if (source->userdata.sequence == 0) {
             SDL_free(source->userdata.data);
         }
         SDL_free(source);
@@ -375,7 +376,7 @@ void Wayland_primary_selection_source_destroy(SDL_WaylandPrimarySelectionSource
 }
 
 void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer,
-                                 size_t *length, const char *mime_type,
+                                 const char *mime_type, size_t *length,
                                  SDL_bool null_terminate)
 {
     SDL_WaylandDataDevice *data_device = NULL;
@@ -409,7 +410,7 @@ void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer,
 }
 
 void *Wayland_primary_selection_offer_receive(SDL_WaylandPrimarySelectionOffer *offer,
-                                              size_t *length, const char *mime_type,
+                                              const char *mime_type, size_t *length,
                                               SDL_bool null_terminate)
 {
     SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
@@ -525,8 +526,8 @@ int Wayland_primary_selection_device_clear_selection(SDL_WaylandPrimarySelection
 
 int Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device,
                                       SDL_WaylandDataSource *source,
-                                      size_t mime_count,
-                                      const char **mime_types)
+                                      const char **mime_types,
+                                      size_t mime_count)
 {
     int status = 0;
 
@@ -567,8 +568,8 @@ int Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device,
 
 int Wayland_primary_selection_device_set_selection(SDL_WaylandPrimarySelectionDevice *primary_selection_device,
                                                    SDL_WaylandPrimarySelectionSource *source,
-                                                   size_t mime_count,
-                                                   const char **mime_types)
+                                                   const char **mime_types,
+                                                   size_t mime_count)
 {
     int status = 0;
 

+ 10 - 10
src/video/wayland/SDL_waylanddatamanager.h

@@ -40,7 +40,7 @@ typedef struct
 
 typedef struct SDL_WaylandUserdata
 {
-    SDL_bool internal;
+    Uint32 sequence;
     void *data;
 } SDL_WaylandUserdata;
 
@@ -111,29 +111,29 @@ extern ssize_t Wayland_primary_selection_source_send(SDL_WaylandPrimarySelection
 extern void Wayland_data_source_set_callback(SDL_WaylandDataSource *source,
                                             SDL_ClipboardDataCallback callback,
                                             void *userdata,
-                                            SDL_bool internal);
+                                            Uint32 sequence);
 extern int Wayland_primary_selection_source_set_callback(SDL_WaylandPrimarySelectionSource *source,
                                                          SDL_ClipboardDataCallback callback,
                                                          void *userdata);
 extern void *Wayland_data_source_get_data(SDL_WaylandDataSource *source,
-                                          size_t *length,
                                           const char *mime_type,
+                                          size_t *length,
                                           SDL_bool null_terminate);
 extern void *Wayland_primary_selection_source_get_data(SDL_WaylandPrimarySelectionSource *source,
-                                                       size_t *length,
                                                        const char *mime_type,
+                                                       size_t *length,
                                                        SDL_bool null_terminate);
 extern void Wayland_data_source_destroy(SDL_WaylandDataSource *source);
 extern void Wayland_primary_selection_source_destroy(SDL_WaylandPrimarySelectionSource *source);
 
 /* Wayland Data / Primary Selection Offer - (Receiving) */
 extern void *Wayland_data_offer_receive(SDL_WaylandDataOffer *offer,
-                                        size_t *length,
                                         const char *mime_type,
+                                        size_t *length,
                                         SDL_bool null_terminate);
 extern void *Wayland_primary_selection_offer_receive(SDL_WaylandPrimarySelectionOffer *offer,
-                                                     size_t *length,
                                                      const char *mime_type,
+                                                     size_t *length,
                                                      SDL_bool null_terminate);
 extern SDL_bool Wayland_data_offer_has_mime(SDL_WaylandDataOffer *offer,
                                             const char *mime_type);
@@ -151,12 +151,12 @@ extern int Wayland_data_device_clear_selection(SDL_WaylandDataDevice *device);
 extern int Wayland_primary_selection_device_clear_selection(SDL_WaylandPrimarySelectionDevice *device);
 extern int Wayland_data_device_set_selection(SDL_WaylandDataDevice *device,
                                              SDL_WaylandDataSource *source,
-                                             size_t mime_count,
-                                             const char **mime_types);
+                                             const char **mime_types,
+                                             size_t mime_count);
 extern int Wayland_primary_selection_device_set_selection(SDL_WaylandPrimarySelectionDevice *device,
                                                           SDL_WaylandPrimarySelectionSource *source,
-                                                          size_t mime_count,
-                                                          const char **mime_types);
+                                                          const char **mime_types,
+                                                          size_t mime_count);
 extern int Wayland_data_device_set_serial(SDL_WaylandDataDevice *device,
                                           uint32_t serial);
 extern int Wayland_primary_selection_device_set_serial(SDL_WaylandPrimarySelectionDevice *device,

+ 2 - 2
src/video/wayland/SDL_waylandevents.c

@@ -1873,7 +1873,7 @@ static void data_device_handle_motion(void *data, struct wl_data_device *wl_data
         /* TODO: SDL Support more mime types */
         size_t length;
         void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
-                &length, FILE_MIME, SDL_TRUE);
+                                                  FILE_MIME, &length, SDL_TRUE);
         if (buffer) {
             char *saveptr = NULL;
             char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr);
@@ -2022,7 +2022,7 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d
         /* TODO: SDL Support more mime types */
         size_t length;
         void *buffer = Wayland_data_offer_receive(data_device->drag_offer,
-                                                  &length, FILE_MIME, SDL_TRUE);
+                                                  FILE_MIME, &length, SDL_TRUE);
         if (buffer) {
             char *saveptr = NULL;
             char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr);

+ 0 - 1
src/video/wayland/SDL_waylandvideo.c

@@ -224,7 +224,6 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
     device->SetPrimarySelectionText = Wayland_SetPrimarySelectionText;
     device->GetPrimarySelectionText = Wayland_GetPrimarySelectionText;
     device->HasPrimarySelectionText = Wayland_HasPrimarySelectionText;
-    device->GetClipboardUserdata = Wayland_GetClipboardUserdata;
     device->StartTextInput = Wayland_StartTextInput;
     device->StopTextInput = Wayland_StopTextInput;
     device->SetTextInputRect = Wayland_SetTextInputRect;

+ 37 - 53
src/video/x11/SDL_x11clipboard.c

@@ -37,31 +37,24 @@ static const char *text_mime_types[TEXT_MIME_TYPES_LEN] = {
     "STRING",
 };
 
-static void *X11_ClipboardTextCallback(size_t *length, const char *mime_type, void *userdata)
+static const void *X11_ClipboardTextCallback(void *userdata, const char *mime_type, size_t *length)
 {
-    void *data = NULL;
-    SDL_bool valid_mime_type = SDL_FALSE;
-    size_t i;
+    const void *data = NULL;
 
     *length = 0;
 
-    if (userdata == NULL) {
-        return data;
-    }
+    if (userdata) {
+        size_t i;
 
-    for (i = 0; i < TEXT_MIME_TYPES_LEN; ++i) {
-        if (SDL_strcmp(mime_type, text_mime_types[i]) == 0) {
-            valid_mime_type = SDL_TRUE;
-            break;
+        for (i = 0; i < TEXT_MIME_TYPES_LEN; ++i) {
+            if (SDL_strcmp(mime_type, text_mime_types[i]) == 0) {
+                char *text = userdata;
+                *length = SDL_strlen(text);
+                data = userdata;
+                break;
+            }
         }
     }
-
-    if (valid_mime_type) {
-        char *text = userdata;
-        *length = SDL_strlen(text);
-        data = userdata;
-    }
-
     return data;
 }
 
@@ -88,7 +81,7 @@ static Window GetWindow(SDL_VideoDevice *_this)
 }
 
 static int SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_ClipboardDataCallback callback,
-                            size_t mime_count, const char **mime_types, void *userdata, SDL_bool internal)
+                            void *userdata, const char **mime_types, size_t mime_count, Uint32 sequence)
 {
     SDL_VideoData *videodata = _this->driverdata;
     Display *display = videodata->display;
@@ -110,19 +103,15 @@ static int SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_Clipboar
     clipboard_owner = X11_XGetSelectionOwner(display, selection) == window;
 
     /* If we are cancelling our own data we need to clean it up */
-    if (clipboard_owner) {
-        if (clipboard->internal == SDL_TRUE) {
-            SDL_free(clipboard->userdata);
-        } else {
-            SDL_SendClipboardCancelled(clipboard->userdata);
-        }
+    if (clipboard_owner && clipboard->sequence == 0) {
+        SDL_free(clipboard->userdata);
     }
 
     clipboard->callback = callback;
     clipboard->userdata = userdata;
     clipboard->mime_types = mime_types;
     clipboard->mime_count = mime_count;
-    clipboard->internal = internal;
+    clipboard->sequence = sequence;
 
     if (!clipboard_owner) {
         X11_XSetSelectionOwner(display, selection, window, CurrentTime);
@@ -130,7 +119,7 @@ static int SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_Clipboar
     return 0;
 }
 
-static void *CloneDataBuffer(void *buffer, size_t *len, SDL_bool nullterminate)
+static void *CloneDataBuffer(const void *buffer, size_t *len, SDL_bool nullterminate)
 {
     void *clone = NULL;
     if (*len > 0 && buffer != NULL) {
@@ -155,8 +144,8 @@ static void *CloneDataBuffer(void *buffer, size_t *len, SDL_bool nullterminate)
     return clone;
 }
 
-static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type, size_t *length,
-                              const char *mime_type, SDL_bool nullterminate)
+static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type,
+                              const char *mime_type, size_t *length, SDL_bool nullterminate)
 {
     SDL_VideoData *videodata = _this->driverdata;
     Display *display = videodata->display;
@@ -174,6 +163,7 @@ static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type, size_
     void *data = NULL;
     unsigned char *src = NULL;
     Atom XA_MIME = X11_XInternAtom(display, mime_type, False);
+    Atom XA_INCR = X11_XInternAtom(display, "INCR", False);
 
     *length = 0;
 
@@ -192,8 +182,8 @@ static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type, size_
         }
 
         if (clipboard->callback) {
-            src = clipboard->callback(length, mime_type, clipboard->userdata);
-            data = CloneDataBuffer(src, length, nullterminate);
+            const void *clipboard_data = clipboard->callback(clipboard->userdata, mime_type, length);
+            data = CloneDataBuffer(clipboard_data, length, nullterminate);
         }
     } else {
         /* Request that the selection owner copy the data to our window */
@@ -216,8 +206,8 @@ static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type, size_
                 SDL_SetError("Selection timeout");
                 /* We need to set the selection text so that next time we won't
                    timeout, otherwise we will hang on every call to this function. */
-                SetSelectionData(_this, selection_type, X11_ClipboardTextCallback, TEXT_MIME_TYPES_LEN,
-                                 text_mime_types, NULL, SDL_TRUE);
+                SetSelectionData(_this, selection_type, X11_ClipboardTextCallback, NULL,
+                                 text_mime_types, TEXT_MIME_TYPES_LEN, 0);
                 data = NULL;
                 *length = 0;
             }
@@ -228,6 +218,9 @@ static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type, size_
             if (seln_type == XA_MIME) {
                 *length = (size_t)count;
                 data = CloneDataBuffer(src, length, nullterminate);
+            } else if (seln_type == XA_INCR) {
+                /* FIXME: Need to implement the X11 INCR protocol */
+                /*SDL_Log("Need to implement the X11 INCR protocol");*/
             }
             X11_XFree(src);
         }
@@ -240,18 +233,17 @@ static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type, size_
     return data;
 }
 
-int X11_SetClipboardData(SDL_VideoDevice *_this, SDL_ClipboardDataCallback callback, size_t mime_count,
-                         const char **mime_types, void *userdata)
+int X11_SetClipboardData(SDL_VideoDevice *_this)
 {
     SDL_VideoData *videodata = _this->driverdata;
     Atom XA_CLIPBOARD = X11_XInternAtom(videodata->display, "CLIPBOARD", 0);
     if (XA_CLIPBOARD == None) {
         return SDL_SetError("Couldn't access X clipboard");
     }
-    return SetSelectionData(_this, XA_CLIPBOARD, callback, mime_count, mime_types, userdata, SDL_FALSE);
+    return SetSelectionData(_this, XA_CLIPBOARD, _this->clipboard_callback, _this->clipboard_userdata, (const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types, _this->clipboard_sequence);
 }
 
-void *X11_GetClipboardData(SDL_VideoDevice *_this, size_t *length, const char *mime_type)
+void *X11_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length)
 {
     SDL_VideoData *videodata = _this->driverdata;
     Atom XA_CLIPBOARD = X11_XInternAtom(videodata->display, "CLIPBOARD", 0);
@@ -260,26 +252,20 @@ void *X11_GetClipboardData(SDL_VideoDevice *_this, size_t *length, const char *m
         *length = 0;
         return NULL;
     }
-    return GetSelectionData(_this, XA_CLIPBOARD, length, mime_type, SDL_FALSE);
+    return GetSelectionData(_this, XA_CLIPBOARD, mime_type, length, SDL_FALSE);
 }
 
 SDL_bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
 {
     size_t length;
     void *data;
-    data = X11_GetClipboardData(_this, &length, mime_type);
+    data = X11_GetClipboardData(_this, mime_type, &length);
     if (data != NULL) {
         SDL_free(data);
     }
     return length > 0;
 }
 
-void *X11_GetClipboardUserdata(SDL_VideoDevice *_this)
-{
-    SDLX11_ClipboardData *cb = &_this->driverdata->clipboard;
-    return cb->internal ? NULL : cb->userdata;
-}
-
 int X11_SetClipboardText(SDL_VideoDevice *_this, const char *text)
 {
     SDL_VideoData *videodata = _this->driverdata;
@@ -287,14 +273,12 @@ int X11_SetClipboardText(SDL_VideoDevice *_this, const char *text)
     if (XA_CLIPBOARD == None) {
         return SDL_SetError("Couldn't access X clipboard");
     }
-    return SetSelectionData(_this, XA_CLIPBOARD, X11_ClipboardTextCallback, TEXT_MIME_TYPES_LEN, text_mime_types,
-                            SDL_strdup(text), SDL_TRUE);
+    return SetSelectionData(_this, XA_CLIPBOARD, X11_ClipboardTextCallback, SDL_strdup(text), text_mime_types, TEXT_MIME_TYPES_LEN, 0);
 }
 
 int X11_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
 {
-    return SetSelectionData(_this, XA_PRIMARY, X11_ClipboardTextCallback, TEXT_MIME_TYPES_LEN, text_mime_types,
-                            SDL_strdup(text), SDL_TRUE);
+    return SetSelectionData(_this, XA_PRIMARY, X11_ClipboardTextCallback, SDL_strdup(text), text_mime_types, TEXT_MIME_TYPES_LEN, 0);
 }
 
 char *X11_GetClipboardText(SDL_VideoDevice *_this)
@@ -307,13 +291,13 @@ char *X11_GetClipboardText(SDL_VideoDevice *_this)
         return SDL_strdup("");
     }
 
-    return GetSelectionData(_this, XA_CLIPBOARD, &length, text_mime_types[0], SDL_TRUE);
+    return GetSelectionData(_this, XA_CLIPBOARD, text_mime_types[0], &length, SDL_TRUE);
 }
 
 char *X11_GetPrimarySelectionText(SDL_VideoDevice *_this)
 {
     size_t length;
-    return GetSelectionData(_this, XA_PRIMARY, &length, text_mime_types[0], SDL_TRUE);
+    return GetSelectionData(_this, XA_PRIMARY, text_mime_types[0], &length, SDL_TRUE);
 }
 
 SDL_bool X11_HasClipboardText(SDL_VideoDevice *_this)
@@ -341,10 +325,10 @@ SDL_bool X11_HasPrimarySelectionText(SDL_VideoDevice *_this)
 void X11_QuitClipboard(SDL_VideoDevice *_this)
 {
     SDL_VideoData *data = _this->driverdata;
-    if (data->primary_selection.internal == SDL_TRUE) {
+    if (data->primary_selection.sequence == 0) {
         SDL_free(data->primary_selection.userdata);
     }
-    if (data->clipboard.internal == SDL_TRUE) {
+    if (data->clipboard.sequence == 0) {
         SDL_free(data->clipboard.userdata);
     }
 }

+ 3 - 5
src/video/x11/SDL_x11clipboard.h

@@ -30,14 +30,12 @@ typedef struct X11_ClipboardData {
     void *userdata;
     const char **mime_types;
     size_t mime_count;
-    SDL_bool internal;
+    Uint32 sequence;
 } SDLX11_ClipboardData;
 
-extern int X11_SetClipboardData(SDL_VideoDevice *_this, SDL_ClipboardDataCallback callback, size_t mime_count,
-                                const char **mime_types, void *userdata);
-extern void *X11_GetClipboardData(SDL_VideoDevice *_this, size_t *length, const char *mime_type);
+extern int X11_SetClipboardData(SDL_VideoDevice *_this);
+extern void *X11_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length);
 extern SDL_bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type);
-extern void *X11_GetClipboardUserdata(SDL_VideoDevice *_this);
 extern int X11_SetClipboardText(SDL_VideoDevice *_this, const char *text);
 extern char *X11_GetClipboardText(SDL_VideoDevice *_this);
 extern SDL_bool X11_HasClipboardText(SDL_VideoDevice *_this);

+ 11 - 8
src/video/x11/SDL_x11events.c

@@ -32,6 +32,7 @@
 #include "SDL_x11touch.h"
 #include "SDL_x11xinput2.h"
 #include "SDL_x11xfixes.h"
+#include "../SDL_clipboard_c.h"
 #include "../../core/unix/SDL_poll.h"
 #include "../../events/SDL_events_c.h"
 #include "../../events/SDL_mouse_c.h"
@@ -685,8 +686,9 @@ static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xeven
                         continue;
                     }
 
-                    /* FIXME: We don't support the X11 INCR protocol for large clipboards. Do we want that? */
-                    seln_data = clipboard->callback(&seln_length, mime_type, clipboard->userdata);
+                    /* FIXME: We don't support the X11 INCR protocol for large clipboards. Do we want that? - Yes, yes we do. */
+                    /* This is a safe cast, XChangeProperty() doesn't take a const value, but it doesn't modify the data */
+                    seln_data = (unsigned char *)clipboard->callback(clipboard->userdata, mime_type, &seln_length);
                     if (seln_data != NULL) {
                         X11_XChangeProperty(display, req->requestor, req->property,
                                             req->target, 8, PropModeReplace,
@@ -726,13 +728,14 @@ static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xeven
             clipboard = &videodata->primary_selection;
         } else if (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD) {
             clipboard = &videodata->clipboard;
-            if (clipboard->internal == SDL_FALSE) {
-                SDL_SendClipboardCancelled(clipboard->userdata);
-            }
         }
-        if (clipboard != NULL && clipboard->internal == SDL_TRUE) {
-            SDL_free(clipboard->userdata);
-            clipboard->userdata = NULL;
+        if (clipboard && clipboard->callback) {
+            if (clipboard->sequence) {
+                SDL_CancelClipboardData(clipboard->sequence);
+            } else {
+                SDL_free(clipboard->userdata);
+            }
+            SDL_zerop(clipboard);
         }
     } break;
     }

+ 0 - 1
src/video/x11/SDL_x11video.c

@@ -258,7 +258,6 @@ static SDL_VideoDevice *X11_CreateDevice(void)
     device->SetClipboardText = X11_SetClipboardText;
     device->GetClipboardText = X11_GetClipboardText;
     device->HasClipboardText = X11_HasClipboardText;
-    device->GetClipboardUserdata = X11_GetClipboardUserdata;
     device->SetPrimarySelectionText = X11_SetPrimarySelectionText;
     device->GetPrimarySelectionText = X11_GetPrimarySelectionText;
     device->HasPrimarySelectionText = X11_HasPrimarySelectionText;

+ 2 - 5
test/testautomation_clipboard.c

@@ -73,7 +73,7 @@ static int clipboard_testGetClipboardData(void *arg)
 {
     void *buffer = NULL;
     size_t length;
-    buffer = SDL_GetClipboardData(&length, "image/png");
+    buffer = SDL_GetClipboardData("image/png", &length);
     SDLTest_AssertPass("Call to SDL_GetClipboardData succeeded");
 
     if (buffer != NULL) {
@@ -134,16 +134,13 @@ static int clipboard_testSetClipboardData(void *arg)
 {
     int result = -1;
 
-    result = SDL_SetClipboardData(NULL, 0, NULL, NULL);
+    result = SDL_SetClipboardData(NULL, NULL, NULL, NULL, 0);
     SDLTest_AssertPass("Call to SDL_SetClipboardData succeeded");
     SDLTest_AssertCheck(
         result == 0,
         "Validate SDL_SetClipboardData result, expected 0, got %i",
         result);
 
-    SDL_GetClipboardUserdata();
-    SDLTest_AssertPass("Call to SDL_GetClipboardUserdata succeeded");
-
     return TEST_COMPLETED;
 }