]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkdnd-x11.c
313bb4fc8ac9c646904c372bf254fcfd8142d305
[~andy/gtk] / gdk / x11 / gdkdnd-x11.c
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
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/. 
25  */
26
27 #include "config.h"
28
29 #include "gdkx11dnd.h"
30 #include "gdkdndprivate.h"
31
32 #include "gdkmain.h"
33 #include "gdkinternals.h"
34 #include "gdkasync.h"
35 #include "gdkproperty.h"
36 #include "gdkprivate-x11.h"
37 #include "gdkscreen-x11.h"
38 #include "gdkdisplay-x11.h"
39
40 #include <X11/Xlib.h>
41 #include <X11/Xutil.h>
42 #include <X11/Xatom.h>
43 #include <X11/extensions/shape.h>
44 #include <X11/extensions/Xcomposite.h>
45
46 #include <string.h>
47
48 typedef enum {
49   GDK_DRAG_STATUS_DRAG,
50   GDK_DRAG_STATUS_MOTION_WAIT,
51   GDK_DRAG_STATUS_ACTION_WAIT,
52   GDK_DRAG_STATUS_DROP
53 } GtkDragStatus;
54
55 typedef struct {
56   guint32 xid;
57   gint x, y, width, height;
58   gboolean mapped;
59   gboolean shape_selected;
60   gboolean shape_valid;
61   cairo_region_t *shape;
62 } GdkCacheChild;
63
64 typedef struct {
65   GList *children;
66   GHashTable *child_hash;
67   guint old_event_mask;
68   GdkScreen *screen;
69 } GdkWindowCache;
70
71
72 struct _GdkX11DragContext
73 {
74   GdkDragContext context;
75
76   Atom motif_selection;
77   guint   ref_count;
78
79   guint16 last_x;              /* Coordinates from last event */
80   guint16 last_y;
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 */
84
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 */
92
93   guint drop_failed       : 1; /* Whether the drop was unsuccessful */
94   guint version;               /* Xdnd protocol version */
95
96   GSList *window_caches;
97 };
98
99 struct _GdkX11DragContextClass
100 {
101   GdkDragContextClass parent_class;
102 };
103
104 /* Forward declarations */
105
106 static void gdk_window_cache_destroy (GdkWindowCache *cache);
107
108 static void motif_read_target_table (GdkDisplay *display);
109
110 static GdkFilterReturn motif_dnd_filter (GdkXEvent *xev,
111                                          GdkEvent  *event,
112                                          gpointer   data);
113
114 static GdkFilterReturn xdnd_enter_filter    (GdkXEvent *xev,
115                                              GdkEvent  *event,
116                                              gpointer   data);
117 static GdkFilterReturn xdnd_leave_filter    (GdkXEvent *xev,
118                                              GdkEvent  *event,
119                                              gpointer   data);
120 static GdkFilterReturn xdnd_position_filter (GdkXEvent *xev,
121                                              GdkEvent  *event,
122                                              gpointer   data);
123 static GdkFilterReturn xdnd_status_filter   (GdkXEvent *xev,
124                                              GdkEvent  *event,
125                                              gpointer   data);
126 static GdkFilterReturn xdnd_finished_filter (GdkXEvent *xev,
127                                              GdkEvent  *event,
128                                              gpointer   data);
129 static GdkFilterReturn xdnd_drop_filter     (GdkXEvent *xev,
130                                              GdkEvent  *event,
131                                              gpointer   data);
132
133 static void   xdnd_manage_source_filter (GdkDragContext *context,
134                                          GdkWindow      *window,
135                                          gboolean        add_filter);
136
137 static GList *contexts;
138
139 static const struct {
140   const char *atom_name;
141   GdkFilterFunc func;
142 } xdnd_filters[] = {
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 },
149 };
150
151
152 G_DEFINE_TYPE (GdkX11DragContext, gdk_x11_drag_context, GDK_TYPE_DRAG_CONTEXT)
153
154 static void
155 gdk_x11_drag_context_init (GdkX11DragContext *context)
156 {
157   contexts = g_list_prepend (contexts, context);
158 }
159
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,
163                                                      GdkScreen       *screen,
164                                                      gint             x_root,
165                                                      gint             y_root,
166                                                      GdkDragProtocol *protocol);
167 static gboolean    gdk_x11_drag_context_drag_motion (GdkDragContext  *context,
168                                                      GdkWindow       *dest_window,
169                                                      GdkDragProtocol  protocol,
170                                                      gint             x_root,
171                                                      gint             y_root,
172                                                      GdkDragAction    suggested_action,
173                                                      GdkDragAction    possible_actions,
174                                                      guint32          time);
175 static void        gdk_x11_drag_context_drag_status (GdkDragContext  *context,
176                                                      GdkDragAction    action,
177                                                      guint32          time_);
178 static void        gdk_x11_drag_context_drag_abort  (GdkDragContext  *context,
179                                                      guint32          time_);
180 static void        gdk_x11_drag_context_drag_drop   (GdkDragContext  *context,
181                                                      guint32          time_);
182 static void        gdk_x11_drag_context_drop_reply  (GdkDragContext  *context,
183                                                      gboolean         accept,
184                                                      guint32          time_);
185 static void        gdk_x11_drag_context_drop_finish (GdkDragContext  *context,
186                                                      gboolean         success,
187                                                      guint32          time_);
188 static gboolean    gdk_x11_drag_context_drop_status (GdkDragContext  *context);
189 static GdkAtom     gdk_x11_drag_context_get_selection (GdkDragContext  *context);
190
191 static void
192 gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass)
193 {
194   GObjectClass *object_class = G_OBJECT_CLASS (klass);
195   GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
196
197   object_class->finalize = gdk_x11_drag_context_finalize;
198
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;
208 }
209
210 static void
211 gdk_x11_drag_context_finalize (GObject *object)
212 {
213   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (object);
214   GdkDragContext *context = GDK_DRAG_CONTEXT (object);
215
216   if (context->source_window)
217     {
218       if ((context->protocol == GDK_DRAG_PROTO_XDND) && !context->is_source)
219         xdnd_manage_source_filter (context, context->source_window, FALSE);
220     }
221
222   g_slist_free_full (context_x11->window_caches, (GDestroyNotify)gdk_window_cache_destroy);
223
224   contexts = g_list_remove (contexts, context);
225
226   G_OBJECT_CLASS (gdk_x11_drag_context_parent_class)->finalize (object);
227 }
228
229 /* Drag Contexts */
230
231 static GdkDragContext *
232 gdk_drag_context_find (GdkDisplay *display,
233                        gboolean    is_source,
234                        Window      source_xid,
235                        Window      dest_xid)
236 {
237   GList *tmp_list = contexts;
238   GdkDragContext *context;
239   GdkX11DragContext *context_x11;
240   Window context_dest_xid;
241
242   while (tmp_list)
243     {
244       context = (GdkDragContext *)tmp_list->data;
245       context_x11 = (GdkX11DragContext *)context;
246
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))
249         continue;
250
251       context_dest_xid = context->dest_window
252                             ? (context_x11->drop_xid
253                                   ? context_x11->drop_xid
254                                   : GDK_WINDOW_XID (context->dest_window))
255                             : None;
256
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)))
261         return context;
262
263       tmp_list = tmp_list->next;
264     }
265
266   return NULL;
267 }
268
269 static void
270 precache_target_list (GdkDragContext *context)
271 {
272   if (context->targets)
273     {
274       GPtrArray *targets = g_ptr_array_new ();
275       GList *tmp_list;
276       int i;
277
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)));
280
281       _gdk_x11_precache_atoms (GDK_WINDOW_DISPLAY (context->source_window),
282                                (const gchar **)targets->pdata,
283                                targets->len);
284
285       for (i =0; i < targets->len; i++)
286         g_free (targets->pdata[i]);
287
288       g_ptr_array_free (targets, TRUE);
289     }
290 }
291
292 /* Utility functions */
293
294 static void
295 free_cache_child (GdkCacheChild *child,
296                   GdkDisplay    *display)
297 {
298   if (child->shape)
299     cairo_region_destroy (child->shape);
300
301   if (child->shape_selected && display)
302     {
303       GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
304
305       XShapeSelectInput (display_x11->xdisplay, child->xid, 0);
306     }
307
308   g_free (child);
309 }
310
311 static void
312 gdk_window_cache_add (GdkWindowCache *cache,
313                       guint32         xid,
314                       gint            x,
315                       gint            y,
316                       gint            width,
317                       gint            height,
318                       gboolean        mapped)
319 {
320   GdkCacheChild *child = g_new (GdkCacheChild, 1);
321
322   child->xid = xid;
323   child->x = x;
324   child->y = y;
325   child->width = width;
326   child->height = height;
327   child->mapped = mapped;
328   child->shape_selected = FALSE;
329   child->shape_valid = FALSE;
330   child->shape = NULL;
331
332   cache->children = g_list_prepend (cache->children, child);
333   g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
334                        cache->children);
335 }
336
337 static GdkFilterReturn
338 gdk_window_cache_shape_filter (GdkXEvent *xev,
339                                GdkEvent  *event,
340                                gpointer   data)
341 {
342   XEvent *xevent = (XEvent *)xev;
343   GdkWindowCache *cache = data;
344
345   GdkX11Display *display = GDK_X11_DISPLAY (gdk_screen_get_display (cache->screen));
346
347   if (display->have_shapes &&
348       xevent->type == display->shape_event_base + ShapeNotify)
349     {
350       XShapeEvent *xse = (XShapeEvent*)xevent;
351       GList *node;
352
353       node = g_hash_table_lookup (cache->child_hash,
354                                   GUINT_TO_POINTER (xse->window));
355       if (node)
356         {
357           GdkCacheChild *child = node->data;
358           child->shape_valid = FALSE;
359           if (child->shape)
360             {
361               cairo_region_destroy (child->shape);
362               child->shape = NULL;
363             }
364         }
365
366       return GDK_FILTER_REMOVE;
367     }
368
369   return GDK_FILTER_CONTINUE;
370 }
371
372 static GdkFilterReturn
373 gdk_window_cache_filter (GdkXEvent *xev,
374                          GdkEvent  *event,
375                          gpointer   data)
376 {
377   XEvent *xevent = (XEvent *)xev;
378   GdkWindowCache *cache = data;
379
380   switch (xevent->type)
381     {
382     case CirculateNotify:
383       break;
384     case ConfigureNotify:
385       {
386         XConfigureEvent *xce = &xevent->xconfigure;
387         GList *node;
388
389         node = g_hash_table_lookup (cache->child_hash,
390                                     GUINT_TO_POINTER (xce->window));
391         if (node)
392           {
393             GdkCacheChild *child = node->data;
394             child->x = xce->x;
395             child->y = xce->y;
396             child->width = xce->width;
397             child->height = xce->height;
398             if (xce->above == None && (node->next))
399               {
400                 GList *last = g_list_last (cache->children);
401                 cache->children = g_list_remove_link (cache->children, node);
402                 last->next = node;
403                 node->next = NULL;
404                 node->prev = last;
405               }
406             else
407               {
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)
411                   {
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;
415                     if (node->prev)
416                       node->prev->next = node;
417                     else
418                       cache->children = node;
419                     node->next = above_node;
420                     above_node->prev = node;
421                   }
422               }
423           }
424         break;
425       }
426     case CreateNotify:
427       {
428         XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
429
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,
434                                 FALSE);
435         break;
436       }
437     case DestroyNotify:
438       {
439         XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
440         GList *node;
441
442         node = g_hash_table_lookup (cache->child_hash,
443                                     GUINT_TO_POINTER (xdwe->window));
444         if (node)
445           {
446             GdkCacheChild *child = node->data;
447
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);
454           }
455         break;
456       }
457     case MapNotify:
458       {
459         XMapEvent *xme = &xevent->xmap;
460         GList *node;
461
462         node = g_hash_table_lookup (cache->child_hash,
463                                     GUINT_TO_POINTER (xme->window));
464         if (node)
465           {
466             GdkCacheChild *child = node->data;
467             child->mapped = TRUE;
468           }
469         break;
470       }
471     case ReparentNotify:
472       break;
473     case UnmapNotify:
474       {
475         XMapEvent *xume = &xevent->xmap;
476         GList *node;
477
478         node = g_hash_table_lookup (cache->child_hash,
479                                     GUINT_TO_POINTER (xume->window));
480         if (node)
481           {
482             GdkCacheChild *child = node->data;
483             child->mapped = FALSE;
484           }
485         break;
486       }
487     default:
488       return GDK_FILTER_CONTINUE;
489     }
490   return GDK_FILTER_REMOVE;
491 }
492
493 static GdkWindowCache *
494 gdk_window_cache_new (GdkScreen *screen)
495 {
496   XWindowAttributes xwa;
497   Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
498   GdkWindow *root_window = gdk_screen_get_root_window (screen);
499   GdkChildInfoX11 *children;
500   guint nchildren, i;
501   Window cow;
502
503   GdkWindowCache *result = g_new (GdkWindowCache, 1);
504
505   result->children = NULL;
506   result->child_hash = g_hash_table_new (g_direct_hash, NULL);
507   result->screen = screen;
508
509   XGetWindowAttributes (xdisplay, GDK_WINDOW_XID (root_window), &xwa);
510   result->old_event_mask = xwa.your_event_mask;
511
512   if (G_UNLIKELY (!GDK_X11_DISPLAY (GDK_X11_SCREEN (screen)->display)->trusted_client))
513     {
514       GList *toplevel_windows, *list;
515       GdkWindow *window;
516       gint x, y, width, height;
517
518       toplevel_windows = gdk_screen_get_toplevel_windows (screen);
519       for (list = toplevel_windows; list; list = list->next)
520         {
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),
524                                 x, y, width, height,
525                                 gdk_window_is_visible (window));
526         }
527       g_list_free (toplevel_windows);
528       return result;
529     }
530
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);
535
536   if (!_gdk_x11_get_window_child_info (gdk_screen_get_display (screen),
537                                        GDK_WINDOW_XID (root_window),
538                                        FALSE, NULL,
539                                        &children, &nchildren))
540     return result;
541
542   for (i = 0; i < nchildren ; i++)
543     {
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);
547     }
548
549   g_free (children);
550
551 #ifdef HAVE_XCOMPOSITE
552   /*
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).
558    */
559   if (gdk_screen_is_composited (screen))
560     {
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));
564     }
565 #endif
566
567   return result;
568 }
569
570 static void
571 gdk_window_cache_destroy (GdkWindowCache *cache)
572 {
573   GdkWindow *root_window = gdk_screen_get_root_window (cache->screen);
574   GdkDisplay *display;
575
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);
581
582   display = gdk_screen_get_display (cache->screen);
583
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);
587
588   g_list_free (cache->children);
589   g_hash_table_destroy (cache->child_hash);
590
591   g_free (cache);
592 }
593
594 static gboolean
595 is_pointer_within_shape (GdkDisplay    *display,
596                          GdkCacheChild *child,
597                          gint           x_pos,
598                          gint           y_pos)
599 {
600   if (!child->shape_selected)
601     {
602       GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
603
604       XShapeSelectInput (display_x11->xdisplay, child->xid, ShapeNotifyMask);
605       child->shape_selected = TRUE;
606     }
607   if (!child->shape_valid)
608     {
609       GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
610       cairo_region_t *input_shape;
611
612       child->shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
613                                                  child->xid, ShapeBounding);
614 #ifdef ShapeInput
615       input_shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
616                                                 child->xid, ShapeInput);
617       if (child->shape && input_shape)
618         {
619           cairo_region_intersect (child->shape, input_shape);
620           cairo_region_destroy (input_shape);
621         }
622       else if (input_shape)
623         {
624           child->shape = input_shape;
625         }
626 #endif
627
628       child->shape_valid = TRUE;
629     }
630
631   return child->shape == NULL ||
632          cairo_region_contains_point (child->shape, x_pos, y_pos);
633 }
634
635 static Window
636 get_client_window_at_coords_recurse (GdkDisplay *display,
637                                      Window      win,
638                                      gboolean    is_toplevel,
639                                      gint        x,
640                                      gint        y)
641 {
642   GdkChildInfoX11 *children;
643   unsigned int nchildren;
644   int i;
645   gboolean found_child = FALSE;
646   GdkChildInfoX11 child = { 0, };
647   gboolean has_wm_state = FALSE;
648
649   if (!_gdk_x11_get_window_child_info (display, win, TRUE,
650                                        is_toplevel? &has_wm_state : NULL,
651                                        &children, &nchildren))
652     return None;
653
654   if (has_wm_state)
655     {
656       g_free (children);
657
658       return win;
659     }
660
661   for (i = nchildren - 1; (i >= 0) && !found_child; i--)
662     {
663       GdkChildInfoX11 *cur_child = &children[i];
664
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))
668         {
669           x -= cur_child->x;
670           y -= cur_child->y;
671           child = *cur_child;
672           found_child = TRUE;
673         }
674     }
675
676   g_free (children);
677
678   if (found_child)
679     {
680       if (child.has_wm_state)
681         return child.window;
682       else
683         return get_client_window_at_coords_recurse (display, child.window, FALSE, x, y);
684     }
685   else
686     return None;
687 }
688
689 static Window
690 get_client_window_at_coords (GdkWindowCache *cache,
691                              Window          ignore,
692                              gint            x_root,
693                              gint            y_root)
694 {
695   GList *tmp_list;
696   Window retval = None;
697   GdkDisplay *display;
698
699   display = gdk_screen_get_display (cache->screen);
700
701   gdk_x11_display_error_trap_push (display);
702
703   tmp_list = cache->children;
704
705   while (tmp_list && !retval)
706     {
707       GdkCacheChild *child = tmp_list->data;
708
709       if ((child->xid != ignore) && (child->mapped))
710         {
711           if ((x_root >= child->x) && (x_root < child->x + child->width) &&
712               (y_root >= child->y) && (y_root < child->y + child->height))
713             {
714               if (!is_pointer_within_shape (display, child,
715                                             x_root - child->x,
716                                             y_root - child->y))
717                 {
718                   tmp_list = tmp_list->next;
719                   continue;
720                 }
721
722               retval = get_client_window_at_coords_recurse (display,
723                   child->xid, TRUE,
724                   x_root - child->x,
725                   y_root - child->y);
726               if (!retval)
727                 retval = child->xid;
728             }
729         }
730       tmp_list = tmp_list->next;
731     }
732
733   gdk_x11_display_error_trap_pop_ignored (display);
734
735   if (retval)
736     return retval;
737   else
738     return GDK_WINDOW_XID (gdk_screen_get_root_window (cache->screen));
739 }
740
741 /*************************************************************
742  ***************************** MOTIF *************************
743  *************************************************************/
744
745 /* values used in the message type for Motif DND */
746 enum {
747     XmTOP_LEVEL_ENTER,
748     XmTOP_LEVEL_LEAVE,
749     XmDRAG_MOTION,
750     XmDROP_SITE_ENTER,
751     XmDROP_SITE_LEAVE,
752     XmDROP_START,
753     XmDROP_FINISH,
754     XmDRAG_DROP_FINISH,
755     XmOPERATION_CHANGED
756 };
757
758 /* Values used to specify type of protocol to use */
759 enum {
760     XmDRAG_NONE,
761     XmDRAG_DROP_ONLY,
762     XmDRAG_PREFER_PREREGISTER,
763     XmDRAG_PREREGISTER,
764     XmDRAG_PREFER_DYNAMIC,
765     XmDRAG_DYNAMIC,
766     XmDRAG_PREFER_RECEIVER
767 };
768
769 /* Operation codes */
770 enum {
771   XmDROP_NOOP,
772   XmDROP_MOVE = 0x01,
773   XmDROP_COPY = 0x02,
774   XmDROP_LINK = 0x04
775 };
776
777 /* Drop site status */
778 enum {
779   XmNO_DROP_SITE = 0x01,
780   XmDROP_SITE_INVALID = 0x02,
781   XmDROP_SITE_VALID = 0x03
782 };
783
784 /* completion status */
785 enum {
786   XmDROP,
787   XmDROP_HELP,
788   XmDROP_CANCEL,
789   XmDROP_INTERRUPT
790 };
791
792 /* Byte swapping routines. The motif specification leaves it
793  * up to us to save a few bytes in the client messages
794  */
795 static gchar local_byte_order = '\0';
796
797 #ifdef G_ENABLE_DEBUG
798 static void
799 print_target_list (GList *targets)
800 {
801   while (targets)
802     {
803       gchar *name = gdk_atom_name (GDK_POINTER_TO_ATOM (targets->data));
804       g_message ("\t%s", name);
805       g_free (name);
806       targets = targets->next;
807     }
808 }
809 #endif /* G_ENABLE_DEBUG */
810
811 static void
812 init_byte_order (void)
813 {
814   guint32 myint = 0x01020304;
815   local_byte_order = (*(gchar *)&myint == 1) ? 'B' : 'l';
816 }
817
818 static guint16
819 card16_to_host (guint16 x, gchar byte_order)
820 {
821   if (byte_order == local_byte_order)
822     return x;
823   else
824     return (x << 8) | (x >> 8);
825 }
826
827 static guint32
828 card32_to_host (guint32 x, gchar byte_order)
829 {
830   if (byte_order == local_byte_order)
831     return x;
832   else
833     return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
834 }
835
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.
840  */
841
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]
848
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))
854
855 /***** Dest side ***********/
856
857 /* Property placed on source windows */
858 typedef struct _MotifDragInitiatorInfo
859 {
860   guint8 byte_order;
861   guint8 protocol_version;
862   guint16 targets_index;
863   guint32 selection_atom;
864 } MotifDragInitiatorInfo;
865
866 /* Header for target table on the drag window */
867 typedef struct _MotifTargetTableHeader
868 {
869   guchar byte_order;
870   guchar protocol_version;
871   guint16 n_lists;
872   guint32 total_size;
873 } MotifTargetTableHeader;
874
875 /* Property placed on target windows */
876 typedef struct _MotifDragReceiverInfo
877 {
878   guint8 byte_order;
879   guint8 protocol_version;
880   guint8 protocol_style;
881   guint8 pad;
882   guint32 proxy_window;
883   guint16 num_drop_sites;
884   guint16 padding;
885   guint32 total_size;
886 } MotifDragReceiverInfo;
887
888 /* Target table handling */
889
890 static GdkFilterReturn
891 motif_drag_window_filter (GdkXEvent *xevent,
892                           GdkEvent  *event,
893                           gpointer data)
894 {
895   XEvent *xev = (XEvent *)xevent;
896   GdkDisplay *display = GDK_WINDOW_DISPLAY (event->any.window);
897   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
898
899   switch (xev->xany.type)
900     {
901     case DestroyNotify:
902       display_x11->motif_drag_window = None;
903       display_x11->motif_drag_gdk_window = NULL;
904       break;
905     case PropertyNotify:
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);
909       break;
910     }
911   return GDK_FILTER_REMOVE;
912 }
913
914 static Window
915 motif_lookup_drag_window (GdkDisplay *display,
916                           Display    *lookup_xdisplay)
917 {
918   Window retval = None;
919   gulong bytes_after, nitems;
920   Atom type;
921   gint format;
922   guchar *data;
923
924   XGetWindowProperty (lookup_xdisplay, RootWindow (lookup_xdisplay, 0),
925                       gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_WINDOW"),
926                       0, 1, FALSE,
927                       XA_WINDOW, &type, &format, &nitems, &bytes_after,
928                       &data);
929
930   if ((format == 32) && (nitems == 1) && (bytes_after == 0))
931     {
932       retval = *(Window *)data;
933       GDK_NOTE (DND,
934                 g_message ("Found drag window %#lx\n", GDK_X11_DISPLAY (display)->motif_drag_window));
935     }
936
937   if (type != None)
938     XFree (data);
939
940   return retval;
941 }
942
943 /* Finds the window where global Motif drag information is stored.
944  * If it doesn't exist and 'create' is TRUE, create one.
945  */
946 static Window
947 motif_find_drag_window (GdkDisplay *display,
948                         gboolean    create)
949 {
950   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
951
952   if (!display_x11->motif_drag_window)
953     {
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);
956
957       if (!display_x11->motif_drag_window && create)
958         {
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);
964
965           XGrabServer (persistant_xdisplay);
966
967           display_x11->motif_drag_window = motif_lookup_drag_window (display, persistant_xdisplay);
968
969           if (!display_x11->motif_drag_window)
970             {
971               attr.override_redirect = True;
972               attr.event_mask = PropertyChangeMask;
973
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);
980
981               GDK_NOTE (DND,
982                         g_message ("Created drag window %#lx\n", display_x11->motif_drag_window));
983
984               XChangeProperty (persistant_xdisplay,
985                                RootWindow (persistant_xdisplay, 0),
986                                motif_drag_window_atom, XA_WINDOW,
987                                32, PropModeReplace,
988                                (guchar *)&motif_drag_window_atom, 1);
989             }
990           XUngrabServer (persistant_xdisplay);
991           XCloseDisplay (persistant_xdisplay);
992         }
993
994       /* There is a miniscule race condition here if the drag window
995        * gets destroyed exactly now.
996        */
997       if (display_x11->motif_drag_window)
998         {
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,
1003                                  NULL);
1004         }
1005     }
1006
1007   return display_x11->motif_drag_window;
1008 }
1009
1010 static void
1011 motif_read_target_table (GdkDisplay *display)
1012 {
1013   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1014   gulong bytes_after, nitems;
1015   Atom type;
1016   gint format;
1017   gint i, j;
1018
1019   Atom motif_drag_targets_atom = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_TARGETS");
1020
1021   if (display_x11->motif_target_lists)
1022     {
1023       for (i=0; i<display_x11->motif_n_target_lists; i++)
1024         g_list_free (display_x11->motif_target_lists[i]);
1025
1026       g_free (display_x11->motif_target_lists);
1027       display_x11->motif_target_lists = NULL;
1028       display_x11->motif_n_target_lists = 0;
1029     }
1030
1031   if (motif_find_drag_window (display, FALSE))
1032     {
1033       guchar *data;
1034       MotifTargetTableHeader *header = NULL;
1035       guchar *target_bytes = NULL;
1036       guchar *p;
1037       gboolean success = FALSE;
1038
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,
1046                           &data);
1047
1048       if (gdk_x11_display_error_trap_pop (display) ||
1049           (format != 8) || (nitems < sizeof (MotifTargetTableHeader)))
1050         goto error;
1051
1052       header = (MotifTargetTableHeader *)data;
1053
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);
1056
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,
1063                           FALSE,
1064                           motif_drag_targets_atom, &type, &format, &nitems,
1065                           &bytes_after, &target_bytes);
1066
1067       if (gdk_x11_display_error_trap_pop (display) ||
1068           (format != 8) || (bytes_after != 0) ||
1069           (nitems != header->total_size - sizeof(MotifTargetTableHeader)))
1070           goto error;
1071
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);
1074
1075       p = target_bytes;
1076       for (i=0; i<header->n_lists; i++)
1077         {
1078           gint n_targets;
1079           guint32 *targets;
1080
1081           if (p + sizeof(guint16) - target_bytes > nitems)
1082             goto error;
1083
1084           n_targets = card16_to_host (*(gushort *)p, header->byte_order);
1085
1086           /* We need to make a copy of the targets, since it may
1087            * be unaligned
1088            */
1089           targets = g_new (guint32, n_targets);
1090           memcpy (targets, p + sizeof(guint16), sizeof(guint32) * n_targets);
1091
1092           p +=  sizeof(guint16) + n_targets * sizeof(guint32);
1093           if (p - target_bytes > nitems)
1094             goto error;
1095
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)));
1101           g_free (targets);
1102           display_x11->motif_target_lists[i] = g_list_reverse (display_x11->motif_target_lists[i]);
1103         }
1104
1105       success = TRUE;
1106
1107     error:
1108       if (header)
1109         XFree (header);
1110
1111       if (target_bytes)
1112         XFree (target_bytes);
1113
1114       if (!success)
1115         {
1116           if (display_x11->motif_target_lists)
1117             {
1118               g_free (display_x11->motif_target_lists);
1119               display_x11->motif_target_lists = NULL;
1120               display_x11->motif_n_target_lists = 0;
1121             }
1122           g_warning ("Error reading Motif target table\n");
1123         }
1124     }
1125 }
1126
1127 static gint
1128 targets_sort_func (gconstpointer a, gconstpointer b)
1129 {
1130   return (GPOINTER_TO_UINT (a) < GPOINTER_TO_UINT (b)) ?
1131     -1 : ((GPOINTER_TO_UINT (a) > GPOINTER_TO_UINT (b)) ? 1 : 0);
1132 }
1133
1134 /* Check if given (sorted) list is in the targets table */
1135 static gint
1136 motif_target_table_check (GdkDisplay *display,
1137                           GList      *sorted)
1138 {
1139   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1140   GList *tmp_list1, *tmp_list2;
1141   gint i;
1142
1143   for (i=0; i<display_x11->motif_n_target_lists; i++)
1144     {
1145       tmp_list1 = display_x11->motif_target_lists[i];
1146       tmp_list2 = sorted;
1147
1148       while (tmp_list1 && tmp_list2)
1149         {
1150           if (tmp_list1->data != tmp_list2->data)
1151             break;
1152
1153           tmp_list1 = tmp_list1->next;
1154           tmp_list2 = tmp_list2->next;
1155         }
1156       if (!tmp_list1 && !tmp_list2)     /* Found it */
1157         return i;
1158     }
1159
1160   return -1;
1161 }
1162
1163 static gint
1164 motif_add_to_target_table (GdkDisplay *display,
1165                            GList      *targets) /* targets is list of GdkAtom */
1166 {
1167   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1168   GList *sorted = NULL;
1169   gint index = -1;
1170   gint i;
1171   GList *tmp_list;
1172
1173   /* make a sorted copy of the list */
1174   while (targets)
1175     {
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;
1179     }
1180
1181   /* First check if it is there already */
1182
1183   if (display_x11->motif_target_lists)
1184     index = motif_target_table_check (display, sorted);
1185
1186   /* We need to grab the server while doing this, to ensure
1187    * atomiticity. Ugh
1188    */
1189
1190   if (index < 0)
1191     {
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
1194        * grab the server.
1195        */
1196       motif_find_drag_window (display, TRUE);
1197
1198       gdk_x11_display_grab (display);
1199       motif_read_target_table (display);
1200
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);
1204
1205       if (index < 0)
1206         {
1207           guint32 total_size = 0;
1208           guchar *data;
1209           guchar *p;
1210           guint16 *p16;
1211           MotifTargetTableHeader *header;
1212
1213           if (!display_x11->motif_target_lists)
1214             {
1215               display_x11->motif_target_lists = g_new (GList *, 1);
1216               display_x11->motif_n_target_lists = 1;
1217             }
1218           else
1219             {
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);
1223             }
1224           display_x11->motif_target_lists[display_x11->motif_n_target_lists - 1] = sorted;
1225           sorted = NULL;
1226           index = display_x11->motif_n_target_lists - 1;
1227
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]);
1231
1232           data = g_malloc (total_size);
1233
1234           header = (MotifTargetTableHeader *)data;
1235           p = data + sizeof(MotifTargetTableHeader);
1236
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;
1241
1242           for (i = 0; i < display_x11->motif_n_target_lists ; i++)
1243             {
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;
1247
1248               tmp_list = display_x11->motif_target_lists[i];
1249               while (tmp_list)
1250                 {
1251                   *p32 = GPOINTER_TO_UINT (tmp_list->data);
1252
1253                   tmp_list = tmp_list->next;
1254                   p32++;
1255                 }
1256
1257               p16 = (guint16 *)p;
1258               p += sizeof(guint16);
1259
1260               memcpy (p, targets, n_targets * sizeof(guint32));
1261
1262               *p16 = n_targets;
1263               p += sizeof(guint32) * n_targets;
1264               g_free (targets);
1265             }
1266
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"),
1271                            8, PropModeReplace,
1272                            data, total_size);
1273         }
1274       gdk_x11_display_ungrab (display);
1275     }
1276
1277   g_list_free (sorted);
1278   return index;
1279 }
1280
1281 /* Translate flags */
1282
1283 static void
1284 motif_dnd_translate_flags (GdkX11DragContext *context_x11,
1285                            guint16            flags)
1286 {
1287   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1288   guint recommended_op = flags & 0x000f;
1289   guint possible_ops = (flags & 0x0f0) >> 4;
1290
1291   switch (recommended_op)
1292     {
1293     case XmDROP_MOVE:
1294       context->suggested_action = GDK_ACTION_MOVE;
1295       break;
1296     case XmDROP_COPY:
1297       context->suggested_action = GDK_ACTION_COPY;
1298       break;
1299     case XmDROP_LINK:
1300       context->suggested_action = GDK_ACTION_LINK;
1301       break;
1302     default:
1303       context->suggested_action = GDK_ACTION_COPY;
1304       break;
1305     }
1306
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;
1314 }
1315
1316 static guint16
1317 motif_dnd_get_flags (GdkDragContext *context)
1318 {
1319   guint16 flags = 0;
1320
1321   switch (context->suggested_action)
1322     {
1323     case GDK_ACTION_MOVE:
1324       flags = XmDROP_MOVE;
1325       break;
1326     case GDK_ACTION_COPY:
1327       flags = XmDROP_COPY;
1328       break;
1329     case GDK_ACTION_LINK:
1330       flags = XmDROP_LINK;
1331       break;
1332     default:
1333       flags = XmDROP_NOOP;
1334       break;
1335     }
1336
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;
1343
1344   return flags;
1345 }
1346
1347 /* Source Side */
1348
1349 static void
1350 motif_set_targets (GdkX11DragContext *context_x11)
1351 {
1352   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1353   MotifDragInitiatorInfo info;
1354   gint i;
1355   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1356
1357   info.byte_order = local_byte_order;
1358   info.protocol_version = 0;
1359   info.targets_index = motif_add_to_target_table (display, context->targets);
1360
1361   for (i = 0; ; i++)
1362     {
1363       gchar buf[20];
1364       g_snprintf (buf, 20, "_GDK_SELECTION_%d", i);
1365
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))
1368         break;
1369     }
1370
1371   info.selection_atom = context_x11->motif_selection;
1372
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"),
1377                    8, PropModeReplace,
1378                    (guchar *)&info, sizeof (info));
1379
1380   context_x11->motif_targets_set = 1;
1381 }
1382
1383 static guint32
1384 motif_check_dest (GdkDisplay *display,
1385                   Window      win)
1386 {
1387   gboolean retval = FALSE;
1388   guchar *data;
1389   MotifDragReceiverInfo *info;
1390   Atom type = None;
1391   int format;
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");
1394
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,
1400                       &data);
1401
1402   if (gdk_x11_display_error_trap_pop (display) == 0)
1403     {
1404       if (type != None)
1405         {
1406           info = (MotifDragReceiverInfo *)data;
1407
1408           if ((format == 8) && (nitems == sizeof(*info)))
1409             {
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)))
1414                 retval = TRUE;
1415             }
1416           else
1417             {
1418               GDK_NOTE (DND,
1419                         g_warning ("Invalid Motif drag receiver property on window %ld\n", win));
1420             }
1421
1422           XFree (info);
1423         }
1424     }
1425
1426   return retval ? win : None;
1427 }
1428
1429 static void
1430 motif_send_enter (GdkX11DragContext *context_x11,
1431                   guint32            time)
1432 {
1433   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1434   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1435   XEvent xev;
1436
1437   if (!G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client))
1438     return; /* Motif Dnd requires getting properties on the root window */
1439
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);
1444
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);
1450
1451   if (!context_x11->motif_targets_set)
1452     motif_set_targets (context_x11);
1453
1454   MOTIF_XCLIENT_LONG (&xev, 3) = context_x11->motif_selection;
1455   MOTIF_XCLIENT_LONG (&xev, 4) = 0;
1456
1457   if (!_gdk_x11_display_send_xevent (display,
1458                                      GDK_WINDOW_XID (context->dest_window),
1459                                      FALSE, 0, &xev))
1460     GDK_NOTE (DND,
1461               g_message ("Send event to %lx failed",
1462               GDK_WINDOW_XID (context->dest_window)));
1463 }
1464
1465 static void
1466 motif_send_leave (GdkX11DragContext *context_x11,
1467                   guint32            time)
1468 {
1469   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1470   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1471   XEvent xev;
1472
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);
1477
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;
1485
1486   if (!_gdk_x11_display_send_xevent (display,
1487                                      GDK_WINDOW_XID (context->dest_window),
1488                                      FALSE, 0, &xev))
1489     GDK_NOTE (DND,
1490               g_message ("Send event to %lx failed",
1491                          GDK_WINDOW_XID (context->dest_window)));
1492 }
1493
1494 static gboolean
1495 motif_send_motion (GdkX11DragContext *context_x11,
1496                     gint              x_root,
1497                     gint              y_root,
1498                     GdkDragAction     action,
1499                     guint32           time)
1500 {
1501   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1502   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1503   gboolean retval;
1504   XEvent xev;
1505
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);
1510
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;
1516
1517   if ((context->suggested_action != context_x11->old_action) ||
1518       (context->actions != context_x11->old_actions))
1519     {
1520       MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED;
1521
1522       /* context_x11->drag_status = GDK_DRAG_STATUS_ACTION_WAIT; */
1523       retval = TRUE;
1524     }
1525   else
1526     {
1527       MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION;
1528
1529       MOTIF_XCLIENT_SHORT (&xev, 4) = x_root;
1530       MOTIF_XCLIENT_SHORT (&xev, 5) = y_root;
1531
1532       context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1533       retval = FALSE;
1534     }
1535
1536   if (!_gdk_x11_display_send_xevent (display,
1537                                      GDK_WINDOW_XID (context->dest_window),
1538                                      FALSE, 0, &xev))
1539     GDK_NOTE (DND,
1540               g_message ("Send event to %lx failed",
1541                          GDK_WINDOW_XID (context->dest_window)));
1542
1543   return retval;
1544 }
1545
1546 static void
1547 motif_send_drop (GdkX11DragContext *context_x11,
1548                  guint32            time)
1549 {
1550   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1551   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1552   XEvent xev;
1553
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);
1558
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;
1563
1564   MOTIF_XCLIENT_SHORT (&xev, 4) = context_x11->last_x;
1565   MOTIF_XCLIENT_SHORT (&xev, 5) = context_x11->last_y;
1566
1567   MOTIF_XCLIENT_LONG (&xev, 3)  = context_x11->motif_selection;
1568   MOTIF_XCLIENT_LONG (&xev, 4)  = GDK_WINDOW_XID (context->source_window);
1569
1570   if (!_gdk_x11_display_send_xevent (display,
1571                                      GDK_WINDOW_XID (context->dest_window),
1572                                      FALSE, 0, &xev))
1573     GDK_NOTE (DND,
1574               g_message ("Send event to %lx failed",
1575                          GDK_WINDOW_XID (context->dest_window)));
1576 }
1577
1578 /* Target Side */
1579
1580 static gboolean
1581 motif_read_initiator_info (GdkDisplay *display,
1582                            Window      source_window,
1583                            Atom        atom,
1584                            GList     **targets,
1585                            Atom       *selection)
1586 {
1587   GList *tmp_list;
1588   Atom type;
1589   gint format;
1590   gulong nitems;
1591   gulong bytes_after;
1592   guchar *data;
1593   MotifDragInitiatorInfo *initiator_info;
1594
1595   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1596
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,
1602                       &data);
1603
1604   if (gdk_x11_display_error_trap_pop (display) ||
1605       (format != 8) || (nitems != sizeof (MotifDragInitiatorInfo)) ||
1606       (bytes_after != 0))
1607     {
1608       g_warning ("Error reading initiator info\n");
1609       return FALSE;
1610     }
1611
1612   initiator_info = (MotifDragInitiatorInfo *)data;
1613
1614   motif_read_target_table (display);
1615
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);
1620
1621   if (initiator_info->targets_index >= display_x11->motif_n_target_lists)
1622     {
1623       g_warning ("Invalid target index in TOP_LEVEL_ENTER MESSAGE");
1624       XFree (initiator_info);
1625       return FALSE;
1626     }
1627
1628   tmp_list = g_list_last (display_x11->motif_target_lists[initiator_info->targets_index]);
1629
1630   *targets = NULL;
1631   while (tmp_list)
1632     {
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;
1636     }
1637
1638 #ifdef G_ENABLE_DEBUG
1639   if (_gdk_debug_flags & GDK_DEBUG_DND)
1640     print_target_list (*targets);
1641 #endif /* G_ENABLE_DEBUG */
1642
1643   *selection = initiator_info->selection_atom;
1644
1645   XFree (initiator_info);
1646
1647   return TRUE;
1648 }
1649
1650 static GdkDragContext *
1651 motif_drag_context_new (GdkWindow *dest_window,
1652                         guint32    timestamp,
1653                         guint32    source_window,
1654                         guint32    atom)
1655 {
1656   GdkX11DragContext *context_x11;
1657   GdkDragContext *context;
1658   GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
1659   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1660
1661   /* FIXME, current_dest_drag really shouldn't be NULL'd
1662    * if we error below.
1663    */
1664   if (display_x11->current_dest_drag != NULL)
1665     {
1666       if (timestamp >= display_x11->current_dest_drag->start_time)
1667         {
1668           g_object_unref (display_x11->current_dest_drag);
1669           display_x11->current_dest_drag = NULL;
1670         }
1671       else
1672         return NULL;
1673     }
1674
1675   context_x11 = g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, NULL);
1676   context = GDK_DRAG_CONTEXT (context_x11);
1677
1678   context->protocol = GDK_DRAG_PROTO_MOTIF;
1679   context->is_source = FALSE;
1680
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);
1684   else
1685     {
1686       context->source_window = gdk_x11_window_foreign_new_for_display (display, source_window);
1687       if (!context->source_window)
1688         {
1689           g_object_unref (context_x11);
1690           return NULL;
1691         }
1692     }
1693
1694   context->dest_window = dest_window;
1695   g_object_ref (dest_window);
1696   context->start_time = timestamp;
1697
1698   if (!motif_read_initiator_info (GDK_WINDOW_DISPLAY (dest_window),
1699                                   source_window,
1700                                   atom,
1701                                   &context->targets,
1702                                   &context_x11->motif_selection))
1703     {
1704       g_object_unref (context_x11);
1705       return NULL;
1706     }
1707
1708   return context;
1709 }
1710
1711 /*
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.
1716  */
1717
1718 static GdkFilterReturn
1719 motif_top_level_enter (GdkEvent *event,
1720                        guint16   flags,
1721                        guint32   timestamp,
1722                        guint32   source_window,
1723                        guint32   atom)
1724 {
1725   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1726   GdkDragContext *new_context;
1727
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));
1730
1731   new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1732   if (!new_context)
1733     return GDK_FILTER_REMOVE;
1734
1735   event->dnd.type = GDK_DRAG_ENTER;
1736   event->dnd.context = new_context;
1737   g_object_ref (new_context);
1738
1739   display_x11->current_dest_drag = new_context;
1740
1741   return GDK_FILTER_TRANSLATE;
1742 }
1743
1744 static GdkFilterReturn
1745 motif_top_level_leave (GdkEvent *event,
1746                        guint16   flags,
1747                        guint32   timestamp)
1748 {
1749   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1750
1751   GDK_NOTE(DND, g_message ("Motif DND top level leave: flags: %#4x time: %d",
1752                            flags, timestamp));
1753
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))
1757     {
1758       event->dnd.type = GDK_DRAG_LEAVE;
1759       /* Pass ownership of context to the event */
1760       event->dnd.context = display_x11->current_dest_drag;
1761
1762       display_x11->current_dest_drag = NULL;
1763
1764       return GDK_FILTER_TRANSLATE;
1765     }
1766   else
1767     return GDK_FILTER_REMOVE;
1768 }
1769
1770 static GdkFilterReturn
1771 motif_motion (GdkEvent *event,
1772               guint16   flags,
1773               guint32   timestamp,
1774               gint16    x_root,
1775               gint16    y_root)
1776 {
1777   GdkX11DragContext *context_x11;
1778   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1779
1780   GDK_NOTE(DND, g_message ("Motif DND motion: flags: %#4x time: %d (%d, %d)",
1781                            flags, timestamp, x_root, y_root));
1782
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))
1786     {
1787       context_x11 = GDK_X11_DRAG_CONTEXT (display_x11->current_dest_drag);
1788
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);
1792
1793       event->dnd.time = timestamp;
1794
1795       motif_dnd_translate_flags (context_x11, flags);
1796
1797       event->dnd.x_root = x_root;
1798       event->dnd.y_root = y_root;
1799
1800       context_x11->last_x = x_root;
1801       context_x11->last_y = y_root;
1802
1803       context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1804
1805       return GDK_FILTER_TRANSLATE;
1806     }
1807
1808   return GDK_FILTER_REMOVE;
1809 }
1810
1811 static GdkFilterReturn
1812 motif_operation_changed (GdkEvent *event,
1813                          guint16   flags,
1814                          guint32   timestamp)
1815 {
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",
1819                            flags, timestamp));
1820
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))
1824     {
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);
1829
1830       event->dnd.time = timestamp;
1831       context_x11 = GDK_X11_DRAG_CONTEXT (display_x11->current_dest_drag);
1832
1833       motif_dnd_translate_flags (context_x11, flags);
1834
1835       event->dnd.x_root = context_x11->last_x;
1836       event->dnd.y_root = context_x11->last_y;
1837
1838       context_x11->drag_status = GDK_DRAG_STATUS_ACTION_WAIT;
1839
1840       return GDK_FILTER_TRANSLATE;
1841     }
1842
1843   return GDK_FILTER_REMOVE;
1844 }
1845
1846 static GdkFilterReturn
1847 motif_drop_start (GdkEvent *event,
1848                   guint16   flags,
1849                   guint32   timestamp,
1850                   guint32   source_window,
1851                   guint32   atom,
1852                   gint16    x_root,
1853                   gint16    y_root)
1854 {
1855   GdkDragContext *new_context;
1856   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1857
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));
1860
1861   new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1862   if (!new_context)
1863     return GDK_FILTER_REMOVE;
1864
1865   motif_dnd_translate_flags (GDK_X11_DRAG_CONTEXT (new_context), flags);
1866
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;
1872
1873   gdk_x11_window_set_user_time (event->any.window, timestamp);
1874
1875   g_object_ref (new_context);
1876   display_x11->current_dest_drag = new_context;
1877
1878   return GDK_FILTER_TRANSLATE;
1879 }
1880
1881 static GdkFilterReturn
1882 motif_drag_status (GdkEvent *event,
1883                    guint16   flags,
1884                    guint32   timestamp)
1885 {
1886   GdkDragContext *context;
1887   GdkDisplay *display;
1888
1889   GDK_NOTE (DND,
1890             g_message ("Motif status message: flags %x", flags));
1891
1892   display = gdk_window_get_display (event->any.window);
1893   if (!display)
1894     return GDK_FILTER_REMOVE;
1895
1896   context = gdk_drag_context_find (display, TRUE, GDK_WINDOW_XID (event->any.window), None);
1897
1898   if (context)
1899     {
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;
1904
1905       event->dnd.type = GDK_DRAG_STATUS;
1906       event->dnd.send_event = FALSE;
1907       event->dnd.context = context;
1908       g_object_ref (context);
1909
1910       event->dnd.time = timestamp;
1911
1912       if ((flags & 0x00f0) >> 4 == XmDROP_SITE_VALID)
1913         {
1914           switch (flags & 0x000f)
1915             {
1916             case XmDROP_NOOP:
1917               context->action = 0;
1918               break;
1919             case XmDROP_MOVE:
1920                 context->action = GDK_ACTION_MOVE;
1921                 break;
1922             case XmDROP_COPY:
1923               context->action = GDK_ACTION_COPY;
1924               break;
1925             case XmDROP_LINK:
1926               context->action = GDK_ACTION_LINK;
1927               break;
1928             }
1929         }
1930       else
1931         context->action = 0;
1932
1933       return GDK_FILTER_TRANSLATE;
1934     }
1935   return GDK_FILTER_REMOVE;
1936 }
1937
1938 static GdkFilterReturn
1939 motif_dnd_filter (GdkXEvent *xev,
1940                   GdkEvent  *event,
1941                   gpointer data)
1942 {
1943   XEvent *xevent = (XEvent *)xev;
1944
1945   guint8 reason;
1946   guint16 flags;
1947   guint32 timestamp;
1948   guint32 source_window;
1949   Atom atom;
1950   gint16 x_root, y_root;
1951   gboolean is_reply;
1952
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 */
1956
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);
1961
1962   is_reply = ((reason & 0x80) != 0);
1963
1964   switch (reason & 0x7f)
1965     {
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);
1972
1973     case XmDRAG_MOTION:
1974       x_root = MOTIF_UNPACK_SHORT (xevent, 4);
1975       y_root = MOTIF_UNPACK_SHORT (xevent, 5);
1976
1977       if (!is_reply)
1978         return motif_motion (event, flags, timestamp, x_root, y_root);
1979       else
1980         return motif_drag_status (event, flags, timestamp);
1981
1982     case XmDROP_SITE_ENTER:
1983       return motif_drag_status (event, flags, timestamp);
1984
1985     case XmDROP_SITE_LEAVE:
1986       return motif_drag_status (event,
1987                                 XmNO_DROP_SITE << 8 | XmDROP_NOOP,
1988                                 timestamp);
1989     case XmDROP_START:
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);
1994
1995       if (!is_reply)
1996         return motif_drop_start (event, flags, timestamp, source_window, atom, x_root, y_root);
1997
1998      break;
1999     case XmOPERATION_CHANGED:
2000       if (!is_reply)
2001         return motif_operation_changed (event, flags, timestamp);
2002       else
2003         return motif_drag_status (event, flags, timestamp);
2004
2005       break;
2006       /* To the best of my knowledge, these next two messages are
2007        * not part of the protocol, though they are defined in
2008        * the header files.
2009        */
2010     case XmDROP_FINISH:
2011     case XmDRAG_DROP_FINISH:
2012       break;
2013     }
2014
2015   return GDK_FILTER_REMOVE;
2016 }
2017
2018 /*************************************************************
2019  ***************************** XDND **************************
2020  *************************************************************/
2021
2022 /* Utility functions */
2023
2024 static struct {
2025   const gchar *name;
2026   GdkAtom atom;
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 },
2034   };
2035
2036 static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
2037 static gboolean xdnd_actions_initialized = FALSE;
2038
2039 static void
2040 xdnd_initialize_actions (void)
2041 {
2042   gint i;
2043
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);
2047 }
2048
2049 static GdkDragAction
2050 xdnd_action_from_atom (GdkDisplay *display,
2051                        Atom        xatom)
2052 {
2053   GdkAtom atom;
2054   gint i;
2055
2056   if (xatom == None)
2057     return 0;
2058
2059   atom = gdk_x11_xatom_to_atom_for_display (display, xatom);
2060
2061   if (!xdnd_actions_initialized)
2062     xdnd_initialize_actions();
2063
2064   for (i = 0; i < xdnd_n_actions; i++)
2065     if (atom == xdnd_actions_table[i].atom)
2066       return xdnd_actions_table[i].action;
2067
2068   return 0;
2069 }
2070
2071 static Atom
2072 xdnd_action_to_atom (GdkDisplay    *display,
2073                      GdkDragAction  action)
2074 {
2075   gint i;
2076
2077   if (!xdnd_actions_initialized)
2078     xdnd_initialize_actions();
2079
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);
2083
2084   return None;
2085 }
2086
2087 /* Source side */
2088
2089 static GdkFilterReturn
2090 xdnd_status_filter (GdkXEvent *xev,
2091                     GdkEvent  *event,
2092                     gpointer   data)
2093 {
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;
2100
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 */
2104
2105   GDK_NOTE (DND,
2106             g_message ("XdndStatus: dest_window: %#x  action: %ld",
2107                        dest_window, action));
2108
2109   display = gdk_window_get_display (event->any.window);
2110   context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
2111
2112   if (context)
2113     {
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;
2117
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);
2123
2124       event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2125       if (!(action != 0) != !(flags & 1))
2126         {
2127           GDK_NOTE (DND,
2128                     g_warning ("Received status event with flags not corresponding to action!\n"));
2129           action = 0;
2130         }
2131
2132       context->action = xdnd_action_from_atom (display, action);
2133
2134       return GDK_FILTER_TRANSLATE;
2135     }
2136
2137   return GDK_FILTER_REMOVE;
2138 }
2139
2140 static GdkFilterReturn
2141 xdnd_finished_filter (GdkXEvent *xev,
2142                       GdkEvent  *event,
2143                       gpointer   data)
2144 {
2145   GdkDisplay *display;
2146   XEvent *xevent = (XEvent *)xev;
2147   guint32 dest_window = xevent->xclient.data.l[0];
2148   GdkDragContext *context;
2149   GdkX11DragContext *context_x11;
2150
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 */
2154
2155   GDK_NOTE (DND,
2156             g_message ("XdndFinished: dest_window: %#x", dest_window));
2157
2158   display = gdk_window_get_display (event->any.window);
2159   context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
2160
2161   if (context)
2162     {
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;
2166
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);
2171
2172       event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2173
2174       return GDK_FILTER_TRANSLATE;
2175     }
2176
2177   return GDK_FILTER_REMOVE;
2178 }
2179
2180 static void
2181 xdnd_set_targets (GdkX11DragContext *context_x11)
2182 {
2183   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2184   Atom *atomlist;
2185   GList *tmp_list = context->targets;
2186   gint i;
2187   gint n_atoms = g_list_length (context->targets);
2188   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2189
2190   atomlist = g_new (Atom, n_atoms);
2191   i = 0;
2192   while (tmp_list)
2193     {
2194       atomlist[i] = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (tmp_list->data));
2195       tmp_list = tmp_list->next;
2196       i++;
2197     }
2198
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);
2204
2205   g_free (atomlist);
2206
2207   context_x11->xdnd_targets_set = 1;
2208 }
2209
2210 static void
2211 xdnd_set_actions (GdkX11DragContext *context_x11)
2212 {
2213   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2214   Atom *atomlist;
2215   gint i;
2216   gint n_atoms;
2217   guint actions;
2218   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2219
2220   if (!xdnd_actions_initialized)
2221     xdnd_initialize_actions();
2222
2223   actions = context->actions;
2224   n_atoms = 0;
2225   for (i = 0; i < xdnd_n_actions; i++)
2226     {
2227       if (actions & xdnd_actions_table[i].action)
2228         {
2229           actions &= ~xdnd_actions_table[i].action;
2230           n_atoms++;
2231         }
2232     }
2233
2234   atomlist = g_new (Atom, n_atoms);
2235
2236   actions = context->actions;
2237   n_atoms = 0;
2238   for (i = 0; i < xdnd_n_actions; i++)
2239     {
2240       if (actions & xdnd_actions_table[i].action)
2241         {
2242           actions &= ~xdnd_actions_table[i].action;
2243           atomlist[n_atoms] = gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
2244           n_atoms++;
2245         }
2246     }
2247
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);
2253
2254   g_free (atomlist);
2255
2256   context_x11->xdnd_actions_set = TRUE;
2257   context_x11->xdnd_actions = context->actions;
2258 }
2259
2260 static void
2261 send_client_message_async_cb (Window   window,
2262                               gboolean success,
2263                               gpointer data)
2264 {
2265   GdkDragContext *context = data;
2266   GDK_NOTE (DND,
2267             g_message ("Got async callback for #%lx, success = %d",
2268                        window, success));
2269
2270   /* On failure, we immediately continue with the protocol
2271    * so we don't end up blocking for a timeout
2272    */
2273   if (!success &&
2274       context->dest_window &&
2275       window == GDK_WINDOW_XID (context->dest_window))
2276     {
2277       GdkEvent *temp_event;
2278       GdkX11DragContext *context_x11 = data;
2279
2280       g_object_unref (context->dest_window);
2281       context->dest_window = NULL;
2282       context->action = 0;
2283
2284       context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
2285
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));
2292
2293       gdk_event_put (temp_event);
2294
2295       gdk_event_free (temp_event);
2296     }
2297
2298   g_object_unref (context);
2299 }
2300
2301
2302 static GdkDisplay *
2303 gdk_drag_context_get_display (GdkDragContext *context)
2304 {
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);
2309
2310   g_assert_not_reached ();
2311   return NULL;
2312 }
2313
2314 static void
2315 send_client_message_async (GdkDragContext      *context,
2316                            Window               window,
2317                            gboolean             propagate,
2318                            glong                event_mask,
2319                            XClientMessageEvent *event_send)
2320 {
2321   GdkDisplay *display = gdk_drag_context_get_display (context);
2322
2323   g_object_ref (context);
2324
2325   _gdk_x11_send_client_message_async (display, window,
2326                                       propagate, event_mask, event_send,
2327                                       send_client_message_async_cb, context);
2328 }
2329
2330 static gboolean
2331 xdnd_send_xevent (GdkX11DragContext *context_x11,
2332                   GdkWindow         *window,
2333                   gboolean           propagate,
2334                   XEvent            *event_send)
2335 {
2336   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2337   GdkDisplay *display = gdk_drag_context_get_display (context);
2338   Window xwindow;
2339   glong event_mask;
2340
2341   g_assert (event_send->xany.type == ClientMessage);
2342
2343   /* We short-circuit messages to ourselves */
2344   if (gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
2345     {
2346       gint i;
2347
2348       for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
2349         {
2350           if (gdk_x11_get_xatom_by_name_for_display (display, xdnd_filters[i].atom_name) ==
2351               event_send->xclient.message_type)
2352             {
2353               GdkEvent *temp_event;
2354
2355               temp_event = gdk_event_new (GDK_NOTHING);
2356               temp_event->any.window = g_object_ref (window);
2357
2358               if ((*xdnd_filters[i].func) (event_send, temp_event, NULL) == GDK_FILTER_TRANSLATE)
2359                 {
2360                   gdk_event_put (temp_event);
2361                   gdk_event_free (temp_event);
2362                 }
2363
2364               return TRUE;
2365             }
2366         }
2367     }
2368
2369   xwindow = GDK_WINDOW_XID (window);
2370
2371   if (_gdk_x11_display_is_root_window (display, xwindow))
2372     event_mask = ButtonPressMask;
2373   else
2374     event_mask = 0;
2375
2376   send_client_message_async (context, xwindow, propagate, event_mask,
2377                              &event_send->xclient);
2378
2379   return TRUE;
2380 }
2381
2382 static void
2383 xdnd_send_enter (GdkX11DragContext *context_x11)
2384 {
2385   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2386   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->dest_window);
2387   XEvent xev;
2388
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;
2400
2401   GDK_NOTE(DND,
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)
2405     {
2406       if (!context_x11->xdnd_targets_set)
2407         xdnd_set_targets (context_x11);
2408       xev.xclient.data.l[1] |= 1;
2409     }
2410   else
2411     {
2412       GList *tmp_list = context->targets;
2413       gint i = 2;
2414
2415       while (tmp_list)
2416         {
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;
2420           i++;
2421         }
2422     }
2423
2424   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2425     {
2426       GDK_NOTE (DND,
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;
2431     }
2432 }
2433
2434 static void
2435 xdnd_send_leave (GdkX11DragContext *context_x11)
2436 {
2437   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2438   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2439   XEvent xev;
2440
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;
2452
2453   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2454     {
2455       GDK_NOTE (DND,
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;
2460     }
2461 }
2462
2463 static void
2464 xdnd_send_drop (GdkX11DragContext *context_x11,
2465                 guint32            time)
2466 {
2467   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2468   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2469   XEvent xev;
2470
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;
2482
2483   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2484     {
2485       GDK_NOTE (DND,
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;
2490     }
2491 }
2492
2493 static void
2494 xdnd_send_motion (GdkX11DragContext *context_x11,
2495                   gint               x_root,
2496                   gint               y_root,
2497                   GdkDragAction      action,
2498                   guint32            time)
2499 {
2500   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2501   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2502   XEvent xev;
2503
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);
2515
2516   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2517     {
2518       GDK_NOTE (DND,
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;
2523     }
2524   context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
2525 }
2526
2527 static guint32
2528 xdnd_check_dest (GdkDisplay *display,
2529                  Window      win,
2530                  guint      *xdnd_version)
2531 {
2532   gboolean retval = FALSE;
2533   Atom type = None;
2534   int format;
2535   unsigned long nitems, after;
2536   guchar *data;
2537   Atom *version;
2538   Window *proxy_data;
2539   Window proxy;
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");
2542
2543   proxy = None;
2544
2545   gdk_x11_display_error_trap_push (display);
2546   if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win,
2547                           xdnd_proxy_atom, 0,
2548                           1, False, AnyPropertyType,
2549                           &type, &format, &nitems, &after,
2550                           &data) == Success)
2551     {
2552       if (type != None)
2553         {
2554           proxy_data = (Window *)data;
2555
2556           if ((format == 32) && (nitems == 1))
2557             {
2558               proxy = *proxy_data;
2559             }
2560           else
2561             GDK_NOTE (DND,
2562                       g_warning ("Invalid XdndProxy "
2563                                  "property on window %ld\n", win));
2564
2565           XFree (proxy_data);
2566         }
2567
2568       if ((XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), proxy ? proxy : win,
2569                                xdnd_aware_atom, 0,
2570                                1, False, AnyPropertyType,
2571                                &type, &format, &nitems, &after,
2572                                &data) == Success) &&
2573           type != None)
2574         {
2575           version = (Atom *)data;
2576
2577           if ((format == 32) && (nitems == 1))
2578             {
2579               if (*version >= 3)
2580                 retval = TRUE;
2581               if (xdnd_version)
2582                 *xdnd_version = *version;
2583             }
2584           else
2585             GDK_NOTE (DND,
2586                       g_warning ("Invalid XdndAware "
2587                                  "property on window %ld\n", win));
2588
2589           XFree (version);
2590         }
2591     }
2592
2593   gdk_x11_display_error_trap_pop_ignored (display);
2594
2595   return retval ? (proxy ? proxy : win) : None;
2596 }
2597
2598 /* Target side */
2599
2600 static void
2601 xdnd_read_actions (GdkX11DragContext *context_x11)
2602 {
2603   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2604   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2605   Atom type;
2606   int format;
2607   gulong nitems, after;
2608   guchar *data;
2609   Atom *atoms;
2610   gint i;
2611
2612   context_x11->xdnd_have_actions = FALSE;
2613
2614   if (gdk_window_get_window_type (context->source_window) == GDK_WINDOW_FOREIGN)
2615     {
2616       /* Get the XdndActionList, if set */
2617
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"),
2622                               0, 65536,
2623                               False, XA_ATOM, &type, &format, &nitems,
2624                               &after, &data) == Success &&
2625           type == XA_ATOM)
2626         {
2627           atoms = (Atom *)data;
2628
2629           context->actions = 0;
2630
2631           for (i = 0; i < nitems; i++)
2632             context->actions |= xdnd_action_from_atom (display, atoms[i]);
2633
2634           context_x11->xdnd_have_actions = TRUE;
2635
2636 #ifdef G_ENABLE_DEBUG
2637           if (_gdk_debug_flags & GDK_DEBUG_DND)
2638             {
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 ");
2648
2649               g_message("Xdnd actions = %s", action_str->str);
2650               g_string_free (action_str, TRUE);
2651             }
2652 #endif /* G_ENABLE_DEBUG */
2653
2654         }
2655
2656       if (data)
2657         XFree (data);
2658
2659       gdk_x11_display_error_trap_pop_ignored (display);
2660     }
2661   else
2662     {
2663       /* Local drag
2664        */
2665       GdkDragContext *source_context;
2666
2667       source_context = gdk_drag_context_find (display, TRUE,
2668                                               GDK_WINDOW_XID (context->source_window),
2669                                               GDK_WINDOW_XID (context->dest_window));
2670
2671       if (source_context)
2672         {
2673           context->actions = source_context->actions;
2674           context_x11->xdnd_have_actions = TRUE;
2675         }
2676     }
2677 }
2678
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.
2684  */
2685 static GdkFilterReturn
2686 xdnd_source_window_filter (GdkXEvent *xev,
2687                            GdkEvent  *event,
2688                            gpointer   cb_data)
2689 {
2690   XEvent *xevent = (XEvent *)xev;
2691   GdkX11DragContext *context_x11 = cb_data;
2692   GdkDisplay *display = GDK_WINDOW_DISPLAY(event->any.window);
2693
2694   if ((xevent->xany.type == PropertyNotify) &&
2695       (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList")))
2696     {
2697       xdnd_read_actions (context_x11);
2698
2699       return GDK_FILTER_REMOVE;
2700     }
2701
2702   return GDK_FILTER_CONTINUE;
2703 }
2704
2705 static void
2706 xdnd_manage_source_filter (GdkDragContext *context,
2707                            GdkWindow      *window,
2708                            gboolean        add_filter)
2709 {
2710   if (!GDK_WINDOW_DESTROYED (window) &&
2711       gdk_window_get_window_type (window) == GDK_WINDOW_FOREIGN)
2712     {
2713       gdk_x11_display_error_trap_push (GDK_WINDOW_DISPLAY (window));
2714
2715       if (add_filter)
2716         {
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);
2721         }
2722       else
2723         {
2724           gdk_window_remove_filter (window,
2725                                     xdnd_source_window_filter,
2726                                     context);
2727           /* Should we remove the GDK_PROPERTY_NOTIFY mask?
2728            * but we might want it for other reasons. (Like
2729            * INCR selection transactions).
2730            */
2731         }
2732
2733       gdk_x11_display_error_trap_pop_ignored (GDK_WINDOW_DISPLAY (window));
2734     }
2735 }
2736
2737 static void
2738 base_precache_atoms (GdkDisplay *display)
2739 {
2740   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
2741
2742   if (!display_x11->base_dnd_atoms_precached)
2743     {
2744       static const char *const precache_atoms[] = {
2745         "ENLIGHTENMENT_DESKTOP",
2746         "WM_STATE",
2747         "XdndAware",
2748         "XdndProxy",
2749         "_MOTIF_DRAG_RECEIVER_INFO"
2750       };
2751
2752       _gdk_x11_precache_atoms (display,
2753                                precache_atoms, G_N_ELEMENTS (precache_atoms));
2754
2755       display_x11->base_dnd_atoms_precached = TRUE;
2756     }
2757 }
2758
2759 static void
2760 xdnd_precache_atoms (GdkDisplay *display)
2761 {
2762   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
2763
2764   if (!display_x11->xdnd_atoms_precached)
2765     {
2766       static const gchar *const precache_atoms[] = {
2767         "XdndActionAsk",
2768         "XdndActionCopy",
2769         "XdndActionLink",
2770         "XdndActionList",
2771         "XdndActionMove",
2772         "XdndActionPrivate",
2773         "XdndDrop",
2774         "XdndEnter",
2775         "XdndFinished",
2776         "XdndLeave",
2777         "XdndPosition",
2778         "XdndSelection",
2779         "XdndStatus",
2780         "XdndTypeList"
2781       };
2782
2783       _gdk_x11_precache_atoms (display,
2784                                precache_atoms, G_N_ELEMENTS (precache_atoms));
2785
2786       display_x11->xdnd_atoms_precached = TRUE;
2787     }
2788 }
2789
2790 static GdkFilterReturn
2791 xdnd_enter_filter (GdkXEvent *xev,
2792                    GdkEvent  *event,
2793                    gpointer   cb_data)
2794 {
2795   GdkDeviceManager *device_manager;
2796   GdkDisplay *display;
2797   GdkX11Display *display_x11;
2798   XEvent *xevent = (XEvent *)xev;
2799   GdkDragContext *context;
2800   GdkX11DragContext *context_x11;
2801   gint i;
2802   Atom type;
2803   int format;
2804   gulong nitems, after;
2805   guchar *data;
2806   Atom *atoms;
2807   guint32 source_window;
2808   gboolean get_types;
2809   gint version;
2810
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 */
2814
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;
2818
2819   display = GDK_WINDOW_DISPLAY (event->any.window);
2820   display_x11 = GDK_X11_DISPLAY (display);
2821
2822   xdnd_precache_atoms (display);
2823
2824   GDK_NOTE (DND,
2825             g_message ("XdndEnter: source_window: %#x, version: %#x",
2826                        source_window, version));
2827
2828   if (version < 3)
2829     {
2830       /* Old source ignore */
2831       GDK_NOTE (DND, g_message ("Ignored old XdndEnter message"));
2832       return GDK_FILTER_REMOVE;
2833     }
2834
2835   if (display_x11->current_dest_drag != NULL)
2836     {
2837       g_object_unref (display_x11->current_dest_drag);
2838       display_x11->current_dest_drag = NULL;
2839     }
2840
2841   context_x11 = (GdkX11DragContext *)g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, NULL);
2842   context = (GdkDragContext *)context_x11;
2843
2844   context->protocol = GDK_DRAG_PROTO_XDND;
2845   context_x11->version = version;
2846
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));
2850
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);
2854   else
2855     {
2856       context->source_window = gdk_x11_window_foreign_new_for_display (display, source_window);
2857       if (!context->source_window)
2858         {
2859           g_object_unref (context);
2860           return GDK_FILTER_REMOVE;
2861         }
2862     }
2863   context->dest_window = event->any.window;
2864   g_object_ref (context->dest_window);
2865
2866   context->targets = NULL;
2867   if (get_types)
2868     {
2869       gdk_x11_display_error_trap_push (display);
2870       XGetWindowProperty (GDK_WINDOW_XDISPLAY (event->any.window),
2871                           source_window,
2872                           gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
2873                           0, 65536,
2874                           False, XA_ATOM, &type, &format, &nitems,
2875                           &after, &data);
2876
2877       if (gdk_x11_display_error_trap_pop (display) || (format != 32) || (type != XA_ATOM))
2878         {
2879           g_object_unref (context);
2880
2881           if (data)
2882             XFree (data);
2883
2884           return GDK_FILTER_REMOVE;
2885         }
2886
2887       atoms = (Atom *)data;
2888
2889       for (i = 0; i < nitems; i++)
2890         context->targets =
2891           g_list_append (context->targets,
2892                          GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display,
2893                                                                                  atoms[i])));
2894
2895       XFree (atoms);
2896     }
2897   else
2898     {
2899       for (i = 0; i < 3; i++)
2900         if (xevent->xclient.data.l[2 + i])
2901           context->targets =
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])));
2905     }
2906
2907 #ifdef G_ENABLE_DEBUG
2908   if (_gdk_debug_flags & GDK_DEBUG_DND)
2909     print_target_list (context->targets);
2910 #endif /* G_ENABLE_DEBUG */
2911
2912   xdnd_manage_source_filter (context, context->source_window, TRUE);
2913   xdnd_read_actions (context_x11);
2914
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);
2919
2920   display_x11->current_dest_drag = context;
2921
2922   return GDK_FILTER_TRANSLATE;
2923 }
2924
2925 static GdkFilterReturn
2926 xdnd_leave_filter (GdkXEvent *xev,
2927                    GdkEvent  *event,
2928                    gpointer   data)
2929 {
2930   XEvent *xevent = (XEvent *)xev;
2931   guint32 source_window = xevent->xclient.data.l[0];
2932   GdkDisplay *display;
2933   GdkX11Display *display_x11;
2934
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 */
2938
2939   GDK_NOTE (DND,
2940             g_message ("XdndLeave: source_window: %#x",
2941                        source_window));
2942
2943   display = GDK_WINDOW_DISPLAY (event->any.window);
2944   display_x11 = GDK_X11_DISPLAY (display);
2945
2946   xdnd_precache_atoms (display);
2947
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))
2951     {
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));
2956
2957       display_x11->current_dest_drag = NULL;
2958
2959       return GDK_FILTER_TRANSLATE;
2960     }
2961   else
2962     return GDK_FILTER_REMOVE;
2963 }
2964
2965 static GdkFilterReturn
2966 xdnd_position_filter (GdkXEvent *xev,
2967                       GdkEvent  *event,
2968                       gpointer   data)
2969 {
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;
2980
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 */
2984
2985   GDK_NOTE (DND,
2986             g_message ("XdndPosition: source_window: %#x position: (%d, %d)  time: %d  action: %ld",
2987                        source_window, x_root, y_root, time, action));
2988
2989   display = GDK_WINDOW_DISPLAY (event->any.window);
2990   display_x11 = GDK_X11_DISPLAY (display);
2991
2992   xdnd_precache_atoms (display);
2993
2994   context = display_x11->current_dest_drag;
2995
2996   if ((context != NULL) &&
2997       (context->protocol == GDK_DRAG_PROTO_XDND) &&
2998       (GDK_WINDOW_XID (context->source_window) == source_window))
2999     {
3000       context_x11 = GDK_X11_DRAG_CONTEXT (context);
3001
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);
3006
3007       event->dnd.time = time;
3008
3009       context->suggested_action = xdnd_action_from_atom (display, action);
3010
3011       if (!context_x11->xdnd_have_actions)
3012         context->actions = context->suggested_action;
3013
3014       event->dnd.x_root = x_root;
3015       event->dnd.y_root = y_root;
3016
3017       context_x11->last_x = x_root;
3018       context_x11->last_y = y_root;
3019
3020       return GDK_FILTER_TRANSLATE;
3021     }
3022
3023   return GDK_FILTER_REMOVE;
3024 }
3025
3026 static GdkFilterReturn
3027 xdnd_drop_filter (GdkXEvent *xev,
3028                   GdkEvent  *event,
3029                   gpointer   data)
3030 {
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;
3038
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 */
3042
3043   GDK_NOTE (DND,
3044             g_message ("XdndDrop: source_window: %#x  time: %d",
3045                        source_window, time));
3046
3047   display = GDK_WINDOW_DISPLAY (event->any.window);
3048   display_x11 = GDK_X11_DISPLAY (display);
3049
3050   xdnd_precache_atoms (display);
3051
3052   context = display_x11->current_dest_drag;
3053
3054   if ((context != NULL) &&
3055       (context->protocol == GDK_DRAG_PROTO_XDND) &&
3056       (GDK_WINDOW_XID (context->source_window) == source_window))
3057     {
3058       context_x11 = GDK_X11_DRAG_CONTEXT (context);
3059       event->dnd.type = GDK_DROP_START;
3060
3061       event->dnd.context = context;
3062       gdk_event_set_device (event, gdk_drag_context_get_device (context));
3063       g_object_ref (context);
3064
3065       event->dnd.time = time;
3066       event->dnd.x_root = context_x11->last_x;
3067       event->dnd.y_root = context_x11->last_y;
3068
3069       gdk_x11_window_set_user_time (event->any.window, time);
3070
3071       return GDK_FILTER_TRANSLATE;
3072     }
3073
3074   return GDK_FILTER_REMOVE;
3075 }
3076
3077 void
3078 _gdk_x11_display_init_dnd (GdkDisplay *display)
3079 {
3080   int i;
3081   init_byte_order ();
3082
3083   gdk_display_add_client_message_filter (
3084         display,
3085         gdk_atom_intern_static_string ("_MOTIF_DRAG_AND_DROP_MESSAGE"),
3086         motif_dnd_filter, NULL);
3087
3088   for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
3089     {
3090       gdk_display_add_client_message_filter (
3091         display,
3092         gdk_atom_intern_static_string (xdnd_filters[i].atom_name),
3093         xdnd_filters[i].func, NULL);
3094     }
3095 }
3096
3097 /* Source side */
3098
3099 static void
3100 gdk_drag_do_leave (GdkX11DragContext *context_x11,
3101                    guint32            time)
3102 {
3103   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
3104
3105   if (context->dest_window)
3106     {
3107       switch (context->protocol)
3108         {
3109         case GDK_DRAG_PROTO_MOTIF:
3110           motif_send_leave (context_x11, time);
3111           break;
3112         case GDK_DRAG_PROTO_XDND:
3113           xdnd_send_leave (context_x11);
3114           break;
3115         case GDK_DRAG_PROTO_ROOTWIN:
3116         case GDK_DRAG_PROTO_NONE:
3117         default:
3118           break;
3119         }
3120
3121       g_object_unref (context->dest_window);
3122       context->dest_window = NULL;
3123     }
3124 }
3125
3126 GdkDragContext *
3127 _gdk_x11_window_drag_begin (GdkWindow *window,
3128                             GdkDevice *device,
3129                             GList     *targets)
3130 {
3131   GdkDragContext *context;
3132
3133   context = (GdkDragContext *) g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, NULL);
3134
3135   context->is_source = TRUE;
3136   context->source_window = window;
3137   g_object_ref (window);
3138
3139   context->targets = g_list_copy (targets);
3140   precache_target_list (context);
3141
3142   context->actions = 0;
3143
3144   gdk_drag_context_set_device (context, device);
3145
3146   return context;
3147 }
3148
3149 GdkNativeWindow
3150 _gdk_x11_display_get_drag_protocol (GdkDisplay      *display,
3151                                     GdkNativeWindow  xid,
3152                                     GdkDragProtocol *protocol,
3153                                     guint           *version)
3154
3155 {
3156   GdkWindow *window;
3157   GdkNativeWindow retval;
3158
3159   base_precache_atoms (display);
3160
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)
3164     {
3165       if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
3166         {
3167           *protocol = GDK_DRAG_PROTO_XDND;
3168           *version = 5;
3169           xdnd_precache_atoms (display);
3170           GDK_NOTE (DND, g_message ("Entering local Xdnd window %#x\n", xid));
3171           return xid;
3172         }
3173       else if (_gdk_x11_display_is_root_window (display, (Window) xid))
3174         {
3175           *protocol = GDK_DRAG_PROTO_ROOTWIN;
3176           GDK_NOTE (DND, g_message ("Entering root window\n"));
3177           return xid;
3178         }
3179     }
3180   else if ((retval = xdnd_check_dest (display, xid, version)))
3181     {
3182       *protocol = GDK_DRAG_PROTO_XDND;
3183       xdnd_precache_atoms (display);
3184       GDK_NOTE (DND, g_message ("Entering Xdnd window %#x\n", xid));
3185       return retval;
3186     }
3187   else if ((retval = motif_check_dest (display, xid)))
3188     {
3189       *protocol = GDK_DRAG_PROTO_MOTIF;
3190       GDK_NOTE (DND, g_message ("Entering motif window %#x\n", xid));
3191       return retval;
3192     }
3193   else
3194     {
3195       /* Check if this is a root window */
3196       gboolean rootwin = FALSE;
3197
3198       if (_gdk_x11_display_is_root_window (display, (Window) xid))
3199         rootwin = TRUE;
3200
3201       if (rootwin)
3202         {
3203           GDK_NOTE (DND, g_message ("Entering root window\n"));
3204           *protocol = GDK_DRAG_PROTO_ROOTWIN;
3205           return xid;
3206         }
3207     }
3208
3209   *protocol = GDK_DRAG_PROTO_NONE;
3210
3211   return 0; /* a.k.a. None */
3212 }
3213
3214 static GdkWindowCache *
3215 drag_context_find_window_cache (GdkX11DragContext *context_x11,
3216                                 GdkScreen         *screen)
3217 {
3218   GSList *list;
3219   GdkWindowCache *cache;
3220
3221   for (list = context_x11->window_caches; list; list = list->next)
3222     {
3223       cache = list->data;
3224       if (cache->screen == screen)
3225         return cache;
3226     }
3227
3228   cache = gdk_window_cache_new (screen);
3229   context_x11->window_caches = g_slist_prepend (context_x11->window_caches, cache);
3230
3231   return cache;
3232 }
3233
3234 static GdkWindow *
3235 gdk_x11_drag_context_find_window (GdkDragContext  *context,
3236                                   GdkWindow       *drag_window,
3237                                   GdkScreen       *screen,
3238                                   gint             x_root,
3239                                   gint             y_root,
3240                                   GdkDragProtocol *protocol)
3241 {
3242   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3243   GdkWindowCache *window_cache;
3244   GdkDisplay *display;
3245   Window dest;
3246   GdkWindow *dest_window;
3247
3248   display = GDK_WINDOW_DISPLAY (context->source_window);
3249
3250   window_cache = drag_context_find_window_cache (context_x11, screen);
3251
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,
3255                                       x_root, y_root);
3256
3257   if (context_x11->dest_xid != dest)
3258     {
3259       Window recipient;
3260       context_x11->dest_xid = dest;
3261
3262       /* Check if new destination accepts drags, and which protocol */
3263
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.
3269        */
3270       recipient = _gdk_x11_display_get_drag_protocol (display,
3271                                                       dest,
3272                                                       protocol,
3273                                                       &context_x11->version);
3274
3275       if (recipient != None)
3276         {
3277           dest_window = gdk_x11_window_lookup_for_display (display, recipient);
3278           if (dest_window)
3279             g_object_ref (dest_window);
3280           else
3281             dest_window = gdk_x11_window_foreign_new_for_display (display, recipient);
3282         }
3283       else
3284         dest_window = NULL;
3285     }
3286   else
3287     {
3288       dest_window = context->dest_window;
3289       if (dest_window)
3290         g_object_ref (dest_window);
3291       *protocol = context->protocol;
3292     }
3293
3294   return dest_window;
3295 }
3296
3297 static gboolean
3298 gdk_x11_drag_context_drag_motion (GdkDragContext *context,
3299                                   GdkWindow      *dest_window,
3300                                   GdkDragProtocol protocol,
3301                                   gint            x_root,
3302                                   gint            y_root,
3303                                   GdkDragAction   suggested_action,
3304                                   GdkDragAction   possible_actions,
3305                                   guint32         time)
3306 {
3307   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3308
3309   context_x11->old_actions = context->actions;
3310   context->actions = possible_actions;
3311
3312   if (context_x11->old_actions != possible_actions)
3313     context_x11->xdnd_actions_set = FALSE;
3314
3315   if (protocol == GDK_DRAG_PROTO_XDND && context_x11->version == 0)
3316     {
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.
3324        */
3325       if (dest_window)
3326         {
3327           GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
3328
3329           xdnd_check_dest (display,
3330                            GDK_WINDOW_XID (dest_window),
3331                            &context_x11->version);
3332         }
3333     }
3334
3335   /* When we have a Xdnd target, make sure our XdndActionList
3336    * matches the current actions;
3337    */
3338   if (protocol == GDK_DRAG_PROTO_XDND && !context_x11->xdnd_actions_set)
3339     {
3340       if (dest_window)
3341         {
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)
3345             {
3346               GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
3347               GdkDragContext *dest_context;
3348
3349               dest_context = gdk_drag_context_find (display, FALSE,
3350                                                     GDK_WINDOW_XID (context->source_window),
3351                                                     GDK_WINDOW_XID (dest_window));
3352
3353               if (dest_context)
3354                 {
3355                   dest_context->actions = context->actions;
3356                   GDK_X11_DRAG_CONTEXT (dest_context)->xdnd_have_actions = TRUE;
3357                 }
3358             }
3359         }
3360     }
3361
3362   if (context->dest_window != dest_window)
3363     {
3364       GdkEvent *temp_event;
3365
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;
3369
3370       /* Check if new destination accepts drags, and which protocol */
3371
3372       if (dest_window)
3373         {
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;
3378
3379           switch (protocol)
3380             {
3381             case GDK_DRAG_PROTO_MOTIF:
3382               motif_send_enter (context_x11, time);
3383               break;
3384
3385             case GDK_DRAG_PROTO_XDND:
3386               xdnd_send_enter (context_x11);
3387               break;
3388
3389             case GDK_DRAG_PROTO_ROOTWIN:
3390             case GDK_DRAG_PROTO_NONE:
3391             default:
3392               break;
3393             }
3394           context_x11->old_action = suggested_action;
3395           context->suggested_action = suggested_action;
3396           context_x11->old_actions = possible_actions;
3397         }
3398       else
3399         {
3400           context->dest_window = NULL;
3401           context_x11->drop_xid = None;
3402           context->action = 0;
3403         }
3404
3405       /* Push a status event, to let the client know that
3406        * the drag changed
3407        */
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...
3412        */
3413       temp_event->dnd.send_event = TRUE;
3414
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));
3418
3419       gdk_event_put (temp_event);
3420       gdk_event_free (temp_event);
3421     }
3422   else
3423     {
3424       context_x11->old_action = context->suggested_action;
3425       context->suggested_action = suggested_action;
3426     }
3427
3428   /* Send a drag-motion event */
3429
3430   context_x11->last_x = x_root;
3431   context_x11->last_y = y_root;
3432
3433   if (context->dest_window)
3434     {
3435       if (context_x11->drag_status == GDK_DRAG_STATUS_DRAG)
3436         {
3437           switch (context->protocol)
3438             {
3439             case GDK_DRAG_PROTO_MOTIF:
3440               motif_send_motion (context_x11, x_root, y_root, suggested_action, time);
3441               break;
3442
3443             case GDK_DRAG_PROTO_XDND:
3444               xdnd_send_motion (context_x11, x_root, y_root, suggested_action, time);
3445               break;
3446
3447             case GDK_DRAG_PROTO_ROOTWIN:
3448               {
3449                 GdkEvent *temp_event;
3450                 /* GTK+ traditionally has used application/x-rootwin-drop,
3451                  * but the XDND spec specifies x-rootwindow-drop.
3452                  */
3453                 GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3454                 GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3455
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;
3461                 else
3462                   context->action = 0;
3463
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));
3470
3471                 gdk_event_put (temp_event);
3472                 gdk_event_free (temp_event);
3473               }
3474               break;
3475             case GDK_DRAG_PROTO_NONE:
3476               g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
3477               break;
3478             default:
3479               break;
3480             }
3481         }
3482       else
3483         return TRUE;
3484     }
3485
3486   return FALSE;
3487 }
3488
3489 static void
3490 gdk_x11_drag_context_drag_abort (GdkDragContext *context,
3491                                  guint32         time)
3492 {
3493   gdk_drag_do_leave (GDK_X11_DRAG_CONTEXT (context), time);
3494 }
3495
3496 static void
3497 gdk_x11_drag_context_drag_drop (GdkDragContext *context,
3498                                 guint32         time)
3499 {
3500   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3501
3502   if (context->dest_window)
3503     {
3504       switch (context->protocol)
3505         {
3506         case GDK_DRAG_PROTO_MOTIF:
3507           motif_send_leave (context_x11, time);
3508           motif_send_drop (context_x11, time);
3509           break;
3510
3511         case GDK_DRAG_PROTO_XDND:
3512           xdnd_send_drop (context_x11, time);
3513           break;
3514
3515         case GDK_DRAG_PROTO_ROOTWIN:
3516           g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
3517           break;
3518         case GDK_DRAG_PROTO_NONE:
3519           g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
3520           break;
3521         default:
3522           break;
3523         }
3524     }
3525 }
3526
3527 /* Destination side */
3528
3529 static void
3530 gdk_x11_drag_context_drag_status (GdkDragContext *context,
3531                                   GdkDragAction   action,
3532                                   guint32         time_)
3533 {
3534   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3535   XEvent xev;
3536   GdkDisplay *display;
3537
3538   display = GDK_WINDOW_DISPLAY (context->source_window);
3539
3540   context->action = action;
3541
3542   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3543     {
3544       gboolean need_coords = FALSE;
3545
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);
3552
3553       if (context_x11->drag_status == GDK_DRAG_STATUS_ACTION_WAIT)
3554         {
3555           MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED | 0x80;
3556         }
3557       else
3558         {
3559           if ((action != 0) != (context_x11->old_action != 0))
3560             {
3561               if (action != 0)
3562                 {
3563                   MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_ENTER | 0x80;
3564                   need_coords = TRUE;
3565                 }
3566               else
3567                 {
3568                   MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_LEAVE | 0x80;
3569                 }
3570             }
3571           else
3572             {
3573               MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION | 0x80;
3574               need_coords = TRUE;
3575             }
3576         }
3577
3578       MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3579
3580       switch (action)
3581         {
3582         case GDK_ACTION_MOVE:
3583           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_MOVE;
3584           break;
3585         case GDK_ACTION_COPY:
3586           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY;
3587           break;
3588         case GDK_ACTION_LINK:
3589           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_LINK;
3590           break;
3591         default:
3592           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP;
3593           break;
3594         }
3595
3596       if (action)
3597         MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmDROP_SITE_VALID << 4);
3598       else
3599         MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmNO_DROP_SITE << 4);
3600
3601       MOTIF_XCLIENT_LONG (&xev, 1) = time_;
3602
3603       if (need_coords)
3604         {
3605           MOTIF_XCLIENT_SHORT (&xev, 4) = context_x11->last_x;
3606           MOTIF_XCLIENT_SHORT (&xev, 5) = context_x11->last_y;
3607         }
3608       else
3609         MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3610
3611       MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3612       MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3613
3614       if (!_gdk_x11_display_send_xevent (display,
3615                                          GDK_WINDOW_XID (context->source_window),
3616                                          FALSE, 0, &xev))
3617         GDK_NOTE (DND,
3618                   g_message ("Send event to %lx failed",
3619                              GDK_WINDOW_XID (context->source_window)));
3620     }
3621   else if (context->protocol == GDK_DRAG_PROTO_XDND)
3622     {
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);
3627
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))
3634         GDK_NOTE (DND,
3635                   g_message ("Send event to %lx failed",
3636                              GDK_WINDOW_XID (context->source_window)));
3637     }
3638
3639   context_x11->old_action = action;
3640 }
3641
3642 static void
3643 gdk_x11_drag_context_drop_reply (GdkDragContext *context,
3644                                  gboolean        accepted,
3645                                  guint32         time_)
3646 {
3647   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3648
3649   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3650     {
3651       GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
3652       XEvent xev;
3653
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;
3658
3659       MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START | 0x80;
3660       MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3661       if (accepted)
3662         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY |
3663                                        (XmDROP_SITE_VALID << 4) |
3664                                        (XmDROP_NOOP << 8) |
3665                                        (XmDROP << 12);
3666       else
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;
3676
3677       _gdk_x11_display_send_xevent (display,
3678                                     GDK_WINDOW_XID (context->source_window),
3679                                     FALSE, 0, &xev);
3680     }
3681 }
3682
3683 static void
3684 gdk_x11_drag_context_drop_finish (GdkDragContext *context,
3685                                   gboolean        success,
3686                                   guint32         time)
3687 {
3688   if (context->protocol == GDK_DRAG_PROTO_XDND)
3689     {
3690       GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
3691       XEvent xev;
3692
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);
3697
3698       xev.xclient.data.l[0] = GDK_WINDOW_XID (context->dest_window);
3699       if (success)
3700         {
3701           xev.xclient.data.l[1] = 1;
3702           xev.xclient.data.l[2] = xdnd_action_to_atom (display,
3703                                                        context->action);
3704         }
3705       else
3706         {
3707           xev.xclient.data.l[1] = 0;
3708           xev.xclient.data.l[2] = None;
3709         }
3710       xev.xclient.data.l[3] = 0;
3711       xev.xclient.data.l[4] = 0;
3712
3713       if (!xdnd_send_xevent (GDK_X11_DRAG_CONTEXT (context), context->source_window, FALSE, &xev))
3714         GDK_NOTE (DND,
3715                   g_message ("Send event to %lx failed",
3716                              GDK_WINDOW_XID (context->source_window)));
3717     }
3718 }
3719
3720 void
3721 _gdk_x11_window_register_dnd (GdkWindow *window)
3722 {
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);
3727
3728   g_return_if_fail (window != NULL);
3729
3730   if (gdk_window_get_window_type (window) == GDK_WINDOW_OFFSCREEN)
3731     return;
3732
3733   base_precache_atoms (display);
3734
3735   if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
3736     return;
3737   else
3738     g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
3739
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);
3751
3752   XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
3753                    GDK_WINDOW_XID (window),
3754                    motif_drag_receiver_info_atom,
3755                    motif_drag_receiver_info_atom,
3756                    8, PropModeReplace,
3757                    (guchar *)&info,
3758                    sizeof (info));
3759
3760   /* Set XdndAware */
3761
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);
3768 }
3769
3770 static GdkAtom
3771 gdk_x11_drag_context_get_selection (GdkDragContext *context)
3772 {
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");
3778   else
3779     return GDK_NONE;
3780 }
3781
3782 static gboolean
3783 gdk_x11_drag_context_drop_status (GdkDragContext *context)
3784 {
3785   return ! GDK_X11_DRAG_CONTEXT (context)->drop_failed;
3786 }