diff --git a/include/SDL_clipboard.h b/include/SDL_clipboard.h index 935136305..fbe57719f 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 c0fc9bb1a..1836fc06c 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 d25b8f721..d9e3545c3 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 4b68f1ab4..2e982b81b 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 b8ecadbcf..b40effffc 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 c3669af6e..bee570dea 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 d3f0d83e2..4f602d305 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 ede016c79..ff19639f7 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 b30e1605b..b0e443d95 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 e61ba457f..63e9d489c 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); } -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 */ diff --git a/src/video/wayland/SDL_waylanddatamanager.h b/src/video/wayland/SDL_waylanddatamanager.h index 0030a3915..3e1aa0349 100644 --- a/src/video/wayland/SDL_waylanddatamanager.h +++ b/src/video/wayland/SDL_waylanddatamanager.h @@ -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: */ diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 5c3ac204a..02c48bcca 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -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 @@ -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); } diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h index 491c38526..d70fdee44 100644 --- a/src/video/wayland/SDL_waylandevents_c.h +++ b/src/video/wayland/SDL_waylandevents_c.h @@ -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); diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 113ae6f6c..97e5efbdd 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -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 @@ -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); diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h index 9b2d6835f..3e7b363c5 100644 --- a/src/video/wayland/SDL_waylandvideo.h +++ b/src/video/wayland/SDL_waylandvideo.h @@ -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; diff --git a/src/video/x11/SDL_x11clipboard.c b/src/video/x11/SDL_x11clipboard.c index 37a17f407..fd198bd20 100644 --- a/src/video/x11/SDL_x11clipboard.c +++ b/src/video/x11/SDL_x11clipboard.c @@ -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: */ diff --git a/src/video/x11/SDL_x11clipboard.h b/src/video/x11/SDL_x11clipboard.h index ec98445c4..ddeb016e8 100644 --- a/src/video/x11/SDL_x11clipboard.h +++ b/src/video/x11/SDL_x11clipboard.h @@ -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); diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index fca0d3855..5c99a65d6 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -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, diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c index 92045caa0..2e5e190bd 100644 --- a/src/video/x11/SDL_x11video.c +++ b/src/video/x11/SDL_x11video.c @@ -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; diff --git a/test/testautomation_clipboard.c b/test/testautomation_clipboard.c index 113c7dc89..76090d1ea 100644 --- a/test/testautomation_clipboard.c +++ b/test/testautomation_clipboard.c @@ -27,6 +27,21 @@ clipboard_testHasClipboardText(void *arg) return TEST_COMPLETED; } +/** + * \brief Check call to SDL_HasPrimarySelectionText + * + * \sa + * http://wiki.libsdl.org/SDL_HasPrimarySelectionText + */ +int +clipboard_testHasPrimarySelectionText(void *arg) +{ + SDL_HasPrimarySelectionText(); + SDLTest_AssertPass("Call to SDL_HasPrimarySelectionText succeeded"); + + return TEST_COMPLETED; +} + /** * \brief Check call to SDL_GetClipboardText * @@ -45,6 +60,24 @@ clipboard_testGetClipboardText(void *arg) return TEST_COMPLETED; } +/** + * \brief Check call to SDL_GetPrimarySelectionText + * + * \sa + * http://wiki.libsdl.org/SDL_GetPrimarySelectionText + */ +int +clipboard_testGetPrimarySelectionText(void *arg) +{ + char *charResult; + charResult = SDL_GetPrimarySelectionText(); + SDLTest_AssertPass("Call to SDL_GetPrimarySelectionText succeeded"); + + SDL_free(charResult); + + return TEST_COMPLETED; +} + /** * \brief Check call to SDL_SetClipboardText * \sa @@ -71,7 +104,36 @@ clipboard_testSetClipboardText(void *arg) SDL_free(textRef); SDL_free(text); - return TEST_COMPLETED; + return TEST_COMPLETED; +} + +/** + * \brief Check call to SDL_SetPrimarySelectionText + * \sa + * http://wiki.libsdl.org/SDL_SetPrimarySelectionText + */ +int +clipboard_testSetPrimarySelectionText(void *arg) +{ + char *textRef = SDLTest_RandomAsciiString(); + char *text = SDL_strdup(textRef); + int result; + result = SDL_SetPrimarySelectionText((const char *)text); + SDLTest_AssertPass("Call to SDL_SetPrimarySelectionText succeeded"); + SDLTest_AssertCheck( + result == 0, + "Validate SDL_SetPrimarySelectionText result, expected 0, got %i", + result); + SDLTest_AssertCheck( + SDL_strcmp(textRef, text) == 0, + "Verify SDL_SetPrimarySelectionText did not modify input string, expected '%s', got '%s'", + textRef, text); + + /* Cleanup */ + SDL_free(textRef); + SDL_free(text); + + return TEST_COMPLETED; } /** @@ -150,7 +212,86 @@ clipboard_testClipboardTextFunctions(void *arg) SDL_free(text); SDL_free(charResult); - return TEST_COMPLETED; + return TEST_COMPLETED; +} + +/** + * \brief End-to-end test of SDL_xyzPrimarySelectionText functions + * \sa + * http://wiki.libsdl.org/SDL_HasPrimarySelectionText + * http://wiki.libsdl.org/SDL_GetPrimarySelectionText + * http://wiki.libsdl.org/SDL_SetPrimarySelectionText + */ +int +clipboard_testPrimarySelectionTextFunctions(void *arg) +{ + char *textRef = SDLTest_RandomAsciiString(); + char *text = SDL_strdup(textRef); + SDL_bool boolResult; + int intResult; + char *charResult; + + /* Clear primary selection text state */ + boolResult = SDL_HasPrimarySelectionText(); + SDLTest_AssertPass("Call to SDL_HasPrimarySelectionText succeeded"); + if (boolResult == SDL_TRUE) { + intResult = SDL_SetPrimarySelectionText((const char *)NULL); + SDLTest_AssertPass("Call to SDL_SetPrimarySelectionText(NULL) succeeded"); + SDLTest_AssertCheck( + intResult == 0, + "Verify result from SDL_SetPrimarySelectionText(NULL), expected 0, got %i", + intResult); + charResult = SDL_GetPrimarySelectionText(); + SDLTest_AssertPass("Call to SDL_GetPrimarySelectionText succeeded"); + SDL_free(charResult); + boolResult = SDL_HasPrimarySelectionText(); + SDLTest_AssertPass("Call to SDL_HasPrimarySelectionText succeeded"); + SDLTest_AssertCheck( + boolResult == SDL_FALSE, + "Verify SDL_HasPrimarySelectionText returned SDL_FALSE, got %s", + (boolResult) ? "SDL_TRUE" : "SDL_FALSE"); + } + + /* Empty primary selection */ + charResult = SDL_GetPrimarySelectionText(); + SDLTest_AssertPass("Call to SDL_GetPrimarySelectionText succeeded"); + SDLTest_AssertCheck( + charResult != NULL, + "Verify SDL_GetPrimarySelectionText did not return NULL"); + SDLTest_AssertCheck( + charResult[0] == '\0', + "Verify SDL_GetPrimarySelectionText returned string with length 0, got length %i", + (int) SDL_strlen(charResult)); + intResult = SDL_SetPrimarySelectionText((const char *)text); + SDLTest_AssertPass("Call to SDL_SetPrimarySelectionText succeeded"); + SDLTest_AssertCheck( + intResult == 0, + "Verify result from SDL_SetPrimarySelectionText(NULL), expected 0, got %i", + intResult); + SDLTest_AssertCheck( + SDL_strcmp(textRef, text) == 0, + "Verify SDL_SetPrimarySelectionText did not modify input string, expected '%s', got '%s'", + textRef, text); + boolResult = SDL_HasPrimarySelectionText(); + SDLTest_AssertPass("Call to SDL_HasPrimarySelectionText succeeded"); + SDLTest_AssertCheck( + boolResult == SDL_TRUE, + "Verify SDL_HasPrimarySelectionText returned SDL_TRUE, got %s", + (boolResult) ? "SDL_TRUE" : "SDL_FALSE"); + SDL_free(charResult); + charResult = SDL_GetPrimarySelectionText(); + SDLTest_AssertPass("Call to SDL_GetPrimarySelectionText succeeded"); + SDLTest_AssertCheck( + SDL_strcmp(textRef, charResult) == 0, + "Verify SDL_GetPrimarySelectionText returned correct string, expected '%s', got '%s'", + textRef, charResult); + + /* Cleanup */ + SDL_free(textRef); + SDL_free(text); + SDL_free(charResult); + + return TEST_COMPLETED; } @@ -161,17 +302,29 @@ static const SDLTest_TestCaseReference clipboardTest1 = { (SDLTest_TestCaseFp)clipboard_testHasClipboardText, "clipboard_testHasClipboardText", "Check call to SDL_HasClipboardText", TEST_ENABLED }; static const SDLTest_TestCaseReference clipboardTest2 = - { (SDLTest_TestCaseFp)clipboard_testGetClipboardText, "clipboard_testGetClipboardText", "Check call to SDL_GetClipboardText", TEST_ENABLED }; + { (SDLTest_TestCaseFp)clipboard_testHasPrimarySelectionText, "clipboard_testHasPrimarySelectionText", "Check call to SDL_HasPrimarySelectionText", TEST_ENABLED }; static const SDLTest_TestCaseReference clipboardTest3 = - { (SDLTest_TestCaseFp)clipboard_testSetClipboardText, "clipboard_testSetClipboardText", "Check call to SDL_SetClipboardText", TEST_ENABLED }; + { (SDLTest_TestCaseFp)clipboard_testGetClipboardText, "clipboard_testGetClipboardText", "Check call to SDL_GetClipboardText", TEST_ENABLED }; static const SDLTest_TestCaseReference clipboardTest4 = + { (SDLTest_TestCaseFp)clipboard_testGetPrimarySelectionText, "clipboard_testGetPrimarySelectionText", "Check call to SDL_GetPrimarySelectionText", TEST_ENABLED }; + +static const SDLTest_TestCaseReference clipboardTest5 = + { (SDLTest_TestCaseFp)clipboard_testSetClipboardText, "clipboard_testSetClipboardText", "Check call to SDL_SetClipboardText", TEST_ENABLED }; + +static const SDLTest_TestCaseReference clipboardTest6 = + { (SDLTest_TestCaseFp)clipboard_testSetPrimarySelectionText, "clipboard_testSetPrimarySelectionText", "Check call to SDL_SetPrimarySelectionText", TEST_ENABLED }; + +static const SDLTest_TestCaseReference clipboardTest7 = { (SDLTest_TestCaseFp)clipboard_testClipboardTextFunctions, "clipboard_testClipboardTextFunctions", "End-to-end test of SDL_xyzClipboardText functions", TEST_ENABLED }; +static const SDLTest_TestCaseReference clipboardTest8 = + { (SDLTest_TestCaseFp)clipboard_testPrimarySelectionTextFunctions, "clipboard_testPrimarySelectionTextFunctions", "End-to-end test of SDL_xyzPrimarySelectionText functions", TEST_ENABLED }; + /* Sequence of Clipboard test cases */ static const SDLTest_TestCaseReference *clipboardTests[] = { - &clipboardTest1, &clipboardTest2, &clipboardTest3, &clipboardTest4, NULL + &clipboardTest1, &clipboardTest2, &clipboardTest3, &clipboardTest4, &clipboardTest5, &clipboardTest6, &clipboardTest7, &clipboardTest8, NULL }; /* Clipboard test suite (global) */ diff --git a/wayland-protocols/primary-selection-unstable-v1.xml b/wayland-protocols/primary-selection-unstable-v1.xml new file mode 100644 index 000000000..e5a39e34c --- /dev/null +++ b/wayland-protocols/primary-selection-unstable-v1.xml @@ -0,0 +1,225 @@ + + + + Copyright © 2015, 2016 Red Hat + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol provides the ability to have a primary selection device to + match that of the X server. This primary selection is a shortcut to the + common clipboard selection, where text just needs to be selected in order + to allow copying it elsewhere. The de facto way to perform this action + is the middle mouse button, although it is not limited to this one. + + Clients wishing to honor primary selection should create a primary + selection source and set it as the selection through + wp_primary_selection_device.set_selection whenever the text selection + changes. In order to minimize calls in pointer-driven text selection, + it should happen only once after the operation finished. Similarly, + a NULL source should be set when text is unselected. + + wp_primary_selection_offer objects are first announced through the + wp_primary_selection_device.data_offer event. Immediately after this event, + the primary data offer will emit wp_primary_selection_offer.offer events + to let know of the mime types being offered. + + When the primary selection changes, the client with the keyboard focus + will receive wp_primary_selection_device.selection events. Only the client + with the keyboard focus will receive such events with a non-NULL + wp_primary_selection_offer. Across keyboard focus changes, previously + focused clients will receive wp_primary_selection_device.events with a + NULL wp_primary_selection_offer. + + In order to request the primary selection data, the client must pass + a recent serial pertaining to the press event that is triggering the + operation, if the compositor deems the serial valid and recent, the + wp_primary_selection_source.send event will happen in the other end + to let the transfer begin. The client owning the primary selection + should write the requested data, and close the file descriptor + immediately. + + If the primary selection owner client disappeared during the transfer, + the client reading the data will receive a + wp_primary_selection_device.selection event with a NULL + wp_primary_selection_offer, the client should take this as a hint + to finish the reads related to the no longer existing offer. + + The primary selection owner should be checking for errors during + writes, merely cancelling the ongoing transfer if any happened. + + + + + The primary selection device manager is a singleton global object that + provides access to the primary selection. It allows to create + wp_primary_selection_source objects, as well as retrieving the per-seat + wp_primary_selection_device objects. + + + + + Create a new primary selection source. + + + + + + + Create a new data device for a given seat. + + + + + + + + Destroy the primary selection device manager. + + + + + + + + Replaces the current selection. The previous owner of the primary + selection will receive a wp_primary_selection_source.cancelled event. + + To unset the selection, set the source to NULL. + + + + + + + + Introduces a new wp_primary_selection_offer object that may be used + to receive the current primary selection. Immediately following this + event, the new wp_primary_selection_offer object will send + wp_primary_selection_offer.offer events to describe the offered mime + types. + + + + + + + The wp_primary_selection_device.selection event is sent to notify the + client of a new primary selection. This event is sent after the + wp_primary_selection.data_offer event introducing this object, and after + the offer has announced its mimetypes through + wp_primary_selection_offer.offer. + + The data_offer is valid until a new offer or NULL is received + or until the client loses keyboard focus. The client must destroy the + previous selection data_offer, if any, upon receiving this event. + + + + + + + Destroy the primary selection device. + + + + + + + A wp_primary_selection_offer represents an offer to transfer the contents + of the primary selection clipboard to the client. Similar to + wl_data_offer, the offer also describes the mime types that the data can + be converted to and provides the mechanisms for transferring the data + directly to the client. + + + + + To transfer the contents of the primary selection clipboard, the client + issues this request and indicates the mime type that it wants to + receive. The transfer happens through the passed file descriptor + (typically created with the pipe system call). The source client writes + the data in the mime type representation requested and then closes the + file descriptor. + + The receiving client reads from the read end of the pipe until EOF and + closes its end, at which point the transfer is complete. + + + + + + + + Destroy the primary selection offer. + + + + + + Sent immediately after creating announcing the + wp_primary_selection_offer through + wp_primary_selection_device.data_offer. One event is sent per offered + mime type. + + + + + + + + The source side of a wp_primary_selection_offer, it provides a way to + describe the offered data and respond to requests to transfer the + requested contents of the primary selection clipboard. + + + + + This request adds a mime type to the set of mime types advertised to + targets. Can be called several times to offer multiple types. + + + + + + + Destroy the primary selection source. + + + + + + Request for the current primary selection contents from the client. + Send the specified mime type over the passed file descriptor, then + close it. + + + + + + + + This primary selection source is no longer valid. The client should + clean up and destroy this primary selection source. + + + +