Skip to content

Commit

Permalink
data-device: allow multiple devices for the same seat
Browse files Browse the repository at this point in the history
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 swaywm#1041
  • Loading branch information
emersion committed Nov 21, 2018
1 parent 040d62d commit 06b5a77
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 141 deletions.
3 changes: 2 additions & 1 deletion include/types/wlr_data_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ 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,
struct wl_list *resource_list);
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,
Expand Down
13 changes: 6 additions & 7 deletions include/wlr/types/wlr_data_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
#include <wayland-server.h>
#include <wlr/types/wlr_seat.h>

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;
Expand Down Expand Up @@ -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;
Expand Down
33 changes: 16 additions & 17 deletions types/data_device/wlr_data_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *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);
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;
}

wl_data_device_send_selection(device_resource, offer->resource);
} else {
wl_data_device_send_selection(device_resource, NULL);
}
}
}
Expand Down
123 changes: 66 additions & 57 deletions types/data_device/wlr_data_offer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
return;
}

wlr_data_source_send(offer->source, mime_type, fd);
}

static void data_offer_destroy(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_finish(struct wl_client *client,
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->source || offer->source->offer != offer) {
if (offer == NULL) {
return;
}

data_source_notify_finish(offer->source);
data_offer_dnd_finish(offer);
data_offer_destroy(offer);
}

static void data_offer_set_actions(struct wl_client *client,
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,
Expand All @@ -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,
Expand All @@ -200,16 +209,16 @@ 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;
}
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;
Expand Down
35 changes: 5 additions & 30 deletions types/data_device/wlr_data_source.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Loading

0 comments on commit 06b5a77

Please sign in to comment.