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