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