From 35876da3c468db13834a80c1baaa0b34dd9d2536 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 3 Jul 2023 23:24:01 -0700 Subject: [PATCH] Clipboard data API revamp The clipboard data API is now supported on all platforms, at least for internal use. --- VisualC-GDK/SDL/SDL.vcxproj | 3 +- VisualC-GDK/SDL/SDL.vcxproj.filters | 5 +- VisualC-WinRT/SDL-UWP.vcxproj | 1 + VisualC-WinRT/SDL-UWP.vcxproj.filters | 3 + VisualC/SDL/SDL.vcxproj | 1 + VisualC/SDL/SDL.vcxproj.filters | 3 + include/SDL3/SDL_clipboard.h | 49 +++--- include/SDL3/SDL_events.h | 4 +- src/dynapi/SDL_dynapi.sym | 2 +- src/dynapi/SDL_dynapi_overrides.h | 2 +- src/dynapi/SDL_dynapi_procs.h | 6 +- src/events/SDL_clipboardevents_c.h | 1 - src/events/SDL_events.c | 2 + src/test/SDL_test_common.c | 146 +++++++++++++++-- src/video/SDL_clipboard.c | 182 +++++++++++++++++---- src/video/SDL_clipboard_c.h | 29 ++++ src/video/SDL_sysvideo.h | 12 +- src/video/SDL_video.c | 3 + src/video/cocoa/SDL_cocoaclipboard.h | 6 +- src/video/cocoa/SDL_cocoaclipboard.m | 101 ++++++------ src/video/cocoa/SDL_cocoavideo.m | 2 +- src/video/wayland/SDL_waylandclipboard.c | 93 ++++------- src/video/wayland/SDL_waylandclipboard.h | 6 +- src/video/wayland/SDL_waylanddatamanager.c | 55 ++++--- src/video/wayland/SDL_waylanddatamanager.h | 20 +-- src/video/wayland/SDL_waylandevents.c | 4 +- src/video/wayland/SDL_waylandvideo.c | 1 - src/video/x11/SDL_x11clipboard.c | 90 +++++----- src/video/x11/SDL_x11clipboard.h | 8 +- src/video/x11/SDL_x11events.c | 19 ++- src/video/x11/SDL_x11video.c | 1 - test/testautomation_clipboard.c | 7 +- 32 files changed, 552 insertions(+), 315 deletions(-) create mode 100644 src/video/SDL_clipboard_c.h diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index 6d898f98e..fd9d84557 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -511,6 +511,7 @@ + @@ -785,4 +786,4 @@ - \ No newline at end of file + diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index 769c45215..81dee5cc3 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -573,6 +573,9 @@ video + + video + video @@ -1386,4 +1389,4 @@ - \ No newline at end of file + diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj index 3a503634f..c6df98236 100644 --- a/VisualC-WinRT/SDL-UWP.vcxproj +++ b/VisualC-WinRT/SDL-UWP.vcxproj @@ -169,6 +169,7 @@ + diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters index 26bb708b7..871a43e1e 100644 --- a/VisualC-WinRT/SDL-UWP.vcxproj.filters +++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters @@ -366,6 +366,9 @@ Source Files + + video + Source Files diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index a111aaeb3..8b8f6d6a7 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -433,6 +433,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index cb03064bf..63fd04c06 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -564,6 +564,9 @@ video + + video + video diff --git a/include/SDL3/SDL_clipboard.h b/include/SDL3/SDL_clipboard.h index 4e223a57a..ee9d7a821 100644 --- a/include/SDL3/SDL_clipboard.h +++ b/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 diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index cf80b5d5b..55ce261da 100644 --- a/include/SDL3/SDL_events.h +++ b/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; diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 2ec47ef11..10a0a2f39 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/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: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 16c71f4ec..f4d14b66a 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/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 diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index d4978ec3c..956accf23 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/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) diff --git a/src/events/SDL_clipboardevents_c.h b/src/events/SDL_clipboardevents_c.h index 265eb3893..c07804213 100644 --- a/src/events/SDL_clipboardevents_c.h +++ b/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_ */ diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 865a9b063..b228738f4 100644 --- a/src/events/SDL_events.c +++ b/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) diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 0e7595e4a..ffb721009 100644 --- a/src/test/SDL_test_common.c +++ b/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: diff --git a/src/video/SDL_clipboard.c b/src/video/SDL_clipboard.c index d6809e1b4..8858f4b45 100644 --- a/src/video/SDL_clipboard.c +++ b/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"); } - if (_this->SetClipboardData) { - return _this->SetClipboardData(_this, callback, mime_count, mime_types, userdata); - } else { - return SDL_Unsupported(); + /* 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) { + 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; } diff --git a/src/video/SDL_clipboard_c.h b/src/video/SDL_clipboard_c.h new file mode 100644 index 000000000..e741f888d --- /dev/null +++ b/src/video/SDL_clipboard_c.h @@ -0,0 +1,29 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + 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_ */ diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 758c6fbf7..eff11b949 100644 --- a/src/video/SDL_sysvideo.h +++ b/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; diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 04dd087d0..b6fcee353 100644 --- a/src/video/SDL_video.c +++ b/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(); diff --git a/src/video/cocoa/SDL_cocoaclipboard.h b/src/video/cocoa/SDL_cocoaclipboard.h index bad760cf8..8e4b2acd5 100644 --- a/src/video/cocoa/SDL_cocoaclipboard.h +++ b/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_ */ diff --git a/src/video/cocoa/SDL_cocoaclipboard.m b/src/video/cocoa/SDL_cocoaclipboard.m index f2b295fc3..ed7d3bbfa 100644 --- a/src/video/cocoa/SDL_cocoaclipboard.m +++ b/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,62 +149,19 @@ void Cocoa_CheckClipboardUpdate(SDL_CocoaVideoData *data) } } -void *Cocoa_GetClipboardData(SDL_VideoDevice *_this, size_t *len, const char *mime_type) -{ - @autoreleasepool { - NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; - NSData *itemData; - void *data; - *len = 0; - for (NSPasteboardItem *item in [pasteboard pasteboardItems]) { - 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; - } - } - return nil; - } -} - -SDL_bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type) -{ - - SDL_bool result = SDL_FALSE; - @autoreleasepool { - NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; - CFStringRef mimeType = CFStringCreateWithCString(NULL, mime_type, kCFStringEncodingUTF8); - CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL); - CFRelease(mimeType); - if ([pasteboard canReadItemWithDataConformingToTypes: @[(__bridge NSString *)utiType]]) { - result = SDL_TRUE; - } - CFRelease(utiType); - } - return result; - -} - -int Cocoa_SetClipboardData(SDL_VideoDevice *_this, SDL_ClipboardDataCallback callback, size_t mime_count, - const char **mime_types, void *userdata) +int Cocoa_SetClipboardData(SDL_VideoDevice *_this) { @autoreleasepool { NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; NSPasteboardItem *newItem = [NSPasteboardItem new]; NSMutableArray *utiTypes = [NSMutableArray new]; - Cocoa_PasteboardDataProvider *provider = [[Cocoa_PasteboardDataProvider alloc] initWith: callback userData: userdata]; + 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 < mime_count; i++) { - CFStringRef mimeType = CFStringCreateWithCString(NULL, mime_types[i], kCFStringEncodingUTF8); + 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); @@ -226,4 +183,50 @@ int Cocoa_SetClipboardData(SDL_VideoDevice *_this, SDL_ClipboardDataCallback cal 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) { + NSUInteger length = [itemData length]; + *size = (size_t)length; + data = SDL_malloc(*size); + if (data) { + [itemData getBytes: data length: length]; + } else { + SDL_OutOfMemory(); + } + break; + } + } + return data; + } +} + +SDL_bool Cocoa_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type) +{ + SDL_bool result = SDL_FALSE; + @autoreleasepool { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + CFStringRef mimeType = CFStringCreateWithCString(NULL, mime_type, kCFStringEncodingUTF8); + CFStringRef utiType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL); + CFRelease(mimeType); + if ([pasteboard canReadItemWithDataConformingToTypes: @[(__bridge NSString *)utiType]]) { + result = SDL_TRUE; + } + CFRelease(utiType); + } + return result; + +} + #endif /* SDL_VIDEO_DRIVER_COCOA */ diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m index 167e73b18..4ca146eb5 100644 --- a/src/video/cocoa/SDL_cocoavideo.m +++ b/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; diff --git a/src/video/wayland/SDL_waylandclipboard.c b/src/video/wayland/SDL_waylandclipboard.c index bc3dc102d..a9a7e0a24 100644 --- a/src/video/wayland/SDL_waylandclipboard.c +++ b/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; + const void *data = NULL; + *length = 0; - if (userdata == NULL) { - return data; - } - - 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 */ diff --git a/src/video/wayland/SDL_waylandclipboard.h b/src/video/wayland/SDL_waylandclipboard.h index 9876aef32..b46c384f1 100644 --- a/src/video/wayland/SDL_waylandclipboard.h +++ b/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_ */ diff --git a/src/video/wayland/SDL_waylanddatamanager.c b/src/video/wayland/SDL_waylanddatamanager.c index c2d14141d..40f84f821 100644 --- a/src/video/wayland/SDL_waylanddatamanager.c +++ b/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; diff --git a/src/video/wayland/SDL_waylanddatamanager.h b/src/video/wayland/SDL_waylanddatamanager.h index 5a4a31307..58ba0f6b3 100644 --- a/src/video/wayland/SDL_waylanddatamanager.h +++ b/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, diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 06e81c5b6..5c1e34983 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/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); diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 5e74d4f5e..b9dba35b3 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/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; diff --git a/src/video/x11/SDL_x11clipboard.c b/src/video/x11/SDL_x11clipboard.c index 41098409f..c4e1fb445 100644 --- a/src/video/x11/SDL_x11clipboard.c +++ b/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); } } diff --git a/src/video/x11/SDL_x11clipboard.h b/src/video/x11/SDL_x11clipboard.h index 1143bc8c3..f5b47a984 100644 --- a/src/video/x11/SDL_x11clipboard.h +++ b/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); diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 212ea4117..f391992b7 100644 --- a/src/video/x11/SDL_x11events.c +++ b/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; } diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c index b3dd6fef4..b2185e535 100644 --- a/src/video/x11/SDL_x11video.c +++ b/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; diff --git a/test/testautomation_clipboard.c b/test/testautomation_clipboard.c index f5f71b308..2c31664e3 100644 --- a/test/testautomation_clipboard.c +++ b/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; }