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