1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <strings.h>
5 #include <unistd.h>
6 #include <wayland-server-core.h>
7 #include <wlr/types/wlr_data_device.h>
8 #include <wlr/types/wlr_seat.h>
9 #include <wlr/util/log.h>
10 #include "types/wlr_data_device.h"
11 #include "util/signal.h"
12
drag_handle_seat_client_destroy(struct wl_listener * listener,void * data)13 static void drag_handle_seat_client_destroy(struct wl_listener *listener,
14 void *data) {
15 struct wlr_drag *drag =
16 wl_container_of(listener, drag, seat_client_destroy);
17
18 drag->focus_client = NULL;
19 wl_list_remove(&drag->seat_client_destroy.link);
20 }
21
drag_set_focus(struct wlr_drag * drag,struct wlr_surface * surface,double sx,double sy)22 static void drag_set_focus(struct wlr_drag *drag,
23 struct wlr_surface *surface, double sx, double sy) {
24 if (drag->focus == surface) {
25 return;
26 }
27
28 if (drag->focus_client) {
29 wl_list_remove(&drag->seat_client_destroy.link);
30
31 // If we're switching focus to another client, we want to destroy all
32 // offers without destroying the source. If the drag operation ends, we
33 // want to keep the offer around for the data transfer.
34 struct wlr_data_offer *offer, *tmp;
35 wl_list_for_each_safe(offer, tmp,
36 &drag->focus_client->seat->drag_offers, link) {
37 struct wl_client *client = wl_resource_get_client(offer->resource);
38 if (!drag->dropped && offer->source == drag->source &&
39 client == drag->focus_client->client) {
40 offer->source = NULL;
41 data_offer_destroy(offer);
42 }
43 }
44
45 struct wl_resource *resource;
46 wl_resource_for_each(resource, &drag->focus_client->data_devices) {
47 wl_data_device_send_leave(resource);
48 }
49
50 drag->focus_client = NULL;
51 drag->focus = NULL;
52 }
53
54 if (!surface) {
55 goto out;
56 }
57
58 if (!drag->source &&
59 wl_resource_get_client(surface->resource) !=
60 drag->seat_client->client) {
61 goto out;
62 }
63
64 struct wlr_seat_client *focus_client = wlr_seat_client_for_wl_client(
65 drag->seat_client->seat, wl_resource_get_client(surface->resource));
66 if (!focus_client) {
67 goto out;
68 }
69
70 if (drag->source != NULL) {
71 drag->source->accepted = false;
72
73 uint32_t serial =
74 wl_display_next_serial(drag->seat_client->seat->display);
75
76 struct wl_resource *device_resource;
77 wl_resource_for_each(device_resource, &focus_client->data_devices) {
78 struct wlr_data_offer *offer = data_offer_create(device_resource,
79 drag->source, WLR_DATA_OFFER_DRAG);
80 if (offer == NULL) {
81 wl_resource_post_no_memory(device_resource);
82 return;
83 }
84
85 data_offer_update_action(offer);
86
87 if (wl_resource_get_version(offer->resource) >=
88 WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
89 wl_data_offer_send_source_actions(offer->resource,
90 drag->source->actions);
91 }
92
93 wl_data_device_send_enter(device_resource, serial,
94 surface->resource,
95 wl_fixed_from_double(sx), wl_fixed_from_double(sy),
96 offer->resource);
97 }
98 }
99
100 drag->focus = surface;
101 drag->focus_client = focus_client;
102 drag->seat_client_destroy.notify = drag_handle_seat_client_destroy;
103 wl_signal_add(&focus_client->events.destroy, &drag->seat_client_destroy);
104
105 out:
106 wlr_signal_emit_safe(&drag->events.focus, drag);
107 }
108
drag_icon_set_mapped(struct wlr_drag_icon * icon,bool mapped)109 static void drag_icon_set_mapped(struct wlr_drag_icon *icon, bool mapped) {
110 if (mapped && !icon->mapped) {
111 icon->mapped = true;
112 wlr_signal_emit_safe(&icon->events.map, icon);
113 } else if (!mapped && icon->mapped) {
114 wlr_signal_emit_safe(&icon->events.unmap, icon);
115 icon->mapped = false;
116 }
117 }
118
119 static void drag_icon_destroy(struct wlr_drag_icon *icon);
120
drag_destroy(struct wlr_drag * drag)121 static void drag_destroy(struct wlr_drag *drag) {
122 if (drag->cancelling) {
123 return;
124 }
125 drag->cancelling = true;
126
127 if (drag->started) {
128 wlr_seat_keyboard_end_grab(drag->seat);
129 switch (drag->grab_type) {
130 case WLR_DRAG_GRAB_KEYBOARD:
131 break;
132 case WLR_DRAG_GRAB_KEYBOARD_POINTER:
133 wlr_seat_pointer_end_grab(drag->seat);
134 break;
135 case WLR_DRAG_GRAB_KEYBOARD_TOUCH:
136 wlr_seat_touch_end_grab(drag->seat);
137 break;
138 }
139 }
140
141 // We issue destroy after ending the grab to allow focus changes.
142 wlr_signal_emit_safe(&drag->events.destroy, drag);
143
144 if (drag->started) {
145 drag_set_focus(drag, NULL, 0, 0);
146
147 assert(drag->seat->drag == drag);
148 drag->seat->drag = NULL;
149 }
150
151 if (drag->source) {
152 wl_list_remove(&drag->source_destroy.link);
153 }
154
155 drag_icon_destroy(drag->icon);
156 free(drag);
157 }
158
drag_handle_pointer_enter(struct wlr_seat_pointer_grab * grab,struct wlr_surface * surface,double sx,double sy)159 static void drag_handle_pointer_enter(struct wlr_seat_pointer_grab *grab,
160 struct wlr_surface *surface, double sx, double sy) {
161 struct wlr_drag *drag = grab->data;
162 drag_set_focus(drag, surface, sx, sy);
163 }
164
drag_handle_pointer_clear_focus(struct wlr_seat_pointer_grab * grab)165 static void drag_handle_pointer_clear_focus(struct wlr_seat_pointer_grab *grab) {
166 struct wlr_drag *drag = grab->data;
167 drag_set_focus(drag, NULL, 0, 0);
168 }
169
drag_handle_pointer_motion(struct wlr_seat_pointer_grab * grab,uint32_t time,double sx,double sy)170 static void drag_handle_pointer_motion(struct wlr_seat_pointer_grab *grab,
171 uint32_t time, double sx, double sy) {
172 struct wlr_drag *drag = grab->data;
173 if (drag->focus != NULL && drag->focus_client != NULL) {
174 struct wl_resource *resource;
175 wl_resource_for_each(resource, &drag->focus_client->data_devices) {
176 wl_data_device_send_motion(resource, time, wl_fixed_from_double(sx),
177 wl_fixed_from_double(sy));
178 }
179
180 struct wlr_drag_motion_event event = {
181 .drag = drag,
182 .time = time,
183 .sx = sx,
184 .sy = sy,
185 };
186 wlr_signal_emit_safe(&drag->events.motion, &event);
187 }
188 }
189
drag_drop(struct wlr_drag * drag,uint32_t time)190 static void drag_drop(struct wlr_drag *drag, uint32_t time) {
191 assert(drag->focus_client);
192
193 drag->dropped = true;
194
195 struct wl_resource *resource;
196 wl_resource_for_each(resource, &drag->focus_client->data_devices) {
197 wl_data_device_send_drop(resource);
198 }
199 if (drag->source) {
200 wlr_data_source_dnd_drop(drag->source);
201 }
202
203 struct wlr_drag_drop_event event = {
204 .drag = drag,
205 .time = time,
206 };
207 wlr_signal_emit_safe(&drag->events.drop, &event);
208 }
209
drag_handle_pointer_button(struct wlr_seat_pointer_grab * grab,uint32_t time,uint32_t button,uint32_t state)210 static uint32_t drag_handle_pointer_button(struct wlr_seat_pointer_grab *grab,
211 uint32_t time, uint32_t button, uint32_t state) {
212 struct wlr_drag *drag = grab->data;
213
214 if (drag->source &&
215 grab->seat->pointer_state.grab_button == button &&
216 state == WL_POINTER_BUTTON_STATE_RELEASED) {
217 if (drag->focus_client && drag->source->current_dnd_action &&
218 drag->source->accepted) {
219 drag_drop(drag, time);
220 } else if (drag->source->impl->dnd_finish) {
221 // This will end the grab and free `drag`
222 wlr_data_source_destroy(drag->source);
223 return 0;
224 }
225 }
226
227 if (grab->seat->pointer_state.button_count == 0 &&
228 state == WL_POINTER_BUTTON_STATE_RELEASED) {
229 drag_destroy(drag);
230 }
231
232 return 0;
233 }
234
drag_handle_pointer_axis(struct wlr_seat_pointer_grab * grab,uint32_t time,enum wlr_axis_orientation orientation,double value,int32_t value_discrete,enum wlr_axis_source source)235 static void drag_handle_pointer_axis(struct wlr_seat_pointer_grab *grab,
236 uint32_t time, enum wlr_axis_orientation orientation, double value,
237 int32_t value_discrete, enum wlr_axis_source source) {
238 // This space is intentionally left blank
239 }
240
drag_handle_pointer_cancel(struct wlr_seat_pointer_grab * grab)241 static void drag_handle_pointer_cancel(struct wlr_seat_pointer_grab *grab) {
242 struct wlr_drag *drag = grab->data;
243 drag_destroy(drag);
244 }
245
246 static const struct wlr_pointer_grab_interface
247 data_device_pointer_drag_interface = {
248 .enter = drag_handle_pointer_enter,
249 .clear_focus = drag_handle_pointer_clear_focus,
250 .motion = drag_handle_pointer_motion,
251 .button = drag_handle_pointer_button,
252 .axis = drag_handle_pointer_axis,
253 .cancel = drag_handle_pointer_cancel,
254 };
255
drag_handle_touch_down(struct wlr_seat_touch_grab * grab,uint32_t time,struct wlr_touch_point * point)256 static uint32_t drag_handle_touch_down(struct wlr_seat_touch_grab *grab,
257 uint32_t time, struct wlr_touch_point *point) {
258 // eat the event
259 return 0;
260 }
261
drag_handle_touch_up(struct wlr_seat_touch_grab * grab,uint32_t time,struct wlr_touch_point * point)262 static void drag_handle_touch_up(struct wlr_seat_touch_grab *grab,
263 uint32_t time, struct wlr_touch_point *point) {
264 struct wlr_drag *drag = grab->data;
265 if (drag->grab_touch_id != point->touch_id) {
266 return;
267 }
268
269 if (drag->focus_client) {
270 drag_drop(drag, time);
271 }
272
273 drag_destroy(drag);
274 }
275
drag_handle_touch_motion(struct wlr_seat_touch_grab * grab,uint32_t time,struct wlr_touch_point * point)276 static void drag_handle_touch_motion(struct wlr_seat_touch_grab *grab,
277 uint32_t time, struct wlr_touch_point *point) {
278 struct wlr_drag *drag = grab->data;
279 if (drag->focus && drag->focus_client) {
280 struct wl_resource *resource;
281 wl_resource_for_each(resource, &drag->focus_client->data_devices) {
282 wl_data_device_send_motion(resource, time,
283 wl_fixed_from_double(point->sx),
284 wl_fixed_from_double(point->sy));
285 }
286 }
287 }
288
drag_handle_touch_enter(struct wlr_seat_touch_grab * grab,uint32_t time,struct wlr_touch_point * point)289 static void drag_handle_touch_enter(struct wlr_seat_touch_grab *grab,
290 uint32_t time, struct wlr_touch_point *point) {
291 struct wlr_drag *drag = grab->data;
292 drag_set_focus(drag, point->focus_surface, point->sx, point->sy);
293 }
294
drag_handle_touch_cancel(struct wlr_seat_touch_grab * grab)295 static void drag_handle_touch_cancel(struct wlr_seat_touch_grab *grab) {
296 struct wlr_drag *drag = grab->data;
297 drag_destroy(drag);
298 }
299
300 static const struct wlr_touch_grab_interface
301 data_device_touch_drag_interface = {
302 .down = drag_handle_touch_down,
303 .up = drag_handle_touch_up,
304 .motion = drag_handle_touch_motion,
305 .enter = drag_handle_touch_enter,
306 .cancel = drag_handle_touch_cancel,
307 };
308
drag_handle_keyboard_enter(struct wlr_seat_keyboard_grab * grab,struct wlr_surface * surface,uint32_t keycodes[],size_t num_keycodes,struct wlr_keyboard_modifiers * modifiers)309 static void drag_handle_keyboard_enter(struct wlr_seat_keyboard_grab *grab,
310 struct wlr_surface *surface, uint32_t keycodes[], size_t num_keycodes,
311 struct wlr_keyboard_modifiers *modifiers) {
312 // nothing has keyboard focus during drags
313 }
314
drag_handle_keyboard_clear_focus(struct wlr_seat_keyboard_grab * grab)315 static void drag_handle_keyboard_clear_focus(struct wlr_seat_keyboard_grab *grab) {
316 // nothing has keyboard focus during drags
317 }
318
drag_handle_keyboard_key(struct wlr_seat_keyboard_grab * grab,uint32_t time,uint32_t key,uint32_t state)319 static void drag_handle_keyboard_key(struct wlr_seat_keyboard_grab *grab,
320 uint32_t time, uint32_t key, uint32_t state) {
321 // no keyboard input during drags
322 }
323
drag_handle_keyboard_modifiers(struct wlr_seat_keyboard_grab * grab,struct wlr_keyboard_modifiers * modifiers)324 static void drag_handle_keyboard_modifiers(struct wlr_seat_keyboard_grab *grab,
325 struct wlr_keyboard_modifiers *modifiers) {
326 //struct wlr_keyboard *keyboard = grab->seat->keyboard_state.keyboard;
327 // TODO change the dnd action based on what modifier is pressed on the
328 // keyboard
329 }
330
drag_handle_keyboard_cancel(struct wlr_seat_keyboard_grab * grab)331 static void drag_handle_keyboard_cancel(struct wlr_seat_keyboard_grab *grab) {
332 struct wlr_drag *drag = grab->data;
333 drag_destroy(drag);
334 }
335
336 static const struct wlr_keyboard_grab_interface
337 data_device_keyboard_drag_interface = {
338 .enter = drag_handle_keyboard_enter,
339 .clear_focus = drag_handle_keyboard_clear_focus,
340 .key = drag_handle_keyboard_key,
341 .modifiers = drag_handle_keyboard_modifiers,
342 .cancel = drag_handle_keyboard_cancel,
343 };
344
drag_handle_icon_destroy(struct wl_listener * listener,void * data)345 static void drag_handle_icon_destroy(struct wl_listener *listener, void *data) {
346 struct wlr_drag *drag = wl_container_of(listener, drag, icon_destroy);
347 drag->icon = NULL;
348 }
349
drag_handle_drag_source_destroy(struct wl_listener * listener,void * data)350 static void drag_handle_drag_source_destroy(struct wl_listener *listener,
351 void *data) {
352 struct wlr_drag *drag = wl_container_of(listener, drag, source_destroy);
353 drag_destroy(drag);
354 }
355
356
drag_icon_destroy(struct wlr_drag_icon * icon)357 static void drag_icon_destroy(struct wlr_drag_icon *icon) {
358 if (icon == NULL) {
359 return;
360 }
361 drag_icon_set_mapped(icon, false);
362 wlr_signal_emit_safe(&icon->events.destroy, icon);
363 icon->surface->role_data = NULL;
364 wl_list_remove(&icon->surface_destroy.link);
365 free(icon);
366 }
367
drag_icon_handle_surface_destroy(struct wl_listener * listener,void * data)368 static void drag_icon_handle_surface_destroy(struct wl_listener *listener,
369 void *data) {
370 struct wlr_drag_icon *icon =
371 wl_container_of(listener, icon, surface_destroy);
372 drag_icon_destroy(icon);
373 }
374
drag_icon_surface_role_commit(struct wlr_surface * surface)375 static void drag_icon_surface_role_commit(struct wlr_surface *surface) {
376 assert(surface->role == &drag_icon_surface_role);
377 struct wlr_drag_icon *icon = surface->role_data;
378 if (icon == NULL) {
379 return;
380 }
381
382 drag_icon_set_mapped(icon, wlr_surface_has_buffer(surface));
383 }
384
385 const struct wlr_surface_role drag_icon_surface_role = {
386 .name = "wl_data_device-icon",
387 .commit = drag_icon_surface_role_commit,
388 };
389
drag_icon_create(struct wlr_drag * drag,struct wlr_surface * surface)390 static struct wlr_drag_icon *drag_icon_create(struct wlr_drag *drag,
391 struct wlr_surface *surface) {
392 struct wlr_drag_icon *icon = calloc(1, sizeof(struct wlr_drag_icon));
393 if (!icon) {
394 return NULL;
395 }
396
397 icon->drag = drag;
398 icon->surface = surface;
399
400 wl_signal_init(&icon->events.map);
401 wl_signal_init(&icon->events.unmap);
402 wl_signal_init(&icon->events.destroy);
403
404 wl_signal_add(&icon->surface->events.destroy, &icon->surface_destroy);
405 icon->surface_destroy.notify = drag_icon_handle_surface_destroy;
406
407 icon->surface->role_data = icon;
408
409 if (wlr_surface_has_buffer(surface)) {
410 drag_icon_set_mapped(icon, true);
411 }
412
413 return icon;
414 }
415
416
wlr_drag_create(struct wlr_seat_client * seat_client,struct wlr_data_source * source,struct wlr_surface * icon_surface)417 struct wlr_drag *wlr_drag_create(struct wlr_seat_client *seat_client,
418 struct wlr_data_source *source, struct wlr_surface *icon_surface) {
419 struct wlr_drag *drag = calloc(1, sizeof(struct wlr_drag));
420 if (drag == NULL) {
421 return NULL;
422 }
423
424 wl_signal_init(&drag->events.focus);
425 wl_signal_init(&drag->events.motion);
426 wl_signal_init(&drag->events.drop);
427 wl_signal_init(&drag->events.destroy);
428
429 drag->seat = seat_client->seat;
430 drag->seat_client = seat_client;
431
432 if (icon_surface) {
433 struct wlr_drag_icon *icon = drag_icon_create(drag, icon_surface);
434 if (icon == NULL) {
435 free(drag);
436 return NULL;
437 }
438
439 drag->icon = icon;
440
441 drag->icon_destroy.notify = drag_handle_icon_destroy;
442 wl_signal_add(&icon->events.destroy, &drag->icon_destroy);
443 }
444
445 drag->source = source;
446 if (source != NULL) {
447 drag->source_destroy.notify = drag_handle_drag_source_destroy;
448 wl_signal_add(&source->events.destroy, &drag->source_destroy);
449 }
450
451 drag->pointer_grab.data = drag;
452 drag->pointer_grab.interface = &data_device_pointer_drag_interface;
453
454 drag->touch_grab.data = drag;
455 drag->touch_grab.interface = &data_device_touch_drag_interface;
456
457 drag->keyboard_grab.data = drag;
458 drag->keyboard_grab.interface = &data_device_keyboard_drag_interface;
459
460 return drag;
461 }
462
wlr_seat_request_start_drag(struct wlr_seat * seat,struct wlr_drag * drag,struct wlr_surface * origin,uint32_t serial)463 void wlr_seat_request_start_drag(struct wlr_seat *seat, struct wlr_drag *drag,
464 struct wlr_surface *origin, uint32_t serial) {
465 assert(drag->seat == seat);
466
467 if (seat->drag != NULL) {
468 wlr_log(WLR_DEBUG, "Rejecting start_drag request, "
469 "another drag-and-drop operation is already in progress");
470 return;
471 }
472
473 struct wlr_seat_request_start_drag_event event = {
474 .drag = drag,
475 .origin = origin,
476 .serial = serial,
477 };
478 wlr_signal_emit_safe(&seat->events.request_start_drag, &event);
479 }
480
seat_handle_drag_source_destroy(struct wl_listener * listener,void * data)481 static void seat_handle_drag_source_destroy(struct wl_listener *listener,
482 void *data) {
483 struct wlr_seat *seat =
484 wl_container_of(listener, seat, drag_source_destroy);
485 wl_list_remove(&seat->drag_source_destroy.link);
486 seat->drag_source = NULL;
487 }
488
wlr_seat_start_drag(struct wlr_seat * seat,struct wlr_drag * drag,uint32_t serial)489 void wlr_seat_start_drag(struct wlr_seat *seat, struct wlr_drag *drag,
490 uint32_t serial) {
491 assert(drag->seat == seat);
492 assert(!drag->started);
493 drag->started = true;
494
495 wlr_seat_keyboard_start_grab(seat, &drag->keyboard_grab);
496
497 seat->drag = drag;
498 seat->drag_serial = serial;
499
500 // We need to destroy the previous source, because listeners only expect one
501 // active drag source at a time.
502 wlr_data_source_destroy(seat->drag_source);
503 seat->drag_source = drag->source;
504 if (drag->source != NULL) {
505 seat->drag_source_destroy.notify = seat_handle_drag_source_destroy;
506 wl_signal_add(&drag->source->events.destroy, &seat->drag_source_destroy);
507 }
508
509 wlr_signal_emit_safe(&seat->events.start_drag, drag);
510 }
511
wlr_seat_start_pointer_drag(struct wlr_seat * seat,struct wlr_drag * drag,uint32_t serial)512 void wlr_seat_start_pointer_drag(struct wlr_seat *seat, struct wlr_drag *drag,
513 uint32_t serial) {
514 drag->grab_type = WLR_DRAG_GRAB_KEYBOARD_POINTER;
515
516 wlr_seat_pointer_clear_focus(seat);
517 wlr_seat_pointer_start_grab(seat, &drag->pointer_grab);
518
519 wlr_seat_start_drag(seat, drag, serial);
520 }
521
wlr_seat_start_touch_drag(struct wlr_seat * seat,struct wlr_drag * drag,uint32_t serial,struct wlr_touch_point * point)522 void wlr_seat_start_touch_drag(struct wlr_seat *seat, struct wlr_drag *drag,
523 uint32_t serial, struct wlr_touch_point *point) {
524 drag->grab_type = WLR_DRAG_GRAB_KEYBOARD_TOUCH;
525 drag->grab_touch_id = seat->touch_state.grab_id;
526 drag->touch_id = point->touch_id;
527
528 wlr_seat_touch_start_grab(seat, &drag->touch_grab);
529 drag_set_focus(drag, point->surface, point->sx, point->sy);
530
531 wlr_seat_start_drag(seat, drag, serial);
532 }
533