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