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