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