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