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