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
This commit is contained in:
parent
040d62de00
commit
b9a2e4ba4c
|
@ -24,8 +24,6 @@ struct wlr_primary_selection_device_manager {
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_primary_selection_offer;
|
|
||||||
|
|
||||||
struct wlr_primary_selection_source {
|
struct wlr_primary_selection_source {
|
||||||
// source metadata
|
// source metadata
|
||||||
struct wl_array mime_types;
|
struct wl_array mime_types;
|
||||||
|
@ -36,7 +34,6 @@ struct wlr_primary_selection_source {
|
||||||
void (*cancel)(struct wlr_primary_selection_source *source);
|
void (*cancel)(struct wlr_primary_selection_source *source);
|
||||||
|
|
||||||
// source status
|
// source status
|
||||||
struct wlr_primary_selection_offer *offer;
|
|
||||||
struct wlr_seat_client *seat_client;
|
struct wlr_seat_client *seat_client;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include "gtk-primary-selection-protocol.h"
|
#include "gtk-primary-selection-protocol.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
|
#define DEVICE_MANAGER_VERSION 1
|
||||||
|
|
||||||
static const struct gtk_primary_selection_offer_interface offer_impl;
|
static const struct gtk_primary_selection_offer_interface offer_impl;
|
||||||
|
|
||||||
static struct wlr_primary_selection_offer *offer_from_resource(
|
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,
|
static void offer_handle_receive(struct wl_client *client,
|
||||||
struct wl_resource *resource, const char *mime_type, int32_t fd) {
|
struct wl_resource *resource, const char *mime_type, int32_t fd) {
|
||||||
struct wlr_primary_selection_offer *offer = offer_from_resource(resource);
|
struct wlr_primary_selection_offer *offer = offer_from_resource(resource);
|
||||||
|
if (offer == NULL) {
|
||||||
if (offer->source && offer == offer->source->offer) {
|
|
||||||
offer->source->send(offer->source, mime_type, fd);
|
|
||||||
} else {
|
|
||||||
close(fd);
|
close(fd);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offer->source->send(offer->source, mime_type, fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void offer_handle_destroy(struct wl_client *client,
|
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,
|
.destroy = offer_handle_destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void offer_resource_handle_destroy(struct wl_resource *resource) {
|
static void offer_destroy(struct wlr_primary_selection_offer *offer) {
|
||||||
struct wlr_primary_selection_offer *offer = offer_from_resource(resource);
|
if (offer == NULL) {
|
||||||
|
return;
|
||||||
if (!offer->source) {
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
// Make resource inert
|
||||||
|
wl_resource_set_user_data(offer->resource, NULL);
|
||||||
wl_list_remove(&offer->source_destroy.link);
|
wl_list_remove(&offer->source_destroy.link);
|
||||||
|
|
||||||
if (offer->source->offer != offer) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
offer->source->offer = NULL;
|
|
||||||
|
|
||||||
out:
|
|
||||||
free(offer);
|
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,
|
static void offer_handle_source_destroy(struct wl_listener *listener,
|
||||||
void *data) {
|
void *data) {
|
||||||
struct wlr_primary_selection_offer *offer =
|
struct wlr_primary_selection_offer *offer =
|
||||||
wl_container_of(listener, offer, source_destroy);
|
wl_container_of(listener, offer, source_destroy);
|
||||||
|
offer_destroy(offer);
|
||||||
offer->source = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,38 +82,32 @@ static void client_source_cancel(
|
||||||
gtk_primary_selection_source_send_cancelled(source->resource);
|
gtk_primary_selection_source_send_cancelled(source->resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_primary_selection_offer *source_send_offer(
|
static void source_send_offer(struct wlr_primary_selection_source *source,
|
||||||
struct wlr_primary_selection_source *source,
|
struct wl_resource *device_resource) {
|
||||||
struct wlr_seat_client *target) {
|
|
||||||
if (wl_list_empty(&target->primary_selection_devices)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_primary_selection_offer *offer =
|
struct wlr_primary_selection_offer *offer =
|
||||||
calloc(1, sizeof(struct wlr_primary_selection_offer));
|
calloc(1, sizeof(struct wlr_primary_selection_offer));
|
||||||
if (offer == NULL) {
|
if (offer == NULL) {
|
||||||
return NULL;
|
wl_resource_post_no_memory(device_resource);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t version = wl_resource_get_version(
|
struct wl_client *client = wl_resource_get_client(device_resource);
|
||||||
wl_resource_from_link(target->primary_selection_devices.next));
|
uint32_t version = wl_resource_get_version(device_resource);
|
||||||
offer->resource = wl_resource_create(target->client,
|
offer->resource = wl_resource_create(client,
|
||||||
>k_primary_selection_offer_interface, version, 0);
|
>k_primary_selection_offer_interface, version, 0);
|
||||||
if (offer->resource == NULL) {
|
if (offer->resource == NULL) {
|
||||||
free(offer);
|
free(offer);
|
||||||
return NULL;
|
wl_resource_post_no_memory(device_resource);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
wl_resource_set_implementation(offer->resource, &offer_impl, offer,
|
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;
|
offer->source_destroy.notify = offer_handle_source_destroy;
|
||||||
wl_signal_add(&source->events.destroy, &offer->source_destroy);
|
wl_signal_add(&source->events.destroy, &offer->source_destroy);
|
||||||
|
|
||||||
struct wl_resource *target_resource;
|
gtk_primary_selection_device_send_data_offer(device_resource,
|
||||||
wl_resource_for_each(target_resource, &target->primary_selection_devices) {
|
|
||||||
gtk_primary_selection_device_send_data_offer(target_resource,
|
|
||||||
offer->resource);
|
offer->resource);
|
||||||
}
|
|
||||||
|
|
||||||
char **p;
|
char **p;
|
||||||
wl_array_for_each(p, &source->mime_types) {
|
wl_array_for_each(p, &source->mime_types) {
|
||||||
|
@ -124,9 +115,9 @@ static struct wlr_primary_selection_offer *source_send_offer(
|
||||||
}
|
}
|
||||||
|
|
||||||
offer->source = source;
|
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;
|
static const struct gtk_primary_selection_source_interface source_impl;
|
||||||
|
@ -179,20 +170,13 @@ void wlr_seat_client_send_primary_selection(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seat_client->seat->primary_selection_source) {
|
struct wlr_primary_selection_source *source =
|
||||||
struct wlr_primary_selection_offer *offer = source_send_offer(
|
seat_client->seat->primary_selection_source;
|
||||||
seat_client->seat->primary_selection_source, seat_client);
|
|
||||||
if (offer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_resource *resource;
|
struct wl_resource *resource;
|
||||||
wl_resource_for_each(resource, &seat_client->primary_selection_devices) {
|
wl_resource_for_each(resource, &seat_client->primary_selection_devices) {
|
||||||
gtk_primary_selection_device_send_selection(resource, offer->resource);
|
if (source) {
|
||||||
}
|
source_send_offer(source, resource);
|
||||||
} else {
|
} else {
|
||||||
struct wl_resource *resource;
|
|
||||||
wl_resource_for_each(resource, &seat_client->primary_selection_devices) {
|
|
||||||
gtk_primary_selection_device_send_selection(resource, NULL);
|
gtk_primary_selection_device_send_selection(resource, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -401,8 +385,8 @@ struct wlr_primary_selection_device_manager *
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
manager->global = wl_global_create(display,
|
manager->global = wl_global_create(display,
|
||||||
>k_primary_selection_device_manager_interface, 1, manager,
|
>k_primary_selection_device_manager_interface, DEVICE_MANAGER_VERSION,
|
||||||
primary_selection_device_manager_bind);
|
manager, primary_selection_device_manager_bind);
|
||||||
if (manager->global == NULL) {
|
if (manager->global == NULL) {
|
||||||
free(manager);
|
free(manager);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
Loading…
Reference in New Issue