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