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