1 /* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
29 #include "gdkx11dnd.h"
30 #include "gdkdndprivate.h"
33 #include "gdkinternals.h"
35 #include "gdkproperty.h"
36 #include "gdkprivate-x11.h"
37 #include "gdkscreen-x11.h"
38 #include "gdkdisplay-x11.h"
41 #include <X11/Xutil.h>
42 #include <X11/Xatom.h>
43 #include <X11/extensions/shape.h>
44 #include <X11/extensions/Xcomposite.h>
50 GDK_DRAG_STATUS_MOTION_WAIT,
51 GDK_DRAG_STATUS_ACTION_WAIT,
57 gint x, y, width, height;
59 gboolean shape_selected;
61 cairo_region_t *shape;
66 GHashTable *child_hash;
72 struct _GdkX11DragContext
74 GdkDragContext context;
79 guint16 last_x; /* Coordinates from last event */
81 GdkDragAction old_action; /* The last action we sent to the source */
82 GdkDragAction old_actions; /* The last actions we sent to the source */
83 GdkDragAction xdnd_actions; /* What is currently set in XdndActionList */
85 Window dest_xid; /* The last window we looked up */
86 Window drop_xid; /* The (non-proxied) window that is receiving drops */
87 guint xdnd_targets_set : 1; /* Whether we've already set XdndTypeList */
88 guint xdnd_actions_set : 1; /* Whether we've already set XdndActionList */
89 guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
90 guint motif_targets_set : 1; /* Whether we've already set motif initiator info */
91 guint drag_status : 4; /* current status of drag */
93 guint drop_failed : 1; /* Whether the drop was unsuccessful */
94 guint version; /* Xdnd protocol version */
96 GSList *window_caches;
99 struct _GdkX11DragContextClass
101 GdkDragContextClass parent_class;
104 /* Forward declarations */
106 static void gdk_window_cache_destroy (GdkWindowCache *cache);
108 static void motif_read_target_table (GdkDisplay *display);
110 static GdkFilterReturn motif_dnd_filter (GdkXEvent *xev,
114 static GdkFilterReturn xdnd_enter_filter (GdkXEvent *xev,
117 static GdkFilterReturn xdnd_leave_filter (GdkXEvent *xev,
120 static GdkFilterReturn xdnd_position_filter (GdkXEvent *xev,
123 static GdkFilterReturn xdnd_status_filter (GdkXEvent *xev,
126 static GdkFilterReturn xdnd_finished_filter (GdkXEvent *xev,
129 static GdkFilterReturn xdnd_drop_filter (GdkXEvent *xev,
133 static void xdnd_manage_source_filter (GdkDragContext *context,
135 gboolean add_filter);
137 static GList *contexts;
139 static const struct {
140 const char *atom_name;
143 { "XdndEnter", xdnd_enter_filter },
144 { "XdndLeave", xdnd_leave_filter },
145 { "XdndPosition", xdnd_position_filter },
146 { "XdndStatus", xdnd_status_filter },
147 { "XdndFinished", xdnd_finished_filter },
148 { "XdndDrop", xdnd_drop_filter },
152 G_DEFINE_TYPE (GdkX11DragContext, gdk_x11_drag_context, GDK_TYPE_DRAG_CONTEXT)
155 gdk_x11_drag_context_init (GdkX11DragContext *context)
157 contexts = g_list_prepend (contexts, context);
160 static void gdk_x11_drag_context_finalize (GObject *object);
161 static GdkWindow * gdk_x11_drag_context_find_window (GdkDragContext *context,
162 GdkWindow *drag_window,
166 GdkDragProtocol *protocol);
167 static gboolean gdk_x11_drag_context_drag_motion (GdkDragContext *context,
168 GdkWindow *dest_window,
169 GdkDragProtocol protocol,
172 GdkDragAction suggested_action,
173 GdkDragAction possible_actions,
175 static void gdk_x11_drag_context_drag_status (GdkDragContext *context,
176 GdkDragAction action,
178 static void gdk_x11_drag_context_drag_abort (GdkDragContext *context,
180 static void gdk_x11_drag_context_drag_drop (GdkDragContext *context,
182 static void gdk_x11_drag_context_drop_reply (GdkDragContext *context,
185 static void gdk_x11_drag_context_drop_finish (GdkDragContext *context,
188 static gboolean gdk_x11_drag_context_drop_status (GdkDragContext *context);
189 static GdkAtom gdk_x11_drag_context_get_selection (GdkDragContext *context);
192 gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass)
194 GObjectClass *object_class = G_OBJECT_CLASS (klass);
195 GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
197 object_class->finalize = gdk_x11_drag_context_finalize;
199 context_class->find_window = gdk_x11_drag_context_find_window;
200 context_class->drag_status = gdk_x11_drag_context_drag_status;
201 context_class->drag_motion = gdk_x11_drag_context_drag_motion;
202 context_class->drag_abort = gdk_x11_drag_context_drag_abort;
203 context_class->drag_drop = gdk_x11_drag_context_drag_drop;
204 context_class->drop_reply = gdk_x11_drag_context_drop_reply;
205 context_class->drop_finish = gdk_x11_drag_context_drop_finish;
206 context_class->drop_status = gdk_x11_drag_context_drop_status;
207 context_class->get_selection = gdk_x11_drag_context_get_selection;
211 gdk_x11_drag_context_finalize (GObject *object)
213 GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (object);
214 GdkDragContext *context = GDK_DRAG_CONTEXT (object);
216 if (context->source_window)
218 if ((context->protocol == GDK_DRAG_PROTO_XDND) && !context->is_source)
219 xdnd_manage_source_filter (context, context->source_window, FALSE);
222 g_slist_free_full (context_x11->window_caches, (GDestroyNotify)gdk_window_cache_destroy);
224 contexts = g_list_remove (contexts, context);
226 G_OBJECT_CLASS (gdk_x11_drag_context_parent_class)->finalize (object);
231 static GdkDragContext *
232 gdk_drag_context_find (GdkDisplay *display,
237 GList *tmp_list = contexts;
238 GdkDragContext *context;
239 GdkX11DragContext *context_x11;
240 Window context_dest_xid;
244 context = (GdkDragContext *)tmp_list->data;
245 context_x11 = (GdkX11DragContext *)context;
247 if ((context->source_window && gdk_window_get_display (context->source_window) != display) ||
248 (context->dest_window && gdk_window_get_display (context->dest_window) != display))
251 context_dest_xid = context->dest_window
252 ? (context_x11->drop_xid
253 ? context_x11->drop_xid
254 : GDK_WINDOW_XID (context->dest_window))
257 if ((!context->is_source == !is_source) &&
258 ((source_xid == None) || (context->source_window &&
259 (GDK_WINDOW_XID (context->source_window) == source_xid))) &&
260 ((dest_xid == None) || (context_dest_xid == dest_xid)))
263 tmp_list = tmp_list->next;
270 precache_target_list (GdkDragContext *context)
272 if (context->targets)
274 GPtrArray *targets = g_ptr_array_new ();
278 for (tmp_list = context->targets; tmp_list; tmp_list = tmp_list->next)
279 g_ptr_array_add (targets, gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)));
281 _gdk_x11_precache_atoms (GDK_WINDOW_DISPLAY (context->source_window),
282 (const gchar **)targets->pdata,
285 for (i =0; i < targets->len; i++)
286 g_free (targets->pdata[i]);
288 g_ptr_array_free (targets, TRUE);
292 /* Utility functions */
295 free_cache_child (GdkCacheChild *child,
299 cairo_region_destroy (child->shape);
301 if (child->shape_selected && display)
303 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
305 XShapeSelectInput (display_x11->xdisplay, child->xid, 0);
312 gdk_window_cache_add (GdkWindowCache *cache,
320 GdkCacheChild *child = g_new (GdkCacheChild, 1);
325 child->width = width;
326 child->height = height;
327 child->mapped = mapped;
328 child->shape_selected = FALSE;
329 child->shape_valid = FALSE;
332 cache->children = g_list_prepend (cache->children, child);
333 g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
337 static GdkFilterReturn
338 gdk_window_cache_shape_filter (GdkXEvent *xev,
342 XEvent *xevent = (XEvent *)xev;
343 GdkWindowCache *cache = data;
345 GdkX11Display *display = GDK_X11_DISPLAY (gdk_screen_get_display (cache->screen));
347 if (display->have_shapes &&
348 xevent->type == display->shape_event_base + ShapeNotify)
350 XShapeEvent *xse = (XShapeEvent*)xevent;
353 node = g_hash_table_lookup (cache->child_hash,
354 GUINT_TO_POINTER (xse->window));
357 GdkCacheChild *child = node->data;
358 child->shape_valid = FALSE;
361 cairo_region_destroy (child->shape);
366 return GDK_FILTER_REMOVE;
369 return GDK_FILTER_CONTINUE;
372 static GdkFilterReturn
373 gdk_window_cache_filter (GdkXEvent *xev,
377 XEvent *xevent = (XEvent *)xev;
378 GdkWindowCache *cache = data;
380 switch (xevent->type)
382 case CirculateNotify:
384 case ConfigureNotify:
386 XConfigureEvent *xce = &xevent->xconfigure;
389 node = g_hash_table_lookup (cache->child_hash,
390 GUINT_TO_POINTER (xce->window));
393 GdkCacheChild *child = node->data;
396 child->width = xce->width;
397 child->height = xce->height;
398 if (xce->above == None && (node->next))
400 GList *last = g_list_last (cache->children);
401 cache->children = g_list_remove_link (cache->children, node);
408 GList *above_node = g_hash_table_lookup (cache->child_hash,
409 GUINT_TO_POINTER (xce->above));
410 if (above_node && node->next != above_node)
412 /* Put the window above (before in the list) above_node */
413 cache->children = g_list_remove_link (cache->children, node);
414 node->prev = above_node->prev;
416 node->prev->next = node;
418 cache->children = node;
419 node->next = above_node;
420 above_node->prev = node;
428 XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
430 if (!g_hash_table_lookup (cache->child_hash,
431 GUINT_TO_POINTER (xcwe->window)))
432 gdk_window_cache_add (cache, xcwe->window,
433 xcwe->x, xcwe->y, xcwe->width, xcwe->height,
439 XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
442 node = g_hash_table_lookup (cache->child_hash,
443 GUINT_TO_POINTER (xdwe->window));
446 GdkCacheChild *child = node->data;
448 g_hash_table_remove (cache->child_hash,
449 GUINT_TO_POINTER (xdwe->window));
450 cache->children = g_list_remove_link (cache->children, node);
451 /* window is destroyed, no need to disable ShapeNotify */
452 free_cache_child (child, NULL);
453 g_list_free_1 (node);
459 XMapEvent *xme = &xevent->xmap;
462 node = g_hash_table_lookup (cache->child_hash,
463 GUINT_TO_POINTER (xme->window));
466 GdkCacheChild *child = node->data;
467 child->mapped = TRUE;
475 XMapEvent *xume = &xevent->xmap;
478 node = g_hash_table_lookup (cache->child_hash,
479 GUINT_TO_POINTER (xume->window));
482 GdkCacheChild *child = node->data;
483 child->mapped = FALSE;
488 return GDK_FILTER_CONTINUE;
490 return GDK_FILTER_REMOVE;
493 static GdkWindowCache *
494 gdk_window_cache_new (GdkScreen *screen)
496 XWindowAttributes xwa;
497 Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
498 GdkWindow *root_window = gdk_screen_get_root_window (screen);
499 GdkChildInfoX11 *children;
503 GdkWindowCache *result = g_new (GdkWindowCache, 1);
505 result->children = NULL;
506 result->child_hash = g_hash_table_new (g_direct_hash, NULL);
507 result->screen = screen;
509 XGetWindowAttributes (xdisplay, GDK_WINDOW_XID (root_window), &xwa);
510 result->old_event_mask = xwa.your_event_mask;
512 if (G_UNLIKELY (!GDK_X11_DISPLAY (GDK_X11_SCREEN (screen)->display)->trusted_client))
514 GList *toplevel_windows, *list;
516 gint x, y, width, height;
518 toplevel_windows = gdk_screen_get_toplevel_windows (screen);
519 for (list = toplevel_windows; list; list = list->next)
521 window = GDK_WINDOW (list->data);
522 gdk_window_get_geometry (window, &x, &y, &width, &height);
523 gdk_window_cache_add (result, GDK_WINDOW_XID (window),
525 gdk_window_is_visible (window));
527 g_list_free (toplevel_windows);
531 XSelectInput (xdisplay, GDK_WINDOW_XID (root_window),
532 result->old_event_mask | SubstructureNotifyMask);
533 gdk_window_add_filter (root_window, gdk_window_cache_filter, result);
534 gdk_window_add_filter (NULL, gdk_window_cache_shape_filter, result);
536 if (!_gdk_x11_get_window_child_info (gdk_screen_get_display (screen),
537 GDK_WINDOW_XID (root_window),
539 &children, &nchildren))
542 for (i = 0; i < nchildren ; i++)
544 gdk_window_cache_add (result, children[i].window,
545 children[i].x, children[i].y, children[i].width, children[i].height,
546 children[i].is_mapped);
551 #ifdef HAVE_XCOMPOSITE
553 * Add the composite overlay window to the cache, as this can be a reasonable
554 * Xdnd proxy as well.
555 * This is only done when the screen is composited in order to avoid mapping
556 * the COW. We assume that the CM is using the COW (which is true for pretty
557 * much any CM currently in use).
559 if (gdk_screen_is_composited (screen))
561 cow = XCompositeGetOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
562 gdk_window_cache_add (result, cow, 0, 0, gdk_screen_get_width (screen), gdk_screen_get_height (screen), TRUE);
563 XCompositeReleaseOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
571 gdk_window_cache_destroy (GdkWindowCache *cache)
573 GdkWindow *root_window = gdk_screen_get_root_window (cache->screen);
576 XSelectInput (GDK_WINDOW_XDISPLAY (root_window),
577 GDK_WINDOW_XID (root_window),
578 cache->old_event_mask);
579 gdk_window_remove_filter (root_window, gdk_window_cache_filter, cache);
580 gdk_window_remove_filter (NULL, gdk_window_cache_shape_filter, cache);
582 display = gdk_screen_get_display (cache->screen);
584 gdk_x11_display_error_trap_push (display);
585 g_list_foreach (cache->children, (GFunc)free_cache_child, display);
586 gdk_x11_display_error_trap_pop_ignored (display);
588 g_list_free (cache->children);
589 g_hash_table_destroy (cache->child_hash);
595 is_pointer_within_shape (GdkDisplay *display,
596 GdkCacheChild *child,
600 if (!child->shape_selected)
602 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
604 XShapeSelectInput (display_x11->xdisplay, child->xid, ShapeNotifyMask);
605 child->shape_selected = TRUE;
607 if (!child->shape_valid)
609 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
610 cairo_region_t *input_shape;
612 child->shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
613 child->xid, ShapeBounding);
615 input_shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
616 child->xid, ShapeInput);
617 if (child->shape && input_shape)
619 cairo_region_intersect (child->shape, input_shape);
620 cairo_region_destroy (input_shape);
622 else if (input_shape)
624 child->shape = input_shape;
628 child->shape_valid = TRUE;
631 return child->shape == NULL ||
632 cairo_region_contains_point (child->shape, x_pos, y_pos);
636 get_client_window_at_coords_recurse (GdkDisplay *display,
638 gboolean is_toplevel,
642 GdkChildInfoX11 *children;
643 unsigned int nchildren;
645 gboolean found_child = FALSE;
646 GdkChildInfoX11 child = { 0, };
647 gboolean has_wm_state = FALSE;
649 if (!_gdk_x11_get_window_child_info (display, win, TRUE,
650 is_toplevel? &has_wm_state : NULL,
651 &children, &nchildren))
661 for (i = nchildren - 1; (i >= 0) && !found_child; i--)
663 GdkChildInfoX11 *cur_child = &children[i];
665 if ((cur_child->is_mapped) && (cur_child->window_class == InputOutput) &&
666 (x >= cur_child->x) && (x < cur_child->x + cur_child->width) &&
667 (y >= cur_child->y) && (y < cur_child->y + cur_child->height))
680 if (child.has_wm_state)
683 return get_client_window_at_coords_recurse (display, child.window, FALSE, x, y);
690 get_client_window_at_coords (GdkWindowCache *cache,
696 Window retval = None;
699 display = gdk_screen_get_display (cache->screen);
701 gdk_x11_display_error_trap_push (display);
703 tmp_list = cache->children;
705 while (tmp_list && !retval)
707 GdkCacheChild *child = tmp_list->data;
709 if ((child->xid != ignore) && (child->mapped))
711 if ((x_root >= child->x) && (x_root < child->x + child->width) &&
712 (y_root >= child->y) && (y_root < child->y + child->height))
714 if (!is_pointer_within_shape (display, child,
718 tmp_list = tmp_list->next;
722 retval = get_client_window_at_coords_recurse (display,
730 tmp_list = tmp_list->next;
733 gdk_x11_display_error_trap_pop_ignored (display);
738 return GDK_WINDOW_XID (gdk_screen_get_root_window (cache->screen));
741 /*************************************************************
742 ***************************** MOTIF *************************
743 *************************************************************/
745 /* values used in the message type for Motif DND */
758 /* Values used to specify type of protocol to use */
762 XmDRAG_PREFER_PREREGISTER,
764 XmDRAG_PREFER_DYNAMIC,
766 XmDRAG_PREFER_RECEIVER
769 /* Operation codes */
777 /* Drop site status */
779 XmNO_DROP_SITE = 0x01,
780 XmDROP_SITE_INVALID = 0x02,
781 XmDROP_SITE_VALID = 0x03
784 /* completion status */
792 /* Byte swapping routines. The motif specification leaves it
793 * up to us to save a few bytes in the client messages
795 static gchar local_byte_order = '\0';
797 #ifdef G_ENABLE_DEBUG
799 print_target_list (GList *targets)
803 gchar *name = gdk_atom_name (GDK_POINTER_TO_ATOM (targets->data));
804 g_message ("\t%s", name);
806 targets = targets->next;
809 #endif /* G_ENABLE_DEBUG */
812 init_byte_order (void)
814 guint32 myint = 0x01020304;
815 local_byte_order = (*(gchar *)&myint == 1) ? 'B' : 'l';
819 card16_to_host (guint16 x, gchar byte_order)
821 if (byte_order == local_byte_order)
824 return (x << 8) | (x >> 8);
828 card32_to_host (guint32 x, gchar byte_order)
830 if (byte_order == local_byte_order)
833 return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
836 /* Motif packs together fields of varying length into the
837 * client message. We can't rely on accessing these
838 * through data.s[], data.l[], etc, because on some architectures
839 * (i.e., Alpha) these won't be valid for format == 8.
842 #define MOTIF_XCLIENT_BYTE(xevent,i) \
843 (xevent)->xclient.data.b[i]
844 #define MOTIF_XCLIENT_SHORT(xevent,i) \
845 ((gint16 *)&((xevent)->xclient.data.b[0]))[i]
846 #define MOTIF_XCLIENT_LONG(xevent,i) \
847 ((gint32 *)&((xevent)->xclient.data.b[0]))[i]
849 #define MOTIF_UNPACK_BYTE(xevent,i) MOTIF_XCLIENT_BYTE(xevent,i)
850 #define MOTIF_UNPACK_SHORT(xevent,i) \
851 card16_to_host (MOTIF_XCLIENT_SHORT(xevent,i), MOTIF_XCLIENT_BYTE(xevent, 1))
852 #define MOTIF_UNPACK_LONG(xevent,i) \
853 card32_to_host (MOTIF_XCLIENT_LONG(xevent,i), MOTIF_XCLIENT_BYTE(xevent, 1))
855 /***** Dest side ***********/
857 /* Property placed on source windows */
858 typedef struct _MotifDragInitiatorInfo
861 guint8 protocol_version;
862 guint16 targets_index;
863 guint32 selection_atom;
864 } MotifDragInitiatorInfo;
866 /* Header for target table on the drag window */
867 typedef struct _MotifTargetTableHeader
870 guchar protocol_version;
873 } MotifTargetTableHeader;
875 /* Property placed on target windows */
876 typedef struct _MotifDragReceiverInfo
879 guint8 protocol_version;
880 guint8 protocol_style;
882 guint32 proxy_window;
883 guint16 num_drop_sites;
886 } MotifDragReceiverInfo;
888 /* Target table handling */
890 static GdkFilterReturn
891 motif_drag_window_filter (GdkXEvent *xevent,
895 XEvent *xev = (XEvent *)xevent;
896 GdkDisplay *display = GDK_WINDOW_DISPLAY (event->any.window);
897 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
899 switch (xev->xany.type)
902 display_x11->motif_drag_window = None;
903 display_x11->motif_drag_gdk_window = NULL;
906 if (display_x11->motif_target_lists &&
907 (xev->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_TARGETS")))
908 motif_read_target_table (display);
911 return GDK_FILTER_REMOVE;
915 motif_lookup_drag_window (GdkDisplay *display,
916 Display *lookup_xdisplay)
918 Window retval = None;
919 gulong bytes_after, nitems;
924 XGetWindowProperty (lookup_xdisplay, RootWindow (lookup_xdisplay, 0),
925 gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_WINDOW"),
927 XA_WINDOW, &type, &format, &nitems, &bytes_after,
930 if ((format == 32) && (nitems == 1) && (bytes_after == 0))
932 retval = *(Window *)data;
934 g_message ("Found drag window %#lx\n", GDK_X11_DISPLAY (display)->motif_drag_window));
943 /* Finds the window where global Motif drag information is stored.
944 * If it doesn't exist and 'create' is TRUE, create one.
947 motif_find_drag_window (GdkDisplay *display,
950 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
952 if (!display_x11->motif_drag_window)
954 Atom motif_drag_window_atom = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_WINDOW");
955 display_x11->motif_drag_window = motif_lookup_drag_window (display, display_x11->xdisplay);
957 if (!display_x11->motif_drag_window && create)
959 /* Create a persistant window. (Copied from LessTif) */
960 Display *persistant_xdisplay;
961 XSetWindowAttributes attr;
962 persistant_xdisplay = XOpenDisplay (gdk_display_get_name (display));
963 XSetCloseDownMode (persistant_xdisplay, RetainPermanent);
965 XGrabServer (persistant_xdisplay);
967 display_x11->motif_drag_window = motif_lookup_drag_window (display, persistant_xdisplay);
969 if (!display_x11->motif_drag_window)
971 attr.override_redirect = True;
972 attr.event_mask = PropertyChangeMask;
974 display_x11->motif_drag_window =
975 XCreateWindow (persistant_xdisplay,
976 RootWindow (persistant_xdisplay, 0),
977 -100, -100, 10, 10, 0, 0,
978 InputOnly, (Visual *)CopyFromParent,
979 (CWOverrideRedirect | CWEventMask), &attr);
982 g_message ("Created drag window %#lx\n", display_x11->motif_drag_window));
984 XChangeProperty (persistant_xdisplay,
985 RootWindow (persistant_xdisplay, 0),
986 motif_drag_window_atom, XA_WINDOW,
988 (guchar *)&motif_drag_window_atom, 1);
990 XUngrabServer (persistant_xdisplay);
991 XCloseDisplay (persistant_xdisplay);
994 /* There is a miniscule race condition here if the drag window
995 * gets destroyed exactly now.
997 if (display_x11->motif_drag_window)
999 display_x11->motif_drag_gdk_window =
1000 gdk_x11_window_foreign_new_for_display (display, display_x11->motif_drag_window);
1001 gdk_window_add_filter (display_x11->motif_drag_gdk_window,
1002 motif_drag_window_filter,
1007 return display_x11->motif_drag_window;
1011 motif_read_target_table (GdkDisplay *display)
1013 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1014 gulong bytes_after, nitems;
1019 Atom motif_drag_targets_atom = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_TARGETS");
1021 if (display_x11->motif_target_lists)
1023 for (i=0; i<display_x11->motif_n_target_lists; i++)
1024 g_list_free (display_x11->motif_target_lists[i]);
1026 g_free (display_x11->motif_target_lists);
1027 display_x11->motif_target_lists = NULL;
1028 display_x11->motif_n_target_lists = 0;
1031 if (motif_find_drag_window (display, FALSE))
1034 MotifTargetTableHeader *header = NULL;
1035 guchar *target_bytes = NULL;
1037 gboolean success = FALSE;
1039 gdk_x11_display_error_trap_push (display);
1040 XGetWindowProperty (display_x11->xdisplay,
1041 display_x11->motif_drag_window,
1042 motif_drag_targets_atom,
1043 0, (sizeof(MotifTargetTableHeader)+3)/4, FALSE,
1044 motif_drag_targets_atom,
1045 &type, &format, &nitems, &bytes_after,
1048 if (gdk_x11_display_error_trap_pop (display) ||
1049 (format != 8) || (nitems < sizeof (MotifTargetTableHeader)))
1052 header = (MotifTargetTableHeader *)data;
1054 header->n_lists = card16_to_host (header->n_lists, header->byte_order);
1055 header->total_size = card32_to_host (header->total_size, header->byte_order);
1057 gdk_x11_display_error_trap_push (display);
1058 XGetWindowProperty (display_x11->xdisplay,
1059 display_x11->motif_drag_window,
1060 motif_drag_targets_atom,
1061 (sizeof(MotifTargetTableHeader)+3)/4,
1062 (header->total_size + 3)/4 - (sizeof(MotifTargetTableHeader) + 3)/4,
1064 motif_drag_targets_atom, &type, &format, &nitems,
1065 &bytes_after, &target_bytes);
1067 if (gdk_x11_display_error_trap_pop (display) ||
1068 (format != 8) || (bytes_after != 0) ||
1069 (nitems != header->total_size - sizeof(MotifTargetTableHeader)))
1072 display_x11->motif_n_target_lists = header->n_lists;
1073 display_x11->motif_target_lists = g_new0 (GList *, display_x11->motif_n_target_lists);
1076 for (i=0; i<header->n_lists; i++)
1081 if (p + sizeof(guint16) - target_bytes > nitems)
1084 n_targets = card16_to_host (*(gushort *)p, header->byte_order);
1086 /* We need to make a copy of the targets, since it may
1089 targets = g_new (guint32, n_targets);
1090 memcpy (targets, p + sizeof(guint16), sizeof(guint32) * n_targets);
1092 p += sizeof(guint16) + n_targets * sizeof(guint32);
1093 if (p - target_bytes > nitems)
1096 for (j=0; j<n_targets; j++)
1097 display_x11->motif_target_lists[i] =
1098 g_list_prepend (display_x11->motif_target_lists[i],
1099 GUINT_TO_POINTER (card32_to_host (targets[j],
1100 header->byte_order)));
1102 display_x11->motif_target_lists[i] = g_list_reverse (display_x11->motif_target_lists[i]);
1112 XFree (target_bytes);
1116 if (display_x11->motif_target_lists)
1118 g_free (display_x11->motif_target_lists);
1119 display_x11->motif_target_lists = NULL;
1120 display_x11->motif_n_target_lists = 0;
1122 g_warning ("Error reading Motif target table\n");
1128 targets_sort_func (gconstpointer a, gconstpointer b)
1130 return (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b)) ?
1131 -1 : ((GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b)) ? 1 : 0);
1134 /* Check if given (sorted) list is in the targets table */
1136 motif_target_table_check (GdkDisplay *display,
1139 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1140 GList *tmp_list1, *tmp_list2;
1143 for (i=0; i<display_x11->motif_n_target_lists; i++)
1145 tmp_list1 = display_x11->motif_target_lists[i];
1148 while (tmp_list1 && tmp_list2)
1150 if (tmp_list1->data != tmp_list2->data)
1153 tmp_list1 = tmp_list1->next;
1154 tmp_list2 = tmp_list2->next;
1156 if (!tmp_list1 && !tmp_list2) /* Found it */
1164 motif_add_to_target_table (GdkDisplay *display,
1165 GList *targets) /* targets is list of GdkAtom */
1167 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1168 GList *sorted = NULL;
1173 /* make a sorted copy of the list */
1176 Atom xatom = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (targets->data));
1177 sorted = g_list_insert_sorted (sorted, GUINT_TO_POINTER (xatom), targets_sort_func);
1178 targets = targets->next;
1181 /* First check if it is there already */
1183 if (display_x11->motif_target_lists)
1184 index = motif_target_table_check (display, sorted);
1186 /* We need to grab the server while doing this, to ensure
1192 /* We need to make sure that it exists _before_ we grab the
1193 * server, since we can't open a new connection after we
1196 motif_find_drag_window (display, TRUE);
1198 gdk_x11_display_grab (display);
1199 motif_read_target_table (display);
1201 /* Check again, in case it was added in the meantime */
1202 if (display_x11->motif_target_lists)
1203 index = motif_target_table_check (display, sorted);
1207 guint32 total_size = 0;
1211 MotifTargetTableHeader *header;
1213 if (!display_x11->motif_target_lists)
1215 display_x11->motif_target_lists = g_new (GList *, 1);
1216 display_x11->motif_n_target_lists = 1;
1220 display_x11->motif_n_target_lists++;
1221 display_x11->motif_target_lists = g_realloc (display_x11->motif_target_lists,
1222 sizeof(GList *) * display_x11->motif_n_target_lists);
1224 display_x11->motif_target_lists[display_x11->motif_n_target_lists - 1] = sorted;
1226 index = display_x11->motif_n_target_lists - 1;
1228 total_size = sizeof (MotifTargetTableHeader);
1229 for (i = 0; i < display_x11->motif_n_target_lists ; i++)
1230 total_size += sizeof(guint16) + sizeof(guint32) * g_list_length (display_x11->motif_target_lists[i]);
1232 data = g_malloc (total_size);
1234 header = (MotifTargetTableHeader *)data;
1235 p = data + sizeof(MotifTargetTableHeader);
1237 header->byte_order = local_byte_order;
1238 header->protocol_version = 0;
1239 header->n_lists = display_x11->motif_n_target_lists;
1240 header->total_size = total_size;
1242 for (i = 0; i < display_x11->motif_n_target_lists ; i++)
1244 guint16 n_targets = g_list_length (display_x11->motif_target_lists[i]);
1245 guint32 *targets = g_new (guint32, n_targets);
1246 guint32 *p32 = targets;
1248 tmp_list = display_x11->motif_target_lists[i];
1251 *p32 = GPOINTER_TO_UINT (tmp_list->data);
1253 tmp_list = tmp_list->next;
1258 p += sizeof(guint16);
1260 memcpy (p, targets, n_targets * sizeof(guint32));
1263 p += sizeof(guint32) * n_targets;
1267 XChangeProperty (display_x11->xdisplay,
1268 display_x11->motif_drag_window,
1269 gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_TARGETS"),
1270 gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_TARGETS"),
1274 gdk_x11_display_ungrab (display);
1277 g_list_free (sorted);
1281 /* Translate flags */
1284 motif_dnd_translate_flags (GdkX11DragContext *context_x11,
1287 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1288 guint recommended_op = flags & 0x000f;
1289 guint possible_ops = (flags & 0x0f0) >> 4;
1291 switch (recommended_op)
1294 context->suggested_action = GDK_ACTION_MOVE;
1297 context->suggested_action = GDK_ACTION_COPY;
1300 context->suggested_action = GDK_ACTION_LINK;
1303 context->suggested_action = GDK_ACTION_COPY;
1307 context->actions = 0;
1308 if (possible_ops & XmDROP_MOVE)
1309 context->actions |= GDK_ACTION_MOVE;
1310 if (possible_ops & XmDROP_COPY)
1311 context->actions |= GDK_ACTION_COPY;
1312 if (possible_ops & XmDROP_LINK)
1313 context->actions |= GDK_ACTION_LINK;
1317 motif_dnd_get_flags (GdkDragContext *context)
1321 switch (context->suggested_action)
1323 case GDK_ACTION_MOVE:
1324 flags = XmDROP_MOVE;
1326 case GDK_ACTION_COPY:
1327 flags = XmDROP_COPY;
1329 case GDK_ACTION_LINK:
1330 flags = XmDROP_LINK;
1333 flags = XmDROP_NOOP;
1337 if (context->actions & GDK_ACTION_MOVE)
1338 flags |= XmDROP_MOVE << 8;
1339 if (context->actions & GDK_ACTION_COPY)
1340 flags |= XmDROP_COPY << 8;
1341 if (context->actions & GDK_ACTION_LINK)
1342 flags |= XmDROP_LINK << 8;
1350 motif_set_targets (GdkX11DragContext *context_x11)
1352 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1353 MotifDragInitiatorInfo info;
1355 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1357 info.byte_order = local_byte_order;
1358 info.protocol_version = 0;
1359 info.targets_index = motif_add_to_target_table (display, context->targets);
1364 g_snprintf (buf, 20, "_GDK_SELECTION_%d", i);
1366 context_x11->motif_selection = gdk_x11_get_xatom_by_name_for_display (display, buf);
1367 if (!XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), context_x11->motif_selection))
1371 info.selection_atom = context_x11->motif_selection;
1373 XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
1374 GDK_WINDOW_XID (context->source_window),
1375 context_x11->motif_selection,
1376 gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_INITIATOR_INFO"),
1378 (guchar *)&info, sizeof (info));
1380 context_x11->motif_targets_set = 1;
1384 motif_check_dest (GdkDisplay *display,
1387 gboolean retval = FALSE;
1389 MotifDragReceiverInfo *info;
1392 unsigned long nitems, after;
1393 Atom motif_drag_receiver_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_RECEIVER_INFO");
1395 gdk_x11_display_error_trap_push (display);
1396 XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win,
1397 motif_drag_receiver_info_atom,
1398 0, (sizeof(*info)+3)/4, False, AnyPropertyType,
1399 &type, &format, &nitems, &after,
1402 if (gdk_x11_display_error_trap_pop (display) == 0)
1406 info = (MotifDragReceiverInfo *)data;
1408 if ((format == 8) && (nitems == sizeof(*info)))
1410 if ((info->protocol_version == 0) &&
1411 ((info->protocol_style == XmDRAG_PREFER_PREREGISTER) ||
1412 (info->protocol_style == XmDRAG_PREFER_DYNAMIC) ||
1413 (info->protocol_style == XmDRAG_DYNAMIC)))
1419 g_warning ("Invalid Motif drag receiver property on window %ld\n", win));
1426 return retval ? win : None;
1430 motif_send_enter (GdkX11DragContext *context_x11,
1433 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1434 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1437 if (!G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client))
1438 return; /* Motif Dnd requires getting properties on the root window */
1440 xev.xclient.type = ClientMessage;
1441 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
1442 xev.xclient.format = 8;
1443 xev.xclient.window = GDK_WINDOW_XID (context->dest_window);
1445 MOTIF_XCLIENT_BYTE (&xev, 0) = XmTOP_LEVEL_ENTER;
1446 MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1447 MOTIF_XCLIENT_SHORT (&xev, 1) = 0;
1448 MOTIF_XCLIENT_LONG (&xev, 1) = time;
1449 MOTIF_XCLIENT_LONG (&xev, 2) = GDK_WINDOW_XID (context->source_window);
1451 if (!context_x11->motif_targets_set)
1452 motif_set_targets (context_x11);
1454 MOTIF_XCLIENT_LONG (&xev, 3) = context_x11->motif_selection;
1455 MOTIF_XCLIENT_LONG (&xev, 4) = 0;
1457 if (!_gdk_x11_display_send_xevent (display,
1458 GDK_WINDOW_XID (context->dest_window),
1461 g_message ("Send event to %lx failed",
1462 GDK_WINDOW_XID (context->dest_window)));
1466 motif_send_leave (GdkX11DragContext *context_x11,
1469 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1470 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1473 xev.xclient.type = ClientMessage;
1474 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
1475 xev.xclient.format = 8;
1476 xev.xclient.window = GDK_WINDOW_XID (context->dest_window);
1478 MOTIF_XCLIENT_BYTE (&xev, 0) = XmTOP_LEVEL_LEAVE;
1479 MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1480 MOTIF_XCLIENT_SHORT (&xev, 1) = 0;
1481 MOTIF_XCLIENT_LONG (&xev, 1) = time;
1482 MOTIF_XCLIENT_LONG (&xev, 2) = 0;
1483 MOTIF_XCLIENT_LONG (&xev, 3) = 0;
1484 MOTIF_XCLIENT_LONG (&xev, 4) = 0;
1486 if (!_gdk_x11_display_send_xevent (display,
1487 GDK_WINDOW_XID (context->dest_window),
1490 g_message ("Send event to %lx failed",
1491 GDK_WINDOW_XID (context->dest_window)));
1495 motif_send_motion (GdkX11DragContext *context_x11,
1498 GdkDragAction action,
1501 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1502 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1506 xev.xclient.type = ClientMessage;
1507 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
1508 xev.xclient.format = 8;
1509 xev.xclient.window = GDK_WINDOW_XID (context->dest_window);
1511 MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1512 MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
1513 MOTIF_XCLIENT_LONG (&xev, 1) = time;
1514 MOTIF_XCLIENT_LONG (&xev, 3) = 0;
1515 MOTIF_XCLIENT_LONG (&xev, 4) = 0;
1517 if ((context->suggested_action != context_x11->old_action) ||
1518 (context->actions != context_x11->old_actions))
1520 MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED;
1522 /* context_x11->drag_status = GDK_DRAG_STATUS_ACTION_WAIT; */
1527 MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION;
1529 MOTIF_XCLIENT_SHORT (&xev, 4) = x_root;
1530 MOTIF_XCLIENT_SHORT (&xev, 5) = y_root;
1532 context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1536 if (!_gdk_x11_display_send_xevent (display,
1537 GDK_WINDOW_XID (context->dest_window),
1540 g_message ("Send event to %lx failed",
1541 GDK_WINDOW_XID (context->dest_window)));
1547 motif_send_drop (GdkX11DragContext *context_x11,
1550 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1551 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1554 xev.xclient.type = ClientMessage;
1555 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
1556 xev.xclient.format = 8;
1557 xev.xclient.window = GDK_WINDOW_XID (context->dest_window);
1559 MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START;
1560 MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1561 MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
1562 MOTIF_XCLIENT_LONG (&xev, 1) = time;
1564 MOTIF_XCLIENT_SHORT (&xev, 4) = context_x11->last_x;
1565 MOTIF_XCLIENT_SHORT (&xev, 5) = context_x11->last_y;
1567 MOTIF_XCLIENT_LONG (&xev, 3) = context_x11->motif_selection;
1568 MOTIF_XCLIENT_LONG (&xev, 4) = GDK_WINDOW_XID (context->source_window);
1570 if (!_gdk_x11_display_send_xevent (display,
1571 GDK_WINDOW_XID (context->dest_window),
1574 g_message ("Send event to %lx failed",
1575 GDK_WINDOW_XID (context->dest_window)));
1581 motif_read_initiator_info (GdkDisplay *display,
1582 Window source_window,
1593 MotifDragInitiatorInfo *initiator_info;
1595 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1597 gdk_x11_display_error_trap_push (display);
1598 XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), source_window, atom,
1599 0, sizeof(*initiator_info), FALSE,
1600 gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_INITIATOR_INFO"),
1601 &type, &format, &nitems, &bytes_after,
1604 if (gdk_x11_display_error_trap_pop (display) ||
1605 (format != 8) || (nitems != sizeof (MotifDragInitiatorInfo)) ||
1608 g_warning ("Error reading initiator info\n");
1612 initiator_info = (MotifDragInitiatorInfo *)data;
1614 motif_read_target_table (display);
1616 initiator_info->targets_index =
1617 card16_to_host (initiator_info->targets_index, initiator_info->byte_order);
1618 initiator_info->selection_atom =
1619 card32_to_host (initiator_info->selection_atom, initiator_info->byte_order);
1621 if (initiator_info->targets_index >= display_x11->motif_n_target_lists)
1623 g_warning ("Invalid target index in TOP_LEVEL_ENTER MESSAGE");
1624 XFree (initiator_info);
1628 tmp_list = g_list_last (display_x11->motif_target_lists[initiator_info->targets_index]);
1633 GdkAtom atom = gdk_x11_xatom_to_atom_for_display (display, GPOINTER_TO_UINT (tmp_list->data));
1634 *targets = g_list_prepend (*targets, GDK_ATOM_TO_POINTER (atom));
1635 tmp_list = tmp_list->prev;
1638 #ifdef G_ENABLE_DEBUG
1639 if (_gdk_debug_flags & GDK_DEBUG_DND)
1640 print_target_list (*targets);
1641 #endif /* G_ENABLE_DEBUG */
1643 *selection = initiator_info->selection_atom;
1645 XFree (initiator_info);
1650 static GdkDragContext *
1651 motif_drag_context_new (GdkWindow *dest_window,
1653 guint32 source_window,
1656 GdkX11DragContext *context_x11;
1657 GdkDragContext *context;
1658 GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
1659 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1661 /* FIXME, current_dest_drag really shouldn't be NULL'd
1662 * if we error below.
1664 if (display_x11->current_dest_drag != NULL)
1666 if (timestamp >= display_x11->current_dest_drag->start_time)
1668 g_object_unref (display_x11->current_dest_drag);
1669 display_x11->current_dest_drag = NULL;
1675 context_x11 = g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, NULL);
1676 context = GDK_DRAG_CONTEXT (context_x11);
1678 context->protocol = GDK_DRAG_PROTO_MOTIF;
1679 context->is_source = FALSE;
1681 context->source_window = gdk_x11_window_lookup_for_display (display, source_window);
1682 if (context->source_window)
1683 g_object_ref (context->source_window);
1686 context->source_window = gdk_x11_window_foreign_new_for_display (display, source_window);
1687 if (!context->source_window)
1689 g_object_unref (context_x11);
1694 context->dest_window = dest_window;
1695 g_object_ref (dest_window);
1696 context->start_time = timestamp;
1698 if (!motif_read_initiator_info (GDK_WINDOW_DISPLAY (dest_window),
1702 &context_x11->motif_selection))
1704 g_object_unref (context_x11);
1712 * The MOTIF drag protocol has no real provisions for distinguishing
1713 * multiple simultaneous drops. If the sources grab the pointer
1714 * when doing drags, that shouldn't happen, in any case. If it
1715 * does, we can't do much except hope for the best.
1718 static GdkFilterReturn
1719 motif_top_level_enter (GdkEvent *event,
1722 guint32 source_window,
1725 GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1726 GdkDragContext *new_context;
1728 GDK_NOTE(DND, g_message ("Motif DND top level enter: flags: %#4x time: %d source_widow: %#4x atom: %d",
1729 flags, timestamp, source_window, atom));
1731 new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1733 return GDK_FILTER_REMOVE;
1735 event->dnd.type = GDK_DRAG_ENTER;
1736 event->dnd.context = new_context;
1737 g_object_ref (new_context);
1739 display_x11->current_dest_drag = new_context;
1741 return GDK_FILTER_TRANSLATE;
1744 static GdkFilterReturn
1745 motif_top_level_leave (GdkEvent *event,
1749 GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1751 GDK_NOTE(DND, g_message ("Motif DND top level leave: flags: %#4x time: %d",
1754 if ((display_x11->current_dest_drag != NULL) &&
1755 (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1756 (timestamp >= display_x11->current_dest_drag->start_time))
1758 event->dnd.type = GDK_DRAG_LEAVE;
1759 /* Pass ownership of context to the event */
1760 event->dnd.context = display_x11->current_dest_drag;
1762 display_x11->current_dest_drag = NULL;
1764 return GDK_FILTER_TRANSLATE;
1767 return GDK_FILTER_REMOVE;
1770 static GdkFilterReturn
1771 motif_motion (GdkEvent *event,
1777 GdkX11DragContext *context_x11;
1778 GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1780 GDK_NOTE(DND, g_message ("Motif DND motion: flags: %#4x time: %d (%d, %d)",
1781 flags, timestamp, x_root, y_root));
1783 if ((display_x11->current_dest_drag != NULL) &&
1784 (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1785 (timestamp >= display_x11->current_dest_drag->start_time))
1787 context_x11 = GDK_X11_DRAG_CONTEXT (display_x11->current_dest_drag);
1789 event->dnd.type = GDK_DRAG_MOTION;
1790 event->dnd.context = display_x11->current_dest_drag;
1791 g_object_ref (display_x11->current_dest_drag);
1793 event->dnd.time = timestamp;
1795 motif_dnd_translate_flags (context_x11, flags);
1797 event->dnd.x_root = x_root;
1798 event->dnd.y_root = y_root;
1800 context_x11->last_x = x_root;
1801 context_x11->last_y = y_root;
1803 context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1805 return GDK_FILTER_TRANSLATE;
1808 return GDK_FILTER_REMOVE;
1811 static GdkFilterReturn
1812 motif_operation_changed (GdkEvent *event,
1816 GdkX11DragContext *context_x11;
1817 GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1818 GDK_NOTE(DND, g_message ("Motif DND operation changed: flags: %#4x time: %d",
1821 if ((display_x11->current_dest_drag != NULL) &&
1822 (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1823 (timestamp >= display_x11->current_dest_drag->start_time))
1825 event->dnd.type = GDK_DRAG_MOTION;
1826 event->dnd.send_event = FALSE;
1827 event->dnd.context = display_x11->current_dest_drag;
1828 g_object_ref (display_x11->current_dest_drag);
1830 event->dnd.time = timestamp;
1831 context_x11 = GDK_X11_DRAG_CONTEXT (display_x11->current_dest_drag);
1833 motif_dnd_translate_flags (context_x11, flags);
1835 event->dnd.x_root = context_x11->last_x;
1836 event->dnd.y_root = context_x11->last_y;
1838 context_x11->drag_status = GDK_DRAG_STATUS_ACTION_WAIT;
1840 return GDK_FILTER_TRANSLATE;
1843 return GDK_FILTER_REMOVE;
1846 static GdkFilterReturn
1847 motif_drop_start (GdkEvent *event,
1850 guint32 source_window,
1855 GdkDragContext *new_context;
1856 GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1858 GDK_NOTE(DND, g_message ("Motif DND drop start: flags: %#4x time: %d (%d, %d) source_widow: %#4x atom: %d",
1859 flags, timestamp, x_root, y_root, source_window, atom));
1861 new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1863 return GDK_FILTER_REMOVE;
1865 motif_dnd_translate_flags (GDK_X11_DRAG_CONTEXT (new_context), flags);
1867 event->dnd.type = GDK_DROP_START;
1868 event->dnd.context = new_context;
1869 event->dnd.time = timestamp;
1870 event->dnd.x_root = x_root;
1871 event->dnd.y_root = y_root;
1873 gdk_x11_window_set_user_time (event->any.window, timestamp);
1875 g_object_ref (new_context);
1876 display_x11->current_dest_drag = new_context;
1878 return GDK_FILTER_TRANSLATE;
1881 static GdkFilterReturn
1882 motif_drag_status (GdkEvent *event,
1886 GdkDragContext *context;
1887 GdkDisplay *display;
1890 g_message ("Motif status message: flags %x", flags));
1892 display = gdk_window_get_display (event->any.window);
1894 return GDK_FILTER_REMOVE;
1896 context = gdk_drag_context_find (display, TRUE, GDK_WINDOW_XID (event->any.window), None);
1900 GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
1901 if ((context_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT) ||
1902 (context_x11->drag_status == GDK_DRAG_STATUS_ACTION_WAIT))
1903 context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
1905 event->dnd.type = GDK_DRAG_STATUS;
1906 event->dnd.send_event = FALSE;
1907 event->dnd.context = context;
1908 g_object_ref (context);
1910 event->dnd.time = timestamp;
1912 if ((flags & 0x00f0) >> 4 == XmDROP_SITE_VALID)
1914 switch (flags & 0x000f)
1917 context->action = 0;
1920 context->action = GDK_ACTION_MOVE;
1923 context->action = GDK_ACTION_COPY;
1926 context->action = GDK_ACTION_LINK;
1931 context->action = 0;
1933 return GDK_FILTER_TRANSLATE;
1935 return GDK_FILTER_REMOVE;
1938 static GdkFilterReturn
1939 motif_dnd_filter (GdkXEvent *xev,
1943 XEvent *xevent = (XEvent *)xev;
1948 guint32 source_window;
1950 gint16 x_root, y_root;
1953 if (!event->any.window ||
1954 gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
1955 return GDK_FILTER_CONTINUE; /* Not for us */
1957 /* First read some fields common to all Motif DND messages */
1958 reason = MOTIF_UNPACK_BYTE (xevent, 0);
1959 flags = MOTIF_UNPACK_SHORT (xevent, 1);
1960 timestamp = MOTIF_UNPACK_LONG (xevent, 1);
1962 is_reply = ((reason & 0x80) != 0);
1964 switch (reason & 0x7f)
1966 case XmTOP_LEVEL_ENTER:
1967 source_window = MOTIF_UNPACK_LONG (xevent, 2);
1968 atom = MOTIF_UNPACK_LONG (xevent, 3);
1969 return motif_top_level_enter (event, flags, timestamp, source_window, atom);
1970 case XmTOP_LEVEL_LEAVE:
1971 return motif_top_level_leave (event, flags, timestamp);
1974 x_root = MOTIF_UNPACK_SHORT (xevent, 4);
1975 y_root = MOTIF_UNPACK_SHORT (xevent, 5);
1978 return motif_motion (event, flags, timestamp, x_root, y_root);
1980 return motif_drag_status (event, flags, timestamp);
1982 case XmDROP_SITE_ENTER:
1983 return motif_drag_status (event, flags, timestamp);
1985 case XmDROP_SITE_LEAVE:
1986 return motif_drag_status (event,
1987 XmNO_DROP_SITE << 8 | XmDROP_NOOP,
1990 x_root = MOTIF_UNPACK_SHORT (xevent, 4);
1991 y_root = MOTIF_UNPACK_SHORT (xevent, 5);
1992 atom = MOTIF_UNPACK_LONG (xevent, 3);
1993 source_window = MOTIF_UNPACK_LONG (xevent, 4);
1996 return motif_drop_start (event, flags, timestamp, source_window, atom, x_root, y_root);
1999 case XmOPERATION_CHANGED:
2001 return motif_operation_changed (event, flags, timestamp);
2003 return motif_drag_status (event, flags, timestamp);
2006 /* To the best of my knowledge, these next two messages are
2007 * not part of the protocol, though they are defined in
2011 case XmDRAG_DROP_FINISH:
2015 return GDK_FILTER_REMOVE;
2018 /*************************************************************
2019 ***************************** XDND **************************
2020 *************************************************************/
2022 /* Utility functions */
2027 GdkDragAction action;
2028 } xdnd_actions_table[] = {
2029 { "XdndActionCopy", None, GDK_ACTION_COPY },
2030 { "XdndActionMove", None, GDK_ACTION_MOVE },
2031 { "XdndActionLink", None, GDK_ACTION_LINK },
2032 { "XdndActionAsk", None, GDK_ACTION_ASK },
2033 { "XdndActionPrivate", None, GDK_ACTION_COPY },
2036 static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
2037 static gboolean xdnd_actions_initialized = FALSE;
2040 xdnd_initialize_actions (void)
2044 xdnd_actions_initialized = TRUE;
2045 for (i = 0; i < xdnd_n_actions; i++)
2046 xdnd_actions_table[i].atom = gdk_atom_intern_static_string (xdnd_actions_table[i].name);
2049 static GdkDragAction
2050 xdnd_action_from_atom (GdkDisplay *display,
2059 atom = gdk_x11_xatom_to_atom_for_display (display, xatom);
2061 if (!xdnd_actions_initialized)
2062 xdnd_initialize_actions();
2064 for (i = 0; i < xdnd_n_actions; i++)
2065 if (atom == xdnd_actions_table[i].atom)
2066 return xdnd_actions_table[i].action;
2072 xdnd_action_to_atom (GdkDisplay *display,
2073 GdkDragAction action)
2077 if (!xdnd_actions_initialized)
2078 xdnd_initialize_actions();
2080 for (i = 0; i < xdnd_n_actions; i++)
2081 if (action == xdnd_actions_table[i].action)
2082 return gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
2089 static GdkFilterReturn
2090 xdnd_status_filter (GdkXEvent *xev,
2094 GdkDisplay *display;
2095 XEvent *xevent = (XEvent *)xev;
2096 guint32 dest_window = xevent->xclient.data.l[0];
2097 guint32 flags = xevent->xclient.data.l[1];
2098 Atom action = xevent->xclient.data.l[4];
2099 GdkDragContext *context;
2101 if (!event->any.window ||
2102 gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2103 return GDK_FILTER_CONTINUE; /* Not for us */
2106 g_message ("XdndStatus: dest_window: %#x action: %ld",
2107 dest_window, action));
2109 display = gdk_window_get_display (event->any.window);
2110 context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
2114 GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
2115 if (context_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
2116 context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
2118 event->dnd.send_event = FALSE;
2119 event->dnd.type = GDK_DRAG_STATUS;
2120 event->dnd.context = context;
2121 gdk_event_set_device (event, gdk_drag_context_get_device (context));
2122 g_object_ref (context);
2124 event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2125 if (!(action != 0) != !(flags & 1))
2128 g_warning ("Received status event with flags not corresponding to action!\n"));
2132 context->action = xdnd_action_from_atom (display, action);
2134 return GDK_FILTER_TRANSLATE;
2137 return GDK_FILTER_REMOVE;
2140 static GdkFilterReturn
2141 xdnd_finished_filter (GdkXEvent *xev,
2145 GdkDisplay *display;
2146 XEvent *xevent = (XEvent *)xev;
2147 guint32 dest_window = xevent->xclient.data.l[0];
2148 GdkDragContext *context;
2149 GdkX11DragContext *context_x11;
2151 if (!event->any.window ||
2152 gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2153 return GDK_FILTER_CONTINUE; /* Not for us */
2156 g_message ("XdndFinished: dest_window: %#x", dest_window));
2158 display = gdk_window_get_display (event->any.window);
2159 context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
2163 context_x11 = GDK_X11_DRAG_CONTEXT (context);
2164 if (context_x11->version == 5)
2165 context_x11->drop_failed = xevent->xclient.data.l[1] == 0;
2167 event->dnd.type = GDK_DROP_FINISHED;
2168 event->dnd.context = context;
2169 gdk_event_set_device (event, gdk_drag_context_get_device (context));
2170 g_object_ref (context);
2172 event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2174 return GDK_FILTER_TRANSLATE;
2177 return GDK_FILTER_REMOVE;
2181 xdnd_set_targets (GdkX11DragContext *context_x11)
2183 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2185 GList *tmp_list = context->targets;
2187 gint n_atoms = g_list_length (context->targets);
2188 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2190 atomlist = g_new (Atom, n_atoms);
2194 atomlist[i] = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (tmp_list->data));
2195 tmp_list = tmp_list->next;
2199 XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
2200 GDK_WINDOW_XID (context->source_window),
2201 gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
2202 XA_ATOM, 32, PropModeReplace,
2203 (guchar *)atomlist, n_atoms);
2207 context_x11->xdnd_targets_set = 1;
2211 xdnd_set_actions (GdkX11DragContext *context_x11)
2213 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2218 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2220 if (!xdnd_actions_initialized)
2221 xdnd_initialize_actions();
2223 actions = context->actions;
2225 for (i = 0; i < xdnd_n_actions; i++)
2227 if (actions & xdnd_actions_table[i].action)
2229 actions &= ~xdnd_actions_table[i].action;
2234 atomlist = g_new (Atom, n_atoms);
2236 actions = context->actions;
2238 for (i = 0; i < xdnd_n_actions; i++)
2240 if (actions & xdnd_actions_table[i].action)
2242 actions &= ~xdnd_actions_table[i].action;
2243 atomlist[n_atoms] = gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
2248 XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
2249 GDK_WINDOW_XID (context->source_window),
2250 gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
2251 XA_ATOM, 32, PropModeReplace,
2252 (guchar *)atomlist, n_atoms);
2256 context_x11->xdnd_actions_set = TRUE;
2257 context_x11->xdnd_actions = context->actions;
2261 send_client_message_async_cb (Window window,
2265 GdkDragContext *context = data;
2267 g_message ("Got async callback for #%lx, success = %d",
2270 /* On failure, we immediately continue with the protocol
2271 * so we don't end up blocking for a timeout
2274 context->dest_window &&
2275 window == GDK_WINDOW_XID (context->dest_window))
2277 GdkEvent *temp_event;
2278 GdkX11DragContext *context_x11 = data;
2280 g_object_unref (context->dest_window);
2281 context->dest_window = NULL;
2282 context->action = 0;
2284 context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
2286 temp_event = gdk_event_new (GDK_DRAG_STATUS);
2287 temp_event->dnd.window = g_object_ref (context->source_window);
2288 temp_event->dnd.send_event = TRUE;
2289 temp_event->dnd.context = g_object_ref (context);
2290 temp_event->dnd.time = GDK_CURRENT_TIME;
2291 gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
2293 gdk_event_put (temp_event);
2295 gdk_event_free (temp_event);
2298 g_object_unref (context);
2303 gdk_drag_context_get_display (GdkDragContext *context)
2305 if (context->source_window)
2306 return GDK_WINDOW_DISPLAY (context->source_window);
2307 else if (context->dest_window)
2308 return GDK_WINDOW_DISPLAY (context->dest_window);
2310 g_assert_not_reached ();
2315 send_client_message_async (GdkDragContext *context,
2319 XClientMessageEvent *event_send)
2321 GdkDisplay *display = gdk_drag_context_get_display (context);
2323 g_object_ref (context);
2325 _gdk_x11_send_client_message_async (display, window,
2326 propagate, event_mask, event_send,
2327 send_client_message_async_cb, context);
2331 xdnd_send_xevent (GdkX11DragContext *context_x11,
2336 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2337 GdkDisplay *display = gdk_drag_context_get_display (context);
2341 g_assert (event_send->xany.type == ClientMessage);
2343 /* We short-circuit messages to ourselves */
2344 if (gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
2348 for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
2350 if (gdk_x11_get_xatom_by_name_for_display (display, xdnd_filters[i].atom_name) ==
2351 event_send->xclient.message_type)
2353 GdkEvent *temp_event;
2355 temp_event = gdk_event_new (GDK_NOTHING);
2356 temp_event->any.window = g_object_ref (window);
2358 if ((*xdnd_filters[i].func) (event_send, temp_event, NULL) == GDK_FILTER_TRANSLATE)
2360 gdk_event_put (temp_event);
2361 gdk_event_free (temp_event);
2369 xwindow = GDK_WINDOW_XID (window);
2371 if (_gdk_x11_display_is_root_window (display, xwindow))
2372 event_mask = ButtonPressMask;
2376 send_client_message_async (context, xwindow, propagate, event_mask,
2377 &event_send->xclient);
2383 xdnd_send_enter (GdkX11DragContext *context_x11)
2385 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2386 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->dest_window);
2389 xev.xclient.type = ClientMessage;
2390 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndEnter");
2391 xev.xclient.format = 32;
2392 xev.xclient.window = context_x11->drop_xid
2393 ? context_x11->drop_xid
2394 : GDK_WINDOW_XID (context->dest_window);
2395 xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2396 xev.xclient.data.l[1] = (context_x11->version << 24); /* version */
2397 xev.xclient.data.l[2] = 0;
2398 xev.xclient.data.l[3] = 0;
2399 xev.xclient.data.l[4] = 0;
2402 g_message ("Sending enter source window %#lx XDND protocol version %d\n",
2403 GDK_WINDOW_XID (context->source_window), context_x11->version));
2404 if (g_list_length (context->targets) > 3)
2406 if (!context_x11->xdnd_targets_set)
2407 xdnd_set_targets (context_x11);
2408 xev.xclient.data.l[1] |= 1;
2412 GList *tmp_list = context->targets;
2417 xev.xclient.data.l[i] = gdk_x11_atom_to_xatom_for_display (display,
2418 GDK_POINTER_TO_ATOM (tmp_list->data));
2419 tmp_list = tmp_list->next;
2424 if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2427 g_message ("Send event to %lx failed",
2428 GDK_WINDOW_XID (context->dest_window)));
2429 g_object_unref (context->dest_window);
2430 context->dest_window = NULL;
2435 xdnd_send_leave (GdkX11DragContext *context_x11)
2437 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2438 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2441 xev.xclient.type = ClientMessage;
2442 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndLeave");
2443 xev.xclient.format = 32;
2444 xev.xclient.window = context_x11->drop_xid
2445 ? context_x11->drop_xid
2446 : GDK_WINDOW_XID (context->dest_window);
2447 xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2448 xev.xclient.data.l[1] = 0;
2449 xev.xclient.data.l[2] = 0;
2450 xev.xclient.data.l[3] = 0;
2451 xev.xclient.data.l[4] = 0;
2453 if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2456 g_message ("Send event to %lx failed",
2457 GDK_WINDOW_XID (context->dest_window)));
2458 g_object_unref (context->dest_window);
2459 context->dest_window = NULL;
2464 xdnd_send_drop (GdkX11DragContext *context_x11,
2467 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2468 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2471 xev.xclient.type = ClientMessage;
2472 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndDrop");
2473 xev.xclient.format = 32;
2474 xev.xclient.window = context_x11->drop_xid
2475 ? context_x11->drop_xid
2476 : GDK_WINDOW_XID (context->dest_window);
2477 xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2478 xev.xclient.data.l[1] = 0;
2479 xev.xclient.data.l[2] = time;
2480 xev.xclient.data.l[3] = 0;
2481 xev.xclient.data.l[4] = 0;
2483 if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2486 g_message ("Send event to %lx failed",
2487 GDK_WINDOW_XID (context->dest_window)));
2488 g_object_unref (context->dest_window);
2489 context->dest_window = NULL;
2494 xdnd_send_motion (GdkX11DragContext *context_x11,
2497 GdkDragAction action,
2500 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2501 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2504 xev.xclient.type = ClientMessage;
2505 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndPosition");
2506 xev.xclient.format = 32;
2507 xev.xclient.window = context_x11->drop_xid
2508 ? context_x11->drop_xid
2509 : GDK_WINDOW_XID (context->dest_window);
2510 xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2511 xev.xclient.data.l[1] = 0;
2512 xev.xclient.data.l[2] = (x_root << 16) | y_root;
2513 xev.xclient.data.l[3] = time;
2514 xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
2516 if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2519 g_message ("Send event to %lx failed",
2520 GDK_WINDOW_XID (context->dest_window)));
2521 g_object_unref (context->dest_window);
2522 context->dest_window = NULL;
2524 context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
2528 xdnd_check_dest (GdkDisplay *display,
2530 guint *xdnd_version)
2532 gboolean retval = FALSE;
2535 unsigned long nitems, after;
2540 Atom xdnd_proxy_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndProxy");
2541 Atom xdnd_aware_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndAware");
2545 gdk_x11_display_error_trap_push (display);
2546 if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win,
2548 1, False, AnyPropertyType,
2549 &type, &format, &nitems, &after,
2554 proxy_data = (Window *)data;
2556 if ((format == 32) && (nitems == 1))
2558 proxy = *proxy_data;
2562 g_warning ("Invalid XdndProxy "
2563 "property on window %ld\n", win));
2568 if ((XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), proxy ? proxy : win,
2570 1, False, AnyPropertyType,
2571 &type, &format, &nitems, &after,
2572 &data) == Success) &&
2575 version = (Atom *)data;
2577 if ((format == 32) && (nitems == 1))
2582 *xdnd_version = *version;
2586 g_warning ("Invalid XdndAware "
2587 "property on window %ld\n", win));
2593 gdk_x11_display_error_trap_pop_ignored (display);
2595 return retval ? (proxy ? proxy : win) : None;
2601 xdnd_read_actions (GdkX11DragContext *context_x11)
2603 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2604 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2607 gulong nitems, after;
2612 context_x11->xdnd_have_actions = FALSE;
2614 if (gdk_window_get_window_type (context->source_window) == GDK_WINDOW_FOREIGN)
2616 /* Get the XdndActionList, if set */
2618 gdk_x11_display_error_trap_push (display);
2619 if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
2620 GDK_WINDOW_XID (context->source_window),
2621 gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
2623 False, XA_ATOM, &type, &format, &nitems,
2624 &after, &data) == Success &&
2627 atoms = (Atom *)data;
2629 context->actions = 0;
2631 for (i = 0; i < nitems; i++)
2632 context->actions |= xdnd_action_from_atom (display, atoms[i]);
2634 context_x11->xdnd_have_actions = TRUE;
2636 #ifdef G_ENABLE_DEBUG
2637 if (_gdk_debug_flags & GDK_DEBUG_DND)
2639 GString *action_str = g_string_new (NULL);
2640 if (context->actions & GDK_ACTION_MOVE)
2641 g_string_append(action_str, "MOVE ");
2642 if (context->actions & GDK_ACTION_COPY)
2643 g_string_append(action_str, "COPY ");
2644 if (context->actions & GDK_ACTION_LINK)
2645 g_string_append(action_str, "LINK ");
2646 if (context->actions & GDK_ACTION_ASK)
2647 g_string_append(action_str, "ASK ");
2649 g_message("Xdnd actions = %s", action_str->str);
2650 g_string_free (action_str, TRUE);
2652 #endif /* G_ENABLE_DEBUG */
2659 gdk_x11_display_error_trap_pop_ignored (display);
2665 GdkDragContext *source_context;
2667 source_context = gdk_drag_context_find (display, TRUE,
2668 GDK_WINDOW_XID (context->source_window),
2669 GDK_WINDOW_XID (context->dest_window));
2673 context->actions = source_context->actions;
2674 context_x11->xdnd_have_actions = TRUE;
2679 /* We have to make sure that the XdndActionList we keep internally
2680 * is up to date with the XdndActionList on the source window
2681 * because we get no notification, because Xdnd wasn't meant
2682 * to continually send actions. So we select on PropertyChangeMask
2683 * and add this filter.
2685 static GdkFilterReturn
2686 xdnd_source_window_filter (GdkXEvent *xev,
2690 XEvent *xevent = (XEvent *)xev;
2691 GdkX11DragContext *context_x11 = cb_data;
2692 GdkDisplay *display = GDK_WINDOW_DISPLAY(event->any.window);
2694 if ((xevent->xany.type == PropertyNotify) &&
2695 (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList")))
2697 xdnd_read_actions (context_x11);
2699 return GDK_FILTER_REMOVE;
2702 return GDK_FILTER_CONTINUE;
2706 xdnd_manage_source_filter (GdkDragContext *context,
2708 gboolean add_filter)
2710 if (!GDK_WINDOW_DESTROYED (window) &&
2711 gdk_window_get_window_type (window) == GDK_WINDOW_FOREIGN)
2713 gdk_x11_display_error_trap_push (GDK_WINDOW_DISPLAY (window));
2717 gdk_window_set_events (window,
2718 gdk_window_get_events (window) |
2719 GDK_PROPERTY_CHANGE_MASK);
2720 gdk_window_add_filter (window, xdnd_source_window_filter, context);
2724 gdk_window_remove_filter (window,
2725 xdnd_source_window_filter,
2727 /* Should we remove the GDK_PROPERTY_NOTIFY mask?
2728 * but we might want it for other reasons. (Like
2729 * INCR selection transactions).
2733 gdk_x11_display_error_trap_pop_ignored (GDK_WINDOW_DISPLAY (window));
2738 base_precache_atoms (GdkDisplay *display)
2740 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
2742 if (!display_x11->base_dnd_atoms_precached)
2744 static const char *const precache_atoms[] = {
2745 "ENLIGHTENMENT_DESKTOP",
2749 "_MOTIF_DRAG_RECEIVER_INFO"
2752 _gdk_x11_precache_atoms (display,
2753 precache_atoms, G_N_ELEMENTS (precache_atoms));
2755 display_x11->base_dnd_atoms_precached = TRUE;
2760 xdnd_precache_atoms (GdkDisplay *display)
2762 GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
2764 if (!display_x11->xdnd_atoms_precached)
2766 static const gchar *const precache_atoms[] = {
2772 "XdndActionPrivate",
2783 _gdk_x11_precache_atoms (display,
2784 precache_atoms, G_N_ELEMENTS (precache_atoms));
2786 display_x11->xdnd_atoms_precached = TRUE;
2790 static GdkFilterReturn
2791 xdnd_enter_filter (GdkXEvent *xev,
2795 GdkDeviceManager *device_manager;
2796 GdkDisplay *display;
2797 GdkX11Display *display_x11;
2798 XEvent *xevent = (XEvent *)xev;
2799 GdkDragContext *context;
2800 GdkX11DragContext *context_x11;
2804 gulong nitems, after;
2807 guint32 source_window;
2811 if (!event->any.window ||
2812 gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2813 return GDK_FILTER_CONTINUE; /* Not for us */
2815 source_window = xevent->xclient.data.l[0];
2816 get_types = ((xevent->xclient.data.l[1] & 1) != 0);
2817 version = (xevent->xclient.data.l[1] & 0xff000000) >> 24;
2819 display = GDK_WINDOW_DISPLAY (event->any.window);
2820 display_x11 = GDK_X11_DISPLAY (display);
2822 xdnd_precache_atoms (display);
2825 g_message ("XdndEnter: source_window: %#x, version: %#x",
2826 source_window, version));
2830 /* Old source ignore */
2831 GDK_NOTE (DND, g_message ("Ignored old XdndEnter message"));
2832 return GDK_FILTER_REMOVE;
2835 if (display_x11->current_dest_drag != NULL)
2837 g_object_unref (display_x11->current_dest_drag);
2838 display_x11->current_dest_drag = NULL;
2841 context_x11 = (GdkX11DragContext *)g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, NULL);
2842 context = (GdkDragContext *)context_x11;
2844 context->protocol = GDK_DRAG_PROTO_XDND;
2845 context_x11->version = version;
2847 /* FIXME: Should extend DnD protocol to have device info */
2848 device_manager = gdk_display_get_device_manager (display);
2849 gdk_drag_context_set_device (context, gdk_device_manager_get_client_pointer (device_manager));
2851 context->source_window = gdk_x11_window_lookup_for_display (display, source_window);
2852 if (context->source_window)
2853 g_object_ref (context->source_window);
2856 context->source_window = gdk_x11_window_foreign_new_for_display (display, source_window);
2857 if (!context->source_window)
2859 g_object_unref (context);
2860 return GDK_FILTER_REMOVE;
2863 context->dest_window = event->any.window;
2864 g_object_ref (context->dest_window);
2866 context->targets = NULL;
2869 gdk_x11_display_error_trap_push (display);
2870 XGetWindowProperty (GDK_WINDOW_XDISPLAY (event->any.window),
2872 gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
2874 False, XA_ATOM, &type, &format, &nitems,
2877 if (gdk_x11_display_error_trap_pop (display) || (format != 32) || (type != XA_ATOM))
2879 g_object_unref (context);
2884 return GDK_FILTER_REMOVE;
2887 atoms = (Atom *)data;
2889 for (i = 0; i < nitems; i++)
2891 g_list_append (context->targets,
2892 GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display,
2899 for (i = 0; i < 3; i++)
2900 if (xevent->xclient.data.l[2 + i])
2902 g_list_append (context->targets,
2903 GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display,
2904 xevent->xclient.data.l[2 + i])));
2907 #ifdef G_ENABLE_DEBUG
2908 if (_gdk_debug_flags & GDK_DEBUG_DND)
2909 print_target_list (context->targets);
2910 #endif /* G_ENABLE_DEBUG */
2912 xdnd_manage_source_filter (context, context->source_window, TRUE);
2913 xdnd_read_actions (context_x11);
2915 event->dnd.type = GDK_DRAG_ENTER;
2916 event->dnd.context = context;
2917 gdk_event_set_device (event, gdk_drag_context_get_device (context));
2918 g_object_ref (context);
2920 display_x11->current_dest_drag = context;
2922 return GDK_FILTER_TRANSLATE;
2925 static GdkFilterReturn
2926 xdnd_leave_filter (GdkXEvent *xev,
2930 XEvent *xevent = (XEvent *)xev;
2931 guint32 source_window = xevent->xclient.data.l[0];
2932 GdkDisplay *display;
2933 GdkX11Display *display_x11;
2935 if (!event->any.window ||
2936 gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2937 return GDK_FILTER_CONTINUE; /* Not for us */
2940 g_message ("XdndLeave: source_window: %#x",
2943 display = GDK_WINDOW_DISPLAY (event->any.window);
2944 display_x11 = GDK_X11_DISPLAY (display);
2946 xdnd_precache_atoms (display);
2948 if ((display_x11->current_dest_drag != NULL) &&
2949 (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2950 (GDK_WINDOW_XID (display_x11->current_dest_drag->source_window) == source_window))
2952 event->dnd.type = GDK_DRAG_LEAVE;
2953 /* Pass ownership of context to the event */
2954 event->dnd.context = display_x11->current_dest_drag;
2955 gdk_event_set_device (event, gdk_drag_context_get_device (event->dnd.context));
2957 display_x11->current_dest_drag = NULL;
2959 return GDK_FILTER_TRANSLATE;
2962 return GDK_FILTER_REMOVE;
2965 static GdkFilterReturn
2966 xdnd_position_filter (GdkXEvent *xev,
2970 XEvent *xevent = (XEvent *)xev;
2971 guint32 source_window = xevent->xclient.data.l[0];
2972 gint16 x_root = xevent->xclient.data.l[2] >> 16;
2973 gint16 y_root = xevent->xclient.data.l[2] & 0xffff;
2974 guint32 time = xevent->xclient.data.l[3];
2975 Atom action = xevent->xclient.data.l[4];
2976 GdkDisplay *display;
2977 GdkX11Display *display_x11;
2978 GdkDragContext *context;
2979 GdkX11DragContext *context_x11;
2981 if (!event->any.window ||
2982 gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2983 return GDK_FILTER_CONTINUE; /* Not for us */
2986 g_message ("XdndPosition: source_window: %#x position: (%d, %d) time: %d action: %ld",
2987 source_window, x_root, y_root, time, action));
2989 display = GDK_WINDOW_DISPLAY (event->any.window);
2990 display_x11 = GDK_X11_DISPLAY (display);
2992 xdnd_precache_atoms (display);
2994 context = display_x11->current_dest_drag;
2996 if ((context != NULL) &&
2997 (context->protocol == GDK_DRAG_PROTO_XDND) &&
2998 (GDK_WINDOW_XID (context->source_window) == source_window))
3000 context_x11 = GDK_X11_DRAG_CONTEXT (context);
3002 event->dnd.type = GDK_DRAG_MOTION;
3003 event->dnd.context = context;
3004 gdk_event_set_device (event, gdk_drag_context_get_device (context));
3005 g_object_ref (context);
3007 event->dnd.time = time;
3009 context->suggested_action = xdnd_action_from_atom (display, action);
3011 if (!context_x11->xdnd_have_actions)
3012 context->actions = context->suggested_action;
3014 event->dnd.x_root = x_root;
3015 event->dnd.y_root = y_root;
3017 context_x11->last_x = x_root;
3018 context_x11->last_y = y_root;
3020 return GDK_FILTER_TRANSLATE;
3023 return GDK_FILTER_REMOVE;
3026 static GdkFilterReturn
3027 xdnd_drop_filter (GdkXEvent *xev,
3031 XEvent *xevent = (XEvent *)xev;
3032 guint32 source_window = xevent->xclient.data.l[0];
3033 guint32 time = xevent->xclient.data.l[2];
3034 GdkDisplay *display;
3035 GdkX11Display *display_x11;
3036 GdkDragContext *context;
3037 GdkX11DragContext *context_x11;
3039 if (!event->any.window ||
3040 gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
3041 return GDK_FILTER_CONTINUE; /* Not for us */
3044 g_message ("XdndDrop: source_window: %#x time: %d",
3045 source_window, time));
3047 display = GDK_WINDOW_DISPLAY (event->any.window);
3048 display_x11 = GDK_X11_DISPLAY (display);
3050 xdnd_precache_atoms (display);
3052 context = display_x11->current_dest_drag;
3054 if ((context != NULL) &&
3055 (context->protocol == GDK_DRAG_PROTO_XDND) &&
3056 (GDK_WINDOW_XID (context->source_window) == source_window))
3058 context_x11 = GDK_X11_DRAG_CONTEXT (context);
3059 event->dnd.type = GDK_DROP_START;
3061 event->dnd.context = context;
3062 gdk_event_set_device (event, gdk_drag_context_get_device (context));
3063 g_object_ref (context);
3065 event->dnd.time = time;
3066 event->dnd.x_root = context_x11->last_x;
3067 event->dnd.y_root = context_x11->last_y;
3069 gdk_x11_window_set_user_time (event->any.window, time);
3071 return GDK_FILTER_TRANSLATE;
3074 return GDK_FILTER_REMOVE;
3078 _gdk_x11_display_init_dnd (GdkDisplay *display)
3083 gdk_display_add_client_message_filter (
3085 gdk_atom_intern_static_string ("_MOTIF_DRAG_AND_DROP_MESSAGE"),
3086 motif_dnd_filter, NULL);
3088 for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
3090 gdk_display_add_client_message_filter (
3092 gdk_atom_intern_static_string (xdnd_filters[i].atom_name),
3093 xdnd_filters[i].func, NULL);
3100 gdk_drag_do_leave (GdkX11DragContext *context_x11,
3103 GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
3105 if (context->dest_window)
3107 switch (context->protocol)
3109 case GDK_DRAG_PROTO_MOTIF:
3110 motif_send_leave (context_x11, time);
3112 case GDK_DRAG_PROTO_XDND:
3113 xdnd_send_leave (context_x11);
3115 case GDK_DRAG_PROTO_ROOTWIN:
3116 case GDK_DRAG_PROTO_NONE:
3121 g_object_unref (context->dest_window);
3122 context->dest_window = NULL;
3127 _gdk_x11_window_drag_begin (GdkWindow *window,
3131 GdkDragContext *context;
3133 context = (GdkDragContext *) g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, NULL);
3135 context->is_source = TRUE;
3136 context->source_window = window;
3137 g_object_ref (window);
3139 context->targets = g_list_copy (targets);
3140 precache_target_list (context);
3142 context->actions = 0;
3144 gdk_drag_context_set_device (context, device);
3150 _gdk_x11_display_get_drag_protocol (GdkDisplay *display,
3151 GdkNativeWindow xid,
3152 GdkDragProtocol *protocol,
3157 GdkNativeWindow retval;
3159 base_precache_atoms (display);
3161 /* Check for a local drag */
3162 window = gdk_x11_window_lookup_for_display (display, xid);
3163 if (window && gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
3165 if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
3167 *protocol = GDK_DRAG_PROTO_XDND;
3169 xdnd_precache_atoms (display);
3170 GDK_NOTE (DND, g_message ("Entering local Xdnd window %#x\n", xid));
3173 else if (_gdk_x11_display_is_root_window (display, (Window) xid))
3175 *protocol = GDK_DRAG_PROTO_ROOTWIN;
3176 GDK_NOTE (DND, g_message ("Entering root window\n"));
3180 else if ((retval = xdnd_check_dest (display, xid, version)))
3182 *protocol = GDK_DRAG_PROTO_XDND;
3183 xdnd_precache_atoms (display);
3184 GDK_NOTE (DND, g_message ("Entering Xdnd window %#x\n", xid));
3187 else if ((retval = motif_check_dest (display, xid)))
3189 *protocol = GDK_DRAG_PROTO_MOTIF;
3190 GDK_NOTE (DND, g_message ("Entering motif window %#x\n", xid));
3195 /* Check if this is a root window */
3196 gboolean rootwin = FALSE;
3198 if (_gdk_x11_display_is_root_window (display, (Window) xid))
3203 GDK_NOTE (DND, g_message ("Entering root window\n"));
3204 *protocol = GDK_DRAG_PROTO_ROOTWIN;
3209 *protocol = GDK_DRAG_PROTO_NONE;
3211 return 0; /* a.k.a. None */
3214 static GdkWindowCache *
3215 drag_context_find_window_cache (GdkX11DragContext *context_x11,
3219 GdkWindowCache *cache;
3221 for (list = context_x11->window_caches; list; list = list->next)
3224 if (cache->screen == screen)
3228 cache = gdk_window_cache_new (screen);
3229 context_x11->window_caches = g_slist_prepend (context_x11->window_caches, cache);
3235 gdk_x11_drag_context_find_window (GdkDragContext *context,
3236 GdkWindow *drag_window,
3240 GdkDragProtocol *protocol)
3242 GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3243 GdkWindowCache *window_cache;
3244 GdkDisplay *display;
3246 GdkWindow *dest_window;
3248 display = GDK_WINDOW_DISPLAY (context->source_window);
3250 window_cache = drag_context_find_window_cache (context_x11, screen);
3252 dest = get_client_window_at_coords (window_cache,
3253 drag_window && GDK_WINDOW_IS_X11 (drag_window) ?
3254 GDK_WINDOW_XID (drag_window) : None,
3257 if (context_x11->dest_xid != dest)
3260 context_x11->dest_xid = dest;
3262 /* Check if new destination accepts drags, and which protocol */
3264 /* There is some ugliness here. We actually need to pass
3265 * _three_ pieces of information to drag_motion - dest_window,
3266 * protocol, and the XID of the unproxied window. The first
3267 * two are passed explicitely, the third implicitly through
3268 * protocol->dest_xid.
3270 recipient = _gdk_x11_display_get_drag_protocol (display,
3273 &context_x11->version);
3275 if (recipient != None)
3277 dest_window = gdk_x11_window_lookup_for_display (display, recipient);
3279 g_object_ref (dest_window);
3281 dest_window = gdk_x11_window_foreign_new_for_display (display, recipient);
3288 dest_window = context->dest_window;
3290 g_object_ref (dest_window);
3291 *protocol = context->protocol;
3298 gdk_x11_drag_context_drag_motion (GdkDragContext *context,
3299 GdkWindow *dest_window,
3300 GdkDragProtocol protocol,
3303 GdkDragAction suggested_action,
3304 GdkDragAction possible_actions,
3307 GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3309 context_x11->old_actions = context->actions;
3310 context->actions = possible_actions;
3312 if (context_x11->old_actions != possible_actions)
3313 context_x11->xdnd_actions_set = FALSE;
3315 if (protocol == GDK_DRAG_PROTO_XDND && context_x11->version == 0)
3317 /* This ugly hack is necessary since GTK+ doesn't know about
3318 * the XDND protocol version, and in particular doesn't know
3319 * that gdk_drag_find_window_for_screen() has the side-effect
3320 * of setting context_x11->version, and therefore sometimes call
3321 * gdk_drag_motion() without a prior call to
3322 * gdk_drag_find_window_for_screen(). This happens, e.g.
3323 * when GTK+ is proxying DND events to embedded windows.
3327 GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
3329 xdnd_check_dest (display,
3330 GDK_WINDOW_XID (dest_window),
3331 &context_x11->version);
3335 /* When we have a Xdnd target, make sure our XdndActionList
3336 * matches the current actions;
3338 if (protocol == GDK_DRAG_PROTO_XDND && !context_x11->xdnd_actions_set)
3342 if (gdk_window_get_window_type (dest_window) == GDK_WINDOW_FOREIGN)
3343 xdnd_set_actions (context_x11);
3344 else if (context->dest_window == dest_window)
3346 GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
3347 GdkDragContext *dest_context;
3349 dest_context = gdk_drag_context_find (display, FALSE,
3350 GDK_WINDOW_XID (context->source_window),
3351 GDK_WINDOW_XID (dest_window));
3355 dest_context->actions = context->actions;
3356 GDK_X11_DRAG_CONTEXT (dest_context)->xdnd_have_actions = TRUE;
3362 if (context->dest_window != dest_window)
3364 GdkEvent *temp_event;
3366 /* Send a leave to the last destination */
3367 gdk_drag_do_leave (context_x11, time);
3368 context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
3370 /* Check if new destination accepts drags, and which protocol */
3374 context->dest_window = dest_window;
3375 context_x11->drop_xid = context_x11->dest_xid;
3376 g_object_ref (context->dest_window);
3377 context->protocol = protocol;
3381 case GDK_DRAG_PROTO_MOTIF:
3382 motif_send_enter (context_x11, time);
3385 case GDK_DRAG_PROTO_XDND:
3386 xdnd_send_enter (context_x11);
3389 case GDK_DRAG_PROTO_ROOTWIN:
3390 case GDK_DRAG_PROTO_NONE:
3394 context_x11->old_action = suggested_action;
3395 context->suggested_action = suggested_action;
3396 context_x11->old_actions = possible_actions;
3400 context->dest_window = NULL;
3401 context_x11->drop_xid = None;
3402 context->action = 0;
3405 /* Push a status event, to let the client know that
3408 temp_event = gdk_event_new (GDK_DRAG_STATUS);
3409 temp_event->dnd.window = g_object_ref (context->source_window);
3410 /* We use this to signal a synthetic status. Perhaps
3411 * we should use an extra field...
3413 temp_event->dnd.send_event = TRUE;
3415 temp_event->dnd.context = g_object_ref (context);
3416 temp_event->dnd.time = time;
3417 gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
3419 gdk_event_put (temp_event);
3420 gdk_event_free (temp_event);
3424 context_x11->old_action = context->suggested_action;
3425 context->suggested_action = suggested_action;
3428 /* Send a drag-motion event */
3430 context_x11->last_x = x_root;
3431 context_x11->last_y = y_root;
3433 if (context->dest_window)
3435 if (context_x11->drag_status == GDK_DRAG_STATUS_DRAG)
3437 switch (context->protocol)
3439 case GDK_DRAG_PROTO_MOTIF:
3440 motif_send_motion (context_x11, x_root, y_root, suggested_action, time);
3443 case GDK_DRAG_PROTO_XDND:
3444 xdnd_send_motion (context_x11, x_root, y_root, suggested_action, time);
3447 case GDK_DRAG_PROTO_ROOTWIN:
3449 GdkEvent *temp_event;
3450 /* GTK+ traditionally has used application/x-rootwin-drop,
3451 * but the XDND spec specifies x-rootwindow-drop.
3453 GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3454 GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3456 if (g_list_find (context->targets,
3457 GDK_ATOM_TO_POINTER (target1)) ||
3458 g_list_find (context->targets,
3459 GDK_ATOM_TO_POINTER (target2)))
3460 context->action = context->suggested_action;
3462 context->action = 0;
3464 temp_event = gdk_event_new (GDK_DRAG_STATUS);
3465 temp_event->dnd.window = g_object_ref (context->source_window);
3466 temp_event->dnd.send_event = FALSE;
3467 temp_event->dnd.context = g_object_ref (context);
3468 temp_event->dnd.time = time;
3469 gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
3471 gdk_event_put (temp_event);
3472 gdk_event_free (temp_event);
3475 case GDK_DRAG_PROTO_NONE:
3476 g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
3490 gdk_x11_drag_context_drag_abort (GdkDragContext *context,
3493 gdk_drag_do_leave (GDK_X11_DRAG_CONTEXT (context), time);
3497 gdk_x11_drag_context_drag_drop (GdkDragContext *context,
3500 GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3502 if (context->dest_window)
3504 switch (context->protocol)
3506 case GDK_DRAG_PROTO_MOTIF:
3507 motif_send_leave (context_x11, time);
3508 motif_send_drop (context_x11, time);
3511 case GDK_DRAG_PROTO_XDND:
3512 xdnd_send_drop (context_x11, time);
3515 case GDK_DRAG_PROTO_ROOTWIN:
3516 g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
3518 case GDK_DRAG_PROTO_NONE:
3519 g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
3527 /* Destination side */
3530 gdk_x11_drag_context_drag_status (GdkDragContext *context,
3531 GdkDragAction action,
3534 GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3536 GdkDisplay *display;
3538 display = GDK_WINDOW_DISPLAY (context->source_window);
3540 context->action = action;
3542 if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3544 gboolean need_coords = FALSE;
3546 xev.xclient.type = ClientMessage;
3547 xev.xclient.message_type =
3548 gdk_x11_get_xatom_by_name_for_display (display,
3549 "_MOTIF_DRAG_AND_DROP_MESSAGE");
3550 xev.xclient.format = 8;
3551 xev.xclient.window = GDK_WINDOW_XID (context->source_window);
3553 if (context_x11->drag_status == GDK_DRAG_STATUS_ACTION_WAIT)
3555 MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED | 0x80;
3559 if ((action != 0) != (context_x11->old_action != 0))
3563 MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_ENTER | 0x80;
3568 MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_LEAVE | 0x80;
3573 MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION | 0x80;
3578 MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3582 case GDK_ACTION_MOVE:
3583 MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_MOVE;
3585 case GDK_ACTION_COPY:
3586 MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY;
3588 case GDK_ACTION_LINK:
3589 MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_LINK;
3592 MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP;
3597 MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmDROP_SITE_VALID << 4);
3599 MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmNO_DROP_SITE << 4);
3601 MOTIF_XCLIENT_LONG (&xev, 1) = time_;
3605 MOTIF_XCLIENT_SHORT (&xev, 4) = context_x11->last_x;
3606 MOTIF_XCLIENT_SHORT (&xev, 5) = context_x11->last_y;
3609 MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3611 MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3612 MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3614 if (!_gdk_x11_display_send_xevent (display,
3615 GDK_WINDOW_XID (context->source_window),
3618 g_message ("Send event to %lx failed",
3619 GDK_WINDOW_XID (context->source_window)));
3621 else if (context->protocol == GDK_DRAG_PROTO_XDND)
3623 xev.xclient.type = ClientMessage;
3624 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus");
3625 xev.xclient.format = 32;
3626 xev.xclient.window = GDK_WINDOW_XID (context->source_window);
3628 xev.xclient.data.l[0] = GDK_WINDOW_XID (context->dest_window);
3629 xev.xclient.data.l[1] = (action != 0) ? (2 | 1) : 0;
3630 xev.xclient.data.l[2] = 0;
3631 xev.xclient.data.l[3] = 0;
3632 xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
3633 if (!xdnd_send_xevent (context_x11, context->source_window, FALSE, &xev))
3635 g_message ("Send event to %lx failed",
3636 GDK_WINDOW_XID (context->source_window)));
3639 context_x11->old_action = action;
3643 gdk_x11_drag_context_drop_reply (GdkDragContext *context,
3647 GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3649 if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3651 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
3654 xev.xclient.type = ClientMessage;
3655 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
3656 "_MOTIF_DRAG_AND_DROP_MESSAGE");
3657 xev.xclient.format = 8;
3659 MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START | 0x80;
3660 MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3662 MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY |
3663 (XmDROP_SITE_VALID << 4) |
3664 (XmDROP_NOOP << 8) |
3667 MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP |
3668 (XmNO_DROP_SITE << 4) |
3669 (XmDROP_NOOP << 8) |
3670 (XmDROP_CANCEL << 12);
3671 MOTIF_XCLIENT_SHORT (&xev, 2) = context_x11->last_x;
3672 MOTIF_XCLIENT_SHORT (&xev, 3) = context_x11->last_y;
3673 MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3674 MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3675 MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3677 _gdk_x11_display_send_xevent (display,
3678 GDK_WINDOW_XID (context->source_window),
3684 gdk_x11_drag_context_drop_finish (GdkDragContext *context,
3688 if (context->protocol == GDK_DRAG_PROTO_XDND)
3690 GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
3693 xev.xclient.type = ClientMessage;
3694 xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished");
3695 xev.xclient.format = 32;
3696 xev.xclient.window = GDK_WINDOW_XID (context->source_window);
3698 xev.xclient.data.l[0] = GDK_WINDOW_XID (context->dest_window);
3701 xev.xclient.data.l[1] = 1;
3702 xev.xclient.data.l[2] = xdnd_action_to_atom (display,
3707 xev.xclient.data.l[1] = 0;
3708 xev.xclient.data.l[2] = None;
3710 xev.xclient.data.l[3] = 0;
3711 xev.xclient.data.l[4] = 0;
3713 if (!xdnd_send_xevent (GDK_X11_DRAG_CONTEXT (context), context->source_window, FALSE, &xev))
3715 g_message ("Send event to %lx failed",
3716 GDK_WINDOW_XID (context->source_window)));
3721 _gdk_x11_window_register_dnd (GdkWindow *window)
3723 static const gulong xdnd_version = 5;
3724 MotifDragReceiverInfo info;
3725 Atom motif_drag_receiver_info_atom;
3726 GdkDisplay *display = gdk_window_get_display (window);
3728 g_return_if_fail (window != NULL);
3730 if (gdk_window_get_window_type (window) == GDK_WINDOW_OFFSCREEN)
3733 base_precache_atoms (display);
3735 if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
3738 g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
3740 /* Set Motif drag receiver information property */
3741 motif_drag_receiver_info_atom = gdk_x11_get_xatom_by_name_for_display (display,
3742 "_MOTIF_DRAG_RECEIVER_INFO");
3743 /* initialize to zero to avoid writing uninitialized data to socket */
3744 memset(&info, 0, sizeof(info));
3745 info.byte_order = local_byte_order;
3746 info.protocol_version = 0;
3747 info.protocol_style = XmDRAG_DYNAMIC;
3748 info.proxy_window = None;
3749 info.num_drop_sites = 0;
3750 info.total_size = sizeof(info);
3752 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
3753 GDK_WINDOW_XID (window),
3754 motif_drag_receiver_info_atom,
3755 motif_drag_receiver_info_atom,
3762 /* The property needs to be of type XA_ATOM, not XA_INTEGER. Blech */
3763 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
3764 GDK_WINDOW_XID (window),
3765 gdk_x11_get_xatom_by_name_for_display (display, "XdndAware"),
3766 XA_ATOM, 32, PropModeReplace,
3767 (guchar *)&xdnd_version, 1);
3771 gdk_x11_drag_context_get_selection (GdkDragContext *context)
3773 if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3774 return gdk_x11_xatom_to_atom_for_display (GDK_WINDOW_DISPLAY (context->source_window),
3775 (GDK_X11_DRAG_CONTEXT (context))->motif_selection);
3776 else if (context->protocol == GDK_DRAG_PROTO_XDND)
3777 return gdk_atom_intern_static_string ("XdndSelection");
3783 gdk_x11_drag_context_drop_status (GdkDragContext *context)
3785 return ! GDK_X11_DRAG_CONTEXT (context)->drop_failed;