]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkdnd-x11.c
gdkframeclock: Fix doc comments
[~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, see <http://www.gnu.org/licenses/>.
16  */
17
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
23  */
24
25 #include "config.h"
26
27 #include "gdkx11dnd.h"
28 #include "gdkdndprivate.h"
29
30 #include "gdkmain.h"
31 #include "gdkinternals.h"
32 #include "gdkasync.h"
33 #include "gdkproperty.h"
34 #include "gdkprivate-x11.h"
35 #include "gdkscreen-x11.h"
36 #include "gdkdisplay-x11.h"
37
38 #include <X11/Xlib.h>
39 #include <X11/Xutil.h>
40 #include <X11/Xatom.h>
41 #include <X11/extensions/shape.h>
42 #ifdef HAVE_XCOMPOSITE
43 #include <X11/extensions/Xcomposite.h>
44 #endif
45
46 #include <string.h>
47
48 typedef enum {
49   GDK_DRAG_STATUS_DRAG,
50   GDK_DRAG_STATUS_MOTION_WAIT,
51   GDK_DRAG_STATUS_ACTION_WAIT,
52   GDK_DRAG_STATUS_DROP
53 } GtkDragStatus;
54
55 typedef struct {
56   guint32 xid;
57   gint x, y, width, height;
58   gboolean mapped;
59   gboolean shape_selected;
60   gboolean shape_valid;
61   cairo_region_t *shape;
62 } GdkCacheChild;
63
64 typedef struct {
65   GList *children;
66   GHashTable *child_hash;
67   guint old_event_mask;
68   GdkScreen *screen;
69   gint ref_count;
70 } GdkWindowCache;
71
72
73 struct _GdkX11DragContext
74 {
75   GdkDragContext context;
76
77   Atom motif_selection;
78   guint   ref_count;
79
80   guint16 last_x;              /* Coordinates from last event */
81   guint16 last_y;
82   GdkDragAction old_action;    /* The last action we sent to the source */
83   GdkDragAction old_actions;   /* The last actions we sent to the source */
84   GdkDragAction xdnd_actions;  /* What is currently set in XdndActionList */
85   guint version;               /* Xdnd protocol version */
86
87   GSList *window_caches;
88
89   Window dest_xid;             /* The last window we looked up */
90   Window drop_xid;             /* The (non-proxied) window that is receiving drops */
91   guint xdnd_targets_set  : 1; /* Whether we've already set XdndTypeList */
92   guint xdnd_actions_set  : 1; /* Whether we've already set XdndActionList */
93   guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
94   guint motif_targets_set : 1; /* Whether we've already set motif initiator info */
95   guint drag_status       : 4; /* current status of drag */
96   guint drop_failed       : 1; /* Whether the drop was unsuccessful */
97 };
98
99 struct _GdkX11DragContextClass
100 {
101   GdkDragContextClass parent_class;
102 };
103
104 /* Forward declarations */
105
106 static GdkWindowCache *gdk_window_cache_get   (GdkScreen      *screen);
107 static GdkWindowCache *gdk_window_cache_ref   (GdkWindowCache *cache);
108 static void            gdk_window_cache_unref (GdkWindowCache *cache);
109
110 static void motif_read_target_table (GdkDisplay *display);
111
112 static GdkFilterReturn motif_dnd_filter (GdkXEvent *xev,
113                                          GdkEvent  *event,
114                                          gpointer   data);
115
116 static GdkFilterReturn xdnd_enter_filter    (GdkXEvent *xev,
117                                              GdkEvent  *event,
118                                              gpointer   data);
119 static GdkFilterReturn xdnd_leave_filter    (GdkXEvent *xev,
120                                              GdkEvent  *event,
121                                              gpointer   data);
122 static GdkFilterReturn xdnd_position_filter (GdkXEvent *xev,
123                                              GdkEvent  *event,
124                                              gpointer   data);
125 static GdkFilterReturn xdnd_status_filter   (GdkXEvent *xev,
126                                              GdkEvent  *event,
127                                              gpointer   data);
128 static GdkFilterReturn xdnd_finished_filter (GdkXEvent *xev,
129                                              GdkEvent  *event,
130                                              gpointer   data);
131 static GdkFilterReturn xdnd_drop_filter     (GdkXEvent *xev,
132                                              GdkEvent  *event,
133                                              gpointer   data);
134
135 static void   xdnd_manage_source_filter (GdkDragContext *context,
136                                          GdkWindow      *window,
137                                          gboolean        add_filter);
138
139 static GList *contexts;
140 static GSList *window_caches;
141
142 static const struct {
143   const char *atom_name;
144   GdkFilterFunc func;
145 } xdnd_filters[] = {
146   { "_MOTIF_DRAG_AND_DROP_MESSAGE", motif_dnd_filter },
147
148   { "XdndEnter",                    xdnd_enter_filter },
149   { "XdndLeave",                    xdnd_leave_filter },
150   { "XdndPosition",                 xdnd_position_filter },
151   { "XdndStatus",                   xdnd_status_filter },
152   { "XdndFinished",                 xdnd_finished_filter },
153   { "XdndDrop",                     xdnd_drop_filter },
154 };
155
156
157 G_DEFINE_TYPE (GdkX11DragContext, gdk_x11_drag_context, GDK_TYPE_DRAG_CONTEXT)
158
159 static void
160 gdk_x11_drag_context_init (GdkX11DragContext *context)
161 {
162   contexts = g_list_prepend (contexts, context);
163 }
164
165 static void        gdk_x11_drag_context_finalize (GObject *object);
166 static GdkWindow * gdk_x11_drag_context_find_window (GdkDragContext  *context,
167                                                      GdkWindow       *drag_window,
168                                                      GdkScreen       *screen,
169                                                      gint             x_root,
170                                                      gint             y_root,
171                                                      GdkDragProtocol *protocol);
172 static gboolean    gdk_x11_drag_context_drag_motion (GdkDragContext  *context,
173                                                      GdkWindow       *dest_window,
174                                                      GdkDragProtocol  protocol,
175                                                      gint             x_root,
176                                                      gint             y_root,
177                                                      GdkDragAction    suggested_action,
178                                                      GdkDragAction    possible_actions,
179                                                      guint32          time);
180 static void        gdk_x11_drag_context_drag_status (GdkDragContext  *context,
181                                                      GdkDragAction    action,
182                                                      guint32          time_);
183 static void        gdk_x11_drag_context_drag_abort  (GdkDragContext  *context,
184                                                      guint32          time_);
185 static void        gdk_x11_drag_context_drag_drop   (GdkDragContext  *context,
186                                                      guint32          time_);
187 static void        gdk_x11_drag_context_drop_reply  (GdkDragContext  *context,
188                                                      gboolean         accept,
189                                                      guint32          time_);
190 static void        gdk_x11_drag_context_drop_finish (GdkDragContext  *context,
191                                                      gboolean         success,
192                                                      guint32          time_);
193 static gboolean    gdk_x11_drag_context_drop_status (GdkDragContext  *context);
194 static GdkAtom     gdk_x11_drag_context_get_selection (GdkDragContext  *context);
195
196 static void
197 gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass)
198 {
199   GObjectClass *object_class = G_OBJECT_CLASS (klass);
200   GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
201
202   object_class->finalize = gdk_x11_drag_context_finalize;
203
204   context_class->find_window = gdk_x11_drag_context_find_window;
205   context_class->drag_status = gdk_x11_drag_context_drag_status;
206   context_class->drag_motion = gdk_x11_drag_context_drag_motion;
207   context_class->drag_abort = gdk_x11_drag_context_drag_abort;
208   context_class->drag_drop = gdk_x11_drag_context_drag_drop;
209   context_class->drop_reply = gdk_x11_drag_context_drop_reply;
210   context_class->drop_finish = gdk_x11_drag_context_drop_finish;
211   context_class->drop_status = gdk_x11_drag_context_drop_status;
212   context_class->get_selection = gdk_x11_drag_context_get_selection;
213 }
214
215 static void
216 gdk_x11_drag_context_finalize (GObject *object)
217 {
218   GdkDragContext *context = GDK_DRAG_CONTEXT (object);
219   GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (object);
220
221   if (context->source_window)
222     {
223       if ((context->protocol == GDK_DRAG_PROTO_XDND) && !context->is_source)
224         xdnd_manage_source_filter (context, context->source_window, FALSE);
225     }
226
227   g_slist_free_full (x11_context->window_caches, (GDestroyNotify)gdk_window_cache_unref);
228   x11_context->window_caches = NULL;
229
230   contexts = g_list_remove (contexts, context);
231
232   G_OBJECT_CLASS (gdk_x11_drag_context_parent_class)->finalize (object);
233 }
234
235 /* Drag Contexts */
236
237 static GdkDragContext *
238 gdk_drag_context_find (GdkDisplay *display,
239                        gboolean    is_source,
240                        Window      source_xid,
241                        Window      dest_xid)
242 {
243   GList *tmp_list;
244   GdkDragContext *context;
245   GdkX11DragContext *context_x11;
246   Window context_dest_xid;
247
248   for (tmp_list = contexts; tmp_list; tmp_list = tmp_list->next)
249     {
250       context = (GdkDragContext *)tmp_list->data;
251       context_x11 = (GdkX11DragContext *)context;
252
253       if ((context->source_window && gdk_window_get_display (context->source_window) != display) ||
254           (context->dest_window && gdk_window_get_display (context->dest_window) != display))
255         continue;
256
257       context_dest_xid = context->dest_window
258                             ? (context_x11->drop_xid
259                                   ? context_x11->drop_xid
260                                   : GDK_WINDOW_XID (context->dest_window))
261                             : None;
262
263       if ((!context->is_source == !is_source) &&
264           ((source_xid == None) || (context->source_window &&
265             (GDK_WINDOW_XID (context->source_window) == source_xid))) &&
266           ((dest_xid == None) || (context_dest_xid == dest_xid)))
267         return context;
268     }
269
270   return NULL;
271 }
272
273 static void
274 precache_target_list (GdkDragContext *context)
275 {
276   if (context->targets)
277     {
278       GPtrArray *targets = g_ptr_array_new ();
279       GList *tmp_list;
280       int i;
281
282       for (tmp_list = context->targets; tmp_list; tmp_list = tmp_list->next)
283         g_ptr_array_add (targets, gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)));
284
285       _gdk_x11_precache_atoms (GDK_WINDOW_DISPLAY (context->source_window),
286                                (const gchar **)targets->pdata,
287                                targets->len);
288
289       for (i =0; i < targets->len; i++)
290         g_free (targets->pdata[i]);
291
292       g_ptr_array_free (targets, TRUE);
293     }
294 }
295
296 /* Utility functions */
297
298 static void
299 free_cache_child (GdkCacheChild *child,
300                   GdkDisplay    *display)
301 {
302   if (child->shape)
303     cairo_region_destroy (child->shape);
304
305   if (child->shape_selected && display)
306     {
307       GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
308
309       XShapeSelectInput (display_x11->xdisplay, child->xid, 0);
310     }
311
312   g_free (child);
313 }
314
315 static void
316 gdk_window_cache_add (GdkWindowCache *cache,
317                       guint32         xid,
318                       gint            x,
319                       gint            y,
320                       gint            width,
321                       gint            height,
322                       gboolean        mapped)
323 {
324   GdkCacheChild *child = g_new (GdkCacheChild, 1);
325
326   child->xid = xid;
327   child->x = x;
328   child->y = y;
329   child->width = width;
330   child->height = height;
331   child->mapped = mapped;
332   child->shape_selected = FALSE;
333   child->shape_valid = FALSE;
334   child->shape = NULL;
335
336   cache->children = g_list_prepend (cache->children, child);
337   g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid),
338                        cache->children);
339 }
340
341 static GdkFilterReturn
342 gdk_window_cache_shape_filter (GdkXEvent *xev,
343                                GdkEvent  *event,
344                                gpointer   data)
345 {
346   XEvent *xevent = (XEvent *)xev;
347   GdkWindowCache *cache = data;
348
349   GdkX11Display *display = GDK_X11_DISPLAY (gdk_screen_get_display (cache->screen));
350
351   if (display->have_shapes &&
352       xevent->type == display->shape_event_base + ShapeNotify)
353     {
354       XShapeEvent *xse = (XShapeEvent*)xevent;
355       GList *node;
356
357       node = g_hash_table_lookup (cache->child_hash,
358                                   GUINT_TO_POINTER (xse->window));
359       if (node)
360         {
361           GdkCacheChild *child = node->data;
362           child->shape_valid = FALSE;
363           if (child->shape)
364             {
365               cairo_region_destroy (child->shape);
366               child->shape = NULL;
367             }
368         }
369
370       return GDK_FILTER_REMOVE;
371     }
372
373   return GDK_FILTER_CONTINUE;
374 }
375
376 static GdkFilterReturn
377 gdk_window_cache_filter (GdkXEvent *xev,
378                          GdkEvent  *event,
379                          gpointer   data)
380 {
381   XEvent *xevent = (XEvent *)xev;
382   GdkWindowCache *cache = data;
383
384   switch (xevent->type)
385     {
386     case CirculateNotify:
387       break;
388     case ConfigureNotify:
389       {
390         XConfigureEvent *xce = &xevent->xconfigure;
391         GList *node;
392
393         node = g_hash_table_lookup (cache->child_hash,
394                                     GUINT_TO_POINTER (xce->window));
395         if (node)
396           {
397             GdkCacheChild *child = node->data;
398             child->x = xce->x;
399             child->y = xce->y;
400             child->width = xce->width;
401             child->height = xce->height;
402             if (xce->above == None && (node->next))
403               {
404                 GList *last = g_list_last (cache->children);
405                 cache->children = g_list_remove_link (cache->children, node);
406                 last->next = node;
407                 node->next = NULL;
408                 node->prev = last;
409               }
410             else
411               {
412                 GList *above_node = g_hash_table_lookup (cache->child_hash,
413                                                          GUINT_TO_POINTER (xce->above));
414                 if (above_node && node->next != above_node)
415                   {
416                     /* Put the window above (before in the list) above_node */
417                     cache->children = g_list_remove_link (cache->children, node);
418                     node->prev = above_node->prev;
419                     if (node->prev)
420                       node->prev->next = node;
421                     else
422                       cache->children = node;
423                     node->next = above_node;
424                     above_node->prev = node;
425                   }
426               }
427           }
428         break;
429       }
430     case CreateNotify:
431       {
432         XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
433
434         if (!g_hash_table_lookup (cache->child_hash,
435                                   GUINT_TO_POINTER (xcwe->window)))
436           gdk_window_cache_add (cache, xcwe->window,
437                                 xcwe->x, xcwe->y, xcwe->width, xcwe->height,
438                                 FALSE);
439         break;
440       }
441     case DestroyNotify:
442       {
443         XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
444         GList *node;
445
446         node = g_hash_table_lookup (cache->child_hash,
447                                     GUINT_TO_POINTER (xdwe->window));
448         if (node)
449           {
450             GdkCacheChild *child = node->data;
451
452             g_hash_table_remove (cache->child_hash,
453                                  GUINT_TO_POINTER (xdwe->window));
454             cache->children = g_list_remove_link (cache->children, node);
455             /* window is destroyed, no need to disable ShapeNotify */
456             free_cache_child (child, NULL);
457             g_list_free_1 (node);
458           }
459         break;
460       }
461     case MapNotify:
462       {
463         XMapEvent *xme = &xevent->xmap;
464         GList *node;
465
466         node = g_hash_table_lookup (cache->child_hash,
467                                     GUINT_TO_POINTER (xme->window));
468         if (node)
469           {
470             GdkCacheChild *child = node->data;
471             child->mapped = TRUE;
472           }
473         break;
474       }
475     case ReparentNotify:
476       break;
477     case UnmapNotify:
478       {
479         XMapEvent *xume = &xevent->xmap;
480         GList *node;
481
482         node = g_hash_table_lookup (cache->child_hash,
483                                     GUINT_TO_POINTER (xume->window));
484         if (node)
485           {
486             GdkCacheChild *child = node->data;
487             child->mapped = FALSE;
488           }
489         break;
490       }
491     default:
492       return GDK_FILTER_CONTINUE;
493     }
494   return GDK_FILTER_REMOVE;
495 }
496
497 static GdkWindowCache *
498 gdk_window_cache_new (GdkScreen *screen)
499 {
500   XWindowAttributes xwa;
501   Display *xdisplay = GDK_SCREEN_XDISPLAY (screen);
502   GdkWindow *root_window = gdk_screen_get_root_window (screen);
503   GdkChildInfoX11 *children;
504   guint nchildren, i;
505 #ifdef HAVE_XCOMPOSITE
506   Window cow;
507 #endif
508
509   GdkWindowCache *result = g_new (GdkWindowCache, 1);
510
511   result->children = NULL;
512   result->child_hash = g_hash_table_new (g_direct_hash, NULL);
513   result->screen = screen;
514   result->ref_count = 1;
515
516   XGetWindowAttributes (xdisplay, GDK_WINDOW_XID (root_window), &xwa);
517   result->old_event_mask = xwa.your_event_mask;
518
519   if (G_UNLIKELY (!GDK_X11_DISPLAY (GDK_X11_SCREEN (screen)->display)->trusted_client))
520     {
521       GList *toplevel_windows, *list;
522       GdkWindow *window;
523       gint x, y, width, height;
524
525       toplevel_windows = gdk_screen_get_toplevel_windows (screen);
526       for (list = toplevel_windows; list; list = list->next)
527         {
528           window = GDK_WINDOW (list->data);
529           gdk_window_get_geometry (window, &x, &y, &width, &height);
530           gdk_window_cache_add (result, GDK_WINDOW_XID (window),
531                                 x, y, width, height,
532                                 gdk_window_is_visible (window));
533         }
534       g_list_free (toplevel_windows);
535       return result;
536     }
537
538   XSelectInput (xdisplay, GDK_WINDOW_XID (root_window),
539                 result->old_event_mask | SubstructureNotifyMask);
540   gdk_window_add_filter (root_window, gdk_window_cache_filter, result);
541   gdk_window_add_filter (NULL, gdk_window_cache_shape_filter, result);
542
543   if (!_gdk_x11_get_window_child_info (gdk_screen_get_display (screen),
544                                        GDK_WINDOW_XID (root_window),
545                                        FALSE, NULL,
546                                        &children, &nchildren))
547     return result;
548
549   for (i = 0; i < nchildren ; i++)
550     {
551       gdk_window_cache_add (result, children[i].window,
552                             children[i].x, children[i].y, children[i].width, children[i].height,
553                             children[i].is_mapped);
554     }
555
556   g_free (children);
557
558 #ifdef HAVE_XCOMPOSITE
559   /*
560    * Add the composite overlay window to the cache, as this can be a reasonable
561    * Xdnd proxy as well.
562    * This is only done when the screen is composited in order to avoid mapping
563    * the COW. We assume that the CM is using the COW (which is true for pretty
564    * much any CM currently in use).
565    */
566   if (gdk_screen_is_composited (screen))
567     {
568       cow = XCompositeGetOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
569       gdk_window_cache_add (result, cow, 0, 0, gdk_screen_get_width (screen), gdk_screen_get_height (screen), TRUE);
570       XCompositeReleaseOverlayWindow (xdisplay, GDK_WINDOW_XID (root_window));
571     }
572 #endif
573
574   return result;
575 }
576
577 static void
578 gdk_window_cache_destroy (GdkWindowCache *cache)
579 {
580   GdkWindow *root_window = gdk_screen_get_root_window (cache->screen);
581   GdkDisplay *display;
582
583   XSelectInput (GDK_WINDOW_XDISPLAY (root_window),
584                 GDK_WINDOW_XID (root_window),
585                 cache->old_event_mask);
586   gdk_window_remove_filter (root_window, gdk_window_cache_filter, cache);
587   gdk_window_remove_filter (NULL, gdk_window_cache_shape_filter, cache);
588
589   display = gdk_screen_get_display (cache->screen);
590
591   gdk_x11_display_error_trap_push (display);
592   g_list_foreach (cache->children, (GFunc)free_cache_child, display);
593   gdk_x11_display_error_trap_pop_ignored (display);
594
595   g_list_free (cache->children);
596   g_hash_table_destroy (cache->child_hash);
597
598   g_free (cache);
599 }
600
601 static GdkWindowCache *
602 gdk_window_cache_ref (GdkWindowCache *cache)
603 {
604   cache->ref_count += 1;
605
606   return cache;
607 }
608
609 static void
610 gdk_window_cache_unref (GdkWindowCache *cache)
611 {
612   g_assert (cache->ref_count > 0);
613
614   cache->ref_count -= 1;
615
616   if (cache->ref_count == 0)
617     {
618       window_caches = g_slist_remove (window_caches, cache);
619       gdk_window_cache_destroy (cache);
620     }
621 }
622
623 GdkWindowCache *
624 gdk_window_cache_get (GdkScreen *screen)
625 {
626   GSList *list;
627   GdkWindowCache *cache;
628
629   for (list = window_caches; list; list = list->next)
630     {
631       cache = list->data;
632       if (cache->screen == screen)
633         return gdk_window_cache_ref (cache);
634     }
635
636   cache = gdk_window_cache_new (screen);
637
638   window_caches = g_slist_prepend (window_caches, cache);
639
640   return cache;
641 }
642
643 static gboolean
644 is_pointer_within_shape (GdkDisplay    *display,
645                          GdkCacheChild *child,
646                          gint           x_pos,
647                          gint           y_pos)
648 {
649   if (!child->shape_selected)
650     {
651       GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
652
653       XShapeSelectInput (display_x11->xdisplay, child->xid, ShapeNotifyMask);
654       child->shape_selected = TRUE;
655     }
656   if (!child->shape_valid)
657     {
658       GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
659       cairo_region_t *input_shape;
660
661       child->shape = NULL;
662       if (gdk_display_supports_shapes (display))
663         child->shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
664                                                    child->xid, ShapeBounding);
665 #ifdef ShapeInput
666       input_shape = NULL;
667       if (gdk_display_supports_input_shapes (display))
668         input_shape = _gdk_x11_xwindow_get_shape (display_x11->xdisplay,
669                                                   child->xid, ShapeInput);
670
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     {
1512       GDK_NOTE (DND,
1513                 g_message ("Send event to %lx failed",
1514                 GDK_WINDOW_XID (context->dest_window)));
1515     }
1516 }
1517
1518 static void
1519 motif_send_leave (GdkX11DragContext *context_x11,
1520                   guint32            time)
1521 {
1522   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1523   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1524   XEvent xev;
1525
1526   xev.xclient.type = ClientMessage;
1527   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
1528   xev.xclient.format = 8;
1529   xev.xclient.window = GDK_WINDOW_XID (context->dest_window);
1530
1531   MOTIF_XCLIENT_BYTE (&xev, 0) = XmTOP_LEVEL_LEAVE;
1532   MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1533   MOTIF_XCLIENT_SHORT (&xev, 1) = 0;
1534   MOTIF_XCLIENT_LONG (&xev, 1) = time;
1535   MOTIF_XCLIENT_LONG (&xev, 2) = 0;
1536   MOTIF_XCLIENT_LONG (&xev, 3) = 0;
1537   MOTIF_XCLIENT_LONG (&xev, 4) = 0;
1538
1539   if (!_gdk_x11_display_send_xevent (display,
1540                                      GDK_WINDOW_XID (context->dest_window),
1541                                      FALSE, 0, &xev))
1542     {
1543       GDK_NOTE (DND,
1544                 g_message ("Send event to %lx failed",
1545                            GDK_WINDOW_XID (context->dest_window)));
1546     }
1547 }
1548
1549 static gboolean
1550 motif_send_motion (GdkX11DragContext *context_x11,
1551                     gint              x_root,
1552                     gint              y_root,
1553                     GdkDragAction     action,
1554                     guint32           time)
1555 {
1556   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1557   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1558   gboolean retval;
1559   XEvent xev;
1560
1561   xev.xclient.type = ClientMessage;
1562   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
1563   xev.xclient.format = 8;
1564   xev.xclient.window = GDK_WINDOW_XID (context->dest_window);
1565
1566   MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1567   MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
1568   MOTIF_XCLIENT_LONG (&xev, 1) = time;
1569   MOTIF_XCLIENT_LONG (&xev, 3) = 0;
1570   MOTIF_XCLIENT_LONG (&xev, 4) = 0;
1571
1572   if ((context->suggested_action != context_x11->old_action) ||
1573       (context->actions != context_x11->old_actions))
1574     {
1575       MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED;
1576
1577       /* context_x11->drag_status = GDK_DRAG_STATUS_ACTION_WAIT; */
1578       retval = TRUE;
1579     }
1580   else
1581     {
1582       MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION;
1583
1584       MOTIF_XCLIENT_SHORT (&xev, 4) = x_root;
1585       MOTIF_XCLIENT_SHORT (&xev, 5) = y_root;
1586
1587       context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1588       retval = FALSE;
1589     }
1590
1591   if (!_gdk_x11_display_send_xevent (display,
1592                                      GDK_WINDOW_XID (context->dest_window),
1593                                      FALSE, 0, &xev))
1594     {
1595       GDK_NOTE (DND,
1596                 g_message ("Send event to %lx failed",
1597                            GDK_WINDOW_XID (context->dest_window)));
1598     }
1599
1600   return retval;
1601 }
1602
1603 static void
1604 motif_send_drop (GdkX11DragContext *context_x11,
1605                  guint32            time)
1606 {
1607   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
1608   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
1609   XEvent xev;
1610
1611   xev.xclient.type = ClientMessage;
1612   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
1613   xev.xclient.format = 8;
1614   xev.xclient.window = GDK_WINDOW_XID (context->dest_window);
1615
1616   MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START;
1617   MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1618   MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
1619   MOTIF_XCLIENT_LONG (&xev, 1)  = time;
1620
1621   MOTIF_XCLIENT_SHORT (&xev, 4) = context_x11->last_x;
1622   MOTIF_XCLIENT_SHORT (&xev, 5) = context_x11->last_y;
1623
1624   MOTIF_XCLIENT_LONG (&xev, 3)  = context_x11->motif_selection;
1625   MOTIF_XCLIENT_LONG (&xev, 4)  = GDK_WINDOW_XID (context->source_window);
1626
1627   if (!_gdk_x11_display_send_xevent (display,
1628                                      GDK_WINDOW_XID (context->dest_window),
1629                                      FALSE, 0, &xev))
1630     {
1631       GDK_NOTE (DND,
1632                 g_message ("Send event to %lx failed",
1633                            GDK_WINDOW_XID (context->dest_window)));
1634     }
1635 }
1636
1637 /* Target Side */
1638
1639 static gboolean
1640 motif_read_initiator_info (GdkDisplay *display,
1641                            Window      source_window,
1642                            Atom        atom,
1643                            GList     **targets,
1644                            Atom       *selection)
1645 {
1646   GList *tmp_list;
1647   Atom type;
1648   gint format;
1649   gulong nitems;
1650   gulong bytes_after;
1651   guchar *data;
1652   MotifDragInitiatorInfo *initiator_info;
1653
1654   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1655
1656   gdk_x11_display_error_trap_push (display);
1657   XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), source_window, atom,
1658                       0, sizeof(*initiator_info), FALSE,
1659                       gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_INITIATOR_INFO"),
1660                       &type, &format, &nitems, &bytes_after,
1661                       &data);
1662
1663   if (gdk_x11_display_error_trap_pop (display) ||
1664       (format != 8) || (nitems != sizeof (MotifDragInitiatorInfo)) ||
1665       (bytes_after != 0))
1666     {
1667       g_warning ("Error reading initiator info\n");
1668       return FALSE;
1669     }
1670
1671   initiator_info = (MotifDragInitiatorInfo *)data;
1672
1673   motif_read_target_table (display);
1674
1675   initiator_info->targets_index =
1676     card16_to_host (initiator_info->targets_index, initiator_info->byte_order);
1677   initiator_info->selection_atom =
1678     card32_to_host (initiator_info->selection_atom, initiator_info->byte_order);
1679
1680   if (initiator_info->targets_index >= display_x11->motif_n_target_lists)
1681     {
1682       g_warning ("Invalid target index in TOP_LEVEL_ENTER MESSAGE");
1683       XFree (initiator_info);
1684       return FALSE;
1685     }
1686
1687   tmp_list = g_list_last (display_x11->motif_target_lists[initiator_info->targets_index]);
1688
1689   *targets = NULL;
1690   while (tmp_list)
1691     {
1692       GdkAtom atom = gdk_x11_xatom_to_atom_for_display (display, GPOINTER_TO_UINT (tmp_list->data));
1693       *targets = g_list_prepend (*targets, GDK_ATOM_TO_POINTER (atom));
1694       tmp_list = tmp_list->prev;
1695     }
1696
1697 #ifdef G_ENABLE_DEBUG
1698   if (_gdk_debug_flags & GDK_DEBUG_DND)
1699     print_target_list (*targets);
1700 #endif /* G_ENABLE_DEBUG */
1701
1702   *selection = initiator_info->selection_atom;
1703
1704   XFree (initiator_info);
1705
1706   return TRUE;
1707 }
1708
1709 static GdkDragContext *
1710 motif_drag_context_new (GdkWindow *dest_window,
1711                         guint32    timestamp,
1712                         guint32    source_window,
1713                         guint32    atom)
1714 {
1715   GdkX11DragContext *context_x11;
1716   GdkDragContext *context;
1717   GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
1718   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1719
1720   /* FIXME, current_dest_drag really shouldn't be NULL'd
1721    * if we error below.
1722    */
1723   if (display_x11->current_dest_drag != NULL)
1724     {
1725       if (timestamp >= display_x11->current_dest_drag->start_time)
1726         {
1727           g_object_unref (display_x11->current_dest_drag);
1728           display_x11->current_dest_drag = NULL;
1729         }
1730       else
1731         return NULL;
1732     }
1733
1734   context_x11 = g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, NULL);
1735   context = GDK_DRAG_CONTEXT (context_x11);
1736
1737   context->protocol = GDK_DRAG_PROTO_MOTIF;
1738   context->is_source = FALSE;
1739
1740   context->source_window = gdk_x11_window_foreign_new_for_display (display, source_window);
1741   if (!context->source_window)
1742     {
1743       g_object_unref (context_x11);
1744       return NULL;
1745     }
1746
1747   context->dest_window = dest_window;
1748   g_object_ref (dest_window);
1749   context->start_time = timestamp;
1750
1751   if (!motif_read_initiator_info (GDK_WINDOW_DISPLAY (dest_window),
1752                                   source_window,
1753                                   atom,
1754                                   &context->targets,
1755                                   &context_x11->motif_selection))
1756     {
1757       g_object_unref (context_x11);
1758       return NULL;
1759     }
1760
1761   return context;
1762 }
1763
1764 /*
1765  * The MOTIF drag protocol has no real provisions for distinguishing
1766  * multiple simultaneous drops. If the sources grab the pointer
1767  * when doing drags, that shouldn't happen, in any case. If it
1768  * does, we can't do much except hope for the best.
1769  */
1770
1771 static GdkFilterReturn
1772 motif_top_level_enter (GdkEvent *event,
1773                        guint16   flags,
1774                        guint32   timestamp,
1775                        guint32   source_window,
1776                        guint32   atom)
1777 {
1778   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1779   GdkDragContext *new_context;
1780
1781   GDK_NOTE(DND, g_message ("Motif DND top level enter: flags: %#4x time: %d source_widow: %#4x atom: %d",
1782                            flags, timestamp, source_window, atom));
1783
1784   new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1785   if (!new_context)
1786     return GDK_FILTER_REMOVE;
1787
1788   event->dnd.type = GDK_DRAG_ENTER;
1789   event->dnd.context = new_context;
1790   g_object_ref (new_context);
1791
1792   display_x11->current_dest_drag = new_context;
1793
1794   return GDK_FILTER_TRANSLATE;
1795 }
1796
1797 static GdkFilterReturn
1798 motif_top_level_leave (GdkEvent *event,
1799                        guint16   flags,
1800                        guint32   timestamp)
1801 {
1802   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1803
1804   GDK_NOTE(DND, g_message ("Motif DND top level leave: flags: %#4x time: %d",
1805                            flags, timestamp));
1806
1807   if ((display_x11->current_dest_drag != NULL) &&
1808       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1809       (timestamp >= display_x11->current_dest_drag->start_time))
1810     {
1811       event->dnd.type = GDK_DRAG_LEAVE;
1812       /* Pass ownership of context to the event */
1813       event->dnd.context = display_x11->current_dest_drag;
1814
1815       display_x11->current_dest_drag = NULL;
1816
1817       return GDK_FILTER_TRANSLATE;
1818     }
1819   else
1820     return GDK_FILTER_REMOVE;
1821 }
1822
1823 static GdkFilterReturn
1824 motif_motion (GdkEvent *event,
1825               guint16   flags,
1826               guint32   timestamp,
1827               gint16    x_root,
1828               gint16    y_root)
1829 {
1830   GdkX11DragContext *context_x11;
1831   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1832
1833   GDK_NOTE(DND, g_message ("Motif DND motion: flags: %#4x time: %d (%d, %d)",
1834                            flags, timestamp, x_root, y_root));
1835
1836   if ((display_x11->current_dest_drag != NULL) &&
1837       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1838       (timestamp >= display_x11->current_dest_drag->start_time))
1839     {
1840       context_x11 = GDK_X11_DRAG_CONTEXT (display_x11->current_dest_drag);
1841
1842       event->dnd.type = GDK_DRAG_MOTION;
1843       event->dnd.context = display_x11->current_dest_drag;
1844       g_object_ref (display_x11->current_dest_drag);
1845
1846       event->dnd.time = timestamp;
1847
1848       motif_dnd_translate_flags (context_x11, flags);
1849
1850       event->dnd.x_root = x_root;
1851       event->dnd.y_root = y_root;
1852
1853       context_x11->last_x = x_root;
1854       context_x11->last_y = y_root;
1855
1856       context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1857
1858       return GDK_FILTER_TRANSLATE;
1859     }
1860
1861   return GDK_FILTER_REMOVE;
1862 }
1863
1864 static GdkFilterReturn
1865 motif_operation_changed (GdkEvent *event,
1866                          guint16   flags,
1867                          guint32   timestamp)
1868 {
1869   GdkX11DragContext *context_x11;
1870   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1871   GDK_NOTE(DND, g_message ("Motif DND operation changed: flags: %#4x time: %d",
1872                            flags, timestamp));
1873
1874   if ((display_x11->current_dest_drag != NULL) &&
1875       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1876       (timestamp >= display_x11->current_dest_drag->start_time))
1877     {
1878       event->dnd.type = GDK_DRAG_MOTION;
1879       event->dnd.send_event = FALSE;
1880       event->dnd.context = display_x11->current_dest_drag;
1881       g_object_ref (display_x11->current_dest_drag);
1882
1883       event->dnd.time = timestamp;
1884       context_x11 = GDK_X11_DRAG_CONTEXT (display_x11->current_dest_drag);
1885
1886       motif_dnd_translate_flags (context_x11, flags);
1887
1888       event->dnd.x_root = context_x11->last_x;
1889       event->dnd.y_root = context_x11->last_y;
1890
1891       context_x11->drag_status = GDK_DRAG_STATUS_ACTION_WAIT;
1892
1893       return GDK_FILTER_TRANSLATE;
1894     }
1895
1896   return GDK_FILTER_REMOVE;
1897 }
1898
1899 static GdkFilterReturn
1900 motif_drop_start (GdkEvent *event,
1901                   guint16   flags,
1902                   guint32   timestamp,
1903                   guint32   source_window,
1904                   guint32   atom,
1905                   gint16    x_root,
1906                   gint16    y_root)
1907 {
1908   GdkDragContext *new_context;
1909   GdkX11Display *display_x11 = GDK_X11_DISPLAY (GDK_WINDOW_DISPLAY (event->any.window));
1910
1911   GDK_NOTE(DND, g_message ("Motif DND drop start: flags: %#4x time: %d (%d, %d) source_widow: %#4x atom: %d",
1912                            flags, timestamp, x_root, y_root, source_window, atom));
1913
1914   new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1915   if (!new_context)
1916     return GDK_FILTER_REMOVE;
1917
1918   motif_dnd_translate_flags (GDK_X11_DRAG_CONTEXT (new_context), flags);
1919
1920   event->dnd.type = GDK_DROP_START;
1921   event->dnd.context = new_context;
1922   event->dnd.time = timestamp;
1923   event->dnd.x_root = x_root;
1924   event->dnd.y_root = y_root;
1925
1926   gdk_x11_window_set_user_time (event->any.window, timestamp);
1927
1928   g_object_ref (new_context);
1929   display_x11->current_dest_drag = new_context;
1930
1931   return GDK_FILTER_TRANSLATE;
1932 }
1933
1934 static GdkFilterReturn
1935 motif_drag_status (GdkEvent *event,
1936                    guint16   flags,
1937                    guint32   timestamp)
1938 {
1939   GdkDragContext *context;
1940   GdkDisplay *display;
1941
1942   GDK_NOTE (DND,
1943             g_message ("Motif status message: flags %x", flags));
1944
1945   display = gdk_window_get_display (event->any.window);
1946   if (!display)
1947     return GDK_FILTER_REMOVE;
1948
1949   context = gdk_drag_context_find (display, TRUE, GDK_WINDOW_XID (event->any.window), None);
1950
1951   if (context)
1952     {
1953       GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
1954       if ((context_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT) ||
1955           (context_x11->drag_status == GDK_DRAG_STATUS_ACTION_WAIT))
1956         context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
1957
1958       event->dnd.type = GDK_DRAG_STATUS;
1959       event->dnd.send_event = FALSE;
1960       event->dnd.context = context;
1961       g_object_ref (context);
1962
1963       event->dnd.time = timestamp;
1964
1965       if ((flags & 0x00f0) >> 4 == XmDROP_SITE_VALID)
1966         {
1967           switch (flags & 0x000f)
1968             {
1969             case XmDROP_NOOP:
1970               context->action = 0;
1971               break;
1972             case XmDROP_MOVE:
1973                 context->action = GDK_ACTION_MOVE;
1974                 break;
1975             case XmDROP_COPY:
1976               context->action = GDK_ACTION_COPY;
1977               break;
1978             case XmDROP_LINK:
1979               context->action = GDK_ACTION_LINK;
1980               break;
1981             }
1982         }
1983       else
1984         context->action = 0;
1985
1986       return GDK_FILTER_TRANSLATE;
1987     }
1988   return GDK_FILTER_REMOVE;
1989 }
1990
1991 static GdkFilterReturn
1992 motif_dnd_filter (GdkXEvent *xev,
1993                   GdkEvent  *event,
1994                   gpointer data)
1995 {
1996   XEvent *xevent = (XEvent *)xev;
1997
1998   guint8 reason;
1999   guint16 flags;
2000   guint32 timestamp;
2001   guint32 source_window;
2002   Atom atom;
2003   gint16 x_root, y_root;
2004   gboolean is_reply;
2005
2006   if (!event->any.window ||
2007       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2008     return GDK_FILTER_CONTINUE;                 /* Not for us */
2009
2010   /* First read some fields common to all Motif DND messages */
2011   reason = MOTIF_UNPACK_BYTE (xevent, 0);
2012   flags = MOTIF_UNPACK_SHORT (xevent, 1);
2013   timestamp = MOTIF_UNPACK_LONG (xevent, 1);
2014
2015   is_reply = ((reason & 0x80) != 0);
2016
2017   switch (reason & 0x7f)
2018     {
2019     case XmTOP_LEVEL_ENTER:
2020       source_window = MOTIF_UNPACK_LONG (xevent, 2);
2021       atom = MOTIF_UNPACK_LONG (xevent, 3);
2022       return motif_top_level_enter (event, flags, timestamp, source_window, atom);
2023     case XmTOP_LEVEL_LEAVE:
2024       return motif_top_level_leave (event, flags, timestamp);
2025
2026     case XmDRAG_MOTION:
2027       x_root = MOTIF_UNPACK_SHORT (xevent, 4);
2028       y_root = MOTIF_UNPACK_SHORT (xevent, 5);
2029
2030       if (!is_reply)
2031         return motif_motion (event, flags, timestamp, x_root, y_root);
2032       else
2033         return motif_drag_status (event, flags, timestamp);
2034
2035     case XmDROP_SITE_ENTER:
2036       return motif_drag_status (event, flags, timestamp);
2037
2038     case XmDROP_SITE_LEAVE:
2039       return motif_drag_status (event,
2040                                 XmNO_DROP_SITE << 8 | XmDROP_NOOP,
2041                                 timestamp);
2042     case XmDROP_START:
2043       x_root = MOTIF_UNPACK_SHORT (xevent, 4);
2044       y_root = MOTIF_UNPACK_SHORT (xevent, 5);
2045       atom = MOTIF_UNPACK_LONG (xevent, 3);
2046       source_window = MOTIF_UNPACK_LONG (xevent, 4);
2047
2048       if (!is_reply)
2049         return motif_drop_start (event, flags, timestamp, source_window, atom, x_root, y_root);
2050
2051      break;
2052     case XmOPERATION_CHANGED:
2053       if (!is_reply)
2054         return motif_operation_changed (event, flags, timestamp);
2055       else
2056         return motif_drag_status (event, flags, timestamp);
2057
2058       break;
2059       /* To the best of my knowledge, these next two messages are
2060        * not part of the protocol, though they are defined in
2061        * the header files.
2062        */
2063     case XmDROP_FINISH:
2064     case XmDRAG_DROP_FINISH:
2065       break;
2066     }
2067
2068   return GDK_FILTER_REMOVE;
2069 }
2070
2071 /*************************************************************
2072  ***************************** XDND **************************
2073  *************************************************************/
2074
2075 /* Utility functions */
2076
2077 static struct {
2078   const gchar *name;
2079   GdkAtom atom;
2080   GdkDragAction action;
2081 } xdnd_actions_table[] = {
2082     { "XdndActionCopy",    None, GDK_ACTION_COPY },
2083     { "XdndActionMove",    None, GDK_ACTION_MOVE },
2084     { "XdndActionLink",    None, GDK_ACTION_LINK },
2085     { "XdndActionAsk",     None, GDK_ACTION_ASK  },
2086     { "XdndActionPrivate", None, GDK_ACTION_COPY },
2087   };
2088
2089 static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table);
2090 static gboolean xdnd_actions_initialized = FALSE;
2091
2092 static void
2093 xdnd_initialize_actions (void)
2094 {
2095   gint i;
2096
2097   xdnd_actions_initialized = TRUE;
2098   for (i = 0; i < xdnd_n_actions; i++)
2099     xdnd_actions_table[i].atom = gdk_atom_intern_static_string (xdnd_actions_table[i].name);
2100 }
2101
2102 static GdkDragAction
2103 xdnd_action_from_atom (GdkDisplay *display,
2104                        Atom        xatom)
2105 {
2106   GdkAtom atom;
2107   gint i;
2108
2109   if (xatom == None)
2110     return 0;
2111
2112   atom = gdk_x11_xatom_to_atom_for_display (display, xatom);
2113
2114   if (!xdnd_actions_initialized)
2115     xdnd_initialize_actions();
2116
2117   for (i = 0; i < xdnd_n_actions; i++)
2118     if (atom == xdnd_actions_table[i].atom)
2119       return xdnd_actions_table[i].action;
2120
2121   return 0;
2122 }
2123
2124 static Atom
2125 xdnd_action_to_atom (GdkDisplay    *display,
2126                      GdkDragAction  action)
2127 {
2128   gint i;
2129
2130   if (!xdnd_actions_initialized)
2131     xdnd_initialize_actions();
2132
2133   for (i = 0; i < xdnd_n_actions; i++)
2134     if (action == xdnd_actions_table[i].action)
2135       return gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
2136
2137   return None;
2138 }
2139
2140 /* Source side */
2141
2142 static GdkFilterReturn
2143 xdnd_status_filter (GdkXEvent *xev,
2144                     GdkEvent  *event,
2145                     gpointer   data)
2146 {
2147   GdkDisplay *display;
2148   XEvent *xevent = (XEvent *)xev;
2149   guint32 dest_window = xevent->xclient.data.l[0];
2150   guint32 flags = xevent->xclient.data.l[1];
2151   Atom action = xevent->xclient.data.l[4];
2152   GdkDragContext *context;
2153
2154   if (!event->any.window ||
2155       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2156     return GDK_FILTER_CONTINUE;                 /* Not for us */
2157
2158   GDK_NOTE (DND,
2159             g_message ("XdndStatus: dest_window: %#x  action: %ld",
2160                        dest_window, action));
2161
2162   display = gdk_window_get_display (event->any.window);
2163   context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
2164
2165   if (context)
2166     {
2167       GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
2168       if (context_x11->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
2169         context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
2170
2171       event->dnd.send_event = FALSE;
2172       event->dnd.type = GDK_DRAG_STATUS;
2173       event->dnd.context = context;
2174       gdk_event_set_device (event, gdk_drag_context_get_device (context));
2175       g_object_ref (context);
2176
2177       event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2178       if (!(action != 0) != !(flags & 1))
2179         {
2180           GDK_NOTE (DND,
2181                     g_warning ("Received status event with flags not corresponding to action!\n"));
2182           action = 0;
2183         }
2184
2185       context->action = xdnd_action_from_atom (display, action);
2186
2187       return GDK_FILTER_TRANSLATE;
2188     }
2189
2190   return GDK_FILTER_REMOVE;
2191 }
2192
2193 static GdkFilterReturn
2194 xdnd_finished_filter (GdkXEvent *xev,
2195                       GdkEvent  *event,
2196                       gpointer   data)
2197 {
2198   GdkDisplay *display;
2199   XEvent *xevent = (XEvent *)xev;
2200   guint32 dest_window = xevent->xclient.data.l[0];
2201   GdkDragContext *context;
2202   GdkX11DragContext *context_x11;
2203
2204   if (!event->any.window ||
2205       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2206     return GDK_FILTER_CONTINUE;                 /* Not for us */
2207
2208   GDK_NOTE (DND,
2209             g_message ("XdndFinished: dest_window: %#x", dest_window));
2210
2211   display = gdk_window_get_display (event->any.window);
2212   context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
2213
2214   if (context)
2215     {
2216       context_x11 = GDK_X11_DRAG_CONTEXT (context);
2217       if (context_x11->version == 5)
2218         context_x11->drop_failed = xevent->xclient.data.l[1] == 0;
2219
2220       event->dnd.type = GDK_DROP_FINISHED;
2221       event->dnd.context = context;
2222       gdk_event_set_device (event, gdk_drag_context_get_device (context));
2223       g_object_ref (context);
2224
2225       event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
2226
2227       return GDK_FILTER_TRANSLATE;
2228     }
2229
2230   return GDK_FILTER_REMOVE;
2231 }
2232
2233 static void
2234 xdnd_set_targets (GdkX11DragContext *context_x11)
2235 {
2236   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2237   Atom *atomlist;
2238   GList *tmp_list = context->targets;
2239   gint i;
2240   gint n_atoms = g_list_length (context->targets);
2241   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2242
2243   atomlist = g_new (Atom, n_atoms);
2244   i = 0;
2245   while (tmp_list)
2246     {
2247       atomlist[i] = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (tmp_list->data));
2248       tmp_list = tmp_list->next;
2249       i++;
2250     }
2251
2252   XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
2253                    GDK_WINDOW_XID (context->source_window),
2254                    gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
2255                    XA_ATOM, 32, PropModeReplace,
2256                    (guchar *)atomlist, n_atoms);
2257
2258   g_free (atomlist);
2259
2260   context_x11->xdnd_targets_set = 1;
2261 }
2262
2263 static void
2264 xdnd_set_actions (GdkX11DragContext *context_x11)
2265 {
2266   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2267   Atom *atomlist;
2268   gint i;
2269   gint n_atoms;
2270   guint actions;
2271   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2272
2273   if (!xdnd_actions_initialized)
2274     xdnd_initialize_actions();
2275
2276   actions = context->actions;
2277   n_atoms = 0;
2278   for (i = 0; i < xdnd_n_actions; i++)
2279     {
2280       if (actions & xdnd_actions_table[i].action)
2281         {
2282           actions &= ~xdnd_actions_table[i].action;
2283           n_atoms++;
2284         }
2285     }
2286
2287   atomlist = g_new (Atom, n_atoms);
2288
2289   actions = context->actions;
2290   n_atoms = 0;
2291   for (i = 0; i < xdnd_n_actions; i++)
2292     {
2293       if (actions & xdnd_actions_table[i].action)
2294         {
2295           actions &= ~xdnd_actions_table[i].action;
2296           atomlist[n_atoms] = gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
2297           n_atoms++;
2298         }
2299     }
2300
2301   XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
2302                    GDK_WINDOW_XID (context->source_window),
2303                    gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
2304                    XA_ATOM, 32, PropModeReplace,
2305                    (guchar *)atomlist, n_atoms);
2306
2307   g_free (atomlist);
2308
2309   context_x11->xdnd_actions_set = TRUE;
2310   context_x11->xdnd_actions = context->actions;
2311 }
2312
2313 static void
2314 send_client_message_async_cb (Window   window,
2315                               gboolean success,
2316                               gpointer data)
2317 {
2318   GdkDragContext *context = data;
2319   GDK_NOTE (DND,
2320             g_message ("Got async callback for #%lx, success = %d",
2321                        window, success));
2322
2323   /* On failure, we immediately continue with the protocol
2324    * so we don't end up blocking for a timeout
2325    */
2326   if (!success &&
2327       context->dest_window &&
2328       window == GDK_WINDOW_XID (context->dest_window))
2329     {
2330       GdkEvent *temp_event;
2331       GdkX11DragContext *context_x11 = data;
2332
2333       g_object_unref (context->dest_window);
2334       context->dest_window = NULL;
2335       context->action = 0;
2336
2337       context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
2338
2339       temp_event = gdk_event_new (GDK_DRAG_STATUS);
2340       temp_event->dnd.window = g_object_ref (context->source_window);
2341       temp_event->dnd.send_event = TRUE;
2342       temp_event->dnd.context = g_object_ref (context);
2343       temp_event->dnd.time = GDK_CURRENT_TIME;
2344       gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
2345
2346       gdk_event_put (temp_event);
2347
2348       gdk_event_free (temp_event);
2349     }
2350
2351   g_object_unref (context);
2352 }
2353
2354
2355 static GdkDisplay *
2356 gdk_drag_context_get_display (GdkDragContext *context)
2357 {
2358   if (context->source_window)
2359     return GDK_WINDOW_DISPLAY (context->source_window);
2360   else if (context->dest_window)
2361     return GDK_WINDOW_DISPLAY (context->dest_window);
2362
2363   g_assert_not_reached ();
2364   return NULL;
2365 }
2366
2367 static void
2368 send_client_message_async (GdkDragContext      *context,
2369                            Window               window,
2370                            gboolean             propagate,
2371                            glong                event_mask,
2372                            XClientMessageEvent *event_send)
2373 {
2374   GdkDisplay *display = gdk_drag_context_get_display (context);
2375
2376   g_object_ref (context);
2377
2378   _gdk_x11_send_client_message_async (display, window,
2379                                       propagate, event_mask, event_send,
2380                                       send_client_message_async_cb, context);
2381 }
2382
2383 static gboolean
2384 xdnd_send_xevent (GdkX11DragContext *context_x11,
2385                   GdkWindow         *window,
2386                   gboolean           propagate,
2387                   XEvent            *event_send)
2388 {
2389   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2390   GdkDisplay *display = gdk_drag_context_get_display (context);
2391   Window xwindow;
2392   glong event_mask;
2393
2394   g_assert (event_send->xany.type == ClientMessage);
2395
2396   /* We short-circuit messages to ourselves */
2397   if (gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
2398     {
2399       gint i;
2400
2401       for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
2402         {
2403           if (gdk_x11_get_xatom_by_name_for_display (display, xdnd_filters[i].atom_name) ==
2404               event_send->xclient.message_type)
2405             {
2406               GdkEvent *temp_event;
2407
2408               temp_event = gdk_event_new (GDK_NOTHING);
2409               temp_event->any.window = g_object_ref (window);
2410
2411               if ((*xdnd_filters[i].func) (event_send, temp_event, NULL) == GDK_FILTER_TRANSLATE)
2412                 {
2413                   gdk_event_put (temp_event);
2414                   gdk_event_free (temp_event);
2415                 }
2416
2417               return TRUE;
2418             }
2419         }
2420     }
2421
2422   xwindow = GDK_WINDOW_XID (window);
2423
2424   if (_gdk_x11_display_is_root_window (display, xwindow))
2425     event_mask = ButtonPressMask;
2426   else
2427     event_mask = 0;
2428
2429   send_client_message_async (context, xwindow, propagate, event_mask,
2430                              &event_send->xclient);
2431
2432   return TRUE;
2433 }
2434
2435 static void
2436 xdnd_send_enter (GdkX11DragContext *context_x11)
2437 {
2438   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2439   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->dest_window);
2440   XEvent xev;
2441
2442   xev.xclient.type = ClientMessage;
2443   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndEnter");
2444   xev.xclient.format = 32;
2445   xev.xclient.window = context_x11->drop_xid
2446                            ? context_x11->drop_xid
2447                            : GDK_WINDOW_XID (context->dest_window);
2448   xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2449   xev.xclient.data.l[1] = (context_x11->version << 24); /* version */
2450   xev.xclient.data.l[2] = 0;
2451   xev.xclient.data.l[3] = 0;
2452   xev.xclient.data.l[4] = 0;
2453
2454   GDK_NOTE(DND,
2455            g_message ("Sending enter source window %#lx XDND protocol version %d\n",
2456                       GDK_WINDOW_XID (context->source_window), context_x11->version));
2457   if (g_list_length (context->targets) > 3)
2458     {
2459       if (!context_x11->xdnd_targets_set)
2460         xdnd_set_targets (context_x11);
2461       xev.xclient.data.l[1] |= 1;
2462     }
2463   else
2464     {
2465       GList *tmp_list = context->targets;
2466       gint i = 2;
2467
2468       while (tmp_list)
2469         {
2470           xev.xclient.data.l[i] = gdk_x11_atom_to_xatom_for_display (display,
2471                                                                      GDK_POINTER_TO_ATOM (tmp_list->data));
2472           tmp_list = tmp_list->next;
2473           i++;
2474         }
2475     }
2476
2477   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2478     {
2479       GDK_NOTE (DND,
2480                 g_message ("Send event to %lx failed",
2481                            GDK_WINDOW_XID (context->dest_window)));
2482       g_object_unref (context->dest_window);
2483       context->dest_window = NULL;
2484     }
2485 }
2486
2487 static void
2488 xdnd_send_leave (GdkX11DragContext *context_x11)
2489 {
2490   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2491   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2492   XEvent xev;
2493
2494   xev.xclient.type = ClientMessage;
2495   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndLeave");
2496   xev.xclient.format = 32;
2497   xev.xclient.window = context_x11->drop_xid
2498                            ? context_x11->drop_xid
2499                            : GDK_WINDOW_XID (context->dest_window);
2500   xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2501   xev.xclient.data.l[1] = 0;
2502   xev.xclient.data.l[2] = 0;
2503   xev.xclient.data.l[3] = 0;
2504   xev.xclient.data.l[4] = 0;
2505
2506   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2507     {
2508       GDK_NOTE (DND,
2509                 g_message ("Send event to %lx failed",
2510                            GDK_WINDOW_XID (context->dest_window)));
2511       g_object_unref (context->dest_window);
2512       context->dest_window = NULL;
2513     }
2514 }
2515
2516 static void
2517 xdnd_send_drop (GdkX11DragContext *context_x11,
2518                 guint32            time)
2519 {
2520   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2521   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2522   XEvent xev;
2523
2524   xev.xclient.type = ClientMessage;
2525   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndDrop");
2526   xev.xclient.format = 32;
2527   xev.xclient.window = context_x11->drop_xid
2528                            ? context_x11->drop_xid
2529                            : GDK_WINDOW_XID (context->dest_window);
2530   xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2531   xev.xclient.data.l[1] = 0;
2532   xev.xclient.data.l[2] = time;
2533   xev.xclient.data.l[3] = 0;
2534   xev.xclient.data.l[4] = 0;
2535
2536   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2537     {
2538       GDK_NOTE (DND,
2539                 g_message ("Send event to %lx failed",
2540                            GDK_WINDOW_XID (context->dest_window)));
2541       g_object_unref (context->dest_window);
2542       context->dest_window = NULL;
2543     }
2544 }
2545
2546 static void
2547 xdnd_send_motion (GdkX11DragContext *context_x11,
2548                   gint               x_root,
2549                   gint               y_root,
2550                   GdkDragAction      action,
2551                   guint32            time)
2552 {
2553   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2554   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2555   XEvent xev;
2556
2557   xev.xclient.type = ClientMessage;
2558   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndPosition");
2559   xev.xclient.format = 32;
2560   xev.xclient.window = context_x11->drop_xid
2561                            ? context_x11->drop_xid
2562                            : GDK_WINDOW_XID (context->dest_window);
2563   xev.xclient.data.l[0] = GDK_WINDOW_XID (context->source_window);
2564   xev.xclient.data.l[1] = 0;
2565   xev.xclient.data.l[2] = (x_root << 16) | y_root;
2566   xev.xclient.data.l[3] = time;
2567   xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
2568
2569   if (!xdnd_send_xevent (context_x11, context->dest_window, FALSE, &xev))
2570     {
2571       GDK_NOTE (DND,
2572                 g_message ("Send event to %lx failed",
2573                            GDK_WINDOW_XID (context->dest_window)));
2574       g_object_unref (context->dest_window);
2575       context->dest_window = NULL;
2576     }
2577   context_x11->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
2578 }
2579
2580 static guint32
2581 xdnd_check_dest (GdkDisplay *display,
2582                  Window      win,
2583                  guint      *xdnd_version)
2584 {
2585   gboolean retval = FALSE;
2586   Atom type = None;
2587   int format;
2588   unsigned long nitems, after;
2589   guchar *data;
2590   Atom *version;
2591   Window *proxy_data;
2592   Window proxy;
2593   Atom xdnd_proxy_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndProxy");
2594   Atom xdnd_aware_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndAware");
2595
2596   proxy = None;
2597
2598   gdk_x11_display_error_trap_push (display);
2599   if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win,
2600                           xdnd_proxy_atom, 0,
2601                           1, False, AnyPropertyType,
2602                           &type, &format, &nitems, &after,
2603                           &data) == Success)
2604     {
2605       if (type != None)
2606         {
2607           proxy_data = (Window *)data;
2608
2609           if ((format == 32) && (nitems == 1))
2610             {
2611               proxy = *proxy_data;
2612             }
2613           else
2614             {
2615               GDK_NOTE (DND,
2616                         g_warning ("Invalid XdndProxy "
2617                                    "property on window %ld\n", win));
2618             }
2619
2620           XFree (proxy_data);
2621         }
2622
2623       if ((XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), proxy ? proxy : win,
2624                                xdnd_aware_atom, 0,
2625                                1, False, AnyPropertyType,
2626                                &type, &format, &nitems, &after,
2627                                &data) == Success) &&
2628           type != None)
2629         {
2630           version = (Atom *)data;
2631
2632           if ((format == 32) && (nitems == 1))
2633             {
2634               if (*version >= 3)
2635                 retval = TRUE;
2636               if (xdnd_version)
2637                 *xdnd_version = *version;
2638             }
2639           else
2640             {
2641               GDK_NOTE (DND,
2642                         g_warning ("Invalid XdndAware "
2643                                    "property on window %ld\n", win));
2644             }
2645
2646           XFree (version);
2647         }
2648     }
2649
2650   gdk_x11_display_error_trap_pop_ignored (display);
2651
2652   return retval ? (proxy ? proxy : win) : None;
2653 }
2654
2655 /* Target side */
2656
2657 static void
2658 xdnd_read_actions (GdkX11DragContext *context_x11)
2659 {
2660   GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11);
2661   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2662   Atom type;
2663   int format;
2664   gulong nitems, after;
2665   guchar *data;
2666   Atom *atoms;
2667   gint i;
2668
2669   context_x11->xdnd_have_actions = FALSE;
2670
2671   if (gdk_window_get_window_type (context->source_window) == GDK_WINDOW_FOREIGN)
2672     {
2673       /* Get the XdndActionList, if set */
2674
2675       gdk_x11_display_error_trap_push (display);
2676       if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
2677                               GDK_WINDOW_XID (context->source_window),
2678                               gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
2679                               0, 65536,
2680                               False, XA_ATOM, &type, &format, &nitems,
2681                               &after, &data) == Success &&
2682           type == XA_ATOM)
2683         {
2684           atoms = (Atom *)data;
2685
2686           context->actions = 0;
2687
2688           for (i = 0; i < nitems; i++)
2689             context->actions |= xdnd_action_from_atom (display, atoms[i]);
2690
2691           context_x11->xdnd_have_actions = TRUE;
2692
2693 #ifdef G_ENABLE_DEBUG
2694           if (_gdk_debug_flags & GDK_DEBUG_DND)
2695             {
2696               GString *action_str = g_string_new (NULL);
2697               if (context->actions & GDK_ACTION_MOVE)
2698                 g_string_append(action_str, "MOVE ");
2699               if (context->actions & GDK_ACTION_COPY)
2700                 g_string_append(action_str, "COPY ");
2701               if (context->actions & GDK_ACTION_LINK)
2702                 g_string_append(action_str, "LINK ");
2703               if (context->actions & GDK_ACTION_ASK)
2704                 g_string_append(action_str, "ASK ");
2705
2706               g_message("Xdnd actions = %s", action_str->str);
2707               g_string_free (action_str, TRUE);
2708             }
2709 #endif /* G_ENABLE_DEBUG */
2710
2711         }
2712
2713       if (data)
2714         XFree (data);
2715
2716       gdk_x11_display_error_trap_pop_ignored (display);
2717     }
2718   else
2719     {
2720       /* Local drag
2721        */
2722       GdkDragContext *source_context;
2723
2724       source_context = gdk_drag_context_find (display, TRUE,
2725                                               GDK_WINDOW_XID (context->source_window),
2726                                               GDK_WINDOW_XID (context->dest_window));
2727
2728       if (source_context)
2729         {
2730           context->actions = source_context->actions;
2731           context_x11->xdnd_have_actions = TRUE;
2732         }
2733     }
2734 }
2735
2736 /* We have to make sure that the XdndActionList we keep internally
2737  * is up to date with the XdndActionList on the source window
2738  * because we get no notification, because Xdnd wasn't meant
2739  * to continually send actions. So we select on PropertyChangeMask
2740  * and add this filter.
2741  */
2742 static GdkFilterReturn
2743 xdnd_source_window_filter (GdkXEvent *xev,
2744                            GdkEvent  *event,
2745                            gpointer   cb_data)
2746 {
2747   XEvent *xevent = (XEvent *)xev;
2748   GdkX11DragContext *context_x11 = cb_data;
2749   GdkDisplay *display = GDK_WINDOW_DISPLAY(event->any.window);
2750
2751   if ((xevent->xany.type == PropertyNotify) &&
2752       (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList")))
2753     {
2754       xdnd_read_actions (context_x11);
2755
2756       return GDK_FILTER_REMOVE;
2757     }
2758
2759   return GDK_FILTER_CONTINUE;
2760 }
2761
2762 static void
2763 xdnd_manage_source_filter (GdkDragContext *context,
2764                            GdkWindow      *window,
2765                            gboolean        add_filter)
2766 {
2767   if (!GDK_WINDOW_DESTROYED (window) &&
2768       gdk_window_get_window_type (window) == GDK_WINDOW_FOREIGN)
2769     {
2770       gdk_x11_display_error_trap_push (GDK_WINDOW_DISPLAY (window));
2771
2772       if (add_filter)
2773         {
2774           gdk_window_set_events (window,
2775                                  gdk_window_get_events (window) |
2776                                  GDK_PROPERTY_CHANGE_MASK);
2777           gdk_window_add_filter (window, xdnd_source_window_filter, context);
2778         }
2779       else
2780         {
2781           gdk_window_remove_filter (window,
2782                                     xdnd_source_window_filter,
2783                                     context);
2784           /* Should we remove the GDK_PROPERTY_NOTIFY mask?
2785            * but we might want it for other reasons. (Like
2786            * INCR selection transactions).
2787            */
2788         }
2789
2790       gdk_x11_display_error_trap_pop_ignored (GDK_WINDOW_DISPLAY (window));
2791     }
2792 }
2793
2794 static void
2795 base_precache_atoms (GdkDisplay *display)
2796 {
2797   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
2798
2799   if (!display_x11->base_dnd_atoms_precached)
2800     {
2801       static const char *const precache_atoms[] = {
2802         "ENLIGHTENMENT_DESKTOP",
2803         "WM_STATE",
2804         "XdndAware",
2805         "XdndProxy",
2806         "_MOTIF_DRAG_RECEIVER_INFO"
2807       };
2808
2809       _gdk_x11_precache_atoms (display,
2810                                precache_atoms, G_N_ELEMENTS (precache_atoms));
2811
2812       display_x11->base_dnd_atoms_precached = TRUE;
2813     }
2814 }
2815
2816 static void
2817 xdnd_precache_atoms (GdkDisplay *display)
2818 {
2819   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
2820
2821   if (!display_x11->xdnd_atoms_precached)
2822     {
2823       static const gchar *const precache_atoms[] = {
2824         "XdndActionAsk",
2825         "XdndActionCopy",
2826         "XdndActionLink",
2827         "XdndActionList",
2828         "XdndActionMove",
2829         "XdndActionPrivate",
2830         "XdndDrop",
2831         "XdndEnter",
2832         "XdndFinished",
2833         "XdndLeave",
2834         "XdndPosition",
2835         "XdndSelection",
2836         "XdndStatus",
2837         "XdndTypeList"
2838       };
2839
2840       _gdk_x11_precache_atoms (display,
2841                                precache_atoms, G_N_ELEMENTS (precache_atoms));
2842
2843       display_x11->xdnd_atoms_precached = TRUE;
2844     }
2845 }
2846
2847 static GdkFilterReturn
2848 xdnd_enter_filter (GdkXEvent *xev,
2849                    GdkEvent  *event,
2850                    gpointer   cb_data)
2851 {
2852   GdkDeviceManager *device_manager;
2853   GdkDisplay *display;
2854   GdkX11Display *display_x11;
2855   XEvent *xevent = (XEvent *)xev;
2856   GdkDragContext *context;
2857   GdkX11DragContext *context_x11;
2858   gint i;
2859   Atom type;
2860   int format;
2861   gulong nitems, after;
2862   guchar *data;
2863   Atom *atoms;
2864   guint32 source_window;
2865   gboolean get_types;
2866   gint version;
2867
2868   if (!event->any.window ||
2869       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2870     return GDK_FILTER_CONTINUE;                 /* Not for us */
2871
2872   source_window = xevent->xclient.data.l[0];
2873   get_types = ((xevent->xclient.data.l[1] & 1) != 0);
2874   version = (xevent->xclient.data.l[1] & 0xff000000) >> 24;
2875
2876   display = GDK_WINDOW_DISPLAY (event->any.window);
2877   display_x11 = GDK_X11_DISPLAY (display);
2878
2879   xdnd_precache_atoms (display);
2880
2881   GDK_NOTE (DND,
2882             g_message ("XdndEnter: source_window: %#x, version: %#x",
2883                        source_window, version));
2884
2885   if (version < 3)
2886     {
2887       /* Old source ignore */
2888       GDK_NOTE (DND, g_message ("Ignored old XdndEnter message"));
2889       return GDK_FILTER_REMOVE;
2890     }
2891
2892   if (display_x11->current_dest_drag != NULL)
2893     {
2894       g_object_unref (display_x11->current_dest_drag);
2895       display_x11->current_dest_drag = NULL;
2896     }
2897
2898   context_x11 = (GdkX11DragContext *)g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, NULL);
2899   context = (GdkDragContext *)context_x11;
2900
2901   context->protocol = GDK_DRAG_PROTO_XDND;
2902   context_x11->version = version;
2903
2904   /* FIXME: Should extend DnD protocol to have device info */
2905   device_manager = gdk_display_get_device_manager (display);
2906   gdk_drag_context_set_device (context, gdk_device_manager_get_client_pointer (device_manager));
2907
2908   context->source_window = gdk_x11_window_foreign_new_for_display (display, source_window);
2909   if (!context->source_window)
2910     {
2911       g_object_unref (context);
2912       return GDK_FILTER_REMOVE;
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 Window
3209 _gdk_x11_display_get_drag_protocol (GdkDisplay      *display,
3210                                     Window           xid,
3211                                     GdkDragProtocol *protocol,
3212                                     guint           *version)
3213
3214 {
3215   GdkWindow *window;
3216   Window 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", (guint) xid));
3230           return xid;
3231         }
3232       else if (_gdk_x11_display_is_root_window (display, 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", (guint) 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", (guint) 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         dest_window = gdk_x11_window_foreign_new_for_display (display, recipient);
3336       else
3337         dest_window = NULL;
3338     }
3339   else
3340     {
3341       dest_window = context->dest_window;
3342       if (dest_window)
3343         g_object_ref (dest_window);
3344       *protocol = context->protocol;
3345     }
3346
3347   return dest_window;
3348 }
3349
3350 static gboolean
3351 gdk_x11_drag_context_drag_motion (GdkDragContext *context,
3352                                   GdkWindow      *dest_window,
3353                                   GdkDragProtocol protocol,
3354                                   gint            x_root,
3355                                   gint            y_root,
3356                                   GdkDragAction   suggested_action,
3357                                   GdkDragAction   possible_actions,
3358                                   guint32         time)
3359 {
3360   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3361
3362   context_x11->old_actions = context->actions;
3363   context->actions = possible_actions;
3364
3365   if (context_x11->old_actions != possible_actions)
3366     context_x11->xdnd_actions_set = FALSE;
3367
3368   if (protocol == GDK_DRAG_PROTO_XDND && context_x11->version == 0)
3369     {
3370       /* This ugly hack is necessary since GTK+ doesn't know about
3371        * the XDND protocol version, and in particular doesn't know
3372        * that gdk_drag_find_window_for_screen() has the side-effect
3373        * of setting context_x11->version, and therefore sometimes call
3374        * gdk_drag_motion() without a prior call to
3375        * gdk_drag_find_window_for_screen(). This happens, e.g.
3376        * when GTK+ is proxying DND events to embedded windows.
3377        */
3378       if (dest_window)
3379         {
3380           GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
3381
3382           xdnd_check_dest (display,
3383                            GDK_WINDOW_XID (dest_window),
3384                            &context_x11->version);
3385         }
3386     }
3387
3388   /* When we have a Xdnd target, make sure our XdndActionList
3389    * matches the current actions;
3390    */
3391   if (protocol == GDK_DRAG_PROTO_XDND && !context_x11->xdnd_actions_set)
3392     {
3393       if (dest_window)
3394         {
3395           if (gdk_window_get_window_type (dest_window) == GDK_WINDOW_FOREIGN)
3396             xdnd_set_actions (context_x11);
3397           else if (context->dest_window == dest_window)
3398             {
3399               GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
3400               GdkDragContext *dest_context;
3401
3402               dest_context = gdk_drag_context_find (display, FALSE,
3403                                                     GDK_WINDOW_XID (context->source_window),
3404                                                     GDK_WINDOW_XID (dest_window));
3405
3406               if (dest_context)
3407                 {
3408                   dest_context->actions = context->actions;
3409                   GDK_X11_DRAG_CONTEXT (dest_context)->xdnd_have_actions = TRUE;
3410                 }
3411             }
3412         }
3413     }
3414
3415   if (context->dest_window != dest_window)
3416     {
3417       GdkEvent *temp_event;
3418
3419       /* Send a leave to the last destination */
3420       gdk_drag_do_leave (context_x11, time);
3421       context_x11->drag_status = GDK_DRAG_STATUS_DRAG;
3422
3423       /* Check if new destination accepts drags, and which protocol */
3424
3425       if (dest_window)
3426         {
3427           context->dest_window = dest_window;
3428           context_x11->drop_xid = context_x11->dest_xid;
3429           g_object_ref (context->dest_window);
3430           context->protocol = protocol;
3431
3432           switch (protocol)
3433             {
3434             case GDK_DRAG_PROTO_MOTIF:
3435               motif_send_enter (context_x11, time);
3436               break;
3437
3438             case GDK_DRAG_PROTO_XDND:
3439               xdnd_send_enter (context_x11);
3440               break;
3441
3442             case GDK_DRAG_PROTO_ROOTWIN:
3443             case GDK_DRAG_PROTO_NONE:
3444             default:
3445               break;
3446             }
3447           context_x11->old_action = suggested_action;
3448           context->suggested_action = suggested_action;
3449           context_x11->old_actions = possible_actions;
3450         }
3451       else
3452         {
3453           context->dest_window = NULL;
3454           context_x11->drop_xid = None;
3455           context->action = 0;
3456         }
3457
3458       /* Push a status event, to let the client know that
3459        * the drag changed
3460        */
3461       temp_event = gdk_event_new (GDK_DRAG_STATUS);
3462       temp_event->dnd.window = g_object_ref (context->source_window);
3463       /* We use this to signal a synthetic status. Perhaps
3464        * we should use an extra field...
3465        */
3466       temp_event->dnd.send_event = TRUE;
3467
3468       temp_event->dnd.context = g_object_ref (context);
3469       temp_event->dnd.time = time;
3470       gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
3471
3472       gdk_event_put (temp_event);
3473       gdk_event_free (temp_event);
3474     }
3475   else
3476     {
3477       context_x11->old_action = context->suggested_action;
3478       context->suggested_action = suggested_action;
3479     }
3480
3481   /* Send a drag-motion event */
3482
3483   context_x11->last_x = x_root;
3484   context_x11->last_y = y_root;
3485
3486   if (context->dest_window)
3487     {
3488       if (context_x11->drag_status == GDK_DRAG_STATUS_DRAG)
3489         {
3490           switch (context->protocol)
3491             {
3492             case GDK_DRAG_PROTO_MOTIF:
3493               motif_send_motion (context_x11, x_root, y_root, suggested_action, time);
3494               break;
3495
3496             case GDK_DRAG_PROTO_XDND:
3497               xdnd_send_motion (context_x11, x_root, y_root, suggested_action, time);
3498               break;
3499
3500             case GDK_DRAG_PROTO_ROOTWIN:
3501               {
3502                 GdkEvent *temp_event;
3503                 /* GTK+ traditionally has used application/x-rootwin-drop,
3504                  * but the XDND spec specifies x-rootwindow-drop.
3505                  */
3506                 GdkAtom target1 = gdk_atom_intern_static_string ("application/x-rootwindow-drop");
3507                 GdkAtom target2 = gdk_atom_intern_static_string ("application/x-rootwin-drop");
3508
3509                 if (g_list_find (context->targets,
3510                                  GDK_ATOM_TO_POINTER (target1)) ||
3511                     g_list_find (context->targets,
3512                                  GDK_ATOM_TO_POINTER (target2)))
3513                   context->action = context->suggested_action;
3514                 else
3515                   context->action = 0;
3516
3517                 temp_event = gdk_event_new (GDK_DRAG_STATUS);
3518                 temp_event->dnd.window = g_object_ref (context->source_window);
3519                 temp_event->dnd.send_event = FALSE;
3520                 temp_event->dnd.context = g_object_ref (context);
3521                 temp_event->dnd.time = time;
3522                 gdk_event_set_device (temp_event, gdk_drag_context_get_device (context));
3523
3524                 gdk_event_put (temp_event);
3525                 gdk_event_free (temp_event);
3526               }
3527               break;
3528             case GDK_DRAG_PROTO_NONE:
3529               g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
3530               break;
3531             default:
3532               break;
3533             }
3534         }
3535       else
3536         return TRUE;
3537     }
3538
3539   return FALSE;
3540 }
3541
3542 static void
3543 gdk_x11_drag_context_drag_abort (GdkDragContext *context,
3544                                  guint32         time)
3545 {
3546   gdk_drag_do_leave (GDK_X11_DRAG_CONTEXT (context), time);
3547 }
3548
3549 static void
3550 gdk_x11_drag_context_drag_drop (GdkDragContext *context,
3551                                 guint32         time)
3552 {
3553   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3554
3555   if (context->dest_window)
3556     {
3557       switch (context->protocol)
3558         {
3559         case GDK_DRAG_PROTO_MOTIF:
3560           motif_send_leave (context_x11, time);
3561           motif_send_drop (context_x11, time);
3562           break;
3563
3564         case GDK_DRAG_PROTO_XDND:
3565           xdnd_send_drop (context_x11, time);
3566           break;
3567
3568         case GDK_DRAG_PROTO_ROOTWIN:
3569           g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
3570           break;
3571         case GDK_DRAG_PROTO_NONE:
3572           g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
3573           break;
3574         default:
3575           break;
3576         }
3577     }
3578 }
3579
3580 /* Destination side */
3581
3582 static void
3583 gdk_x11_drag_context_drag_status (GdkDragContext *context,
3584                                   GdkDragAction   action,
3585                                   guint32         time_)
3586 {
3587   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3588   XEvent xev;
3589   GdkDisplay *display;
3590
3591   display = GDK_WINDOW_DISPLAY (context->source_window);
3592
3593   context->action = action;
3594
3595   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3596     {
3597       gboolean need_coords = FALSE;
3598
3599       xev.xclient.type = ClientMessage;
3600       xev.xclient.message_type =
3601         gdk_x11_get_xatom_by_name_for_display (display,
3602                                                "_MOTIF_DRAG_AND_DROP_MESSAGE");
3603       xev.xclient.format = 8;
3604       xev.xclient.window = GDK_WINDOW_XID (context->source_window);
3605
3606       if (context_x11->drag_status == GDK_DRAG_STATUS_ACTION_WAIT)
3607         {
3608           MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED | 0x80;
3609         }
3610       else
3611         {
3612           if ((action != 0) != (context_x11->old_action != 0))
3613             {
3614               if (action != 0)
3615                 {
3616                   MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_ENTER | 0x80;
3617                   need_coords = TRUE;
3618                 }
3619               else
3620                 {
3621                   MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_LEAVE | 0x80;
3622                 }
3623             }
3624           else
3625             {
3626               MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION | 0x80;
3627               need_coords = TRUE;
3628             }
3629         }
3630
3631       MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3632
3633       switch (action)
3634         {
3635         case GDK_ACTION_MOVE:
3636           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_MOVE;
3637           break;
3638         case GDK_ACTION_COPY:
3639           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY;
3640           break;
3641         case GDK_ACTION_LINK:
3642           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_LINK;
3643           break;
3644         default:
3645           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP;
3646           break;
3647         }
3648
3649       if (action)
3650         MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmDROP_SITE_VALID << 4);
3651       else
3652         MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmNO_DROP_SITE << 4);
3653
3654       MOTIF_XCLIENT_LONG (&xev, 1) = time_;
3655
3656       if (need_coords)
3657         {
3658           MOTIF_XCLIENT_SHORT (&xev, 4) = context_x11->last_x;
3659           MOTIF_XCLIENT_SHORT (&xev, 5) = context_x11->last_y;
3660         }
3661       else
3662         MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3663
3664       MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3665       MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3666
3667       if (!_gdk_x11_display_send_xevent (display,
3668                                          GDK_WINDOW_XID (context->source_window),
3669                                          FALSE, 0, &xev))
3670         {
3671           GDK_NOTE (DND,
3672                     g_message ("Send event to %lx failed",
3673                                GDK_WINDOW_XID (context->source_window)));
3674         }
3675     }
3676   else if (context->protocol == GDK_DRAG_PROTO_XDND)
3677     {
3678       xev.xclient.type = ClientMessage;
3679       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus");
3680       xev.xclient.format = 32;
3681       xev.xclient.window = GDK_WINDOW_XID (context->source_window);
3682
3683       xev.xclient.data.l[0] = GDK_WINDOW_XID (context->dest_window);
3684       xev.xclient.data.l[1] = (action != 0) ? (2 | 1) : 0;
3685       xev.xclient.data.l[2] = 0;
3686       xev.xclient.data.l[3] = 0;
3687       xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
3688       if (!xdnd_send_xevent (context_x11, context->source_window, FALSE, &xev))
3689         {
3690           GDK_NOTE (DND,
3691                     g_message ("Send event to %lx failed",
3692                                GDK_WINDOW_XID (context->source_window)));
3693         }
3694     }
3695
3696   context_x11->old_action = action;
3697 }
3698
3699 static void
3700 gdk_x11_drag_context_drop_reply (GdkDragContext *context,
3701                                  gboolean        accepted,
3702                                  guint32         time_)
3703 {
3704   GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (context);
3705
3706   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3707     {
3708       GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
3709       XEvent xev;
3710
3711       xev.xclient.type = ClientMessage;
3712       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
3713                                                                         "_MOTIF_DRAG_AND_DROP_MESSAGE");
3714       xev.xclient.format = 8;
3715
3716       MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START | 0x80;
3717       MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3718       if (accepted)
3719         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY |
3720                                        (XmDROP_SITE_VALID << 4) |
3721                                        (XmDROP_NOOP << 8) |
3722                                        (XmDROP << 12);
3723       else
3724         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP |
3725                                        (XmNO_DROP_SITE << 4) |
3726                                        (XmDROP_NOOP << 8) |
3727                                        (XmDROP_CANCEL << 12);
3728       MOTIF_XCLIENT_SHORT (&xev, 2) = context_x11->last_x;
3729       MOTIF_XCLIENT_SHORT (&xev, 3) = context_x11->last_y;
3730       MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3731       MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3732       MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3733
3734       _gdk_x11_display_send_xevent (display,
3735                                     GDK_WINDOW_XID (context->source_window),
3736                                     FALSE, 0, &xev);
3737     }
3738 }
3739
3740 static void
3741 gdk_x11_drag_context_drop_finish (GdkDragContext *context,
3742                                   gboolean        success,
3743                                   guint32         time)
3744 {
3745   if (context->protocol == GDK_DRAG_PROTO_XDND)
3746     {
3747       GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
3748       XEvent xev;
3749
3750       xev.xclient.type = ClientMessage;
3751       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished");
3752       xev.xclient.format = 32;
3753       xev.xclient.window = GDK_WINDOW_XID (context->source_window);
3754
3755       xev.xclient.data.l[0] = GDK_WINDOW_XID (context->dest_window);
3756       if (success)
3757         {
3758           xev.xclient.data.l[1] = 1;
3759           xev.xclient.data.l[2] = xdnd_action_to_atom (display,
3760                                                        context->action);
3761         }
3762       else
3763         {
3764           xev.xclient.data.l[1] = 0;
3765           xev.xclient.data.l[2] = None;
3766         }
3767       xev.xclient.data.l[3] = 0;
3768       xev.xclient.data.l[4] = 0;
3769
3770       if (!xdnd_send_xevent (GDK_X11_DRAG_CONTEXT (context), context->source_window, FALSE, &xev))
3771         {
3772           GDK_NOTE (DND,
3773                     g_message ("Send event to %lx failed",
3774                                GDK_WINDOW_XID (context->source_window)));
3775         }
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 }