aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoremersion <contact@emersion.fr>2018-11-21 10:59:11 +0100
committeremersion <contact@emersion.fr>2018-11-21 11:16:25 +0100
commitb9a2e4ba4c92cd4c19f8cf5ed7e6603731eca9ab (patch)
tree2bba6f5007ad5a46c9d6874d2e32d0148e843ec6
parent040d62de0076a349612b7c2c28c5dc5e93bb9760 (diff)
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
-rw-r--r--include/wlr/types/wlr_primary_selection.h3
-rw-r--r--types/wlr_primary_selection.c96
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,
&gtk_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,
- &gtk_primary_selection_device_manager_interface, 1, manager,
- primary_selection_device_manager_bind);
+ &gtk_primary_selection_device_manager_interface, DEVICE_MANAGER_VERSION,
+ manager, primary_selection_device_manager_bind);
if (manager->global == NULL) {
free(manager);
return NULL;