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