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;
}
```
</details>

* 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>`
This commit is contained in:
DS
2022-09-14 18:28:35 +02:00
committed by GitHub
parent 72fe6cc8f1
commit ac5b9bc4ee
21 changed files with 1207 additions and 129 deletions

View File

@@ -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'

View File

@@ -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

View File

@@ -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)

View File

@@ -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: */

View File

@@ -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

View File

@@ -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;

View File

@@ -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_ */

View File

@@ -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);
}
SDL_bool
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
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_offer_has_mime(SDL_WaylandDataOffer *offer,
const char *mime_type)
{
@@ -391,6 +514,18 @@ Wayland_data_offer_has_mime(SDL_WaylandDataOffer *offer,
return found;
}
SDL_bool
Wayland_primary_selection_offer_has_mime(SDL_WaylandPrimarySelectionOffer *offer,
const char *mime_type)
{
SDL_bool found = SDL_FALSE;
if (offer != NULL) {
found = mime_data_list_find(&offer->mimes, mime_type) != NULL;
}
return found;
}
void
Wayland_data_offer_destroy(SDL_WaylandDataOffer *offer)
{
@@ -401,6 +536,16 @@ Wayland_data_offer_destroy(SDL_WaylandDataOffer *offer)
}
}
void
Wayland_primary_selection_offer_destroy(SDL_WaylandPrimarySelectionOffer *offer)
{
if (offer != NULL) {
zwp_primary_selection_offer_v1_destroy(offer->offer);
mime_data_list_free(&offer->mimes);
SDL_free(offer);
}
}
int
Wayland_data_device_clear_selection(SDL_WaylandDataDevice *data_device)
{
@@ -416,6 +561,22 @@ Wayland_data_device_clear_selection(SDL_WaylandDataDevice *data_device)
return status;
}
int
Wayland_primary_selection_device_clear_selection(SDL_WaylandPrimarySelectionDevice *primary_selection_device)
{
int status = 0;
if (primary_selection_device == NULL || primary_selection_device->primary_selection_device == NULL) {
status = SDL_SetError("Invalid Primary Selection Device");
} else if (primary_selection_device->selection_source != NULL) {
zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
NULL, 0);
Wayland_primary_selection_source_destroy(primary_selection_device->selection_source);
primary_selection_device->selection_source = NULL;
}
return status;
}
int
Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device,
SDL_WaylandDataSource *source)
@@ -433,7 +594,7 @@ Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device,
wl_list_for_each(mime_data, &(source->mimes), link) {
wl_data_source_offer(source->source,
mime_data->mime_type);
mime_data->mime_type);
/* TODO - Improve system for multiple mime types to same data */
for (index = 0; index < MIME_LIST_SIZE; ++index) {
@@ -443,9 +604,9 @@ Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device,
}
}
/* */
++num_offers;
}
}
if (num_offers == 0) {
Wayland_data_device_clear_selection(data_device);
@@ -455,7 +616,7 @@ Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device,
if (data_device->selection_serial != 0) {
wl_data_device_set_selection(data_device->data_device,
source->source,
data_device->selection_serial);
data_device->selection_serial);
}
if (data_device->selection_source != NULL) {
Wayland_data_source_destroy(data_device->selection_source);
@@ -468,6 +629,58 @@ Wayland_data_device_set_selection(SDL_WaylandDataDevice *data_device,
return status;
}
int
Wayland_primary_selection_device_set_selection(SDL_WaylandPrimarySelectionDevice *primary_selection_device,
SDL_WaylandPrimarySelectionSource *source)
{
int status = 0;
size_t num_offers = 0;
size_t index = 0;
if (primary_selection_device == NULL) {
status = SDL_SetError("Invalid Primary Selection Device");
} else if (source == NULL) {
status = SDL_SetError("Invalid source");
} else {
SDL_MimeDataList *mime_data = NULL;
wl_list_for_each(mime_data, &(source->mimes), link) {
zwp_primary_selection_source_v1_offer(source->source,
mime_data->mime_type);
/* TODO - Improve system for multiple mime types to same data */
for (index = 0; index < MIME_LIST_SIZE; ++index) {
if (SDL_strcmp(mime_conversion_list[index][1], mime_data->mime_type) == 0) {
zwp_primary_selection_source_v1_offer(source->source,
mime_conversion_list[index][0]);
}
}
/* */
++num_offers;
}
if (num_offers == 0) {
Wayland_primary_selection_device_clear_selection(primary_selection_device);
status = SDL_SetError("No mime data");
} else {
/* Only set if there is a valid serial if not set it later */
if (primary_selection_device->selection_serial != 0) {
zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
source->source,
primary_selection_device->selection_serial);
}
if (primary_selection_device->selection_source != NULL) {
Wayland_primary_selection_source_destroy(primary_selection_device->selection_source);
}
primary_selection_device->selection_source = source;
source->primary_selection_device = primary_selection_device;
}
}
return status;
}
int
Wayland_data_device_set_serial(SDL_WaylandDataDevice *data_device,
uint32_t serial)
@@ -481,13 +694,35 @@ Wayland_data_device_set_serial(SDL_WaylandDataDevice *data_device,
&& data_device->selection_source != NULL) {
wl_data_device_set_selection(data_device->data_device,
data_device->selection_source->source,
serial);
data_device->selection_serial);
}
data_device->selection_serial = serial;
}
return status;
return status;
}
int
Wayland_primary_selection_device_set_serial(SDL_WaylandPrimarySelectionDevice *primary_selection_device,
uint32_t serial)
{
int status = -1;
if (primary_selection_device != NULL) {
status = 0;
/* If there was no serial and there is a pending selection set it now. */
if (primary_selection_device->selection_serial == 0
&& primary_selection_device->selection_source != NULL) {
zwp_primary_selection_device_v1_set_selection(primary_selection_device->primary_selection_device,
primary_selection_device->selection_source->source,
primary_selection_device->selection_serial);
}
primary_selection_device->selection_serial = serial;
}
return status;
}
#endif /* SDL_VIDEO_DRIVER_WAYLAND */

