From 35876da3c468db13834a80c1baaa0b34dd9d2536 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
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 | 180 +++++++++++++++++----
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 | 97 +++++------
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, 549 insertions(+), 312 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 6d898f98eaa9..fd9d84557ae6 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/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>
\ No newline at end of file
+</Project>
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index 769c45215ba8..81dee5cc3d56 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/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>
\ No newline at end of file
+</Project>
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj
index 3a503634fb13..c6df98236a64 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj
+++ b/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" />
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters
index 26bb708b761c..871a43e1ea13 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj.filters
+++ b/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>
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index a111aaeb3359..8b8f6d6a79cf 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/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" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index cb03064bfe91..63fd04c06953 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/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>
diff --git a/include/SDL3/SDL_clipboard.h b/include/SDL3/SDL_clipboard.h
index 4e223a57ac9f..ee9d7a821082 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 cf80b5d5b784..55ce261daa02 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 2ec47ef1184a..10a0a2f39119 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 16c71f4ec1ee..f4d14b66a186 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 d4978ec3ca1c..956accf23d67 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 265eb38935c7..c078042133c3 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 865a9b063ae0..b228738f414d 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 0e7595e4a81a..ffb721009f43 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 d6809e1b4f4c..8858f4b45531 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");
}
+ /* 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;
}
diff --git a/src/video/SDL_clipboard_c.h b/src/video/SDL_clipboard_c.h
new file mode 100644
index 000000000000..e741f888d170
--- /dev/null
+++ b/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
(Patch may be truncated, please check the link at the top of this post.)