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