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