jpokorny / rpms / wlroots

Forked from rpms/wlroots 5 years ago
Clone
Blob Blame History Raw
From 5d26da9d15d4874c383bbc9948a802fcd605dc8b Mon Sep 17 00:00:00 2001
From: emersion <contact@emersion.fr>
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 |  33 ++++----
 types/data_device/wlr_data_offer.c  | 123 +++++++++++++++-------------
 types/data_device/wlr_data_source.c |  35 ++------
 types/data_device/wlr_drag.c        |  46 ++++-------
 6 files changed, 112 insertions(+), 141 deletions(-)

diff --git a/include/types/wlr_data_device.h b/include/types/wlr_data_device.h
index 388e91a5e..376c5f09f 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 9ce8f400f..9c4ce995e 100644
--- a/include/wlr/types/wlr_data_device.h
+++ b/include/wlr/types/wlr_data_device.h
@@ -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;
@@ -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 a50f0b4ae..f868ea370 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 *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);
 		}
 	}
 }
diff --git a/types/data_device/wlr_data_offer.c b/types/data_device/wlr_data_offer.c
index 9847e07c2..6135ffe33 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);
+		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,
@@ -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 64db3a706..413f461a2 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 8ed6e034d..8e737597b 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,