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