1 #define _POSIX_C_SOURCE 200809L
2 #include <assert.h>
3 #include <inttypes.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <wlr/types/wlr_gtk_primary_selection.h>
8 #include <wlr/types/wlr_primary_selection.h>
9 #include <wlr/types/wlr_seat.h>
10 #include <wlr/util/log.h>
11 #include "gtk-primary-selection-protocol.h"
12 #include "util/signal.h"
13 
14 #define DEVICE_MANAGER_VERSION 1
15 
16 static const struct gtk_primary_selection_offer_interface offer_impl;
17 
device_from_offer_resource(struct wl_resource * resource)18 static struct wlr_gtk_primary_selection_device *device_from_offer_resource(
19 		struct wl_resource *resource) {
20 	assert(wl_resource_instance_of(resource,
21 		&gtk_primary_selection_offer_interface, &offer_impl));
22 	return wl_resource_get_user_data(resource);
23 }
24 
offer_handle_receive(struct wl_client * client,struct wl_resource * resource,const char * mime_type,int32_t fd)25 static void offer_handle_receive(struct wl_client *client,
26 		struct wl_resource *resource, const char *mime_type, int32_t fd) {
27 	struct wlr_gtk_primary_selection_device *device =
28 		device_from_offer_resource(resource);
29 	if (device == NULL || device->seat->primary_selection_source == NULL) {
30 		close(fd);
31 		return;
32 	}
33 
34 	wlr_primary_selection_source_send(device->seat->primary_selection_source,
35 		mime_type, fd);
36 }
37 
offer_handle_destroy(struct wl_client * client,struct wl_resource * resource)38 static void offer_handle_destroy(struct wl_client *client,
39 		struct wl_resource *resource) {
40 	wl_resource_destroy(resource);
41 }
42 
43 static const struct gtk_primary_selection_offer_interface offer_impl = {
44 	.receive = offer_handle_receive,
45 	.destroy = offer_handle_destroy,
46 };
47 
offer_handle_resource_destroy(struct wl_resource * resource)48 static void offer_handle_resource_destroy(struct wl_resource *resource) {
49 	wl_list_remove(wl_resource_get_link(resource));
50 }
51 
52 static struct wlr_gtk_primary_selection_device *device_from_resource(
53 	struct wl_resource *resource);
54 
create_offer(struct wl_resource * device_resource,struct wlr_primary_selection_source * source)55 static void create_offer(struct wl_resource *device_resource,
56 		struct wlr_primary_selection_source *source) {
57 	struct wlr_gtk_primary_selection_device *device =
58 		device_from_resource(device_resource);
59 	assert(device != NULL);
60 
61 	struct wl_client *client = wl_resource_get_client(device_resource);
62 	uint32_t version = wl_resource_get_version(device_resource);
63 	struct wl_resource *resource = wl_resource_create(client,
64 		&gtk_primary_selection_offer_interface, version, 0);
65 	if (resource == NULL) {
66 		wl_resource_post_no_memory(device_resource);
67 		return;
68 	}
69 	wl_resource_set_implementation(resource, &offer_impl, device,
70 		offer_handle_resource_destroy);
71 
72 	wl_list_insert(&device->offers, wl_resource_get_link(resource));
73 
74 	gtk_primary_selection_device_send_data_offer(device_resource, resource);
75 
76 	char **p;
77 	wl_array_for_each(p, &source->mime_types) {
78 		gtk_primary_selection_offer_send_offer(resource, *p);
79 	}
80 
81 	gtk_primary_selection_device_send_selection(device_resource, resource);
82 }
83 
destroy_offer(struct wl_resource * resource)84 static void destroy_offer(struct wl_resource *resource) {
85 	if (device_from_offer_resource(resource) == NULL) {
86 		return;
87 	}
88 
89 	// Make the offer inert
90 	wl_resource_set_user_data(resource, NULL);
91 
92 	struct wl_list *link = wl_resource_get_link(resource);
93 	wl_list_remove(link);
94 	wl_list_init(link);
95 }
96 
97 
98 struct client_data_source {
99 	struct wlr_primary_selection_source source;
100 	struct wl_resource *resource;
101 	bool finalized;
102 };
103 
client_source_send(struct wlr_primary_selection_source * wlr_source,const char * mime_type,int fd)104 static void client_source_send(
105 		struct wlr_primary_selection_source *wlr_source,
106 		const char *mime_type, int fd) {
107 	struct client_data_source *source = (struct client_data_source *)wlr_source;
108 	gtk_primary_selection_source_send_send(source->resource, mime_type, fd);
109 	close(fd);
110 }
111 
client_source_destroy(struct wlr_primary_selection_source * wlr_source)112 static void client_source_destroy(
113 		struct wlr_primary_selection_source *wlr_source) {
114 	struct client_data_source *source = (struct client_data_source *)wlr_source;
115 	gtk_primary_selection_source_send_cancelled(source->resource);
116 	// Make the source resource inert
117 	wl_resource_set_user_data(source->resource, NULL);
118 	free(source);
119 }
120 
121 static const struct wlr_primary_selection_source_impl client_source_impl = {
122 	.send = client_source_send,
123 	.destroy = client_source_destroy,
124 };
125 
126 static const struct gtk_primary_selection_source_interface source_impl;
127 
client_data_source_from_resource(struct wl_resource * resource)128 static struct client_data_source *client_data_source_from_resource(
129 		struct wl_resource *resource) {
130 	assert(wl_resource_instance_of(resource,
131 		&gtk_primary_selection_source_interface, &source_impl));
132 	return wl_resource_get_user_data(resource);
133 }
134 
source_handle_offer(struct wl_client * client,struct wl_resource * resource,const char * mime_type)135 static void source_handle_offer(struct wl_client *client,
136 		struct wl_resource *resource, const char *mime_type) {
137 	struct client_data_source *source =
138 		client_data_source_from_resource(resource);
139 	if (source == NULL) {
140 		return;
141 	}
142 	if (source->finalized) {
143 		wlr_log(WLR_DEBUG, "Offering additional MIME type after set_selection");
144 	}
145 
146 	const char **mime_type_ptr;
147 	wl_array_for_each(mime_type_ptr, &source->source.mime_types) {
148 		if (strcmp(*mime_type_ptr, mime_type) == 0) {
149 			wlr_log(WLR_DEBUG, "Ignoring duplicate MIME type offer %s",
150 				mime_type);
151 			return;
152 		}
153 	}
154 
155 	char *dup_mime_type = strdup(mime_type);
156 	if (dup_mime_type == NULL) {
157 		wl_resource_post_no_memory(resource);
158 		return;
159 	}
160 
161 	char **p = wl_array_add(&source->source.mime_types, sizeof(*p));
162 	if (p == NULL) {
163 		free(dup_mime_type);
164 		wl_resource_post_no_memory(resource);
165 		return;
166 	}
167 
168 	*p = dup_mime_type;
169 }
170 
source_handle_destroy(struct wl_client * client,struct wl_resource * resource)171 static void source_handle_destroy(struct wl_client *client,
172 		struct wl_resource *resource) {
173 	wl_resource_destroy(resource);
174 }
175 
176 static const struct gtk_primary_selection_source_interface source_impl = {
177 	.offer = source_handle_offer,
178 	.destroy = source_handle_destroy,
179 };
180 
source_resource_handle_destroy(struct wl_resource * resource)181 static void source_resource_handle_destroy(struct wl_resource *resource) {
182 	struct client_data_source *source =
183 		client_data_source_from_resource(resource);
184 	if (source == NULL) {
185 		return;
186 	}
187 	wlr_primary_selection_source_destroy(&source->source);
188 }
189 
190 
191 static const struct gtk_primary_selection_device_interface device_impl;
192 
device_from_resource(struct wl_resource * resource)193 static struct wlr_gtk_primary_selection_device *device_from_resource(
194 		struct wl_resource *resource) {
195 	assert(wl_resource_instance_of(resource,
196 		&gtk_primary_selection_device_interface, &device_impl));
197 	return wl_resource_get_user_data(resource);
198 }
199 
device_handle_set_selection(struct wl_client * client,struct wl_resource * resource,struct wl_resource * source_resource,uint32_t serial)200 static void device_handle_set_selection(struct wl_client *client,
201 		struct wl_resource *resource, struct wl_resource *source_resource,
202 		uint32_t serial) {
203 	struct wlr_gtk_primary_selection_device *device =
204 		device_from_resource(resource);
205 	if (device == NULL) {
206 		return;
207 	}
208 
209 	struct client_data_source *client_source = NULL;
210 	if (source_resource != NULL) {
211 		client_source = client_data_source_from_resource(source_resource);
212 	}
213 
214 	struct wlr_primary_selection_source *source = NULL;
215 	if (client_source != NULL) {
216 		client_source->finalized = true;
217 		source = &client_source->source;
218 	}
219 
220 	struct wlr_seat_client *seat_client =
221 		wlr_seat_client_for_wl_client(device->seat, client);
222 
223 	wlr_seat_request_set_primary_selection(device->seat, seat_client, source, serial);
224 }
225 
device_handle_destroy(struct wl_client * client,struct wl_resource * resource)226 static void device_handle_destroy(struct wl_client *client,
227 		struct wl_resource *resource) {
228 	wl_resource_destroy(resource);
229 }
230 
231 static const struct gtk_primary_selection_device_interface device_impl = {
232 	.set_selection = device_handle_set_selection,
233 	.destroy = device_handle_destroy,
234 };
235 
device_handle_resource_destroy(struct wl_resource * resource)236 static void device_handle_resource_destroy(struct wl_resource *resource) {
237 	wl_list_remove(wl_resource_get_link(resource));
238 }
239 
240 
device_resource_send_selection(struct wl_resource * resource,struct wlr_primary_selection_source * source)241 static void device_resource_send_selection(struct wl_resource *resource,
242 		struct wlr_primary_selection_source *source) {
243 	assert(device_from_resource(resource) != NULL);
244 
245 	if (source != NULL) {
246 		create_offer(resource, source);
247 	} else {
248 		gtk_primary_selection_device_send_selection(resource, NULL);
249 	}
250 }
251 
device_send_selection(struct wlr_gtk_primary_selection_device * device)252 static void device_send_selection(
253 		struct wlr_gtk_primary_selection_device *device) {
254 	struct wlr_seat_client *seat_client =
255 		device->seat->keyboard_state.focused_client;
256 	if (seat_client == NULL) {
257 		return;
258 	}
259 
260 	struct wl_resource *resource;
261 	wl_resource_for_each(resource, &device->resources) {
262 		if (wl_resource_get_client(resource) == seat_client->client) {
263 			device_resource_send_selection(resource,
264 				device->seat->primary_selection_source);
265 		}
266 	}
267 }
268 
269 static void device_destroy(struct wlr_gtk_primary_selection_device *device);
270 
device_handle_seat_destroy(struct wl_listener * listener,void * data)271 static void device_handle_seat_destroy(struct wl_listener *listener,
272 		void *data) {
273 	struct wlr_gtk_primary_selection_device *device =
274 		wl_container_of(listener, device, seat_destroy);
275 	device_destroy(device);
276 }
277 
device_handle_seat_focus_change(struct wl_listener * listener,void * data)278 static void device_handle_seat_focus_change(struct wl_listener *listener,
279 		void *data) {
280 	struct wlr_gtk_primary_selection_device *device =
281 		wl_container_of(listener, device, seat_focus_change);
282 	// TODO: maybe make previous offers inert, or set a NULL selection for
283 	// previous client?
284 	device_send_selection(device);
285 }
286 
device_handle_seat_set_primary_selection(struct wl_listener * listener,void * data)287 static void device_handle_seat_set_primary_selection(
288 		struct wl_listener *listener, void *data) {
289 	struct wlr_gtk_primary_selection_device *device =
290 		wl_container_of(listener, device, seat_set_primary_selection);
291 
292 	struct wl_resource *resource, *tmp;
293 	wl_resource_for_each_safe(resource, tmp, &device->offers) {
294 		destroy_offer(resource);
295 	}
296 
297 	device_send_selection(device);
298 }
299 
get_or_create_device(struct wlr_gtk_primary_selection_device_manager * manager,struct wlr_seat * seat)300 static struct wlr_gtk_primary_selection_device *get_or_create_device(
301 		struct wlr_gtk_primary_selection_device_manager *manager,
302 		struct wlr_seat *seat) {
303 	struct wlr_gtk_primary_selection_device *device;
304 	wl_list_for_each(device, &manager->devices, link) {
305 		if (device->seat == seat) {
306 			return device;
307 		}
308 	}
309 
310 	device = calloc(1, sizeof(struct wlr_gtk_primary_selection_device));
311 	if (device == NULL) {
312 		return NULL;
313 	}
314 	device->manager = manager;
315 	device->seat = seat;
316 
317 	wl_list_init(&device->resources);
318 	wl_list_insert(&manager->devices, &device->link);
319 
320 	wl_list_init(&device->offers);
321 
322 	device->seat_destroy.notify = device_handle_seat_destroy;
323 	wl_signal_add(&seat->events.destroy, &device->seat_destroy);
324 
325 	device->seat_focus_change.notify = device_handle_seat_focus_change;
326 	wl_signal_add(&seat->keyboard_state.events.focus_change,
327 		&device->seat_focus_change);
328 
329 	device->seat_set_primary_selection.notify =
330 		device_handle_seat_set_primary_selection;
331 	wl_signal_add(&seat->events.set_primary_selection,
332 		&device->seat_set_primary_selection);
333 
334 	return device;
335 }
336 
device_destroy(struct wlr_gtk_primary_selection_device * device)337 static void device_destroy(struct wlr_gtk_primary_selection_device *device) {
338 	if (device == NULL) {
339 		return;
340 	}
341 	wl_list_remove(&device->link);
342 	wl_list_remove(&device->seat_destroy.link);
343 	wl_list_remove(&device->seat_focus_change.link);
344 	wl_list_remove(&device->seat_set_primary_selection.link);
345 	struct wl_resource *resource, *resource_tmp;
346 	wl_resource_for_each_safe(resource, resource_tmp, &device->offers) {
347 		destroy_offer(resource);
348 	}
349 	wl_resource_for_each_safe(resource, resource_tmp, &device->resources) {
350 		// Make the resource inert
351 		wl_resource_set_user_data(resource, NULL);
352 
353 		struct wl_list *link = wl_resource_get_link(resource);
354 		wl_list_remove(link);
355 		wl_list_init(link);
356 	}
357 	free(device);
358 }
359 
360 
361 static const struct gtk_primary_selection_device_manager_interface
362 	device_manager_impl;
363 
manager_from_resource(struct wl_resource * resource)364 static struct wlr_gtk_primary_selection_device_manager *manager_from_resource(
365 		struct wl_resource *resource) {
366 	assert(wl_resource_instance_of(resource,
367 		&gtk_primary_selection_device_manager_interface, &device_manager_impl));
368 	return wl_resource_get_user_data(resource);
369 }
370 
device_manager_handle_create_source(struct wl_client * client,struct wl_resource * manager_resource,uint32_t id)371 static void device_manager_handle_create_source(struct wl_client *client,
372 		struct wl_resource *manager_resource, uint32_t id) {
373 	struct client_data_source *source =
374 		calloc(1, sizeof(struct client_data_source));
375 	if (source == NULL) {
376 		wl_client_post_no_memory(client);
377 		return;
378 	}
379 	wlr_primary_selection_source_init(&source->source, &client_source_impl);
380 
381 	uint32_t version = wl_resource_get_version(manager_resource);
382 	source->resource = wl_resource_create(client,
383 		&gtk_primary_selection_source_interface, version, id);
384 	if (source->resource == NULL) {
385 		free(source);
386 		wl_client_post_no_memory(client);
387 		return;
388 	}
389 	wl_resource_set_implementation(source->resource, &source_impl, source,
390 		source_resource_handle_destroy);
391 }
392 
device_manager_handle_get_device(struct wl_client * client,struct wl_resource * manager_resource,uint32_t id,struct wl_resource * seat_resource)393 static void device_manager_handle_get_device(struct wl_client *client,
394 		struct wl_resource *manager_resource, uint32_t id,
395 		struct wl_resource *seat_resource) {
396 	struct wlr_seat_client *seat_client =
397 		wlr_seat_client_from_resource(seat_resource);
398 	struct wlr_gtk_primary_selection_device_manager *manager =
399 		manager_from_resource(manager_resource);
400 
401 	struct wlr_gtk_primary_selection_device *device =
402 		get_or_create_device(manager, seat_client->seat);
403 	if (device == NULL) {
404 		wl_resource_post_no_memory(manager_resource);
405 		return;
406 	}
407 
408 	uint32_t version = wl_resource_get_version(manager_resource);
409 	struct wl_resource *resource = wl_resource_create(client,
410 		&gtk_primary_selection_device_interface, version, id);
411 	if (resource == NULL) {
412 		wl_resource_post_no_memory(manager_resource);
413 		return;
414 	}
415 	wl_resource_set_implementation(resource, &device_impl, device,
416 		device_handle_resource_destroy);
417 	wl_list_insert(&device->resources, wl_resource_get_link(resource));
418 
419 	if (device->seat->keyboard_state.focused_client == seat_client) {
420 		device_resource_send_selection(resource,
421 			device->seat->primary_selection_source);
422 	}
423 }
424 
device_manager_handle_destroy(struct wl_client * client,struct wl_resource * manager_resource)425 static void device_manager_handle_destroy(struct wl_client *client,
426 		struct wl_resource *manager_resource) {
427 	wl_resource_destroy(manager_resource);
428 }
429 
430 static const struct gtk_primary_selection_device_manager_interface
431 		device_manager_impl = {
432 	.create_source = device_manager_handle_create_source,
433 	.get_device = device_manager_handle_get_device,
434 	.destroy = device_manager_handle_destroy,
435 };
436 
437 
primary_selection_device_manager_bind(struct wl_client * client,void * data,uint32_t version,uint32_t id)438 static void primary_selection_device_manager_bind(struct wl_client *client,
439 		void *data, uint32_t version, uint32_t id) {
440 	struct wlr_gtk_primary_selection_device_manager *manager = data;
441 
442 	struct wl_resource *resource = wl_resource_create(client,
443 		&gtk_primary_selection_device_manager_interface, version, id);
444 	if (resource == NULL) {
445 		wl_client_post_no_memory(client);
446 		return;
447 	}
448 	wl_resource_set_implementation(resource, &device_manager_impl, manager,
449 		NULL);
450 }
451 
handle_display_destroy(struct wl_listener * listener,void * data)452 static void handle_display_destroy(struct wl_listener *listener, void *data) {
453 	struct wlr_gtk_primary_selection_device_manager *manager =
454 		wl_container_of(listener, manager, display_destroy);
455 	wlr_signal_emit_safe(&manager->events.destroy, manager);
456 	wl_list_remove(&manager->display_destroy.link);
457 	wl_global_destroy(manager->global);
458 	free(manager);
459 }
460 
461 struct wlr_gtk_primary_selection_device_manager *
wlr_gtk_primary_selection_device_manager_create(struct wl_display * display)462 		wlr_gtk_primary_selection_device_manager_create(
463 		struct wl_display *display) {
464 	struct wlr_gtk_primary_selection_device_manager *manager =
465 		calloc(1, sizeof(struct wlr_gtk_primary_selection_device_manager));
466 	if (manager == NULL) {
467 		return NULL;
468 	}
469 	manager->global = wl_global_create(display,
470 		&gtk_primary_selection_device_manager_interface, DEVICE_MANAGER_VERSION,
471 		manager, primary_selection_device_manager_bind);
472 	if (manager->global == NULL) {
473 		free(manager);
474 		return NULL;
475 	}
476 
477 	wl_list_init(&manager->devices);
478 	wl_signal_init(&manager->events.destroy);
479 
480 	manager->display_destroy.notify = handle_display_destroy;
481 	wl_display_add_destroy_listener(display, &manager->display_destroy);
482 
483 	return manager;
484 }
485