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