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 | |
54 | typedef 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 | */ |
71 | typedef enum |
72 | { |
73 | GDK_DRAG_PROTO_NONE = 0, |
74 | GDK_DRAG_PROTO_XDND, |
75 | GDK_DRAG_PROTO_ROOTWIN |
76 | } GdkDragProtocol; |
77 | |
78 | typedef 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 | |
87 | struct _GdkSurfaceCache { |
88 | GList *children; |
89 | GHashTable *child_hash; |
90 | guint old_event_mask; |
91 | GdkDisplay *display; |
92 | int ref_count; |
93 | }; |
94 | |
95 | |
96 | struct _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 | |
132 | struct _GdkX11DragClass |
133 | { |
134 | GdkDragClass parent_class; |
135 | }; |
136 | |
137 | typedef struct { |
138 | int keysym; |
139 | int modifiers; |
140 | } GrabKey; |
141 | |
142 | static 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 | |
168 | static GdkSurfaceCache *gdk_surface_cache_ref (GdkSurfaceCache *cache); |
169 | static void gdk_surface_cache_unref (GdkSurfaceCache *cache); |
170 | |
171 | gboolean gdk_x11_drag_handle_event (GdkDrag *drag, |
172 | GdkEvent *event); |
173 | |
174 | static GList *drags; |
175 | static GSList *window_caches; |
176 | |
177 | G_DEFINE_TYPE (GdkX11Drag, gdk_x11_drag, GDK_TYPE_DRAG) |
178 | |
179 | static void |
180 | gdk_x11_drag_init (GdkX11Drag *drag) |
181 | { |
182 | drags = g_list_prepend (list: drags, data: drag); |
183 | } |
184 | |
185 | static void gdk_x11_drag_finalize (GObject *object); |
186 | static Window gdk_x11_drag_find_surface (GdkDrag *drag, |
187 | GdkSurface *drag_surface, |
188 | int x_root, |
189 | int y_root, |
190 | GdkDragProtocol *protocol); |
191 | static 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); |
199 | static void gdk_x11_drag_drop (GdkDrag *drag, |
200 | guint32 time_); |
201 | static GdkSurface * gdk_x11_drag_get_drag_surface (GdkDrag *drag); |
202 | static void gdk_x11_drag_set_hotspot (GdkDrag *drag, |
203 | int hot_x, |
204 | int hot_y); |
205 | static void gdk_x11_drag_drop_done (GdkDrag *drag, |
206 | gboolean success); |
207 | static void gdk_x11_drag_set_cursor (GdkDrag *drag, |
208 | GdkCursor *cursor); |
209 | static void gdk_x11_drag_cancel (GdkDrag *drag, |
210 | GdkDragCancelReason reason); |
211 | static void gdk_x11_drag_drop_performed (GdkDrag *drag, |
212 | guint32 time); |
213 | |
214 | static void |
215 | gdk_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 | |
231 | static void |
232 | gdk_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 | |
256 | GdkDrag * |
257 | gdk_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 | |
294 | static void |
295 | precache_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 | |
315 | static void |
316 | free_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 | |
332 | static void |
333 | gdk_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 | |
358 | GdkFilterReturn |
359 | gdk_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 | |
391 | GdkFilterReturn |
392 | gdk_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 | |
510 | static GdkSurfaceCache * |
511 | gdk_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 | |
572 | static void |
573 | gdk_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 | |
589 | static GdkSurfaceCache * |
590 | gdk_surface_cache_ref (GdkSurfaceCache *cache) |
591 | { |
592 | cache->ref_count += 1; |
593 | |
594 | return cache; |
595 | } |
596 | |
597 | static void |
598 | gdk_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 | |
611 | GdkSurfaceCache * |
612 | gdk_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 | |
631 | static gboolean |
632 | is_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 | |
677 | static Window |
678 | get_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 | |
731 | static Window |
732 | get_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 | |
789 | static 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 | |
800 | static const int xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table); |
801 | |
802 | static GdkDragAction |
803 | xdnd_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 | |
821 | static Atom |
822 | xdnd_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 | |
836 | void |
837 | gdk_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 | |
869 | void |
870 | gdk_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 | |
895 | static void |
896 | xdnd_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 | |
926 | static void |
927 | xdnd_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 | |
972 | static void |
973 | send_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 | |
999 | static void |
1000 | send_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 | |
1014 | static void |
1015 | xdnd_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 | |
1042 | static void |
1043 | xdnd_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 | |
1091 | static void |
1092 | xdnd_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 | |
1113 | static void |
1114 | xdnd_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 | |
1136 | static void |
1137 | xdnd_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 | |
1163 | static guint32 |
1164 | xdnd_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 | |
1238 | static void |
1239 | gdk_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 | |
1258 | static GdkSurface * |
1259 | create_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 | |
1271 | static 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 | |
1326 | static GdkSurfaceCache * |
1327 | drag_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 | |
1336 | static Window |
1337 | gdk_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 | |
1387 | static void |
1388 | move_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 | |
1400 | static gboolean |
1401 | gdk_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 | |
1540 | static void |
1541 | gdk_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 | |
1569 | void |
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 | |
1592 | static GdkSurface * |
1593 | gdk_x11_drag_get_drag_surface (GdkDrag *drag) |
1594 | { |
1595 | return GDK_X11_DRAG (drag)->drag_surface; |
1596 | } |
1597 | |
1598 | static void |
1599 | gdk_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 | |
1615 | static void |
1616 | gdk_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 | |
1633 | static void |
1634 | gdk_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 | |
1654 | static void |
1655 | gdk_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 | |
1668 | static gboolean |
1669 | gdk_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 | |
1763 | static double |
1764 | ease_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 | |
1773 | typedef struct _GdkDragAnim GdkDragAnim; |
1774 | struct _GdkDragAnim { |
1775 | GdkX11Drag *drag; |
1776 | GdkFrameClock *frame_clock; |
1777 | gint64 start_time; |
1778 | }; |
1779 | |
1780 | static void |
1781 | gdk_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 | |
1788 | static gboolean |
1789 | gdk_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 | |
1821 | static void |
1822 | gdk_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 | |
1839 | static void |
1840 | gdk_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 | |
1896 | static gboolean |
1897 | drag_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 | |
1969 | static void |
1970 | drag_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 | |
2010 | GdkDrag * |
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 | |
2096 | static void |
2097 | gdk_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 | |
2117 | static void |
2118 | gdk_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 | |
2126 | static void |
2127 | gdk_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 | |
2137 | static void |
2138 | gdk_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 | |
2194 | static void |
2195 | gdk_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 | |
2218 | static gboolean |
2219 | gdk_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 | |
2234 | static gboolean |
2235 | gdk_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 | |
2331 | static gboolean |
2332 | gdk_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 | |
2357 | static gboolean |
2358 | gdk_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 | |
2380 | gboolean |
2381 | gdk_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 | |