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