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