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