View File

@@ -43,12 +43,24 @@ typedef struct {
void *data_device;
} SDL_WaylandDataSource;
typedef struct {
struct zwp_primary_selection_source_v1 *source;
struct wl_list mimes;
void *primary_selection_device;
} SDL_WaylandPrimarySelectionSource;
typedef struct {
struct wl_data_offer *offer;
struct wl_list mimes;
void *data_device;
} SDL_WaylandDataOffer;
typedef struct {
struct zwp_primary_selection_offer_v1 *offer;
struct wl_list mimes;
void *primary_selection_device;
} SDL_WaylandPrimarySelectionOffer;
typedef struct {
struct wl_data_device *data_device;
SDL_VideoData *video_data;
@@ -58,46 +70,83 @@ typedef struct {
SDL_WaylandDataOffer *drag_offer;
SDL_WaylandDataOffer *selection_offer;
/* Clipboard */
/* Clipboard and Primary Selection */
uint32_t selection_serial;
SDL_WaylandDataSource *selection_source;
} SDL_WaylandDataDevice;
typedef struct {
struct zwp_primary_selection_device_v1 *primary_selection_device;
SDL_VideoData *video_data;
uint32_t selection_serial;
SDL_WaylandPrimarySelectionSource *selection_source;
SDL_WaylandPrimarySelectionOffer *selection_offer;
} SDL_WaylandPrimarySelectionDevice;
extern const char* Wayland_convert_mime_type(const char *mime_type);
/* Wayland Data Source - (Sending) */
/* Wayland Data Source / Primary Selection Source - (Sending) */
extern SDL_WaylandDataSource* Wayland_data_source_create(_THIS);
extern ssize_t Wayland_data_source_send(SDL_WaylandDataSource *source,
extern SDL_WaylandPrimarySelectionSource* Wayland_primary_selection_source_create(_THIS);
extern ssize_t Wayland_data_source_send(SDL_WaylandDataSource *source,
const char *mime_type, int fd);
extern ssize_t Wayland_primary_selection_source_send(SDL_WaylandPrimarySelectionSource *source,
const char *mime_type, int fd);
extern int Wayland_data_source_add_data(SDL_WaylandDataSource *source,
const char *mime_type,
const void *buffer,
const char *mime_type,
const void *buffer,
size_t length);
extern int Wayland_primary_selection_source_add_data(SDL_WaylandPrimarySelectionSource *source,
const char *mime_type,
const void *buffer,
size_t length);
extern SDL_bool Wayland_data_source_has_mime(SDL_WaylandDataSource *source,
const char *mime_type);
extern SDL_bool Wayland_primary_selection_source_has_mime(SDL_WaylandPrimarySelectionSource *source,
const char *mime_type);
extern void* Wayland_data_source_get_data(SDL_WaylandDataSource *source,
size_t *length,
const char *mime_type,
SDL_bool null_terminate);
extern void* Wayland_primary_selection_source_get_data(SDL_WaylandPrimarySelectionSource *source,
size_t *length,
const char *mime_type,
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 Offer - (Receiving) */
/* Wayland Data / Primary Selection Offer - (Receiving) */
extern void* Wayland_data_offer_receive(SDL_WaylandDataOffer *offer,
size_t *length,
const char *mime_type,
SDL_bool null_terminate);
extern void* Wayland_primary_selection_offer_receive(SDL_WaylandPrimarySelectionOffer *offer,
size_t *length,
const char *mime_type,
SDL_bool null_terminate);
extern SDL_bool Wayland_data_offer_has_mime(SDL_WaylandDataOffer *offer,
const char *mime_type);
extern SDL_bool Wayland_primary_selection_offer_has_mime(SDL_WaylandPrimarySelectionOffer *offer,
const char *mime_type);
extern int Wayland_data_offer_add_mime(SDL_WaylandDataOffer *offer,
const char *mime_type);
extern int Wayland_primary_selection_offer_add_mime(SDL_WaylandPrimarySelectionOffer *offer,
const char *mime_type);
extern void Wayland_data_offer_destroy(SDL_WaylandDataOffer *offer);
extern void Wayland_primary_selection_offer_destroy(SDL_WaylandPrimarySelectionOffer *offer);
/* Clipboard */
/* Clipboard / Primary Selection */
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);
extern int Wayland_primary_selection_device_set_selection(SDL_WaylandPrimarySelectionDevice *device,
SDL_WaylandPrimarySelectionSource *source);
extern int Wayland_data_device_set_serial(SDL_WaylandDataDevice *device,
uint32_t serial);
extern int Wayland_primary_selection_device_set_serial(SDL_WaylandPrimarySelectionDevice *device,
uint32_t serial);
#endif /* SDL_waylanddatamanager_h_ */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -42,6 +42,7 @@
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "text-input-unstable-v3-client-protocol.h"
#include "tablet-unstable-v2-client-protocol.h"
#include "primary-selection-unstable-v1-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
@@ -587,6 +588,7 @@ pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_t serial,
}
Wayland_data_device_set_serial(input->data_device, serial);
Wayland_primary_selection_device_set_serial(input->primary_selection_device, serial);
SDL_SendMouseButton(window->sdlwindow, 0,
state ? SDL_PRESSED : SDL_RELEASED, sdl_button);
@@ -1106,6 +1108,7 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if (has_text && !(SDL_GetModState() & KMOD_CTRL)) {
Wayland_data_device_set_serial(input->data_device, serial);
Wayland_primary_selection_device_set_serial(input->primary_selection_device, serial);
if (!handled_by_ime) {
SDL_SendKeyboardText(text);
}
@@ -1319,6 +1322,25 @@ static const struct wl_data_source_listener data_source_listener = {
data_source_handle_action, // Version 3
};
static void
primary_selection_source_send(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1,
const char *mime_type, int32_t fd)
{
Wayland_primary_selection_source_send((SDL_WaylandPrimarySelectionSource *)data,
mime_type, fd);
}
static void
primary_selection_source_cancelled(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1)
{
Wayland_primary_selection_source_destroy(data);
}
static const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener = {
primary_selection_source_send,
primary_selection_source_cancelled,
};
SDL_WaylandDataSource*
Wayland_data_source_create(_THIS)
{
@@ -1355,6 +1377,41 @@ Wayland_data_source_create(_THIS)
return data_source;
}
SDL_WaylandPrimarySelectionSource*
Wayland_primary_selection_source_create(_THIS)
{
SDL_WaylandPrimarySelectionSource *primary_selection_source = NULL;
SDL_VideoData *driver_data = NULL;
struct zwp_primary_selection_source_v1 *id = NULL;
if (_this == NULL || _this->driverdata == NULL) {
SDL_SetError("Video driver uninitialized");
} else {
driver_data = _this->driverdata;
if (driver_data->primary_selection_device_manager != NULL) {
id = zwp_primary_selection_device_manager_v1_create_source(
driver_data->primary_selection_device_manager);
}
if (id == NULL) {
SDL_SetError("Wayland unable to create primary selection source");
} else {
primary_selection_source = SDL_calloc(1, sizeof *primary_selection_source);
if (primary_selection_source == NULL) {
SDL_OutOfMemory();
zwp_primary_selection_source_v1_destroy(id);
} else {
WAYLAND_wl_list_init(&(primary_selection_source->mimes));
primary_selection_source->source = id;
zwp_primary_selection_source_v1_add_listener(id, &primary_selection_source_listener,
primary_selection_source);
}
}
}
return primary_selection_source;
}
static void
data_offer_handle_offer(void *data, struct wl_data_offer *wl_data_offer,
const char *mime_type)
@@ -1381,6 +1438,18 @@ static const struct wl_data_offer_listener data_offer_listener = {
data_offer_handle_actions, // Version 3
};
static void
primary_selection_offer_handle_offer(void *data, struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1,
const char *mime_type)
{
SDL_WaylandPrimarySelectionOffer *offer = data;
Wayland_primary_selection_offer_add_mime(offer, mime_type);
}
static const struct zwp_primary_selection_offer_v1_listener primary_selection_offer_listener = {
primary_selection_offer_handle_offer,
};
static void
data_device_handle_data_offer(void *data, struct wl_data_device *wl_data_device,
struct wl_data_offer *id)
@@ -1620,6 +1689,48 @@ static const struct wl_data_device_listener data_device_listener = {
data_device_handle_selection
};
static void
primary_selection_device_handle_offer(void *data, struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1,
struct zwp_primary_selection_offer_v1 *id)
{
SDL_WaylandPrimarySelectionOffer *primary_selection_offer = NULL;
primary_selection_offer = SDL_calloc(1, sizeof *primary_selection_offer);
if (primary_selection_offer == NULL) {
SDL_OutOfMemory();
} else {
primary_selection_offer->offer = id;
primary_selection_offer->primary_selection_device = data;
WAYLAND_wl_list_init(&(primary_selection_offer->mimes));
zwp_primary_selection_offer_v1_set_user_data(id, primary_selection_offer);
zwp_primary_selection_offer_v1_add_listener(id, &primary_selection_offer_listener, primary_selection_offer);
}
}
static void
primary_selection_device_handle_selection(void *data, struct zwp_primary_selection_device_v1 *zwp_primary_selection_device_v1,
struct zwp_primary_selection_offer_v1 *id)
{
SDL_WaylandPrimarySelectionDevice *primary_selection_device = data;
SDL_WaylandPrimarySelectionOffer *offer = NULL;
if (id != NULL) {
offer = zwp_primary_selection_offer_v1_get_user_data(id);
}
if (primary_selection_device->selection_offer != offer) {
Wayland_primary_selection_offer_destroy(primary_selection_device->selection_offer);
primary_selection_device->selection_offer = offer;
}
SDL_SendClipboardUpdate();
}
static const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = {
primary_selection_device_handle_offer,
primary_selection_device_handle_selection
};
static void
text_input_enter(void *data,
struct zwp_text_input_v3 *zwp_text_input_v3,
@@ -1667,9 +1778,9 @@ text_input_preedit_string(void *data,
do {
const int sz = (int)SDL_utf8strlcpy(buf, text+i, sizeof(buf));
const int chars = (int)SDL_utf8strlen(buf);
SDL_SendEditingText(buf, cursor, chars);
i += sz;
cursor += chars;
} while (i < text_bytes);
@@ -1753,6 +1864,32 @@ Wayland_create_data_device(SDL_VideoData *d)
}
}
static void
Wayland_create_primary_selection_device(SDL_VideoData *d)
{
SDL_WaylandPrimarySelectionDevice *primary_selection_device = NULL;
primary_selection_device = SDL_calloc(1, sizeof *primary_selection_device);
if (primary_selection_device == NULL) {
return;
}
primary_selection_device->primary_selection_device = zwp_primary_selection_device_manager_v1_get_device(
d->primary_selection_device_manager, d->input->seat
);
primary_selection_device->video_data = d;
if (primary_selection_device->primary_selection_device == NULL) {
SDL_free(primary_selection_device);
} else {
zwp_primary_selection_device_v1_set_user_data(primary_selection_device->primary_selection_device,
primary_selection_device);
zwp_primary_selection_device_v1_add_listener(primary_selection_device->primary_selection_device,
&primary_selection_device_listener, primary_selection_device);
d->input->primary_selection_device = primary_selection_device;
}
}
static void
Wayland_create_text_input(SDL_VideoData *d)
{
@@ -1787,6 +1924,16 @@ Wayland_add_data_device_manager(SDL_VideoData *d, uint32_t id, uint32_t version)
}
}
void
Wayland_add_primary_selection_device_manager(SDL_VideoData *d, uint32_t id, uint32_t version)
{
d->primary_selection_device_manager = wl_registry_bind(d->registry, id, &zwp_primary_selection_device_manager_v1_interface, 1);
if (d->input != NULL) {
Wayland_create_primary_selection_device(d);
}
}
void
Wayland_add_text_input_manager(SDL_VideoData *d, uint32_t id, uint32_t version)
{
@@ -2173,6 +2320,9 @@ Wayland_display_add_input(SDL_VideoData *d, uint32_t id, uint32_t version)
if (d->data_device_manager != NULL) {
Wayland_create_data_device(d);
}
if (d->primary_selection_device_manager != NULL) {
Wayland_create_primary_selection_device(d);
}
if (d->text_input_manager != NULL) {
Wayland_create_text_input(d);
}

View File

@@ -85,6 +85,7 @@ struct SDL_WaylandInput {
struct wl_touch *touch;
struct wl_keyboard *keyboard;
SDL_WaylandDataDevice *data_device;
SDL_WaylandPrimarySelectionDevice *primary_selection_device;
SDL_WaylandTextInput *text_input;
struct zwp_relative_pointer_v1 *relative_pointer;
SDL_WindowData *pointer_focus;
@@ -137,6 +138,7 @@ extern void Wayland_SendWakeupEvent(_THIS, SDL_Window *window);
extern int Wayland_WaitEventTimeout(_THIS, int timeout);
extern void Wayland_add_data_device_manager(SDL_VideoData *d, uint32_t id, uint32_t version);
extern void Wayland_add_primary_selection_device_manager(SDL_VideoData *d, uint32_t id, uint32_t version);
extern void Wayland_add_text_input_manager(SDL_VideoData *d, uint32_t id, uint32_t version);
extern void Wayland_display_add_input(SDL_VideoData *d, uint32_t id, uint32_t version);

View File

@@ -55,6 +55,8 @@
#include "tablet-unstable-v2-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "viewporter-client-protocol.h"
#include "primary-selection-unstable-v1-client-protocol.h"
#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
@@ -265,6 +267,9 @@ Wayland_CreateDevice(void)
device->SetClipboardText = Wayland_SetClipboardText;
device->GetClipboardText = Wayland_GetClipboardText;
device->HasClipboardText = Wayland_HasClipboardText;
device->SetPrimarySelectionText = Wayland_SetPrimarySelectionText;
device->GetPrimarySelectionText = Wayland_GetPrimarySelectionText;
device->HasPrimarySelectionText = Wayland_HasPrimarySelectionText;
device->StartTextInput = Wayland_StartTextInput;
device->StopTextInput = Wayland_StopTextInput;
device->SetTextInputRect = Wayland_SetTextInputRect;
@@ -868,6 +873,8 @@ display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
Wayland_add_text_input_manager(d, id, version);
} else if (SDL_strcmp(interface, "wl_data_device_manager") == 0) {
Wayland_add_data_device_manager(d, id, version);
} else if (SDL_strcmp(interface, "zwp_primary_selection_device_manager_v1") == 0) {
Wayland_add_primary_selection_device_manager(d, id, version);
} else if (SDL_strcmp(interface, "zxdg_decoration_manager_v1") == 0) {
d->decoration_manager = wl_registry_bind(d->registry, id, &zxdg_decoration_manager_v1_interface, 1);
} else if (SDL_strcmp(interface, "zwp_tablet_manager_v2") == 0) {
@@ -908,7 +915,7 @@ static const struct wl_registry_listener registry_listener = {
display_handle_global,
display_remove_global
};
#ifdef HAVE_LIBDECOR_H
static SDL_bool should_use_libdecor(SDL_VideoData *data, SDL_bool ignore_xdg)
{
@@ -1107,6 +1114,10 @@ Wayland_VideoQuit(_THIS)
wp_viewporter_destroy(data->viewporter);
}
if (data->primary_selection_device_manager) {
zwp_primary_selection_device_manager_v1_destroy(data->primary_selection_device_manager);
}
if (data->compositor)
wl_compositor_destroy(data->compositor);

View File

@@ -68,6 +68,7 @@ typedef struct {
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
struct zwp_pointer_constraints_v1 *pointer_constraints;
struct wl_data_device_manager *data_device_manager;
struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager;
struct zxdg_decoration_manager_v1 *decoration_manager;
struct zwp_keyboard_shortcuts_inhibit_manager_v1 *key_inhibitor_manager;
struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager;

View File

@@ -52,11 +52,11 @@ GetWindow(_THIS)
return data->clipboard_window;
}
/* We use our own cut-buffer for intermediate storage instead of
XA_CUT_BUFFER0 because their use isn't really defined for holding UTF8. */
/* We use our own cut-buffer for intermediate storage instead of
XA_CUT_BUFFER0 because their use isn't really defined for holding UTF8. */
Atom
X11_GetSDLCutBufferClipboardType(Display *display, enum ESDLX11ClipboardMimeType mime_type)
X11_GetSDLCutBufferClipboardType(Display *display, enum ESDLX11ClipboardMimeType mime_type,
Atom selection_type)
{
switch (mime_type) {
case SDL_X11_CLIPBOARD_MIME_TYPE_STRING:
@@ -65,7 +65,9 @@ X11_GetSDLCutBufferClipboardType(Display *display, enum ESDLX11ClipboardMimeType
case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT_PLAIN_UTF8:
#endif
case SDL_X11_CLIPBOARD_MIME_TYPE_TEXT:
return X11_XInternAtom(display, "SDL_CUTBUFFER", False);
return X11_XInternAtom(display, selection_type == XA_PRIMARY ?
"SDL_CUTBUFFER_PRIMARY_SELECTION" : "SDL_CUTBUFFER",
False);
default:
SDL_SetError("Can't find mime_type.");
return XA_STRING;
@@ -118,13 +120,11 @@ X11_GetSDLCutBufferClipboardInternalFormat(Display *display, enum ESDLX11Clipboa
}
}
int
X11_SetClipboardText(_THIS, const char *text)
static int
SetSelectionText(_THIS, const char *text, Atom selection_type)
{
Display *display = ((SDL_VideoData *) _this->driverdata)->display;
Window window;
Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0);
/* Get the SDL window that will own the selection */
window = GetWindow(_this);
@@ -134,22 +134,19 @@ X11_SetClipboardText(_THIS, const char *text)
/* Save the selection on the root window */
X11_XChangeProperty(display, DefaultRootWindow(display),
X11_GetSDLCutBufferClipboardType(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING), X11_GetSDLCutBufferClipboardInternalFormat(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING), 8, PropModeReplace,
X11_GetSDLCutBufferClipboardType(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING, selection_type),
X11_GetSDLCutBufferClipboardInternalFormat(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING), 8, PropModeReplace,
(const unsigned char *)text, SDL_strlen(text));
if (XA_CLIPBOARD != None &&
X11_XGetSelectionOwner(display, XA_CLIPBOARD) != window) {
X11_XSetSelectionOwner(display, XA_CLIPBOARD, window, CurrentTime);
if (X11_XGetSelectionOwner(display, selection_type) != window) {
X11_XSetSelectionOwner(display, selection_type, window, CurrentTime);
}
if (X11_XGetSelectionOwner(display, XA_PRIMARY) != window) {
X11_XSetSelectionOwner(display, XA_PRIMARY, window, CurrentTime);
}
return 0;
}
char *
X11_GetClipboardText(_THIS)
static char *
GetSlectionText(_THIS, Atom selection_type)
{
SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
Display *display = videodata->display;
@@ -165,18 +162,13 @@ X11_GetClipboardText(_THIS)
char *text;
Uint32 waitStart;
Uint32 waitElapsed;
Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0);
if (XA_CLIPBOARD == None) {
SDL_SetError("Couldn't access X clipboard");
return SDL_strdup("");
}
text = NULL;
/* Get the window that holds the selection */
window = GetWindow(_this);
format = X11_GetSDLCutBufferClipboardInternalFormat(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING);
owner = X11_XGetSelectionOwner(display, XA_CLIPBOARD);
owner = X11_XGetSelectionOwner(display, selection_type);
if (owner == None) {
/* Fall back to ancient X10 cut-buffers which do not support UTF8 strings*/
owner = DefaultRootWindow(display);
@@ -184,12 +176,12 @@ X11_GetClipboardText(_THIS)
format = XA_STRING;
} else if (owner == window) {
owner = DefaultRootWindow(display);
selection = X11_GetSDLCutBufferClipboardType(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING);
selection = X11_GetSDLCutBufferClipboardType(display, SDL_X11_CLIPBOARD_MIME_TYPE_STRING, selection_type);
} else {
/* Request that the selection owner copy the data to our window */
owner = window;
selection = X11_XInternAtom(display, "SDL_SELECTION", False);
X11_XConvertSelection(display, XA_CLIPBOARD, format, selection, owner,
X11_XConvertSelection(display, selection_type, format, selection, owner,
CurrentTime);
/* When using synergy on Linux and when data has been put in the clipboard
@@ -200,13 +192,13 @@ X11_GetClipboardText(_THIS)
while (videodata->selection_waiting) {
SDL_PumpEvents();
waitElapsed = SDL_GetTicks() - waitStart;
/* Wait one second for a clipboard response. */
/* Wait one second for a selection response. */
if (waitElapsed > 1000) {
videodata->selection_waiting = SDL_FALSE;
SDL_SetError("Clipboard timeout");
/* We need to set the clipboard text so that next time we won't
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. */
X11_SetClipboardText(_this, "");
SetSelectionText(_this, "", selection_type);
return SDL_strdup("");
}
}
@@ -232,6 +224,41 @@ X11_GetClipboardText(_THIS)
return text;
}
int
X11_SetClipboardText(_THIS, const char *text)
{
SDL_VideoData *videodata = (SDL_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 SetSelectionText(_this, text, XA_CLIPBOARD);
}
int
X11_SetPrimarySelectionText(_THIS, const char *text)
{
return SetSelectionText(_this, text, XA_PRIMARY);
}
char *
X11_GetClipboardText(_THIS)
{
SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
Atom XA_CLIPBOARD = X11_XInternAtom(videodata->display, "CLIPBOARD", 0);
if (XA_CLIPBOARD == None) {
SDL_SetError("Couldn't access X clipboard");
return SDL_strdup("");
}
return GetSlectionText(_this, XA_CLIPBOARD);
}
char *
X11_GetPrimarySelectionText(_THIS)
{
return GetSlectionText(_this, XA_PRIMARY);
}
SDL_bool
X11_HasClipboardText(_THIS)
{
@@ -244,6 +271,18 @@ X11_HasClipboardText(_THIS)
return result;
}
SDL_bool
X11_HasPrimarySelectionText(_THIS)
{
SDL_bool result = SDL_FALSE;
char *text = X11_GetPrimarySelectionText(_this);
if (text) {
result = text[0] != '\0' ? SDL_TRUE : SDL_FALSE;
SDL_free(text);
}
return result;
}
#endif /* SDL_VIDEO_DRIVER_X11 */
/* vi: set ts=4 sw=4 expandtab: */

View File

@@ -36,7 +36,10 @@ enum ESDLX11ClipboardMimeType {
extern int X11_SetClipboardText(_THIS, const char *text);
extern char *X11_GetClipboardText(_THIS);
extern SDL_bool X11_HasClipboardText(_THIS);
extern Atom X11_GetSDLCutBufferClipboardType(Display *display, enum ESDLX11ClipboardMimeType mime_type);
extern int X11_SetPrimarySelectionText(_THIS, const char *text);
extern char *X11_GetPrimarySelectionText(_THIS);
extern SDL_bool X11_HasPrimarySelectionText(_THIS);
extern Atom X11_GetSDLCutBufferClipboardType(Display *display, enum ESDLX11ClipboardMimeType mime_type, Atom selection_type);
extern Atom X11_GetSDLCutBufferClipboardExternalFormat(Display *display, enum ESDLX11ClipboardMimeType mime_type);
extern Atom X11_GetSDLCutBufferClipboardInternalFormat(Display *display, enum ESDLX11ClipboardMimeType mime_type);

View File

@@ -620,7 +620,7 @@ X11_HandleClipboardEvent(_THIS, const XEvent *xevent)
int seln_format, mime_formats;
unsigned long nbytes;
unsigned long overflow;
unsigned char *seln_data;
unsigned char *seln_data;
Atom supportedFormats[SDL_X11_CLIPBOARD_MIME_TYPE_MAX+1];
Atom XA_TARGETS = X11_XInternAtom(display, "TARGETS", 0);
@@ -640,11 +640,11 @@ X11_HandleClipboardEvent(_THIS, const XEvent *xevent)
/* !!! FIXME: We were probably storing this on the root window
because an SDL window might go away...? but we don't have to do
this now (or ever, really). */
if (req->target == XA_TARGETS) {
supportedFormats[0] = XA_TARGETS;
mime_formats = 1;
for (i = 0; i < SDL_X11_CLIPBOARD_MIME_TYPE_MAX; ++i)
for (i = 0; i < SDL_X11_CLIPBOARD_MIME_TYPE_MAX; ++i)
supportedFormats[mime_formats++] = X11_GetSDLCutBufferClipboardExternalFormat(display, i);
X11_XChangeProperty(display, req->requestor, req->property,
XA_ATOM, 32, PropModeReplace,
@@ -657,7 +657,7 @@ X11_HandleClipboardEvent(_THIS, const XEvent *xevent)
if (X11_GetSDLCutBufferClipboardExternalFormat(display, i) != req->target)
continue;
if (X11_XGetWindowProperty(display, DefaultRootWindow(display),
X11_GetSDLCutBufferClipboardType(display, i), 0, INT_MAX/4, False, X11_GetSDLCutBufferClipboardInternalFormat(display, i),
X11_GetSDLCutBufferClipboardType(display, i, req->selection), 0, INT_MAX/4, False, X11_GetSDLCutBufferClipboardInternalFormat(display, i),
&sevent.xselection.target, &seln_format, &nbytes,
&overflow, &seln_data) == Success) {
if (seln_format != None) {
@@ -946,7 +946,7 @@ X11_DispatchEvent(_THIS, XEvent *xevent)
if (xevent->xcrossing.mode != NotifyGrab &&
xevent->xcrossing.mode != NotifyUngrab &&
xevent->xcrossing.detail != NotifyInferior) {
/* In order for interaction with the window decorations and menu to work properly
on Mutter, we need to ungrab the keyboard when the the mouse leaves. */
if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) {
@@ -1158,7 +1158,7 @@ X11_DispatchEvent(_THIS, XEvent *xevent)
&xevent->xconfigure.x, &xevent->xconfigure.y,
&ChildReturn);
}
if (xevent->xconfigure.x != data->last_xconfigure.x ||
xevent->xconfigure.y != data->last_xconfigure.y) {
SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED,

View File

@@ -301,6 +301,9 @@ X11_CreateDevice(void)
device->SetClipboardText = X11_SetClipboardText;
device->GetClipboardText = X11_GetClipboardText;
device->HasClipboardText = X11_HasClipboardText;
device->SetPrimarySelectionText = X11_SetPrimarySelectionText;
device->GetPrimarySelectionText = X11_GetPrimarySelectionText;
device->HasPrimarySelectionText = X11_HasPrimarySelectionText;
device->StartTextInput = X11_StartTextInput;
device->StopTextInput = X11_StopTextInput;
device->SetTextInputRect = X11_SetTextInputRect;