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