aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/wlr/types/wlr_data_device.h2
-rw-r--r--types/data_device/wlr_data_offer.c28
-rw-r--r--types/data_device/wlr_drag.c60
3 files changed, 56 insertions, 34 deletions
diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h
index 9da7cc0d..256654e5 100644
--- a/include/wlr/types/wlr_data_device.h
+++ b/include/wlr/types/wlr_data_device.h
@@ -127,7 +127,7 @@ struct wlr_drag {
struct wlr_surface *focus; // can be NULL
struct wlr_data_source *source; // can be NULL
- bool started, cancelling;
+ bool started, dropped, cancelling;
int32_t grab_touch_id, touch_id; // if WLR_DRAG_GRAB_TOUCH
struct {
diff --git a/types/data_device/wlr_data_offer.c b/types/data_device/wlr_data_offer.c
index 26b894ea..086feb11 100644
--- a/types/data_device/wlr_data_offer.c
+++ b/types/data_device/wlr_data_offer.c
@@ -111,20 +111,6 @@ static void data_offer_dnd_finish(struct wlr_data_offer *offer) {
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);
- }
-
-out:
wl_resource_destroy(resource);
}
@@ -204,6 +190,18 @@ void data_offer_destroy(struct wlr_data_offer *offer) {
wl_list_remove(&offer->source_destroy.link);
wl_list_remove(&offer->link);
+ if (offer->type == WLR_DATA_OFFER_DRAG) {
+ // 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 && offer->source->impl->dnd_finish) {
+ wlr_data_source_destroy(offer->source);
+ }
+ }
+
// Make the resource inert
wl_resource_set_user_data(offer->resource, NULL);
@@ -227,6 +225,8 @@ 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);
+ // Prevent data_offer_destroy from destroying the source again
+ offer->source = NULL;
data_offer_destroy(offer);
}
diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c
index bfdc04aa..ab0005d3 100644
--- a/types/data_device/wlr_drag.c
+++ b/types/data_device/wlr_drag.c
@@ -28,6 +28,20 @@ static void drag_set_focus(struct wlr_drag *drag,
if (drag->focus_client) {
wl_list_remove(&drag->seat_client_destroy.link);
+ // If we're switching focus to another client, we want to destroy all
+ // offers without destroying the source. If the drag operation ends, we
+ // want to keep the offer around for the data transfer.
+ struct wlr_data_offer *offer, *tmp;
+ wl_list_for_each_safe(offer, tmp,
+ &drag->focus_client->seat->drag_offers, link) {
+ struct wl_client *client = wl_resource_get_client(offer->resource);
+ if (!drag->dropped && offer->source == drag->source &&
+ client == drag->focus_client->client) {
+ offer->source = NULL;
+ data_offer_destroy(offer);
+ }
+ }
+
struct wl_resource *resource;
wl_resource_for_each(resource, &drag->focus_client->data_devices) {
wl_data_device_send_leave(resource);
@@ -37,20 +51,20 @@ static void drag_set_focus(struct wlr_drag *drag,
drag->focus = NULL;
}
- if (!surface || !surface->resource) {
- return;
+ if (!surface) {
+ goto out;
}
if (!drag->source &&
wl_resource_get_client(surface->resource) !=
drag->seat_client->client) {
- return;
+ goto out;
}
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;
+ goto out;
}
if (drag->source != NULL) {
@@ -88,6 +102,7 @@ static void drag_set_focus(struct wlr_drag *drag,
drag->seat_client_destroy.notify = drag_handle_seat_client_destroy;
wl_signal_add(&focus_client->events.destroy, &drag->seat_client_destroy);
+out:
wlr_signal_emit_safe(&drag->events.focus, drag);
}
@@ -164,6 +179,26 @@ static void drag_handle_pointer_motion(struct wlr_seat_pointer_grab *grab,
}
}
+static void drag_drop(struct wlr_drag *drag, uint32_t time) {
+ assert(drag->focus_client);
+
+ drag->dropped = true;
+
+ struct wl_resource *resource;
+ wl_resource_for_each(resource, &drag->focus_client->data_devices) {
+ wl_data_device_send_drop(resource);
+ }
+ if (drag->source) {
+ wlr_data_source_dnd_drop(drag->source);
+ }
+
+ struct wlr_drag_drop_event event = {
+ .drag = drag,
+ .time = time,
+ };
+ wlr_signal_emit_safe(&drag->events.drop, &event);
+}
+
static uint32_t drag_handle_pointer_button(struct wlr_seat_pointer_grab *grab,
uint32_t time, uint32_t button, uint32_t state) {
struct wlr_drag *drag = grab->data;
@@ -173,17 +208,7 @@ static uint32_t drag_handle_pointer_button(struct wlr_seat_pointer_grab *grab,
state == WL_POINTER_BUTTON_STATE_RELEASED) {
if (drag->focus_client && drag->source->current_dnd_action &&
drag->source->accepted) {
- struct wl_resource *resource;
- wl_resource_for_each(resource, &drag->focus_client->data_devices) {
- wl_data_device_send_drop(resource);
- }
- wlr_data_source_dnd_drop(drag->source);
-
- struct wlr_drag_drop_event event = {
- .drag = drag,
- .time = time,
- };
- wlr_signal_emit_safe(&drag->events.drop, &event);
+ drag_drop(drag, time);
} else if (drag->source->impl->dnd_finish) {
// This will end the grab and free `drag`
wlr_data_source_destroy(drag->source);
@@ -233,10 +258,7 @@ static void drag_handle_touch_up(struct wlr_seat_touch_grab *grab,
}
if (drag->focus_client) {
- struct wl_resource *resource;
- wl_resource_for_each(resource, &drag->focus_client->data_devices) {
- wl_data_device_send_drop(resource);
- }
+ drag_drop(drag, time);
}
drag_destroy(drag);