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