]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkdnd-x11.c
Use application/x-rootwindow-drop for root window drops. (#108670, Alex
[~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->next != 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 gint
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 there 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   MOTIF_XCLIENT_LONG (&xev, 4) = 0;
1311
1312   if (!_gdk_send_xevent (display,
1313                          GDK_DRAWABLE_XID (context->dest_window),
1314                          FALSE, 0, &xev))
1315     GDK_NOTE (DND, 
1316               g_message ("Send event to %lx failed",
1317                          GDK_DRAWABLE_XID (context->dest_window)));
1318 }
1319
1320 static void
1321 motif_send_leave (GdkDragContext  *context,
1322                   guint32          time)
1323 {
1324   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
1325   XEvent xev;
1326
1327   xev.xclient.type = ClientMessage;
1328   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
1329   xev.xclient.format = 8;
1330   xev.xclient.window = GDK_DRAWABLE_XID (context->dest_window);
1331
1332   MOTIF_XCLIENT_BYTE (&xev, 0) = XmTOP_LEVEL_LEAVE;
1333   MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1334   MOTIF_XCLIENT_SHORT (&xev, 1) = 0;
1335   MOTIF_XCLIENT_LONG (&xev, 1) = time;
1336   MOTIF_XCLIENT_LONG (&xev, 2) = 0;
1337   MOTIF_XCLIENT_LONG (&xev, 3) = 0;
1338   MOTIF_XCLIENT_LONG (&xev, 4) = 0;
1339
1340   if (!_gdk_send_xevent (display,
1341                          GDK_DRAWABLE_XID (context->dest_window),
1342                          FALSE, 0, &xev))
1343     GDK_NOTE (DND, 
1344               g_message ("Send event to %lx failed",
1345                          GDK_DRAWABLE_XID (context->dest_window)));
1346 }
1347
1348 static gboolean
1349 motif_send_motion (GdkDragContext  *context,
1350                     gint            x_root, 
1351                     gint            y_root,
1352                     GdkDragAction   action,
1353                     guint32         time)
1354 {
1355   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
1356   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
1357   gboolean retval;
1358   XEvent xev;
1359
1360   xev.xclient.type = ClientMessage;
1361   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
1362   xev.xclient.format = 8;
1363   xev.xclient.window = GDK_DRAWABLE_XID (context->dest_window);
1364
1365   MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1366   MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
1367   MOTIF_XCLIENT_LONG (&xev, 1) = time;
1368   MOTIF_XCLIENT_LONG (&xev, 3) = 0;
1369   MOTIF_XCLIENT_LONG (&xev, 4) = 0;
1370
1371   if ((context->suggested_action != private->old_action) ||
1372       (context->actions != private->old_actions))
1373     {
1374       MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED;
1375
1376       /* private->drag_status = GDK_DRAG_STATUS_ACTION_WAIT; */
1377       retval = TRUE;
1378     }
1379   else
1380     {
1381       MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION;
1382
1383       MOTIF_XCLIENT_SHORT (&xev, 4) = x_root;
1384       MOTIF_XCLIENT_SHORT (&xev, 5) = y_root;
1385       
1386       private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1387       retval = FALSE;
1388     }
1389
1390   if (!_gdk_send_xevent (display,
1391                          GDK_DRAWABLE_XID (context->dest_window),
1392                          FALSE, 0, &xev))
1393     GDK_NOTE (DND, 
1394               g_message ("Send event to %lx failed",
1395                          GDK_DRAWABLE_XID (context->dest_window)));
1396
1397   return retval;
1398 }
1399
1400 static void
1401 motif_send_drop (GdkDragContext *context, guint32 time)
1402 {
1403   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
1404   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
1405   XEvent xev;
1406
1407   xev.xclient.type = ClientMessage;
1408   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_AND_DROP_MESSAGE");
1409   xev.xclient.format = 8;
1410   xev.xclient.window = GDK_DRAWABLE_XID (context->dest_window);
1411
1412   MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START;
1413   MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
1414   MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
1415   MOTIF_XCLIENT_LONG (&xev, 1)  = time;
1416
1417   MOTIF_XCLIENT_SHORT (&xev, 4) = private->last_x;
1418   MOTIF_XCLIENT_SHORT (&xev, 5) = private->last_y;
1419
1420   MOTIF_XCLIENT_LONG (&xev, 3)  = private->motif_selection;
1421   MOTIF_XCLIENT_LONG (&xev, 4)  = GDK_DRAWABLE_XID (context->source_window);
1422
1423   if (!_gdk_send_xevent (display,
1424                          GDK_DRAWABLE_XID (context->dest_window),
1425                          FALSE, 0, &xev))
1426     GDK_NOTE (DND, 
1427               g_message ("Send event to %lx failed",
1428                          GDK_DRAWABLE_XID (context->dest_window)));
1429 }
1430
1431 /* Target Side */
1432
1433 static gboolean
1434 motif_read_initiator_info (GdkDisplay *display,
1435                            Window      source_window, 
1436                            Atom        atom,
1437                            GList     **targets,
1438                            Atom       *selection)
1439 {
1440   GList *tmp_list;
1441   Atom type;
1442   gint format;
1443   gulong nitems;
1444   gulong bytes_after;
1445   MotifDragInitiatorInfo *initiator_info;
1446   
1447   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
1448   
1449   gdk_error_trap_push ();
1450   XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), source_window, atom,
1451                       0, sizeof(*initiator_info), FALSE,
1452                       gdk_x11_get_xatom_by_name_for_display (display, "_MOTIF_DRAG_INITIATOR_INFO"),
1453                       &type, &format, &nitems, &bytes_after,
1454                       (guchar **)&initiator_info);
1455
1456   if (gdk_error_trap_pop () || (format != 8) || (nitems != sizeof (MotifDragInitiatorInfo)) || (bytes_after != 0))
1457     {
1458       g_warning ("Error reading initiator info\n");
1459       return FALSE;
1460     }
1461
1462   motif_read_target_table (display);
1463
1464   initiator_info->targets_index = 
1465     card16_to_host (initiator_info->targets_index, initiator_info->byte_order);
1466   initiator_info->selection_atom = 
1467     card32_to_host (initiator_info->selection_atom, initiator_info->byte_order);
1468   
1469   if (initiator_info->targets_index >= display_x11->motif_n_target_lists)
1470     {
1471       g_warning ("Invalid target index in TOP_LEVEL_ENTER MESSAGE");
1472       XFree (initiator_info);
1473       return FALSE;
1474     }
1475
1476   tmp_list = g_list_last (display_x11->motif_target_lists[initiator_info->targets_index]);
1477
1478   *targets = NULL;
1479   while (tmp_list)
1480     {
1481       GdkAtom atom = gdk_x11_xatom_to_atom_for_display (display, GPOINTER_TO_UINT (tmp_list->data));
1482       *targets = g_list_prepend (*targets, GDK_ATOM_TO_POINTER (atom));
1483       tmp_list = tmp_list->prev;
1484     }
1485
1486 #ifdef G_ENABLE_DEBUG
1487   if (_gdk_debug_flags & GDK_DEBUG_DND)
1488     print_target_list (*targets);
1489 #endif /* G_ENABLE_DEBUG */
1490
1491   *selection = initiator_info->selection_atom;
1492
1493   XFree (initiator_info);
1494
1495   return TRUE;
1496 }
1497
1498 static GdkDragContext *
1499 motif_drag_context_new (GdkWindow *dest_window,
1500                         guint32    timestamp,
1501                         guint32    source_window,
1502                         guint32    atom)
1503 {
1504   GdkDragContext *new_context;
1505   GdkDragContextPrivateX11 *private;
1506   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (dest_window);
1507   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
1508
1509   /* FIXME, current_dest_drag really shouldn't be NULL'd
1510    * if we error below.
1511    */
1512   if (display_x11->current_dest_drag != NULL)
1513     {
1514       if (timestamp >= display_x11->current_dest_drag->start_time)
1515         {
1516           g_object_unref (display_x11->current_dest_drag);
1517           display_x11->current_dest_drag = NULL;
1518         }
1519       else
1520         return NULL;
1521     }
1522
1523   new_context = gdk_drag_context_new ();
1524   private = PRIVATE_DATA (new_context);
1525
1526   new_context->protocol = GDK_DRAG_PROTO_MOTIF;
1527   new_context->is_source = FALSE;
1528
1529   new_context->source_window = gdk_window_lookup_for_display (display, source_window);
1530   if (new_context->source_window)
1531     g_object_ref (new_context->source_window);
1532   else
1533     {
1534       new_context->source_window = gdk_window_foreign_new_for_display (display, source_window);
1535       if (!new_context->source_window)
1536         {
1537           g_object_unref (new_context);
1538           return NULL;
1539         }
1540     }
1541
1542   new_context->dest_window = dest_window;
1543   g_object_ref (dest_window);
1544   new_context->start_time = timestamp;
1545
1546   if (!motif_read_initiator_info (GDK_WINDOW_DISPLAY (dest_window),
1547                                   source_window,
1548                                   atom,
1549                                   &new_context->targets,
1550                                   &private->motif_selection))
1551     {
1552       g_object_unref (new_context);
1553       return NULL;
1554     }
1555
1556   return new_context;
1557 }
1558
1559 /*
1560  * The MOTIF drag protocol has no real provisions for distinguishing
1561  * multiple simultaneous drops. If the sources grab the pointer
1562  * when doing drags, that shouldn't happen, in any case. If it
1563  * does, we can't do much except hope for the best.
1564  */
1565
1566 static GdkFilterReturn
1567 motif_top_level_enter (GdkEvent *event,
1568                        guint16   flags, 
1569                        guint32   timestamp, 
1570                        guint32   source_window, 
1571                        guint32   atom)
1572 {
1573   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_DRAWABLE_DISPLAY (event->any.window));
1574   GdkDragContext *new_context;
1575
1576   GDK_NOTE(DND, g_message ("Motif DND top level enter: flags: %#4x time: %d source_widow: %#4x atom: %d",
1577                            flags, timestamp, source_window, atom));
1578
1579   new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1580   if (!new_context)
1581     return GDK_FILTER_REMOVE;
1582
1583   event->dnd.type = GDK_DRAG_ENTER;
1584   event->dnd.context = new_context;
1585   g_object_ref (new_context);
1586
1587   display_x11->current_dest_drag = new_context;
1588
1589   return GDK_FILTER_TRANSLATE;
1590 }
1591
1592 static GdkFilterReturn
1593 motif_top_level_leave (GdkEvent *event,
1594                        guint16   flags, 
1595                        guint32   timestamp)
1596 {
1597   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_DRAWABLE_DISPLAY (event->any.window));
1598
1599   GDK_NOTE(DND, g_message ("Motif DND top level leave: flags: %#4x time: %d",
1600                            flags, timestamp));
1601
1602   if ((display_x11->current_dest_drag != NULL) &&
1603       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1604       (timestamp >= display_x11->current_dest_drag->start_time))
1605     {
1606       event->dnd.type = GDK_DRAG_LEAVE;
1607       /* Pass ownership of context to the event */
1608       event->dnd.context = display_x11->current_dest_drag;
1609
1610       display_x11->current_dest_drag = NULL;
1611
1612       return GDK_FILTER_TRANSLATE;
1613     }
1614   else
1615     return GDK_FILTER_REMOVE;
1616 }
1617
1618 static GdkFilterReturn
1619 motif_motion (GdkEvent *event,
1620               guint16   flags, 
1621               guint32   timestamp,
1622               gint16    x_root,
1623               gint16    y_root)
1624 {
1625   GdkDragContextPrivateX11 *private;
1626   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_DRAWABLE_DISPLAY (event->any.window));
1627   
1628   GDK_NOTE(DND, g_message ("Motif DND motion: flags: %#4x time: %d (%d, %d)",
1629                            flags, timestamp, x_root, y_root));
1630
1631   if ((display_x11->current_dest_drag != NULL) &&
1632       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1633       (timestamp >= display_x11->current_dest_drag->start_time))
1634     {
1635       private = PRIVATE_DATA (display_x11->current_dest_drag);
1636
1637       event->dnd.type = GDK_DRAG_MOTION;
1638       event->dnd.context = display_x11->current_dest_drag;
1639       g_object_ref (display_x11->current_dest_drag);
1640
1641       event->dnd.time = timestamp;
1642
1643       motif_dnd_translate_flags (display_x11->current_dest_drag, flags);
1644
1645       event->dnd.x_root = x_root;
1646       event->dnd.y_root = y_root;
1647
1648       private->last_x = x_root;
1649       private->last_y = y_root;
1650
1651       private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
1652
1653       return GDK_FILTER_TRANSLATE;
1654     }
1655
1656   return GDK_FILTER_REMOVE;
1657 }
1658
1659 static GdkFilterReturn
1660 motif_operation_changed (GdkEvent *event,
1661                          guint16   flags, 
1662                          guint32   timestamp)
1663 {
1664   GdkDragContextPrivateX11 *private;
1665   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_DRAWABLE_DISPLAY (event->any.window));
1666   GDK_NOTE(DND, g_message ("Motif DND operation changed: flags: %#4x time: %d",
1667                            flags, timestamp));
1668
1669   if ((display_x11->current_dest_drag != NULL) &&
1670       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_MOTIF) &&
1671       (timestamp >= display_x11->current_dest_drag->start_time))
1672     {
1673       event->dnd.type = GDK_DRAG_MOTION;
1674       event->dnd.send_event = FALSE;
1675       event->dnd.context = display_x11->current_dest_drag;
1676       g_object_ref (display_x11->current_dest_drag);
1677
1678       event->dnd.time = timestamp;
1679       private = PRIVATE_DATA (display_x11->current_dest_drag);
1680
1681       motif_dnd_translate_flags (display_x11->current_dest_drag, flags);
1682
1683       event->dnd.x_root = private->last_x;
1684       event->dnd.y_root = private->last_y;
1685
1686       private->drag_status = GDK_DRAG_STATUS_ACTION_WAIT;
1687
1688       return GDK_FILTER_TRANSLATE;
1689     }
1690
1691   return GDK_FILTER_REMOVE;
1692 }
1693
1694 static GdkFilterReturn
1695 motif_drop_start (GdkEvent *event,
1696                   guint16   flags,
1697                   guint32   timestamp,
1698                   guint32   source_window,
1699                   guint32   atom,
1700                   gint16    x_root,
1701                   gint16    y_root)
1702 {
1703   GdkDragContext *new_context;
1704   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_DRAWABLE_DISPLAY (event->any.window));
1705
1706   GDK_NOTE(DND, g_message ("Motif DND drop start: flags: %#4x time: %d (%d, %d) source_widow: %#4x atom: %d",
1707                            flags, timestamp, x_root, y_root, source_window, atom));
1708
1709   new_context = motif_drag_context_new (event->any.window, timestamp, source_window, atom);
1710   if (!new_context)
1711     return GDK_FILTER_REMOVE;
1712
1713   motif_dnd_translate_flags (new_context, flags);
1714
1715   event->dnd.type = GDK_DROP_START;
1716   event->dnd.context = new_context;
1717   event->dnd.time = timestamp;
1718   event->dnd.x_root = x_root;
1719   event->dnd.y_root = y_root;
1720
1721   g_object_ref (new_context);
1722   display_x11->current_dest_drag = new_context;
1723
1724   return GDK_FILTER_TRANSLATE;
1725 }  
1726
1727 static GdkFilterReturn
1728 motif_drag_status (GdkEvent *event,
1729                    guint16   flags,
1730                    guint32   timestamp)
1731 {
1732   GdkDragContext *context;
1733   GdkDisplay *display;
1734   
1735   GDK_NOTE (DND, 
1736             g_message ("Motif status message: flags %x", flags));
1737
1738   display = gdk_drawable_get_display (event->any.window);
1739   if (!display)
1740     return GDK_FILTER_REMOVE;
1741   
1742   context = gdk_drag_context_find (display, TRUE, GDK_DRAWABLE_XID (event->any.window), None);
1743
1744   if (context)
1745     {
1746       GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
1747       if ((private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT) ||
1748           (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT))
1749         private->drag_status = GDK_DRAG_STATUS_DRAG;
1750       
1751       event->dnd.type = GDK_DRAG_STATUS;
1752       event->dnd.send_event = FALSE;
1753       event->dnd.context = context;
1754       g_object_ref (context);
1755
1756       event->dnd.time = timestamp;
1757
1758       if ((flags & 0x00f0) >> 4 == XmDROP_SITE_VALID)
1759         {
1760           switch (flags & 0x000f)
1761             {
1762             case XmDROP_NOOP:
1763               context->action = 0;
1764               break;
1765             case XmDROP_MOVE:
1766                 context->action = GDK_ACTION_MOVE;
1767                 break;
1768             case XmDROP_COPY:
1769               context->action = GDK_ACTION_COPY;
1770               break;
1771             case XmDROP_LINK:
1772               context->action = GDK_ACTION_LINK;
1773               break;
1774             }
1775         }
1776       else
1777         context->action = 0;
1778
1779       return GDK_FILTER_TRANSLATE;
1780     }
1781   return GDK_FILTER_REMOVE;
1782 }
1783
1784 static GdkFilterReturn
1785 motif_dnd_filter (GdkXEvent *xev,
1786                   GdkEvent  *event,
1787                   gpointer data)
1788 {
1789   XEvent *xevent = (XEvent *)xev;
1790
1791   guint8 reason;
1792   guint16 flags;
1793   guint32 timestamp;
1794   guint32 source_window;
1795   Atom atom;
1796   gint16 x_root, y_root;
1797   gboolean is_reply;
1798
1799   if (!event->any.window ||
1800       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
1801     return GDK_FILTER_CONTINUE;                 /* Not for us */
1802   
1803   /* First read some fields common to all Motif DND messages */
1804
1805   reason = MOTIF_UNPACK_BYTE (xevent, 0);
1806   flags = MOTIF_UNPACK_SHORT (xevent, 1);
1807   timestamp = MOTIF_UNPACK_LONG (xevent, 1);
1808
1809   is_reply = ((reason & 0x80) != 0);
1810
1811   switch (reason & 0x7f)
1812     {
1813     case XmTOP_LEVEL_ENTER:
1814       source_window = MOTIF_UNPACK_LONG (xevent, 2);
1815       atom = MOTIF_UNPACK_LONG (xevent, 3);
1816       return motif_top_level_enter (event, flags, timestamp, source_window, atom);
1817     case XmTOP_LEVEL_LEAVE:
1818       return motif_top_level_leave (event, flags, timestamp);
1819
1820     case XmDRAG_MOTION:
1821       x_root = MOTIF_UNPACK_SHORT (xevent, 4);
1822       y_root = MOTIF_UNPACK_SHORT (xevent, 5);
1823       
1824       if (!is_reply)
1825         return motif_motion (event, flags, timestamp, x_root, y_root);
1826       else
1827         return motif_drag_status (event, flags, timestamp);
1828
1829     case XmDROP_SITE_ENTER:
1830       return motif_drag_status (event, flags, timestamp);
1831
1832     case XmDROP_SITE_LEAVE:
1833       return motif_drag_status (event,
1834                                 XmNO_DROP_SITE << 8 | XmDROP_NOOP, 
1835                                 timestamp);
1836     case XmDROP_START:
1837       x_root = MOTIF_UNPACK_SHORT (xevent, 4);
1838       y_root = MOTIF_UNPACK_SHORT (xevent, 5);
1839       atom = MOTIF_UNPACK_LONG (xevent, 3);
1840       source_window = MOTIF_UNPACK_LONG (xevent, 4);
1841
1842       if (!is_reply)
1843         return motif_drop_start (event, flags, timestamp, source_window, atom, x_root, y_root);
1844       
1845      break;
1846     case XmOPERATION_CHANGED:
1847       if (!is_reply)
1848         return motif_operation_changed (event, flags, timestamp);
1849       else
1850         return motif_drag_status (event, flags, timestamp);
1851
1852       break;
1853       /* To the best of my knowledge, these next two messages are 
1854        * not part of the protocol, though they are defined in
1855        * the header files.
1856        */
1857     case XmDROP_FINISH:
1858     case XmDRAG_DROP_FINISH:
1859       break;
1860     }
1861
1862   return GDK_FILTER_REMOVE;
1863 }
1864
1865 /*************************************************************
1866  ***************************** XDND **************************
1867  *************************************************************/
1868
1869 /* Utility functions */
1870
1871 static struct {
1872   gchar *name;
1873   GdkAtom atom;
1874   GdkDragAction action;
1875 } xdnd_actions_table[] = {
1876     { "XdndActionCopy",    None, GDK_ACTION_COPY },
1877     { "XdndActionMove",    None, GDK_ACTION_MOVE },
1878     { "XdndActionLink",    None, GDK_ACTION_LINK },
1879     { "XdndActionAsk",     None, GDK_ACTION_ASK  },
1880     { "XdndActionPrivate", None, GDK_ACTION_COPY },
1881   };
1882
1883 static const gint xdnd_n_actions = sizeof(xdnd_actions_table) / sizeof(xdnd_actions_table[0]);
1884 static gboolean xdnd_actions_initialized = FALSE;
1885
1886 static void
1887 xdnd_initialize_actions (void)
1888 {
1889   gint i;
1890   
1891   xdnd_actions_initialized = TRUE;
1892   for (i=0; i < xdnd_n_actions; i++)
1893     xdnd_actions_table[i].atom = gdk_atom_intern (xdnd_actions_table[i].name, FALSE);
1894 }
1895
1896 static GdkDragAction
1897 xdnd_action_from_atom (GdkDisplay *display,
1898                        Atom        xatom)
1899 {
1900   GdkAtom atom = gdk_x11_xatom_to_atom_for_display (display, xatom);
1901   gint i;
1902
1903   if (!xdnd_actions_initialized)
1904     xdnd_initialize_actions();
1905
1906   for (i=0; i<xdnd_n_actions; i++)
1907     if (atom == xdnd_actions_table[i].atom)
1908       return xdnd_actions_table[i].action;
1909
1910   return 0;
1911 }
1912
1913 static Atom
1914 xdnd_action_to_atom (GdkDisplay    *display,
1915                      GdkDragAction  action)
1916 {
1917   gint i;
1918
1919   if (!xdnd_actions_initialized)
1920     xdnd_initialize_actions();
1921
1922   for (i=0; i<xdnd_n_actions; i++)
1923     if (action == xdnd_actions_table[i].action)
1924       return gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
1925
1926   return None;
1927 }
1928
1929 /* Source side */
1930
1931 static GdkFilterReturn 
1932 xdnd_status_filter (GdkXEvent *xev,
1933                     GdkEvent  *event,
1934                     gpointer   data)
1935 {
1936   GdkDisplay *display;
1937   XEvent *xevent = (XEvent *)xev;
1938   guint32 dest_window = xevent->xclient.data.l[0];
1939   guint32 flags = xevent->xclient.data.l[1];
1940   Atom action = xevent->xclient.data.l[4];
1941   GdkDragContext *context;
1942
1943   if (!event->any.window ||
1944       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
1945     return GDK_FILTER_CONTINUE;                 /* Not for us */
1946   
1947   GDK_NOTE (DND, 
1948             g_message ("XdndStatus: dest_window: %#x  action: %ld",
1949                        dest_window, action));
1950
1951   display = gdk_drawable_get_display (event->any.window);
1952   context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
1953   
1954   if (context)
1955     {
1956       GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
1957       if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
1958         private->drag_status = GDK_DRAG_STATUS_DRAG;
1959       
1960       event->dnd.send_event = FALSE;
1961       event->dnd.type = GDK_DRAG_STATUS;
1962       event->dnd.context = context;
1963       g_object_ref (context);
1964
1965       event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
1966       if (!(action != 0) != !(flags & 1))
1967         {
1968           GDK_NOTE (DND,
1969                     g_warning ("Received status event with flags not corresponding to action!\n"));
1970           action = 0;
1971         }
1972
1973       context->action = xdnd_action_from_atom (display, action);
1974
1975       return GDK_FILTER_TRANSLATE;
1976     }
1977
1978   return GDK_FILTER_REMOVE;
1979 }
1980
1981 static GdkFilterReturn 
1982 xdnd_finished_filter (GdkXEvent *xev,
1983                       GdkEvent  *event,
1984                       gpointer   data)
1985 {
1986   GdkDisplay *display;
1987   XEvent *xevent = (XEvent *)xev;
1988   guint32 dest_window = xevent->xclient.data.l[0];
1989   GdkDragContext *context;
1990
1991   if (!event->any.window ||
1992       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
1993     return GDK_FILTER_CONTINUE;                 /* Not for us */
1994   
1995   GDK_NOTE (DND, 
1996             g_message ("XdndFinished: dest_window: %#x", dest_window));
1997
1998   display = gdk_drawable_get_display (event->any.window);
1999   context = gdk_drag_context_find (display, TRUE, xevent->xclient.window, dest_window);
2000   
2001   if (context)
2002     {
2003       event->dnd.type = GDK_DROP_FINISHED;
2004       event->dnd.context = context;
2005       g_object_ref (context);
2006
2007       return GDK_FILTER_TRANSLATE;
2008     }
2009
2010   return GDK_FILTER_REMOVE;
2011 }
2012
2013 static void
2014 xdnd_set_targets (GdkDragContext *context)
2015 {
2016   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2017   Atom *atomlist;
2018   GList *tmp_list = context->targets;
2019   gint i;
2020   gint n_atoms = g_list_length (context->targets);
2021   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
2022
2023   atomlist = g_new (Atom, n_atoms);
2024   i = 0;
2025   while (tmp_list)
2026     {
2027       atomlist[i] = gdk_x11_atom_to_xatom_for_display (display, GDK_POINTER_TO_ATOM (tmp_list->data));
2028       tmp_list = tmp_list->next;
2029       i++;
2030     }
2031
2032   XChangeProperty (GDK_DRAWABLE_XDISPLAY (context->source_window),
2033                    GDK_DRAWABLE_XID (context->source_window),
2034                    gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
2035                    XA_ATOM, 32, PropModeReplace,
2036                    (guchar *)atomlist, n_atoms);
2037
2038   g_free (atomlist);
2039
2040   private->xdnd_targets_set = 1;
2041 }
2042
2043 static void
2044 xdnd_set_actions (GdkDragContext *context)
2045 {
2046   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2047   Atom *atomlist;
2048   gint i;
2049   gint n_atoms;
2050   guint actions;
2051   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
2052
2053   if (!xdnd_actions_initialized)
2054     xdnd_initialize_actions();
2055   
2056   actions = context->actions;
2057   n_atoms = 0;
2058   for (i=0; i<xdnd_n_actions; i++)
2059     {
2060       if (actions & xdnd_actions_table[i].action)
2061         {
2062           actions &= ~xdnd_actions_table[i].action;
2063           n_atoms++;
2064         }
2065     }
2066
2067   atomlist = g_new (Atom, n_atoms);
2068
2069   actions = context->actions;
2070   n_atoms = 0;
2071   for (i=0; i<xdnd_n_actions; i++)
2072     {
2073       if (actions & xdnd_actions_table[i].action)
2074         {
2075           actions &= ~xdnd_actions_table[i].action;
2076           atomlist[n_atoms] = gdk_x11_atom_to_xatom_for_display (display, xdnd_actions_table[i].atom);
2077           n_atoms++;
2078         }
2079     }
2080
2081   XChangeProperty (GDK_DRAWABLE_XDISPLAY (context->source_window),
2082                    GDK_DRAWABLE_XID (context->source_window),
2083                    gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
2084                    XA_ATOM, 32, PropModeReplace,
2085                    (guchar *)atomlist, n_atoms);
2086
2087   g_free (atomlist);
2088
2089   private->xdnd_actions_set = 1;
2090   private->xdnd_actions = context->actions;
2091 }
2092
2093 /*************************************************************
2094  * xdnd_send_xevent:
2095  *     Like gdk_send_event, but if the target is the root
2096  *     window, sets an event mask of ButtonPressMask, otherwise
2097  *     an event mask of 0.
2098  *   arguments:
2099  *     
2100  *   results:
2101  *************************************************************/
2102
2103 static gboolean
2104 xdnd_send_xevent (GdkDisplay *display, 
2105                   Window      window, 
2106                   gboolean    propagate, 
2107                   XEvent     *event_send)
2108 {
2109   if (_gdk_x11_display_is_root_window (display, window))
2110     return _gdk_send_xevent (display, window, propagate, ButtonPressMask, event_send);
2111   else
2112     return _gdk_send_xevent (display, window, propagate, 0, event_send);
2113 }
2114
2115 static void
2116 xdnd_send_enter (GdkDragContext *context)
2117 {
2118   XEvent xev;
2119   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2120   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->dest_window);
2121
2122   xev.xclient.type = ClientMessage;
2123   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndEnter");
2124   xev.xclient.format = 32;
2125   xev.xclient.window = private->drop_xid ? 
2126                            private->drop_xid : 
2127                            GDK_DRAWABLE_XID (context->dest_window);
2128   xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
2129   xev.xclient.data.l[1] = (3 << 24); /* version */
2130   xev.xclient.data.l[2] = 0;
2131   xev.xclient.data.l[3] = 0;
2132   xev.xclient.data.l[4] = 0;
2133
2134   if (!private->xdnd_selection)
2135     private->xdnd_selection = gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
2136
2137   if (g_list_length (context->targets) > 3)
2138     {
2139       if (!private->xdnd_targets_set)
2140         xdnd_set_targets (context);
2141       xev.xclient.data.l[1] |= 1;
2142     }
2143   else
2144     {
2145       GList *tmp_list = context->targets;
2146       gint i = 2;
2147
2148       while (tmp_list)
2149         {
2150           xev.xclient.data.l[i] = gdk_x11_atom_to_xatom_for_display (display,
2151                                                                      GDK_POINTER_TO_ATOM (tmp_list->data));
2152           tmp_list = tmp_list->next;
2153           i++;
2154         }
2155     }
2156
2157   if (!xdnd_send_xevent (display,
2158                          GDK_DRAWABLE_XID (context->dest_window),
2159                          FALSE, &xev))
2160     {
2161       GDK_NOTE (DND, 
2162                 g_message ("Send event to %lx failed",
2163                            GDK_DRAWABLE_XID (context->dest_window)));
2164       g_object_unref (context->dest_window);
2165       context->dest_window = NULL;
2166     }
2167 }
2168
2169 static void
2170 xdnd_send_leave (GdkDragContext *context)
2171 {
2172   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
2173   XEvent xev;
2174
2175   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2176
2177   xev.xclient.type = ClientMessage;
2178   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndLeave");
2179   xev.xclient.format = 32;
2180   xev.xclient.window = private->drop_xid ? 
2181                            private->drop_xid : 
2182                            GDK_DRAWABLE_XID (context->dest_window);
2183   xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
2184   xev.xclient.data.l[1] = 0;
2185   xev.xclient.data.l[2] = 0;
2186   xev.xclient.data.l[3] = 0;
2187   xev.xclient.data.l[4] = 0;
2188
2189   if (!xdnd_send_xevent (display,
2190                          GDK_DRAWABLE_XID (context->dest_window),
2191                          FALSE, &xev))
2192     {
2193       GDK_NOTE (DND, 
2194                 g_message ("Send event to %lx failed",
2195                            GDK_DRAWABLE_XID (context->dest_window)));
2196       g_object_unref (context->dest_window);
2197       context->dest_window = NULL;
2198     }
2199 }
2200
2201 static void
2202 xdnd_send_drop (GdkDragContext *context, guint32 time)
2203 {
2204   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2205   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
2206   XEvent xev;
2207
2208   xev.xclient.type = ClientMessage;
2209   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndDrop");
2210   xev.xclient.format = 32;
2211   xev.xclient.window = private->drop_xid ? 
2212                            private->drop_xid : 
2213                            GDK_DRAWABLE_XID (context->dest_window);
2214   xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
2215   xev.xclient.data.l[1] = 0;
2216   xev.xclient.data.l[2] = time;
2217   xev.xclient.data.l[3] = 0;
2218   xev.xclient.data.l[4] = 0;
2219
2220   if (!xdnd_send_xevent (display,
2221                          GDK_DRAWABLE_XID (context->dest_window),
2222                          FALSE, &xev))
2223     {
2224       GDK_NOTE (DND, 
2225                 g_message ("Send event to %lx failed",
2226                            GDK_DRAWABLE_XID (context->dest_window)));
2227       g_object_unref (context->dest_window);
2228       context->dest_window = NULL;
2229     }
2230 }
2231
2232 static void
2233 xdnd_send_motion (GdkDragContext *context,
2234                   gint            x_root, 
2235                   gint            y_root,
2236                   GdkDragAction   action,
2237                   guint32         time)
2238 {
2239   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2240   GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
2241   XEvent xev;
2242
2243   xev.xclient.type = ClientMessage;
2244   xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndPosition");
2245   xev.xclient.format = 32;
2246   xev.xclient.window = private->drop_xid ? 
2247                            private->drop_xid : 
2248                            GDK_DRAWABLE_XID (context->dest_window);
2249   xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->source_window);
2250   xev.xclient.data.l[1] = 0;
2251   xev.xclient.data.l[2] = (x_root << 16) | y_root;
2252   xev.xclient.data.l[3] = time;
2253   xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
2254
2255   if (!xdnd_send_xevent (display,
2256                          GDK_DRAWABLE_XID (context->dest_window),
2257                          FALSE, &xev))
2258     {
2259       GDK_NOTE (DND, 
2260                 g_message ("Send event to %lx failed",
2261                            GDK_DRAWABLE_XID (context->dest_window)));
2262       g_object_unref (context->dest_window);
2263       context->dest_window = NULL;
2264     }
2265   private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
2266 }
2267
2268 static guint32
2269 xdnd_check_dest (GdkDisplay *display,
2270                  Window      win)
2271 {
2272   gboolean retval = FALSE;
2273   Atom type = None;
2274   int format;
2275   unsigned long nitems, after;
2276   Atom *version;
2277   Window *proxy_data;
2278   Window proxy;
2279   Atom xdnd_proxy_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndProxy");
2280   Atom xdnd_aware_atom = gdk_x11_get_xatom_by_name_for_display (display, "XdndAware");
2281
2282   proxy = None;
2283
2284   gdk_error_trap_push ();
2285   
2286   if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), win, 
2287                           xdnd_proxy_atom, 0, 
2288                           1, False, AnyPropertyType,
2289                           &type, &format, &nitems, &after, 
2290                           (guchar **)&proxy_data) == Success)
2291     {
2292       if (type != None)
2293         {
2294           if ((format == 32) && (nitems == 1))
2295             {
2296               proxy = *proxy_data;
2297             }
2298           else
2299             GDK_NOTE (DND, 
2300                       g_warning ("Invalid XdndOwner "
2301                                  "property on window %ld\n", win));
2302           
2303           XFree (proxy_data);
2304         }
2305       
2306       if ((XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), proxy ? proxy : win,
2307                                xdnd_aware_atom, 0, 
2308                                1, False, AnyPropertyType,
2309                                &type, &format, &nitems, &after, 
2310                                (guchar **)&version) == Success) &&
2311           type != None)
2312         {
2313           if ((format == 32) && (nitems == 1))
2314             {
2315               if (*version >= 3)
2316                 retval = TRUE;
2317             }
2318           else
2319             GDK_NOTE (DND, 
2320                       g_warning ("Invalid XdndAware property on window %ld\n", win));
2321           
2322           XFree (version);
2323         }
2324     }
2325
2326   gdk_error_trap_pop ();
2327   
2328   return retval ? (proxy ? proxy : win) : None;
2329 }
2330
2331 /* Target side */
2332
2333 static void
2334 xdnd_read_actions (GdkDragContext *context)
2335 {
2336   GdkDisplay *display = GDK_WINDOW_DISPLAY (context->source_window);
2337   Atom type;
2338   int format;
2339   gulong nitems, after;
2340   Atom *data;
2341
2342   gint i;
2343
2344   /* Get the XdndActionList, if set */
2345
2346   gdk_error_trap_push ();
2347   
2348   if (XGetWindowProperty (GDK_DRAWABLE_XDISPLAY (context->source_window),
2349                           GDK_DRAWABLE_XID (context->source_window),
2350                           gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"),
2351                           0, 65536,
2352                           False, XA_ATOM, &type, &format, &nitems,
2353                           &after, (guchar **)&data) == Success &&
2354       type == XA_ATOM)
2355     {
2356       context->actions = 0;
2357
2358       for (i=0; i<nitems; i++)
2359         context->actions |= xdnd_action_from_atom (display, data[i]);
2360
2361       (PRIVATE_DATA (context))->xdnd_have_actions = TRUE;
2362
2363 #ifdef G_ENABLE_DEBUG
2364       if (_gdk_debug_flags & GDK_DEBUG_DND)
2365         {
2366           GString *action_str = g_string_new (NULL);
2367           if (context->actions & GDK_ACTION_MOVE)
2368             g_string_append(action_str, "MOVE ");
2369           if (context->actions & GDK_ACTION_COPY)
2370             g_string_append(action_str, "COPY ");
2371           if (context->actions & GDK_ACTION_LINK)
2372             g_string_append(action_str, "LINK ");
2373           if (context->actions & GDK_ACTION_ASK)
2374             g_string_append(action_str, "ASK ");
2375           
2376           g_message("Xdnd actions = %s", action_str->str);
2377           g_string_free (action_str, TRUE);
2378         }
2379 #endif /* G_ENABLE_DEBUG */
2380
2381       XFree(data);
2382     }
2383
2384   gdk_error_trap_pop ();
2385 }
2386
2387 /* We have to make sure that the XdndActionList we keep internally
2388  * is up to date with the XdndActionList on the source window
2389  * because we get no notification, because Xdnd wasn't meant
2390  * to continually send actions. So we select on PropertyChangeMask
2391  * and add this filter.
2392  */
2393 static GdkFilterReturn 
2394 xdnd_source_window_filter (GdkXEvent *xev,
2395                            GdkEvent  *event,
2396                            gpointer   cb_data)
2397 {
2398   XEvent *xevent = (XEvent *)xev;
2399   GdkDragContext *context = cb_data;
2400   GdkDisplay *display = GDK_WINDOW_DISPLAY(event->any.window);
2401
2402   if ((xevent->xany.type == PropertyNotify) &&
2403       (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList")))
2404     {
2405       xdnd_read_actions (context);
2406
2407       return GDK_FILTER_REMOVE;
2408     }
2409
2410   return GDK_FILTER_CONTINUE;
2411 }
2412
2413 static void
2414 xdnd_manage_source_filter (GdkDragContext *context,
2415                            GdkWindow      *window,
2416                            gboolean        add_filter)
2417 {
2418   gdk_error_trap_push ();
2419
2420   if (!GDK_WINDOW_DESTROYED (window))
2421     {
2422       if (add_filter)
2423         {
2424           gdk_window_set_events (window,
2425                                  gdk_window_get_events (window) |
2426                                  GDK_PROPERTY_CHANGE_MASK);
2427           gdk_window_add_filter (window, xdnd_source_window_filter, context);
2428
2429         }
2430       else
2431         {
2432           gdk_window_remove_filter (window,
2433                                     xdnd_source_window_filter,
2434                                     context);
2435           /* Should we remove the GDK_PROPERTY_NOTIFY mask?
2436            * but we might want it for other reasons. (Like
2437            * INCR selection transactions).
2438            */
2439         }
2440     }
2441
2442   gdk_display_sync (gdk_drawable_get_display (window));
2443   gdk_error_trap_pop ();  
2444 }
2445
2446 static GdkFilterReturn 
2447 xdnd_enter_filter (GdkXEvent *xev,
2448                    GdkEvent  *event,
2449                    gpointer   cb_data)
2450 {
2451   GdkDisplay *display;
2452   GdkDisplayX11 *display_x11;
2453   XEvent *xevent = (XEvent *)xev;
2454   GdkDragContext *new_context;
2455   gint i;
2456   
2457   Atom type;
2458   int format;
2459   gulong nitems, after;
2460   Atom *data;
2461
2462   guint32 source_window;
2463   gboolean get_types;
2464   gint version;
2465
2466   if (!event->any.window ||
2467       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2468     return GDK_FILTER_CONTINUE;                 /* Not for us */
2469
2470   source_window = xevent->xclient.data.l[0];
2471   get_types = ((xevent->xclient.data.l[1] & 1) != 0);
2472   version = (xevent->xclient.data.l[1] & 0xff000000) >> 24;
2473   
2474   display = GDK_DRAWABLE_DISPLAY (event->any.window);
2475   display_x11 = GDK_DISPLAY_X11 (display);
2476
2477   GDK_NOTE (DND, 
2478             g_message ("XdndEnter: source_window: %#x, version: %#x",
2479                        source_window, version));
2480
2481   if (version != 3)
2482     {
2483       /* Old source ignore */
2484       GDK_NOTE (DND, g_message ("Ignored old XdndEnter message"));
2485       return GDK_FILTER_REMOVE;
2486     }
2487   
2488   if (display_x11->current_dest_drag != NULL)
2489     {
2490       g_object_unref (display_x11->current_dest_drag);
2491       display_x11->current_dest_drag = NULL;
2492     }
2493
2494   new_context = gdk_drag_context_new ();
2495   new_context->protocol = GDK_DRAG_PROTO_XDND;
2496   new_context->is_source = FALSE;
2497
2498   new_context->source_window = gdk_window_lookup_for_display (display, source_window);
2499   if (new_context->source_window)
2500     g_object_ref (new_context->source_window);
2501   else
2502     {
2503       new_context->source_window = gdk_window_foreign_new_for_display (display, source_window);
2504       if (!new_context->source_window)
2505         {
2506           g_object_unref (new_context);
2507           return GDK_FILTER_REMOVE;
2508         }
2509     }
2510   new_context->dest_window = event->any.window;
2511   g_object_ref (new_context->dest_window);
2512
2513   new_context->targets = NULL;
2514   if (get_types)
2515     {
2516       gdk_error_trap_push ();
2517       XGetWindowProperty (GDK_DRAWABLE_XDISPLAY (event->any.window), 
2518                           source_window, 
2519                           gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"),
2520                           0, 65536,
2521                           False, XA_ATOM, &type, &format, &nitems,
2522                           &after, (guchar **)&data);
2523
2524       if (gdk_error_trap_pop () || (format != 32) || (type != XA_ATOM))
2525         {
2526           g_object_unref (new_context);
2527           return GDK_FILTER_REMOVE;
2528         }
2529
2530       for (i=0; i<nitems; i++)
2531         new_context->targets = 
2532           g_list_append (new_context->targets,
2533                          GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display,
2534                                                                                  data[i])));
2535
2536       XFree(data);
2537     }
2538   else
2539     {
2540       for (i=0; i<3; i++)
2541         if (xevent->xclient.data.l[2+i])
2542           new_context->targets =
2543             g_list_append (new_context->targets,
2544                            GDK_ATOM_TO_POINTER (gdk_x11_xatom_to_atom_for_display (display, 
2545                                                                                    xevent->xclient.data.l[2+i])));
2546     }
2547
2548 #ifdef G_ENABLE_DEBUG
2549   if (_gdk_debug_flags & GDK_DEBUG_DND)
2550     print_target_list (new_context->targets);
2551 #endif /* G_ENABLE_DEBUG */
2552
2553   xdnd_manage_source_filter (new_context, new_context->source_window, TRUE);
2554   xdnd_read_actions (new_context);
2555
2556   event->dnd.type = GDK_DRAG_ENTER;
2557   event->dnd.context = new_context;
2558   g_object_ref (new_context);
2559
2560   display_x11->current_dest_drag = new_context;
2561
2562   (PRIVATE_DATA (new_context))->xdnd_selection = 
2563     gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
2564   return GDK_FILTER_TRANSLATE;
2565 }
2566
2567 static GdkFilterReturn 
2568 xdnd_leave_filter (GdkXEvent *xev,
2569                    GdkEvent  *event,
2570                    gpointer   data)
2571 {
2572   XEvent *xevent = (XEvent *)xev;
2573   guint32 source_window = xevent->xclient.data.l[0];
2574   GdkDisplayX11 *display_x11;
2575
2576   if (!event->any.window ||
2577       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2578     return GDK_FILTER_CONTINUE;                 /* Not for us */
2579  
2580   GDK_NOTE (DND, 
2581             g_message ("XdndLeave: source_window: %#x",
2582                        source_window));
2583
2584   display_x11 = GDK_DISPLAY_X11 (GDK_DRAWABLE_DISPLAY (event->any.window));
2585
2586   if ((display_x11->current_dest_drag != NULL) &&
2587       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2588       (GDK_DRAWABLE_XID (display_x11->current_dest_drag->source_window) == source_window))
2589     {
2590       event->dnd.type = GDK_DRAG_LEAVE;
2591       /* Pass ownership of context to the event */
2592       event->dnd.context = display_x11->current_dest_drag;
2593
2594       display_x11->current_dest_drag = NULL;
2595
2596       return GDK_FILTER_TRANSLATE;
2597     }
2598   else
2599     return GDK_FILTER_REMOVE;
2600 }
2601
2602 static GdkFilterReturn 
2603 xdnd_position_filter (GdkXEvent *xev,
2604                       GdkEvent  *event,
2605                       gpointer   data)
2606 {
2607   XEvent *xevent = (XEvent *)xev;
2608   guint32 source_window = xevent->xclient.data.l[0];
2609   gint16 x_root = xevent->xclient.data.l[2] >> 16;
2610   gint16 y_root = xevent->xclient.data.l[2] & 0xffff;
2611   guint32 time = xevent->xclient.data.l[3];
2612   Atom action = xevent->xclient.data.l[4];
2613
2614   GdkDisplay *display;
2615   GdkDisplayX11 *display_x11;
2616
2617    if (!event->any.window ||
2618        gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2619      return GDK_FILTER_CONTINUE;                        /* Not for us */
2620    
2621   GDK_NOTE (DND, 
2622             g_message ("XdndPosition: source_window: %#x position: (%d, %d)  time: %d  action: %ld",
2623                        source_window, x_root, y_root, time, action));
2624
2625   display = GDK_DRAWABLE_DISPLAY (event->any.window);
2626   display_x11 = GDK_DISPLAY_X11 (display);
2627   
2628   if ((display_x11->current_dest_drag != NULL) &&
2629       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2630       (GDK_DRAWABLE_XID (display_x11->current_dest_drag->source_window) == source_window))
2631     {
2632       event->dnd.type = GDK_DRAG_MOTION;
2633       event->dnd.context = display_x11->current_dest_drag;
2634       g_object_ref (display_x11->current_dest_drag);
2635
2636       event->dnd.time = time;
2637
2638       display_x11->current_dest_drag->suggested_action = xdnd_action_from_atom (display, action);
2639       
2640       if (!(PRIVATE_DATA (display_x11->current_dest_drag))->xdnd_have_actions)
2641         display_x11->current_dest_drag->actions = display_x11->current_dest_drag->suggested_action;
2642
2643       event->dnd.x_root = x_root;
2644       event->dnd.y_root = y_root;
2645
2646       (PRIVATE_DATA (display_x11->current_dest_drag))->last_x = x_root;
2647       (PRIVATE_DATA (display_x11->current_dest_drag))->last_y = y_root;
2648       
2649       return GDK_FILTER_TRANSLATE;
2650     }
2651
2652   return GDK_FILTER_REMOVE;
2653 }
2654
2655 static GdkFilterReturn 
2656 xdnd_drop_filter (GdkXEvent *xev,
2657                   GdkEvent  *event,
2658                   gpointer   data)
2659 {
2660   XEvent *xevent = (XEvent *)xev;
2661   guint32 source_window = xevent->xclient.data.l[0];
2662   guint32 time = xevent->xclient.data.l[2];
2663   GdkDisplayX11 *display_x11;
2664   
2665   if (!event->any.window ||
2666       gdk_window_get_window_type (event->any.window) == GDK_WINDOW_FOREIGN)
2667     return GDK_FILTER_CONTINUE;                 /* Not for us */
2668   
2669   GDK_NOTE (DND, 
2670             g_message ("XdndDrop: source_window: %#x  time: %d",
2671                        source_window, time));
2672
2673   display_x11 = GDK_DISPLAY_X11 (GDK_DRAWABLE_DISPLAY (event->any.window));
2674
2675   if ((display_x11->current_dest_drag != NULL) &&
2676       (display_x11->current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2677       (GDK_DRAWABLE_XID (display_x11->current_dest_drag->source_window) == source_window))
2678     {
2679       GdkDragContextPrivateX11 *private;
2680       private = PRIVATE_DATA (display_x11->current_dest_drag);
2681
2682       event->dnd.type = GDK_DROP_START;
2683
2684       event->dnd.context = display_x11->current_dest_drag;
2685       g_object_ref (display_x11->current_dest_drag);
2686
2687       event->dnd.time = time;
2688       event->dnd.x_root = private->last_x;
2689       event->dnd.y_root = private->last_y;
2690       
2691       return GDK_FILTER_TRANSLATE;
2692     }
2693
2694   return GDK_FILTER_REMOVE;
2695 }
2696
2697 /*************************************************************
2698  ************************** Public API ***********************
2699  *************************************************************/
2700 void
2701 _gdk_dnd_init (GdkDisplay *display)
2702 {
2703   init_byte_order ();
2704
2705   gdk_display_add_client_message_filter (
2706         display,
2707         gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE),
2708         motif_dnd_filter, NULL);
2709   gdk_display_add_client_message_filter (
2710         display,                                     
2711         gdk_atom_intern ("XdndEnter", FALSE),
2712         xdnd_enter_filter, NULL);
2713   gdk_display_add_client_message_filter (
2714         display,                                     
2715         gdk_atom_intern ("XdndLeave", FALSE),
2716         xdnd_leave_filter, NULL);
2717   gdk_display_add_client_message_filter (
2718         display,
2719         gdk_atom_intern ("XdndPosition", FALSE),
2720         xdnd_position_filter, NULL);
2721   gdk_display_add_client_message_filter (
2722         display,
2723         gdk_atom_intern ("XdndStatus", FALSE),
2724         xdnd_status_filter, NULL);
2725   gdk_display_add_client_message_filter (
2726         display,
2727         gdk_atom_intern ("XdndFinished", FALSE),
2728         xdnd_finished_filter, NULL);
2729   gdk_display_add_client_message_filter (
2730         display,                                     
2731         gdk_atom_intern ("XdndDrop", FALSE),
2732         xdnd_drop_filter, NULL);
2733 }                     
2734
2735 /* Source side */
2736
2737 static void
2738 gdk_drag_do_leave (GdkDragContext *context, guint32 time)
2739 {
2740   if (context->dest_window)
2741     {
2742       switch (context->protocol)
2743         {
2744         case GDK_DRAG_PROTO_MOTIF:
2745           motif_send_leave (context, time);
2746           break;
2747         case GDK_DRAG_PROTO_XDND:
2748           xdnd_send_leave (context);
2749           break;
2750         case GDK_DRAG_PROTO_ROOTWIN:
2751         case GDK_DRAG_PROTO_NONE:
2752         default:
2753           break;
2754         }
2755
2756       g_object_unref (context->dest_window);
2757       context->dest_window = NULL;
2758     }
2759 }
2760
2761 /**
2762  * gdk_drag_begin:
2763  * @window: the source window for this drag.
2764  * @targets: the list of offered targets.
2765  * 
2766  * Starts a drag and creates a new drag context for it.
2767  *
2768  * This function is called by the drag source.
2769  * 
2770  * Return value: a newly created #GdkDragContext.
2771  **/
2772 GdkDragContext * 
2773 gdk_drag_begin (GdkWindow     *window,
2774                 GList         *targets)
2775 {
2776   GdkDragContext *new_context;
2777   
2778   g_return_val_if_fail (window != NULL, NULL);
2779
2780   new_context = gdk_drag_context_new ();
2781   new_context->is_source = TRUE;
2782   new_context->source_window = window;
2783   g_object_ref (window);
2784
2785   new_context->targets = g_list_copy (targets);
2786   new_context->actions = 0;
2787
2788   return new_context;
2789 }
2790
2791 /**
2792  * gdk_drag_get_protocol_for_display:
2793  * @display: the #GdkDisplay where the destination window resides
2794  * @xid: the X id of the destination window.
2795  * @protocol: location where the supported DND protocol is returned.
2796  * @returns: the X id of the window where the drop should happen. This 
2797  *     may be @xid or the X id of a proxy window, or None if @xid doesn't
2798  *     support Drag and Drop.
2799  *
2800  * Finds out the DND protocol supported by a window.
2801  *
2802  * Since: 2.2
2803  */ 
2804 guint32
2805 gdk_drag_get_protocol_for_display (GdkDisplay      *display,
2806                                    guint32          xid,
2807                                    GdkDragProtocol *protocol)
2808 {
2809   guint32 retval;
2810   g_return_val_if_fail (GDK_IS_DISPLAY (display), None);
2811   
2812   if ((retval = xdnd_check_dest (display, xid)))
2813     {
2814       *protocol = GDK_DRAG_PROTO_XDND;
2815       GDK_NOTE (DND, g_message ("Entering dnd window %#x\n", xid));
2816       return retval;
2817     }
2818   else if ((retval = motif_check_dest (display, xid)))
2819     {
2820       *protocol = GDK_DRAG_PROTO_MOTIF;
2821       GDK_NOTE (DND, g_message ("Entering motif window %#x\n", xid));
2822       return retval;
2823     }
2824   else
2825     {
2826       /* Check if this is a root window */
2827
2828       gboolean rootwin = FALSE;
2829       Atom type = None;
2830       int format;
2831       unsigned long nitems, after;
2832       unsigned char *data;
2833
2834       if (_gdk_x11_display_is_root_window (display, (Window) xid))
2835         rootwin = TRUE;
2836
2837       gdk_error_trap_push ();
2838       
2839       if (!rootwin)
2840         {
2841           if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xid,
2842                                   gdk_x11_get_xatom_by_name_for_display (display, "ENLIGHTENMENT_DESKTOP"),
2843                                   0, 0, False, AnyPropertyType,
2844                                   &type, &format, &nitems, &after, &data) == Success &&
2845               type != None)
2846             {
2847               XFree (data);
2848               rootwin = TRUE;
2849             }
2850         }
2851
2852       /* Until I find out what window manager the next one is for,
2853        * I'm leaving it commented out. It's supported in the
2854        * xscreensaver sources, though.
2855        */
2856 #if 0
2857       if (!rootwin)
2858         {
2859           if (XGetWindowProperty (gdk_display, win,
2860                                   gdk_x11_get_xatom_by_name ("__SWM_VROOT"),
2861                                   0, 0, False, AnyPropertyType,
2862                                   &type, &format, &nitems, &data) &&
2863               type != None)
2864             rootwin = TRUE;
2865         }
2866 #endif      
2867
2868       gdk_error_trap_pop ();
2869
2870       if (rootwin)
2871         {
2872           *protocol = GDK_DRAG_PROTO_ROOTWIN;
2873           return xid;
2874         }
2875     }
2876
2877   *protocol = GDK_DRAG_PROTO_NONE;
2878   return None;
2879 }
2880
2881 static GdkWindowCache *
2882 drag_context_find_window_cache (GdkDragContext  *context,
2883                                 GdkScreen       *screen)
2884 {
2885   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2886   GSList *tmp_list;
2887   GdkWindowCache *cache;
2888
2889   for (tmp_list = private->window_caches; tmp_list; tmp_list = tmp_list->next)
2890     {
2891       cache = tmp_list->data;
2892       if (cache->screen == screen)
2893         return cache;
2894     }
2895
2896   cache = gdk_window_cache_new (screen);
2897   private->window_caches = g_slist_prepend (private->window_caches, cache);
2898   
2899   return cache;
2900 }
2901
2902 /**
2903  * gdk_drag_find_window_for_screen:
2904  * @context: a #GdkDragContext
2905  * @drag_window: a window which may be at the pointer position, but
2906  * should be ignored, since it is put up by the drag source as an icon.
2907  * @screen: the screen where the destination window is sought. 
2908  * @x_root: the x position of the pointer in root coordinates.
2909  * @y_root: the y position of the pointer in root coordinates.
2910  * @dest_window: location to store the destination window in.
2911  * @protocol: location to store the DND protocol in.
2912  * 
2913  * Finds the destination window and DND protocol to use at the
2914  * given pointer position. 
2915  *
2916  * This function is called by the drag source to obtain the 
2917  * @dest_window and @protocol parameters for gdk_drag_motion().
2918  *
2919  * Since: 2.2
2920  **/
2921 void
2922 gdk_drag_find_window_for_screen (GdkDragContext  *context,
2923                                  GdkWindow       *drag_window,
2924                                  GdkScreen       *screen,
2925                                  gint             x_root,
2926                                  gint             y_root,
2927                                  GdkWindow      **dest_window,
2928                                  GdkDragProtocol *protocol)
2929 {
2930   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
2931   GdkWindowCache *window_cache;
2932   GdkDisplay *display;
2933   Window dest;
2934
2935   g_return_if_fail (context != NULL);
2936
2937   display = GDK_WINDOW_DISPLAY (context->source_window);
2938
2939   window_cache = drag_context_find_window_cache (context, screen);
2940
2941   dest = get_client_window_at_coords (window_cache,
2942                                       drag_window ? 
2943                                       GDK_DRAWABLE_XID (drag_window) : None,
2944                                       x_root, y_root);
2945
2946   if (private->dest_xid != dest)
2947     {
2948       Window recipient;
2949       private->dest_xid = dest;
2950
2951       /* Check if new destination accepts drags, and which protocol */
2952
2953       /* There is some ugliness here. We actually need to pass
2954        * _three_ pieces of information to drag_motion - dest_window,
2955        * protocol, and the XID of the unproxied window. The first
2956        * two are passed explicitely, the third implicitly through
2957        * protocol->dest_xid.
2958        */
2959       if ((recipient = gdk_drag_get_protocol_for_display (display, dest, protocol)))
2960         {
2961           *dest_window = gdk_window_lookup_for_display (display, recipient);
2962           if (*dest_window)
2963             g_object_ref (*dest_window);
2964           else
2965             *dest_window = gdk_window_foreign_new_for_display (display, recipient);
2966         }
2967       else
2968         *dest_window = NULL;
2969     }
2970   else
2971     {
2972       *dest_window = context->dest_window;
2973       if (*dest_window)
2974         g_object_ref (*dest_window);
2975       *protocol = context->protocol;
2976     }
2977 }
2978
2979 /**
2980  * gdk_drag_motion:
2981  * @context: a #GdkDragContext.
2982  * @dest_window: the new destination window, obtained by 
2983  *     gdk_drag_find_window().
2984  * @protocol: the DND protocol in use, obtained by gdk_drag_find_window().
2985  * @x_root: the x position of the pointer in root coordinates.
2986  * @y_root: the y position of the pointer in root coordinates.
2987  * @suggested_action: the suggested action.
2988  * @possible_actions: the possible actions.
2989  * @time_: the timestamp for this operation.
2990  * 
2991  * Updates the drag context when the pointer moves or the 
2992  * set of actions changes.
2993  *
2994  * This function is called by the drag source.
2995  * 
2996  * Return value: FIXME
2997  **/
2998 gboolean        
2999 gdk_drag_motion (GdkDragContext *context,
3000                  GdkWindow      *dest_window,
3001                  GdkDragProtocol protocol,
3002                  gint            x_root, 
3003                  gint            y_root,
3004                  GdkDragAction   suggested_action,
3005                  GdkDragAction   possible_actions,
3006                  guint32         time)
3007 {
3008   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
3009
3010   g_return_val_if_fail (context != NULL, FALSE);
3011
3012   /* When we have a Xdnd target, make sure our XdndActionList
3013    * matches the current actions;
3014    */
3015   private->old_actions = context->actions;
3016   context->actions = possible_actions;
3017   
3018   if ((protocol == GDK_DRAG_PROTO_XDND) &&
3019       (!private->xdnd_actions_set ||
3020        private->xdnd_actions != possible_actions))
3021     xdnd_set_actions (context);
3022
3023   if (context->dest_window != dest_window)
3024     {
3025       GdkEvent temp_event;
3026
3027       /* Send a leave to the last destination */
3028       gdk_drag_do_leave (context, time);
3029       private->drag_status = GDK_DRAG_STATUS_DRAG;
3030
3031       /* Check if new destination accepts drags, and which protocol */
3032
3033       if (dest_window)
3034         {
3035           context->dest_window = dest_window;
3036           private->drop_xid = private->dest_xid;
3037           g_object_ref (context->dest_window);
3038           context->protocol = protocol;
3039
3040           switch (protocol)
3041             {
3042             case GDK_DRAG_PROTO_MOTIF:
3043               motif_send_enter (context, time);
3044               break;
3045
3046             case GDK_DRAG_PROTO_XDND:
3047               xdnd_send_enter (context);
3048               break;
3049
3050             case GDK_DRAG_PROTO_ROOTWIN:
3051             case GDK_DRAG_PROTO_NONE:
3052             default:
3053               break;
3054             }
3055           private->old_action = suggested_action;
3056           context->suggested_action = suggested_action;
3057           private->old_actions = possible_actions;
3058         }
3059       else
3060         {
3061           context->dest_window = NULL;
3062           private->drop_xid = None;
3063           context->action = 0;
3064         }
3065
3066       /* Push a status event, to let the client know that
3067        * the drag changed 
3068        */
3069
3070       temp_event.dnd.type = GDK_DRAG_STATUS;
3071       temp_event.dnd.window = context->source_window;
3072       /* We use this to signal a synthetic status. Perhaps
3073        * we should use an extra field...
3074        */
3075       temp_event.dnd.send_event = TRUE;
3076
3077       temp_event.dnd.context = context;
3078       temp_event.dnd.time = time;
3079
3080       gdk_event_put (&temp_event);
3081     }
3082   else
3083     {
3084       private->old_action = context->suggested_action;
3085       context->suggested_action = suggested_action;
3086     }
3087
3088   /* Send a drag-motion event */
3089
3090   private->last_x = x_root;
3091   private->last_y = y_root;
3092       
3093   if (context->dest_window)
3094     {
3095       if (private->drag_status == GDK_DRAG_STATUS_DRAG)
3096         {
3097           switch (context->protocol)
3098             {
3099             case GDK_DRAG_PROTO_MOTIF:
3100               motif_send_motion (context, x_root, y_root, suggested_action, time);
3101               break;
3102               
3103             case GDK_DRAG_PROTO_XDND:
3104               xdnd_send_motion (context, x_root, y_root, suggested_action, time);
3105               break;
3106
3107             case GDK_DRAG_PROTO_ROOTWIN:
3108               {
3109                 GdkEvent temp_event;
3110                 /* GTK+ traditionally has used application/x-rootwin-drop,
3111                  * but the XDND spec specifies x-rootwindow-drop.
3112                  */
3113                 GdkAtom target1 = gdk_atom_intern ("application/x-rootwindow-drop", FALSE);
3114                 GdkAtom target2 = gdk_atom_intern ("application/x-rootwin-drop", FALSE);
3115
3116                 if (g_list_find (context->targets,
3117                                  GDK_ATOM_TO_POINTER (target1)) ||
3118                     g_list_find (context->targets,
3119                                  GDK_ATOM_TO_POINTER (target2)))
3120                   context->action = context->suggested_action;
3121                 else
3122                   context->action = 0;
3123
3124                 temp_event.dnd.type = GDK_DRAG_STATUS;
3125                 temp_event.dnd.window = context->source_window;
3126                 temp_event.dnd.send_event = FALSE;
3127                 temp_event.dnd.context = context;
3128                 temp_event.dnd.time = time;
3129
3130                 gdk_event_put (&temp_event);
3131               }
3132               break;
3133             case GDK_DRAG_PROTO_NONE:
3134               g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
3135               break;
3136             default:
3137               break;
3138             }
3139         }
3140       else
3141         return TRUE;
3142     }
3143
3144   return FALSE;
3145 }
3146
3147 /**
3148  * gdk_drag_drop:
3149  * @context: a #GdkDragContext.
3150  * @time_: the timestamp for this operation.
3151  * 
3152  * Drops on the current destination.
3153  * 
3154  * This function is called by the drag source.
3155  **/
3156 void
3157 gdk_drag_drop (GdkDragContext *context,
3158                guint32         time)
3159 {
3160   g_return_if_fail (context != NULL);
3161
3162   if (context->dest_window)
3163     {
3164       switch (context->protocol)
3165         {
3166         case GDK_DRAG_PROTO_MOTIF:
3167           motif_send_leave (context, time);
3168           motif_send_drop (context, time);
3169           break;
3170           
3171         case GDK_DRAG_PROTO_XDND:
3172           xdnd_send_drop (context, time);
3173           break;
3174
3175         case GDK_DRAG_PROTO_ROOTWIN:
3176           g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
3177           break;
3178         case GDK_DRAG_PROTO_NONE:
3179           g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
3180           break;
3181         default:
3182           break;
3183         }
3184     }
3185 }
3186
3187 /**
3188  * gdk_drag_abort:
3189  * @context: a #GdkDragContext.
3190  * @time_: the timestamp for this operation.
3191  * 
3192  * Aborts a drag without dropping. 
3193  *
3194  * This function is called by the drag source.
3195  **/
3196 void
3197 gdk_drag_abort (GdkDragContext *context,
3198                 guint32         time)
3199 {
3200   g_return_if_fail (context != NULL);
3201
3202   gdk_drag_do_leave (context, time);
3203 }
3204
3205 /* Destination side */
3206
3207 /**
3208  * gdk_drag_status:
3209  * @context: a #GdkDragContext.
3210  * @action: the selected action which will be taken when a drop happens, 
3211  *    or 0 to indicate that a drop will not be accepted.
3212  * @time_: the timestamp for this operation.
3213  * 
3214  * Selects one of the actions offered by the drag source.
3215  *
3216  * This function is called by the drag destination in response to
3217  * gdk_drag_motion() called by the drag source.
3218  **/
3219 void             
3220 gdk_drag_status (GdkDragContext   *context,
3221                  GdkDragAction     action,
3222                  guint32           time)
3223 {
3224   GdkDragContextPrivateX11 *private;
3225   XEvent xev;
3226   GdkDisplay *display;
3227
3228   g_return_if_fail (context != NULL);
3229
3230   private = PRIVATE_DATA (context);
3231   display = GDK_DRAWABLE_DISPLAY (context->source_window);
3232   
3233   context->action = action;
3234
3235   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3236     {
3237       gboolean need_coords = FALSE;
3238       
3239       xev.xclient.type = ClientMessage;
3240       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
3241                                                                         "_MOTIF_DRAG_AND_DROP_MESSAGE");
3242       xev.xclient.format = 8;
3243       xev.xclient.window = GDK_DRAWABLE_XID (context->source_window);
3244
3245       if (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT)
3246         {
3247           MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED | 0x80;
3248         }
3249       else
3250         {
3251           if ((action != 0) != (private->old_action != 0))
3252             {
3253               if (action != 0)
3254                 {
3255                   MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_ENTER | 0x80;
3256                   need_coords = TRUE;
3257                 }
3258               else
3259                 MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_LEAVE | 0x80;
3260             }
3261           else
3262             {
3263               MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION | 0x80;
3264               need_coords = TRUE;
3265             }
3266         }
3267
3268       MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3269
3270       switch (action)
3271         {
3272         case GDK_ACTION_MOVE:
3273           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_MOVE;
3274           break;
3275         case GDK_ACTION_COPY:
3276           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY;
3277           break;
3278         case GDK_ACTION_LINK:
3279           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_LINK;
3280           break;
3281         default:
3282           MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP;
3283           break;
3284         }
3285
3286       if (action)
3287         MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmDROP_SITE_VALID << 4);
3288       else
3289         MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmNO_DROP_SITE << 4);
3290
3291       MOTIF_XCLIENT_LONG (&xev, 1) = time;
3292       
3293       if (need_coords)
3294         {
3295           MOTIF_XCLIENT_SHORT (&xev, 4) = private->last_x;
3296           MOTIF_XCLIENT_SHORT (&xev, 5) = private->last_y;
3297         }
3298       else
3299         MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3300       
3301       MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3302       MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3303
3304       if (!_gdk_send_xevent (display,
3305                              GDK_DRAWABLE_XID (context->source_window),
3306                              FALSE, 0, &xev))
3307         GDK_NOTE (DND, 
3308                   g_message ("Send event to %lx failed",
3309                              GDK_DRAWABLE_XID (context->source_window)));
3310     }
3311   else if (context->protocol == GDK_DRAG_PROTO_XDND)
3312     {
3313       xev.xclient.type = ClientMessage;
3314       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus");
3315       xev.xclient.format = 32;
3316       xev.xclient.window = GDK_DRAWABLE_XID (context->source_window);
3317
3318       xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->dest_window);
3319       xev.xclient.data.l[1] = (action != 0) ? (2 | 1) : 0;
3320       xev.xclient.data.l[2] = 0;
3321       xev.xclient.data.l[3] = 0;
3322       xev.xclient.data.l[4] = xdnd_action_to_atom (display, action);
3323       
3324       if (!xdnd_send_xevent (display,
3325                              GDK_DRAWABLE_XID (context->source_window),
3326                              FALSE, &xev))
3327         GDK_NOTE (DND, 
3328                   g_message ("Send event to %lx failed",
3329                              GDK_DRAWABLE_XID (context->source_window)));
3330     }
3331
3332   private->old_action = action;
3333 }
3334
3335 /**
3336  * gdk_drop_reply:
3337  * @context: a #GdkDragContext.
3338  * @ok: %TRUE if the drop is accepted.
3339  * @time_: the timestamp for this operation.
3340  * 
3341  * Accepts or rejects a drop. 
3342  *
3343  * This function is called by the drag destination in response
3344  * to a drop initiated by the drag source.
3345  **/
3346 void 
3347 gdk_drop_reply (GdkDragContext   *context,
3348                 gboolean          ok,
3349                 guint32           time)
3350 {
3351   GdkDragContextPrivateX11 *private;
3352
3353   g_return_if_fail (context != NULL);
3354
3355   private = PRIVATE_DATA (context);
3356   
3357   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3358     {
3359       GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
3360       XEvent xev;
3361
3362       xev.xclient.type = ClientMessage;
3363       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
3364                                                                         "_MOTIF_DRAG_AND_DROP_MESSAGE");
3365       xev.xclient.format = 8;
3366
3367       MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START | 0x80;
3368       MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
3369       if (ok)
3370         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY | 
3371                                        (XmDROP_SITE_VALID << 4) |
3372                                        (XmDROP_NOOP << 8) |
3373                                        (XmDROP << 12);
3374       else
3375         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP | 
3376                                        (XmNO_DROP_SITE << 4) |
3377                                        (XmDROP_NOOP << 8) |
3378                                        (XmDROP_CANCEL << 12);
3379       MOTIF_XCLIENT_SHORT (&xev, 2) = private->last_x;
3380       MOTIF_XCLIENT_SHORT (&xev, 3) = private->last_y;
3381       MOTIF_XCLIENT_LONG (&xev, 2) = 0;
3382       MOTIF_XCLIENT_LONG (&xev, 3) = 0;
3383       MOTIF_XCLIENT_LONG (&xev, 4) = 0;
3384       
3385       _gdk_send_xevent (display,
3386                         GDK_DRAWABLE_XID (context->source_window),
3387                         FALSE, 0, &xev);
3388     }
3389 }
3390
3391 /**
3392  * gdk_drop_finish:
3393  * @context: a #GtkDragContext.
3394  * @success: %TRUE if the data was successfully received.
3395  * @time_: the timestamp for this operation.
3396  * 
3397  * Ends the drag operation after a drop.
3398  *
3399  * This function is called by the drag destination.
3400  **/
3401 void             
3402 gdk_drop_finish (GdkDragContext   *context,
3403                  gboolean          success,
3404                  guint32           time)
3405 {
3406   g_return_if_fail (context != NULL);
3407
3408   if (context->protocol == GDK_DRAG_PROTO_XDND)
3409     {
3410       GdkDisplay *display = GDK_DRAWABLE_DISPLAY (context->source_window);
3411       XEvent xev;
3412
3413       xev.xclient.type = ClientMessage;
3414       xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished");
3415       xev.xclient.format = 32;
3416       xev.xclient.window = GDK_DRAWABLE_XID (context->source_window);
3417       
3418       xev.xclient.data.l[0] = GDK_DRAWABLE_XID (context->dest_window);
3419       xev.xclient.data.l[1] = 0;
3420       xev.xclient.data.l[2] = 0;
3421       xev.xclient.data.l[3] = 0;
3422       xev.xclient.data.l[4] = 0;
3423
3424       if (!xdnd_send_xevent (display,
3425                              GDK_DRAWABLE_XID (context->source_window),
3426                              FALSE, &xev))
3427         GDK_NOTE (DND, 
3428                   g_message ("Send event to %lx failed",
3429                              GDK_DRAWABLE_XID (context->source_window)));
3430     }
3431 }
3432
3433
3434 void            
3435 gdk_window_register_dnd (GdkWindow      *window)
3436 {
3437   static gulong xdnd_version = 3;
3438   MotifDragReceiverInfo info;
3439   Atom motif_drag_receiver_info_atom;
3440   GdkDisplay *display = gdk_drawable_get_display (window);
3441
3442   g_return_if_fail (window != NULL);
3443
3444   if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "gdk-dnd-registered")))
3445     return;
3446   else
3447     g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
3448   
3449   /* Set Motif drag receiver information property */
3450
3451   motif_drag_receiver_info_atom = gdk_x11_get_xatom_by_name_for_display (display,
3452                                                                          "_MOTIF_DRAG_RECEIVER_INFO");
3453   info.byte_order = local_byte_order;
3454   info.protocol_version = 0;
3455   info.protocol_style = XmDRAG_DYNAMIC;
3456   info.proxy_window = None;
3457   info.num_drop_sites = 0;
3458   info.total_size = sizeof(info);
3459
3460   XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_DRAWABLE_XID (window),
3461                    motif_drag_receiver_info_atom,
3462                    motif_drag_receiver_info_atom,
3463                    8, PropModeReplace,
3464                    (guchar *)&info,
3465                    sizeof (info));
3466
3467   /* Set XdndAware */
3468
3469   /* The property needs to be of type XA_ATOM, not XA_INTEGER. Blech */
3470   XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
3471                    GDK_DRAWABLE_XID (window),
3472                    gdk_x11_get_xatom_by_name_for_display (display, "XdndAware"),
3473                    XA_ATOM, 32, PropModeReplace,
3474                    (guchar *)&xdnd_version, 1);
3475 }
3476
3477 /**
3478  * gdk_drag_get_selection:
3479  * @context: a #GdkDragContext.
3480  * 
3481  * Returns the selection atom for the current source window.
3482  * 
3483  * Return value: the selection atom.
3484  **/
3485 GdkAtom
3486 gdk_drag_get_selection (GdkDragContext *context)
3487 {
3488   g_return_val_if_fail (context != NULL, GDK_NONE);
3489
3490   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
3491     return gdk_x11_xatom_to_atom_for_display (GDK_DRAWABLE_DISPLAY (context->source_window),
3492                                               (PRIVATE_DATA (context))->motif_selection);
3493   else if (context->protocol == GDK_DRAG_PROTO_XDND)
3494     return gdk_x11_xatom_to_atom_for_display (GDK_DRAWABLE_DISPLAY (context->source_window),
3495                                               (PRIVATE_DATA (context))->xdnd_selection);
3496   else
3497     return GDK_NONE;
3498 }
3499