Skip to content

Commit

Permalink
gtk-primary-selection: support multiple devices
Browse files Browse the repository at this point in the history
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 swaywm#1041
Supersedes swaywm#1113
  • Loading branch information
emersion committed Nov 21, 2018
1 parent 040d62d commit 1c0d626
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 57 deletions.
1 change: 0 additions & 1 deletion include/wlr/types/wlr_primary_selection.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,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 {
Expand Down
96 changes: 40 additions & 56 deletions types/wlr_primary_selection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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,
Expand All @@ -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);
}


Expand All @@ -85,48 +82,42 @@ 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) {
gtk_primary_selection_offer_send_offer(offer->resource, *p);
}

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;
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 1c0d626

Please sign in to comment.