]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkdnd-x11.c
Don't leak references to the drag context. (#144324, Alex Larsson)
[~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                 {
2197                   gdk_event_put (&temp_event);
2198                   g_object_unref (tmp_event.dnd.context);
2199                 }
2200               
2201               return TRUE;
2202             }
2203         }
2204     }
2205
2206   xwindow = GDK_WINDOW_XWINDOW (window);
2207   
2208   if (_gdk_x11_display_is_root_window (display, xwindow))
2209     event_mask = ButtonPressMask;
2210   else
2211     event_mask = 0;
2212   
2213   send_client_message_async (context, xwindow, propagate, event_mask,
2214                              &event_send->xclient);
2215
2216   return TRUE;
2217 }
2218  
2219 static void
2220 xdnd_send_enter (GdkDragContext *context)
2221 {
2222   XEvent xev;
2223   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2224   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->dest_window);
2225
2226   xev.xclient.type = ClientMessage;
2227   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndEnter");
2228   xev.xclient.format = 32;
2229   xev.xclient.window = private->drop_xid ? 
2230                            private->drop_xid : 
2231                            GDK_DRAWABLE_XID (context->dest_window);
2232   xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
2233   xev.xclient.data.l[1] = (3 << 24); /* version */
2234   xev.xclient.data.l[2] = 0;
2235   xev.xclient.data.l[3] = 0;
2236   xev.xclient.data.l[4] = 0;
2237
2238   if (g_list_length (context->targets) > 3)
2239     {
2240       if (!private->xdnd_targets_set)
2241         xdnd_set_targets (context);
2242       xev.xclient.data.l[1] |= 1;
2243     }
2244   else
2245     {
2246       GList *tmp_list = context->targets;
2247       gint i = 2;
2248
2249       while (tmp_list)
2250         {
2251           xev.xclient.data.l[i] = gdk_x11_atom_to_xatom_for_display (display,
2252                                                                      GDK_POINTER_TO_ATOM (tmp_list->data));
2253           tmp_list = tmp_list->next;
2254           i++;
2255         }
2256     }
2257
2258   if (!xdnd_send_xevent (context, context->dest_window,
2259                          FALSE, &xev))
2260     {
2261       GDK_NOTE (DND, 
2262                 g_message ("Send event to %lx failed",
2263                            GDK_DRAWABLE_XID (context->dest_window)));
2264       g_object_unref (context->dest_window);
2265       context->dest_window = NULL;
2266     }
2267 }
2268
2269 static void
2270 xdnd_send_leave (GdkDragContext *context)
2271 {
2272   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
2273   XEvent xev;
2274
2275   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2276
2277   xev.xclient.type = ClientMessage;
2278   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndLeave");
2279   xev.xclient.format = 32;
2280   xev.xclient.window = private->drop_xid ? 
2281                            private->drop_xid : 
2282                            GDK_DRAWABLE_XID (context->dest_window);
2283   xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
2284   xev.xclient.data.l[1] = 0;
2285   xev.xclient.data.l[2] = 0;
2286   xev.xclient.data.l[3] = 0;
2287   xev.xclient.data.l[4] = 0;
2288
2289   if (!xdnd_send_xevent (context, context->dest_window,
2290                          FALSE, &xev))
2291     {
2292       GDK_NOTE (DND, 
2293                 g_message ("Send event to %lx failed",
2294                            GDK_DRAWABLE_XID (context->dest_window)));
2295       g_object_unref (context->dest_window);
2296       context->dest_window = NULL;
2297     }
2298 }
2299
2300 static void
2301 xdnd_send_drop (GdkDragContext *context, guint32 time)
2302 {
2303   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2304   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
2305   XEvent xev;
2306
2307   xev.xclient.type = ClientMessage;
2308   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndDrop");
2309   xev.xclient.format = 32;
2310   xev.xclient.window = private->drop_xid ? 
2311                            private->drop_xid : 
2312                            GDK_DRAWABLE_XID (context->dest_window);
2313   xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
2314   xev.xclient.data.l[1] = 0;
2315   xev.xclient.data.l[2] = time;
2316   xev.xclient.data.l[3] = 0;
2317   xev.xclient.data.l[4] = 0;
2318
2319   if (!xdnd_send_xevent (context, context->dest_window,
2320                          FALSE, &xev))
2321     {
2322       GDK_NOTE (DND, 
2323                 g_message ("Send event to %lx failed",
2324                            GDK_DRAWABLE_XID (context->dest_window)));
2325       g_object_unref (context->dest_window);
2326       context->dest_window = NULL;
2327     }
2328 }
2329
2330 static void
2331 xdnd_send_motion (GdkDragContext *context,
2332                   gint            x_root, 
2333                   gint            y_root,
2334                   GdkDragAction   action,
2335                   guint32         time)
2336 {
2337   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2338   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
2339   XEvent xev;
2340
2341   xev.xclient.type = ClientMessage;
2342   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndPosition");
2343   xev.xclient.format = 32;
2344   xev.xclient.window = private->drop_xid ? 
2345                            private->drop_xid : 
2346                            GDK_DRAWABLE_XID (context->dest_window);
2347   xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
2348   xev.xclient.data.l[1] = 0;
2349   xev.xclient.data.l[2] = (x_root << 16) | y_root;
2350   xev.xclient.data.l[3] = time;
2351   xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
2352
2353   if (!xdnd_send_xevent (context, context->dest_window,
2354                          FALSE, &xev))
2355     {
2356       GDK_NOTE (DND, 
2357                 g_message ("Send event to %lx failed",
2358                            GDK_DRAWABLE_XID (context->dest_window)));
2359       g_object_unref (context->dest_window);
2360       context->dest_window = NULL;
2361     }
2362   private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
2363 }
2364
2365 static guint32
2366 xdnd_check_dest (GdkDisplay *display,
2367                  Window      win)
2368 {
2369   gboolean retval = FALSE;
2370   Atom type = None;
2371   int format;
2372   unsigned long nitems, after;
2373   Atom *version;
2374   Window *proxy_data;
2375   Window proxy;
2376   Atom xdnd_proxy_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndProxy");
2377   Atom xdnd_aware_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndAware");
2378
2379   proxy = None;
2380
2381   gdk_error_trap_push ();
2382   
2383   if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win, 
2384                           xdnd_proxy_atom, 0, 
2385                           1, False, AnyPropertyType,
2386                           &type, &format, &nitems, &after, 
2387                           (guchar **)&proxy_data) == Success)
2388     {
2389       if (type != None)
2390         {
2391           if ((format == 32) && (nitems == 1))
2392             {
2393               proxy = *proxy_data;
2394             }
2395           else
2396             GDK_NOTE (DND, 
2397                       g_warning ("Invalid XdndOwner "
2398                                  "property on window %ld\n", win));
2399           
2400           XFree (proxy_data);
2401         }
2402       
2403       if ((XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), proxy ? proxy : win,
2404                                xdnd_aware_atom, 0, 
2405                                1, False, AnyPropertyType,
2406                                &type, &format, &nitems, &after, 
2407                                (guchar **)&version) == Success) &&
2408           type != None)
2409         {
2410           if ((format == 32) && (nitems == 1))
2411             {
2412               if (*version >= 3)
2413                 retval = TRUE;
2414             }
2415           else
2416             GDK_NOTE (DND, 
2417                       g_warning ("Invalid XdndAware property on window %ld\n", win));
2418           
2419           XFree (version);
2420         }
2421     }
2422
2423   gdk_error_trap_pop ();
2424   
2425   return retval ? (proxy ? proxy : win) : None;
2426 }
2427
2428 /* Target side */
2429
2430 static void
2431 xdnd_read_actions (GdkDragContext *context)
2432 {
2433   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2434   Atom type;
2435   int format;
2436   gulong nitems, after;
2437   Atom *data;
2438
2439   gint i;
2440   
2441   PRIVATE_DATA (context)->xdnd_have_actions = FALSE;
2442
2443   if (gdk_window_get_window_type (context->source_window) == GDK_WINDOW_FOREIGN)
2444     {
2445       /* Get the XdndActionList, if set */
2446       
2447       gdk_error_trap_push ();
2448       
2449       if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
2450                               GDK_DRAWABLE_XID (context->source_window),
2451                               gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
2452                               0, 65536,
2453                               False, XA_ATOM, &type, &format, &nitems,
2454                               &after, (guchar **)&data) == Success &&
2455           type == XA_ATOM)
2456         {
2457           context->actions = 0;
2458           
2459           for (i=0; i<nitems; i++)
2460             context->actions |= xdnd_action_from_atom (display, data[i]);
2461           
2462           PRIVATE_DATA (context)->xdnd_have_actions = TRUE;
2463           
2464 #ifdef G_ENABLE_DEBUG
2465           if (_gdk_debug_flags & GDK_DEBUG_DND)
2466             {
2467               GString *action_str = g_string_new (NULL);
2468               if (context->actions & GDK_ACTION_MOVE)
2469                 g_string_append(action_str, "MOVE ");
2470               if (context->actions & GDK_ACTION_COPY)
2471                 g_string_append(action_str, "COPY ");
2472               if (context->actions & GDK_ACTION_LINK)
2473                 g_string_append(action_str, "LINK ");
2474               if (context->actions & GDK_ACTION_ASK)
2475                 g_string_append(action_str, "ASK ");
2476               
2477               g_message("Xdnd actions = %s", action_str->str);
2478               g_string_free (action_str, TRUE);
2479             }
2480 #endif /* G_ENABLE_DEBUG */
2481           
2482           XFree(data);
2483         }
2484       
2485       gdk_error_trap_pop ();
2486     }
2487   else
2488     {
2489       /* Local drag
2490        */
2491       GdkDragContext *source_context;
2492
2493       source_context = gdk_drag_context_find (display, TRUE,
2494                                               GDK_DRAWABLE_XID (context->source_window),
2495                                               GDK_DRAWABLE_XID (context->dest_window));
2496
2497       if (source_context)
2498         {
2499           context->actions = source_context->actions;
2500           PRIVATE_DATA (context)->xdnd_have_actions = TRUE;
2501         }
2502     }
2503 }
2504
2505 /* We have to make sure that the XdndActionList we keep internally
2506  * is up to date with the XdndActionList on the source window
2507  * because we get no notification, because Xdnd wasn't meant
2508  * to continually send actions. So we select on PropertyChangeMask
2509  * and add this filter.
2510  */
2511 static GdkFilterReturn 
2512 xdnd_source_window_filter (GdkXEvent *xev,
2513                            GdkEvent  *event,
2514                            gpointer   cb_data)
2515 {
2516   XEvent *xevent = (XEvent *)xev;
2517   GdkDragContext *context = cb_data;
2518   GdkDisplay *display = GDK_WINDOW_DISPLAY(event->any.window);
2519
2520   if ((xevent->xany.type == PropertyNotify) &&
2521       (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList")))
2522     {
2523       xdnd_read_actions (context);
2524
2525       return GDK_FILTER_REMOVE;
2526     }
2527
2528   return GDK_FILTER_CONTINUE;
2529 }
2530
2531 static void
2532 xdnd_manage_source_filter (GdkDragContext *context,
2533                            GdkWindow      *window,
2534                            gboolean        add_filter)
2535 {
2536   if (!GDK_WINDOW_DESTROYED (window) &&
2537       gdk_window_get_window_type (window) == GDK_WINDOW_FOREIGN)
2538     {
2539       gdk_error_trap_push ();
2540
2541       if (add_filter)
2542         {
2543           gdk_window_set_events (window,
2544                                  gdk_window_get_events (window) |
2545                                  GDK_PROPERTY_CHANGE_MASK);
2546           gdk_window_add_filter (window, xdnd_source_window_filter, context);
2547         }
2548       else
2549         {
2550           gdk_window_remove_filter (window,
2551                                     xdnd_source_window_filter,
2552                                     context);
2553           /* Should we remove the GDK_PROPERTY_NOTIFY mask?
2554            * but we might want it for other reasons. (Like
2555            * INCR selection transactions).
2556            */
2557         }
2558       
2559       gdk_display_sync (gdk_drawable_get_display (window));
2560       gdk_error_trap_pop ();  
2561     }
2562 }
2563
2564 static void
2565 base_precache_atoms (GdkDisplay *display)
2566 {
2567   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
2568
2569   if (!display_x11->base_dnd_atoms_precached)
2570     {
2571       static const char *const precache_atoms[] = {
2572         "ENLIGHTENMENT_DESKTOP",
2573         "WM_STATE",
2574         "XdndAware",
2575         "XdndProxy",
2576         "_MOTIF_DRAG_RECEIVER_INFO"
2577       };
2578
2579       _gdk_x11_precache_atoms (display,
2580                                precache_atoms, G_N_ELEMENTS (precache_atoms));
2581
2582       display_x11->base_dnd_atoms_precached = TRUE;
2583     }
2584 }
2585
2586 static void
2587 xdnd_precache_atoms (GdkDisplay *display)
2588 {
2589   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
2590
2591   if (!display_x11->xdnd_atoms_precached)
2592     {
2593       static const char *const precache_atoms[] = {
2594         "XdndActionAsk",
2595         "XdndActionCopy",
2596         "XdndActionLink",
2597         "XdndActionList",
2598         "XdndActionMove",
2599         "XdndActionPrivate",
2600         "XdndDrop",
2601         "XdndEnter",
2602         "XdndFinished",
2603         "XdndLeave",
2604         "XdndPosition",
2605         "XdndSelection",
2606         "XdndStatus",
2607         "XdndTypeList"
2608       };
2609
2610       _gdk_x11_precache_atoms (display,
2611                                precache_atoms, G_N_ELEMENTS (precache_atoms));
2612
2613       display_x11->xdnd_atoms_precached = TRUE;
2614     }
2615 }
2616
2617 static GdkFilterReturn 
2618 xdnd_enter_filter (GdkXEvent *xev,
2619                    GdkEvent  *event,
2620                    gpointer   cb_data)
2621 {
2622   GdkDisplay *display;
2623   GdkDisplayX11 *display_x11;
2624   XEvent *xevent = (XEvent *)xev;
2625   GdkDragContext *new_context;
2626   gint i;
2627   
2628   Atom type;
2629   int format;
2630   gulong nitems, after;
2631   Atom *data;
2632
2633   guint32 source_window;
2634   gboolean get_types;
2635   gint version;
2636
2637   if (!event->any.window ||
2638       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2639     return GDK_FILTER_CONTINUE;                 /* Not for us */
2640
2641   source_window = xevent->xclient.data.l[0];
2642   get_types = ((xevent->xclient.data.l[1] & 1) != 0);
2643   version = (xevent->xclient.data.l[1] & 0xff000000) >> 24;
2644   
2645   display = GDK_DRAWABLE_DISPLAY (event->any.window);
2646   display_x11 = GDK_DISPLAY_X11 (display);
2647
2648   xdnd_precache_atoms (display);
2649
2650   GDK_NOTE (DND, 
2651             g_message ("XdndEnter: source_window: %#x, version: %#x",
2652                        source_window, version));
2653
2654   if (version != 3)
2655     {
2656       /* Old source ignore */
2657       GDK_NOTE (DND, g_message ("Ignored old XdndEnter message"));
2658       return GDK_FILTER_REMOVE;
2659     }
2660   
2661   if (display_x11->current_dest_drag != NULL)
2662     {
2663       g_object_unref (display_x11->current_dest_drag);
2664       display_x11->current_dest_drag = NULL;
2665     }
2666
2667   new_context = gdk_drag_context_new ();
2668   new_context->protocol = GDK_DRAG_PROTO_XDND;
2669   new_context->is_source = FALSE;
2670
2671   new_context->source_window = gdk_window_lookup_for_display (display, source_window);
2672   if (new_context->source_window)
2673     g_object_ref (new_context->source_window);
2674   else
2675     {
2676       new_context->source_window = gdk_window_foreign_new_for_display (display, source_window);
2677       if (!new_context->source_window)
2678         {
2679           g_object_unref (new_context);
2680           return GDK_FILTER_REMOVE;
2681         }
2682     }
2683   new_context->dest_window = event->any.window;
2684   g_object_ref (new_context->dest_window);
2685
2686   new_context->targets = NULL;
2687   if (get_types)
2688     {
2689       gdk_error_trap_push ();
2690       XGetWindowProperty (GDK_DRAWABLE_XDISPLAY (event->any.window), 
2691                           source_window, 
2692                           gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
2693                           0, 65536,
2694                           False, XA_ATOM, &type, &format, &nitems,
2695                           &after, (guchar **)&data);
2696
2697       if (gdk_error_trap_pop () || (format != 32) || (type != XA_ATOM))
2698         {
2699           g_object_unref (new_context);
2700           return GDK_FILTER_REMOVE;
2701         }
2702
2703       for (i=0; i<nitems; i++)
2704         new_context->targets = 
2705           g_list_append (new_context->targets,
2706                          GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display,
2707                                                                                  data[i])));
2708
2709       XFree(data);
2710     }
2711   else
2712     {
2713       for (i=0; i<3; i++)
2714         if (xevent->xclient.data.l[2+i])
2715           new_context->targets =
2716             g_list_append (new_context->targets,
2717                            GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display, 
2718                                                                                    xevent->xclient.data.l[2+i])));
2719     }
2720
2721 #ifdef G_ENABLE_DEBUG
2722   if (_gdk_debug_flags & GDK_DEBUG_DND)
2723     print_target_list (new_context->targets);
2724 #endif /* G_ENABLE_DEBUG */
2725
2726   xdnd_manage_source_filter (new_context, new_context->source_window, TRUE);
2727   xdnd_read_actions (new_context);
2728
2729   event->dnd.type = GDK_DRAG_ENTER;
2730   event->dnd.context = new_context;
2731   g_object_ref (new_context);
2732
2733   display_x11->current_dest_drag = new_context;
2734
2735   return GDK_FILTER_TRANSLATE;
2736 }
2737
2738 static GdkFilterReturn 
2739 xdnd_leave_filter (GdkXEvent *xev,
2740                    GdkEvent  *event,
2741                    gpointer   data)
2742 {
2743   XEvent *xevent = (XEvent *)xev;
2744   guint32 source_window = xevent->xclient.data.l[0];
2745   GdkDisplay *display;
2746   GdkDisplayX11 *display_x11;
2747
2748   if (!event->any.window ||
2749       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2750     return GDK_FILTER_CONTINUE;                 /* Not for us */
2751  
2752   GDK_NOTE (DND, 
2753             g_message ("XdndLeave: source_window: %#x",
2754                        source_window));
2755
2756   display = GDK_DRAWABLE_DISPLAY (event->any.window);
2757   display_x11 = GDK_DISPLAY_X11 (display);
2758
2759   xdnd_precache_atoms (display);
2760
2761   if ((display_x11->current_dest_drag != NULL) &&
2762       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2763       (GDK_DRAWABLE_XID (display_x11->current_dest_drag->source_window) == source_window))
2764     {
2765       event->dnd.type = GDK_DRAG_LEAVE;
2766       /* Pass ownership of context to the event */
2767       event->dnd.context = display_x11->current_dest_drag;
2768
2769       display_x11->current_dest_drag = NULL;
2770
2771       return GDK_FILTER_TRANSLATE;
2772     }
2773   else
2774     return GDK_FILTER_REMOVE;
2775 }
2776
2777 static GdkFilterReturn 
2778 xdnd_position_filter (GdkXEvent *xev,
2779                       GdkEvent  *event,
2780                       gpointer   data)
2781 {
2782   XEvent *xevent = (XEvent *)xev;
2783   guint32 source_window = xevent->xclient.data.l[0];
2784   gint16 x_root = xevent->xclient.data.l[2] >> 16;
2785   gint16 y_root = xevent->xclient.data.l[2] & 0xffff;
2786   guint32 time = xevent->xclient.data.l[3];
2787   Atom action = xevent->xclient.data.l[4];
2788
2789   GdkDisplay *display;
2790   GdkDisplayX11 *display_x11;
2791
2792    if (!event->any.window ||
2793        gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2794      return GDK_FILTER_CONTINUE;                        /* Not for us */
2795    
2796   GDK_NOTE (DND, 
2797             g_message ("XdndPosition: source_window: %#x position: (%d, %d)  time: %d  action: %ld",
2798                        source_window, x_root, y_root, time, action));
2799
2800   display = GDK_DRAWABLE_DISPLAY (event->any.window);
2801   display_x11 = GDK_DISPLAY_X11 (display);
2802   
2803   xdnd_precache_atoms (display);
2804
2805   if ((display_x11->current_dest_drag != NULL) &&
2806       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2807       (GDK_DRAWABLE_XID (display_x11->current_dest_drag->source_window) == source_window))
2808     {
2809       event->dnd.type = GDK_DRAG_MOTION;
2810       event->dnd.context = display_x11->current_dest_drag;
2811       g_object_ref (display_x11->current_dest_drag);
2812
2813       event->dnd.time = time;
2814
2815       display_x11->current_dest_drag->suggested_action = xdnd_action_from_atom (display, action);
2816       
2817       if (!(PRIVATE_DATA (display_x11->current_dest_drag))->xdnd_have_actions)
2818         display_x11->current_dest_drag->actions = display_x11->current_dest_drag->suggested_action;
2819
2820       event->dnd.x_root = x_root;
2821       event->dnd.y_root = y_root;
2822
2823       (PRIVATE_DATA (display_x11->current_dest_drag))->last_x = x_root;
2824       (PRIVATE_DATA (display_x11->current_dest_drag))->last_y = y_root;
2825       
2826       return GDK_FILTER_TRANSLATE;
2827     }
2828
2829   return GDK_FILTER_REMOVE;
2830 }
2831
2832 static GdkFilterReturn 
2833 xdnd_drop_filter (GdkXEvent *xev,
2834                   GdkEvent  *event,
2835                   gpointer   data)
2836 {
2837   XEvent *xevent = (XEvent *)xev;
2838   guint32 source_window = xevent->xclient.data.l[0];
2839   guint32 time = xevent->xclient.data.l[2];
2840   GdkDisplay *display;
2841   GdkDisplayX11 *display_x11;
2842   
2843   if (!event->any.window ||
2844       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2845     return GDK_FILTER_CONTINUE;                 /* Not for us */
2846   
2847   GDK_NOTE (DND, 
2848             g_message ("XdndDrop: source_window: %#x  time: %d",
2849                        source_window, time));
2850
2851   display = GDK_DRAWABLE_DISPLAY (event->any.window);
2852   display_x11 = GDK_DISPLAY_X11 (display);
2853
2854   xdnd_precache_atoms (display);
2855
2856   if ((display_x11->current_dest_drag != NULL) &&
2857       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2858       (GDK_DRAWABLE_XID (display_x11->current_dest_drag->source_window) == source_window))
2859     {
2860       GdkDragContextPrivateX11 *private;
2861       private = PRIVATE_DATA (display_x11->current_dest_drag);
2862
2863       event->dnd.type = GDK_DROP_START;
2864
2865       event->dnd.context = display_x11->current_dest_drag;
2866       g_object_ref (display_x11->current_dest_drag);
2867
2868       event->dnd.time = time;
2869       event->dnd.x_root = private->last_x;
2870       event->dnd.y_root = private->last_y;
2871       
2872       return GDK_FILTER_TRANSLATE;
2873     }
2874
2875   return GDK_FILTER_REMOVE;
2876 }
2877
2878 /*************************************************************
2879  ************************** Public API ***********************
2880  *************************************************************/
2881 void
2882 _gdk_dnd_init (GdkDisplay *display)
2883 {
2884   int i;
2885   init_byte_order ();
2886
2887   gdk_display_add_client_message_filter (
2888         display,
2889         gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE),
2890         motif_dnd_filter, NULL);
2891   
2892   for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++)
2893     {
2894       gdk_display_add_client_message_filter (
2895         display,
2896         gdk_atom_intern (xdnd_filters[i].atom_name, FALSE),
2897         xdnd_filters[i].func, NULL);
2898     }
2899 }                     
2900
2901 /* Source side */
2902
2903 static void
2904 gdk_drag_do_leave (GdkDragContext *context, guint32 time)
2905 {
2906   if (context->dest_window)
2907     {
2908       switch (context->protocol)
2909         {
2910         case GDK_DRAG_PROTO_MOTIF:
2911           motif_send_leave (context, time);
2912           break;
2913         case GDK_DRAG_PROTO_XDND:
2914           xdnd_send_leave (context);
2915           break;
2916         case GDK_DRAG_PROTO_ROOTWIN:
2917         case GDK_DRAG_PROTO_NONE:
2918         default:
2919           break;
2920         }
2921
2922       g_object_unref (context->dest_window);
2923       context->dest_window = NULL;
2924     }
2925 }
2926
2927 /**
2928  * gdk_drag_begin:
2929  * @window: the source window for this drag.
2930  * @targets: the list of offered targets.
2931  * 
2932  * Starts a drag and creates a new drag context for it.
2933  *
2934  * This function is called by the drag source.
2935  * 
2936  * Return value: a newly created #GdkDragContext.
2937  **/
2938 GdkDragContext * 
2939 gdk_drag_begin (GdkWindow     *window,
2940                 GList         *targets)
2941 {
2942   GdkDragContext *new_context;
2943   
2944   g_return_val_if_fail (window != NULL, NULL);
2945
2946   new_context = gdk_drag_context_new ();
2947   new_context->is_source = TRUE;
2948   new_context->source_window = window;
2949   g_object_ref (window);
2950
2951   new_context->targets = g_list_copy (targets);
2952   precache_target_list (new_context);
2953   
2954   new_context->actions = 0;
2955
2956   return new_context;
2957 }
2958
2959 /**
2960  * gdk_drag_get_protocol_for_display:
2961  * @display: the #GdkDisplay where the destination window resides
2962  * @xid: the X id of the destination window.
2963  * @protocol: location where the supported DND protocol is returned.
2964  * @returns: the X id of the window where the drop should happen. This 
2965  *     may be @xid or the X id of a proxy window, or None if @xid doesn't
2966  *     support Drag and Drop.
2967  *
2968  * Finds out the DND protocol supported by a window.
2969  *
2970  * Since: 2.2
2971  */ 
2972 guint32
2973 gdk_drag_get_protocol_for_display (GdkDisplay      *display,
2974                                    guint32          xid,
2975                                    GdkDragProtocol *protocol)
2976 {
2977   GdkWindow *window;
2978   guint32 retval;
2979   g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
2980
2981   base_precache_atoms (display);
2982
2983   /* Check for a local drag
2984    */
2985   window = gdk_window_lookup_for_display (display, xid);
2986   if (window &&
2987       gdk_window_get_window_type (window) != GDK_WINDOW_FOREIGN)
2988     {
2989       if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
2990         {
2991           *protocol = GDK_DRAG_PROTO_XDND;
2992           xdnd_precache_atoms (display);
2993           GDK_NOTE (DND, g_message ("Entering local Xdnd window %#x\n", xid));
2994           return xid;
2995         }
2996       else
2997         return None;
2998     }
2999   
3000   if ((retval = xdnd_check_dest (display, xid)))
3001     {
3002       *protocol = GDK_DRAG_PROTO_XDND;
3003       xdnd_precache_atoms (display);
3004       GDK_NOTE (DND, g_message ("Entering Xdnd window %#x\n", xid));
3005       return retval;
3006     }
3007   else if ((retval = motif_check_dest (display, xid)))
3008     {
3009       *protocol = GDK_DRAG_PROTO_MOTIF;
3010       GDK_NOTE (DND, g_message ("Entering motif window %#x\n", xid));
3011       return retval;
3012     }
3013   else
3014     {
3015       /* Check if this is a root window */
3016
3017       gboolean rootwin = FALSE;
3018       Atom type = None;
3019       int format;
3020       unsigned long nitems, after;
3021       unsigned char *data;
3022
3023       if (_gdk_x11_display_is_root_window (display, (Window) xid))
3024         rootwin = TRUE;
3025
3026       gdk_error_trap_push ();
3027       
3028       if (!rootwin)
3029         {
3030           if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xid,
3031                                   gdk_x11_get_xatom_by_name_for_display (display, "ENLIGHTENMENT_DESKTOP"),
3032                                   0, 0, False, AnyPropertyType,
3033                                   &type, &format, &nitems, &after, &data) == Success &&
3034               type != None)
3035             {
3036               XFree (data);
3037               rootwin = TRUE;
3038             }
3039         }
3040
3041       /* Until I find out what window manager the next one is for,
3042        * I'm leaving it commented out. It's supported in the
3043        * xscreensaver sources, though.
3044        */
3045 #if 0
3046       if (!rootwin)
3047         {
3048           if (XGetWindowProperty (gdk_display, win,
3049                                   gdk_x11_get_xatom_by_name ("__SWM_VROOT"),
3050                                   0, 0, False, AnyPropertyType,
3051                                   &type, &format, &nitems, &data) &&
3052               type != None)
3053             rootwin = TRUE;
3054         }
3055 #endif      
3056
3057       gdk_error_trap_pop ();
3058
3059       if (rootwin)
3060         {
3061           *protocol = GDK_DRAG_PROTO_ROOTWIN;
3062           return xid;
3063         }
3064     }
3065
3066   *protocol = GDK_DRAG_PROTO_NONE;
3067   return None;
3068 }
3069
3070 static GdkWindowCache *
3071 drag_context_find_window_cache (GdkDragContext  *context,
3072                                 GdkScreen       *screen)
3073 {
3074   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
3075   GSList *tmp_list;
3076   GdkWindowCache *cache;
3077
3078   for (tmp_list = private->window_caches; tmp_list; tmp_list = tmp_list->next)
3079     {
3080       cache = tmp_list->data;
3081       if (cache->screen == screen)
3082         return cache;
3083     }
3084
3085   cache = gdk_window_cache_new (screen);
3086   private->window_caches = g_slist_prepend (private->window_caches, cache);
3087   
3088   return cache;
3089 }
3090
3091 /**
3092  * gdk_drag_find_window_for_screen:
3093  * @context: a #GdkDragContext
3094  * @drag_window: a window which may be at the pointer position, but
3095  * should be ignored, since it is put up by the drag source as an icon.
3096  * @screen: the screen where the destination window is sought. 
3097  * @x_root: the x position of the pointer in root coordinates.
3098  * @y_root: the y position of the pointer in root coordinates.
3099  * @dest_window: location to store the destination window in.
3100  * @protocol: location to store the DND protocol in.
3101  * 
3102  * Finds the destination window and DND protocol to use at the
3103  * given pointer position. 
3104  *
3105  * This function is called by the drag source to obtain the 
3106  * @dest_window and @protocol parameters for gdk_drag_motion().
3107  *
3108  * Since: 2.2
3109  **/
3110 void
3111 gdk_drag_find_window_for_screen (GdkDragContext  *context,
3112                                  GdkWindow       *drag_window,
3113                                  GdkScreen       *screen,
3114                                  gint             x_root,
3115                                  gint             y_root,
3116                                  GdkWindow      **dest_window,
3117                                  GdkDragProtocol *protocol)
3118 {
3119   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
3120   GdkWindowCache *window_cache;
3121   GdkDisplay *display;
3122   Window dest;
3123
3124   g_return_if_fail (context != NULL);
3125
3126   display = GDK_WINDOW_DISPLAY (context->source_window);
3127
3128   window_cache = drag_context_find_window_cache (context, screen);
3129
3130   dest = get_client_window_at_coords (window_cache,
3131                                       drag_window ? 
3132                                       GDK_DRAWABLE_XID (drag_window) : None,
3133                                       x_root, y_root);
3134
3135   if (private->dest_xid != dest)
3136     {
3137       Window recipient;
3138       private->dest_xid = dest;
3139
3140       /* Check if new destination accepts drags, and which protocol */
3141
3142       /* There is some ugliness here. We actually need to pass
3143        * _three_ pieces of information to drag_motion - dest_window,
3144        * protocol, and the XID of the unproxied window. The first
3145        * two are passed explicitely, the third implicitly through
3146        * protocol->dest_xid.
3147        */
3148       if ((recipient = gdk_drag_get_protocol_for_display (display, dest, protocol)))
3149         {
3150           *dest_window = gdk_window_lookup_for_display (display, recipient);
3151           if (*dest_window)
3152             g_object_ref (*dest_window);
3153           else
3154             *dest_window = gdk_window_foreign_new_for_display (display, recipient);
3155         }
3156       else
3157         *dest_window = NULL;
3158     }
3159   else
3160     {
3161       *dest_window = context->dest_window;
3162       if (*dest_window)
3163         g_object_ref (*dest_window);
3164       *protocol = context->protocol;
3165     }
3166 }
3167
3168 /**
3169  * gdk_drag_motion:
3170  * @context: a #GdkDragContext.
3171  * @dest_window: the new destination window, obtained by 
3172  *     gdk_drag_find_window().
3173  * @protocol: the DND protocol in use, obtained by gdk_drag_find_window().
3174  * @x_root: the x position of the pointer in root coordinates.
3175  * @y_root: the y position of the pointer in root coordinates.
3176  * @suggested_action: the suggested action.
3177  * @possible_actions: the possible actions.
3178  * @time_: the timestamp for this operation.
3179  * 
3180  * Updates the drag context when the pointer moves or the 
3181  * set of actions changes.
3182  *
3183  * This function is called by the drag source.
3184  * 
3185  * Return value: FIXME
3186  **/
3187 gboolean        
3188 gdk_drag_motion (GdkDragContext *context,
3189                  GdkWindow      *dest_window,
3190                  GdkDragProtocol protocol,
3191                  gint            x_root, 
3192                  gint            y_root,
3193                  GdkDragAction   suggested_action,
3194                  GdkDragAction   possible_actions,
3195                  guint32         time)
3196 {
3197   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
3198
3199   g_return_val_if_fail (context != NULL, FALSE);
3200
3201   private->old_actions = context->actions;
3202   context->actions = possible_actions;
3203   
3204   if (private->old_actions != possible_actions)
3205     private->xdnd_actions_set = FALSE;
3206       
3207   /* When we have a Xdnd target, make sure our XdndActionList
3208    * matches the current actions;
3209    */
3210   if (protocol == GDK_DRAG_PROTO_XDND && !private->xdnd_actions_set)
3211     {
3212       if (dest_window)
3213         {
3214           if (gdk_window_get_window_type (dest_window) == GDK_WINDOW_FOREIGN)
3215             xdnd_set_actions (context);
3216           else if (context->dest_window == dest_window)
3217             {
3218               GdkDisplay *display = GDK_WINDOW_DISPLAY (dest_window);
3219               GdkDragContext *dest_context;
3220                     
3221               dest_context = gdk_drag_context_find (display, FALSE,
3222                                                     GDK_DRAWABLE_XID (context->source_window),
3223                                                     GDK_DRAWABLE_XID (dest_window));
3224
3225               if (dest_context)
3226                 {
3227                   dest_context->actions = context->actions;
3228                   PRIVATE_DATA (dest_context)->xdnd_have_actions = TRUE;
3229                 }
3230             }
3231         }
3232     }
3233
3234   if (context->dest_window != dest_window)
3235     {
3236       GdkEvent temp_event;
3237
3238       /* Send a leave to the last destination */
3239       gdk_drag_do_leave (context, time);
3240       private->drag_status = GDK_DRAG_STATUS_DRAG;
3241
3242       /* Check if new destination accepts drags, and which protocol */
3243
3244       if (dest_window)
3245         {
3246           context->dest_window = dest_window;
3247           private->drop_xid = private->dest_xid;
3248           g_object_ref (context->dest_window);
3249           context->protocol = protocol;
3250
3251           switch (protocol)
3252             {
3253             case GDK_DRAG_PROTO_MOTIF:
3254               motif_send_enter (context, time);
3255               break;
3256
3257             case GDK_DRAG_PROTO_XDND:
3258               xdnd_send_enter (context);
3259               break;
3260
3261             case GDK_DRAG_PROTO_ROOTWIN:
3262             case GDK_DRAG_PROTO_NONE:
3263             default:
3264               break;
3265             }
3266           private->old_action = suggested_action;
3267           context->suggested_action = suggested_action;
3268           private->old_actions = possible_actions;
3269         }
3270       else
3271         {
3272           context->dest_window = NULL;
3273           private->drop_xid = None;
3274           context->action = 0;
3275         }
3276
3277       /* Push a status event, to let the client know that
3278        * the drag changed 
3279        */
3280
3281       temp_event.dnd.type = GDK_DRAG_STATUS;
3282       temp_event.dnd.window = context->source_window;
3283       /* We use this to signal a synthetic status. Perhaps
3284        * we should use an extra field...
3285        */
3286       temp_event.dnd.send_event = TRUE;
3287
3288       temp_event.dnd.context = context;
3289       temp_event.dnd.time = time;
3290
3291       gdk_event_put (&temp_event);
3292     }
3293   else
3294     {
3295       private->old_action = context->suggested_action;
3296       context->suggested_action = suggested_action;
3297     }
3298
3299   /* Send a drag-motion event */
3300
3301   private->last_x = x_root;
3302   private->last_y = y_root;
3303       
3304   if (context->dest_window)
3305     {
3306       if (private->drag_status == GDK_DRAG_STATUS_DRAG)
3307         {
3308           switch (context->protocol)
3309             {
3310             case GDK_DRAG_PROTO_MOTIF:
3311               motif_send_motion (context, x_root, y_root, suggested_action, time);
3312               break;
3313               
3314             case GDK_DRAG_PROTO_XDND:
3315               xdnd_send_motion (context, x_root, y_root, suggested_action, time);
3316               break;
3317
3318             case GDK_DRAG_PROTO_ROOTWIN:
3319               {
3320                 GdkEvent temp_event;
3321                 /* GTK+ traditionally has used application/x-rootwin-drop,
3322                  * but the XDND spec specifies x-rootwindow-drop.
3323                  */
3324                 GdkAtom target1 = gdk_atom_intern ("application/x-rootwindow-drop", FALSE);
3325                 GdkAtom target2 = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
3326
3327                 if (g_list_find (context->targets,
3328                                  GDK_ATOM_TO_POINTER (target1)) ||
3329                     g_list_find (context->targets,
3330                                  GDK_ATOM_TO_POINTER (target2)))
3331                   context->action = context->suggested_action;
3332                 else
3333                   context->action = 0;
3334
3335                 temp_event.dnd.type = GDK_DRAG_STATUS;
3336                 temp_event.dnd.window = context->source_window;
3337                 temp_event.dnd.send_event = FALSE;
3338                 temp_event.dnd.context = context;
3339                 temp_event.dnd.time = time;
3340
3341                 gdk_event_put (&temp_event);
3342               }
3343               break;
3344             case GDK_DRAG_PROTO_NONE:
3345               g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
3346               break;
3347             default:
3348               break;
3349             }
3350         }
3351       else
3352         return TRUE;
3353     }
3354
3355   return FALSE;
3356 }
3357
3358 /**
3359  * gdk_drag_drop:
3360  * @context: a #GdkDragContext.
3361  * @time_: the timestamp for this operation.
3362  * 
3363  * Drops on the current destination.
3364  * 
3365  * This function is called by the drag source.
3366  **/
3367 void
3368 gdk_drag_drop (GdkDragContext *context,
3369                guint32         time)
3370 {
3371   g_return_if_fail (context != NULL);
3372
3373   if (context->dest_window)
3374     {
3375       switch (context->protocol)
3376         {
3377         case GDK_DRAG_PROTO_MOTIF:
3378           motif_send_leave (context, time);
3379           motif_send_drop (context, time);
3380           break;
3381           
3382         case GDK_DRAG_PROTO_XDND:
3383           xdnd_send_drop (context, time);
3384           break;
3385
3386         case GDK_DRAG_PROTO_ROOTWIN:
3387           g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
3388           break;
3389         case GDK_DRAG_PROTO_NONE:
3390           g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
3391           break;
3392         default:
3393           break;
3394         }
3395     }
3396 }
3397
3398 /**
3399  * gdk_drag_abort:
3400  * @context: a #GdkDragContext.
3401  * @time_: the timestamp for this operation.
3402  * 
3403  * Aborts a drag without dropping. 
3404  *
3405  * This function is called by the drag source.
3406  **/
3407 void
3408 gdk_drag_abort (GdkDragContext *context,
3409                 guint32         time)
3410 {
3411   g_return_if_fail (context != NULL);
3412
3413   gdk_drag_do_leave (context, time);
3414 }
3415
3416 /* Destination side */
3417
3418 /**
3419  * gdk_drag_status:
3420  * @context: a #GdkDragContext.
3421  * @action: the selected action which will be taken when a drop happens, 
3422  *    or 0 to indicate that a drop will not be accepted.
3423  * @time_: the timestamp for this operation.
3424  * 
3425  * Selects one of the actions offered by the drag source.
3426  *
3427  * This function is called by the drag destination in response to
3428  * gdk_drag_motion() called by the drag source.
3429  **/
3430 void             
3431 gdk_drag_status (GdkDragContext   *context,
3432                  GdkDragAction     action,
3433                  guint32           time)
3434 {
3435   GdkDragContextPrivateX11 *private;
3436   XEvent xev;
3437   GdkDisplay *display;
3438
3439   g_return_if_fail (context != NULL);
3440
3441   private = PRIVATE_DATA (context);
3442   display = GDK_DRAWABLE_DISPLAY (context->source_window);
3443   
3444   context->action = action;
3445
3446   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3447     {
3448       gboolean need_coords = FALSE;
3449       
3450       xev.xclient.type = ClientMessage;
3451       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
3452                                                                         "_MOTIF_DRAG_AND_DROP_MESSAGE");
3453       xev.xclient.format = 8;
3454       xev.xclient.window = GDK_DRAWABLE_XID (context->source_window);
3455
3456       if (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT)
3457         {
3458           MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED | 0x80;
3459         }
3460       else
3461         {
3462           if ((action != 0) != (private->old_action != 0))
3463             {
3464               if (action != 0)
3465                 {
3466                   MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_ENTER | 0x80;
3467                   need_coords = TRUE;
3468                 }
3469               else
3470                 MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_LEAVE | 0x80;
3471             }
3472           else
3473             {
3474               MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION | 0x80;
3475               need_coords = TRUE;
3476             }
3477         }
3478
3479       MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3480
3481       switch (action)
3482         {
3483         case GDK_ACTION_MOVE:
3484           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_MOVE;
3485           break;
3486         case GDK_ACTION_COPY:
3487           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY;
3488           break;
3489         case GDK_ACTION_LINK:
3490           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_LINK;
3491           break;
3492         default:
3493           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP;
3494           break;
3495         }
3496
3497       if (action)
3498         MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmDROP_SITE_VALID << 4);
3499       else
3500         MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmNO_DROP_SITE << 4);
3501
3502       MOTIF_XCLIENT_LONG (&xev, 1) = time;
3503       
3504       if (need_coords)
3505         {
3506           MOTIF_XCLIENT_SHORT (&xev, 4) = private->last_x;
3507           MOTIF_XCLIENT_SHORT (&xev, 5) = private->last_y;
3508         }
3509       else
3510         MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3511       
3512       MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3513       MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3514
3515       if (!_gdk_send_xevent (display,
3516                              GDK_DRAWABLE_XID (context->source_window),
3517                              FALSE, 0, &xev))
3518         GDK_NOTE (DND, 
3519                   g_message ("Send event to %lx failed",
3520                              GDK_DRAWABLE_XID (context->source_window)));
3521     }
3522   else if (context->protocol == GDK_DRAG_PROTO_XDND)
3523     {
3524       xev.xclient.type = ClientMessage;
3525       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus");
3526       xev.xclient.format = 32;
3527       xev.xclient.window = GDK_DRAWABLE_XID (context->source_window);
3528
3529       xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->dest_window);
3530       xev.xclient.data.l[1] = (action != 0) ? (2 | 1) : 0;
3531       xev.xclient.data.l[2] = 0;
3532       xev.xclient.data.l[3] = 0;
3533       xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
3534       
3535       if (!xdnd_send_xevent (context, context->source_window,
3536                              FALSE, &xev))
3537         GDK_NOTE (DND, 
3538                   g_message ("Send event to %lx failed",
3539                              GDK_DRAWABLE_XID (context->source_window)));
3540     }
3541
3542   private->old_action = action;
3543 }
3544
3545 /**
3546  * gdk_drop_reply:
3547  * @context: a #GdkDragContext.
3548  * @ok: %TRUE if the drop is accepted.
3549  * @time_: the timestamp for this operation.
3550  * 
3551  * Accepts or rejects a drop. 
3552  *
3553  * This function is called by the drag destination in response
3554  * to a drop initiated by the drag source.
3555  **/
3556 void 
3557 gdk_drop_reply (GdkDragContext   *context,
3558                 gboolean          ok,
3559                 guint32           time)
3560 {
3561   GdkDragContextPrivateX11 *private;
3562
3563   g_return_if_fail (context != NULL);
3564
3565   private = PRIVATE_DATA (context);
3566   
3567   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3568     {
3569       GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
3570       XEvent xev;
3571
3572       xev.xclient.type = ClientMessage;
3573       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
3574                                                                         "_MOTIF_DRAG_AND_DROP_MESSAGE");
3575       xev.xclient.format = 8;
3576
3577       MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START | 0x80;
3578       MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3579       if (ok)
3580         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY | 
3581                                        (XmDROP_SITE_VALID << 4) |
3582                                        (XmDROP_NOOP << 8) |
3583                                        (XmDROP << 12);
3584       else
3585         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP | 
3586                                        (XmNO_DROP_SITE << 4) |
3587                                        (XmDROP_NOOP << 8) |
3588                                        (XmDROP_CANCEL << 12);
3589       MOTIF_XCLIENT_SHORT (&xev, 2) = private->last_x;
3590       MOTIF_XCLIENT_SHORT (&xev, 3) = private->last_y;
3591       MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3592       MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3593       MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3594       
3595       _gdk_send_xevent (display,
3596                         GDK_DRAWABLE_XID (context->source_window),
3597                         FALSE, 0, &xev);
3598     }
3599 }
3600
3601 /**
3602  * gdk_drop_finish:
3603  * @context: a #GtkDragContext.
3604  * @success: %TRUE if the data was successfully received.
3605  * @time_: the timestamp for this operation.
3606  * 
3607  * Ends the drag operation after a drop.
3608  *
3609  * This function is called by the drag destination.
3610  **/
3611 void             
3612 gdk_drop_finish (GdkDragContext   *context,
3613                  gboolean          success,
3614                  guint32           time)
3615 {
3616   g_return_if_fail (context != NULL);
3617
3618   if (context->protocol == GDK_DRAG_PROTO_XDND)
3619     {
3620       GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
3621       XEvent xev;
3622
3623       xev.xclient.type = ClientMessage;
3624       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished");
3625       xev.xclient.format = 32;
3626       xev.xclient.window = GDK_DRAWABLE_XID (context->source_window);
3627       
3628       xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->dest_window);
3629       xev.xclient.data.l[1] = 0;
3630       xev.xclient.data.l[2] = 0;
3631       xev.xclient.data.l[3] = 0;
3632       xev.xclient.data.l[4] = 0;
3633
3634       if (!xdnd_send_xevent (context, context->source_window,
3635                              FALSE, &xev))
3636         GDK_NOTE (DND, 
3637                   g_message ("Send event to %lx failed",
3638                              GDK_DRAWABLE_XID (context->source_window)));
3639     }
3640 }
3641
3642
3643 void            
3644 gdk_window_register_dnd (GdkWindow      *window)
3645 {
3646   static gulong xdnd_version = 3;
3647   MotifDragReceiverInfo info;
3648   Atom motif_drag_receiver_info_atom;
3649   GdkDisplay *display = gdk_drawable_get_display (window);
3650
3651   g_return_if_fail (window != NULL);
3652
3653   base_precache_atoms (display);
3654
3655   if (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered") != NULL)
3656     return;
3657   else
3658     g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
3659   
3660   /* Set Motif drag receiver information property */
3661
3662   motif_drag_receiver_info_atom = gdk_x11_get_xatom_by_name_for_display (display,
3663                                                                          "_MOTIF_DRAG_RECEIVER_INFO");
3664   info.byte_order = local_byte_order;
3665   info.protocol_version = 0;
3666   info.protocol_style = XmDRAG_DYNAMIC;
3667   info.proxy_window = None;
3668   info.num_drop_sites = 0;
3669   info.total_size = sizeof(info);
3670
3671   XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_DRAWABLE_XID (window),
3672                    motif_drag_receiver_info_atom,
3673                    motif_drag_receiver_info_atom,
3674                    8, PropModeReplace,
3675                    (guchar *)&info,
3676                    sizeof (info));
3677
3678   /* Set XdndAware */
3679
3680   /* The property needs to be of type XA_ATOM, not XA_INTEGER. Blech */
3681   XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
3682                    GDK_DRAWABLE_XID (window),
3683                    gdk_x11_get_xatom_by_name_for_display (display, "XdndAware"),
3684                    XA_ATOM, 32, PropModeReplace,
3685                    (guchar *)&xdnd_version, 1);
3686 }
3687
3688 /**
3689  * gdk_drag_get_selection:
3690  * @context: a #GdkDragContext.
3691  * 
3692  * Returns the selection atom for the current source window.
3693  * 
3694  * Return value: the selection atom.
3695  **/
3696 GdkAtom
3697 gdk_drag_get_selection (GdkDragContext *context)
3698 {
3699   g_return_val_if_fail (context != NULL, GDK_NONE);
3700
3701   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3702     return gdk_x11_xatom_to_atom_for_display (GDK_DRAWABLE_DISPLAY (context->source_window),
3703                                               (PRIVATE_DATA (context))->motif_selection);
3704   else if (context->protocol == GDK_DRAG_PROTO_XDND)
3705     return gdk_atom_intern ("XdndSelection", FALSE);
3706   else
3707     return GDK_NONE;
3708 }
3709