From b9a2e4ba4c92cd4c19f8cf5ed7e6603731eca9ab Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 21 Nov 2018 10:59:11 +0100 Subject: [PATCH 1/2] gtk-primary-selection: support multiple devices When a client was creating multiple data devices for the same seat, we were only creating one resource. This is a protocol error. Instead, create one offer per data device. This commit also makes offers inert when their source is destroyed. Fixes part of https://github.com/swaywm/wlroots/issues/1041 Supersedes https://github.com/swaywm/wlroots/pull/1113 --- include/wlr/types/wlr_primary_selection.h | 3 - types/wlr_primary_selection.c | 96 ++++++++++------------- 2 files changed, 40 insertions(+), 59 deletions(-) diff --git a/include/wlr/types/wlr_primary_selection.h b/include/wlr/types/wlr_primary_selection.h index f33f6368..c11e2fab 100644 --- a/include/wlr/types/wlr_primary_selection.h +++ b/include/wlr/types/wlr_primary_selection.h @@ -24,8 +24,6 @@ struct wlr_primary_selection_device_manager { void *data; }; -struct wlr_primary_selection_offer; - struct wlr_primary_selection_source { // source metadata struct wl_array mime_types; @@ -36,7 +34,6 @@ struct wlr_primary_selection_source { void (*cancel)(struct wlr_primary_selection_source *source); // source status - struct wlr_primary_selection_offer *offer; struct wlr_seat_client *seat_client; struct { diff --git a/types/wlr_primary_selection.c b/types/wlr_primary_selection.c index 3fed0f64..0539773b 100644 --- a/types/wlr_primary_selection.c +++ b/types/wlr_primary_selection.c @@ -9,6 +9,8 @@ #include "gtk-primary-selection-protocol.h" #include "util/signal.h" +#define DEVICE_MANAGER_VERSION 1 + static const struct gtk_primary_selection_offer_interface offer_impl; static struct wlr_primary_selection_offer *offer_from_resource( @@ -21,12 +23,12 @@ static struct wlr_primary_selection_offer *offer_from_resource( static void offer_handle_receive(struct wl_client *client, struct wl_resource *resource, const char *mime_type, int32_t fd) { struct wlr_primary_selection_offer *offer = offer_from_resource(resource); - - if (offer->source && offer == offer->source->offer) { - offer->source->send(offer->source, mime_type, fd); - } else { + if (offer == NULL) { close(fd); + return; } + + offer->source->send(offer->source, mime_type, fd); } static void offer_handle_destroy(struct wl_client *client, @@ -39,31 +41,26 @@ static const struct gtk_primary_selection_offer_interface offer_impl = { .destroy = offer_handle_destroy, }; -static void offer_resource_handle_destroy(struct wl_resource *resource) { - struct wlr_primary_selection_offer *offer = offer_from_resource(resource); - - if (!offer->source) { - goto out; +static void offer_destroy(struct wlr_primary_selection_offer *offer) { + if (offer == NULL) { + return; } - + // Make resource inert + wl_resource_set_user_data(offer->resource, NULL); wl_list_remove(&offer->source_destroy.link); - - if (offer->source->offer != offer) { - goto out; - } - - offer->source->offer = NULL; - -out: free(offer); } +static void offer_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_primary_selection_offer *offer = offer_from_resource(resource); + offer_destroy(offer); +} + static void offer_handle_source_destroy(struct wl_listener *listener, void *data) { struct wlr_primary_selection_offer *offer = wl_container_of(listener, offer, source_destroy); - - offer->source = NULL; + offer_destroy(offer); } @@ -85,38 +82,32 @@ static void client_source_cancel( gtk_primary_selection_source_send_cancelled(source->resource); } -static struct wlr_primary_selection_offer *source_send_offer( - struct wlr_primary_selection_source *source, - struct wlr_seat_client *target) { - if (wl_list_empty(&target->primary_selection_devices)) { - return NULL; - } - +static void source_send_offer(struct wlr_primary_selection_source *source, + struct wl_resource *device_resource) { struct wlr_primary_selection_offer *offer = calloc(1, sizeof(struct wlr_primary_selection_offer)); if (offer == NULL) { - return NULL; + wl_resource_post_no_memory(device_resource); + return; } - uint32_t version = wl_resource_get_version( - wl_resource_from_link(target->primary_selection_devices.next)); - offer->resource = wl_resource_create(target->client, + struct wl_client *client = wl_resource_get_client(device_resource); + uint32_t version = wl_resource_get_version(device_resource); + offer->resource = wl_resource_create(client, >k_primary_selection_offer_interface, version, 0); if (offer->resource == NULL) { free(offer); - return NULL; + wl_resource_post_no_memory(device_resource); + return; } wl_resource_set_implementation(offer->resource, &offer_impl, offer, - offer_resource_handle_destroy); + offer_handle_resource_destroy); offer->source_destroy.notify = offer_handle_source_destroy; wl_signal_add(&source->events.destroy, &offer->source_destroy); - struct wl_resource *target_resource; - wl_resource_for_each(target_resource, &target->primary_selection_devices) { - gtk_primary_selection_device_send_data_offer(target_resource, - offer->resource); - } + gtk_primary_selection_device_send_data_offer(device_resource, + offer->resource); char **p; wl_array_for_each(p, &source->mime_types) { @@ -124,9 +115,9 @@ static struct wlr_primary_selection_offer *source_send_offer( } offer->source = source; - source->offer = offer; - return offer; + gtk_primary_selection_device_send_selection(device_resource, + offer->resource); } static const struct gtk_primary_selection_source_interface source_impl; @@ -179,20 +170,13 @@ void wlr_seat_client_send_primary_selection( return; } - if (seat_client->seat->primary_selection_source) { - struct wlr_primary_selection_offer *offer = source_send_offer( - seat_client->seat->primary_selection_source, seat_client); - if (offer == NULL) { - return; - } - - struct wl_resource *resource; - wl_resource_for_each(resource, &seat_client->primary_selection_devices) { - gtk_primary_selection_device_send_selection(resource, offer->resource); - } - } else { - struct wl_resource *resource; - wl_resource_for_each(resource, &seat_client->primary_selection_devices) { + struct wlr_primary_selection_source *source = + seat_client->seat->primary_selection_source; + struct wl_resource *resource; + wl_resource_for_each(resource, &seat_client->primary_selection_devices) { + if (source) { + source_send_offer(source, resource); + } else { gtk_primary_selection_device_send_selection(resource, NULL); } } @@ -401,8 +385,8 @@ struct wlr_primary_selection_device_manager * return NULL; } manager->global = wl_global_create(display, - >k_primary_selection_device_manager_interface, 1, manager, - primary_selection_device_manager_bind); + >k_primary_selection_device_manager_interface, DEVICE_MANAGER_VERSION, + manager, primary_selection_device_manager_bind); if (manager->global == NULL) { free(manager); return NULL; From eaafd65a12d6b15cc8e503ef6e9e99a65bd1e791 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 21 Nov 2018 11:15:35 +0100 Subject: [PATCH 2/2] gtk-primary-selection: track resources --- include/wlr/types/wlr_primary_selection.h | 1 + types/wlr_primary_selection.c | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/wlr/types/wlr_primary_selection.h b/include/wlr/types/wlr_primary_selection.h index c11e2fab..192bfc02 100644 --- a/include/wlr/types/wlr_primary_selection.h +++ b/include/wlr/types/wlr_primary_selection.h @@ -14,6 +14,7 @@ struct wlr_primary_selection_device_manager { struct wl_global *global; + struct wl_list resources; struct wl_listener display_destroy; diff --git a/types/wlr_primary_selection.c b/types/wlr_primary_selection.c index 0539773b..74312540 100644 --- a/types/wlr_primary_selection.c +++ b/types/wlr_primary_selection.c @@ -212,6 +212,7 @@ void wlr_seat_set_primary_selection(struct wlr_seat *seat, return; } + // TODO: make all offers inert if (seat->primary_selection_source) { wl_list_remove(&seat->primary_selection_source_destroy.link); seat->primary_selection_source->cancel(seat->primary_selection_source); @@ -349,12 +350,17 @@ static void device_manager_handle_destroy(struct wl_client *client, } static const struct gtk_primary_selection_device_manager_interface -device_manager_impl = { + device_manager_impl = { .create_source = device_manager_handle_create_source, .get_device = device_manager_handle_get_device, .destroy = device_manager_handle_destroy, }; +static void device_manager_handle_resource_destroy( + struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + static void primary_selection_device_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { @@ -367,7 +373,9 @@ static void primary_selection_device_manager_bind(struct wl_client *client, return; } wl_resource_set_implementation(resource, &device_manager_impl, manager, - NULL); + device_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) { @@ -392,6 +400,7 @@ struct wlr_primary_selection_device_manager * return NULL; } + wl_list_init(&manager->resources); wl_signal_init(&manager->events.destroy); manager->display_destroy.notify = handle_display_destroy; @@ -407,7 +416,10 @@ void wlr_primary_selection_device_manager_destroy( } wlr_signal_emit_safe(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); - // TODO: free resources + struct wl_resource *resource, *resource_tmp; + wl_resource_for_each_safe(resource, resource_tmp, &manager->resources) { + wl_resource_destroy(resource); + } wl_global_destroy(manager->global); free(manager); }