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