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
This commit is contained in:
parent
eaa852ed77
commit
5d26da9d15
|
@ -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_offer *data_offer_create(struct wl_client *client,
|
||||||
struct wlr_data_source *source, uint32_t version);
|
struct wlr_data_source *source, uint32_t version);
|
||||||
void data_offer_update_action(struct wlr_data_offer *offer);
|
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 wlr_client_data_source *client_data_source_create(
|
||||||
struct wl_client *client, uint32_t version, uint32_t id,
|
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 wlr_client_data_source *client_data_source_from_resource(
|
||||||
struct wl_resource *resource);
|
struct wl_resource *resource);
|
||||||
struct wlr_data_offer *data_source_send_offer(struct wlr_data_source *source,
|
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);
|
void data_source_notify_finish(struct wlr_data_source *source);
|
||||||
|
|
||||||
bool seat_client_start_drag(struct wlr_seat_client *client,
|
bool seat_client_start_drag(struct wlr_seat_client *client,
|
||||||
|
|
|
@ -12,14 +12,14 @@
|
||||||
#include <wayland-server.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/types/wlr_seat.h>
|
#include <wlr/types/wlr_seat.h>
|
||||||
|
|
||||||
extern const struct
|
extern const struct wlr_pointer_grab_interface
|
||||||
wlr_pointer_grab_interface wlr_data_device_pointer_drag_interface;
|
wlr_data_device_pointer_drag_interface;
|
||||||
|
|
||||||
extern const struct
|
extern const struct wlr_keyboard_grab_interface
|
||||||
wlr_keyboard_grab_interface wlr_data_device_keyboard_drag_interface;
|
wlr_data_device_keyboard_drag_interface;
|
||||||
|
|
||||||
extern const struct
|
extern const struct wlr_touch_grab_interface
|
||||||
wlr_touch_grab_interface wlr_data_device_touch_drag_interface;
|
wlr_data_device_touch_drag_interface;
|
||||||
|
|
||||||
struct wlr_data_device_manager {
|
struct wlr_data_device_manager {
|
||||||
struct wl_global *global;
|
struct wl_global *global;
|
||||||
|
@ -72,7 +72,6 @@ struct wlr_data_source {
|
||||||
|
|
||||||
// source status
|
// source status
|
||||||
bool accepted;
|
bool accepted;
|
||||||
struct wlr_data_offer *offer;
|
|
||||||
|
|
||||||
// drag'n'drop status
|
// drag'n'drop status
|
||||||
enum wl_data_device_manager_dnd_action current_dnd_action;
|
enum wl_data_device_manager_dnd_action current_dnd_action;
|
||||||
|
|
|
@ -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) {
|
void wlr_seat_client_send_selection(struct wlr_seat_client *seat_client) {
|
||||||
if (wl_list_empty(&seat_client->data_devices)) {
|
struct wlr_data_source *source = seat_client->seat->selection_source;
|
||||||
return;
|
if (source != NULL) {
|
||||||
|
source->accepted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seat_client->seat->selection_source) {
|
struct wl_resource *device_resource;
|
||||||
struct wlr_data_offer *offer = data_source_send_offer(
|
wl_resource_for_each(device_resource, &seat_client->data_devices) {
|
||||||
seat_client->seat->selection_source, seat_client);
|
if (source != NULL) {
|
||||||
if (offer == NULL) {
|
struct wlr_data_offer *offer =
|
||||||
return;
|
data_source_send_offer(source, device_resource);
|
||||||
}
|
if (offer == NULL) {
|
||||||
|
wl_client_post_no_memory(seat_client->client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct wl_resource *resource;
|
wl_data_device_send_selection(device_resource, offer->resource);
|
||||||
wl_resource_for_each(resource, &seat_client->data_devices) {
|
} else {
|
||||||
wl_data_device_send_selection(resource, offer->resource);
|
wl_data_device_send_selection(device_resource, NULL);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
struct wl_resource *resource;
|
|
||||||
wl_resource_for_each(resource, &seat_client->data_devices) {
|
|
||||||
wl_data_device_send_selection(resource, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
void data_offer_update_action(struct wlr_data_offer *offer) {
|
||||||
if (!offer->source) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t action = data_offer_choose_action(offer);
|
uint32_t action = data_offer_choose_action(offer);
|
||||||
if (offer->source->current_dnd_action == action) {
|
if (offer->source->current_dnd_action == action) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
offer->source->current_dnd_action = action;
|
offer->source->current_dnd_action = action;
|
||||||
|
|
||||||
if (offer->in_ask) {
|
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 wl_resource *resource, uint32_t serial, const char *mime_type) {
|
||||||
struct wlr_data_offer *offer = data_offer_from_resource(resource);
|
struct wlr_data_offer *offer = data_offer_from_resource(resource);
|
||||||
|
if (offer == NULL) {
|
||||||
if (!offer->source || offer != offer->source->offer) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO check that client is currently focused by the input device
|
|
||||||
|
|
||||||
wlr_data_source_accept(offer->source, serial, mime_type);
|
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 wl_resource *resource, const char *mime_type, int32_t fd) {
|
||||||
struct wlr_data_offer *offer = data_offer_from_resource(resource);
|
struct wlr_data_offer *offer = data_offer_from_resource(resource);
|
||||||
|
if (offer == NULL) {
|
||||||
if (offer->source && offer == offer->source->offer) {
|
|
||||||
wlr_data_source_send(offer->source, mime_type, fd);
|
|
||||||
} else {
|
|
||||||
close(fd);
|
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;
|
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,
|
struct wl_resource *resource, uint32_t actions,
|
||||||
uint32_t preferred_action) {
|
uint32_t preferred_action) {
|
||||||
struct wlr_data_offer *offer = data_offer_from_resource(resource);
|
struct wlr_data_offer *offer = data_offer_from_resource(resource);
|
||||||
|
if (offer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (actions & ~DATA_DEVICE_ALL_ACTIONS) {
|
if (actions & ~DATA_DEVICE_ALL_ACTIONS) {
|
||||||
wl_resource_post_error(offer->resource,
|
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);
|
data_offer_update_action(offer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void data_offer_handle_resource_destroy(struct wl_resource *resource) {
|
void data_offer_destroy(struct wlr_data_offer *offer) {
|
||||||
struct wlr_data_offer *offer = data_offer_from_resource(resource);
|
if (offer == NULL) {
|
||||||
|
return;
|
||||||
if (!offer->source) {
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_list_remove(&offer->source_destroy.link);
|
wl_list_remove(&offer->source_destroy.link);
|
||||||
|
|
||||||
if (offer->source->offer != offer) {
|
// Make the resource inert
|
||||||
goto out;
|
wl_resource_set_user_data(offer->resource, NULL);
|
||||||
}
|
|
||||||
|
|
||||||
// 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:
|
|
||||||
free(offer);
|
free(offer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_data_offer_interface data_offer_impl = {
|
static const struct wl_data_offer_interface data_offer_impl = {
|
||||||
.accept = data_offer_accept,
|
.accept = data_offer_handle_accept,
|
||||||
.receive = data_offer_receive,
|
.receive = data_offer_handle_receive,
|
||||||
.destroy = data_offer_destroy,
|
.destroy = data_offer_handle_destroy,
|
||||||
.finish = data_offer_finish,
|
.finish = data_offer_handle_finish,
|
||||||
.set_actions = data_offer_set_actions,
|
.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) {
|
void *data) {
|
||||||
struct wlr_data_offer *offer =
|
struct wlr_data_offer *offer =
|
||||||
wl_container_of(listener, offer, source_destroy);
|
wl_container_of(listener, offer, source_destroy);
|
||||||
|
data_offer_destroy(offer);
|
||||||
offer->source = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_data_offer *data_offer_create(struct wl_client *client,
|
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->source = source;
|
||||||
|
|
||||||
offer->resource = wl_resource_create(client,
|
offer->resource =
|
||||||
&wl_data_offer_interface, version, 0);
|
wl_resource_create(client, &wl_data_offer_interface, version, 0);
|
||||||
if (offer->resource == NULL) {
|
if (offer->resource == NULL) {
|
||||||
free(offer);
|
free(offer);
|
||||||
return NULL;
|
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,
|
wl_resource_set_implementation(offer->resource, &data_offer_impl, offer,
|
||||||
data_offer_handle_resource_destroy);
|
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);
|
wl_signal_add(&source->events.destroy, &offer->source_destroy);
|
||||||
|
|
||||||
return offer;
|
return offer;
|
||||||
|
|
|
@ -11,47 +11,22 @@
|
||||||
#include "types/wlr_data_device.h"
|
#include "types/wlr_data_device.h"
|
||||||
#include "util/signal.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_data_offer *data_source_send_offer(struct wlr_data_source *source,
|
||||||
struct wlr_seat_client *target) {
|
struct wl_resource *device_resource) {
|
||||||
if (wl_list_empty(&target->data_devices)) {
|
struct wl_client *client = wl_resource_get_client(device_resource);
|
||||||
return NULL;
|
uint32_t version = wl_resource_get_version(device_resource);
|
||||||
}
|
struct wlr_data_offer *offer = data_offer_create(client, source, version);
|
||||||
|
|
||||||
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);
|
|
||||||
if (offer == NULL) {
|
if (offer == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wl_resource *target_resource;
|
wl_data_device_send_data_offer(device_resource, offer->resource);
|
||||||
wl_resource_for_each(target_resource, &target->data_devices) {
|
|
||||||
wl_data_device_send_data_offer(target_resource, offer->resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
char **p;
|
char **p;
|
||||||
wl_array_for_each(p, &source->mime_types) {
|
wl_array_for_each(p, &source->mime_types) {
|
||||||
wl_data_offer_send_offer(offer->resource, *p);
|
wl_data_offer_send_offer(offer->resource, *p);
|
||||||
}
|
}
|
||||||
|
|
||||||
source->offer = offer;
|
|
||||||
source->accepted = false;
|
|
||||||
return offer;
|
return offer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,25 +47,27 @@ static void drag_set_focus(struct wlr_drag *drag,
|
||||||
return;
|
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(
|
struct wlr_seat_client *focus_client = wlr_seat_client_for_wl_client(
|
||||||
drag->seat_client->seat, wl_resource_get_client(surface->resource));
|
drag->seat_client->seat, wl_resource_get_client(surface->resource));
|
||||||
if (!focus_client) {
|
if (!focus_client) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wl_resource *offer_resource = NULL;
|
if (drag->source != NULL) {
|
||||||
if (drag->source) {
|
|
||||||
drag->source->accepted = false;
|
drag->source->accepted = false;
|
||||||
struct wlr_data_offer *offer = data_source_send_offer(drag->source,
|
|
||||||
focus_client);
|
uint32_t serial =
|
||||||
if (offer != NULL) {
|
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);
|
data_offer_update_action(offer);
|
||||||
|
|
||||||
if (wl_resource_get_version(offer->resource) >=
|
if (wl_resource_get_version(offer->resource) >=
|
||||||
|
@ -74,18 +76,10 @@ static void drag_set_focus(struct wlr_drag *drag,
|
||||||
drag->source->actions);
|
drag->source->actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
offer_resource = offer->resource;
|
wl_data_device_send_enter(device_resource, serial,
|
||||||
}
|
surface->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_fixed_from_double(sx), wl_fixed_from_double(sy),
|
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);
|
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 = {
|
struct wlr_drag_drop_event event = {
|
||||||
.drag = drag,
|
.drag = drag,
|
||||||
.time = time,
|
.time = time,
|
||||||
|
|
Loading…
Reference in New Issue