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