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