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 >k_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 >k_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 >k_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 >k_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 >k_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 >k_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 >k_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 >k_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 >k_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