From 19adfa3ad90f31bbc5420b0af3fea98aea4bc000 Mon Sep 17 00:00:00 2001
From: Linus Probert <[EMAIL REDACTED]>
Date: Fri, 21 Apr 2023 20:07:24 +0200
Subject: [PATCH] wayland: Add support for images in clipboard.
Re-writes the clipboard data handling in wayland to an on demand
solution where callbacks are provided to generate/provide the clipboard
data when requested by the OS.
---
include/SDL3/SDL_clipboard.h | 90 ++++++++
include/SDL3/SDL_events.h | 14 ++
src/dynapi/SDL_dynapi.sym | 4 +
src/dynapi/SDL_dynapi_overrides.h | 4 +
src/dynapi/SDL_dynapi_procs.h | 4 +
src/events/SDL_clipboardevents.c | 19 +-
src/events/SDL_clipboardevents_c.h | 2 +
src/video/SDL_clipboard.c | 63 ++++++
src/video/SDL_sysvideo.h | 5 +
src/video/wayland/SDL_waylandclipboard.c | 153 ++++++++++++--
src/video/wayland/SDL_waylandclipboard.h | 5 +
src/video/wayland/SDL_waylanddatamanager.c | 234 ++++++++-------------
src/video/wayland/SDL_waylanddatamanager.h | 42 ++--
src/video/wayland/SDL_waylandevents.c | 10 +-
src/video/wayland/SDL_waylandvideo.c | 4 +
test/testautomation_clipboard.c | 67 +++++-
16 files changed, 529 insertions(+), 191 deletions(-)
diff --git a/include/SDL3/SDL_clipboard.h b/include/SDL3/SDL_clipboard.h
index 06b3ac84eced..a17a24fb7f80 100644
--- a/include/SDL3/SDL_clipboard.h
+++ b/include/SDL3/SDL_clipboard.h
@@ -129,6 +129,96 @@ extern DECLSPEC char * SDLCALL SDL_GetPrimarySelectionText(void);
*/
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
+ * \param userdata A pointer to provided user 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
+ * less undefined behavior and may cause breakage in receiving applications.
+ * The returned data will not be freed so it needs to be retained and dealt
+ * with internally.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_SetClipboardData
+ */
+typedef void *(SDLCALL *SDL_ClipboardDataCallback)(size_t *size, const char *mime_type, void *userdata);
+
+/**
+ * \brief Offer clipboard data to the OS
+ *
+ * Tell the operating system that the application is offering clipboard data
+ * for each of the proivded mime-types. Once another application requests the
+ * 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 mime_types A list of mime-types that are being offered
+ * \param userdata An opaque pointer that will be forwarded to the callback
+ * \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);
+
+/**
+ * Retrieve previously set userdata if any.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \returns a pointer to the data or NULL if no data exists
+ */
+extern DECLSPEC void *SDLCALL SDL_GetClipboardUserdata(void);
+
+/**
+ * Get the data from clipboard for a given mime type
+ *
+ * \param[out] length A pointer to hold the buffer length
+ * \param mime_type The mime type to read from the clipboard
+ * \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.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_SetClipboardData
+ */
+extern DECLSPEC void *SDLCALL SDL_GetClipboardData(size_t *length, const char *mime_type);
+
+/**
+ * Query whether there is data in the clipboard for the provided mime type
+ *
+ * \param mime_type The mime type to check for data for
+ *
+ * \returns SDL_TRUE if there exists data in clipboard for the provided mime
+ * type, SDL_FALSE if it does not.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_SetClipboardData
+ * \sa SDL_GetClipboardData
+ */
+extern DECLSPEC SDL_bool SDLCALL SDL_HasClipboardData(const char *mime_type);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h
index a28cced2d451..a92a2fe4b831 100644
--- a/include/SDL3/SDL_events.h
+++ b/include/SDL3/SDL_events.h
@@ -172,6 +172,7 @@ typedef enum
/* Clipboard events */
SDL_EVENT_CLIPBOARD_UPDATE = 0x900, /**< The clipboard or primary selection changed */
+ SDL_EVENT_CLIPBOARD_CANCELLED, /**< The clipboard or primary selection cancelled */
/* Drag and drop events */
SDL_EVENT_DROP_FILE = 0x1000, /**< The system requests a file open */
@@ -530,6 +531,18 @@ typedef struct SDL_DropEvent
float y; /**< Y coordinate, relative to window (not on begin) */
} SDL_DropEvent;
+/**
+ * \brief An event triggered when the applications active clipboard content is cancelled by new clipboard content
+ * \note Primary use for this event is to free any userdata you may have provided when setting the clipboard data.
+ *
+ * \sa SDL_SetClipboardData
+ */
+typedef struct SDL_ClipboardCancelled
+{
+ Uint32 type; /**< ::SDL_EVENT_CLIPBOARD_CANCELLED or ::SDL_EVENT_CLIPBOARD_UPDATE */
+ 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;
/**
* \brief Sensor event structure (event.sensor.*)
@@ -624,6 +637,7 @@ typedef union SDL_Event
SDL_SysWMEvent syswm; /**< System dependent window event data */
SDL_TouchFingerEvent tfinger; /**< Touch finger event data */
SDL_DropEvent drop; /**< Drag and drop event data */
+ SDL_ClipboardEvent clipboard; /**< Clipboard cancelled event data */
/* This is necessary for ABI compatibility between Visual C++ and GCC.
Visual C++ will respect the push pack pragma and use 52 bytes (size of
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 3927b26234ac..78075cd635eb 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -842,6 +842,10 @@ SDL3_0.0.0 {
SDL_CreatePopupWindow;
SDL_GetWindowParent;
SDL_CreateWindowWithPosition;
+ SDL_SetClipboardData;
+ SDL_GetClipboardUserdata;
+ SDL_GetClipboardData;
+ SDL_HasClipboardData;
SDL_GetAudioStreamFormat;
SDL_SetAudioStreamFormat;
SDL_CreateRWLock;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index d96acd6150a2..c5f65c31f93a 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -868,6 +868,10 @@
#define SDL_CreatePopupWindow SDL_CreatePopupWindow_REAL
#define SDL_GetWindowParent SDL_GetWindowParent_REAL
#define SDL_CreateWindowWithPosition SDL_CreateWindowWithPosition_REAL
+#define SDL_SetClipboardData SDL_SetClipboardData_REAL
+#define SDL_GetClipboardUserdata SDL_GetClipboardUserdata_REAL
+#define SDL_GetClipboardData SDL_GetClipboardData_REAL
+#define SDL_HasClipboardData SDL_HasClipboardData_REAL
#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL
#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL
#define SDL_CreateRWLock SDL_CreateRWLock_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 9ebfecd4e541..5bfb06aadd55 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -913,6 +913,10 @@ SDL_DYNAPI_PROC(SDL_SystemTheme,SDL_GetSystemTheme,(void),(),return)
SDL_DYNAPI_PROC(SDL_Window*,SDL_CreatePopupWindow,(SDL_Window *a, int b, int c, int d, int e, Uint32 f),(a,b,c,d,e,f),return)
SDL_DYNAPI_PROC(SDL_Window*,SDL_GetWindowParent,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(SDL_Window*,SDL_CreateWindowWithPosition,(const char *a, int b, int c, int d, int e, Uint32 f),(a,b,c,d,e,f),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(void*,SDL_GetClipboardUserdata,(void),(),return)
+SDL_DYNAPI_PROC(void*,SDL_GetClipboardData,(size_t *a, const char *b),(a,b),return)
+SDL_DYNAPI_PROC(SDL_bool,SDL_HasClipboardData,(const char *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(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_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(SDL_RWLock*,SDL_CreateRWLock,(void),(),return)
diff --git a/src/events/SDL_clipboardevents.c b/src/events/SDL_clipboardevents.c
index 961f4b2f81ec..ae6519d16d2c 100644
--- a/src/events/SDL_clipboardevents.c
+++ b/src/events/SDL_clipboardevents.c
@@ -34,7 +34,24 @@ int SDL_SendClipboardUpdate(void)
if (SDL_EventEnabled(SDL_EVENT_CLIPBOARD_UPDATE)) {
SDL_Event event;
event.type = SDL_EVENT_CLIPBOARD_UPDATE;
- event.common.timestamp = 0;
+ event.clipboard.timestamp = 0;
+ event.clipboard.userdata = NULL;
+ posted = (SDL_PushEvent(&event) > 0);
+ }
+ return posted;
+}
+
+int SDL_SendClipboardCancelled(void *userdata)
+{
+ int posted;
+
+ /* Post the event, if desired */
+ posted = 0;
+ if (SDL_EventEnabled(SDL_EVENT_CLIPBOARD_CANCELLED)) {
+ SDL_Event event;
+ event.type = SDL_EVENT_CLIPBOARD_CANCELLED;
+ event.clipboard.timestamp = 0;
+ event.clipboard.userdata = userdata;
posted = (SDL_PushEvent(&event) > 0);
}
return posted;
diff --git a/src/events/SDL_clipboardevents_c.h b/src/events/SDL_clipboardevents_c.h
index ce74e3cbdb1a..265eb38935c7 100644
--- a/src/events/SDL_clipboardevents_c.h
+++ b/src/events/SDL_clipboardevents_c.h
@@ -25,4 +25,6 @@
extern int SDL_SendClipboardUpdate(void);
+extern int SDL_SendClipboardCancelled(void *userdata);
+
#endif /* SDL_clipboardevents_c_h_ */
diff --git a/src/video/SDL_clipboard.c b/src/video/SDL_clipboard.c
index ae4163290fe0..fdcbf19b3481 100644
--- a/src/video/SDL_clipboard.c
+++ b/src/video/SDL_clipboard.c
@@ -22,6 +22,22 @@
#include "SDL_sysvideo.h"
+int
+SDL_SetClipboardData(SDL_ClipboardDataCallback callback, size_t mime_count, const char **mime_types, void *userdata)
+{
+ SDL_VideoDevice *_this = SDL_GetVideoDevice();
+
+ 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();
+ }
+}
+
int SDL_SetClipboardText(const char *text)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
@@ -62,6 +78,22 @@ int SDL_SetPrimarySelectionText(const char *text)
}
}
+void *
+SDL_GetClipboardData(size_t *length, const char *mime_type)
+{
+ SDL_VideoDevice *_this = SDL_GetVideoDevice();
+ 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 {
+ return NULL;
+ }
+}
+
char *
SDL_GetClipboardText(void)
{
@@ -104,6 +136,21 @@ SDL_GetPrimarySelectionText(void)
}
}
+SDL_bool
+SDL_HasClipboardData(const char *mime_type)
+{
+ SDL_VideoDevice *_this = SDL_GetVideoDevice();
+ if (_this == NULL) {
+ SDL_SetError("Video subsystem must be initialized to check clipboard data");
+ return SDL_FALSE;
+ }
+
+ if (_this->HasClipboardData) {
+ return _this->HasClipboardData(_this, mime_type);
+ }
+ return SDL_FALSE;
+}
+
SDL_bool
SDL_HasClipboardText(void)
{
@@ -145,3 +192,19 @@ SDL_HasPrimarySelectionText(void)
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_sysvideo.h b/src/video/SDL_sysvideo.h
index 2e87dab5749f..3321db9d9b65 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -334,6 +334,11 @@ 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);
+ 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);
diff --git a/src/video/wayland/SDL_waylandclipboard.c b/src/video/wayland/SDL_waylandclipboard.c
index dd9572f01d98..dd962a801e52 100644
--- a/src/video/wayland/SDL_waylandclipboard.c
+++ b/src/video/wayland/SDL_waylandclipboard.c
@@ -22,10 +22,74 @@
#ifdef SDL_VIDEO_DRIVER_WAYLAND
+#include "../../events/SDL_events_c.h"
#include "SDL_waylanddatamanager.h"
#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)
+{
+ SDL_VideoData *video_data = NULL;
+ SDL_WaylandDataDevice *data_device = NULL;
+
+ int status = 0;
+
+ video_data = _this->driverdata;
+ if (video_data->input != NULL && video_data->input->data_device != NULL) {
+ data_device = video_data->input->data_device;
+
+ if (callback && mime_types) {
+ SDL_WaylandDataSource *source = Wayland_data_source_create(_this);
+ Wayland_data_source_set_callback(source, callback, userdata, SDL_FALSE);
+
+ status = Wayland_data_device_set_selection(data_device, source, mime_count, mime_types);
+ if (status != 0) {
+ Wayland_data_source_destroy(source);
+ }
+ } else {
+ status = Wayland_data_device_clear_selection(data_device);
+ }
+ }
+
+ return status;
+}
+
+#define TEXT_MIME_TYPES_LEN 5
+static const char *text_mime_types[TEXT_MIME_TYPES_LEN] = {
+ TEXT_MIME,
+ "text/plain",
+ "TEXT",
+ "UTF8_STRING",
+ "STRING",
+};
+
+static void *Wayland_ClipboardTextCallback(size_t *length, const char *mime_type, void *userdata)
+{
+ void *data = NULL;
+ SDL_bool valid_mime_type = SDL_FALSE;
+ *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 (valid_mime_type) {
+ char *text = userdata;
+ *length = SDL_strlen(text);
+ data = userdata;
+ }
+
+ return data;
+}
+
int Wayland_SetClipboardText(SDL_VideoDevice *_this, const char *text)
{
SDL_VideoData *video_data = NULL;
@@ -39,12 +103,12 @@ int Wayland_SetClipboardText(SDL_VideoDevice *_this, const char *text)
video_data = _this->driverdata;
if (video_data->input != NULL && video_data->input->data_device != NULL) {
data_device = video_data->input->data_device;
+
if (text[0] != '\0') {
SDL_WaylandDataSource *source = Wayland_data_source_create(_this);
- Wayland_data_source_add_data(source, TEXT_MIME, text,
- SDL_strlen(text));
+ Wayland_data_source_set_callback(source, Wayland_ClipboardTextCallback, SDL_strdup(text), SDL_TRUE);
- status = Wayland_data_device_set_selection(data_device, source);
+ status = Wayland_data_device_set_selection(data_device, source, TEXT_MIME_TYPES_LEN, text_mime_types);
if (status != 0) {
Wayland_data_source_destroy(source);
}
@@ -72,11 +136,12 @@ int Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
primary_selection_device = video_data->input->primary_selection_device;
if (text[0] != '\0') {
SDL_WaylandPrimarySelectionSource *source = Wayland_primary_selection_source_create(_this);
- Wayland_primary_selection_source_add_data(source, TEXT_MIME, text,
- SDL_strlen(text));
+ Wayland_primary_selection_source_set_callback(source, Wayland_ClipboardTextCallback, SDL_strdup(text));
status = Wayland_primary_selection_device_set_selection(primary_selection_device,
- source);
+ source,
+ TEXT_MIME_TYPES_LEN,
+ text_mime_types);
if (status != 0) {
Wayland_primary_selection_source_destroy(source);
}
@@ -89,6 +154,29 @@ int Wayland_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
return status;
}
+void *Wayland_GetClipboardData(SDL_VideoDevice *_this, size_t *length, const char *mime_type)
+{
+ SDL_VideoData *video_data = NULL;
+ SDL_WaylandDataDevice *data_device = NULL;
+
+ void *buffer = 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) {
+ 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);
+ }
+ }
+
+ return buffer;
+}
+
char *Wayland_GetClipboardText(SDL_VideoDevice *_this)
{
SDL_VideoData *video_data = NULL;
@@ -103,13 +191,10 @@ 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;
- /* Prefer own selection, if not canceled */
- if (Wayland_data_source_has_mime(
- data_device->selection_source, TEXT_MIME)) {
- text = Wayland_data_source_get_data(data_device->selection_source,
- &length, TEXT_MIME, SDL_TRUE);
+ if (data_device->selection_source != NULL) {
+ text = Wayland_data_source_get_data(data_device->selection_source, &length, TEXT_MIME, SDL_TRUE);
} else if (Wayland_data_offer_has_mime(
- data_device->selection_offer, TEXT_MIME)) {
+ data_device->selection_offer, TEXT_MIME)) {
text = Wayland_data_offer_receive(data_device->selection_offer,
&length, TEXT_MIME, SDL_TRUE);
}
@@ -137,13 +222,10 @@ char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this)
video_data = _this->driverdata;
if (video_data->input != NULL && video_data->input->primary_selection_device != NULL) {
primary_selection_device = video_data->input->primary_selection_device;
- /* Prefer own selection, if not canceled */
- if (Wayland_primary_selection_source_has_mime(
- primary_selection_device->selection_source, TEXT_MIME)) {
- text = Wayland_primary_selection_source_get_data(primary_selection_device->selection_source,
- &length, TEXT_MIME, SDL_TRUE);
+ if (primary_selection_device->selection_source != NULL) {
+ text = Wayland_primary_selection_source_get_data(primary_selection_device->selection_source, &length, TEXT_MIME, SDL_TRUE);
} else if (Wayland_primary_selection_offer_has_mime(
- primary_selection_device->selection_offer, TEXT_MIME)) {
+ primary_selection_device->selection_offer, TEXT_MIME)) {
text = Wayland_primary_selection_offer_receive(primary_selection_device->selection_offer,
&length, TEXT_MIME, SDL_TRUE);
}
@@ -157,7 +239,7 @@ char *Wayland_GetPrimarySelectionText(SDL_VideoDevice *_this)
return text;
}
-SDL_bool Wayland_HasClipboardText(SDL_VideoDevice *_this)
+static SDL_bool HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
{
SDL_VideoData *video_data = NULL;
SDL_WaylandDataDevice *data_device = NULL;
@@ -170,13 +252,22 @@ SDL_bool Wayland_HasClipboardText(SDL_VideoDevice *_this)
if (video_data->input != NULL && video_data->input->data_device != NULL) {
data_device = video_data->input->data_device;
result = result ||
- Wayland_data_source_has_mime(data_device->selection_source, TEXT_MIME) ||
- Wayland_data_offer_has_mime(data_device->selection_offer, TEXT_MIME);
+ Wayland_data_offer_has_mime(data_device->selection_offer, mime_type);
}
}
return result;
}
+SDL_bool Wayland_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
+{
+ return HasClipboardData(_this, mime_type);
+}
+
+SDL_bool Wayland_HasClipboardText(SDL_VideoDevice *_this)
+{
+ return HasClipboardData(_this, TEXT_MIME);
+}
+
SDL_bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this)
{
SDL_VideoData *video_data = NULL;
@@ -190,8 +281,6 @@ SDL_bool Wayland_HasPrimarySelectionText(SDL_VideoDevice *_this)
if (video_data->input != NULL && video_data->input->primary_selection_device != NULL) {
primary_selection_device = video_data->input->primary_selection_device;
result = result ||
- Wayland_primary_selection_source_has_mime(
- primary_selection_device->selection_source, TEXT_MIME) ||
Wayland_primary_selection_offer_has_mime(
primary_selection_device->selection_offer, TEXT_MIME);
}
@@ -199,4 +288,22 @@ 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 ba79aeedfc8b..9876aef3229f 100644
--- a/src/video/wayland/SDL_waylandclipboard.h
+++ b/src/video/wayland/SDL_waylandclipboard.h
@@ -23,11 +23,16 @@
#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 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);
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 1d9dcc94be2c..47d6a8bf7174 100644
--- a/src/video/wayland/SDL_waylanddatamanager.c
+++ b/src/video/wayland/SDL_waylanddatamanager.c
@@ -29,6 +29,7 @@
#include <signal.h>
#include "../../core/unix/SDL_poll.h"
+#include "../../events/SDL_events_c.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylanddatamanager.h"
@@ -136,31 +137,6 @@ static ssize_t read_pipe(int fd, void **buffer, size_t *total_length, SDL_bool n
return bytes_read;
}
-#define MIME_LIST_SIZE 4
-
-static const char *mime_conversion_list[MIME_LIST_SIZE][2] = {
- { "text/plain", TEXT_MIME },
- { "TEXT", TEXT_MIME },
- { "UTF8_STRING", TEXT_MIME },
- { "STRING", TEXT_MIME }
-};
-
-const char *Wayland_convert_mime_type(const char *mime_type)
-{
- const char *found = mime_type;
-
- size_t index = 0;
-
- for (index = 0; index < MIME_LIST_SIZE; ++index) {
- if (SDL_strcmp(mime_conversion_list[index][0], mime_type) == 0) {
- found = mime_conversion_list[index][1];
- break;
- }
- }
-
- return found;
-}
-
static SDL_MimeDataList *mime_data_list_find(struct wl_list *list,
const char *mime_type)
{
@@ -242,131 +218,112 @@ static void mime_data_list_free(struct wl_list *list)
}
}
-static ssize_t
-Wayland_source_send(SDL_MimeDataList *mime_data, const char *mime_type, int fd)
+static size_t
+Wayland_send_data(void *data, size_t length, int fd)
{
- size_t written_bytes = 0;
- ssize_t status = 0;
+ size_t result = 0;
- if (mime_data == NULL || mime_data->data == NULL) {
- status = SDL_SetError("Invalid mime type");
- close(fd);
- } else {
- while (write_pipe(fd,
- mime_data->data,
- mime_data->length,
- &written_bytes) > 0) {
+ if (length > 0 && data != NULL) {
+ while (write_pipe(fd, data, length, &result) > 0) {
+ /* Just keep spinning */
}
- close(fd);
- status = written_bytes;
}
- return status;
+ close(fd);
+
+ return result;
}
ssize_t
Wayland_data_source_send(SDL_WaylandDataSource *source,
const char *mime_type, int fd)
{
- SDL_MimeDataList *mime_data = NULL;
+ void *data = NULL;
+ size_t length = 0;
- mime_type = Wayland_convert_mime_type(mime_type);
- mime_data = mime_data_list_find(&source->mimes,
- mime_type);
+ if (source->callback) {
+ data = source->callback(&length, mime_type, source->userdata.data);
+ }
- return Wayland_source_send(mime_data, mime_type, fd);
+ return Wayland_send_data(data, length, fd);
}
ssize_t
Wayland_primary_selection_source_send(SDL_WaylandPrimarySelectionSource *source,
const char *mime_type, int fd)
{
- SDL_MimeDataList *mime_data = NULL;
-
- mime_type = Wayland_convert_mime_type(mime_type);
- mime_data = mime_data_list_find(&source->mimes,
- mime_type);
+ void *data = NULL;
+ size_t length = 0;
- return Wayland_source_send(mime_data, mime_type, fd);
-}
-
-int Wayland_data_source_add_data(SDL_WaylandDataSource *source,
- const char *mime_type,
- const void *buffer,
- size_t length)
-{
- return mime_data_list_add(&source->mimes, mime_type, buffer, length);
-}
+ if (source->callback) {
+ data = source->callback(&length, mime_type, source->userdata.data);
+ }
-int Wayland_primary_selection_source_add_data(SDL_WaylandPrimarySelectionSource *source,
- const char *mime_type,
- const void *buffer,
- size_t length)
-{
- return mime_data_list_add(&source->mimes, mime_type, buffer, length);
+ return Wayland_send_data(data, length, fd);
}
-SDL_bool Wayland_data_source_has_mime(SDL_WaylandDataSource *source,
- const char *mime_type)
+void Wayland_data_source_set_callback(SDL_WaylandDataSource *source,
+ SDL_ClipboardDataCallback callback,
+ void *userdata,
+ SDL_bool internal)
{
- SDL_bool found = SDL_FALSE;
-
if (source != NULL) {
- found = mime_data_list_find(&source->mimes, mime_type) != NULL;
+ source->callback = callback;
+ source->userdata.internal = internal;
+ source->userdata.data = userdata;
}
- return found;
}
-SDL_bool Wayland_primary_selection_source_has_mime(SDL_WaylandPrimarySelectionSource *source,
- const char *mime_type)
+int Wayland_primary_selection_source_set_callback(SDL_WaylandPrimarySelectionSource *source,
+ SDL_C
(Patch may be truncated, please check the link at the top of this post.)