diff --git a/.editorconfig b/.editorconfig index 64f189158..b6b6a3673 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,3 +11,4 @@ trim_trailing_whitespace = true [*.xml] indent_style = space indent_size = 2 +tab_width = 8 diff --git a/include/wlr/types/wlr_primary_selection.h b/include/wlr/types/wlr_primary_selection.h new file mode 100644 index 000000000..edbaf81e5 --- /dev/null +++ b/include/wlr/types/wlr_primary_selection.h @@ -0,0 +1,31 @@ +#ifndef WLR_TYPES_WLR_PRIMARY_SELECTION_H +#define WLR_TYPES_WLR_PRIMARY_SELECTION_H + +#include + +struct wlr_primary_selection_device_manager { + struct wl_global *global; + + struct wl_listener display_destroy; + + void *data; +}; + +struct wlr_primary_selection_source { + struct wl_resource *resource; + + struct wl_array mime_types; + + struct { + struct wl_signal destroy; + } events; + + void *data; +}; + +struct wlr_primary_selection_device_manager * + wlr_primary_selection_device_manager_create(struct wl_display *display); +void wlr_primary_selection_device_manager_destroy( + struct wlr_primary_selection_device_manager *manager); + +#endif diff --git a/protocol/gtk-primary-selection.xml b/protocol/gtk-primary-selection.xml new file mode 100644 index 000000000..02cab94fc --- /dev/null +++ b/protocol/gtk-primary-selection.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 source + will transferthat 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. + + + + diff --git a/protocol/meson.build b/protocol/meson.build index ff54815af..f6aca5f56 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -23,6 +23,7 @@ wayland_scanner_client = generator( protocols = [ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], 'gamma-control.xml', + 'gtk-primary-selection.xml', 'screenshooter.xml', 'server-decoration.xml', ] @@ -30,6 +31,7 @@ protocols = [ client_protocols = [ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], 'gamma-control.xml', + 'gtk-primary-selection.xml', 'screenshooter.xml', 'server-decoration.xml', ] diff --git a/types/meson.build b/types/meson.build index 4669165a2..82031e897 100644 --- a/types/meson.build +++ b/types/meson.build @@ -12,6 +12,7 @@ lib_wlr_types = static_library( 'wlr_output.c', 'wlr_output_layout.c', 'wlr_pointer.c', + 'wlr_primary_selection.c', 'wlr_region.c', 'wlr_screenshooter.c', 'wlr_seat.c', diff --git a/types/wlr_primary_selection.c b/types/wlr_primary_selection.c new file mode 100644 index 000000000..7ed4a4024 --- /dev/null +++ b/types/wlr_primary_selection.c @@ -0,0 +1,131 @@ +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include + +static void source_handle_offer(struct wl_client *client, + struct wl_resource *resource, const char *mime_type) { + struct wlr_primary_selection_source *source = + wl_resource_get_user_data(resource); + + char **p = wl_array_add(&source->mime_types, sizeof(*p)); + if (p) { + *p = strdup(mime_type); + } + if (p == NULL || *p == NULL) { + wl_resource_post_no_memory(resource); + } +} + +static void source_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct gtk_primary_selection_source_interface source_impl = { + .offer = source_handle_offer, + .destroy = source_handle_destroy, +}; + +static void source_resource_handle_destroy( struct wl_resource *resource) { + struct wlr_primary_selection_source *source = + wl_resource_get_user_data(resource); + wl_signal_emit(&source->events.destroy, source); + free(source); +} + +static void device_manager_handle_create_source(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id) { + //struct wlr_primary_selection_device_manager *manager = + // wl_resource_get_user_data(manager_resource); + + struct wlr_primary_selection_source *source = + calloc(1, sizeof(struct wlr_primary_selection_source)); + if (source == NULL) { + wl_client_post_no_memory(client); + return; + } + + int version = wl_resource_get_version(manager_resource); + source->resource = wl_resource_create(client, + >k_primary_selection_source_interface, version, id); + if (source->resource == NULL) { + free(source); + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(source->resource, &source_impl, source, + source_resource_handle_destroy); + + wl_signal_init(&source->events.destroy); + wl_array_init(&source->mime_types); + + wlr_log(L_DEBUG, "new gtk_primary_selection_source %p (res %p)", source, + source->resource); +} + +static void device_manager_handle_destroy(struct wl_client *client, + struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct gtk_primary_selection_device_manager_interface +device_manager_impl = { + .create_source = device_manager_handle_create_source, + //.get_device = device_manager_handle_get_device, + .destroy = device_manager_handle_destroy, +}; + +static void primary_selection_device_manager_bind(struct wl_client *client, + void *data, uint32_t version, uint32_t id) { + struct wlr_primary_selection_device_manager *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + >k_primary_selection_device_manager_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &device_manager_impl, manager, + NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_primary_selection_device_manager *manager = + wl_container_of(listener, manager, display_destroy); + wlr_primary_selection_device_manager_destroy(manager); +} + +struct wlr_primary_selection_device_manager * + wlr_primary_selection_device_manager_create( + struct wl_display *display) { + struct wlr_primary_selection_device_manager *manager = + calloc(1, sizeof(struct wlr_primary_selection_device_manager)); + if (manager == NULL) { + return NULL; + } + manager->global = wl_global_create(display, + >k_primary_selection_device_manager_interface, 1, manager, + primary_selection_device_manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} + +void wlr_primary_selection_device_manager_destroy( + struct wlr_primary_selection_device_manager *manager) { + if (manager == NULL) { + return; + } + // TODO: free wl_resources + wl_global_destroy(manager->global); + free(manager); +}