From 5d26da9d15d4874c383bbc9948a802fcd605dc8b Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 21 Nov 2018 21:32:54 +0100 Subject: [PATCH] data-device: allow multiple devices for the same seat This commit makes it possible for a single client to have multiple data devices for the same seat. This fixes issues with Firefox. This mainly removes wlr_data_source.offer. We make sure we create one data offer per device. We now make the offer inert when the source is destroyed. Fixes the second half of https://github.com/swaywm/wlroots/issues/1041 --- include/types/wlr_data_device.h | 3 +- include/wlr/types/wlr_data_device.h | 13 ++- types/data_device/wlr_data_device.c | 31 +++--- types/data_device/wlr_data_offer.c | 141 +++++++++++++++------------- types/data_device/wlr_data_source.c | 35 +------ types/data_device/wlr_drag.c | 46 ++++----- 6 files changed, 120 insertions(+), 149 deletions(-) diff --git a/include/types/wlr_data_device.h b/include/types/wlr_data_device.h index 388e91a5..376c5f09 100644 --- a/include/types/wlr_data_device.h +++ b/include/types/wlr_data_device.h @@ -19,6 +19,7 @@ extern const struct wlr_surface_role drag_icon_surface_role; struct wlr_data_offer *data_offer_create(struct wl_client *client, struct wlr_data_source *source, uint32_t version); void data_offer_update_action(struct wlr_data_offer *offer); +void data_offer_destroy(struct wlr_data_offer *offer); struct wlr_client_data_source *client_data_source_create( struct wl_client *client, uint32_t version, uint32_t id, @@ -26,7 +27,7 @@ struct wlr_client_data_source *client_data_source_create( struct wlr_client_data_source *client_data_source_from_resource( struct wl_resource *resource); struct wlr_data_offer *data_source_send_offer(struct wlr_data_source *source, - struct wlr_seat_client *target); + struct wl_resource *device_resource); void data_source_notify_finish(struct wlr_data_source *source); bool seat_client_start_drag(struct wlr_seat_client *client, diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index 9ce8f400..9c4ce995 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -12,14 +12,14 @@ #include #include -extern const struct -wlr_pointer_grab_interface wlr_data_device_pointer_drag_interface; +extern const struct wlr_pointer_grab_interface + wlr_data_device_pointer_drag_interface; -extern const struct -wlr_keyboard_grab_interface wlr_data_device_keyboard_drag_interface; +extern const struct wlr_keyboard_grab_interface + wlr_data_device_keyboard_drag_interface; -extern const struct -wlr_touch_grab_interface wlr_data_device_touch_drag_interface; +extern const struct wlr_touch_grab_interface + wlr_data_device_touch_drag_interface; struct wlr_data_device_manager { struct wl_global *global; @@ -72,7 +72,6 @@ struct wlr_data_source { // source status bool accepted; - struct wlr_data_offer *offer; // drag'n'drop status enum wl_data_device_manager_dnd_action current_dnd_action; diff --git a/types/data_device/wlr_data_device.c b/types/data_device/wlr_data_device.c index a50f0b4a..f868ea37 100644 --- a/types/data_device/wlr_data_device.c +++ b/types/data_device/wlr_data_device.c @@ -94,25 +94,24 @@ static void data_device_handle_resource_destroy(struct wl_resource *resource) { void wlr_seat_client_send_selection(struct wlr_seat_client *seat_client) { - if (wl_list_empty(&seat_client->data_devices)) { - return; + struct wlr_data_source *source = seat_client->seat->selection_source; + if (source != NULL) { + source->accepted = false; } - if (seat_client->seat->selection_source) { - struct wlr_data_offer *offer = data_source_send_offer( - seat_client->seat->selection_source, seat_client); - if (offer == NULL) { - return; - } + struct wl_resource *device_resource; + wl_resource_for_each(device_resource, &seat_client->data_devices) { + if (source != NULL) { + struct wlr_data_offer *offer = + data_source_send_offer(source, device_resource); + if (offer == NULL) { + wl_client_post_no_memory(seat_client->client); + return; + } - struct wl_resource *resource; - wl_resource_for_each(resource, &seat_client->data_devices) { - wl_data_device_send_selection(resource, offer->resource); - } - } else { - struct wl_resource *resource; - wl_resource_for_each(resource, &seat_client->data_devices) { - wl_data_device_send_selection(resource, NULL); + wl_data_device_send_selection(device_resource, offer->resource); + } else { + wl_data_device_send_selection(device_resource, NULL); } } } diff --git a/types/data_device/wlr_data_offer.c b/types/data_device/wlr_data_offer.c index 9847e07c..6135ffe3 100644 --- a/types/data_device/wlr_data_offer.c +++ b/types/data_device/wlr_data_offer.c @@ -55,15 +55,10 @@ static uint32_t data_offer_choose_action(struct wlr_data_offer *offer) { } void data_offer_update_action(struct wlr_data_offer *offer) { - if (!offer->source) { - return; - } - uint32_t action = data_offer_choose_action(offer); if (offer->source->current_dnd_action == action) { return; } - offer->source->current_dnd_action = action; if (offer->in_ask) { @@ -78,50 +73,80 @@ void data_offer_update_action(struct wlr_data_offer *offer) { } } -static void data_offer_accept(struct wl_client *client, +static void data_offer_handle_accept(struct wl_client *client, struct wl_resource *resource, uint32_t serial, const char *mime_type) { struct wlr_data_offer *offer = data_offer_from_resource(resource); - - if (!offer->source || offer != offer->source->offer) { + if (offer == NULL) { return; } - // TODO check that client is currently focused by the input device - wlr_data_source_accept(offer->source, serial, mime_type); } -static void data_offer_receive(struct wl_client *client, +static void data_offer_handle_receive(struct wl_client *client, struct wl_resource *resource, const char *mime_type, int32_t fd) { struct wlr_data_offer *offer = data_offer_from_resource(resource); - - if (offer->source && offer == offer->source->offer) { - wlr_data_source_send(offer->source, mime_type, fd); - } else { + if (offer == NULL) { close(fd); - } -} - -static void data_offer_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void data_offer_finish(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_data_offer *offer = data_offer_from_resource(resource); - - if (!offer->source || offer->source->offer != offer) { return; } - data_source_notify_finish(offer->source); + wlr_data_source_send(offer->source, mime_type, fd); } -static void data_offer_set_actions(struct wl_client *client, +static void data_offer_dnd_finish(struct wlr_data_offer *offer) { + struct wlr_data_source *source = offer->source; + if (source->actions < 0) { + return; + } + + if (offer->in_ask) { + wlr_data_source_dnd_action(source, source->current_dnd_action); + } + + wlr_data_source_dnd_finish(source); +} + +static void data_offer_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_data_offer *offer = data_offer_from_resource(resource); + if (offer == NULL) { + goto out; + } + + // If the drag destination has version < 3, wl_data_offer.finish + // won't be called, so do this here as a safety net, because + // we still want the version >= 3 drag source to be happy. + if (wl_resource_get_version(offer->resource) < + WL_DATA_OFFER_ACTION_SINCE_VERSION) { + data_offer_dnd_finish(offer); + } else if (offer->source->impl->dnd_finish) { + // wlr_data_source_cancel can free the source + wlr_data_source_cancel(offer->source); + } + +out: + wl_resource_destroy(resource); +} + +static void data_offer_handle_finish(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_data_offer *offer = data_offer_from_resource(resource); + if (offer == NULL) { + return; + } + + data_offer_dnd_finish(offer); + data_offer_destroy(offer); +} + +static void data_offer_handle_set_actions(struct wl_client *client, struct wl_resource *resource, uint32_t actions, uint32_t preferred_action) { struct wlr_data_offer *offer = data_offer_from_resource(resource); + if (offer == NULL) { + return; + } if (actions & ~DATA_DEVICE_ALL_ACTIONS) { wl_resource_post_error(offer->resource, @@ -144,52 +169,36 @@ static void data_offer_set_actions(struct wl_client *client, data_offer_update_action(offer); } -static void data_offer_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_data_offer *offer = data_offer_from_resource(resource); - - if (!offer->source) { - goto out; +void data_offer_destroy(struct wlr_data_offer *offer) { + if (offer == NULL) { + return; } wl_list_remove(&offer->source_destroy.link); - if (offer->source->offer != offer) { - goto out; - } - - // If the drag destination has version < 3, wl_data_offer.finish - // won't be called, so do this here as a safety net, because - // we still want the version >= 3 drag source to be happy. - if (wl_resource_get_version(offer->resource) < - WL_DATA_OFFER_ACTION_SINCE_VERSION) { - data_source_notify_finish(offer->source); - offer->source->offer = NULL; - } else if (offer->source->impl->dnd_finish) { - // source->cancel can free the source - offer->source->offer = NULL; - wlr_data_source_cancel(offer->source); - } else { - offer->source->offer = NULL; - } - -out: + // Make the resource inert + wl_resource_set_user_data(offer->resource, NULL); free(offer); } static const struct wl_data_offer_interface data_offer_impl = { - .accept = data_offer_accept, - .receive = data_offer_receive, - .destroy = data_offer_destroy, - .finish = data_offer_finish, - .set_actions = data_offer_set_actions, + .accept = data_offer_handle_accept, + .receive = data_offer_handle_receive, + .destroy = data_offer_handle_destroy, + .finish = data_offer_handle_finish, + .set_actions = data_offer_handle_set_actions, }; -static void handle_offer_source_destroyed(struct wl_listener *listener, +static void data_offer_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_data_offer *offer = data_offer_from_resource(resource); + data_offer_destroy(offer); +} + +static void data_offer_handle_source_destroy(struct wl_listener *listener, void *data) { struct wlr_data_offer *offer = wl_container_of(listener, offer, source_destroy); - - offer->source = NULL; + data_offer_destroy(offer); } struct wlr_data_offer *data_offer_create(struct wl_client *client, @@ -200,8 +209,8 @@ struct wlr_data_offer *data_offer_create(struct wl_client *client, } offer->source = source; - offer->resource = wl_resource_create(client, - &wl_data_offer_interface, version, 0); + offer->resource = + wl_resource_create(client, &wl_data_offer_interface, version, 0); if (offer->resource == NULL) { free(offer); return NULL; @@ -209,7 +218,7 @@ struct wlr_data_offer *data_offer_create(struct wl_client *client, wl_resource_set_implementation(offer->resource, &data_offer_impl, offer, data_offer_handle_resource_destroy); - offer->source_destroy.notify = handle_offer_source_destroyed; + offer->source_destroy.notify = data_offer_handle_source_destroy; wl_signal_add(&source->events.destroy, &offer->source_destroy); return offer; diff --git a/types/data_device/wlr_data_source.c b/types/data_device/wlr_data_source.c index 64db3a70..413f461a 100644 --- a/types/data_device/wlr_data_source.c +++ b/types/data_device/wlr_data_source.c @@ -11,47 +11,22 @@ #include "types/wlr_data_device.h" #include "util/signal.h" -void data_source_notify_finish(struct wlr_data_source *source) { - assert(source->offer); - if (source->actions < 0) { - return; - } - - if (source->offer->in_ask) { - wlr_data_source_dnd_action(source, source->current_dnd_action); - } - - source->offer = NULL; - wlr_data_source_dnd_finish(source); -} - struct wlr_data_offer *data_source_send_offer(struct wlr_data_source *source, - struct wlr_seat_client *target) { - if (wl_list_empty(&target->data_devices)) { - return NULL; - } - - uint32_t version = wl_resource_get_version( - wl_resource_from_link(target->data_devices.next)); - - struct wlr_data_offer *offer = - data_offer_create(target->client, source, version); + struct wl_resource *device_resource) { + struct wl_client *client = wl_resource_get_client(device_resource); + uint32_t version = wl_resource_get_version(device_resource); + struct wlr_data_offer *offer = data_offer_create(client, source, version); if (offer == NULL) { return NULL; } - struct wl_resource *target_resource; - wl_resource_for_each(target_resource, &target->data_devices) { - wl_data_device_send_data_offer(target_resource, offer->resource); - } + wl_data_device_send_data_offer(device_resource, offer->resource); char **p; wl_array_for_each(p, &source->mime_types) { wl_data_offer_send_offer(offer->resource, *p); } - source->offer = offer; - source->accepted = false; return offer; } diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index 8ed6e034..8e737597 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -47,25 +47,27 @@ static void drag_set_focus(struct wlr_drag *drag, return; } - if (drag->source && drag->source->offer) { - // unlink the offer from the source - wl_list_remove(&drag->source->offer->source_destroy.link); - drag->source->offer->source = NULL; - drag->source->offer = NULL; - } - struct wlr_seat_client *focus_client = wlr_seat_client_for_wl_client( drag->seat_client->seat, wl_resource_get_client(surface->resource)); if (!focus_client) { return; } - struct wl_resource *offer_resource = NULL; - if (drag->source) { + if (drag->source != NULL) { drag->source->accepted = false; - struct wlr_data_offer *offer = data_source_send_offer(drag->source, - focus_client); - if (offer != NULL) { + + uint32_t serial = + wl_display_next_serial(drag->seat_client->seat->display); + + struct wl_resource *device_resource; + wl_resource_for_each(device_resource, &focus_client->data_devices) { + struct wlr_data_offer *offer = + data_source_send_offer(drag->source, device_resource); + if (offer == NULL) { + wl_resource_post_no_memory(device_resource); + return; + } + data_offer_update_action(offer); if (wl_resource_get_version(offer->resource) >= @@ -74,18 +76,10 @@ static void drag_set_focus(struct wlr_drag *drag, drag->source->actions); } - offer_resource = offer->resource; - } - } - - if (!wl_list_empty(&focus_client->data_devices)) { - uint32_t serial = - wl_display_next_serial(drag->seat_client->seat->display); - struct wl_resource *resource; - wl_resource_for_each(resource, &focus_client->data_devices) { - wl_data_device_send_enter(resource, serial, surface->resource, + wl_data_device_send_enter(device_resource, serial, + surface->resource, wl_fixed_from_double(sx), wl_fixed_from_double(sy), - offer_resource); + offer->resource); } } @@ -174,12 +168,6 @@ static uint32_t drag_handle_pointer_button(struct wlr_seat_pointer_grab *grab, } wlr_data_source_dnd_drop(drag->source); - if (drag->source->offer != NULL) { - drag->source->offer->in_ask = - drag->source->current_dnd_action == - WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; - } - struct wlr_drag_drop_event event = { .drag = drag, .time = time,