]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkdnd-x11.c
x11: Do dnd event handling via gdk_window_add_filter()
[~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_lookup_for_display (display, source_window);
1733   if (context->source_window)
1734     g_object_ref (context->source_window);
1735   else
1736     {
1737       context->source_window = gdk_x11_window_foreign_new_for_display (display, source_window);
1738       if (!context->source_window)
1739         {
1740           g_object_unref (context_x11);
1741           return NULL;
1742         }
1743     }
1744
1745   context->dest_window = dest_window;
1746   g_object_ref (dest_window);
1747   context->start_time = timestamp;
1748
1749   if (!motif_read_initiator_info (GDK_WINDOW_DISPLAY (dest_window),
1750                                   source_window,
1751                                   atom,
1752                                   &context->targets,
1753                                   &context_x11->motif_selection))
1754     {
1755       g_object_unref (context_x11);
1756       return NULL;
1757     }
1758
1759   return context;
1760 }
1761
1762 /*
1763  * The MOTIF drag protocol has no real provisions for distinguishing
1764  * multiple simultaneous drops. If the sources grab the pointer
1765  * when doing drags, that shouldn't happen, in any case. If it
1766  * does, we can't do much except hope for the best.
1767  */
1768
1769 static GdkFilterReturn
1770 motif_top_level_enter (GdkEvent *event,
1771                        guint16   flags,
1772                        guint32   timestamp,
1773                        guint32   source_window,
1774                        guint32   atom)
1775 {
1776   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1777   GdkDragContext *new_context;
1778
1779   GDK_NOTE(DND, g_message ("Motif DND top level enter: flags: %#4x time: %d source_widow: %#4x atom: %d",
1780                            flags, timestamp, source_window, atom));
1781
1782   new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1783   if (!new_context)
1784     return GDK_FILTER_REMOVE;
1785
1786   event->dnd.type = GDK_DRAG_ENTER;
1787   event->dnd.context = new_context;
1788   g_object_ref (new_context);
1789
1790   display_x11->current_dest_drag = new_context;
1791
1792   return GDK_FILTER_TRANSLATE;
1793 }
1794
1795 static GdkFilterReturn
1796 motif_top_level_leave (GdkEvent *event,
1797                        guint16   flags,
1798                        guint32   timestamp)
1799 {
1800   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1801
1802   GDK_NOTE(DND, g_message ("Motif DND top level leave: flags: %#4x time: %d",
1803                            flags, timestamp));
1804
1805   if ((display_x11->current_dest_drag != NULL) &&
1806       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1807       (timestamp >= display_x11->current_dest_drag->start_time))
1808     {
1809       event->dnd.type = GDK_DRAG_LEAVE;
1810       /* Pass ownership of context to the event */
1811       event->dnd.context = display_x11->current_dest_drag;
1812
1813       display_x11->current_dest_drag = NULL;
1814
1815       return GDK_FILTER_TRANSLATE;
1816     }
1817   else
1818     return GDK_FILTER_REMOVE;
1819 }
1820
1821 static GdkFilterReturn
1822 motif_motion (GdkEvent *event,
1823               guint16   flags,
1824               guint32   timestamp,
1825               gint16    x_root,
1826               gint16    y_root)
1827 {
1828   GdkX11DragContext *context_x11;
1829   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1830
1831   GDK_NOTE(DND, g_message ("Motif DND motion: flags: %#4x time: %d (%d, %d)",
1832                            flags, timestamp, x_root, y_root));
1833
1834   if ((display_x11->current_dest_drag != NULL) &&
1835       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1836       (timestamp >= display_x11->current_dest_drag->start_time))
1837     {
1838       context_x11 = GDK_X11_DRAG_CONTEXT (display_x11->current_dest_drag);
1839
1840       event->dnd.type = GDK_DRAG_MOTION;
1841       event->dnd.context = display_x11->current_dest_drag;
1842       g_object_ref (display_x11->current_dest_drag);
1843
1844       event->dnd.time = timestamp;
1845
1846       motif_dnd_translate_flags (context_x11, flags);
1847
1848       event->dnd.x_root = x_root;
1849       event->dnd.y_root = y_root;
1850
1851       context_x11->last_x = x_root;
1852       context_x11->last_y = y_root;
1853
1854       context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1855
1856       return GDK_FILTER_TRANSLATE;
1857     }
1858
1859   return GDK_FILTER_REMOVE;
1860 }
1861
1862 static GdkFilterReturn
1863 motif_operation_changed (GdkEvent *event,
1864                          guint16   flags,
1865                          guint32   timestamp)
1866 {
1867   GdkX11DragContext *context_x11;
1868   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1869   GDK_NOTE(DND, g_message ("Motif DND operation changed: flags: %#4x time: %d",
1870                            flags, timestamp));
1871
1872   if ((display_x11->current_dest_drag != NULL) &&
1873       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1874       (timestamp >= display_x11->current_dest_drag->start_time))
1875     {
1876       event->dnd.type = GDK_DRAG_MOTION;
1877       event->dnd.send_event = FALSE;
1878       event->dnd.context = display_x11->current_dest_drag;
1879       g_object_ref (display_x11->current_dest_drag);
1880
1881       event->dnd.time = timestamp;
1882       context_x11 = GDK_X11_DRAG_CONTEXT (display_x11->current_dest_drag);
1883
1884       motif_dnd_translate_flags (context_x11, flags);
1885
1886       event->dnd.x_root = context_x11->last_x;
1887       event->dnd.y_root = context_x11->last_y;
1888
1889       context_x11->drag_status = GDK_DRAG_STATUS_ACTION_WAIT;
1890
1891       return GDK_FILTER_TRANSLATE;
1892     }
1893
1894   return GDK_FILTER_REMOVE;
1895 }
1896
1897 static GdkFilterReturn
1898 motif_drop_start (GdkEvent *event,
1899                   guint16   flags,
1900                   guint32   timestamp,
1901                   guint32   source_window,
1902                   guint32   atom,
1903                   gint16    x_root,
1904                   gint16    y_root)
1905 {
1906   GdkDragContext *new_context;
1907   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1908
1909   GDK_NOTE(DND, g_message ("Motif DND drop start: flags: %#4x time: %d (%d, %d) source_widow: %#4x atom: %d",
1910                            flags, timestamp, x_root, y_root, source_window, atom));
1911
1912   new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1913   if (!new_context)
1914     return GDK_FILTER_REMOVE;
1915
1916   motif_dnd_translate_flags (GDK_X11_DRAG_CONTEXT (new_context), flags);
1917
1918   event->dnd.type = GDK_DROP_START;
1919   event->dnd.context = new_context;
1920   event->dnd.time = timestamp;
1921   event->dnd.x_root = x_root;
1922   event->dnd.y_root = y_root;
1923
1924   gdk_x11_window_set_user_time (event->any.window, timestamp);
1925
1926   g_object_ref (new_context);
1927   display_x11->current_dest_drag = new_context;
1928
1929   return GDK_FILTER_TRANSLATE;
1930 }
1931
1932 static GdkFilterReturn
1933 motif_drag_status (GdkEvent *event,
1934                    guint16   flags,
1935                    guint32   timestamp)
1936 {
1937   GdkDragContext *context;
1938   GdkDisplay *display;
1939
1940   GDK_NOTE (DND,
1941             g_message ("Motif status message: flags %x", flags));
1942
1943   display = gdk_window_get_display (event->any.window);
1944   if (!display)
1945     return GDK_FILTER_REMOVE;
1946
1947   context = gdk_drag_context_find (display, TRUE, GDK_WINDOW_XID (event->any.window), None);
1948
1949   if (context)
1950     {
1951       GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
1952       if ((context_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT) ||
1953           (context_x11->drag_status == GDK_DRAG_STATUS_ACTION_WAIT))
1954         context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
1955
1956       event->dnd.type = GDK_DRAG_STATUS;
1957       event->dnd.send_event = FALSE;
1958       event->dnd.context = context;
1959       g_object_ref (context);
1960
1961       event->dnd.time = timestamp;
1962
1963       if ((flags & 0x00f0) >> 4 == XmDROP_SITE_VALID)
1964         {
1965           switch (flags & 0x000f)
1966             {
1967             case XmDROP_NOOP:
1968               context->action = 0;
1969               break;
1970             case XmDROP_MOVE:
1971                 context->action = GDK_ACTION_MOVE;
1972                 break;
1973             case XmDROP_COPY:
1974               context->action = GDK_ACTION_COPY;
1975               break;
1976             case XmDROP_LINK:
1977               context->action = GDK_ACTION_LINK;
1978               break;
1979             }
1980         }
1981       else
1982         context->action = 0;
1983
1984       return GDK_FILTER_TRANSLATE;
1985     }
1986   return GDK_FILTER_REMOVE;
1987 }
1988
1989 static GdkFilterReturn
1990 motif_dnd_filter (GdkXEvent *xev,
1991                   GdkEvent  *event,
1992                   gpointer data)
1993 {
1994   XEvent *xevent = (XEvent *)xev;
1995
1996   guint8 reason;
1997   guint16 flags;
1998   guint32 timestamp;
1999   guint32 source_window;
2000   Atom atom;
2001   gint16 x_root, y_root;
2002   gboolean is_reply;
2003
2004   if (!event->any.window ||
2005       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2006     return GDK_FILTER_CONTINUE;                 /* Not for us */
2007
2008   /* First read some fields common to all Motif DND messages */
2009   reason = MOTIF_UNPACK_BYTE (xevent, 0);
2010   flags = MOTIF_UNPACK_SHORT (xevent, 1);
2011   timestamp = MOTIF_UNPACK_LONG (xevent, 1);
2012
2013   is_reply = ((reason & 0x80) != 0);
2014
2015   switch (reason & 0x7f)
2016     {
2017     case XmTOP_LEVEL_ENTER:
2018       source_window = MOTIF_UNPACK_LONG (xevent, 2);
2019       atom = MOTIF_UNPACK_LONG (xevent, 3);
2020       return motif_top_level_enter (event, flags, timestamp, source_window, atom);
2021     case XmTOP_LEVEL_LEAVE:
2022       return motif_top_level_leave (event, flags, timestamp);
2023
2024     case XmDRAG_MOTION:
2025       x_root = MOTIF_UNPACK_SHORT (xevent, 4);
2026       y_root = MOTIF_UNPACK_SHORT (xevent, 5);
2027
2028       if (!is_reply)
2029         return motif_motion (event, flags, timestamp, x_root, y_root);
2030       else
2031         return motif_drag_status (event, flags, timestamp);
2032
2033     case XmDROP_SITE_ENTER:
2034       return motif_drag_status (event, flags, timestamp);
2035
2036     case XmDROP_SITE_LEAVE:
2037       return motif_drag_status (event,
2038                                 XmNO_DROP_SITE << 8 | XmDROP_NOOP,
2039                                 timestamp);
2040     case XmDROP_START:
2041       x_root = MOTIF_UNPACK_SHORT (xevent, 4);
2042       y_root = MOTIF_UNPACK_SHORT (xevent, 5);
2043       atom = MOTIF_UNPACK_LONG (xevent, 3);
2044       source_window = MOTIF_UNPACK_LONG (xevent, 4);
2045
2046       if (!is_reply)
2047         return motif_drop_start (event, flags, timestamp, source_window, atom, x_root, y_root);
2048
2049      break;
2050     case XmOPERATION_CHANGED:
2051       if (!is_reply)
2052         return motif_operation_changed (event, flags, timestamp);
2053       else
2054         return motif_drag_status (event, flags, timestamp);
2055
2056       break;
2057       /* To the best of my knowledge, these next two messages are
2058        * not part of the protocol, though they are defined in
2059        * the header files.
2060        */
2061     case XmDROP_FINISH:
2062     case XmDRAG_DROP_FINISH:
2063       break;
2064     }
2065
2066   return GDK_FILTER_REMOVE;
2067 }
2068
2069 /*************************************************************
2070  ***************************** XDND **************************
2071  *************************************************************/
2072
2073 /* Utility functions */
2074
2075 static struct {
2076   const gchar *name;
2077   GdkAtom atom;
2078   GdkDragAction action;
2079 } xdnd_actions_table[] = {
2080     { "XdndActionCopy",    None, GDK_ACTION_COPY },
2081     { "XdndActionMove",    None, GDK_ACTION_MOVE },
2082     { "XdndActionLink",    None, GDK_ACTION_LINK },
2083     { "XdndActionAsk",     None, GDK_ACTION_ASK  },
2084     { "XdndActionPrivate", None, GDK_ACTION_COPY },
2085   };
2086
2087 static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
2088 static gboolean xdnd_actions_initialized = FALSE;
2089
2090 static void
2091 xdnd_initialize_actions (void)
2092 {
2093   gint i;
2094
2095   xdnd_actions_initialized = TRUE;
2096   for (i = 0; i < xdnd_n_actions; i++)
2097     xdnd_actions_table[i].atom = gdk_atom_intern_static_string (xdnd_actions_table[i].name);
2098 }
2099
2100 static GdkDragAction
2101 xdnd_action_from_atom (GdkDisplay *display,
2102                        Atom        xatom)
2103 {
2104   GdkAtom atom;
2105   gint i;
2106
2107   if (xatom == None)
2108     return 0;
2109
2110   atom = gdk_x11_xatom_to_atom_for_display (display, xatom);
2111
2112   if (!xdnd_actions_initialized)
2113     xdnd_initialize_actions();
2114
2115   for (i = 0; i < xdnd_n_actions; i++)
2116     if (atom == xdnd_actions_table[i].atom)
2117       return xdnd_actions_table[i].action;
2118
2119   return 0;
2120 }
2121
2122 static Atom
2123 xdnd_action_to_atom (GdkDisplay    *display,
2124                      GdkDragAction  action)
2125 {
2126   gint i;
2127
2128   if (!xdnd_actions_initialized)
2129     xdnd_initialize_actions();
2130
2131   for (i = 0; i < xdnd_n_actions; i++)
2132     if (action == xdnd_actions_table[i].action)
2133       return gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
2134
2135   return None;
2136 }
2137
2138 /* Source side */
2139
2140 static GdkFilterReturn
2141 xdnd_status_filter (GdkXEvent *xev,
2142                     GdkEvent  *event,
2143                     gpointer   data)
2144 {
2145   GdkDisplay *display;
2146   XEvent *xevent = (XEvent *)xev;
2147   guint32 dest_window = xevent->xclient.data.l[0];
2148   guint32 flags = xevent->xclient.data.l[1];
2149   Atom action = xevent->xclient.data.l[4];
2150   GdkDragContext *context;
2151
2152   if (!event->any.window ||
2153       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2154     return GDK_FILTER_CONTINUE;                 /* Not for us */
2155
2156   GDK_NOTE (DND,
2157             g_message ("XdndStatus: dest_window: %#x  action: %ld",
2158                        dest_window, action));
2159
2160   display = gdk_window_get_display (event->any.window);
2161   context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
2162
2163   if (context)
2164     {
2165       GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
2166       if (context_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
2167         context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
2168
2169       event->dnd.send_event = FALSE;
2170       event->dnd.type = GDK_DRAG_STATUS;
2171       event->dnd.context = context;
2172       gdk_event_set_device (event, gdk_drag_context_get_device (context));
2173       g_object_ref (context);
2174
2175       event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2176       if (!(action != 0) != !(flags & 1))
2177         {
2178           GDK_NOTE (DND,
2179                     g_warning ("Received status event with flags not corresponding to action!\n"));
2180           action = 0;
2181         }
2182
2183       context->action = xdnd_action_from_atom (display, action);
2184
2185       return GDK_FILTER_TRANSLATE;
2186     }
2187
2188   return GDK_FILTER_REMOVE;
2189 }
2190
2191 static GdkFilterReturn
2192 xdnd_finished_filter (GdkXEvent *xev,
2193                       GdkEvent  *event,
2194                       gpointer   data)
2195 {
2196   GdkDisplay *display;
2197   XEvent *xevent = (XEvent *)xev;
2198   guint32 dest_window = xevent->xclient.data.l[0];
2199   GdkDragContext *context;
2200   GdkX11DragContext *context_x11;
2201
2202   if (!event->any.window ||
2203       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2204     return GDK_FILTER_CONTINUE;                 /* Not for us */
2205
2206   GDK_NOTE (DND,
2207             g_message ("XdndFinished: dest_window: %#x", dest_window));
2208
2209   display = gdk_window_get_display (event->any.window);
2210   context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
2211
2212   if (context)
2213     {
2214       context_x11 = GDK_X11_DRAG_CONTEXT (context);
2215       if (context_x11->version == 5)
2216         context_x11->drop_failed = xevent->xclient.data.l[1] == 0;
2217
2218       event->dnd.type = GDK_DROP_FINISHED;
2219       event->dnd.context = context;
2220       gdk_event_set_device (event, gdk_drag_context_get_device (context));
2221       g_object_ref (context);
2222
2223       event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2224
2225       return GDK_FILTER_TRANSLATE;
2226     }
2227
2228   return GDK_FILTER_REMOVE;
2229 }
2230
2231 static void
2232 xdnd_set_targets (GdkX11DragContext *context_x11)
2233 {
2234   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2235   Atom *atomlist;
2236   GList *tmp_list = context->targets;
2237   gint i;
2238   gint n_atoms = g_list_length (context->targets);
2239   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2240
2241   atomlist = g_new (Atom, n_atoms);
2242   i = 0;
2243   while (tmp_list)
2244     {
2245       atomlist[i] = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (tmp_list->data));
2246       tmp_list = tmp_list->next;
2247       i++;
2248     }
2249
2250   XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
2251                    GDK_WINDOW_XID (context->source_window),
2252                    gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
2253                    XA_ATOM, 32, PropModeReplace,
2254                    (guchar *)atomlist, n_atoms);
2255
2256   g_free (atomlist);
2257
2258   context_x11->xdnd_targets_set = 1;
2259 }
2260
2261 static void
2262 xdnd_set_actions (GdkX11DragContext *context_x11)
2263 {
2264   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2265   Atom *atomlist;
2266   gint i;
2267   gint n_atoms;
2268   guint actions;
2269   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2270
2271   if (!xdnd_actions_initialized)
2272     xdnd_initialize_actions();
2273
2274   actions = context->actions;
2275   n_atoms = 0;
2276   for (i = 0; i < xdnd_n_actions; i++)
2277     {
2278       if (actions & xdnd_actions_table[i].action)
2279         {
2280           actions &= ~xdnd_actions_table[i].action;
2281           n_atoms++;
2282         }
2283     }
2284
2285   atomlist = g_new (Atom, n_atoms);
2286
2287   actions = context->actions;
2288   n_atoms = 0;
2289   for (i = 0; i < xdnd_n_actions; i++)
2290     {
2291       if (actions & xdnd_actions_table[i].action)
2292         {
2293           actions &= ~xdnd_actions_table[i].action;
2294           atomlist[n_atoms] = gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
2295           n_atoms++;
2296         }
2297     }
2298
2299   XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
2300                    GDK_WINDOW_XID (context->source_window),
2301                    gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
2302                    XA_ATOM, 32, PropModeReplace,
2303                    (guchar *)atomlist, n_atoms);
2304
2305   g_free (atomlist);
2306
2307   context_x11->xdnd_actions_set = TRUE;
2308   context_x11->xdnd_actions = context->actions;
2309 }
2310
2311 static void
2312 send_client_message_async_cb (Window   window,
2313                               gboolean success,
2314                               gpointer data)
2315 {
2316   GdkDragContext *context = data;
2317   GDK_NOTE (DND,
2318             g_message ("Got async callback for #%lx, success = %d",
2319                        window, success));
2320
2321   /* On failure, we immediately continue with the protocol
2322    * so we don't end up blocking for a timeout
2323    */
2324   if (!success &&
2325       context->dest_window &&
2326       window == GDK_WINDOW_XID (context->dest_window))
2327     {
2328       GdkEvent *temp_event;
2329       GdkX11DragContext *context_x11 = data;
2330
2331       g_object_unref (context->dest_window);
2332       context->dest_window = NULL;
2333       context->action = 0;
2334
2335       context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
2336
2337       temp_event = gdk_event_new (GDK_DRAG_STATUS);
2338       temp_event->dnd.window = g_object_ref (context->source_window);
2339       temp_event->dnd.send_event = TRUE;
2340       temp_event->dnd.context = g_object_ref (context);
2341       temp_event->dnd.time = GDK_CURRENT_TIME;
2342       gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
2343
2344       gdk_event_put (temp_event);
2345
2346       gdk_event_free (temp_event);
2347     }
2348
2349   g_object_unref (context);
2350 }
2351
2352
2353 static GdkDisplay *
2354 gdk_drag_context_get_display (GdkDragContext *context)
2355 {
2356   if (context->source_window)
2357     return GDK_WINDOW_DISPLAY (context->source_window);
2358   else if (context->dest_window)
2359     return GDK_WINDOW_DISPLAY (context->dest_window);
2360
2361   g_assert_not_reached ();
2362   return NULL;
2363 }
2364
2365 static void
2366 send_client_message_async (GdkDragContext      *context,
2367                            Window               window,
2368                            gboolean             propagate,
2369                            glong                event_mask,
2370                            XClientMessageEvent *event_send)
2371 {
2372   GdkDisplay *display = gdk_drag_context_get_display (context);
2373
2374   g_object_ref (context);
2375
2376   _gdk_x11_send_client_message_async (display, window,
2377                                       propagate, event_mask, event_send,
2378                                       send_client_message_async_cb, context);
2379 }
2380
2381 static gboolean
2382 xdnd_send_xevent (GdkX11DragContext *context_x11,
2383                   GdkWindow         *window,
2384                   gboolean           propagate,
2385                   XEvent            *event_send)
2386 {
2387   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2388   GdkDisplay *display = gdk_drag_context_get_display (context);
2389   Window xwindow;
2390   glong event_mask;
2391
2392   g_assert (event_send->xany.type == ClientMessage);
2393
2394   /* We short-circuit messages to ourselves */
2395   if (gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
2396     {
2397       gint i;
2398
2399       for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
2400         {
2401           if (gdk_x11_get_xatom_by_name_for_display (display, xdnd_filters[i].atom_name) ==
2402               event_send->xclient.message_type)
2403             {
2404               GdkEvent *temp_event;
2405
2406               temp_event = gdk_event_new (GDK_NOTHING);
2407               temp_event->any.window = g_object_ref (window);
2408
2409               if ((*xdnd_filters[i].func) (event_send, temp_event, NULL) == GDK_FILTER_TRANSLATE)
2410                 {
2411                   gdk_event_put (temp_event);
2412                   gdk_event_free (temp_event);
2413                 }
2414
2415               return TRUE;
2416             }
2417         }
2418     }
2419
2420   xwindow = GDK_WINDOW_XID (window);
2421
2422   if (_gdk_x11_display_is_root_window (display, xwindow))
2423     event_mask = ButtonPressMask;
2424   else
2425     event_mask = 0;
2426
2427   send_client_message_async (context, xwindow, propagate, event_mask,
2428                              &event_send->xclient);
2429
2430   return TRUE;
2431 }
2432
2433 static void
2434 xdnd_send_enter (GdkX11DragContext *context_x11)
2435 {
2436   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2437   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->dest_window);
2438   XEvent xev;
2439
2440   xev.xclient.type = ClientMessage;
2441   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndEnter");
2442   xev.xclient.format = 32;
2443   xev.xclient.window = context_x11->drop_xid
2444                            ? context_x11->drop_xid
2445                            : GDK_WINDOW_XID (context->dest_window);
2446   xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2447   xev.xclient.data.l[1] = (context_x11->version << 24); /* version */
2448   xev.xclient.data.l[2] = 0;
2449   xev.xclient.data.l[3] = 0;
2450   xev.xclient.data.l[4] = 0;
2451
2452   GDK_NOTE(DND,
2453            g_message ("Sending enter source window %#lx XDND protocol version %d\n",
2454                       GDK_WINDOW_XID (context->source_window), context_x11->version));
2455   if (g_list_length (context->targets) > 3)
2456     {
2457       if (!context_x11->xdnd_targets_set)
2458         xdnd_set_targets (context_x11);
2459       xev.xclient.data.l[1] |= 1;
2460     }
2461   else
2462     {
2463       GList *tmp_list = context->targets;
2464       gint i = 2;
2465
2466       while (tmp_list)
2467         {
2468           xev.xclient.data.l[i] = gdk_x11_atom_to_xatom_for_display (display,
2469                                                                      GDK_POINTER_TO_ATOM (tmp_list->data));
2470           tmp_list = tmp_list->next;
2471           i++;
2472         }
2473     }
2474
2475   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2476     {
2477       GDK_NOTE (DND,
2478                 g_message ("Send event to %lx failed",
2479                            GDK_WINDOW_XID (context->dest_window)));
2480       g_object_unref (context->dest_window);
2481       context->dest_window = NULL;
2482     }
2483 }
2484
2485 static void
2486 xdnd_send_leave (GdkX11DragContext *context_x11)
2487 {
2488   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2489   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2490   XEvent xev;
2491
2492   xev.xclient.type = ClientMessage;
2493   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndLeave");
2494   xev.xclient.format = 32;
2495   xev.xclient.window = context_x11->drop_xid
2496                            ? context_x11->drop_xid
2497                            : GDK_WINDOW_XID (context->dest_window);
2498   xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2499   xev.xclient.data.l[1] = 0;
2500   xev.xclient.data.l[2] = 0;
2501   xev.xclient.data.l[3] = 0;
2502   xev.xclient.data.l[4] = 0;
2503
2504   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2505     {
2506       GDK_NOTE (DND,
2507                 g_message ("Send event to %lx failed",
2508                            GDK_WINDOW_XID (context->dest_window)));
2509       g_object_unref (context->dest_window);
2510       context->dest_window = NULL;
2511     }
2512 }
2513
2514 static void
2515 xdnd_send_drop (GdkX11DragContext *context_x11,
2516                 guint32            time)
2517 {
2518   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2519   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2520   XEvent xev;
2521
2522   xev.xclient.type = ClientMessage;
2523   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndDrop");
2524   xev.xclient.format = 32;
2525   xev.xclient.window = context_x11->drop_xid
2526                            ? context_x11->drop_xid
2527                            : GDK_WINDOW_XID (context->dest_window);
2528   xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2529   xev.xclient.data.l[1] = 0;
2530   xev.xclient.data.l[2] = time;
2531   xev.xclient.data.l[3] = 0;
2532   xev.xclient.data.l[4] = 0;
2533
2534   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2535     {
2536       GDK_NOTE (DND,
2537                 g_message ("Send event to %lx failed",
2538                            GDK_WINDOW_XID (context->dest_window)));
2539       g_object_unref (context->dest_window);
2540       context->dest_window = NULL;
2541     }
2542 }
2543
2544 static void
2545 xdnd_send_motion (GdkX11DragContext *context_x11,
2546                   gint               x_root,
2547                   gint               y_root,
2548                   GdkDragAction      action,
2549                   guint32            time)
2550 {
2551   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2552   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2553   XEvent xev;
2554
2555   xev.xclient.type = ClientMessage;
2556   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndPosition");
2557   xev.xclient.format = 32;
2558   xev.xclient.window = context_x11->drop_xid
2559                            ? context_x11->drop_xid
2560                            : GDK_WINDOW_XID (context->dest_window);
2561   xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2562   xev.xclient.data.l[1] = 0;
2563   xev.xclient.data.l[2] = (x_root << 16) | y_root;
2564   xev.xclient.data.l[3] = time;
2565   xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
2566
2567   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2568     {
2569       GDK_NOTE (DND,
2570                 g_message ("Send event to %lx failed",
2571                            GDK_WINDOW_XID (context->dest_window)));
2572       g_object_unref (context->dest_window);
2573       context->dest_window = NULL;
2574     }
2575   context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
2576 }
2577
2578 static guint32
2579 xdnd_check_dest (GdkDisplay *display,
2580                  Window      win,
2581                  guint      *xdnd_version)
2582 {
2583   gboolean retval = FALSE;
2584   Atom type = None;
2585   int format;
2586   unsigned long nitems, after;
2587   guchar *data;
2588   Atom *version;
2589   Window *proxy_data;
2590   Window proxy;
2591   Atom xdnd_proxy_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndProxy");
2592   Atom xdnd_aware_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndAware");
2593
2594   proxy = None;
2595
2596   gdk_x11_display_error_trap_push (display);
2597   if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win,
2598                           xdnd_proxy_atom, 0,
2599                           1, False, AnyPropertyType,
2600                           &type, &format, &nitems, &after,
2601                           &data) == Success)
2602     {
2603       if (type != None)
2604         {
2605           proxy_data = (Window *)data;
2606
2607           if ((format == 32) && (nitems == 1))
2608             {
2609               proxy = *proxy_data;
2610             }
2611           else
2612             GDK_NOTE (DND,
2613                       g_warning ("Invalid XdndProxy "
2614                                  "property on window %ld\n", win));
2615
2616           XFree (proxy_data);
2617         }
2618
2619       if ((XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), proxy ? proxy : win,
2620                                xdnd_aware_atom, 0,
2621                                1, False, AnyPropertyType,
2622                                &type, &format, &nitems, &after,
2623                                &data) == Success) &&
2624           type != None)
2625         {
2626           version = (Atom *)data;
2627
2628           if ((format == 32) && (nitems == 1))
2629             {
2630               if (*version >= 3)
2631                 retval = TRUE;
2632               if (xdnd_version)
2633                 *xdnd_version = *version;
2634             }
2635           else
2636             GDK_NOTE (DND,
2637                       g_warning ("Invalid XdndAware "
2638                                  "property on window %ld\n", win));
2639
2640           XFree (version);
2641         }
2642     }
2643
2644   gdk_x11_display_error_trap_pop_ignored (display);
2645
2646   return retval ? (proxy ? proxy : win) : None;
2647 }
2648
2649 /* Target side */
2650
2651 static void
2652 xdnd_read_actions (GdkX11DragContext *context_x11)
2653 {
2654   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2655   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2656   Atom type;
2657   int format;
2658   gulong nitems, after;
2659   guchar *data;
2660   Atom *atoms;
2661   gint i;
2662
2663   context_x11->xdnd_have_actions = FALSE;
2664
2665   if (gdk_window_get_window_type (context->source_window) == GDK_WINDOW_FOREIGN)
2666     {
2667       /* Get the XdndActionList, if set */
2668
2669       gdk_x11_display_error_trap_push (display);
2670       if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
2671                               GDK_WINDOW_XID (context->source_window),
2672                               gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
2673                               0, 65536,
2674                               False, XA_ATOM, &type, &format, &nitems,
2675                               &after, &data) == Success &&
2676           type == XA_ATOM)
2677         {
2678           atoms = (Atom *)data;
2679
2680           context->actions = 0;
2681
2682           for (i = 0; i < nitems; i++)
2683             context->actions |= xdnd_action_from_atom (display, atoms[i]);
2684
2685           context_x11->xdnd_have_actions = TRUE;
2686
2687 #ifdef G_ENABLE_DEBUG
2688           if (_gdk_debug_flags & GDK_DEBUG_DND)
2689             {
2690               GString *action_str = g_string_new (NULL);
2691               if (context->actions & GDK_ACTION_MOVE)
2692                 g_string_append(action_str, "MOVE ");
2693               if (context->actions & GDK_ACTION_COPY)
2694                 g_string_append(action_str, "COPY ");
2695               if (context->actions & GDK_ACTION_LINK)
2696                 g_string_append(action_str, "LINK ");
2697               if (context->actions & GDK_ACTION_ASK)
2698                 g_string_append(action_str, "ASK ");
2699
2700               g_message("Xdnd actions = %s", action_str->str);
2701               g_string_free (action_str, TRUE);
2702             }
2703 #endif /* G_ENABLE_DEBUG */
2704
2705         }
2706
2707       if (data)
2708         XFree (data);
2709
2710       gdk_x11_display_error_trap_pop_ignored (display);
2711     }
2712   else
2713     {
2714       /* Local drag
2715        */
2716       GdkDragContext *source_context;
2717
2718       source_context = gdk_drag_context_find (display, TRUE,
2719                                               GDK_WINDOW_XID (context->source_window),
2720                                               GDK_WINDOW_XID (context->dest_window));
2721
2722       if (source_context)
2723         {
2724           context->actions = source_context->actions;
2725           context_x11->xdnd_have_actions = TRUE;
2726         }
2727     }
2728 }
2729
2730 /* We have to make sure that the XdndActionList we keep internally
2731  * is up to date with the XdndActionList on the source window
2732  * because we get no notification, because Xdnd wasn't meant
2733  * to continually send actions. So we select on PropertyChangeMask
2734  * and add this filter.
2735  */
2736 static GdkFilterReturn
2737 xdnd_source_window_filter (GdkXEvent *xev,
2738                            GdkEvent  *event,
2739                            gpointer   cb_data)
2740 {
2741   XEvent *xevent = (XEvent *)xev;
2742   GdkX11DragContext *context_x11 = cb_data;
2743   GdkDisplay *display = GDK_WINDOW_DISPLAY(event->any.window);
2744
2745   if ((xevent->xany.type == PropertyNotify) &&
2746       (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList")))
2747     {
2748       xdnd_read_actions (context_x11);
2749
2750       return GDK_FILTER_REMOVE;
2751     }
2752
2753   return GDK_FILTER_CONTINUE;
2754 }
2755
2756 static void
2757 xdnd_manage_source_filter (GdkDragContext *context,
2758                            GdkWindow      *window,
2759                            gboolean        add_filter)
2760 {
2761   if (!GDK_WINDOW_DESTROYED (window) &&
2762       gdk_window_get_window_type (window) == GDK_WINDOW_FOREIGN)
2763     {
2764       gdk_x11_display_error_trap_push (GDK_WINDOW_DISPLAY (window));
2765
2766       if (add_filter)
2767         {
2768           gdk_window_set_events (window,
2769                                  gdk_window_get_events (window) |
2770                                  GDK_PROPERTY_CHANGE_MASK);
2771           gdk_window_add_filter (window, xdnd_source_window_filter, context);
2772         }
2773       else
2774         {
2775           gdk_window_remove_filter (window,
2776                                     xdnd_source_window_filter,
2777                                     context);
2778           /* Should we remove the GDK_PROPERTY_NOTIFY mask?
2779            * but we might want it for other reasons. (Like
2780            * INCR selection transactions).
2781            */
2782         }
2783
2784       gdk_x11_display_error_trap_pop_ignored (GDK_WINDOW_DISPLAY (window));
2785     }
2786 }
2787
2788 static void
2789 base_precache_atoms (GdkDisplay *display)
2790 {
2791   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
2792
2793   if (!display_x11->base_dnd_atoms_precached)
2794     {
2795       static const char *const precache_atoms[] = {
2796         "ENLIGHTENMENT_DESKTOP",
2797         "WM_STATE",
2798         "XdndAware",
2799         "XdndProxy",
2800         "_MOTIF_DRAG_RECEIVER_INFO"
2801       };
2802
2803       _gdk_x11_precache_atoms (display,
2804                                precache_atoms, G_N_ELEMENTS (precache_atoms));
2805
2806       display_x11->base_dnd_atoms_precached = TRUE;
2807     }
2808 }
2809
2810 static void
2811 xdnd_precache_atoms (GdkDisplay *display)
2812 {
2813   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
2814
2815   if (!display_x11->xdnd_atoms_precached)
2816     {
2817       static const gchar *const precache_atoms[] = {
2818         "XdndActionAsk",
2819         "XdndActionCopy",
2820         "XdndActionLink",
2821         "XdndActionList",
2822         "XdndActionMove",
2823         "XdndActionPrivate",
2824         "XdndDrop",
2825         "XdndEnter",
2826         "XdndFinished",
2827         "XdndLeave",
2828         "XdndPosition",
2829         "XdndSelection",
2830         "XdndStatus",
2831         "XdndTypeList"
2832       };
2833
2834       _gdk_x11_precache_atoms (display,
2835                                precache_atoms, G_N_ELEMENTS (precache_atoms));
2836
2837       display_x11->xdnd_atoms_precached = TRUE;
2838     }
2839 }
2840
2841 static GdkFilterReturn
2842 xdnd_enter_filter (GdkXEvent *xev,
2843                    GdkEvent  *event,
2844                    gpointer   cb_data)
2845 {
2846   GdkDeviceManager *device_manager;
2847   GdkDisplay *display;
2848   GdkX11Display *display_x11;
2849   XEvent *xevent = (XEvent *)xev;
2850   GdkDragContext *context;
2851   GdkX11DragContext *context_x11;
2852   gint i;
2853   Atom type;
2854   int format;
2855   gulong nitems, after;
2856   guchar *data;
2857   Atom *atoms;
2858   guint32 source_window;
2859   gboolean get_types;
2860   gint version;
2861
2862   if (!event->any.window ||
2863       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2864     return GDK_FILTER_CONTINUE;                 /* Not for us */
2865
2866   source_window = xevent->xclient.data.l[0];
2867   get_types = ((xevent->xclient.data.l[1] & 1) != 0);
2868   version = (xevent->xclient.data.l[1] & 0xff000000) >> 24;
2869
2870   display = GDK_WINDOW_DISPLAY (event->any.window);
2871   display_x11 = GDK_X11_DISPLAY (display);
2872
2873   xdnd_precache_atoms (display);
2874
2875   GDK_NOTE (DND,
2876             g_message ("XdndEnter: source_window: %#x, version: %#x",
2877                        source_window, version));
2878
2879   if (version < 3)
2880     {
2881       /* Old source ignore */
2882       GDK_NOTE (DND, g_message ("Ignored old XdndEnter message"));
2883       return GDK_FILTER_REMOVE;
2884     }
2885
2886   if (display_x11->current_dest_drag != NULL)
2887     {
2888       g_object_unref (display_x11->current_dest_drag);
2889       display_x11->current_dest_drag = NULL;
2890     }
2891
2892   context_x11 = (GdkX11DragContext *)g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, NULL);
2893   context = (GdkDragContext *)context_x11;
2894
2895   context->protocol = GDK_DRAG_PROTO_XDND;
2896   context_x11->version = version;
2897
2898   /* FIXME: Should extend DnD protocol to have device info */
2899   device_manager = gdk_display_get_device_manager (display);
2900   gdk_drag_context_set_device (context, gdk_device_manager_get_client_pointer (device_manager));
2901
2902   context->source_window = gdk_x11_window_lookup_for_display (display, source_window);
2903   if (context->source_window)
2904     g_object_ref (context->source_window);
2905   else
2906     {
2907       context->source_window = gdk_x11_window_foreign_new_for_display (display, source_window);
2908       if (!context->source_window)
2909         {
2910           g_object_unref (context);
2911           return GDK_FILTER_REMOVE;
2912         }
2913     }
2914   context->dest_window = event->any.window;
2915   g_object_ref (context->dest_window);
2916
2917   context->targets = NULL;
2918   if (get_types)
2919     {
2920       gdk_x11_display_error_trap_push (display);
2921       XGetWindowProperty (GDK_WINDOW_XDISPLAY (event->any.window),
2922                           source_window,
2923                           gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
2924                           0, 65536,
2925                           False, XA_ATOM, &type, &format, &nitems,
2926                           &after, &data);
2927
2928       if (gdk_x11_display_error_trap_pop (display) || (format != 32) || (type != XA_ATOM))
2929         {
2930           g_object_unref (context);
2931
2932           if (data)
2933             XFree (data);
2934
2935           return GDK_FILTER_REMOVE;
2936         }
2937
2938       atoms = (Atom *)data;
2939
2940       for (i = 0; i < nitems; i++)
2941         context->targets =
2942           g_list_append (context->targets,
2943                          GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display,
2944                                                                                  atoms[i])));
2945
2946       XFree (atoms);
2947     }
2948   else
2949     {
2950       for (i = 0; i < 3; i++)
2951         if (xevent->xclient.data.l[2 + i])
2952           context->targets =
2953             g_list_append (context->targets,
2954                            GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display,
2955                                                                                    xevent->xclient.data.l[2 + i])));
2956     }
2957
2958 #ifdef G_ENABLE_DEBUG
2959   if (_gdk_debug_flags & GDK_DEBUG_DND)
2960     print_target_list (context->targets);
2961 #endif /* G_ENABLE_DEBUG */
2962
2963   xdnd_manage_source_filter (context, context->source_window, TRUE);
2964   xdnd_read_actions (context_x11);
2965
2966   event->dnd.type = GDK_DRAG_ENTER;
2967   event->dnd.context = context;
2968   gdk_event_set_device (event, gdk_drag_context_get_device (context));
2969   g_object_ref (context);
2970
2971   display_x11->current_dest_drag = context;
2972
2973   return GDK_FILTER_TRANSLATE;
2974 }
2975
2976 static GdkFilterReturn
2977 xdnd_leave_filter (GdkXEvent *xev,
2978                    GdkEvent  *event,
2979                    gpointer   data)
2980 {
2981   XEvent *xevent = (XEvent *)xev;
2982   guint32 source_window = xevent->xclient.data.l[0];
2983   GdkDisplay *display;
2984   GdkX11Display *display_x11;
2985
2986   if (!event->any.window ||
2987       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2988     return GDK_FILTER_CONTINUE;                 /* Not for us */
2989
2990   GDK_NOTE (DND,
2991             g_message ("XdndLeave: source_window: %#x",
2992                        source_window));
2993
2994   display = GDK_WINDOW_DISPLAY (event->any.window);
2995   display_x11 = GDK_X11_DISPLAY (display);
2996
2997   xdnd_precache_atoms (display);
2998
2999   if ((display_x11->current_dest_drag != NULL) &&
3000       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
3001       (GDK_WINDOW_XID (display_x11->current_dest_drag->source_window) == source_window))
3002     {
3003       event->dnd.type = GDK_DRAG_LEAVE;
3004       /* Pass ownership of context to the event */
3005       event->dnd.context = display_x11->current_dest_drag;
3006       gdk_event_set_device (event, gdk_drag_context_get_device (event->dnd.context));
3007
3008       display_x11->current_dest_drag = NULL;
3009
3010       return GDK_FILTER_TRANSLATE;
3011     }
3012   else
3013     return GDK_FILTER_REMOVE;
3014 }
3015
3016 static GdkFilterReturn
3017 xdnd_position_filter (GdkXEvent *xev,
3018                       GdkEvent  *event,
3019                       gpointer   data)
3020 {
3021   XEvent *xevent = (XEvent *)xev;
3022   guint32 source_window = xevent->xclient.data.l[0];
3023   gint16 x_root = xevent->xclient.data.l[2] >> 16;
3024   gint16 y_root = xevent->xclient.data.l[2] & 0xffff;
3025   guint32 time = xevent->xclient.data.l[3];
3026   Atom action = xevent->xclient.data.l[4];
3027   GdkDisplay *display;
3028   GdkX11Display *display_x11;
3029   GdkDragContext *context;
3030   GdkX11DragContext *context_x11;
3031
3032    if (!event->any.window ||
3033        gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
3034      return GDK_FILTER_CONTINUE;                        /* Not for us */
3035
3036   GDK_NOTE (DND,
3037             g_message ("XdndPosition: source_window: %#x position: (%d, %d)  time: %d  action: %ld",
3038                        source_window, x_root, y_root, time, action));
3039
3040   display = GDK_WINDOW_DISPLAY (event->any.window);
3041   display_x11 = GDK_X11_DISPLAY (display);
3042
3043   xdnd_precache_atoms (display);
3044
3045   context = display_x11->current_dest_drag;
3046
3047   if ((context != NULL) &&
3048       (context->protocol == GDK_DRAG_PROTO_XDND) &&
3049       (GDK_WINDOW_XID (context->source_window) == source_window))
3050     {
3051       context_x11 = GDK_X11_DRAG_CONTEXT (context);
3052
3053       event->dnd.type = GDK_DRAG_MOTION;
3054       event->dnd.context = context;
3055       gdk_event_set_device (event, gdk_drag_context_get_device (context));
3056       g_object_ref (context);
3057
3058       event->dnd.time = time;
3059
3060       context->suggested_action = xdnd_action_from_atom (display, action);
3061
3062       if (!context_x11->xdnd_have_actions)
3063         context->actions = context->suggested_action;
3064
3065       event->dnd.x_root = x_root;
3066       event->dnd.y_root = y_root;
3067
3068       context_x11->last_x = x_root;
3069       context_x11->last_y = y_root;
3070
3071       return GDK_FILTER_TRANSLATE;
3072     }
3073
3074   return GDK_FILTER_REMOVE;
3075 }
3076
3077 static GdkFilterReturn
3078 xdnd_drop_filter (GdkXEvent *xev,
3079                   GdkEvent  *event,
3080                   gpointer   data)
3081 {
3082   XEvent *xevent = (XEvent *)xev;
3083   guint32 source_window = xevent->xclient.data.l[0];
3084   guint32 time = xevent->xclient.data.l[2];
3085   GdkDisplay *display;
3086   GdkX11Display *display_x11;
3087   GdkDragContext *context;
3088   GdkX11DragContext *context_x11;
3089
3090   if (!event->any.window ||
3091       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
3092     return GDK_FILTER_CONTINUE;                 /* Not for us */
3093
3094   GDK_NOTE (DND,
3095             g_message ("XdndDrop: source_window: %#x  time: %d",
3096                        source_window, time));
3097
3098   display = GDK_WINDOW_DISPLAY (event->any.window);
3099   display_x11 = GDK_X11_DISPLAY (display);
3100
3101   xdnd_precache_atoms (display);
3102
3103   context = display_x11->current_dest_drag;
3104
3105   if ((context != NULL) &&
3106       (context->protocol == GDK_DRAG_PROTO_XDND) &&
3107       (GDK_WINDOW_XID (context->source_window) == source_window))
3108     {
3109       context_x11 = GDK_X11_DRAG_CONTEXT (context);
3110       event->dnd.type = GDK_DROP_START;
3111
3112       event->dnd.context = context;
3113       gdk_event_set_device (event, gdk_drag_context_get_device (context));
3114       g_object_ref (context);
3115
3116       event->dnd.time = time;
3117       event->dnd.x_root = context_x11->last_x;
3118       event->dnd.y_root = context_x11->last_y;
3119
3120       gdk_x11_window_set_user_time (event->any.window, time);
3121
3122       return GDK_FILTER_TRANSLATE;
3123     }
3124
3125   return GDK_FILTER_REMOVE;
3126 }
3127
3128 GdkFilterReturn
3129 _gdk_x11_dnd_filter (GdkXEvent *xev,
3130                      GdkEvent  *event,
3131                      gpointer   data)
3132 {
3133   XEvent *xevent = (XEvent *) xev;
3134   GdkDisplay *display;
3135   int i;
3136
3137   if (!GDK_IS_X11_WINDOW (event->any.window))
3138     return GDK_FILTER_CONTINUE;
3139
3140   if (xevent->type != ClientMessage)
3141     return GDK_FILTER_CONTINUE;
3142
3143   display = GDK_WINDOW_DISPLAY (event->any.window);
3144
3145   for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
3146     {
3147       if (xevent->xclient.message_type != gdk_x11_get_xatom_by_name_for_display (display, xdnd_filters[i].atom_name))
3148         continue;
3149
3150       return xdnd_filters[i].func (xev, event, data);
3151     }
3152
3153   return GDK_FILTER_CONTINUE;
3154 }
3155
3156 /* Source side */
3157
3158 static void
3159 gdk_drag_do_leave (GdkX11DragContext *context_x11,
3160                    guint32            time)
3161 {
3162   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
3163
3164   if (context->dest_window)
3165     {
3166       switch (context->protocol)
3167         {
3168         case GDK_DRAG_PROTO_MOTIF:
3169           motif_send_leave (context_x11, time);
3170           break;
3171         case GDK_DRAG_PROTO_XDND:
3172           xdnd_send_leave (context_x11);
3173           break;
3174         case GDK_DRAG_PROTO_ROOTWIN:
3175         case GDK_DRAG_PROTO_NONE:
3176         default:
3177           break;
3178         }
3179
3180       g_object_unref (context->dest_window);
3181       context->dest_window = NULL;
3182     }
3183 }
3184
3185 GdkDragContext *
3186 _gdk_x11_window_drag_begin (GdkWindow *window,
3187                             GdkDevice *device,
3188                             GList     *targets)
3189 {
3190   GdkDragContext *context;
3191
3192   context = (GdkDragContext *) g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, NULL);
3193
3194   context->is_source = TRUE;
3195   context->source_window = window;
3196   g_object_ref (window);
3197
3198   context->targets = g_list_copy (targets);
3199   precache_target_list (context);
3200
3201   context->actions = 0;
3202
3203   gdk_drag_context_set_device (context, device);
3204
3205   return context;
3206 }
3207
3208 GdkNativeWindow
3209 _gdk_x11_display_get_drag_protocol (GdkDisplay      *display,
3210                                     GdkNativeWindow  xid,
3211                                     GdkDragProtocol *protocol,
3212                                     guint           *version)
3213
3214 {
3215   GdkWindow *window;
3216   GdkNativeWindow retval;
3217
3218   base_precache_atoms (display);
3219
3220   /* Check for a local drag */
3221   window = gdk_x11_window_lookup_for_display (display, xid);
3222   if (window && gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
3223     {
3224       if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
3225         {
3226           *protocol = GDK_DRAG_PROTO_XDND;
3227           *version = 5;
3228           xdnd_precache_atoms (display);
3229           GDK_NOTE (DND, g_message ("Entering local Xdnd window %#x\n", xid));
3230           return xid;
3231         }
3232       else if (_gdk_x11_display_is_root_window (display, (Window) xid))
3233         {
3234           *protocol = GDK_DRAG_PROTO_ROOTWIN;
3235           GDK_NOTE (DND, g_message ("Entering root window\n"));
3236           return xid;
3237         }
3238     }
3239   else if ((retval = xdnd_check_dest (display, xid, version)))
3240     {
3241       *protocol = GDK_DRAG_PROTO_XDND;
3242       xdnd_precache_atoms (display);
3243       GDK_NOTE (DND, g_message ("Entering Xdnd window %#x\n", xid));
3244       return retval;
3245     }
3246   else if ((retval = motif_check_dest (display, xid)))
3247     {
3248       *protocol = GDK_DRAG_PROTO_MOTIF;
3249       GDK_NOTE (DND, g_message ("Entering motif window %#x\n", xid));
3250       return retval;
3251     }
3252   else
3253     {
3254       /* Check if this is a root window */
3255       gboolean rootwin = FALSE;
3256
3257       if (_gdk_x11_display_is_root_window (display, (Window) xid))
3258         rootwin = TRUE;
3259
3260       if (rootwin)
3261         {
3262           GDK_NOTE (DND, g_message ("Entering root window\n"));
3263           *protocol = GDK_DRAG_PROTO_ROOTWIN;
3264           return xid;
3265         }
3266     }
3267
3268   *protocol = GDK_DRAG_PROTO_NONE;
3269
3270   return 0; /* a.k.a. None */
3271 }
3272
3273 static GdkWindowCache *
3274 drag_context_find_window_cache (GdkX11DragContext *context_x11,
3275                                 GdkScreen         *screen)
3276 {
3277   GSList *list;
3278   GdkWindowCache *cache;
3279
3280   for (list = context_x11->window_caches; list; list = list->next)
3281     {
3282       cache = list->data;
3283       if (cache->screen == screen)
3284         return cache;
3285     }
3286
3287   cache = gdk_window_cache_get (screen);
3288   context_x11->window_caches = g_slist_prepend (context_x11->window_caches, cache);
3289
3290   return cache;
3291 }
3292
3293 static GdkWindow *
3294 gdk_x11_drag_context_find_window (GdkDragContext  *context,
3295                                   GdkWindow       *drag_window,
3296                                   GdkScreen       *screen,
3297                                   gint             x_root,
3298                                   gint             y_root,
3299                                   GdkDragProtocol *protocol)
3300 {
3301   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3302   GdkWindowCache *window_cache;
3303   GdkDisplay *display;
3304   Window dest;
3305   GdkWindow *dest_window;
3306
3307   display = GDK_WINDOW_DISPLAY (context->source_window);
3308
3309   window_cache = drag_context_find_window_cache (context_x11, screen);
3310
3311   dest = get_client_window_at_coords (window_cache,
3312                                       drag_window && GDK_WINDOW_IS_X11 (drag_window) ?
3313                                       GDK_WINDOW_XID (drag_window) : None,
3314                                       x_root, y_root);
3315
3316   if (context_x11->dest_xid != dest)
3317     {
3318       Window recipient;
3319       context_x11->dest_xid = dest;
3320
3321       /* Check if new destination accepts drags, and which protocol */
3322
3323       /* There is some ugliness here. We actually need to pass
3324        * _three_ pieces of information to drag_motion - dest_window,
3325        * protocol, and the XID of the unproxied window. The first
3326        * two are passed explicitely, the third implicitly through
3327        * protocol->dest_xid.
3328        */
3329       recipient = _gdk_x11_display_get_drag_protocol (display,
3330                                                       dest,
3331                                                       protocol,
3332                                                       &context_x11->version);
3333
3334       if (recipient != None)
3335         {
3336           dest_window = gdk_x11_window_lookup_for_display (display, recipient);
3337           if (dest_window)
3338             g_object_ref (dest_window);
3339           else
3340             dest_window = gdk_x11_window_foreign_new_for_display (display, recipient);
3341         }
3342       else
3343         dest_window = NULL;
3344     }
3345   else
3346     {
3347       dest_window = context->dest_window;
3348       if (dest_window)
3349         g_object_ref (dest_window);
3350       *protocol = context->protocol;
3351     }
3352
3353   return dest_window;
3354 }
3355
3356 static gboolean
3357 gdk_x11_drag_context_drag_motion (GdkDragContext *context,
3358                                   GdkWindow      *dest_window,
3359                                   GdkDragProtocol protocol,
3360                                   gint            x_root,
3361                                   gint            y_root,
3362                                   GdkDragAction   suggested_action,
3363                                   GdkDragAction   possible_actions,
3364                                   guint32         time)
3365 {
3366   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3367
3368   context_x11->old_actions = context->actions;
3369   context->actions = possible_actions;
3370
3371   if (context_x11->old_actions != possible_actions)
3372     context_x11->xdnd_actions_set = FALSE;
3373
3374   if (protocol == GDK_DRAG_PROTO_XDND && context_x11->version == 0)
3375     {
3376       /* This ugly hack is necessary since GTK+ doesn't know about
3377        * the XDND protocol version, and in particular doesn't know
3378        * that gdk_drag_find_window_for_screen() has the side-effect
3379        * of setting context_x11->version, and therefore sometimes call
3380        * gdk_drag_motion() without a prior call to
3381        * gdk_drag_find_window_for_screen(). This happens, e.g.
3382        * when GTK+ is proxying DND events to embedded windows.
3383        */
3384       if (dest_window)
3385         {
3386           GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
3387
3388           xdnd_check_dest (display,
3389                            GDK_WINDOW_XID (dest_window),
3390                            &context_x11->version);
3391         }
3392     }
3393
3394   /* When we have a Xdnd target, make sure our XdndActionList
3395    * matches the current actions;
3396    */
3397   if (protocol == GDK_DRAG_PROTO_XDND && !context_x11->xdnd_actions_set)
3398     {
3399       if (dest_window)
3400         {
3401           if (gdk_window_get_window_type (dest_window) == GDK_WINDOW_FOREIGN)
3402             xdnd_set_actions (context_x11);
3403           else if (context->dest_window == dest_window)
3404             {
3405               GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
3406               GdkDragContext *dest_context;
3407
3408               dest_context = gdk_drag_context_find (display, FALSE,
3409                                                     GDK_WINDOW_XID (context->source_window),
3410                                                     GDK_WINDOW_XID (dest_window));
3411
3412               if (dest_context)
3413                 {
3414                   dest_context->actions = context->actions;
3415                   GDK_X11_DRAG_CONTEXT (dest_context)->xdnd_have_actions = TRUE;
3416                 }
3417             }
3418         }
3419     }
3420
3421   if (context->dest_window != dest_window)
3422     {
3423       GdkEvent *temp_event;
3424
3425       /* Send a leave to the last destination */
3426       gdk_drag_do_leave (context_x11, time);
3427       context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
3428
3429       /* Check if new destination accepts drags, and which protocol */
3430
3431       if (dest_window)
3432         {
3433           context->dest_window = dest_window;
3434           context_x11->drop_xid = context_x11->dest_xid;
3435           g_object_ref (context->dest_window);
3436           context->protocol = protocol;
3437
3438           switch (protocol)
3439             {
3440             case GDK_DRAG_PROTO_MOTIF:
3441               motif_send_enter (context_x11, time);
3442               break;
3443
3444             case GDK_DRAG_PROTO_XDND:
3445               xdnd_send_enter (context_x11);
3446               break;
3447
3448             case GDK_DRAG_PROTO_ROOTWIN:
3449             case GDK_DRAG_PROTO_NONE:
3450             default:
3451               break;
3452             }
3453           context_x11->old_action = suggested_action;
3454           context->suggested_action = suggested_action;
3455           context_x11->old_actions = possible_actions;
3456         }
3457       else
3458         {
3459           context->dest_window = NULL;
3460           context_x11->drop_xid = None;
3461           context->action = 0;
3462         }
3463
3464       /* Push a status event, to let the client know that
3465        * the drag changed
3466        */
3467       temp_event = gdk_event_new (GDK_DRAG_STATUS);
3468       temp_event->dnd.window = g_object_ref (context->source_window);
3469       /* We use this to signal a synthetic status. Perhaps
3470        * we should use an extra field...
3471        */
3472       temp_event->dnd.send_event = TRUE;
3473
3474       temp_event->dnd.context = g_object_ref (context);
3475       temp_event->dnd.time = time;
3476       gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
3477
3478       gdk_event_put (temp_event);
3479       gdk_event_free (temp_event);
3480     }
3481   else
3482     {
3483       context_x11->old_action = context->suggested_action;
3484       context->suggested_action = suggested_action;
3485     }
3486
3487   /* Send a drag-motion event */
3488
3489   context_x11->last_x = x_root;
3490   context_x11->last_y = y_root;
3491
3492   if (context->dest_window)
3493     {
3494       if (context_x11->drag_status == GDK_DRAG_STATUS_DRAG)
3495         {
3496           switch (context->protocol)
3497             {
3498             case GDK_DRAG_PROTO_MOTIF:
3499               motif_send_motion (context_x11, x_root, y_root, suggested_action, time);
3500               break;
3501
3502             case GDK_DRAG_PROTO_XDND:
3503               xdnd_send_motion (context_x11, x_root, y_root, suggested_action, time);
3504               break;
3505
3506             case GDK_DRAG_PROTO_ROOTWIN:
3507               {
3508                 GdkEvent *temp_event;
3509                 /* GTK+ traditionally has used application/x-rootwin-drop,
3510                  * but the XDND spec specifies x-rootwindow-drop.
3511                  */
3512                 GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3513                 GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3514
3515                 if (g_list_find (context->targets,
3516                                  GDK_ATOM_TO_POINTER (target1)) ||
3517                     g_list_find (context->targets,
3518                                  GDK_ATOM_TO_POINTER (target2)))
3519                   context->action = context->suggested_action;
3520                 else
3521                   context->action = 0;
3522
3523                 temp_event = gdk_event_new (GDK_DRAG_STATUS);
3524                 temp_event->dnd.window = g_object_ref (context->source_window);
3525                 temp_event->dnd.send_event = FALSE;
3526                 temp_event->dnd.context = g_object_ref (context);
3527                 temp_event->dnd.time = time;
3528                 gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
3529
3530                 gdk_event_put (temp_event);
3531                 gdk_event_free (temp_event);
3532               }
3533               break;
3534             case GDK_DRAG_PROTO_NONE:
3535               g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
3536               break;
3537             default:
3538               break;
3539             }
3540         }
3541       else
3542         return TRUE;
3543     }
3544
3545   return FALSE;
3546 }
3547
3548 static void
3549 gdk_x11_drag_context_drag_abort (GdkDragContext *context,
3550                                  guint32         time)
3551 {
3552   gdk_drag_do_leave (GDK_X11_DRAG_CONTEXT (context), time);
3553 }
3554
3555 static void
3556 gdk_x11_drag_context_drag_drop (GdkDragContext *context,
3557                                 guint32         time)
3558 {
3559   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3560
3561   if (context->dest_window)
3562     {
3563       switch (context->protocol)
3564         {
3565         case GDK_DRAG_PROTO_MOTIF:
3566           motif_send_leave (context_x11, time);
3567           motif_send_drop (context_x11, time);
3568           break;
3569
3570         case GDK_DRAG_PROTO_XDND:
3571           xdnd_send_drop (context_x11, time);
3572           break;
3573
3574         case GDK_DRAG_PROTO_ROOTWIN:
3575           g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
3576           break;
3577         case GDK_DRAG_PROTO_NONE:
3578           g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
3579           break;
3580         default:
3581           break;
3582         }
3583     }
3584 }
3585
3586 /* Destination side */
3587
3588 static void
3589 gdk_x11_drag_context_drag_status (GdkDragContext *context,
3590                                   GdkDragAction   action,
3591                                   guint32         time_)
3592 {
3593   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3594   XEvent xev;
3595   GdkDisplay *display;
3596
3597   display = GDK_WINDOW_DISPLAY (context->source_window);
3598
3599   context->action = action;
3600
3601   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3602     {
3603       gboolean need_coords = FALSE;
3604
3605       xev.xclient.type = ClientMessage;
3606       xev.xclient.message_type =
3607         gdk_x11_get_xatom_by_name_for_display (display,
3608                                                "_MOTIF_DRAG_AND_DROP_MESSAGE");
3609       xev.xclient.format = 8;
3610       xev.xclient.window = GDK_WINDOW_XID (context->source_window);
3611
3612       if (context_x11->drag_status == GDK_DRAG_STATUS_ACTION_WAIT)
3613         {
3614           MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED | 0x80;
3615         }
3616       else
3617         {
3618           if ((action != 0) != (context_x11->old_action != 0))
3619             {
3620               if (action != 0)
3621                 {
3622                   MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_ENTER | 0x80;
3623                   need_coords = TRUE;
3624                 }
3625               else
3626                 {
3627                   MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_LEAVE | 0x80;
3628                 }
3629             }
3630           else
3631             {
3632               MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION | 0x80;
3633               need_coords = TRUE;
3634             }
3635         }
3636
3637       MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3638
3639       switch (action)
3640         {
3641         case GDK_ACTION_MOVE:
3642           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_MOVE;
3643           break;
3644         case GDK_ACTION_COPY:
3645           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY;
3646           break;
3647         case GDK_ACTION_LINK:
3648           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_LINK;
3649           break;
3650         default:
3651           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP;
3652           break;
3653         }
3654
3655       if (action)
3656         MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmDROP_SITE_VALID << 4);
3657       else
3658         MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmNO_DROP_SITE << 4);
3659
3660       MOTIF_XCLIENT_LONG (&xev, 1) = time_;
3661
3662       if (need_coords)
3663         {
3664           MOTIF_XCLIENT_SHORT (&xev, 4) = context_x11->last_x;
3665           MOTIF_XCLIENT_SHORT (&xev, 5) = context_x11->last_y;
3666         }
3667       else
3668         MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3669
3670       MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3671       MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3672
3673       if (!_gdk_x11_display_send_xevent (display,
3674                                          GDK_WINDOW_XID (context->source_window),
3675                                          FALSE, 0, &xev))
3676         GDK_NOTE (DND,
3677                   g_message ("Send event to %lx failed",
3678                              GDK_WINDOW_XID (context->source_window)));
3679     }
3680   else if (context->protocol == GDK_DRAG_PROTO_XDND)
3681     {
3682       xev.xclient.type = ClientMessage;
3683       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus");
3684       xev.xclient.format = 32;
3685       xev.xclient.window = GDK_WINDOW_XID (context->source_window);
3686
3687       xev.xclient.data.l[0] = GDK_WINDOW_XID (context->dest_window);
3688       xev.xclient.data.l[1] = (action != 0) ? (2 | 1) : 0;
3689       xev.xclient.data.l[2] = 0;
3690       xev.xclient.data.l[3] = 0;
3691       xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
3692       if (!xdnd_send_xevent (context_x11, context->source_window, FALSE, &xev))
3693         GDK_NOTE (DND,
3694                   g_message ("Send event to %lx failed",
3695                              GDK_WINDOW_XID (context->source_window)));
3696     }
3697
3698   context_x11->old_action = action;
3699 }
3700
3701 static void
3702 gdk_x11_drag_context_drop_reply (GdkDragContext *context,
3703                                  gboolean        accepted,
3704                                  guint32         time_)
3705 {
3706   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3707
3708   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3709     {
3710       GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
3711       XEvent xev;
3712
3713       xev.xclient.type = ClientMessage;
3714       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
3715                                                                         "_MOTIF_DRAG_AND_DROP_MESSAGE");
3716       xev.xclient.format = 8;
3717
3718       MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START | 0x80;
3719       MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3720       if (accepted)
3721         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY |
3722                                        (XmDROP_SITE_VALID << 4) |
3723                                        (XmDROP_NOOP << 8) |
3724                                        (XmDROP << 12);
3725       else
3726         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP |
3727                                        (XmNO_DROP_SITE << 4) |
3728                                        (XmDROP_NOOP << 8) |
3729                                        (XmDROP_CANCEL << 12);
3730       MOTIF_XCLIENT_SHORT (&xev, 2) = context_x11->last_x;
3731       MOTIF_XCLIENT_SHORT (&xev, 3) = context_x11->last_y;
3732       MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3733       MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3734       MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3735
3736       _gdk_x11_display_send_xevent (display,
3737                                     GDK_WINDOW_XID (context->source_window),
3738                                     FALSE, 0, &xev);
3739     }
3740 }
3741
3742 static void
3743 gdk_x11_drag_context_drop_finish (GdkDragContext *context,
3744                                   gboolean        success,
3745                                   guint32         time)
3746 {
3747   if (context->protocol == GDK_DRAG_PROTO_XDND)
3748     {
3749       GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
3750       XEvent xev;
3751
3752       xev.xclient.type = ClientMessage;
3753       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished");
3754       xev.xclient.format = 32;
3755       xev.xclient.window = GDK_WINDOW_XID (context->source_window);
3756
3757       xev.xclient.data.l[0] = GDK_WINDOW_XID (context->dest_window);
3758       if (success)
3759         {
3760           xev.xclient.data.l[1] = 1;
3761           xev.xclient.data.l[2] = xdnd_action_to_atom (display,
3762                                                        context->action);
3763         }
3764       else
3765         {
3766           xev.xclient.data.l[1] = 0;
3767           xev.xclient.data.l[2] = None;
3768         }
3769       xev.xclient.data.l[3] = 0;
3770       xev.xclient.data.l[4] = 0;
3771
3772       if (!xdnd_send_xevent (GDK_X11_DRAG_CONTEXT (context), context->source_window, FALSE, &xev))
3773         GDK_NOTE (DND,
3774                   g_message ("Send event to %lx failed",
3775                              GDK_WINDOW_XID (context->source_window)));
3776     }
3777 }
3778
3779 void
3780 _gdk_x11_window_register_dnd (GdkWindow *window)
3781 {
3782   static const gulong xdnd_version = 5;
3783   MotifDragReceiverInfo info;
3784   Atom motif_drag_receiver_info_atom;
3785   GdkDisplay *display = gdk_window_get_display (window);
3786
3787   g_return_if_fail (window != NULL);
3788
3789   if (gdk_window_get_window_type (window) == GDK_WINDOW_OFFSCREEN)
3790     return;
3791
3792   base_precache_atoms (display);
3793
3794   if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
3795     return;
3796   else
3797     g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
3798
3799   /* Set Motif drag receiver information property */
3800   motif_drag_receiver_info_atom = gdk_x11_get_xatom_by_name_for_display (display,
3801                                                                          "_MOTIF_DRAG_RECEIVER_INFO");
3802   /* initialize to zero to avoid writing uninitialized data to socket */
3803   memset(&info, 0, sizeof(info));
3804   info.byte_order = local_byte_order;
3805   info.protocol_version = 0;
3806   info.protocol_style = XmDRAG_DYNAMIC;
3807   info.proxy_window = None;
3808   info.num_drop_sites = 0;
3809   info.total_size = sizeof(info);
3810
3811   XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
3812                    GDK_WINDOW_XID (window),
3813                    motif_drag_receiver_info_atom,
3814                    motif_drag_receiver_info_atom,
3815                    8, PropModeReplace,
3816                    (guchar *)&info,
3817                    sizeof (info));
3818
3819   /* Set XdndAware */
3820
3821   /* The property needs to be of type XA_ATOM, not XA_INTEGER. Blech */
3822   XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
3823                    GDK_WINDOW_XID (window),
3824                    gdk_x11_get_xatom_by_name_for_display (display, "XdndAware"),
3825                    XA_ATOM, 32, PropModeReplace,
3826                    (guchar *)&xdnd_version, 1);
3827 }
3828
3829 static GdkAtom
3830 gdk_x11_drag_context_get_selection (GdkDragContext *context)
3831 {
3832   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3833     return gdk_x11_xatom_to_atom_for_display (GDK_WINDOW_DISPLAY (context->source_window),
3834                                               (GDK_X11_DRAG_CONTEXT (context))->motif_selection);
3835   else if (context->protocol == GDK_DRAG_PROTO_XDND)
3836     return gdk_atom_intern_static_string ("XdndSelection");
3837   else
3838     return GDK_NONE;
3839 }
3840
3841 static gboolean
3842 gdk_x11_drag_context_drop_status (GdkDragContext *context)
3843 {
3844   return ! GDK_X11_DRAG_CONTEXT (context)->drop_failed;
3845 }