From 99d879c8878f774aac66e39d7bf4b92f3ae7f0ed Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 7 Dec 2018 15:50:51 +0100 Subject: [PATCH 1/8] data-control-v1: initial protocol implementation --- include/wlr/types/meson.build | 3 +- include/wlr/types/wlr_data_control_v1.h | 47 +++ protocol/meson.build | 1 + protocol/wlr-data-control-unstable-v1.xml | 220 ++++++++++++ rootston/desktop.c | 11 +- types/meson.build | 1 + types/wlr_data_control_v1.c | 413 ++++++++++++++++++++++ 7 files changed, 691 insertions(+), 5 deletions(-) create mode 100644 include/wlr/types/wlr_data_control_v1.h create mode 100644 protocol/wlr-data-control-unstable-v1.xml create mode 100644 types/wlr_data_control_v1.c diff --git a/include/wlr/types/meson.build b/include/wlr/types/meson.build index 752c0deae..5e05c5e5f 100644 --- a/include/wlr/types/meson.build +++ b/include/wlr/types/meson.build @@ -3,6 +3,7 @@ install_headers( 'wlr_buffer.h', 'wlr_compositor.h', 'wlr_cursor.h', + 'wlr_data_control_v1.h', 'wlr_data_device.h', 'wlr_export_dmabuf_v1.h', 'wlr_foreign_toplevel_management_v1.h', @@ -22,8 +23,8 @@ install_headers( 'wlr_output_damage.h', 'wlr_output_layout.h', 'wlr_output.h', - 'wlr_pointer.h', 'wlr_pointer_constraints_v1.h', + 'wlr_pointer.h', 'wlr_presentation_time.h', 'wlr_primary_selection.h', 'wlr_region.h', diff --git a/include/wlr/types/wlr_data_control_v1.h b/include/wlr/types/wlr_data_control_v1.h new file mode 100644 index 000000000..69d7281be --- /dev/null +++ b/include/wlr/types/wlr_data_control_v1.h @@ -0,0 +1,47 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_DATA_CONTROL_V1_H +#define WLR_TYPES_WLR_DATA_CONTROL_V1_H + +#include +#include + +struct wlr_data_control_manager_v1 { + struct wl_global *global; + struct wl_list resources; // wl_resource_get_link + struct wl_list controls; // wlr_data_control_v1::link + + struct { + struct wl_signal destroy; + struct wl_signal new_control; // wlr_data_control_v1 + } events; + + struct wl_listener display_destroy; +}; + +struct wlr_data_control_v1 { + struct wl_resource *resource; + struct wlr_data_control_manager_v1 *manager; + struct wl_list link; // wlr_data_control_manager_v1::controls + + struct wlr_seat *seat; + struct wl_resource *selection_offer_resource; // current selection offer + + struct wl_listener seat_destroy; + struct wl_listener seat_selection; +}; + +struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create( + struct wl_display *display); +void wlr_data_control_manager_v1_destroy( + struct wlr_data_control_manager_v1 *manager); + +void wlr_data_control_v1_destroy(struct wlr_data_control_v1 *control); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 58f570465..afd10fb69 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -28,6 +28,7 @@ protocols = [ 'server-decoration.xml', 'text-input-unstable-v3.xml', 'virtual-keyboard-unstable-v1.xml', + 'wlr-data-control-unstable-v1.xml', 'wlr-export-dmabuf-unstable-v1.xml', 'wlr-gamma-control-unstable-v1.xml', 'wlr-foreign-toplevel-management-unstable-v1.xml', diff --git a/protocol/wlr-data-control-unstable-v1.xml b/protocol/wlr-data-control-unstable-v1.xml new file mode 100644 index 000000000..c8d50d53b --- /dev/null +++ b/protocol/wlr-data-control-unstable-v1.xml @@ -0,0 +1,220 @@ + + + + Copyright © 2018 Simon Ser + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + This protocol allows a privileged client to control data devices. In + particular, the client will be able to manage the current selection and take + the role of a clipboard manager. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This interface is a manager that allows creating per-seat data controls. + + + + + Create a new data source. + + + + + + + Create a data control that can be used to manage a seat's data device. + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This interface allows a client to manage a data device associated with a + seat. + + When the seat is destroyed, this object becomes inert. + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + Destroys the data control object. + + + + + + The data_offer event introduces a new wlr_data_control_offer object, + which will subsequently be used in the wlr_data_control.selection event. + Immediately following the wlr_data_control.data_offer event, the new + data_offer object will send out wlr_data_control_offer.offer events to + describe the MIME types it offers. + + This event replaces the previous data offer, which should be destroyed + by the client. + + + + + + + The selection event is sent out to notify the client of a new + wlr_data_control_offer for the selection for this device. The + wlr_data_control.data_offer and the wlr_data_control_offer.offer events + are sent out immediately before this event to introduce the data offer + object. The selection event is sent to a client when a new selection is + set. The wlr_data_control_offer is valid until a new + wlr_data_control_offer or NULL is received. The client must destroy the + previous selection wlr_data_control_offer, if any, upon receiving this + event. + + + + + + + This data control object is no longer valid and should be destroyed by + the client. + + + + + + + The wlr_data_control_source object is the source side of a + wlr_data_control_offer. It is created by the source client in a data + transfer and provides a way to describe the offered data and a way to + respond to requests to transfer the data. + + + + + + + + + This request adds a MIME type to the set of MIME types advertised to + targets. Can be called several times to offer multiple types. + + Calling this after wlr_data_control.set_selection is a protocol error. + + + + + + + Destroys the data source object. + + + + + + Request for data from the client. Send the data as the specified MIME + type over the passed file descriptor, then close it. + + + + + + + + This data source is no longer valid. The data source has been replaced + by another data source. + + The client should clean up and destroy this data source. + + + + + + + A wlr_data_control_offer represents a piece of data offered for transfer + by another client (the source client). The offer describes the different + MIME types that the data can be converted to and provides the mechanism + for transferring the data directly from the source client. + + + + + To transfer the offered data, the client issues this request and + indicates the MIME type 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 + then closes its end, at which point the transfer is complete. + + This request may happen multiple times for different MIME types. + + + + + + + + Destroys the data offer object. + + + + + + Sent immediately after creating the wlr_data_control_offer object. + One event per offered MIME type. + + + + + diff --git a/rootston/desktop.c b/rootston/desktop.c index 48e2635c9..5e9e352a1 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -7,24 +7,25 @@ #include #include #include +#include #include -#include #include +#include +#include #include #include #include #include #include #include -#include #include +#include #include #include #include +#include #include #include -#include -#include #include #include "rootston/layers.h" #include "rootston/seat.h" @@ -1082,6 +1083,8 @@ struct roots_desktop *desktop_create(struct roots_server *server, desktop->foreign_toplevel_manager_v1 = wlr_foreign_toplevel_manager_v1_create(server->wl_display); + wlr_data_control_manager_v1_create(server->wl_display); + return desktop; } diff --git a/types/meson.build b/types/meson.build index 1813b1449..299ccc9a9 100644 --- a/types/meson.build +++ b/types/meson.build @@ -27,6 +27,7 @@ lib_wlr_types = static_library( 'wlr_buffer.c', 'wlr_compositor.c', 'wlr_cursor.c', + 'wlr_data_control_v1.c', 'wlr_export_dmabuf_v1.c', 'wlr_foreign_toplevel_management_v1.c', 'wlr_gamma_control_v1.c', diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c new file mode 100644 index 000000000..8d90074ad --- /dev/null +++ b/types/wlr_data_control_v1.c @@ -0,0 +1,413 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include "util/signal.h" +#include "wlr-data-control-unstable-v1-protocol.h" + +#define DATA_CONTROL_MANAGER_VERSION 1 + +struct client_data_source { + struct wlr_data_source source; + struct wl_resource *resource; + bool finalized; +}; + +static const struct wlr_data_source_impl client_source_impl; + +static struct client_data_source * + client_data_source_from_source(struct wlr_data_source *wlr_source) { + assert(wlr_source->impl == &client_source_impl); + return (struct client_data_source *)wlr_source; +} + +static void client_source_send(struct wlr_data_source *wlr_source, + const char *mime_type, int fd) { + struct client_data_source *source = + client_data_source_from_source(wlr_source); + zwlr_data_control_source_v1_send_send(source->resource, mime_type, fd); + close(fd); +} + +static void client_source_cancel(struct wlr_data_source *wlr_source) { + struct client_data_source *source = + client_data_source_from_source(wlr_source); + zwlr_data_control_source_v1_send_cancelled(source->resource); + // Make the resource inert + wl_resource_set_user_data(source->resource, NULL); + free(source); +} + +static const struct wlr_data_source_impl client_source_impl = { + .send = client_source_send, + .cancel = client_source_cancel, +}; + +static const struct zwlr_data_control_source_v1_interface source_impl; + +static struct client_data_source *source_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_data_control_source_v1_interface, &source_impl)); + return wl_resource_get_user_data(resource); +} + +static void source_handle_offer(struct wl_client *client, + struct wl_resource *resource, const char *mime_type) { + struct client_data_source *source = source_from_resource(resource); + if (source == NULL) { + return; + } + + if (source->finalized) { + wl_resource_post_error(resource, + ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_INVALID_OFFER, + "cannot mutate offer after set_selection"); + return; + } + + char *dup_mime_type = strdup(mime_type); + if (dup_mime_type == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + char **p = wl_array_add(&source->source.mime_types, sizeof(char *)); + if (p == NULL) { + free(dup_mime_type); + wl_resource_post_no_memory(resource); + return; + } + + *p = dup_mime_type; +} + +static void source_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zwlr_data_control_source_v1_interface source_impl = { + .offer = source_handle_offer, + .destroy = source_handle_destroy, +}; + +static void source_handle_resource_destroy(struct wl_resource *resource) { + struct client_data_source *source = source_from_resource(resource); + if (source == NULL) { + return; + } + wlr_data_source_cancel(&source->source); +} + + +static const struct zwlr_data_control_offer_v1_interface offer_impl; + +static struct wlr_data_control_v1 *control_from_offer_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_data_control_offer_v1_interface, &offer_impl)); + return wl_resource_get_user_data(resource); +} + +static void offer_handle_receive(struct wl_client *client, + struct wl_resource *resource, const char *mime_type, int fd) { + struct wlr_data_control_v1 *control = control_from_offer_resource(resource); + if (control == NULL || control->seat->selection_source == NULL) { + close(fd); + return; + } + wlr_data_source_send(control->seat->selection_source, mime_type, fd); +} + +static void offer_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zwlr_data_control_offer_v1_interface offer_impl = { + .receive = offer_handle_receive, + .destroy = offer_handle_destroy, +}; + +static void offer_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_data_control_v1 *control = control_from_offer_resource(resource); + if (control != NULL) { + control->selection_offer_resource = NULL; + } +} + +static struct wl_resource *create_offer(struct wlr_data_control_v1 *control, + struct wlr_data_source *source) { + struct wl_client *client = wl_resource_get_client(control->resource); + uint32_t version = wl_resource_get_version(control->resource); + struct wl_resource *resource = wl_resource_create(client, + &zwlr_data_control_offer_v1_interface, version, 0); + if (resource == NULL) { + return NULL; + } + wl_resource_set_implementation(resource, &offer_impl, control, + offer_handle_resource_destroy); + + zwlr_data_control_v1_send_data_offer(control->resource, resource); + + char **p; + wl_array_for_each(p, &source->mime_types) { + zwlr_data_control_offer_v1_send_offer(resource, *p); + } + + return resource; +} + + +static const struct zwlr_data_control_v1_interface control_impl; + +static struct wlr_data_control_v1 *control_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_data_control_v1_interface, &control_impl)); + return wl_resource_get_user_data(resource); +} + +static void control_handle_set_selection(struct wl_client *client, + struct wl_resource *control_resource, + struct wl_resource *source_resource) { + struct wlr_data_control_v1 *control = + control_from_resource(control_resource); + struct client_data_source *source = source_from_resource(source_resource); + if (control == NULL || source == NULL) { + return; + } + + struct wl_display *display = wl_client_get_display(client); + wlr_seat_set_selection(control->seat, &source->source, + wl_display_next_serial(display)); +} + +static void control_handle_destroy(struct wl_client *client, + struct wl_resource *control_resource) { + wl_resource_destroy(control_resource); +} + +static const struct zwlr_data_control_v1_interface control_impl = { + .set_selection = control_handle_set_selection, + .destroy = control_handle_destroy, +}; + +static void control_send_selection(struct wlr_data_control_v1 *control) { + struct wlr_data_source *source = control->seat->selection_source; + + if (control->selection_offer_resource != NULL) { + // Make the offer inert + wl_resource_set_user_data(control->selection_offer_resource, NULL); + } + + control->selection_offer_resource = NULL; + if (source != NULL) { + control->selection_offer_resource = create_offer(control, source); + if (control->selection_offer_resource == NULL) { + wl_resource_post_no_memory(control->resource); + return; + } + } + + zwlr_data_control_v1_send_selection(control->resource, + control->selection_offer_resource); +} + +static void control_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_data_control_v1 *control = control_from_resource(resource); + wlr_data_control_v1_destroy(control); +} + +static void control_handle_seat_destroy(struct wl_listener *listener, + void *data) { + struct wlr_data_control_v1 *control = + wl_container_of(listener, control, seat_destroy); + wlr_data_control_v1_destroy(control); +} + +static void control_handle_seat_selection(struct wl_listener *listener, + void *data) { + struct wlr_data_control_v1 *control = + wl_container_of(listener, control, seat_selection); + control_send_selection(control); +} + +void wlr_data_control_v1_destroy(struct wlr_data_control_v1 *control) { + if (control == NULL) { + return; + } + zwlr_data_control_v1_send_finished(control->resource); + // Make the resource inert + wl_resource_set_user_data(control->resource, NULL); + wl_list_remove(&control->seat_destroy.link); + wl_list_remove(&control->link); + free(control); +} + + +static const struct zwlr_data_control_manager_v1_interface manager_impl; + +static struct wlr_data_control_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_data_control_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void manager_handle_create_data_source(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id) { + struct client_data_source *source = + calloc(1, sizeof(struct client_data_source)); + if (source == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + wlr_data_source_init(&source->source, &client_source_impl); + + uint32_t version = wl_resource_get_version(manager_resource); + source->resource = wl_resource_create(client, + &zwlr_data_control_source_v1_interface, version, id); + if (source->resource == NULL) { + wl_resource_post_no_memory(manager_resource); + free(source); + return; + } + wl_resource_set_implementation(source->resource, &source_impl, source, + source_handle_resource_destroy); +} + +static void manager_handle_get_data_control(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + struct wl_resource *seat_resource) { + struct wlr_data_control_manager_v1 *manager = + manager_from_resource(manager_resource); + struct wlr_seat_client *seat_client = + wlr_seat_client_from_resource(seat_resource); + + struct wlr_data_control_v1 *control = + calloc(1, sizeof(struct wlr_data_control_v1)); + if (control == NULL) { + wl_resource_post_no_memory(manager_resource); + return; + } + control->manager = manager; + control->seat = seat_client->seat; + + uint32_t version = wl_resource_get_version(manager_resource); + control->resource = wl_resource_create(client, + &zwlr_data_control_v1_interface, version, id); + if (control->resource == NULL) { + wl_resource_post_no_memory(manager_resource); + free(control); + return; + } + wl_resource_set_implementation(control->resource, &control_impl, control, + control_handle_resource_destroy); + struct wl_resource *resource = control->resource; + + control->seat_destroy.notify = control_handle_seat_destroy; + wl_signal_add(&control->seat->events.destroy, &control->seat_destroy); + + control->seat_selection.notify = control_handle_seat_selection; + wl_signal_add(&control->seat->events.destroy, &control->seat_selection); + + wl_list_insert(&manager->controls, &control->link); + wlr_signal_emit_safe(&manager->events.new_control, control); + + // At this point maybe the compositor decided to destroy the control. If + // it's the case then the resource will be inert. + control = control_from_resource(resource); + if (control != NULL) { + control_send_selection(control); + } +} + +static void manager_handle_destroy(struct wl_client *client, + struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct zwlr_data_control_manager_v1_interface manager_impl = { + .create_data_source = manager_handle_create_data_source, + .get_data_control = manager_handle_get_data_control, + .destroy = manager_handle_destroy, +}; + +static void manager_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void manager_bind(struct wl_client *client, void *data, uint32_t version, + uint32_t id) { + struct wlr_data_control_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &zwlr_data_control_manager_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, manager, + manager_handle_resource_destroy); + + wl_list_insert(&manager->resources, wl_resource_get_link(resource)); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_data_control_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wlr_data_control_manager_v1_destroy(manager); +} + +struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create( + struct wl_display *display) { + struct wlr_data_control_manager_v1 *manager = + calloc(1, sizeof(struct wlr_data_control_manager_v1)); + if (manager == NULL) { + return NULL; + } + wl_list_init(&manager->resources); + wl_list_init(&manager->controls); + wl_signal_init(&manager->events.destroy); + wl_signal_init(&manager->events.new_control); + + manager->global = wl_global_create(display, + &zwlr_data_control_manager_v1_interface, DATA_CONTROL_MANAGER_VERSION, + manager, 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_data_control_manager_v1_destroy( + struct wlr_data_control_manager_v1 *manager) { + if (manager == NULL) { + return; + } + + wlr_signal_emit_safe(&manager->events.destroy, manager); + + struct wlr_data_control_v1 *control, *control_tmp; + wl_list_for_each_safe(control, control_tmp, &manager->controls, link) { + wl_resource_destroy(control->resource); + } + + struct wl_resource *resource, *resource_tmp; + wl_resource_for_each_safe(resource, resource_tmp, &manager->resources) { + wl_resource_destroy(resource); + } + + wl_list_remove(&manager->display_destroy.link); + free(manager); +} From 1e581ad95cfb50acf6b251c7d3da209ea32ca8d7 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 12 Dec 2018 09:53:36 +0100 Subject: [PATCH 2/8] data-control-v1: fix crash after wlr_data_control_v1_destroy --- types/wlr_data_control_v1.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c index 8d90074ad..fcb37d7aa 100644 --- a/types/wlr_data_control_v1.c +++ b/types/wlr_data_control_v1.c @@ -242,8 +242,11 @@ void wlr_data_control_v1_destroy(struct wlr_data_control_v1 *control) { return; } zwlr_data_control_v1_send_finished(control->resource); - // Make the resource inert + // Make the resources inert wl_resource_set_user_data(control->resource, NULL); + if (control->selection_offer_resource != NULL) { + wl_resource_set_user_data(control->selection_offer_resource, NULL); + } wl_list_remove(&control->seat_destroy.link); wl_list_remove(&control->link); free(control); From 4423630687397e714183e7e0f690613175fd6df8 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 12 Dec 2018 10:01:27 +0100 Subject: [PATCH 3/8] data-control-v1: update protocol to allow NULL selections --- protocol/wlr-data-control-unstable-v1.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/protocol/wlr-data-control-unstable-v1.xml b/protocol/wlr-data-control-unstable-v1.xml index c8d50d53b..bd105f2ef 100644 --- a/protocol/wlr-data-control-unstable-v1.xml +++ b/protocol/wlr-data-control-unstable-v1.xml @@ -82,7 +82,8 @@ All objects created by the manager will still remain valid, until their appropriate destroy request has been called. - + @@ -117,7 +118,8 @@ previous selection wlr_data_control_offer, if any, upon receiving this event. - + From aa2dafb7c8b3c7ee6ef9ab49a0cc576c68f2768c Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 12 Dec 2018 10:53:27 +0100 Subject: [PATCH 4/8] data-control-v1: fix crash in manager_handle_get_data_control --- types/wlr_data_control_v1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c index fcb37d7aa..5f7b2df5f 100644 --- a/types/wlr_data_control_v1.c +++ b/types/wlr_data_control_v1.c @@ -248,6 +248,7 @@ void wlr_data_control_v1_destroy(struct wlr_data_control_v1 *control) { wl_resource_set_user_data(control->selection_offer_resource, NULL); } wl_list_remove(&control->seat_destroy.link); + wl_list_remove(&control->seat_selection.link); wl_list_remove(&control->link); free(control); } @@ -317,7 +318,7 @@ static void manager_handle_get_data_control(struct wl_client *client, wl_signal_add(&control->seat->events.destroy, &control->seat_destroy); control->seat_selection.notify = control_handle_seat_selection; - wl_signal_add(&control->seat->events.destroy, &control->seat_selection); + wl_signal_add(&control->seat->events.selection, &control->seat_selection); wl_list_insert(&manager->controls, &control->link); wlr_signal_emit_safe(&manager->events.new_control, control); From ae1dd635b133dbe0e22c3b1b8ed93be0347e0865 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 12 Dec 2018 11:00:28 +0100 Subject: [PATCH 5/8] data-device: fix dangling listener in seat_client_selection_source_destroy --- types/data_device/wlr_data_device.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/types/data_device/wlr_data_device.c b/types/data_device/wlr_data_device.c index f868ea370..cda755b1c 100644 --- a/types/data_device/wlr_data_device.c +++ b/types/data_device/wlr_data_device.c @@ -122,6 +122,9 @@ static void seat_client_selection_source_destroy( wl_container_of(listener, seat, selection_source_destroy); struct wlr_seat_client *seat_client = seat->keyboard_state.focused_client; + wl_list_remove(&seat->selection_source_destroy.link); + seat->selection_source = NULL; + if (seat_client && seat->keyboard_state.focused_surface) { struct wl_resource *resource; wl_resource_for_each(resource, &seat_client->data_devices) { @@ -129,8 +132,6 @@ static void seat_client_selection_source_destroy( } } - seat->selection_source = NULL; - wlr_signal_emit_safe(&seat->events.selection, seat); } From a28167c255b25bdcca2f7c85dd78528265652679 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 12 Dec 2018 11:24:32 +0100 Subject: [PATCH 6/8] data-control-v1: allow clearing selection --- types/wlr_data_control_v1.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c index 5f7b2df5f..8d8247de9 100644 --- a/types/wlr_data_control_v1.c +++ b/types/wlr_data_control_v1.c @@ -178,12 +178,13 @@ static void control_handle_set_selection(struct wl_client *client, struct wlr_data_control_v1 *control = control_from_resource(control_resource); struct client_data_source *source = source_from_resource(source_resource); - if (control == NULL || source == NULL) { + if (control == NULL) { return; } + struct wlr_data_source *wlr_source = source ? &source->source : NULL; struct wl_display *display = wl_client_get_display(client); - wlr_seat_set_selection(control->seat, &source->source, + wlr_seat_set_selection(control->seat, wlr_source, wl_display_next_serial(display)); } From 340281b48aad852d78148adb58dffd76dfcefe4f Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 12 Dec 2018 11:30:34 +0100 Subject: [PATCH 7/8] data-control-v1: fix data source not properly destroyed --- types/wlr_data_control_v1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c index 8d8247de9..be8bfbe81 100644 --- a/types/wlr_data_control_v1.c +++ b/types/wlr_data_control_v1.c @@ -36,6 +36,7 @@ static void client_source_cancel(struct wlr_data_source *wlr_source) { struct client_data_source *source = client_data_source_from_source(wlr_source); zwlr_data_control_source_v1_send_cancelled(source->resource); + wlr_data_source_finish(wlr_source); // Make the resource inert wl_resource_set_user_data(source->resource, NULL); free(source); From 69e7fd61b7824ce683137b44beff02899d6ce78b Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 30 Dec 2018 02:52:03 +0100 Subject: [PATCH 8/8] data-control-v1: update to latest protocol proposal --- include/wlr/types/wlr_data_control_v1.h | 11 +- protocol/wlr-data-control-unstable-v1.xml | 46 +++---- types/wlr_data_control_v1.c | 150 +++++++++++----------- 3 files changed, 105 insertions(+), 102 deletions(-) diff --git a/include/wlr/types/wlr_data_control_v1.h b/include/wlr/types/wlr_data_control_v1.h index 69d7281be..50c967366 100644 --- a/include/wlr/types/wlr_data_control_v1.h +++ b/include/wlr/types/wlr_data_control_v1.h @@ -15,20 +15,20 @@ struct wlr_data_control_manager_v1 { struct wl_global *global; struct wl_list resources; // wl_resource_get_link - struct wl_list controls; // wlr_data_control_v1::link + struct wl_list devices; // wlr_data_control_device_v1::link struct { struct wl_signal destroy; - struct wl_signal new_control; // wlr_data_control_v1 + struct wl_signal new_device; // wlr_data_control_device_v1 } events; struct wl_listener display_destroy; }; -struct wlr_data_control_v1 { +struct wlr_data_control_device_v1 { struct wl_resource *resource; struct wlr_data_control_manager_v1 *manager; - struct wl_list link; // wlr_data_control_manager_v1::controls + struct wl_list link; // wlr_data_control_manager_v1::devices struct wlr_seat *seat; struct wl_resource *selection_offer_resource; // current selection offer @@ -42,6 +42,7 @@ struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create( void wlr_data_control_manager_v1_destroy( struct wlr_data_control_manager_v1 *manager); -void wlr_data_control_v1_destroy(struct wlr_data_control_v1 *control); +void wlr_data_control_device_v1_destroy( + struct wlr_data_control_device_v1 *device); #endif diff --git a/protocol/wlr-data-control-unstable-v1.xml b/protocol/wlr-data-control-unstable-v1.xml index bd105f2ef..a58875505 100644 --- a/protocol/wlr-data-control-unstable-v1.xml +++ b/protocol/wlr-data-control-unstable-v1.xml @@ -41,8 +41,9 @@ - - This interface is a manager that allows creating per-seat data controls. + + This interface is a manager that allows creating per-seat data device + controls. @@ -53,11 +54,11 @@ summary="data source to create"/> - - - Create a data control that can be used to manage a seat's data device. + + + Create a data device that can be used to manage a seat's selection. - + @@ -69,17 +70,16 @@ - + - This interface allows a client to manage a data device associated with a - seat. + This interface allows a client to manage a seat's selection. When the seat is destroyed, this object becomes inert. - All objects created by the manager will still remain valid, until their + All objects created by the device will still remain valid, until their appropriate destroy request has been called. - - Destroys the data control object. + + Destroys the data device object. The data_offer event introduces a new wlr_data_control_offer object, - which will subsequently be used in the wlr_data_control.selection event. - Immediately following the wlr_data_control.data_offer event, the new - data_offer object will send out wlr_data_control_offer.offer events to - describe the MIME types it offers. + which will subsequently be used in the wlr_data_control_device.selection + event. Immediately following the wlr_data_control_device.data_offer + event, the new data_offer object will send out + wlr_data_control_offer.offer events to describe the MIME types it + offers. This event replaces the previous data offer, which should be destroyed by the client. @@ -110,10 +111,10 @@ The selection event is sent out to notify the client of a new wlr_data_control_offer for the selection for this device. The - wlr_data_control.data_offer and the wlr_data_control_offer.offer events - are sent out immediately before this event to introduce the data offer - object. The selection event is sent to a client when a new selection is - set. The wlr_data_control_offer is valid until a new + wlr_data_control_device.data_offer and the wlr_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The selection event is sent to a client when a new + selection is set. The wlr_data_control_offer is valid until a new wlr_data_control_offer or NULL is received. The client must destroy the previous selection wlr_data_control_offer, if any, upon receiving this event. @@ -140,7 +141,7 @@ + summary="offer sent after wlr_data_control_device.set_selection"/> @@ -148,7 +149,8 @@ This request adds a MIME type to the set of MIME types advertised to targets. Can be called several times to offer multiple types. - Calling this after wlr_data_control.set_selection is a protocol error. + Calling this after wlr_data_control_device.set_selection is a protocol + error. diff --git a/types/wlr_data_control_v1.c b/types/wlr_data_control_v1.c index be8bfbe81..9812220ca 100644 --- a/types/wlr_data_control_v1.c +++ b/types/wlr_data_control_v1.c @@ -107,7 +107,7 @@ static void source_handle_resource_destroy(struct wl_resource *resource) { static const struct zwlr_data_control_offer_v1_interface offer_impl; -static struct wlr_data_control_v1 *control_from_offer_resource( +static struct wlr_data_control_device_v1 *control_from_offer_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_data_control_offer_v1_interface, &offer_impl)); @@ -116,12 +116,12 @@ static struct wlr_data_control_v1 *control_from_offer_resource( static void offer_handle_receive(struct wl_client *client, struct wl_resource *resource, const char *mime_type, int fd) { - struct wlr_data_control_v1 *control = control_from_offer_resource(resource); - if (control == NULL || control->seat->selection_source == NULL) { + struct wlr_data_control_device_v1 *device = control_from_offer_resource(resource); + if (device == NULL || device->seat->selection_source == NULL) { close(fd); return; } - wlr_data_source_send(control->seat->selection_source, mime_type, fd); + wlr_data_source_send(device->seat->selection_source, mime_type, fd); } static void offer_handle_destroy(struct wl_client *client, @@ -135,25 +135,25 @@ static const struct zwlr_data_control_offer_v1_interface offer_impl = { }; static void offer_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_data_control_v1 *control = control_from_offer_resource(resource); - if (control != NULL) { - control->selection_offer_resource = NULL; + struct wlr_data_control_device_v1 *device = control_from_offer_resource(resource); + if (device != NULL) { + device->selection_offer_resource = NULL; } } -static struct wl_resource *create_offer(struct wlr_data_control_v1 *control, +static struct wl_resource *create_offer(struct wlr_data_control_device_v1 *device, struct wlr_data_source *source) { - struct wl_client *client = wl_resource_get_client(control->resource); - uint32_t version = wl_resource_get_version(control->resource); + struct wl_client *client = wl_resource_get_client(device->resource); + uint32_t version = wl_resource_get_version(device->resource); struct wl_resource *resource = wl_resource_create(client, &zwlr_data_control_offer_v1_interface, version, 0); if (resource == NULL) { return NULL; } - wl_resource_set_implementation(resource, &offer_impl, control, + wl_resource_set_implementation(resource, &offer_impl, device, offer_handle_resource_destroy); - zwlr_data_control_v1_send_data_offer(control->resource, resource); + zwlr_data_control_device_v1_send_data_offer(device->resource, resource); char **p; wl_array_for_each(p, &source->mime_types) { @@ -164,28 +164,28 @@ static struct wl_resource *create_offer(struct wlr_data_control_v1 *control, } -static const struct zwlr_data_control_v1_interface control_impl; +static const struct zwlr_data_control_device_v1_interface control_impl; -static struct wlr_data_control_v1 *control_from_resource( +static struct wlr_data_control_device_v1 *control_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, - &zwlr_data_control_v1_interface, &control_impl)); + &zwlr_data_control_device_v1_interface, &control_impl)); return wl_resource_get_user_data(resource); } static void control_handle_set_selection(struct wl_client *client, struct wl_resource *control_resource, struct wl_resource *source_resource) { - struct wlr_data_control_v1 *control = + struct wlr_data_control_device_v1 *device = control_from_resource(control_resource); struct client_data_source *source = source_from_resource(source_resource); - if (control == NULL) { + if (device == NULL) { return; } struct wlr_data_source *wlr_source = source ? &source->source : NULL; struct wl_display *display = wl_client_get_display(client); - wlr_seat_set_selection(control->seat, wlr_source, + wlr_seat_set_selection(device->seat, wlr_source, wl_display_next_serial(display)); } @@ -194,65 +194,65 @@ static void control_handle_destroy(struct wl_client *client, wl_resource_destroy(control_resource); } -static const struct zwlr_data_control_v1_interface control_impl = { +static const struct zwlr_data_control_device_v1_interface control_impl = { .set_selection = control_handle_set_selection, .destroy = control_handle_destroy, }; -static void control_send_selection(struct wlr_data_control_v1 *control) { - struct wlr_data_source *source = control->seat->selection_source; +static void control_send_selection(struct wlr_data_control_device_v1 *device) { + struct wlr_data_source *source = device->seat->selection_source; - if (control->selection_offer_resource != NULL) { + if (device->selection_offer_resource != NULL) { // Make the offer inert - wl_resource_set_user_data(control->selection_offer_resource, NULL); + wl_resource_set_user_data(device->selection_offer_resource, NULL); } - control->selection_offer_resource = NULL; + device->selection_offer_resource = NULL; if (source != NULL) { - control->selection_offer_resource = create_offer(control, source); - if (control->selection_offer_resource == NULL) { - wl_resource_post_no_memory(control->resource); + device->selection_offer_resource = create_offer(device, source); + if (device->selection_offer_resource == NULL) { + wl_resource_post_no_memory(device->resource); return; } } - zwlr_data_control_v1_send_selection(control->resource, - control->selection_offer_resource); + zwlr_data_control_device_v1_send_selection(device->resource, + device->selection_offer_resource); } static void control_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_data_control_v1 *control = control_from_resource(resource); - wlr_data_control_v1_destroy(control); + struct wlr_data_control_device_v1 *device = control_from_resource(resource); + wlr_data_control_device_v1_destroy(device); } static void control_handle_seat_destroy(struct wl_listener *listener, void *data) { - struct wlr_data_control_v1 *control = - wl_container_of(listener, control, seat_destroy); - wlr_data_control_v1_destroy(control); + struct wlr_data_control_device_v1 *device = + wl_container_of(listener, device, seat_destroy); + wlr_data_control_device_v1_destroy(device); } static void control_handle_seat_selection(struct wl_listener *listener, void *data) { - struct wlr_data_control_v1 *control = - wl_container_of(listener, control, seat_selection); - control_send_selection(control); + struct wlr_data_control_device_v1 *device = + wl_container_of(listener, device, seat_selection); + control_send_selection(device); } -void wlr_data_control_v1_destroy(struct wlr_data_control_v1 *control) { - if (control == NULL) { +void wlr_data_control_device_v1_destroy(struct wlr_data_control_device_v1 *device) { + if (device == NULL) { return; } - zwlr_data_control_v1_send_finished(control->resource); + zwlr_data_control_device_v1_send_finished(device->resource); // Make the resources inert - wl_resource_set_user_data(control->resource, NULL); - if (control->selection_offer_resource != NULL) { - wl_resource_set_user_data(control->selection_offer_resource, NULL); + wl_resource_set_user_data(device->resource, NULL); + if (device->selection_offer_resource != NULL) { + wl_resource_set_user_data(device->selection_offer_resource, NULL); } - wl_list_remove(&control->seat_destroy.link); - wl_list_remove(&control->seat_selection.link); - wl_list_remove(&control->link); - free(control); + wl_list_remove(&device->seat_destroy.link); + wl_list_remove(&device->seat_selection.link); + wl_list_remove(&device->link); + free(device); } @@ -287,7 +287,7 @@ static void manager_handle_create_data_source(struct wl_client *client, source_handle_resource_destroy); } -static void manager_handle_get_data_control(struct wl_client *client, +static void manager_handle_get_data_device(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *seat_resource) { struct wlr_data_control_manager_v1 *manager = @@ -295,41 +295,41 @@ static void manager_handle_get_data_control(struct wl_client *client, struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); - struct wlr_data_control_v1 *control = - calloc(1, sizeof(struct wlr_data_control_v1)); - if (control == NULL) { + struct wlr_data_control_device_v1 *device = + calloc(1, sizeof(struct wlr_data_control_device_v1)); + if (device == NULL) { wl_resource_post_no_memory(manager_resource); return; } - control->manager = manager; - control->seat = seat_client->seat; + device->manager = manager; + device->seat = seat_client->seat; uint32_t version = wl_resource_get_version(manager_resource); - control->resource = wl_resource_create(client, - &zwlr_data_control_v1_interface, version, id); - if (control->resource == NULL) { + device->resource = wl_resource_create(client, + &zwlr_data_control_device_v1_interface, version, id); + if (device->resource == NULL) { wl_resource_post_no_memory(manager_resource); - free(control); + free(device); return; } - wl_resource_set_implementation(control->resource, &control_impl, control, + wl_resource_set_implementation(device->resource, &control_impl, device, control_handle_resource_destroy); - struct wl_resource *resource = control->resource; + struct wl_resource *resource = device->resource; - control->seat_destroy.notify = control_handle_seat_destroy; - wl_signal_add(&control->seat->events.destroy, &control->seat_destroy); + device->seat_destroy.notify = control_handle_seat_destroy; + wl_signal_add(&device->seat->events.destroy, &device->seat_destroy); - control->seat_selection.notify = control_handle_seat_selection; - wl_signal_add(&control->seat->events.selection, &control->seat_selection); + device->seat_selection.notify = control_handle_seat_selection; + wl_signal_add(&device->seat->events.selection, &device->seat_selection); - wl_list_insert(&manager->controls, &control->link); - wlr_signal_emit_safe(&manager->events.new_control, control); + wl_list_insert(&manager->devices, &device->link); + wlr_signal_emit_safe(&manager->events.new_device, device); - // At this point maybe the compositor decided to destroy the control. If + // At this point maybe the compositor decided to destroy the device. If // it's the case then the resource will be inert. - control = control_from_resource(resource); - if (control != NULL) { - control_send_selection(control); + device = control_from_resource(resource); + if (device != NULL) { + control_send_selection(device); } } @@ -340,7 +340,7 @@ static void manager_handle_destroy(struct wl_client *client, static const struct zwlr_data_control_manager_v1_interface manager_impl = { .create_data_source = manager_handle_create_data_source, - .get_data_control = manager_handle_get_data_control, + .get_data_device = manager_handle_get_data_device, .destroy = manager_handle_destroy, }; @@ -378,9 +378,9 @@ struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create( return NULL; } wl_list_init(&manager->resources); - wl_list_init(&manager->controls); + wl_list_init(&manager->devices); wl_signal_init(&manager->events.destroy); - wl_signal_init(&manager->events.new_control); + wl_signal_init(&manager->events.new_device); manager->global = wl_global_create(display, &zwlr_data_control_manager_v1_interface, DATA_CONTROL_MANAGER_VERSION, @@ -404,9 +404,9 @@ void wlr_data_control_manager_v1_destroy( wlr_signal_emit_safe(&manager->events.destroy, manager); - struct wlr_data_control_v1 *control, *control_tmp; - wl_list_for_each_safe(control, control_tmp, &manager->controls, link) { - wl_resource_destroy(control->resource); + struct wlr_data_control_device_v1 *device, *control_tmp; + wl_list_for_each_safe(device, control_tmp, &manager->devices, link) { + wl_resource_destroy(device->resource); } struct wl_resource *resource, *resource_tmp;