1/*
2 * Copyright © 2010 Intel Corporation
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include "gdkdndprivate.h"
21
22#include "gdkmain.h"
23#include "gdkinternals.h"
24#include "gdkproperty.h"
25#include "gdkprivate-wayland.h"
26#include "gdkdisplay-wayland.h"
27#include "gdkseat-wayland.h"
28
29#include "gdkdeviceprivate.h"
30
31#include <string.h>
32
33#define GDK_TYPE_WAYLAND_DRAG_CONTEXT (gdk_wayland_drag_context_get_type ())
34#define GDK_WAYLAND_DRAG_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WAYLAND_DRAG_CONTEXT, GdkWaylandDragContext))
35#define GDK_WAYLAND_DRAG_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WAYLAND_DRAG_CONTEXT, GdkWaylandDragContextClass))
36#define GDK_IS_WAYLAND_DRAG_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WAYLAND_DRAG_CONTEXT))
37#define GDK_IS_WAYLAND_DRAG_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WAYLAND_DRAG_CONTEXT))
38#define GDK_WAYLAND_DRAG_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WAYLAND_DRAG_CONTEXT, GdkWaylandDragContextClass))
39
40typedef struct _GdkWaylandDragContext GdkWaylandDragContext;
41typedef struct _GdkWaylandDragContextClass GdkWaylandDragContextClass;
42
43struct _GdkWaylandDragContext
44{
45 GdkDragContext context;
46 GdkWindow *dnd_window;
47 struct wl_surface *dnd_surface;
48 struct wl_data_source *data_source;
49 GdkDragAction selected_action;
50 uint32_t serial;
51 gdouble x;
52 gdouble y;
53 gint hot_x;
54 gint hot_y;
55};
56
57struct _GdkWaylandDragContextClass
58{
59 GdkDragContextClass parent_class;
60};
61
62static GList *contexts;
63
64GType gdk_wayland_drag_context_get_type (void);
65
66G_DEFINE_TYPE (GdkWaylandDragContext, gdk_wayland_drag_context, GDK_TYPE_DRAG_CONTEXT)
67
68static void
69gdk_wayland_drag_context_finalize (GObject *object)
70{
71 GdkWaylandDragContext *wayland_context = GDK_WAYLAND_DRAG_CONTEXT (object);
72 GdkDragContext *context = GDK_DRAG_CONTEXT (object);
73 GdkWindow *dnd_window;
74
75 contexts = g_list_remove (contexts, context);
76
77 if (context->is_source)
78 {
79 GdkDisplay *display = gdk_window_get_display (context->source_window);
80 GdkAtom selection;
81 GdkWindow *selection_owner;
82
83 selection = gdk_drag_get_selection (context);
84 selection_owner = gdk_selection_owner_get_for_display (display, selection);
85 if (selection_owner == context->source_window)
86 gdk_wayland_selection_unset_data_source (display, selection);
87
88 gdk_drag_context_set_cursor (context, NULL);
89 }
90
91 if (wayland_context->data_source)
92 wl_data_source_destroy (wayland_context->data_source);
93
94 dnd_window = wayland_context->dnd_window;
95
96 G_OBJECT_CLASS (gdk_wayland_drag_context_parent_class)->finalize (object);
97
98 if (dnd_window)
99 gdk_window_destroy (dnd_window);
100}
101
102void
103_gdk_wayland_drag_context_emit_event (GdkDragContext *context,
104 GdkEventType type,
105 guint32 time_)
106{
107 GdkWindow *window;
108 GdkEvent *event;
109
110 switch (type)
111 {
112 case GDK_DRAG_ENTER:
113 case GDK_DRAG_LEAVE:
114 case GDK_DRAG_MOTION:
115 case GDK_DRAG_STATUS:
116 case GDK_DROP_START:
117 case GDK_DROP_FINISHED:
118 break;
119 default:
120 return;
121 }
122
123 if (context->is_source)
124 window = gdk_drag_context_get_source_window (context);
125 else
126 window = gdk_drag_context_get_dest_window (context);
127
128 event = gdk_event_new (type);
129 event->dnd.window = g_object_ref (window);
130 event->dnd.context = g_object_ref (context);
131 event->dnd.time = time_;
132 event->dnd.x_root = GDK_WAYLAND_DRAG_CONTEXT (context)->x;
133 event->dnd.y_root = GDK_WAYLAND_DRAG_CONTEXT (context)->y;
134 gdk_event_set_device (event, gdk_drag_context_get_device (context));
135
136 gdk_event_put (event);
137 gdk_event_free (event);
138}
139
140static GdkWindow *
141gdk_wayland_drag_context_find_window (GdkDragContext *context,
142 GdkWindow *drag_window,
143 GdkScreen *screen,
144 gint x_root,
145 gint y_root,
146 GdkDragProtocol *protocol)
147{
148 GdkDevice *device;
149 GdkWindow *window;
150
151 device = gdk_drag_context_get_device (context);
152 window = gdk_device_get_window_at_position (device, NULL, NULL);
153
154 if (window)
155 {
156 window = gdk_window_get_toplevel (window);
157 *protocol = GDK_DRAG_PROTO_WAYLAND;
158 return g_object_ref (window);
159 }
160
161 return NULL;
162}
163
164static inline uint32_t
165gdk_to_wl_actions (GdkDragAction action)
166{
167 uint32_t dnd_actions = 0;
168
169 if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_PRIVATE))
170 dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
171 if (action & GDK_ACTION_MOVE)
172 dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
173 if (action & GDK_ACTION_ASK)
174 dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
175
176 return dnd_actions;
177}
178
179void
180gdk_wayland_drag_context_set_action (GdkDragContext *context,
181 GdkDragAction action)
182{
183 context->suggested_action = context->action = action;
184}
185
186static gboolean
187gdk_wayland_drag_context_drag_motion (GdkDragContext *context,
188 GdkWindow *dest_window,
189 GdkDragProtocol protocol,
190 gint x_root,
191 gint y_root,
192 GdkDragAction suggested_action,
193 GdkDragAction possible_actions,
194 guint32 time)
195{
196 if (context->dest_window != dest_window)
197 {
198 context->dest_window = dest_window ? g_object_ref (dest_window) : NULL;
199 _gdk_wayland_drag_context_set_coords (context, x_root, y_root);
200 _gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS, time);
201 }
202
203 gdk_wayland_drag_context_set_action (context, suggested_action);
204
205 return context->dest_window != NULL;
206}
207
208static void
209gdk_wayland_drag_context_drag_abort (GdkDragContext *context,
210 guint32 time)
211{
212}
213
214static void
215gdk_wayland_drag_context_drag_drop (GdkDragContext *context,
216 guint32 time)
217{
218}
219
220/* Destination side */
221
222static void
223gdk_wayland_drop_context_set_status (GdkDragContext *context,
224 gboolean accepted)
225{
226 GdkWaylandDragContext *context_wayland;
227 GdkDisplay *display;
228 struct wl_data_offer *wl_offer;
229
230 if (!context->dest_window)
231 return;
232
233 context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
234
235 display = gdk_device_get_display (gdk_drag_context_get_device (context));
236 wl_offer = gdk_wayland_selection_get_offer (display,
237 gdk_drag_get_selection (context));
238
239 if (!wl_offer)
240 return;
241
242 if (accepted)
243 {
244 GList *l;
245
246 for (l = context->targets; l; l = l->next)
247 {
248 if (l->data != gdk_atom_intern_static_string ("DELETE"))
249 break;
250 }
251
252 if (l)
253 {
254 gchar *mimetype = gdk_atom_name (l->data);
255
256 wl_data_offer_accept (wl_offer, context_wayland->serial, mimetype);
257 g_free (mimetype);
258 return;
259 }
260 }
261
262 wl_data_offer_accept (wl_offer, context_wayland->serial, NULL);
263}
264
265static void
266gdk_wayland_drag_context_commit_status (GdkDragContext *context)
267{
268 GdkWaylandDragContext *wayland_context;
269 GdkDisplay *display;
270 uint32_t dnd_actions;
271
272 wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
273 display = gdk_device_get_display (gdk_drag_context_get_device (context));
274
275 dnd_actions = gdk_to_wl_actions (wayland_context->selected_action);
276 gdk_wayland_selection_set_current_offer_actions (display, dnd_actions);
277
278 gdk_wayland_drop_context_set_status (context, wayland_context->selected_action != 0);
279}
280
281static void
282gdk_wayland_drag_context_drag_status (GdkDragContext *context,
283 GdkDragAction action,
284 guint32 time_)
285{
286 GdkWaylandDragContext *wayland_context;
287
288 wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
289 wayland_context->selected_action = action;
290}
291
292static void
293gdk_wayland_drag_context_drop_reply (GdkDragContext *context,
294 gboolean accepted,
295 guint32 time_)
296{
297 if (!accepted)
298 gdk_wayland_drop_context_set_status (context, accepted);
299}
300
301static void
302gdk_wayland_drag_context_drop_finish (GdkDragContext *context,
303 gboolean success,
304 guint32 time)
305{
306 GdkDisplay *display = gdk_device_get_display (gdk_drag_context_get_device (context));
307 GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
308 GdkWaylandDragContext *wayland_context;
309 struct wl_data_offer *wl_offer;
310 GdkAtom selection;
311
312 wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
313 selection = gdk_drag_get_selection (context);
314 wl_offer = gdk_wayland_selection_get_offer (display, selection);
315
316 if (wl_offer && success && wayland_context->selected_action &&
317 wayland_context->selected_action != GDK_ACTION_ASK)
318 {
319 gdk_wayland_drag_context_commit_status (context);
320
321 if (display_wayland->data_device_manager_version >=
322 WL_DATA_OFFER_FINISH_SINCE_VERSION)
323 wl_data_offer_finish (wl_offer);
324 }
325
326 gdk_wayland_selection_set_offer (display, selection, NULL);
327}
328
329static gboolean
330gdk_wayland_drag_context_drop_status (GdkDragContext *context)
331{
332 return FALSE;
333}
334
335static GdkAtom
336gdk_wayland_drag_context_get_selection (GdkDragContext *context)
337{
338 return gdk_atom_intern_static_string ("GdkWaylandSelection");
339}
340
341static void
342gdk_wayland_drag_context_init (GdkWaylandDragContext *context_wayland)
343{
344 GdkDragContext *context;
345
346 context = GDK_DRAG_CONTEXT (context_wayland);
347 contexts = g_list_prepend (contexts, context);
348
349 context->action = GDK_ACTION_COPY;
350 context->suggested_action = GDK_ACTION_COPY;
351 context->actions = GDK_ACTION_COPY | GDK_ACTION_MOVE;
352}
353
354static GdkWindow *
355gdk_wayland_drag_context_get_drag_window (GdkDragContext *context)
356{
357 return GDK_WAYLAND_DRAG_CONTEXT (context)->dnd_window;
358}
359
360static void
361gdk_wayland_drag_context_set_hotspot (GdkDragContext *context,
362 gint hot_x,
363 gint hot_y)
364{
365 GdkWaylandDragContext *context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
366 gint prev_hot_x = context_wayland->hot_x;
367 gint prev_hot_y = context_wayland->hot_y;
368 const GdkRectangle damage_rect = { .width = 1, .height = 1 };
369
370 context_wayland->hot_x = hot_x;
371 context_wayland->hot_y = hot_y;
372
373 if (prev_hot_x == hot_x && prev_hot_y == hot_y)
374 return;
375
376 _gdk_wayland_window_offset_next_wl_buffer (context_wayland->dnd_window,
377 -hot_x, -hot_y);
378 gdk_window_invalidate_rect (context_wayland->dnd_window, &damage_rect, FALSE);
379}
380
381static gboolean
382gdk_wayland_drag_context_manage_dnd (GdkDragContext *context,
383 GdkWindow *ipc_window,
384 GdkDragAction actions)
385{
386 GdkWaylandDragContext *context_wayland;
387 GdkWaylandDisplay *display_wayland;
388 GdkDevice *device;
389 GdkWindow *toplevel;
390
391 device = gdk_drag_context_get_device (context);
392 display_wayland = GDK_WAYLAND_DISPLAY (gdk_device_get_display (device));
393 toplevel = _gdk_device_window_at_position (device, NULL, NULL, NULL, TRUE);
394
395 context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
396
397 if (display_wayland->data_device_manager_version >=
398 WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION)
399 {
400 wl_data_source_set_actions (context_wayland->data_source,
401 gdk_to_wl_actions (actions));
402 }
403
404 wl_data_device_start_drag (gdk_wayland_device_get_data_device (device),
405 context_wayland->data_source,
406 gdk_wayland_window_get_wl_surface (toplevel),
407 context_wayland->dnd_surface,
408 _gdk_wayland_display_get_serial (display_wayland));
409
410 gdk_seat_ungrab (gdk_device_get_seat (device));
411
412 return TRUE;
413}
414
415static void
416gdk_wayland_drag_context_set_cursor (GdkDragContext *context,
417 GdkCursor *cursor)
418{
419 GdkDevice *device = gdk_drag_context_get_device (context);
420
421 gdk_wayland_seat_set_global_cursor (gdk_device_get_seat (device), cursor);
422}
423
424static void
425gdk_wayland_drag_context_action_changed (GdkDragContext *context,
426 GdkDragAction action)
427{
428 GdkCursor *cursor;
429
430 cursor = gdk_drag_get_cursor (context, action);
431 gdk_drag_context_set_cursor (context, cursor);
432}
433
434static void
435gdk_wayland_drag_context_drop_performed (GdkDragContext *context,
436 guint32 time_)
437{
438 gdk_drag_context_set_cursor (context, NULL);
439}
440
441static void
442gdk_wayland_drag_context_cancel (GdkDragContext *context,
443 GdkDragCancelReason reason)
444{
445 gdk_drag_context_set_cursor (context, NULL);
446}
447
448static void
449gdk_wayland_drag_context_drop_done (GdkDragContext *context,
450 gboolean success)
451{
452 GdkWaylandDragContext *context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
453
454 if (success)
455 {
456 if (context_wayland->dnd_window)
457 gdk_window_hide (context_wayland->dnd_window);
458 }
459}
460
461static void
462gdk_wayland_drag_context_class_init (GdkWaylandDragContextClass *klass)
463{
464 GObjectClass *object_class = G_OBJECT_CLASS (klass);
465 GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
466
467 object_class->finalize = gdk_wayland_drag_context_finalize;
468
469 context_class->find_window = gdk_wayland_drag_context_find_window;
470 context_class->drag_status = gdk_wayland_drag_context_drag_status;
471 context_class->drag_motion = gdk_wayland_drag_context_drag_motion;
472 context_class->drag_abort = gdk_wayland_drag_context_drag_abort;
473 context_class->drag_drop = gdk_wayland_drag_context_drag_drop;
474 context_class->drop_reply = gdk_wayland_drag_context_drop_reply;
475 context_class->drop_finish = gdk_wayland_drag_context_drop_finish;
476 context_class->drop_status = gdk_wayland_drag_context_drop_status;
477 context_class->get_selection = gdk_wayland_drag_context_get_selection;
478 context_class->get_drag_window = gdk_wayland_drag_context_get_drag_window;
479 context_class->set_hotspot = gdk_wayland_drag_context_set_hotspot;
480 context_class->drop_done = gdk_wayland_drag_context_drop_done;
481 context_class->manage_dnd = gdk_wayland_drag_context_manage_dnd;
482 context_class->set_cursor = gdk_wayland_drag_context_set_cursor;
483 context_class->action_changed = gdk_wayland_drag_context_action_changed;
484 context_class->drop_performed = gdk_wayland_drag_context_drop_performed;
485 context_class->cancel = gdk_wayland_drag_context_cancel;
486 context_class->commit_drag_status = gdk_wayland_drag_context_commit_status;
487}
488
489GdkDragProtocol
490_gdk_wayland_window_get_drag_protocol (GdkWindow *window, GdkWindow **target)
491{
492 return GDK_DRAG_PROTO_WAYLAND;
493}
494
495void
496_gdk_wayland_window_register_dnd (GdkWindow *window)
497{
498}
499
500static GdkWindow *
501create_dnd_window (GdkScreen *screen)
502{
503 GdkWindowAttr attrs;
504 guint mask;
505
506 attrs.x = attrs.y = 0;
507 attrs.width = attrs.height = 100;
508 attrs.wclass = GDK_INPUT_OUTPUT;
509 attrs.window_type = GDK_WINDOW_TEMP;
510 attrs.type_hint = GDK_WINDOW_TYPE_HINT_DND;
511 attrs.visual = gdk_screen_get_system_visual (screen);
512
513 mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_TYPE_HINT;
514
515 return gdk_window_new (gdk_screen_get_root_window (screen), &attrs, mask);
516}
517
518GdkDragContext *
519_gdk_wayland_window_drag_begin (GdkWindow *window,
520 GdkDevice *device,
521 GList *targets,
522 gint x_root,
523 gint y_root)
524{
525 GdkWaylandDragContext *context_wayland;
526 GdkDragContext *context;
527 GList *l;
528
529 context_wayland = g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT, NULL);
530 context = GDK_DRAG_CONTEXT (context_wayland);
531 context->display = gdk_window_get_display (window);
532 context->source_window = g_object_ref (window);
533 context->is_source = TRUE;
534 context->targets = g_list_copy (targets);
535
536 gdk_drag_context_set_device (context, device);
537
538 context_wayland->dnd_window = create_dnd_window (gdk_window_get_screen (window));
539 context_wayland->dnd_surface = gdk_wayland_window_get_wl_surface (context_wayland->dnd_window);
540 context_wayland->data_source =
541 gdk_wayland_selection_get_data_source (window,
542 gdk_wayland_drag_context_get_selection (context));
543
544 for (l = context->targets; l; l = l->next)
545 {
546 gchar *mimetype = gdk_atom_name (l->data);
547
548 wl_data_source_offer (context_wayland->data_source, mimetype);
549 g_free (mimetype);
550 }
551
552 return context;
553}
554
555GdkDragContext *
556_gdk_wayland_drop_context_new (GdkDisplay *display,
557 struct wl_data_device *data_device)
558{
559 GdkWaylandDragContext *context_wayland;
560 GdkDragContext *context;
561
562 context_wayland = g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT, NULL);
563 context = GDK_DRAG_CONTEXT (context_wayland);
564 context->display = display;
565 context->is_source = FALSE;
566
567 return context;
568}
569
570void
571gdk_wayland_drop_context_update_targets (GdkDragContext *context)
572{
573 GdkDisplay *display;
574 GdkDevice *device;
575
576 device = gdk_drag_context_get_device (context);
577 display = gdk_device_get_display (device);
578 g_list_free (context->targets);
579 context->targets = g_list_copy (gdk_wayland_selection_get_targets (display,
580 gdk_drag_get_selection (context)));
581}
582
583void
584_gdk_wayland_drag_context_set_coords (GdkDragContext *context,
585 gdouble x,
586 gdouble y)
587{
588 GdkWaylandDragContext *context_wayland;
589
590 context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
591 context_wayland->x = x;
592 context_wayland->y = y;
593}
594
595void
596_gdk_wayland_drag_context_set_source_window (GdkDragContext *context,
597 GdkWindow *window)
598{
599 if (context->source_window)
600 g_object_unref (context->source_window);
601
602 context->source_window = window ? g_object_ref (window) : NULL;
603}
604
605void
606_gdk_wayland_drag_context_set_dest_window (GdkDragContext *context,
607 GdkWindow *dest_window,
608 uint32_t serial)
609{
610 if (context->dest_window)
611 g_object_unref (context->dest_window);
612
613 context->dest_window = dest_window ? g_object_ref (dest_window) : NULL;
614 GDK_WAYLAND_DRAG_CONTEXT (context)->serial = serial;
615 gdk_wayland_drop_context_update_targets (context);
616}
617
618GdkDragContext *
619gdk_wayland_drag_context_lookup_by_data_source (struct wl_data_source *source)
620{
621 GList *l;
622
623 for (l = contexts; l; l = l->next)
624 {
625 GdkWaylandDragContext *wayland_context = l->data;
626
627 if (wayland_context->data_source == source)
628 return l->data;
629 }
630
631 return NULL;
632}
633
634GdkDragContext *
635gdk_wayland_drag_context_lookup_by_source_window (GdkWindow *window)
636{
637 GList *l;
638
639 for (l = contexts; l; l = l->next)
640 {
641 if (window == gdk_drag_context_get_source_window (l->data))
642 return l->data;
643 }
644
645 return NULL;
646}
647
648struct wl_data_source *
649gdk_wayland_drag_context_get_data_source (GdkDragContext *context)
650{
651 return GDK_WAYLAND_DRAG_CONTEXT (context)->data_source;
652}
653