SDL: Add support for X11 primary selection (#6132)

From ac5b9bc4ee560e6280044803aee5f651d2e39048 Mon Sep 17 00:00:00 2001
From: DS <[EMAIL REDACTED]>
Date: Wed, 14 Sep 2022 18:28:35 +0200
Subject: [PATCH] Add support for X11 primary selection (#6132)

X11 has a so-called primary selection, which you can use by marking text and middle-clicking elsewhere to copy the marked text.

There are 3 new API functions in `SDL_clipboard.h`, which work exactly like their clipboard equivalents.

## Test Instructions

* Run the tests (just a copy of the clipboard tests): `$ ./test/testautomation --filter Clipboard`
* Build and run this small application:
<details>
```C
#include <SDL.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void print_error(const char *where)
{
	const char *errstr = SDL_GetError();
	if (errstr == NULL || errstr[0] == '\0')
		return;
	fprintf(stderr, "SDL Error after '%s': %s\n", where, errstr);
	SDL_ClearError();
}

int main()
{
	char text_buf[256];

	srand(time(NULL));

	SDL_Init(SDL_INIT_VIDEO);
	print_error("SDL_INIT()");
	SDL_Window *window = SDL_CreateWindow("Primary Selection Test", SDL_WINDOWPOS_UNDEFINED,
			SDL_WINDOWPOS_UNDEFINED, 400, 400, SDL_WINDOW_SHOWN);
	print_error("SDL_CreateWindow()");
	SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
	print_error("SDL_CreateRenderer()");

	bool quit = false;
	unsigned int do_render = 0;
	while (!quit) {
		SDL_Event event;
		while (SDL_PollEvent(&event)) {
			print_error("SDL_PollEvent()");
			switch (event.type) {
			case SDL_QUIT: {
				quit = true;
				break;
			} case SDL_KEYDOWN: {
				switch (event.key.keysym.sym) {
				case SDLK_ESCAPE:
				case SDLK_q:
					quit = true;
					break;
				case SDLK_c:
					snprintf(text_buf, sizeof(text_buf), "foo%d", rand());
					SDL_SetClipboardText(text_buf);
					print_error("SDL_SetClipboardText()");
					printf("clipboard: set_to=\"%s\"\n", text_buf);
					break;
				case SDLK_v: {
					printf("clipboard: has=%d, ", SDL_HasClipboardText());
					print_error("SDL_HasClipboardText()");
					char *text = SDL_GetClipboardText();
					print_error("SDL_GetClipboardText()");
					printf("text=\"%s\"\n", text);
					SDL_free(text);
					break;
				} case SDLK_d:
					snprintf(text_buf, sizeof(text_buf), "bar%d", rand());
					SDL_SetPrimarySelectionText(text_buf);
					print_error("SDL_SetPrimarySelectionText()");
					printf("primselec: set_to=\"%s\"\n", text_buf);
					break;
				case SDLK_f: {
					printf("primselec: has=%d, ", SDL_HasPrimarySelectionText());
					print_error("SDL_HasPrimarySelectionText()");
					char *text = SDL_GetPrimarySelectionText();
					print_error("SDL_GetPrimarySelectionText()");
					printf("text=\"%s\"\n", text);
					SDL_free(text);
					break;
				} default:
					break;
				}
				break;
			} default: {
				break;
			}}
		}
		// create less noise with WAYLAND_DEBUG=1
		if (do_render == 0) {
			SDL_RenderPresent(renderer);
			print_error("SDL_RenderPresent()");
		}
		do_render += 1;
		usleep(12000);
	}

	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	SDL_Quit();
	print_error("quit");
	return 0;
}
  • Use c,v,d,f to get and set the clipboard and primary selection.
  • Mark text and middle-click also in other applications.
  • For wayland under x:
    • $ mutter --wayland --no-x11 --nested
    • $ XDG_SESSION_TYPE=wayland SDL_VIDEODRIVER=wayland ./<path_to_test_appl_binary>

include/SDL_clipboard.h | 46 +++
include/SDL_events.h | 2 ±
src/dynapi/SDL2.exports | 3 +
src/dynapi/SDL_dynapi_overrides.h | 3 +
src/dynapi/SDL_dynapi_procs.h | 3 +
src/video/SDL_clipboard.c | 64 ++++
src/video/SDL_sysvideo.h | 8 ±
src/video/wayland/SDL_waylandclipboard.c | 121 ++++±
src/video/wayland/SDL_waylandclipboard.h | 3 +
src/video/wayland/SDL_waylanddatamanager.c | 343 ++++++++++++++±–
src/video/wayland/SDL_waylanddatamanager.h | 63 ++±
src/video/wayland/SDL_waylandevents.c | 154 ++++++±
src/video/wayland/SDL_waylandevents_c.h | 2 +
src/video/wayland/SDL_waylandvideo.c | 13 ±
src/video/wayland/SDL_waylandvideo.h | 1 +
src/video/x11/SDL_x11clipboard.c | 99 ++±-
src/video/x11/SDL_x11clipboard.h | 5 ±
src/video/x11/SDL_x11events.c | 12 ±
src/video/x11/SDL_x11video.c | 3 +
test/testautomation_clipboard.c | 163 +++++++±
…/primary-selection-unstable-v1.xml | 225 ++++++++++++
21 files changed, 1207 insertions(+), 129 deletions(-)
create mode 100644 wayland-protocols/primary-selection-unstable-v1.xml

diff --git a/include/SDL_clipboard.h b/include/SDL_clipboard.h
index 935136305a8d…fbe57719f886 100644
— a/include/SDL_clipboard.h
+++ b/include/SDL_clipboard.h
@@ -82,6 +82,52 @@ extern DECLSPEC char * SDLCALL SDL_GetClipboardText(void);
*/
extern DECLSPEC SDL_bool SDLCALL SDL_HasClipboardText(void);

+/**

    • Put UTF-8 text into the primary selection.
    • \param text the text to store in the primary selection
    • \returns 0 on success or a negative error code on failure; call
    •      SDL_GetError() for more information.
      
    • \since This function is available since SDL 2.25.0.
    • \sa SDL_GetPrimarySelectionText
    • \sa SDL_HasPrimarySelectionText
  • */
    +extern DECLSPEC int SDLCALL SDL_SetPrimarySelectionText(const char *text);

+/**

    • Get UTF-8 text from the primary selection, which must be freed with SDL_free().
    • This functions returns empty string if there was not enough memory left for
    • a copy of the primary selection’s content.
    • \returns the primary selection text on success or an empty string on failure;
    •      call SDL_GetError() for more information. Caller must call SDL_free()
      
    •      on the returned pointer when done with it (even if there was an
      
    •      error).
      
    • \since This function is available since SDL 2.25.0.
    • \sa SDL_HasPrimarySelectionText
    • \sa SDL_SetPrimarySelectionText
  • */
    +extern DECLSPEC char * SDLCALL SDL_GetPrimarySelectionText(void);

+/**

    • Query whether the primary selection exists and contains a non-empty text
    • string.
    • \returns SDL_TRUE if the primary selection has text, or SDL_FALSE if it does
    •      not.
      
    • \since This function is available since SDL 2.25.0.
    • \sa SDL_GetPrimarySelectionText
    • \sa SDL_SetPrimarySelectionText
  • */
    +extern DECLSPEC SDL_bool SDLCALL SDL_HasPrimarySelectionText(void);

/* Ends C function definitions when using C++ */
#ifdef __cplusplus
diff --git a/include/SDL_events.h b/include/SDL_events.h
index c0fc9bb1aa34…1836fc06c378 100644
— a/include/SDL_events.h
+++ b/include/SDL_events.h
@@ -143,7 +143,7 @@ typedef enum
SDL_MULTIGESTURE,

 /* Clipboard events */
  • SDL_CLIPBOARDUPDATE = 0x900, /**< The clipboard changed */
  • SDL_CLIPBOARDUPDATE = 0x900, /**< The clipboard or primary selection changed */

    /* Drag and drop events */
    SDL_DROPFILE = 0x1000, /**< The system requests a file open */
    diff --git a/src/dynapi/SDL2.exports b/src/dynapi/SDL2.exports
    index d25b8f721e5c…d9e3545c3d35 100644
    — a/src/dynapi/SDL2.exports
    +++ b/src/dynapi/SDL2.exports
    @@ -860,3 +860,6 @@
    ++’_SDL_crc16’.‘SDL2.dll’.‘SDL_crc16’
    ++’_SDL_GetWindowSizeInPixels’.‘SDL2.dll’.‘SDL_GetWindowSizeInPixels’
    ++’_SDL_GetJoystickGUIDInfo’.‘SDL2.dll’.‘SDL_GetJoystickGUIDInfo’
    +++’_SDL_SetPrimarySelectionText’.‘SDL2.dll’.‘SDL_SetPrimarySelectionText’
    +++’_SDL_GetPrimarySelectionText’.‘SDL2.dll’.‘SDL_GetPrimarySelectionText’
    +++’_SDL_HasPrimarySelectionText’.‘SDL2.dll’.‘SDL_HasPrimarySelectionText’
    diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
    index 4b68f1ab496c…2e982b81b123 100644
    — a/src/dynapi/SDL_dynapi_overrides.h
    +++ b/src/dynapi/SDL_dynapi_overrides.h
    @@ -886,3 +886,6 @@
    #define SDL_crc16 SDL_crc16_REAL
    #define SDL_GetWindowSizeInPixels SDL_GetWindowSizeInPixels_REAL
    #define SDL_GetJoystickGUIDInfo SDL_GetJoystickGUIDInfo_REAL
    +#define SDL_SetPrimarySelectionText SDL_SetPrimarySelectionText_REAL
    +#define SDL_GetPrimarySelectionText SDL_GetPrimarySelectionText_REAL
    +#define SDL_HasPrimarySelectionText SDL_HasPrimarySelectionText_REAL
    diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
    index b8ecadbcfbf2…b40effffc2f3 100644
    — a/src/dynapi/SDL_dynapi_procs.h
    +++ b/src/dynapi/SDL_dynapi_procs.h
    @@ -969,3 +969,6 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_ResetHint,(const char *a),(a),return)
    SDL_DYNAPI_PROC(Uint16,SDL_crc16,(Uint16 a, const void *b, size_t c),(a,b,c),return)
    SDL_DYNAPI_PROC(void,SDL_GetWindowSizeInPixels,(SDL_Window *a, int *b, int *c),(a,b,c),)
    SDL_DYNAPI_PROC(void,SDL_GetJoystickGUIDInfo,(SDL_JoystickGUID a, Uint16 *b, Uint16 *c, Uint16 *d, Uint16 *e),(a,b,c,d,e),)
    +SDL_DYNAPI_PROC(int,SDL_SetPrimarySelectionText,(const char a),(a),return)
    +SDL_DYNAPI_PROC(char
    ,SDL_GetPrimarySelectionText,(void),(),return)
    +SDL_DYNAPI_PROC(SDL_bool,SDL_HasPrimarySelectionText,(void),(),return)
    diff --git a/src/video/SDL_clipboard.c b/src/video/SDL_clipboard.c
    index c3669af6ec1c…bee570dea8f2 100644
    — a/src/video/SDL_clipboard.c
    +++ b/src/video/SDL_clipboard.c
    @@ -45,6 +45,27 @@ SDL_SetClipboardText(const char *text)
    }
    }

+int
+SDL_SetPrimarySelectionText(const char *text)
+{

  • SDL_VideoDevice *_this = SDL_GetVideoDevice();
  • if (!_this) {
  •    return SDL_SetError("Video subsystem must be initialized to set primary selection text");
    
  • }
  • if (!text) {
  •    text = "";
    
  • }
  • if (_this->SetPrimarySelectionText) {
  •    return _this->SetPrimarySelectionText(_this, text);
    
  • } else {
  •    SDL_free(_this->primary_selection_text);
    
  •    _this->primary_selection_text = SDL_strdup(text);
    
  •    return 0;
    
  • }
    +}

char *
SDL_GetClipboardText(void)
{
@@ -66,6 +87,27 @@ SDL_GetClipboardText(void)
}
}

+char *
+SDL_GetPrimarySelectionText(void)
+{

  • SDL_VideoDevice *_this = SDL_GetVideoDevice();
  • if (!_this) {
  •    SDL_SetError("Video subsystem must be initialized to get primary selection text");
    
  •    return SDL_strdup("");
    
  • }
  • if (_this->GetPrimarySelectionText) {
  •    return _this->GetPrimarySelectionText(_this);
    
  • } else {
  •    const char *text = _this->primary_selection_text;
    
  •    if (!text) {
    
  •        text = "";
    
  •    }
    
  •    return SDL_strdup(text);
    
  • }
    +}

SDL_bool
SDL_HasClipboardText(void)
{
@@ -87,4 +129,26 @@ SDL_HasClipboardText(void)
}
}

+SDL_bool
+SDL_HasPrimarySelectionText(void)
+{

  • SDL_VideoDevice *_this = SDL_GetVideoDevice();
  • if (!_this) {
  •    SDL_SetError("Video subsystem must be initialized to check primary selection text");
    
  •    return SDL_FALSE;
    
  • }
  • 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;
    
  •    }
    
  • }
  • return SDL_FALSE;
    +}

/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index d3f0d83e2a2a…4f602d305a6f 100644
— a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -328,6 +328,9 @@ struct SDL_VideoDevice
int (*SetClipboardText) (_THIS, const char *text);
char * (*GetClipboardText) (_THIS);
SDL_bool (*HasClipboardText) (_THIS);

  • int (*SetPrimarySelectionText) (_THIS, const char *text);

  • char * (*GetPrimarySelectionText) (_THIS);

  • SDL_bool (*HasPrimarySelectionText) (_THIS);

    /* MessageBox */
    int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);
    @@ -353,6 +356,7 @@ struct SDL_VideoDevice
    Uint8 window_magic;
    Uint32 next_object_id;
    char *clipboard_text;

  • char *primary_selection_text;
    SDL_bool setting_display_mode;
    Uint32 quirk_flags;

@@ -422,11 +426,11 @@ struct SDL_VideoDevice
/* Data private to this driver */
void *driverdata;
struct SDL_GLDriverData *gl_data;

#if SDL_VIDEO_OPENGL_EGL
struct SDL_EGL_VideoData *egl_data;
#endif

#if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
struct SDL_PrivateGLESData *gles_data;
#endif
diff --git a/src/video/wayland/SDL_waylandclipboard.c b/src/video/wayland/SDL_waylandclipboard.c
index ede016c79bd9…ff19639f7e3e 100644
— a/src/video/wayland/SDL_waylandclipboard.c
+++ b/src/video/wayland/SDL_waylandclipboard.c
@@ -58,6 +58,39 @@ Wayland_SetClipboardText(_THIS, const char *text)
return status;
}

+int
+Wayland_SetPrimarySelectionText(_THIS, const char *text)
+{

  • SDL_VideoData *video_data = NULL;
  • SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
  • int status = 0;
  • if (_this == NULL || _this->driverdata == NULL) {
  •    status = SDL_SetError("Video driver uninitialized");
    
  • } else {
  •    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;
    
  •        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));
    
  •            status = Wayland_primary_selection_device_set_selection(primary_selection_device,
    
  •                                                                    source);
    
  •            if (status != 0) {
    
  •                Wayland_primary_selection_source_destroy(source);
    
  •            }
    
  •        } else {
    
  •            status = Wayland_primary_selection_device_clear_selection(primary_selection_device);
    
  •        }
    
  •    }
    
  • }
  • return status;
    +}

char *
Wayland_GetClipboardText(_THIS)
{
@@ -65,8 +98,6 @@ Wayland_GetClipboardText(_THIS)
SDL_WaylandDataDevice *data_device = NULL;

 char *text = NULL;
  • void *buffer = NULL;
    size_t length = 0;

    if (_this == NULL || _this->driverdata == NULL) {
    @@ -75,19 +106,50 @@ Wayland_GetClipboardText(_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_offer != NULL) {
    
  •            buffer = Wayland_data_offer_receive(data_device->selection_offer,
    
  •        /* 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 (length > 0) {
    
  •                text = (char*) buffer;
    
  •            }
    
  •        } 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);
           }
    
  •        if (length == 0 && data_device->selection_source != NULL) {
    
  •            buffer = Wayland_data_source_get_data(data_device->selection_source,
    
  •                                                  &length, TEXT_MIME, SDL_TRUE);
    
  •            if (length > 0) {
    
  •                text = (char*) buffer;
    
  •            }
    
  •    }
    
  • }
  • if (text == NULL) {
  •    text = SDL_strdup("");
    
  • }
  • return text;
    +}

+char *
+Wayland_GetPrimarySelectionText(_THIS)
+{

  • SDL_VideoData *video_data = NULL;
  • SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
  • char *text = NULL;
  • size_t length = 0;
  • if (_this == NULL || _this->driverdata == NULL) {
  •    SDL_SetError("Video driver uninitialized");
    
  • } else {
  •    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);
    
  •        } 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);
           }
       }
    
    }
    @@ -112,13 +174,32 @@ Wayland_HasClipboardText(_THIS)
    video_data = _this->driverdata;
    if (video_data->input != NULL && video_data->input->data_device != NULL) {
    data_device = video_data->input->data_device;
  •        if (Wayland_data_offer_has_mime(
    
  •                data_device->selection_offer, TEXT_MIME)) {
    
  •            result = SDL_TRUE;
    
  •        } else if (Wayland_data_source_has_mime(
    
  •                data_device->selection_source, TEXT_MIME)) {
    
  •            result = SDL_TRUE;
    
  •        }
    
  •        result = result ||
    
  •                 Wayland_data_source_has_mime(data_device->selection_source, TEXT_MIME) ||
    
  •                 Wayland_data_offer_has_mime(data_device->selection_offer, TEXT_MIME);
    
  •    }
    
  • }
  • return result;
    +}

+SDL_bool
+Wayland_HasPrimarySelectionText(_THIS)
+{

  • SDL_VideoData *video_data = NULL;
  • SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
  • SDL_bool result = SDL_FALSE;
  • if (_this == NULL || _this->driverdata == NULL) {
  •    SDL_SetError("Video driver uninitialized");
    
  • } else {
  •    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;
    
  •        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);
       }
    
    }
    return result;
    diff --git a/src/video/wayland/SDL_waylandclipboard.h b/src/video/wayland/SDL_waylandclipboard.h
    index b30e1605b9da…b0e443d955f4 100644
    — a/src/video/wayland/SDL_waylandclipboard.h
    +++ b/src/video/wayland/SDL_waylandclipboard.h
    @@ -26,6 +26,9 @@
    extern int Wayland_SetClipboardText(_THIS, const char *text);
    extern char *Wayland_GetClipboardText(_THIS);
    extern SDL_bool Wayland_HasClipboardText(_THIS);
    +extern int Wayland_SetPrimarySelectionText(_THIS, const char *text);
    +extern char *Wayland_GetPrimarySelectionText(_THIS);
    +extern SDL_bool Wayland_HasPrimarySelectionText(_THIS);

#endif /* SDL_waylandclipboard_h_ */

diff --git a/src/video/wayland/SDL_waylanddatamanager.c b/src/video/wayland/SDL_waylanddatamanager.c
index e61ba457fd19…63e9d489ce17 100644
— a/src/video/wayland/SDL_waylanddatamanager.c
+++ b/src/video/wayland/SDL_waylanddatamanager.c
@@ -33,11 +33,12 @@

#include “SDL_waylandvideo.h”
#include “SDL_waylanddatamanager.h”
+#include “primary-selection-unstable-v1-client-protocol.h”

/* FIXME: This is arbitrary, but we want this to be less than a frame because

  • any longer can potentially spin an infinite loop of PumpEvents (!)
    */
    -#define PIPE_MS_TIMEOUT 10
    +#define PIPE_MS_TIMEOUT 14

static ssize_t
write_pipe(int fd, const void* buffer, size_t total_length, size_t pos)
@@ -53,7 +54,7 @@ write_pipe(int fd, const void
buffer, size_t total_length, size_t *pos)
ready = SDL_IOReady(fd, SDL_IOR_WRITE, PIPE_MS_TIMEOUT);

 sigemptyset(&sig_set);
  • sigaddset(&sig_set, SIGPIPE);
  • sigaddset(&sig_set, SIGPIPE);

#if SDL_THREADS_DISABLED
sigprocmask(SIG_BLOCK, &sig_set, &old_sig_set);
@@ -97,7 +98,7 @@ read_pipe(int fd, void** buffer, size_t* total_length, SDL_bool null_terminate)
size_t pos = 0;

 ready = SDL_IOReady(fd, SDL_IOR_READ, PIPE_MS_TIMEOUT);
  • if (ready == 0) {
    bytes_read = SDL_SetError(“Pipe timeout”);
    } else if (ready < 0) {
    @@ -120,8 +121,8 @@ read_pipe(int fd, void** buffer, size_t* total_length, SDL_bool null_terminate)
    output_buffer = SDL_malloc(new_buffer_length);
    } else {
    output_buffer = SDL_realloc(*buffer, new_buffer_length);
  •    }           
    
  •    }
    
  •    if (output_buffer == NULL) {
           bytes_read = SDL_OutOfMemory();
       } else {
    

@@ -130,7 +131,7 @@ read_pipe(int fd, void** buffer, size_t* total_length, SDL_bool null_terminate)
if (null_terminate == SDL_TRUE) {
SDL_memset((Uint8*)output_buffer + (new_buffer_length - 1), 0, 1);
}

  •        *buffer = output_buffer;
       }
    
    }
    @@ -160,28 +161,28 @@ Wayland_convert_mime_type(const char *mime_type)
    break;
    }
    }
  • return found;
    }

static SDL_MimeDataList*
-mime_data_list_find(struct wl_list* list,
+mime_data_list_find(struct wl_list* list,
const char* mime_type)
{
SDL_MimeDataList *found = NULL;

 SDL_MimeDataList *mime_list = NULL;
  • wl_list_for_each(mime_list, list, link) {
  • wl_list_for_each(mime_list, list, link) {
    if (SDL_strcmp(mime_list->mime_type, mime_type) == 0) {
    found = mime_list;
    break;
    }
  • }
  • }
    return found;
    }

static int
-mime_data_list_add(struct wl_list* list,
+mime_data_list_add(struct wl_list* list,
const char* mime_type,
const void* buffer, size_t length)
{
@@ -216,7 +217,7 @@ mime_data_list_add(struct wl_list* list,
}
}
}

  • if (mime_data != NULL && buffer != NULL && length > 0) {
    if (mime_data->data != NULL) {
    SDL_free(mime_data->data);
    @@ -233,31 +234,25 @@ mime_data_list_add(struct wl_list* list,
    static void
    mime_data_list_free(struct wl_list *list)
    {
  • SDL_MimeDataList *mime_data = NULL;
  • SDL_MimeDataList *mime_data = NULL;
    SDL_MimeDataList *next = NULL;

    wl_list_for_each_safe(mime_data, next, list, link) {
    if (mime_data->data != NULL) {
    SDL_free(mime_data->data);

  •    }        
    
  •    }
       if (mime_data->mime_type != NULL) {
           SDL_free(mime_data->mime_type);
       }
    
  •    SDL_free(mime_data);       
    
  • }
  •    SDL_free(mime_data);
    
  • }
    }

-ssize_t
-Wayland_data_source_send(SDL_WaylandDataSource *source,

  •                     const char *mime_type, int fd)
    

+static ssize_t
+Wayland_source_send(SDL_MimeDataList *mime_data, const char *mime_type, int fd)
{
size_t written_bytes = 0;
ssize_t status = 0;

  • SDL_MimeDataList *mime_data = NULL;

  • mime_type = Wayland_convert_mime_type(mime_type);

  • mime_data = mime_data_list_find(&source->mimes,

  •                                                  mime_type);
    

    if (mime_data == NULL || mime_data->data == NULL) {
    status = SDL_SetError(“Invalid mime type”);
    @@ -271,15 +266,49 @@ Wayland_data_source_send(SDL_WaylandDataSource *source,
    return status;
    }

+ssize_t
+Wayland_data_source_send(SDL_WaylandDataSource *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);
    
  • return Wayland_source_send(mime_data, mime_type, 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);
    
  • 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) 
    
  •                             size_t length)
    

+{

  • return mime_data_list_add(&source->mimes, mime_type, buffer, length);
    +}

+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);
}

-SDL_bool
+SDL_bool
Wayland_data_source_has_mime(SDL_WaylandDataSource *source,
const char *mime_type)
{
@@ -291,7 +320,47 @@ Wayland_data_source_has_mime(SDL_WaylandDataSource *source,
return found;
}

-void*
+SDL_bool
+Wayland_primary_selection_source_has_mime(SDL_WaylandPrimarySelectionSource *source,

  •                                      const char *mime_type)
    

+{

  • SDL_bool found = SDL_FALSE;
  • if (source != NULL) {
  •    found = mime_data_list_find(&source->mimes, mime_type) != NULL;
    
  • }
  • return found;
    +}

+static void*
+Wayland_source_get_data(SDL_MimeDataList *mime_data,

  •                    size_t *length,
    
  •                    SDL_bool null_terminate)
    

+{

  • void *buffer = NULL;
  • if (mime_data != NULL && mime_data->length > 0) {
  •    size_t buffer_length = mime_data->length;
    
  •    if (null_terminate == SDL_TRUE) {
    
  •        ++buffer_length;
    
  •    }
    
  •    buffer = SDL_malloc(buffer_length);
    
  •    if (buffer == NULL) {
    
  •        *length = SDL_OutOfMemory();
    
  •    } else {
    
  •        *length = mime_data->length;
    
  •        SDL_memcpy(buffer, mime_data->data, mime_data->length);
    
  •        if (null_terminate) {
    
  •            *((Uint8 *)buffer + mime_data->length) = 0;
    
  •        }
    
  •    }
    
  • }
  • return buffer;
    +}

+void*
Wayland_data_source_get_data(SDL_WaylandDataSource *source,
size_t length, const char mime_type,
SDL_bool null_terminate)
@@ -304,23 +373,26 @@ Wayland_data_source_get_data(SDL_WaylandDataSource *source,
SDL_SetError(“Invalid data source”);
} else {
mime_data = mime_data_list_find(&source->mimes, mime_type);

  •    if (mime_data != NULL && mime_data->length > 0) {
    
  •        size_t buffer_length = mime_data->length;
    
  •    buffer = Wayland_source_get_data(mime_data, length, null_terminate);
    
  • }
  •        if (null_terminate == SDL_TRUE) {
    
  •            ++buffer_length;
    
  •        }
    
  •        buffer = SDL_malloc(buffer_length);
    
  •        if (buffer == NULL) {
    
  •            *length = SDL_OutOfMemory();
    
  •        } else {
    
  •            *length = mime_data->length;
    
  •            SDL_memcpy(buffer, mime_data->data, mime_data->length);
    
  •            if (null_terminate) {
    
  •                *((Uint8 *)buffer + mime_data->length) = 0;
    
  •            }
    
  •        }
    
  •    }
    
  • return buffer;
    +}

+void*
+Wayland_primary_selection_source_get_data(SDL_WaylandPrimarySelectionSource *source,

  •                                      size_t *length, const char* mime_type,
    
  •                                      SDL_bool null_terminate)
    

+{

  • SDL_MimeDataList *mime_data = NULL;

  • void *buffer = NULL;

  • *length = 0;

  • if (source == NULL) {

  •    SDL_SetError("Invalid primary selection source");
    
  • } else {

  •    mime_data = mime_data_list_find(&source->mimes, mime_type);
    
  •    buffer = Wayland_source_get_data(mime_data, length, null_terminate);
    

    }

    return buffer;
    @@ -340,13 +412,27 @@ Wayland_data_source_destroy(SDL_WaylandDataSource *source)
    }
    }

-void*
+void
+Wayland_primary_selection_source_destroy(SDL_WaylandPrimarySelectionSource *source)
+{

  • if (source != NULL) {
  •    SDL_WaylandPrimarySelectionDevice *primary_selection_device = (SDL_WaylandPrimarySelectionDevice *) source->primary_selection_device;
    
  •    if (primary_selection_device && (primary_selection_device->selection_source == source)) {
    
  •        primary_selection_device->selection_source = NULL;
    
  •    }
    
  •    zwp_primary_selection_source_v1_destroy(source->source);
    
  •    mime_data_list_free(&source->mimes);
    
  •    SDL_free(source);
    
  • }
    +}

+void*
Wayland_data_offer_receive(SDL_WaylandDataOffer *offer,
size_t length, const char mime_type,
SDL_bool null_terminate)
{
SDL_WaylandDataDevice *data_device = NULL;

  • int pipefd[2];
    void *buffer = NULL;
    *length = 0;
    @@ -364,22 +450,59 @@ Wayland_data_offer_receive(SDL_WaylandDataOffer *offer,
    WAYLAND_wl_display_flush(data_device->video_data->display);

       close(pipefd[1]);
    
  •    while (read_pipe(pipefd[0], &buffer, length, null_terminate) > 0);
       close(pipefd[0]);
    
    }
    return buffer;
    }

-int
+void*
+Wayland_primary_selection_offer_receive(SDL_WaylandPrimarySelectionOffer *offer,

  •                                    size_t *length, const char* mime_type,
    
  •                                    SDL_bool null_terminate)
    

+{

  • SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
  • int pipefd[2];
  • void *buffer = NULL;
  • *length = 0;
  • if (offer == NULL) {
  •    SDL_SetError("Invalid data offer");
    
  • } else if ((primary_selection_device = offer->primary_selection_device) == NULL) {
  •    SDL_SetError("Primary selection device not initialized");
    
  • } else if (pipe2(pipefd, O_CLOEXEC|O_NONBLOCK) == -1) {
  •    SDL_SetError("Could not read pipe");
    
  • } else {
  •    zwp_primary_selection_offer_v1_receive(offer->offer, mime_type, pipefd[1]);
    
  •    /* TODO: Needs pump and flush? */
    
  •    WAYLAND_wl_display_flush(primary_selection_device->video_data->display);
    
  •    close(pipefd[1]);
    
  •    while (read_pipe(pipefd[0], &buffer, length, null_terminate) > 0);
    
  •    close(pipefd[0]);
    
  • }
  • return buffer;
    +}

+int
Wayland_data_offer_add_mime(SDL_WaylandDataOffer offer,
const char
mime_type)
{
return mime_data_list_add(&offer->mimes, mime_type, NULL, 0);
}

+int
+Wayland_primary_selection_offer_add_mime(SDL_WaylandPrimarySelectionOffer *offer,

  •                                     const char* mime_type)
    

+{

  • return mime_data_list_add(&offer->mimes, mime_type, NULL, 0);
    +}

-SDL_bool
+SDL_bool
Wayland_data_of

(Patch may be truncated, please check the link at the top of this post.)