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