1/* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser 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 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25#include "config.h"
26
27#include "gdkx11dnd.h"
28
29#include "gdk-private.h"
30#include "gdkasync.h"
31#include "gdkclipboardprivate.h"
32#include "gdkclipboard-x11.h"
33#include "gdkdeviceprivate.h"
34#include "gdkdevice-xi2-private.h"
35#include "gdkdisplay-x11.h"
36#include "gdkdragprivate.h"
37#include "gdkeventsprivate.h"
38#include "gdksurfaceprivate.h"
39#include "gdkintl.h"
40#include "gdkprivate-x11.h"
41#include "gdkscreen-x11.h"
42#include "gdkseatprivate.h"
43#include "gdkselectioninputstream-x11.h"
44#include "gdkselectionoutputstream-x11.h"
45
46#include <math.h>
47#include <X11/Xlib.h>
48#include <X11/Xutil.h>
49#include <X11/Xatom.h>
50#include <X11/extensions/shape.h>
51
52#include <string.h>
53
54typedef enum {
55 GDK_DRAG_STATUS_DRAG,
56 GDK_DRAG_STATUS_MOTION_WAIT,
57 GDK_DRAG_STATUS_ACTION_WAIT,
58 GDK_DRAG_STATUS_DROP
59} GtkDragStatus;
60
61/*
62 * GdkDragProtocol:
63 * @GDK_DRAG_PROTO_NONE: no protocol.
64 * @GDK_DRAG_PROTO_XDND: The Xdnd protocol.
65 * @GDK_DRAG_PROTO_ROOTWIN: An extension to the Xdnd protocol for
66 * unclaimed root window drops.
67 *
68 * Used in `GdkDrag` to indicate the protocol according to
69 * which DND is done.
70 */
71typedef enum
72{
73 GDK_DRAG_PROTO_NONE = 0,
74 GDK_DRAG_PROTO_XDND,
75 GDK_DRAG_PROTO_ROOTWIN
76} GdkDragProtocol;
77
78typedef struct {
79 guint32 xid;
80 int x, y, width, height;
81 gboolean mapped;
82 gboolean shape_selected;
83 gboolean shape_valid;
84 cairo_region_t *shape;
85} GdkCacheChild;
86
87struct _GdkSurfaceCache {
88 GList *children;
89 GHashTable *child_hash;
90 guint old_event_mask;
91 GdkDisplay *display;
92 int ref_count;
93};
94
95
96struct _GdkX11Drag
97{
98 GdkDrag drag;
99
100 GdkDragProtocol protocol;
101
102 int start_x; /* Where the drag started */
103 int start_y;
104 guint16 last_x; /* Coordinates from last event */
105 guint16 last_y;
106 gulong timestamp; /* Timestamp we claimed the DND selection with */
107 GdkDragAction xdnd_actions; /* What is currently set in XdndActionList */
108 guint version; /* Xdnd protocol version */
109
110 GdkSurfaceCache *cache;
111
112 GdkSurface *drag_surface;
113
114 GdkSurface *ipc_surface;
115 GdkCursor *cursor;
116 GdkSeat *grab_seat;
117 GdkDragAction actions;
118 GdkDragAction current_action;
119
120 int hot_x;
121 int hot_y;
122
123 Window dest_xid; /* The last window we looked up */
124 Window proxy_xid; /* The proxy window for dest_xid (or dest_xid if no proxying happens) */
125 Window drop_xid; /* The (non-proxied) window that is receiving drops */
126 guint xdnd_targets_set : 1; /* Whether we've already set XdndTypeList */
127 guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
128 guint drag_status : 4; /* current status of drag */
129 guint drop_failed : 1; /* Whether the drop was unsuccessful */
130};
131
132struct _GdkX11DragClass
133{
134 GdkDragClass parent_class;
135};
136
137typedef struct {
138 int keysym;
139 int modifiers;
140} GrabKey;
141
142static GrabKey grab_keys[] = {
143 { XK_Escape, 0 },
144 { XK_space, 0 },
145 { XK_KP_Space, 0 },
146 { XK_Return, 0 },
147 { XK_KP_Enter, 0 },
148 { XK_Up, 0 },
149 { XK_Up, Mod1Mask },
150 { XK_Down, 0 },
151 { XK_Down, Mod1Mask },
152 { XK_Left, 0 },
153 { XK_Left, Mod1Mask },
154 { XK_Right, 0 },
155 { XK_Right, Mod1Mask },
156 { XK_KP_Up, 0 },
157 { XK_KP_Up, Mod1Mask },
158 { XK_KP_Down, 0 },
159 { XK_KP_Down, Mod1Mask },
160 { XK_KP_Left, 0 },
161 { XK_KP_Left, Mod1Mask },
162 { XK_KP_Right, 0 },
163 { XK_KP_Right, Mod1Mask }
164};
165
166/* Forward declarations */
167
168static GdkSurfaceCache *gdk_surface_cache_ref (GdkSurfaceCache *cache);
169static void gdk_surface_cache_unref (GdkSurfaceCache *cache);
170
171gboolean gdk_x11_drag_handle_event (GdkDrag *drag,
172 GdkEvent *event);
173
174static GList *drags;
175static GSList *window_caches;
176
177G_DEFINE_TYPE (GdkX11Drag, gdk_x11_drag, GDK_TYPE_DRAG)
178
179static void
180gdk_x11_drag_init (GdkX11Drag *drag)
181{
182 drags = g_list_prepend (list: drags, data: drag);
183}
184
185static void gdk_x11_drag_finalize (GObject *object);
186static Window gdk_x11_drag_find_surface (GdkDrag *drag,
187 GdkSurface *drag_surface,
188 int x_root,
189 int y_root,
190 GdkDragProtocol *protocol);
191static gboolean gdk_x11_drag_drag_motion (GdkDrag *drag,
192 Window proxy_xid,
193 GdkDragProtocol protocol,
194 int x_root,
195 int y_root,
196 GdkDragAction suggested_action,
197 GdkDragAction possible_actions,
198 guint32 time);
199static void gdk_x11_drag_drop (GdkDrag *drag,
200 guint32 time_);
201static GdkSurface * gdk_x11_drag_get_drag_surface (GdkDrag *drag);
202static void gdk_x11_drag_set_hotspot (GdkDrag *drag,
203 int hot_x,
204 int hot_y);
205static void gdk_x11_drag_drop_done (GdkDrag *drag,
206 gboolean success);
207static void gdk_x11_drag_set_cursor (GdkDrag *drag,
208 GdkCursor *cursor);
209static void gdk_x11_drag_cancel (GdkDrag *drag,
210 GdkDragCancelReason reason);
211static void gdk_x11_drag_drop_performed (GdkDrag *drag,
212 guint32 time);
213
214static void
215gdk_x11_drag_class_init (GdkX11DragClass *klass)
216{
217 GObjectClass *object_class = G_OBJECT_CLASS (klass);
218 GdkDragClass *drag_class = GDK_DRAG_CLASS (klass);
219
220 object_class->finalize = gdk_x11_drag_finalize;
221
222 drag_class->get_drag_surface = gdk_x11_drag_get_drag_surface;
223 drag_class->set_hotspot = gdk_x11_drag_set_hotspot;
224 drag_class->drop_done = gdk_x11_drag_drop_done;
225 drag_class->set_cursor = gdk_x11_drag_set_cursor;
226 drag_class->cancel = gdk_x11_drag_cancel;
227 drag_class->drop_performed = gdk_x11_drag_drop_performed;
228 drag_class->handle_event = gdk_x11_drag_handle_event;
229}
230
231static void
232gdk_x11_drag_finalize (GObject *object)
233{
234 GdkDrag *drag = GDK_DRAG (object);
235 GdkX11Drag *x11_drag = GDK_X11_DRAG (object);
236 GdkSurface *drag_surface, *ipc_surface;
237
238 if (x11_drag->cache)
239 gdk_surface_cache_unref (cache: x11_drag->cache);
240
241 drags = g_list_remove (list: drags, data: drag);
242
243 drag_surface = x11_drag->drag_surface;
244 ipc_surface = x11_drag->ipc_surface;
245
246 G_OBJECT_CLASS (gdk_x11_drag_parent_class)->finalize (object);
247
248 if (drag_surface)
249 gdk_surface_destroy (surface: drag_surface);
250 if (ipc_surface)
251 gdk_surface_destroy (surface: ipc_surface);
252}
253
254/* Drag Contexts */
255
256GdkDrag *
257gdk_x11_drag_find (GdkDisplay *display,
258 Window source_xid,
259 Window dest_xid)
260{
261 GList *tmp_list;
262 GdkDrag *drag;
263 GdkX11Drag *drag_x11;
264 Window drag_dest_xid;
265 GdkSurface *surface;
266 Window surface_xid;
267
268 for (tmp_list = drags; tmp_list; tmp_list = tmp_list->next)
269 {
270 drag = (GdkDrag *)tmp_list->data;
271 drag_x11 = (GdkX11Drag *)drag;
272
273 if (gdk_drag_get_display (drag) != display)
274 continue;
275
276 g_object_get (object: drag, first_property_name: "surface", &surface, NULL);
277 surface_xid = surface ? GDK_SURFACE_XID (surface) : None;
278 g_object_unref (object: surface);
279
280 drag_dest_xid = drag_x11->proxy_xid
281 ? (drag_x11->drop_xid
282 ? drag_x11->drop_xid
283 : drag_x11->proxy_xid)
284 : None;
285
286 if (((source_xid == None) || (surface && (surface_xid == source_xid))) &&
287 ((dest_xid == None) || (drag_dest_xid == dest_xid)))
288 return drag;
289 }
290
291 return NULL;
292}
293
294static void
295precache_target_list (GdkDrag *drag)
296{
297 GdkContentFormats *formats;
298 const char * const *atoms;
299 gsize n_atoms;
300
301 formats = gdk_content_formats_ref (formats: gdk_drag_get_formats (drag));
302 formats = gdk_content_formats_union_serialize_mime_types (formats);
303
304 atoms = gdk_content_formats_get_mime_types (formats, n_mime_types: &n_atoms);
305
306 _gdk_x11_precache_atoms (display: gdk_drag_get_display (drag),
307 atom_names: (const char **) atoms,
308 n_atoms);
309
310 gdk_content_formats_unref (formats);
311}
312
313/* Utility functions */
314
315static void
316free_cache_child (GdkCacheChild *child,
317 GdkDisplay *display)
318{
319 if (child->shape)
320 cairo_region_destroy (region: child->shape);
321
322 if (child->shape_selected && display)
323 {
324 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
325
326 XShapeSelectInput (display_x11->xdisplay, child->xid, 0);
327 }
328
329 g_free (mem: child);
330}
331
332static void
333gdk_surface_cache_add (GdkSurfaceCache *cache,
334 guint32 xid,
335 int x,
336 int y,
337 int width,
338 int height,
339 gboolean mapped)
340{
341 GdkCacheChild *child = g_new (GdkCacheChild, 1);
342
343 child->xid = xid;
344 child->x = x;
345 child->y = y;
346 child->width = width;
347 child->height = height;
348 child->mapped = mapped;
349 child->shape_selected = FALSE;
350 child->shape_valid = FALSE;
351 child->shape = NULL;
352
353 cache->children = g_list_prepend (list: cache->children, data: child);
354 g_hash_table_insert (hash_table: cache->child_hash, GUINT_TO_POINTER (xid),
355 value: cache->children);
356}
357
358GdkFilterReturn
359gdk_surface_cache_shape_filter (const XEvent *xevent,
360 gpointer data)
361{
362 GdkSurfaceCache *cache = data;
363
364 GdkX11Display *display = GDK_X11_DISPLAY (cache->display);
365
366 if (display->have_shapes &&
367 xevent->type == display->shape_event_base + ShapeNotify)
368 {
369 XShapeEvent *xse = (XShapeEvent*)xevent;
370 GList *node;
371
372 node = g_hash_table_lookup (hash_table: cache->child_hash,
373 GUINT_TO_POINTER (xse->window));
374 if (node)
375 {
376 GdkCacheChild *child = node->data;
377 child->shape_valid = FALSE;
378 if (child->shape)
379 {
380 cairo_region_destroy (region: child->shape);
381 child->shape = NULL;
382 }
383 }
384
385 return GDK_FILTER_REMOVE;
386 }
387
388 return GDK_FILTER_CONTINUE;
389}
390
391GdkFilterReturn
392gdk_surface_cache_filter (const XEvent *xevent,
393 gpointer data)
394{
395 GdkSurfaceCache *cache = data;
396
397 switch (xevent->type)
398 {
399 case CirculateNotify:
400 break;
401 case ConfigureNotify:
402 {
403 const XConfigureEvent *xce = &xevent->xconfigure;
404 GList *node;
405
406 node = g_hash_table_lookup (hash_table: cache->child_hash,
407 GUINT_TO_POINTER (xce->window));
408 if (node)
409 {
410 GdkCacheChild *child = node->data;
411 child->x = xce->x;
412 child->y = xce->y;
413 child->width = xce->width;
414 child->height = xce->height;
415 if (xce->above == None && (node->next))
416 {
417 GList *last = g_list_last (list: cache->children);
418 cache->children = g_list_remove_link (list: cache->children, llink: node);
419 last->next = node;
420 node->next = NULL;
421 node->prev = last;
422 }
423 else
424 {
425 GList *above_node = g_hash_table_lookup (hash_table: cache->child_hash,
426 GUINT_TO_POINTER (xce->above));
427 if (above_node && node->next != above_node)
428 {
429 /* Put the window above (before in the list) above_node */
430 cache->children = g_list_remove_link (list: cache->children, llink: node);
431 node->prev = above_node->prev;
432 if (node->prev)
433 node->prev->next = node;
434 else
435 cache->children = node;
436 node->next = above_node;
437 above_node->prev = node;
438 }
439 }
440 }
441 break;
442 }
443 case CreateNotify:
444 {
445 const XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
446
447 if (!g_hash_table_lookup (hash_table: cache->child_hash,
448 GUINT_TO_POINTER (xcwe->window)))
449 gdk_surface_cache_add (cache, xid: xcwe->window,
450 x: xcwe->x, y: xcwe->y, width: xcwe->width, height: xcwe->height,
451 FALSE);
452 break;
453 }
454 case DestroyNotify:
455 {
456 const XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
457 GList *node;
458
459 node = g_hash_table_lookup (hash_table: cache->child_hash,
460 GUINT_TO_POINTER (xdwe->window));
461 if (node)
462 {
463 GdkCacheChild *child = node->data;
464
465 g_hash_table_remove (hash_table: cache->child_hash,
466 GUINT_TO_POINTER (xdwe->window));
467 cache->children = g_list_remove_link (list: cache->children, llink: node);
468 /* window is destroyed, no need to disable ShapeNotify */
469 free_cache_child (child, NULL);
470 g_list_free_1 (list: node);
471 }
472 break;
473 }
474 case MapNotify:
475 {
476 const XMapEvent *xme = &xevent->xmap;
477 GList *node;
478
479 node = g_hash_table_lookup (hash_table: cache->child_hash,
480 GUINT_TO_POINTER (xme->window));
481 if (node)
482 {
483 GdkCacheChild *child = node->data;
484 child->mapped = TRUE;
485 }
486 break;
487 }
488 case ReparentNotify:
489 break;
490 case UnmapNotify:
491 {
492 const XMapEvent *xume = &xevent->xmap;
493 GList *node;
494
495 node = g_hash_table_lookup (hash_table: cache->child_hash,
496 GUINT_TO_POINTER (xume->window));
497 if (node)
498 {
499 GdkCacheChild *child = node->data;
500 child->mapped = FALSE;
501 }
502 break;
503 }
504 default:
505 return GDK_FILTER_CONTINUE;
506 }
507 return GDK_FILTER_REMOVE;
508}
509
510static GdkSurfaceCache *
511gdk_surface_cache_new (GdkDisplay *display)
512{
513 XWindowAttributes xwa;
514 GdkX11Screen *screen = GDK_X11_DISPLAY (display)->screen;
515 Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
516 Window xroot_window = GDK_DISPLAY_XROOTWIN (display);
517 GdkChildInfoX11 *children;
518 guint nchildren, i;
519
520 GdkSurfaceCache *result = g_new (GdkSurfaceCache, 1);
521
522 result->children = NULL;
523 result->child_hash = g_hash_table_new (hash_func: g_direct_hash, NULL);
524 result->display = display;
525 result->ref_count = 1;
526
527 XGetWindowAttributes (xdisplay, xroot_window, &xwa);
528 result->old_event_mask = xwa.your_event_mask;
529
530 if (G_UNLIKELY (!GDK_X11_DISPLAY (display)->trusted_client))
531 {
532 GList *toplevel_windows, *list;
533 GdkSurface *surface;
534 GdkX11Surface *impl;
535 int x, y, width, height;
536
537 toplevel_windows = gdk_x11_display_get_toplevel_windows (display);
538 for (list = toplevel_windows; list; list = list->next)
539 {
540 surface = GDK_SURFACE (list->data);
541 impl = GDK_X11_SURFACE (surface);
542 gdk_surface_get_geometry (surface, x: &x, y: &y, width: &width, height: &height);
543 gdk_surface_cache_add (cache: result, GDK_SURFACE_XID (surface),
544 x: x * impl->surface_scale, y: y * impl->surface_scale,
545 width: width * impl->surface_scale,
546 height: height * impl->surface_scale,
547 mapped: gdk_surface_get_mapped (surface));
548 }
549 return result;
550 }
551
552 XSelectInput (xdisplay, xroot_window, result->old_event_mask | SubstructureNotifyMask);
553
554 if (!_gdk_x11_get_window_child_info (display,
555 window: xroot_window,
556 FALSE, NULL,
557 children: &children, nchildren: &nchildren))
558 return result;
559
560 for (i = 0; i < nchildren ; i++)
561 {
562 gdk_surface_cache_add (cache: result, xid: children[i].window,
563 x: children[i].x, y: children[i].y, width: children[i].width, height: children[i].height,
564 mapped: children[i].is_mapped);
565 }
566
567 g_free (mem: children);
568
569 return result;
570}
571
572static void
573gdk_surface_cache_destroy (GdkSurfaceCache *cache)
574{
575 XSelectInput (GDK_DISPLAY_XDISPLAY (cache->display),
576 GDK_DISPLAY_XROOTWIN (cache->display),
577 cache->old_event_mask);
578
579 gdk_x11_display_error_trap_push (display: cache->display);
580 g_list_foreach (list: cache->children, func: (GFunc)free_cache_child, user_data: cache->display);
581 gdk_x11_display_error_trap_pop_ignored (display: cache->display);
582
583 g_list_free (list: cache->children);
584 g_hash_table_destroy (hash_table: cache->child_hash);
585
586 g_free (mem: cache);
587}
588
589static GdkSurfaceCache *
590gdk_surface_cache_ref (GdkSurfaceCache *cache)
591{
592 cache->ref_count += 1;
593
594 return cache;
595}
596
597static void
598gdk_surface_cache_unref (GdkSurfaceCache *cache)
599{
600 g_assert (cache->ref_count > 0);
601
602 cache->ref_count -= 1;
603
604 if (cache->ref_count == 0)
605 {
606 window_caches = g_slist_remove (list: window_caches, data: cache);
607 gdk_surface_cache_destroy (cache);
608 }
609}
610
611GdkSurfaceCache *
612gdk_surface_cache_get (GdkDisplay *display)
613{
614 GSList *list;
615 GdkSurfaceCache *cache;
616
617 for (list = window_caches; list; list = list->next)
618 {
619 cache = list->data;
620 if (cache->display == display)
621 return gdk_surface_cache_ref (cache);
622 }
623
624 cache = gdk_surface_cache_new (display);
625
626 window_caches = g_slist_prepend (list: window_caches, data: cache);
627
628 return cache;
629}
630
631static gboolean
632is_pointer_within_shape (GdkDisplay *display,
633 GdkCacheChild *child,
634 int x_pos,
635 int y_pos)
636{
637 if (!child->shape_selected)
638 {
639 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
640
641 XShapeSelectInput (display_x11->xdisplay, child->xid, ShapeNotifyMask);
642 child->shape_selected = TRUE;
643 }
644 if (!child->shape_valid)
645 {
646 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
647 cairo_region_t *input_shape;
648
649 child->shape = NULL;
650 if (display_x11->have_shapes)
651 child->shape = _gdk_x11_xwindow_get_shape (xdisplay: display_x11->xdisplay,
652 window: child->xid, scale: 1, ShapeBounding);
653#ifdef ShapeInput
654 input_shape = NULL;
655 if (display_x11->have_input_shapes)
656 input_shape = _gdk_x11_xwindow_get_shape (xdisplay: display_x11->xdisplay,
657 window: child->xid, scale: 1, ShapeInput);
658
659 if (child->shape && input_shape)
660 {
661 cairo_region_intersect (dst: child->shape, other: input_shape);
662 cairo_region_destroy (region: input_shape);
663 }
664 else if (input_shape)
665 {
666 child->shape = input_shape;
667 }
668#endif
669
670 child->shape_valid = TRUE;
671 }
672
673 return child->shape == NULL ||
674 cairo_region_contains_point (region: child->shape, x: x_pos, y: y_pos);
675}
676
677static Window
678get_client_window_at_coords_recurse (GdkDisplay *display,
679 Window win,
680 gboolean is_toplevel,
681 int x,
682 int y)
683{
684 GdkChildInfoX11 *children;
685 unsigned int nchildren;
686 int i;
687 gboolean found_child = FALSE;
688 GdkChildInfoX11 child = { 0, };
689 gboolean has_wm_state = FALSE;
690
691 if (!_gdk_x11_get_window_child_info (display, window: win, TRUE,
692 win_has_wm_state: is_toplevel? &has_wm_state : NULL,
693 children: &children, nchildren: &nchildren))
694 return None;
695
696 if (has_wm_state)
697 {
698 g_free (mem: children);
699
700 return win;
701 }
702
703 for (i = nchildren - 1; (i >= 0) && !found_child; i--)
704 {
705 GdkChildInfoX11 *cur_child = &children[i];
706
707 if ((cur_child->is_mapped) && (cur_child->window_class == InputOutput) &&
708 (x >= cur_child->x) && (x < cur_child->x + cur_child->width) &&
709 (y >= cur_child->y) && (y < cur_child->y + cur_child->height))
710 {
711 x -= cur_child->x;
712 y -= cur_child->y;
713 child = *cur_child;
714 found_child = TRUE;
715 }
716 }
717
718 g_free (mem: children);
719
720 if (found_child)
721 {
722 if (child.has_wm_state)
723 return child.window;
724 else
725 return get_client_window_at_coords_recurse (display, win: child.window, FALSE, x, y);
726 }
727 else
728 return None;
729}
730
731static Window
732get_client_window_at_coords (GdkSurfaceCache *cache,
733 Window ignore,
734 int x_root,
735 int y_root)
736{
737 GList *tmp_list;
738 Window retval = None;
739 GdkDisplay *display;
740
741 display = cache->display;
742
743 gdk_x11_display_error_trap_push (display);
744
745 tmp_list = cache->children;
746
747 while (tmp_list && !retval)
748 {
749 GdkCacheChild *child = tmp_list->data;
750
751 if ((child->xid != ignore) && (child->mapped))
752 {
753 if ((x_root >= child->x) && (x_root < child->x + child->width) &&
754 (y_root >= child->y) && (y_root < child->y + child->height))
755 {
756 if (!is_pointer_within_shape (display, child,
757 x_pos: x_root - child->x,
758 y_pos: y_root - child->y))
759 {
760 tmp_list = tmp_list->next;
761 continue;
762 }
763
764 retval = get_client_window_at_coords_recurse (display,
765 win: child->xid, TRUE,
766 x: x_root - child->x,
767 y: y_root - child->y);
768 if (!retval)
769 retval = child->xid;
770 }
771 }
772 tmp_list = tmp_list->next;
773 }
774
775 gdk_x11_display_error_trap_pop_ignored (display);
776
777 if (retval)
778 return retval;
779 else
780 return GDK_DISPLAY_XROOTWIN (display);
781}
782
783/*************************************************************
784 ***************************** XDND **************************
785 *************************************************************/
786
787/* Utility functions */
788
789static struct {
790 const char *name;
791 GdkDragAction action;
792} xdnd_actions_table[] = {
793 { "XdndActionCopy", GDK_ACTION_COPY },
794 { "XdndActionMove", GDK_ACTION_MOVE },
795 { "XdndActionLink", GDK_ACTION_LINK },
796 { "XdndActionAsk", GDK_ACTION_ASK },
797 { "XdndActionPrivate", GDK_ACTION_COPY },
798 };
799
800static const int xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
801
802static GdkDragAction
803xdnd_action_from_atom (GdkDisplay *display,
804 Atom xatom)
805{
806 const char *name;
807 int i;
808
809 if (xatom == None)
810 return 0;
811
812 name = gdk_x11_get_xatom_name_for_display (display, xatom);
813
814 for (i = 0; i < xdnd_n_actions; i++)
815 if (g_str_equal (v1: name, v2: xdnd_actions_table[i].name))
816 return xdnd_actions_table[i].action;
817
818 return 0;
819}
820
821static Atom
822xdnd_action_to_atom (GdkDisplay *display,
823 GdkDragAction action)
824{
825 int i;
826
827 for (i = 0; i < xdnd_n_actions; i++)
828 if (action == xdnd_actions_table[i].action)
829 return gdk_x11_get_xatom_by_name_for_display (display, atom_name: xdnd_actions_table[i].name);
830
831 return None;
832}
833
834/* Source side */
835
836void
837gdk_x11_drag_handle_status (GdkDisplay *display,
838 const XEvent *xevent)
839{
840 guint32 dest_surface = xevent->xclient.data.l[0];
841 guint32 flags = xevent->xclient.data.l[1];
842 Atom action = xevent->xclient.data.l[4];
843 GdkDrag *drag;
844
845 drag = gdk_x11_drag_find (display, source_xid: xevent->xclient.window, dest_xid: dest_surface);
846
847 GDK_DISPLAY_NOTE (display, DND,
848 g_message ("XdndStatus: dest_surface: %#x action: %ld",
849 dest_surface, action));
850
851 if (drag)
852 {
853 GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
854 if (drag_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
855 drag_x11->drag_status = GDK_DRAG_STATUS_DRAG;
856
857 if (!(action != 0) != !(flags & 1))
858 {
859 GDK_DISPLAY_NOTE (display, DND,
860 g_warning ("Received status event with flags not corresponding to action!"));
861 action = 0;
862 }
863
864 gdk_drag_set_selected_action (drag, action: xdnd_action_from_atom (display, xatom: action));
865 drag_x11->current_action = action;
866 }
867}
868
869void
870gdk_x11_drag_handle_finished (GdkDisplay *display,
871 const XEvent *xevent)
872{
873 guint32 dest_surface = xevent->xclient.data.l[0];
874 GdkDrag *drag;
875 GdkX11Drag *drag_x11;
876
877 drag = gdk_x11_drag_find (display, source_xid: xevent->xclient.window, dest_xid: dest_surface);
878
879 GDK_DISPLAY_NOTE (display, DND,
880 g_message ("XdndFinished: dest_surface: %#x", dest_surface));
881
882 if (drag)
883 {
884 drag_x11 = GDK_X11_DRAG (drag);
885 if (drag_x11->version == 5)
886 drag_x11->drop_failed = xevent->xclient.data.l[1] == 0;
887
888 g_object_ref (drag);
889 g_signal_emit_by_name (instance: drag, detailed_signal: "dnd-finished");
890 gdk_drag_drop_done (drag, success: !drag_x11->drop_failed);
891 g_object_unref (object: drag);
892 }
893}
894
895static void
896xdnd_set_targets (GdkX11Drag *drag_x11)
897{
898 GdkDrag *drag = GDK_DRAG (drag_x11);
899 Atom *atomlist;
900 const char * const *atoms;
901 gsize i, n_atoms;
902 GdkDisplay *display = gdk_drag_get_display (drag);
903 GdkContentFormats *formats;
904
905 formats = gdk_content_formats_ref (formats: gdk_drag_get_formats (drag));
906 formats = gdk_content_formats_union_serialize_mime_types (formats);
907
908 atoms = gdk_content_formats_get_mime_types (formats, n_mime_types: &n_atoms);
909 atomlist = g_new (Atom, n_atoms);
910 for (i = 0; i < n_atoms; i++)
911 atomlist[i] = gdk_x11_get_xatom_by_name_for_display (display, atom_name: atoms[i]);
912
913 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
914 GDK_SURFACE_XID (drag_x11->ipc_surface),
915 gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndTypeList"),
916 XA_ATOM, 32, PropModeReplace,
917 (guchar *)atomlist, n_atoms);
918
919 g_free (mem: atomlist);
920
921 drag_x11->xdnd_targets_set = 1;
922
923 gdk_content_formats_unref (formats);
924}
925
926static void
927xdnd_set_actions (GdkX11Drag *drag_x11)
928{
929 GdkDrag *drag = GDK_DRAG (drag_x11);
930 Atom *atomlist;
931 int i;
932 int n_atoms;
933 guint actions;
934 GdkDisplay *display = gdk_drag_get_display (drag);
935
936 actions = gdk_drag_get_actions (drag);
937 n_atoms = 0;
938 for (i = 0; i < xdnd_n_actions; i++)
939 {
940 if (actions & xdnd_actions_table[i].action)
941 {
942 actions &= ~xdnd_actions_table[i].action;
943 n_atoms++;
944 }
945 }
946
947 atomlist = g_new (Atom, n_atoms);
948
949 actions = gdk_drag_get_actions (drag);
950 n_atoms = 0;
951 for (i = 0; i < xdnd_n_actions; i++)
952 {
953 if (actions & xdnd_actions_table[i].action)
954 {
955 actions &= ~xdnd_actions_table[i].action;
956 atomlist[n_atoms] = gdk_x11_get_xatom_by_name_for_display (display, atom_name: xdnd_actions_table[i].name);
957 n_atoms++;
958 }
959 }
960
961 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
962 GDK_SURFACE_XID (drag_x11->ipc_surface),
963 gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndActionList"),
964 XA_ATOM, 32, PropModeReplace,
965 (guchar *)atomlist, n_atoms);
966
967 g_free (mem: atomlist);
968
969 drag_x11->xdnd_actions = gdk_drag_get_actions (drag);
970}
971
972static void
973send_client_message_async_cb (Window window,
974 gboolean success,
975 gpointer data)
976{
977 GdkX11Drag *drag_x11 = data;
978 GdkDrag *drag = data;
979
980 GDK_DISPLAY_NOTE (gdk_drag_get_display (drag), DND,
981 g_message ("Got async callback for #%lx, success = %d",
982 window, success));
983
984 /* On failure, we immediately continue with the protocol
985 * so we don't end up blocking for a timeout
986 */
987 if (!success &&
988 window == drag_x11->proxy_xid)
989 {
990 drag_x11->proxy_xid = None;
991 gdk_drag_set_selected_action (drag, action: 0);
992 drag_x11->current_action = 0;
993 drag_x11->drag_status = GDK_DRAG_STATUS_DRAG;
994 }
995
996 g_object_unref (object: drag);
997}
998
999static void
1000send_client_message_async (GdkDrag *drag,
1001 Window window,
1002 glong event_mask,
1003 XClientMessageEvent *event_send)
1004{
1005 GdkDisplay *display = gdk_drag_get_display (drag);
1006
1007 g_object_ref (drag);
1008
1009 _gdk_x11_send_client_message_async (display, window,
1010 FALSE, event_mask, event_send,
1011 callback: send_client_message_async_cb, data: drag);
1012}
1013
1014static void
1015xdnd_send_xevent (GdkX11Drag *drag_x11,
1016 XEvent *event_send)
1017{
1018 GdkDrag *drag = GDK_DRAG (drag_x11);
1019 GdkDisplay *display = gdk_drag_get_display (drag);
1020 GdkSurface *surface;
1021 glong event_mask;
1022
1023 g_assert (event_send->xany.type == ClientMessage);
1024
1025 /* We short-circuit messages to ourselves */
1026 surface = gdk_x11_surface_lookup_for_display (display, window: drag_x11->proxy_xid);
1027 if (surface)
1028 {
1029 if (gdk_x11_drop_filter (surface, xevent: event_send))
1030 return;
1031 }
1032
1033 if (_gdk_x11_display_is_root_window (display, xroot_window: drag_x11->proxy_xid))
1034 event_mask = ButtonPressMask;
1035 else
1036 event_mask = 0;
1037
1038 send_client_message_async (drag, window: drag_x11->proxy_xid, event_mask,
1039 event_send: &event_send->xclient);
1040}
1041
1042static void
1043xdnd_send_enter (GdkX11Drag *drag_x11)
1044{
1045 GdkDrag *drag = GDK_DRAG (drag_x11);
1046 GdkDisplay *display = gdk_drag_get_display (drag);
1047 GdkContentFormats *formats;
1048 const char * const *mime_types;
1049 gsize i, n_mime_types;
1050 XEvent xev;
1051
1052 xev.xclient.type = ClientMessage;
1053 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndEnter");
1054 xev.xclient.format = 32;
1055 xev.xclient.window = drag_x11->drop_xid
1056 ? drag_x11->drop_xid
1057 : drag_x11->proxy_xid;
1058 xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
1059 xev.xclient.data.l[1] = (drag_x11->version << 24); /* version */
1060 xev.xclient.data.l[2] = 0;
1061 xev.xclient.data.l[3] = 0;
1062 xev.xclient.data.l[4] = 0;
1063
1064 GDK_DISPLAY_NOTE (display, DND,
1065 g_message ("Sending enter source window %#lx XDND protocol version %d\n",
1066 GDK_SURFACE_XID (drag_x11->ipc_surface), drag_x11->version));
1067 formats = gdk_content_formats_ref (formats: gdk_drag_get_formats (drag));
1068 formats = gdk_content_formats_union_serialize_mime_types (formats);
1069
1070 mime_types = gdk_content_formats_get_mime_types (formats, n_mime_types: &n_mime_types);
1071
1072 if (n_mime_types > 3)
1073 {
1074 if (!drag_x11->xdnd_targets_set)
1075 xdnd_set_targets (drag_x11);
1076 xev.xclient.data.l[1] |= 1;
1077 }
1078 else
1079 {
1080 for (i = 0; i < n_mime_types; i++)
1081 {
1082 xev.xclient.data.l[i + 2] = gdk_x11_get_xatom_by_name_for_display (display, atom_name: mime_types[i]);
1083 }
1084 }
1085
1086 xdnd_send_xevent (drag_x11, event_send: &xev);
1087
1088 gdk_content_formats_unref (formats);
1089}
1090
1091static void
1092xdnd_send_leave (GdkX11Drag *drag_x11)
1093{
1094 GdkDrag *drag = GDK_DRAG (drag_x11);
1095 GdkDisplay *display = gdk_drag_get_display (drag);
1096 XEvent xev;
1097
1098 xev.xclient.type = ClientMessage;
1099 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndLeave");
1100 xev.xclient.format = 32;
1101 xev.xclient.window = drag_x11->drop_xid
1102 ? drag_x11->drop_xid
1103 : drag_x11->proxy_xid;
1104 xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
1105 xev.xclient.data.l[1] = 0;
1106 xev.xclient.data.l[2] = 0;
1107 xev.xclient.data.l[3] = 0;
1108 xev.xclient.data.l[4] = 0;
1109
1110 xdnd_send_xevent (drag_x11, event_send: &xev);
1111}
1112
1113static void
1114xdnd_send_drop (GdkX11Drag *drag_x11,
1115 guint32 time)
1116{
1117 GdkDrag *drag = GDK_DRAG (drag_x11);
1118 GdkDisplay *display = gdk_drag_get_display (drag);
1119 XEvent xev;
1120
1121 xev.xclient.type = ClientMessage;
1122 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndDrop");
1123 xev.xclient.format = 32;
1124 xev.xclient.window = drag_x11->drop_xid
1125 ? drag_x11->drop_xid
1126 : drag_x11->proxy_xid;
1127 xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
1128 xev.xclient.data.l[1] = 0;
1129 xev.xclient.data.l[2] = time;
1130 xev.xclient.data.l[3] = 0;
1131 xev.xclient.data.l[4] = 0;
1132
1133 xdnd_send_xevent (drag_x11, event_send: &xev);
1134}
1135
1136static void
1137xdnd_send_motion (GdkX11Drag *drag_x11,
1138 int x_root,
1139 int y_root,
1140 GdkDragAction action,
1141 guint32 time)
1142{
1143 GdkDrag *drag = GDK_DRAG (drag_x11);
1144 GdkDisplay *display = gdk_drag_get_display (drag);
1145 XEvent xev;
1146
1147 xev.xclient.type = ClientMessage;
1148 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndPosition");
1149 xev.xclient.format = 32;
1150 xev.xclient.window = drag_x11->drop_xid
1151 ? drag_x11->drop_xid
1152 : drag_x11->proxy_xid;
1153 xev.xclient.data.l[0] = GDK_SURFACE_XID (drag_x11->ipc_surface);
1154 xev.xclient.data.l[1] = 0;
1155 xev.xclient.data.l[2] = (x_root << 16) | y_root;
1156 xev.xclient.data.l[3] = time;
1157 xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
1158
1159 xdnd_send_xevent (drag_x11, event_send: &xev);
1160 drag_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1161}
1162
1163static guint32
1164xdnd_check_dest (GdkDisplay *display,
1165 Window win,
1166 guint *xdnd_version)
1167{
1168 gboolean retval = FALSE;
1169 Atom type = None;
1170 int format;
1171 unsigned long nitems, after;
1172 guchar *data;
1173 Atom *version;
1174 Window *proxy_data;
1175 Window proxy;
1176 Atom xdnd_proxy_atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndProxy");
1177 Atom xdnd_aware_atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndAware");
1178
1179 proxy = None;
1180
1181 gdk_x11_display_error_trap_push (display);
1182 if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win,
1183 xdnd_proxy_atom, 0,
1184 1, False, AnyPropertyType,
1185 &type, &format, &nitems, &after,
1186 &data) == Success)
1187 {
1188 if (type != None)
1189 {
1190 proxy_data = (Window *)data;
1191
1192 if ((format == 32) && (nitems == 1))
1193 {
1194 proxy = *proxy_data;
1195 }
1196 else
1197 {
1198 GDK_DISPLAY_NOTE (display, DND,
1199 g_warning ("Invalid XdndProxy property on window %ld", win));
1200 }
1201
1202 XFree (proxy_data);
1203 }
1204
1205 if ((XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), proxy ? proxy : win,
1206 xdnd_aware_atom, 0,
1207 1, False, AnyPropertyType,
1208 &type, &format, &nitems, &after,
1209 &data) == Success) &&
1210 type != None)
1211 {
1212 version = (Atom *)data;
1213
1214 if ((format == 32) && (nitems == 1))
1215 {
1216 if (*version >= 3)
1217 retval = TRUE;
1218 if (xdnd_version)
1219 *xdnd_version = *version;
1220 }
1221 else
1222 {
1223 GDK_DISPLAY_NOTE (display, DND,
1224 g_warning ("Invalid XdndAware property on window %ld", win));
1225 }
1226
1227 XFree (version);
1228 }
1229 }
1230
1231 gdk_x11_display_error_trap_pop_ignored (display);
1232
1233 return retval ? (proxy ? proxy : win) : None;
1234}
1235
1236/* Source side */
1237
1238static void
1239gdk_drag_do_leave (GdkX11Drag *drag_x11)
1240{
1241 if (drag_x11->proxy_xid)
1242 {
1243 switch (drag_x11->protocol)
1244 {
1245 case GDK_DRAG_PROTO_XDND:
1246 xdnd_send_leave (drag_x11);
1247 break;
1248 case GDK_DRAG_PROTO_ROOTWIN:
1249 case GDK_DRAG_PROTO_NONE:
1250 default:
1251 break;
1252 }
1253
1254 drag_x11->proxy_xid = None;
1255 }
1256}
1257
1258static GdkSurface *
1259create_drag_surface (GdkDisplay *display)
1260{
1261 GdkSurface *surface;
1262
1263 surface = _gdk_x11_display_create_surface (display,
1264 surface_type: GDK_SURFACE_TEMP,
1265 NULL,
1266 x: 0, y: 0, width: 100, height: 100);
1267
1268 return surface;
1269}
1270
1271static Window
1272_gdk_x11_display_get_drag_protocol (GdkDisplay *display,
1273 Window xid,
1274 GdkDragProtocol *protocol,
1275 guint *version)
1276
1277{
1278 GdkSurface *surface;
1279 Window retval;
1280
1281 /* Check for a local drag */
1282 surface = gdk_x11_surface_lookup_for_display (display, window: xid);
1283 if (surface)
1284 {
1285 if (g_object_get_data (G_OBJECT (surface), key: "gdk-dnd-registered") != NULL)
1286 {
1287 *protocol = GDK_DRAG_PROTO_XDND;
1288 *version = 5;
1289 GDK_DISPLAY_NOTE (display, DND, g_message ("Entering local Xdnd window %#x\n", (guint) xid));
1290 return xid;
1291 }
1292 else if (_gdk_x11_display_is_root_window (display, xroot_window: xid))
1293 {
1294 *protocol = GDK_DRAG_PROTO_ROOTWIN;
1295 GDK_DISPLAY_NOTE (display, DND, g_message ("Entering root window\n"));
1296 return xid;
1297 }
1298 }
1299 else if ((retval = xdnd_check_dest (display, win: xid, xdnd_version: version)))
1300 {
1301 *protocol = GDK_DRAG_PROTO_XDND;
1302 GDK_DISPLAY_NOTE (display, DND, g_message ("Entering Xdnd window %#x\n", (guint) xid));
1303 return retval;
1304 }
1305 else
1306 {
1307 /* Check if this is a root window */
1308 gboolean rootwin = FALSE;
1309
1310 if (_gdk_x11_display_is_root_window (display, xroot_window: (Window) xid))
1311 rootwin = TRUE;
1312
1313 if (rootwin)
1314 {
1315 GDK_DISPLAY_NOTE (display, DND, g_message ("Entering root window\n"));
1316 *protocol = GDK_DRAG_PROTO_ROOTWIN;
1317 return xid;
1318 }
1319 }
1320
1321 *protocol = GDK_DRAG_PROTO_NONE;
1322
1323 return 0; /* a.k.a. None */
1324}
1325
1326static GdkSurfaceCache *
1327drag_find_window_cache (GdkX11Drag *drag_x11,
1328 GdkDisplay *display)
1329{
1330 if (!drag_x11->cache)
1331 drag_x11->cache = gdk_surface_cache_get (display);
1332
1333 return drag_x11->cache;
1334}
1335
1336static Window
1337gdk_x11_drag_find_surface (GdkDrag *drag,
1338 GdkSurface *drag_surface,
1339 int x_root,
1340 int y_root,
1341 GdkDragProtocol *protocol)
1342{
1343 GdkX11Screen *screen_x11;
1344 GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
1345 GdkSurfaceCache *window_cache;
1346 GdkDisplay *display;
1347 Window dest;
1348 Window proxy;
1349
1350 display = gdk_drag_get_display (drag);
1351 screen_x11 = GDK_X11_SCREEN(GDK_X11_DISPLAY (display)->screen);
1352
1353 window_cache = drag_find_window_cache (drag_x11, display);
1354
1355 dest = get_client_window_at_coords (cache: window_cache,
1356 ignore: drag_surface && GDK_IS_X11_SURFACE (drag_surface) ?
1357 GDK_SURFACE_XID (drag_surface) : None,
1358 x_root: x_root * screen_x11->surface_scale,
1359 y_root: y_root * screen_x11->surface_scale);
1360
1361 if (drag_x11->dest_xid != dest)
1362 {
1363 drag_x11->dest_xid = dest;
1364
1365 /* Check if new destination accepts drags, and which protocol */
1366
1367 /* There is some ugliness here. We actually need to pass
1368 * _three_ pieces of information to drag_motion - dest_surface,
1369 * protocol, and the XID of the unproxied window. The first
1370 * two are passed explicitly, the third implicitly through
1371 * protocol->dest_xid.
1372 */
1373 proxy = _gdk_x11_display_get_drag_protocol (display,
1374 xid: dest,
1375 protocol,
1376 version: &drag_x11->version);
1377 }
1378 else
1379 {
1380 proxy = dest;
1381 *protocol = drag_x11->protocol;
1382 }
1383
1384 return proxy;
1385}
1386
1387static void
1388move_drag_surface (GdkDrag *drag,
1389 guint x_root,
1390 guint y_root)
1391{
1392 GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
1393
1394 gdk_x11_surface_move (surface: drag_x11->drag_surface,
1395 x: x_root - drag_x11->hot_x,
1396 y: y_root - drag_x11->hot_y);
1397 gdk_x11_surface_raise (surface: drag_x11->drag_surface);
1398}
1399
1400static gboolean
1401gdk_x11_drag_drag_motion (GdkDrag *drag,
1402 Window proxy_xid,
1403 GdkDragProtocol protocol,
1404 int x_root,
1405 int y_root,
1406 GdkDragAction suggested_action,
1407 GdkDragAction possible_actions,
1408 guint32 time)
1409{
1410 GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
1411
1412 if (drag_x11->drag_surface)
1413 move_drag_surface (drag, x_root, y_root);
1414
1415 gdk_drag_set_actions (drag, actions: possible_actions);
1416
1417 if (protocol == GDK_DRAG_PROTO_XDND && drag_x11->version == 0)
1418 {
1419 /* This ugly hack is necessary since GTK doesn't know about
1420 * the XDND protocol version, and in particular doesn't know
1421 * that gdk_drag_find_window() has the side-effect
1422 * of setting drag_x11->version, and therefore sometimes call
1423 * gdk_x11_drag_drag_motion() without a prior call to
1424 * gdk_drag_find_window(). This happens, e.g.
1425 * when GTK is proxying DND events to embedded windows.
1426 */
1427 if (proxy_xid)
1428 {
1429 GdkDisplay *display = gdk_drag_get_display (drag);
1430
1431 xdnd_check_dest (display,
1432 win: proxy_xid,
1433 xdnd_version: &drag_x11->version);
1434 }
1435 }
1436
1437 if (drag_x11->proxy_xid != proxy_xid)
1438 {
1439 /* Send a leave to the last destination */
1440 gdk_drag_do_leave (drag_x11);
1441 drag_x11->drag_status = GDK_DRAG_STATUS_DRAG;
1442
1443 /* Check if new destination accepts drags, and which protocol */
1444
1445 if (proxy_xid)
1446 {
1447 drag_x11->proxy_xid = proxy_xid;
1448 drag_x11->drop_xid = drag_x11->dest_xid;
1449 drag_x11->protocol = protocol;
1450
1451 switch (protocol)
1452 {
1453 case GDK_DRAG_PROTO_XDND:
1454 xdnd_send_enter (drag_x11);
1455 break;
1456
1457 case GDK_DRAG_PROTO_ROOTWIN:
1458 case GDK_DRAG_PROTO_NONE:
1459 default:
1460 break;
1461 }
1462 }
1463 else
1464 {
1465 drag_x11->proxy_xid = None;
1466 drag_x11->drop_xid = None;
1467 gdk_drag_set_selected_action (drag, action: 0);
1468 }
1469
1470 /* Push a status event, to let the client know that
1471 * the drag changed
1472 */
1473 drag_x11->current_action = gdk_drag_get_selected_action (drag);
1474 }
1475
1476 /* When we have a Xdnd target, make sure our XdndActionList
1477 * matches the current actions;
1478 */
1479 if (protocol == GDK_DRAG_PROTO_XDND && drag_x11->xdnd_actions != gdk_drag_get_actions (drag))
1480 {
1481 if (proxy_xid)
1482 {
1483 GdkDisplay *display = gdk_drag_get_display (drag);
1484 GdkDrop *drop = GDK_X11_DISPLAY (display)->current_drop;
1485
1486 if (drop && GDK_SURFACE_XID (gdk_drop_get_surface (drop)) == proxy_xid)
1487 gdk_x11_drop_read_actions (drop);
1488 else
1489 xdnd_set_actions (drag_x11);
1490 }
1491 }
1492
1493 /* Send a drag-motion event */
1494
1495 drag_x11->last_x = x_root;
1496 drag_x11->last_y = y_root;
1497
1498 if (drag_x11->proxy_xid)
1499 {
1500 GdkDisplay *display = gdk_drag_get_display (drag);
1501 GdkX11Screen *screen_x11 = GDK_X11_SCREEN(GDK_X11_DISPLAY (display)->screen);
1502
1503 if (drag_x11->drag_status == GDK_DRAG_STATUS_DRAG)
1504 {
1505 switch (drag_x11->protocol)
1506 {
1507 case GDK_DRAG_PROTO_XDND:
1508 xdnd_send_motion (drag_x11, x_root: x_root * screen_x11->surface_scale, y_root: y_root * screen_x11->surface_scale, action: suggested_action, time);
1509 break;
1510
1511 case GDK_DRAG_PROTO_ROOTWIN:
1512 {
1513 GdkContentFormats *formats = gdk_drag_get_formats (drag);
1514 /* GTK traditionally has used application/x-rootwin-drop,
1515 * but the XDND spec specifies x-rootwindow-drop.
1516 */
1517 if (gdk_content_formats_contain_mime_type (formats, mime_type: "application/x-rootwindow-drop") ||
1518 gdk_content_formats_contain_mime_type (formats, mime_type: "application/x-rootwin-drop"))
1519 gdk_drag_set_selected_action (drag, action: suggested_action);
1520 else
1521 gdk_drag_set_selected_action (drag, action: 0);
1522
1523 drag_x11->current_action = gdk_drag_get_selected_action (drag);
1524 }
1525 break;
1526 case GDK_DRAG_PROTO_NONE:
1527 g_warning ("Invalid drag protocol %u in gdk_x11_drag_drag_motion()", drag_x11->protocol);
1528 break;
1529 default:
1530 break;
1531 }
1532 }
1533 else
1534 return TRUE;
1535 }
1536
1537 return FALSE;
1538}
1539
1540static void
1541gdk_x11_drag_drop (GdkDrag *drag,
1542 guint32 time)
1543{
1544 GdkX11Drag *drag_x11 = GDK_X11_DRAG (drag);
1545
1546 if (drag_x11->proxy_xid)
1547 {
1548 switch (drag_x11->protocol)
1549 {
1550 case GDK_DRAG_PROTO_XDND:
1551 xdnd_send_drop (drag_x11, time);
1552 break;
1553
1554 case GDK_DRAG_PROTO_ROOTWIN:
1555 g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
1556 break;
1557 case GDK_DRAG_PROTO_NONE:
1558 g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
1559 break;
1560 default:
1561 g_warning ("Drag protocol %u is not valid in gdk_drag_drop()", drag_x11->protocol);
1562 break;
1563 }
1564 }
1565}
1566
1567/* Destination side */
1568
1569void
1570_gdk_x11_surface_register_dnd (GdkSurface *surface)
1571{
1572 static const gulong xdnd_version = 5;
1573 GdkDisplay *display = gdk_surface_get_display (surface);
1574
1575 g_return_if_fail (surface != NULL);
1576
1577 if (g_object_get_data (G_OBJECT (surface), key: "gdk-dnd-registered") != NULL)
1578 return;
1579 else
1580 g_object_set_data (G_OBJECT (surface), key: "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
1581
1582 /* Set XdndAware */
1583
1584 /* The property needs to be of type XA_ATOM, not XA_INTEGER. Blech */
1585 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
1586 GDK_SURFACE_XID (surface),
1587 gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndAware"),
1588 XA_ATOM, 32, PropModeReplace,
1589 (guchar *)&xdnd_version, 1);
1590}
1591
1592static GdkSurface *
1593gdk_x11_drag_get_drag_surface (GdkDrag *drag)
1594{
1595 return GDK_X11_DRAG (drag)->drag_surface;
1596}
1597
1598static void
1599gdk_x11_drag_set_hotspot (GdkDrag *drag,
1600 int hot_x,
1601 int hot_y)
1602{
1603 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
1604
1605 x11_drag->hot_x = hot_x;
1606 x11_drag->hot_y = hot_y;
1607
1608 if (x11_drag->grab_seat)
1609 {
1610 /* DnD is managed, update current position */
1611 move_drag_surface (drag, x_root: x11_drag->last_x, y_root: x11_drag->last_y);
1612 }
1613}
1614
1615static void
1616gdk_x11_drag_default_output_closed (GObject *stream,
1617 GAsyncResult *result,
1618 gpointer user_data)
1619{
1620 GError *error = NULL;
1621
1622 if (!g_output_stream_close_finish (G_OUTPUT_STREAM (stream), result, error: &error))
1623 {
1624 GDK_NOTE (DND,
1625 g_printerr ("failed to close stream: %s\n",
1626 error->message));
1627 g_error_free (error);
1628 }
1629
1630 g_object_unref (object: stream);
1631}
1632
1633static void
1634gdk_x11_drag_default_output_done (GObject *drag,
1635 GAsyncResult *result,
1636 gpointer user_data)
1637{
1638 GOutputStream *stream = user_data;
1639 GError *error = NULL;
1640
1641 if (!gdk_drag_write_finish (GDK_DRAG (drag), result, error: &error))
1642 {
1643 GDK_DISPLAY_NOTE (gdk_drag_get_display (GDK_DRAG (drag)), DND, g_printerr ("failed to write stream: %s\n", error->message));
1644 g_error_free (error);
1645 }
1646
1647 g_output_stream_close_async (stream,
1648 G_PRIORITY_DEFAULT,
1649 NULL,
1650 callback: gdk_x11_drag_default_output_closed,
1651 NULL);
1652}
1653
1654static void
1655gdk_x11_drag_default_output_handler (GOutputStream *stream,
1656 const char *mime_type,
1657 gpointer user_data)
1658{
1659 gdk_drag_write_async (GDK_DRAG (user_data),
1660 mime_type,
1661 stream,
1662 G_PRIORITY_DEFAULT,
1663 NULL,
1664 callback: gdk_x11_drag_default_output_done,
1665 user_data: stream);
1666}
1667
1668static gboolean
1669gdk_x11_drag_xevent (GdkDisplay *display,
1670 const XEvent *xevent,
1671 gpointer data)
1672{
1673 GdkDrag *drag = GDK_DRAG (data);
1674 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
1675 Window xwindow;
1676 Atom xselection;
1677
1678 xwindow = GDK_SURFACE_XID (x11_drag->ipc_surface);
1679 xselection = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndSelection");
1680
1681 if (xevent->xany.window != xwindow)
1682 return FALSE;
1683
1684 switch (xevent->type)
1685 {
1686 case SelectionClear:
1687 if (xevent->xselectionclear.selection != xselection)
1688 return FALSE;
1689
1690 if (xevent->xselectionclear.time < x11_drag->timestamp)
1691 {
1692 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("ignoring SelectionClear with too old timestamp (%lu vs %lu)\n",
1693 xevent->xselectionclear.time, x11_drag->timestamp));
1694 return FALSE;
1695 }
1696
1697 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("got SelectionClear, aborting DND\n"));
1698 gdk_drag_cancel (drag, reason: GDK_DRAG_CANCEL_ERROR);
1699 return TRUE;
1700
1701 case SelectionRequest:
1702 {
1703 GdkContentFormats *formats;
1704#ifdef G_ENABLE_DEBUG
1705 const char *target, *property;
1706#endif
1707
1708 if (xevent->xselectionrequest.selection != xselection)
1709 return FALSE;
1710
1711#ifdef G_ENABLE_DEBUG
1712 target = gdk_x11_get_xatom_name_for_display (display, xatom: xevent->xselectionrequest.target);
1713 if (xevent->xselectionrequest.property == None)
1714 property = target;
1715 else
1716 property = gdk_x11_get_xatom_name_for_display (display, xatom: xevent->xselectionrequest.property);
1717#endif
1718
1719 if (xevent->xselectionrequest.requestor == None)
1720 {
1721 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("got SelectionRequest for %s @ %s with NULL window, ignoring\n",
1722 target, property));
1723 return TRUE;
1724 }
1725
1726 GDK_DISPLAY_NOTE (display, CLIPBOARD, g_printerr ("got SelectionRequest for %s @ %s\n",
1727 target, property));
1728
1729 formats = gdk_content_formats_ref (formats: gdk_drag_get_formats (drag));
1730 formats = gdk_content_formats_union_serialize_mime_types (formats);
1731
1732 gdk_x11_selection_output_streams_create (display,
1733 formats,
1734 requestor: xevent->xselectionrequest.requestor,
1735 selection: xevent->xselectionrequest.selection,
1736 target: xevent->xselectionrequest.target,
1737 property: xevent->xselectionrequest.property
1738 ? xevent->xselectionrequest.property
1739 : xevent->xselectionrequest.target,
1740 timestamp: xevent->xselectionrequest.time,
1741 handler: gdk_x11_drag_default_output_handler,
1742 user_data: drag);
1743
1744 gdk_content_formats_unref (formats);
1745
1746 return TRUE;
1747 }
1748
1749 case ClientMessage:
1750 if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndStatus"))
1751 gdk_x11_drag_handle_status (display, xevent);
1752 else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndFinished"))
1753 gdk_x11_drag_handle_finished (display, xevent);
1754 else
1755 return FALSE;
1756 return TRUE;
1757
1758 default:
1759 return FALSE;
1760 }
1761}
1762
1763static double
1764ease_out_cubic (double t)
1765{
1766 double p = t - 1;
1767 return p * p * p + 1;
1768}
1769
1770
1771#define ANIM_TIME 500000 /* half a second */
1772
1773typedef struct _GdkDragAnim GdkDragAnim;
1774struct _GdkDragAnim {
1775 GdkX11Drag *drag;
1776 GdkFrameClock *frame_clock;
1777 gint64 start_time;
1778};
1779
1780static void
1781gdk_drag_anim_destroy (GdkDragAnim *anim)
1782{
1783 gdk_surface_hide (surface: anim->drag->drag_surface);
1784 g_object_unref (object: anim->drag);
1785 g_slice_free (GdkDragAnim, anim);
1786}
1787
1788static gboolean
1789gdk_drag_anim_timeout (gpointer data)
1790{
1791 GdkDragAnim *anim = data;
1792 GdkX11Drag *drag = anim->drag;
1793 GdkFrameClock *frame_clock = anim->frame_clock;
1794 gint64 current_time;
1795 double f;
1796 double t;
1797
1798 if (!frame_clock)
1799 return G_SOURCE_REMOVE;
1800
1801 current_time = gdk_frame_clock_get_frame_time (frame_clock);
1802
1803 f = (current_time - anim->start_time) / (double) ANIM_TIME;
1804
1805 if (f >= 1.0)
1806 return G_SOURCE_REMOVE;
1807
1808 t = ease_out_cubic (t: f);
1809
1810 gdk_x11_surface_show (surface: drag->drag_surface, FALSE);
1811 gdk_x11_surface_move (surface: drag->drag_surface,
1812 x: (drag->last_x - drag->hot_x) +
1813 (drag->start_x - drag->last_x) * t,
1814 y: (drag->last_y - drag->hot_y) +
1815 (drag->start_y - drag->last_y) * t);
1816 gdk_x11_surface_set_opacity (surface: drag->drag_surface, opacity: 1.0 - f);
1817
1818 return G_SOURCE_CONTINUE;
1819}
1820
1821static void
1822gdk_x11_drag_release_selection (GdkDrag *drag)
1823{
1824 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
1825 GdkDisplay *display;
1826 Display *xdisplay;
1827 Window xwindow;
1828 Atom xselection;
1829
1830 display = gdk_drag_get_display (drag);
1831 xdisplay = GDK_DISPLAY_XDISPLAY (display);
1832 xselection = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndSelection");
1833 xwindow = GDK_SURFACE_XID (x11_drag->ipc_surface);
1834
1835 if (XGetSelectionOwner (xdisplay, xselection) == xwindow)
1836 XSetSelectionOwner (xdisplay, xselection, None, CurrentTime);
1837}
1838
1839static void
1840gdk_x11_drag_drop_done (GdkDrag *drag,
1841 gboolean success)
1842{
1843 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
1844 GdkDragAnim *anim;
1845/*
1846 cairo_surface_t *win_surface;
1847 cairo_surface_t *surface;
1848 cairo_t *cr;
1849*/
1850 guint id;
1851
1852 gdk_x11_drag_release_selection (drag);
1853
1854 g_signal_handlers_disconnect_by_func (gdk_drag_get_display (drag),
1855 gdk_x11_drag_xevent,
1856 drag);
1857 if (success)
1858 {
1859 gdk_surface_hide (surface: x11_drag->drag_surface);
1860 g_object_unref (object: drag);
1861 return;
1862 }
1863
1864/*
1865 win_surface = _gdk_surface_ref_cairo_surface (x11_drag->drag_surface);
1866 surface = gdk_surface_create_similar_surface (x11_drag->drag_surface,
1867 cairo_surface_get_content (win_surface),
1868 gdk_surface_get_width (x11_drag->drag_surface),
1869 gdk_surface_get_height (x11_drag->drag_surface));
1870 cr = cairo_create (surface);
1871 cairo_set_source_surface (cr, win_surface, 0, 0);
1872 cairo_paint (cr);
1873 cairo_destroy (cr);
1874 cairo_surface_destroy (win_surface);
1875
1876 pattern = cairo_pattern_create_for_surface (surface);
1877
1878 gdk_surface_set_background_pattern (x11_drag->drag_surface, pattern);
1879
1880 cairo_pattern_destroy (pattern);
1881 cairo_surface_destroy (surface);
1882*/
1883
1884 anim = g_slice_new0 (GdkDragAnim);
1885 anim->drag = g_object_ref (x11_drag);
1886 anim->frame_clock = gdk_surface_get_frame_clock (surface: x11_drag->drag_surface);
1887 anim->start_time = gdk_frame_clock_get_frame_time (frame_clock: anim->frame_clock);
1888
1889 id = g_timeout_add_full (G_PRIORITY_DEFAULT, interval: 17,
1890 function: gdk_drag_anim_timeout, data: anim,
1891 notify: (GDestroyNotify) gdk_drag_anim_destroy);
1892 gdk_source_set_static_name_by_id (tag: id, name: "[gtk] gdk_drag_anim_timeout");
1893 g_object_unref (object: drag);
1894}
1895
1896static gboolean
1897drag_grab (GdkDrag *drag)
1898{
1899 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
1900 GdkSeatCapabilities capabilities;
1901 GdkDisplay *display;
1902 Window root;
1903 GdkSeat *seat;
1904 int keycode, i;
1905 GdkCursor *cursor;
1906
1907 if (!x11_drag->ipc_surface)
1908 return FALSE;
1909
1910 display = gdk_drag_get_display (drag);
1911 root = GDK_DISPLAY_XROOTWIN (display);
1912 seat = gdk_device_get_seat (device: gdk_drag_get_device (drag));
1913
1914 capabilities = GDK_SEAT_CAPABILITY_ALL_POINTING;
1915
1916 cursor = gdk_drag_get_cursor (drag, action: x11_drag->current_action);
1917 g_set_object (&x11_drag->cursor, cursor);
1918
1919 if (gdk_seat_grab (seat, surface: x11_drag->ipc_surface,
1920 capabilities, FALSE,
1921 cursor: x11_drag->cursor, NULL, NULL, NULL) != GDK_GRAB_SUCCESS)
1922 return FALSE;
1923
1924 g_set_object (&x11_drag->grab_seat, seat);
1925
1926 gdk_x11_display_error_trap_push (display);
1927
1928 for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
1929 {
1930 int deviceid = gdk_x11_device_get_id (device: gdk_seat_get_keyboard (seat));
1931 unsigned char mask[XIMaskLen(XI_LASTEVENT)];
1932 XIGrabModifiers mods;
1933 XIEventMask evmask;
1934 int num_mods;
1935
1936 keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display),
1937 grab_keys[i].keysym);
1938 if (keycode == NoSymbol)
1939 continue;
1940
1941 memset (s: mask, c: 0, n: sizeof (mask));
1942 XISetMask (mask, XI_KeyPress);
1943 XISetMask (mask, XI_KeyRelease);
1944
1945 evmask.deviceid = deviceid;
1946 evmask.mask_len = sizeof (mask);
1947 evmask.mask = mask;
1948
1949 num_mods = 1;
1950 mods.modifiers = grab_keys[i].modifiers;
1951
1952 XIGrabKeycode (GDK_DISPLAY_XDISPLAY (display),
1953 deviceid,
1954 keycode,
1955 grab_window: root,
1956 GrabModeAsync,
1957 GrabModeAsync,
1958 False,
1959 mask: &evmask,
1960 num_modifiers: num_mods,
1961 modifiers_inout: &mods);
1962 }
1963
1964 gdk_x11_display_error_trap_pop_ignored (display);
1965
1966 return TRUE;
1967}
1968
1969static void
1970drag_ungrab (GdkDrag *drag)
1971{
1972 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
1973 GdkDisplay *display;
1974 GdkDevice *keyboard;
1975 Window root;
1976 int keycode, i;
1977
1978 if (!x11_drag->grab_seat)
1979 return;
1980
1981 gdk_seat_ungrab (seat: x11_drag->grab_seat);
1982
1983 display = gdk_drag_get_display (drag);
1984 keyboard = gdk_seat_get_keyboard (seat: x11_drag->grab_seat);
1985 root = GDK_DISPLAY_XROOTWIN (display);
1986 g_clear_object (&x11_drag->grab_seat);
1987
1988 for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
1989 {
1990 XIGrabModifiers mods;
1991 int num_mods;
1992
1993 keycode = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (display),
1994 grab_keys[i].keysym);
1995 if (keycode == NoSymbol)
1996 continue;
1997
1998 num_mods = 1;
1999 mods.modifiers = grab_keys[i].modifiers;
2000
2001 XIUngrabKeycode (GDK_DISPLAY_XDISPLAY (display),
2002 deviceid: gdk_x11_device_get_id (device: keyboard),
2003 keycode,
2004 grab_window: root,
2005 num_modifiers: num_mods,
2006 modifiers: &mods);
2007 }
2008}
2009
2010GdkDrag *
2011_gdk_x11_surface_drag_begin (GdkSurface *surface,
2012 GdkDevice *device,
2013 GdkContentProvider *content,
2014 GdkDragAction actions,
2015 double dx,
2016 double dy)
2017{
2018 GdkX11Drag *x11_drag;
2019 GdkDrag *drag;
2020 GdkDisplay *display;
2021 double px, py;
2022 int x_root, y_root;
2023 Atom xselection;
2024 GdkSurface *ipc_surface;
2025
2026 display = gdk_surface_get_display (surface);
2027
2028 ipc_surface = _gdk_x11_display_create_surface (display,
2029 surface_type: GDK_SURFACE_TEMP,
2030 NULL,
2031 x: -99, y: -99, width: 1, height: 1);
2032
2033 drag = (GdkDrag *) g_object_new (GDK_TYPE_X11_DRAG,
2034 first_property_name: "surface", ipc_surface,
2035 "device", device,
2036 "content", content,
2037 "actions", actions,
2038 NULL);
2039 x11_drag = GDK_X11_DRAG (drag);
2040
2041 precache_target_list (drag);
2042
2043 gdk_x11_device_xi2_query_state (device, surface, win_x: &px, win_y: &py, NULL);
2044
2045 gdk_x11_surface_get_root_coords (surface,
2046 x: round (x: px + dx),
2047 y: round (x: py + dy),
2048 root_x: &x_root,
2049 root_y: &y_root);
2050
2051 x11_drag->start_x = x_root;
2052 x11_drag->start_y = y_root;
2053 x11_drag->last_x = x_root;
2054 x11_drag->last_y = y_root;
2055
2056 x11_drag->protocol = GDK_DRAG_PROTO_XDND;
2057 x11_drag->actions = actions;
2058 x11_drag->ipc_surface = ipc_surface;
2059 if (gdk_x11_surface_get_group (surface))
2060 gdk_x11_surface_set_group (surface: x11_drag->ipc_surface, leader: surface);
2061
2062 gdk_surface_set_is_mapped (surface: x11_drag->ipc_surface, TRUE);
2063 gdk_x11_surface_show (surface: x11_drag->ipc_surface, FALSE);
2064
2065 x11_drag->drag_surface = create_drag_surface (display);
2066
2067 if (!drag_grab (drag))
2068 {
2069 g_object_unref (object: drag);
2070 return NULL;
2071 }
2072
2073 move_drag_surface (drag, x_root, y_root);
2074
2075 x11_drag->timestamp = gdk_x11_get_server_time (GDK_X11_DISPLAY (display)->leader_gdk_surface);
2076 xselection = gdk_x11_get_xatom_by_name_for_display (display, atom_name: "XdndSelection");
2077 XSetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
2078 xselection,
2079 GDK_SURFACE_XID (x11_drag->ipc_surface),
2080 x11_drag->timestamp);
2081 if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), xselection) != GDK_SURFACE_XID (x11_drag->ipc_surface))
2082 {
2083 GDK_DISPLAY_NOTE (display, DND, g_printerr ("failed XSetSelectionOwner() on \"XdndSelection\", aborting DND\n"));
2084 g_object_unref (object: drag);
2085 return NULL;
2086 }
2087
2088
2089 g_signal_connect_object (instance: display, detailed_signal: "xevent", G_CALLBACK (gdk_x11_drag_xevent), gobject: drag, connect_flags: 0);
2090 /* backend holds a ref until gdk_drag_drop_done is called */
2091 g_object_ref (drag);
2092
2093 return drag;
2094}
2095
2096static void
2097gdk_x11_drag_set_cursor (GdkDrag *drag,
2098 GdkCursor *cursor)
2099{
2100 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
2101
2102 if (!g_set_object (&x11_drag->cursor, cursor))
2103 return;
2104
2105 if (x11_drag->grab_seat)
2106 {
2107 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2108 gdk_device_grab (device: gdk_seat_get_pointer (seat: x11_drag->grab_seat),
2109 surface: x11_drag->ipc_surface,
2110 FALSE,
2111 event_mask: GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
2112 cursor, GDK_CURRENT_TIME);
2113 G_GNUC_END_IGNORE_DEPRECATIONS;
2114 }
2115}
2116
2117static void
2118gdk_x11_drag_cancel (GdkDrag *drag,
2119 GdkDragCancelReason reason)
2120{
2121 gdk_drag_do_leave (GDK_X11_DRAG (drag));
2122 drag_ungrab (drag);
2123 gdk_drag_drop_done (drag, FALSE);
2124}
2125
2126static void
2127gdk_x11_drag_drop_performed (GdkDrag *drag,
2128 guint32 time_)
2129{
2130 gdk_x11_drag_drop (drag, time: time_);
2131 drag_ungrab (drag);
2132}
2133
2134#define BIG_STEP 20
2135#define SMALL_STEP 1
2136
2137static void
2138gdk_drag_get_current_actions (GdkModifierType state,
2139 int button,
2140 GdkDragAction actions,
2141 GdkDragAction *suggested_action,
2142 GdkDragAction *possible_actions)
2143{
2144 *suggested_action = 0;
2145 *possible_actions = 0;
2146
2147 if ((button == GDK_BUTTON_MIDDLE || button == GDK_BUTTON_SECONDARY) && (actions & GDK_ACTION_ASK))
2148 {
2149 *suggested_action = GDK_ACTION_ASK;
2150 *possible_actions = actions;
2151 }
2152 else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
2153 {
2154 if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
2155 {
2156 if (actions & GDK_ACTION_LINK)
2157 {
2158 *suggested_action = GDK_ACTION_LINK;
2159 *possible_actions = GDK_ACTION_LINK;
2160 }
2161 }
2162 else if (state & GDK_CONTROL_MASK)
2163 {
2164 if (actions & GDK_ACTION_COPY)
2165 {
2166 *suggested_action = GDK_ACTION_COPY;
2167 *possible_actions = GDK_ACTION_COPY;
2168 }
2169 }
2170 else
2171 {
2172 if (actions & GDK_ACTION_MOVE)
2173 {
2174 *suggested_action = GDK_ACTION_MOVE;
2175 *possible_actions = GDK_ACTION_MOVE;
2176 }
2177 }
2178 }
2179 else
2180 {
2181 *possible_actions = actions;
2182
2183 if ((state & (GDK_ALT_MASK)) && (actions & GDK_ACTION_ASK))
2184 *suggested_action = GDK_ACTION_ASK;
2185 else if (actions & GDK_ACTION_COPY)
2186 *suggested_action = GDK_ACTION_COPY;
2187 else if (actions & GDK_ACTION_MOVE)
2188 *suggested_action = GDK_ACTION_MOVE;
2189 else if (actions & GDK_ACTION_LINK)
2190 *suggested_action = GDK_ACTION_LINK;
2191 }
2192}
2193
2194static void
2195gdk_drag_update (GdkDrag *drag,
2196 double x_root,
2197 double y_root,
2198 GdkModifierType mods,
2199 guint32 evtime)
2200{
2201 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
2202 GdkDragAction suggested_action;
2203 GdkDragAction possible_actions;
2204 GdkDragProtocol protocol;
2205 Window proxy;
2206
2207 gdk_drag_get_current_actions (state: mods, GDK_BUTTON_PRIMARY, actions: x11_drag->actions,
2208 suggested_action: &suggested_action, possible_actions: &possible_actions);
2209
2210 proxy = gdk_x11_drag_find_surface (drag,
2211 drag_surface: x11_drag->drag_surface,
2212 x_root, y_root, protocol: &protocol);
2213
2214 gdk_x11_drag_drag_motion (drag, proxy_xid: proxy, protocol, x_root, y_root,
2215 suggested_action, possible_actions, time: evtime);
2216}
2217
2218static gboolean
2219gdk_dnd_handle_motion_event (GdkDrag *drag,
2220 GdkEvent *event)
2221{
2222 double x, y;
2223 int x_root, y_root;
2224
2225 gdk_event_get_position (event, x: &x, y: &y);
2226 x_root = event->surface->x + x;
2227 y_root = event->surface->y + y;
2228 gdk_drag_update (drag, x_root, y_root,
2229 mods: gdk_event_get_modifier_state (event),
2230 evtime: gdk_event_get_time (event));
2231 return TRUE;
2232}
2233
2234static gboolean
2235gdk_dnd_handle_key_event (GdkDrag *drag,
2236 GdkEvent *event)
2237{
2238 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
2239 GdkModifierType state;
2240 GdkDevice *pointer;
2241 GdkSeat *seat;
2242 int dx, dy;
2243
2244 dx = dy = 0;
2245 state = gdk_event_get_modifier_state (event);
2246 seat = gdk_event_get_seat (event);
2247 pointer = gdk_seat_get_pointer (seat);
2248
2249 if (event->event_type == GDK_KEY_PRESS)
2250 {
2251 guint keyval = gdk_key_event_get_keyval (event);
2252
2253 switch (keyval)
2254 {
2255 case GDK_KEY_Escape:
2256 gdk_drag_cancel (drag, reason: GDK_DRAG_CANCEL_USER_CANCELLED);
2257 return TRUE;
2258
2259 case GDK_KEY_space:
2260 case GDK_KEY_Return:
2261 case GDK_KEY_ISO_Enter:
2262 case GDK_KEY_KP_Enter:
2263 case GDK_KEY_KP_Space:
2264 if ((gdk_drag_get_selected_action (drag) != 0) &&
2265 (x11_drag->proxy_xid != None))
2266 {
2267 g_signal_emit_by_name (instance: drag, detailed_signal: "drop-performed");
2268 }
2269 else
2270 gdk_drag_cancel (drag, reason: GDK_DRAG_CANCEL_NO_TARGET);
2271
2272 return TRUE;
2273
2274 case GDK_KEY_Up:
2275 case GDK_KEY_KP_Up:
2276 dy = (state & GDK_ALT_MASK) ? -BIG_STEP : -SMALL_STEP;
2277 break;
2278
2279 case GDK_KEY_Down:
2280 case GDK_KEY_KP_Down:
2281 dy = (state & GDK_ALT_MASK) ? BIG_STEP : SMALL_STEP;
2282 break;
2283
2284 case GDK_KEY_Left:
2285 case GDK_KEY_KP_Left:
2286 dx = (state & GDK_ALT_MASK) ? -BIG_STEP : -SMALL_STEP;
2287 break;
2288
2289 case GDK_KEY_Right:
2290 case GDK_KEY_KP_Right:
2291 dx = (state & GDK_ALT_MASK) ? BIG_STEP : SMALL_STEP;
2292 break;
2293
2294 default:
2295 break;
2296 }
2297 }
2298
2299 /* The state is not yet updated in the event, so we need
2300 * to query it here. We could use XGetModifierMapping, but
2301 * that would be overkill.
2302 */
2303 gdk_x11_device_xi2_query_state (device: pointer, NULL, NULL, NULL, mask: &state);
2304
2305 if (dx != 0 || dy != 0)
2306 {
2307 GdkDisplay *display;
2308 Display *xdisplay;
2309 GdkX11Screen *screen;
2310 Window dest;
2311
2312 x11_drag->last_x += dx;
2313 x11_drag->last_y += dy;
2314
2315 display = gdk_event_get_display (event: (GdkEvent *)event);
2316 xdisplay = GDK_DISPLAY_XDISPLAY (display);
2317 screen = GDK_X11_DISPLAY (display)->screen;
2318 dest = GDK_SCREEN_XROOTWIN (screen);
2319
2320 XWarpPointer (xdisplay, None, dest, 0, 0, 0, 0,
2321 round (x: x11_drag->last_x * screen->surface_scale),
2322 round (x: x11_drag->last_y * screen->surface_scale));
2323 }
2324
2325 gdk_drag_update (drag, x_root: x11_drag->last_x, y_root: x11_drag->last_y, mods: state,
2326 evtime: gdk_event_get_time (event));
2327
2328 return TRUE;
2329}
2330
2331static gboolean
2332gdk_dnd_handle_grab_broken_event (GdkDrag *drag,
2333 GdkEvent *event)
2334{
2335 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
2336
2337 gboolean is_implicit = gdk_grab_broken_event_get_implicit (event);
2338 GdkSurface *grab_surface = gdk_grab_broken_event_get_grab_surface (event);
2339
2340 /* Don't cancel if we break the implicit grab from the initial button_press.
2341 * Also, don't cancel if we re-grab on the widget or on our IPC window, for
2342 * example, when changing the drag cursor.
2343 */
2344 if (is_implicit ||
2345 grab_surface == x11_drag->drag_surface ||
2346 grab_surface == x11_drag->ipc_surface)
2347 return FALSE;
2348
2349 if (gdk_event_get_device (event) != gdk_drag_get_device (drag))
2350 return FALSE;
2351
2352 gdk_drag_cancel (drag, reason: GDK_DRAG_CANCEL_ERROR);
2353
2354 return TRUE;
2355}
2356
2357static gboolean
2358gdk_dnd_handle_button_event (GdkDrag *drag,
2359 GdkEvent *event)
2360{
2361 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
2362
2363#if 0
2364 /* FIXME: Check the button matches */
2365 if (event->button != x11_drag->button)
2366 return FALSE;
2367#endif
2368
2369 if ((gdk_drag_get_selected_action (drag) != 0) &&
2370 (x11_drag->proxy_xid != None))
2371 {
2372 g_signal_emit_by_name (instance: drag, detailed_signal: "drop-performed");
2373 }
2374 else
2375 gdk_drag_cancel (drag, reason: GDK_DRAG_CANCEL_NO_TARGET);
2376
2377 return TRUE;
2378}
2379
2380gboolean
2381gdk_x11_drag_handle_event (GdkDrag *drag,
2382 GdkEvent *event)
2383{
2384 GdkX11Drag *x11_drag = GDK_X11_DRAG (drag);
2385
2386 if (!x11_drag->grab_seat)
2387 return FALSE;
2388
2389 switch ((guint) event->event_type)
2390 {
2391 case GDK_MOTION_NOTIFY:
2392 return gdk_dnd_handle_motion_event (drag, event);
2393
2394 case GDK_BUTTON_RELEASE:
2395 return gdk_dnd_handle_button_event (drag, event);
2396
2397 case GDK_KEY_PRESS:
2398 case GDK_KEY_RELEASE:
2399 return gdk_dnd_handle_key_event (drag, event);
2400
2401 case GDK_GRAB_BROKEN:
2402 return gdk_dnd_handle_grab_broken_event (drag, event);
2403
2404 default:
2405 break;
2406 }
2407
2408 return FALSE;
2409}
2410

source code of gtk/gdk/x11/gdkdrag-x11.c