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