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