]> Pileus Git - ~andy/gtk/blob - gdk/gdkdnd.c
Request XmTRANFER_FAILURE on Motif drops not within a drop zone. I don't
[~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 guint32
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 ? win : GDK_NONE;
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 guint32
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   Window *proxy_data;
2110   Window proxy;
2111   static GdkAtom xdnd_proxy_atom = GDK_NONE;
2112
2113   gint old_warnings = gdk_error_warnings;
2114
2115   if (!xdnd_proxy_atom)
2116     xdnd_proxy_atom = gdk_atom_intern ("XdndProxy", FALSE);
2117
2118   if (!xdnd_aware_atom)
2119     xdnd_aware_atom = gdk_atom_intern ("XdndAware", FALSE);
2120
2121   proxy = GDK_NONE;
2122   
2123   gdk_error_code = 0;
2124   gdk_error_warnings = 0;
2125
2126   XGetWindowProperty (gdk_display, win, 
2127                       xdnd_proxy_atom, 0, 
2128                       1, False, AnyPropertyType,
2129                       &type, &format, &nitems, &after, 
2130                       (guchar **)&proxy_data);
2131
2132   if (!gdk_error_code)
2133     {
2134       if (type != None)
2135         {
2136           if ((format == 32) && (nitems == 1))
2137             {
2138               proxy = *proxy_data;
2139             }
2140           else
2141             GDK_NOTE (DND, 
2142                       g_warning ("Invalid XdndOwner property on window %ld\n", win));
2143           
2144           XFree (proxy_data);
2145         }
2146       
2147       XGetWindowProperty (gdk_display, proxy ? proxy : win,
2148                           xdnd_aware_atom, 0, 
2149                           1, False, AnyPropertyType,
2150                           &type, &format, &nitems, &after, 
2151                           (guchar **)&version);
2152       
2153       if (!gdk_error_code && type != None)
2154         {
2155           if ((format == 32) && (nitems == 1))
2156             {
2157               if (*version == 3)
2158                 retval = TRUE;
2159             }
2160           else
2161             GDK_NOTE (DND, 
2162                       g_warning ("Invalid XdndAware property on window %ld\n", win));
2163           
2164           XFree (version);
2165         }
2166       
2167     }
2168
2169   gdk_error_warnings = old_warnings;
2170   gdk_error_code = 0;
2171   
2172   return retval ? (proxy ? proxy : win) : GDK_NONE;
2173 }
2174
2175 /* Target side */
2176
2177 static GdkFilterReturn 
2178 xdnd_enter_filter (GdkXEvent *xev,
2179                    GdkEvent  *event,
2180                    gpointer   data)
2181 {
2182   XEvent *xevent = (XEvent *)xev;
2183   GdkDragContext *new_context;
2184   gint i;
2185   
2186   guint32 source_window = xevent->xclient.data.l[0];
2187   gboolean get_types = ((xevent->xclient.data.l[1] & 1) != 0);
2188   gint version = (xevent->xclient.data.l[1] & 0xff000000) >> 24;
2189   
2190   GDK_NOTE (DND, 
2191             g_message ("XdndEnter: source_window: %#x, version: %#x",
2192                        source_window, version));
2193
2194   if (version != 3)
2195     {
2196       /* Old source ignore */
2197       GDK_NOTE (DND, g_message ("Ignored old XdndEnter message"));
2198       return GDK_FILTER_REMOVE;
2199     }
2200   
2201   if (current_dest_drag != NULL)
2202     {
2203       gdk_drag_context_unref (current_dest_drag);
2204       current_dest_drag = NULL;
2205     }
2206
2207   new_context = gdk_drag_context_new ();
2208   new_context->protocol = GDK_DRAG_PROTO_XDND;
2209   new_context->is_source = FALSE;
2210
2211   new_context->source_window = gdk_window_lookup (source_window);
2212   if (new_context->source_window)
2213     gdk_window_ref (new_context->source_window);
2214   else
2215     {
2216       new_context->source_window = gdk_window_foreign_new (source_window);
2217       if (!new_context->source_window)
2218         {
2219           gdk_drag_context_unref (new_context);
2220           return GDK_FILTER_REMOVE;
2221         }
2222     }
2223   new_context->dest_window = event->any.window;
2224   gdk_window_ref (new_context->dest_window);
2225
2226   new_context->targets = NULL;
2227   if (get_types)
2228     {
2229       Atom type;
2230       int format;
2231       gulong nitems, after;
2232       Atom *data;
2233
2234       XGetWindowProperty (GDK_WINDOW_XDISPLAY (event->any.window), 
2235                           source_window, 
2236                           gdk_atom_intern ("XdndTypeList", FALSE), 0, 65536,
2237                           False, XA_ATOM, &type, &format, &nitems,
2238                           &after, (guchar **)&data);
2239
2240       if ((format != 32) || (type != XA_ATOM))
2241         {
2242           gdk_drag_context_unref (new_context);
2243           return GDK_FILTER_REMOVE;
2244         }
2245
2246       for (i=0; i<nitems; i++)
2247         new_context->targets = g_list_append (new_context->targets,
2248                                               GUINT_TO_POINTER (data[i]));
2249
2250       XFree(data);
2251     }
2252   else
2253     {
2254       for (i=0; i<3; i++)
2255         if (xevent->xclient.data.l[2+i])
2256           new_context->targets = g_list_append (new_context->targets,
2257                                                 GUINT_TO_POINTER (xevent->xclient.data.l[2+i]));
2258     }
2259
2260 #ifdef G_ENABLE_DEBUG
2261   if (gdk_debug_flags & GDK_DEBUG_DND)
2262     print_target_list (new_context->targets);
2263 #endif /* G_ENABLE_DEBUG */
2264
2265   event->dnd.type = GDK_DRAG_ENTER;
2266   event->dnd.context = new_context;
2267   gdk_drag_context_ref (new_context);
2268
2269   current_dest_drag = new_context;
2270   ((GdkDragContextPrivate *)new_context)->xdnd_selection = 
2271     gdk_atom_intern ("XdndSelection", FALSE);
2272
2273   return GDK_FILTER_TRANSLATE;
2274 }
2275
2276 static GdkFilterReturn 
2277 xdnd_leave_filter (GdkXEvent *xev,
2278                    GdkEvent  *event,
2279                    gpointer   data)
2280 {
2281   XEvent *xevent = (XEvent *)xev;
2282   guint32 source_window = xevent->xclient.data.l[0];
2283   
2284   GDK_NOTE (DND, 
2285             g_message ("XdndLeave: source_window: %#x",
2286                        source_window));
2287
2288   if ((current_dest_drag != NULL) &&
2289       (current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2290       (GDK_WINDOW_XWINDOW (current_dest_drag->source_window) == source_window))
2291     {
2292       event->dnd.type = GDK_DRAG_LEAVE;
2293       /* Pass ownership of context to the event */
2294       event->dnd.context = current_dest_drag;
2295
2296       current_dest_drag = NULL;
2297
2298       return GDK_FILTER_TRANSLATE;
2299     }
2300   else
2301     return GDK_FILTER_REMOVE;
2302 }
2303
2304 static GdkFilterReturn 
2305 xdnd_position_filter (GdkXEvent *xev,
2306                       GdkEvent  *event,
2307                       gpointer   data)
2308 {
2309   XEvent *xevent = (XEvent *)xev;
2310   guint32 source_window = xevent->xclient.data.l[0];
2311   gint16 x_root = xevent->xclient.data.l[2] >> 16;
2312   gint16 y_root = xevent->xclient.data.l[2] & 0xffff;
2313   guint32 time = xevent->xclient.data.l[3];
2314   GdkAtom action = xevent->xclient.data.l[4];
2315   
2316   GDK_NOTE (DND, 
2317             g_message ("XdndPosition: source_window: %#x  position: (%d, %d)  time: %d  action: %ld",
2318                        source_window, x_root, y_root, time, action));
2319
2320   
2321   if ((current_dest_drag != NULL) &&
2322       (current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2323       (GDK_WINDOW_XWINDOW (current_dest_drag->source_window) == source_window))
2324     {
2325       event->dnd.type = GDK_DRAG_MOTION;
2326       event->dnd.context = current_dest_drag;
2327       gdk_drag_context_ref (current_dest_drag);
2328
2329       event->dnd.time = time;
2330
2331       current_dest_drag->suggested_action = xdnd_action_from_atom (action);
2332
2333       event->dnd.x_root = x_root;
2334       event->dnd.y_root = y_root;
2335
2336       ((GdkDragContextPrivate *)current_dest_drag)->last_x = x_root;
2337       ((GdkDragContextPrivate *)current_dest_drag)->last_y = y_root;
2338       
2339       return GDK_FILTER_TRANSLATE;
2340     }
2341
2342   return GDK_FILTER_REMOVE;
2343 }
2344
2345 static GdkFilterReturn 
2346 xdnd_drop_filter (GdkXEvent *xev,
2347                   GdkEvent  *event,
2348                   gpointer   data)
2349 {
2350   XEvent *xevent = (XEvent *)xev;
2351   guint32 source_window = xevent->xclient.data.l[0];
2352   guint32 time = xevent->xclient.data.l[2];
2353   
2354   GDK_NOTE (DND, 
2355             g_message ("XdndDrop: source_window: %#x  time: %d",
2356                        source_window, time));
2357
2358   if ((current_dest_drag != NULL) &&
2359       (current_dest_drag->protocol == GDK_DRAG_PROTO_XDND) &&
2360       (GDK_WINDOW_XWINDOW (current_dest_drag->source_window) == source_window))
2361     {
2362       GdkDragContextPrivate *private;
2363       private = (GdkDragContextPrivate *)current_dest_drag;
2364
2365       event->dnd.type = GDK_DROP_START;
2366
2367       event->dnd.context = current_dest_drag;
2368       gdk_drag_context_ref (current_dest_drag);
2369
2370       event->dnd.time = time;
2371       event->dnd.x_root = private->last_x;
2372       event->dnd.y_root = private->last_y;
2373       
2374       return GDK_FILTER_TRANSLATE;
2375     }
2376
2377   return GDK_FILTER_REMOVE;
2378 }
2379
2380 /*************************************************************
2381  ************************** Public API ***********************
2382  *************************************************************/
2383
2384 void
2385 gdk_dnd_init (void)
2386 {
2387   init_byte_order();
2388
2389   gdk_add_client_message_filter (
2390         gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE),
2391         motif_dnd_filter, NULL);
2392   gdk_add_client_message_filter (
2393         gdk_atom_intern ("XdndEnter", FALSE),
2394         xdnd_enter_filter, NULL);
2395   gdk_add_client_message_filter (
2396         gdk_atom_intern ("XdndLeave", FALSE),
2397         xdnd_leave_filter, NULL);
2398   gdk_add_client_message_filter (
2399         gdk_atom_intern ("XdndPosition", FALSE),
2400         xdnd_position_filter, NULL);
2401   gdk_add_client_message_filter (
2402         gdk_atom_intern ("XdndStatus", FALSE),
2403         xdnd_status_filter, NULL);
2404   gdk_add_client_message_filter (
2405         gdk_atom_intern ("XdndFinished", FALSE),
2406         xdnd_finished_filter, NULL);
2407   gdk_add_client_message_filter (
2408         gdk_atom_intern ("XdndDrop", FALSE),
2409         xdnd_drop_filter, NULL);
2410 }                     
2411
2412 /* Source side */
2413
2414 static void
2415 gdk_drag_do_leave (GdkDragContext *context, guint32 time)
2416 {
2417   if (context->dest_window)
2418     {
2419       switch (context->protocol)
2420         {
2421         case GDK_DRAG_PROTO_MOTIF:
2422           motif_send_leave (context, time);
2423           break;
2424         case GDK_DRAG_PROTO_XDND:
2425           xdnd_send_leave (context);
2426           break;
2427         case GDK_DRAG_PROTO_ROOTWIN:
2428           break;
2429         }
2430
2431       gdk_window_unref (context->dest_window);
2432       context->dest_window = NULL;
2433     }
2434 }
2435
2436 GdkDragContext * 
2437 gdk_drag_begin (GdkWindow     *window,
2438                 GList         *targets,
2439                 GdkDragAction  actions)
2440 {
2441   GList *tmp_list;
2442   GdkDragContext *new_context;
2443   
2444   g_return_val_if_fail (window != NULL, NULL);
2445
2446   new_context = gdk_drag_context_new ();
2447   new_context->is_source = TRUE;
2448   new_context->source_window = window;
2449   gdk_window_ref (window);
2450
2451   tmp_list = g_list_last (targets);
2452   new_context->targets = NULL;
2453   while (tmp_list)
2454     {
2455       new_context->targets = g_list_prepend (new_context->targets,
2456                                              tmp_list->data);
2457       tmp_list = tmp_list->prev;
2458     }
2459
2460   new_context->actions = actions;
2461
2462   return new_context;
2463 }
2464
2465 guint32
2466 gdk_drag_get_protocol (guint32          xid,
2467                        GdkDragProtocol *protocol)
2468 {
2469   guint32 retval;
2470   
2471   if ((retval = xdnd_check_dest (xid)))
2472     {
2473       *protocol = GDK_DRAG_PROTO_XDND;
2474       GDK_NOTE (DND, g_message ("Entering dnd window %#x\n", xid));
2475       return retval;
2476     }
2477   else if ((retval = motif_check_dest (xid)))
2478     {
2479       *protocol = GDK_DRAG_PROTO_MOTIF;
2480       GDK_NOTE (DND, g_message ("Entering motif window %#x\n", xid));
2481       return retval;
2482     }
2483   else
2484     {
2485       /* Check if this is a root window */
2486
2487       gboolean rootwin = FALSE;
2488       gint old_warnings = gdk_error_warnings;
2489       Atom type = None;
2490       int format;
2491       unsigned long nitems, after;
2492       unsigned char *data;
2493
2494       if (xid == gdk_root_window)
2495         rootwin = TRUE;
2496
2497       if (!rootwin)
2498         {
2499           gdk_error_code = 0;
2500           
2501           XGetWindowProperty (gdk_display, xid,
2502                               gdk_atom_intern ("ENLIGHTENMENT_DESKTOP", FALSE),
2503                               0, 0, False, AnyPropertyType,
2504                               &type, &format, &nitems, &after, &data);
2505           if ((gdk_error_code == 0) && type != None)
2506             {
2507               XFree (data);
2508               rootwin = TRUE;
2509             }
2510         }
2511
2512       /* Until I find out what window manager the next one is for,
2513        * I'm leaving it commented out. It's supported in the
2514        * xscreensaver sources, though.
2515        */
2516 #if 0
2517       if (!rootwin)
2518         {
2519           gdk_error_code = 0;
2520           
2521           XGetWindowProperty (gdk_display, win,
2522                               gdk_atom_intern ("__SWM_VROOT", FALSE),
2523                               0, 0, False, AnyPropertyType,
2524                               &type, &format, &nitems, &data);
2525           if ((gdk_error_code == 0) && type != None)
2526             rootwin = TRUE;
2527         }
2528 #endif      
2529
2530       gdk_error_warnings = old_warnings;
2531
2532       if (rootwin)
2533         {
2534           *protocol = GDK_DRAG_PROTO_ROOTWIN;
2535           return xid;
2536         }
2537     }
2538
2539   return GDK_NONE;
2540 }
2541
2542 void
2543 gdk_drag_find_window (GdkDragContext  *context,
2544                       GdkWindow       *drag_window,
2545                       gint             x_root,
2546                       gint             y_root,
2547                       GdkWindow      **dest_window,
2548                       GdkDragProtocol *protocol)
2549 {
2550   GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
2551   Window dest;
2552
2553   if (!private->window_cache)
2554     private->window_cache = gdk_window_cache_new();
2555
2556   dest = get_client_window_at_coords (private->window_cache,
2557                                       drag_window ? 
2558                                       GDK_WINDOW_XWINDOW (drag_window) : GDK_NONE,
2559                                       x_root, y_root);
2560
2561   if (private->dest_xid != dest)
2562     {
2563       Window recipient;
2564       private->dest_xid = dest;
2565
2566       /* Check if new destination accepts drags, and which protocol */
2567
2568       if ((recipient = gdk_drag_get_protocol (dest, protocol)))
2569         {
2570           *dest_window = gdk_window_lookup (recipient);
2571           if (*dest_window)
2572             gdk_window_ref (*dest_window);
2573           else
2574             *dest_window = gdk_window_foreign_new (recipient);
2575         }
2576       else
2577         *dest_window = NULL;
2578     }
2579   else
2580     {
2581       *dest_window = context->dest_window;
2582       if (*dest_window)
2583         gdk_window_ref (*dest_window);
2584       *protocol = context->protocol;
2585     }
2586 }
2587
2588 gboolean        
2589 gdk_drag_motion (GdkDragContext *context,
2590                  GdkWindow      *dest_window,
2591                  GdkDragProtocol protocol,
2592                  gint            x_root, 
2593                  gint            y_root,
2594                  GdkDragAction   action,
2595                  guint32         time)
2596 {
2597   GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
2598
2599   if (context->dest_window != dest_window)
2600     {
2601       GdkEvent temp_event;
2602
2603       /* Send a leave to the last destination */
2604       gdk_drag_do_leave (context, time);
2605       private->drag_status = GDK_DRAG_STATUS_DRAG;
2606
2607       /* Check if new destination accepts drags, and which protocol */
2608
2609       if (dest_window)
2610         {
2611           context->dest_window = dest_window;
2612           gdk_window_ref (context->dest_window);
2613           context->protocol = protocol;
2614
2615           switch (protocol)
2616             {
2617             case GDK_DRAG_PROTO_MOTIF:
2618               motif_send_enter (context, time);
2619               break;
2620
2621             case GDK_DRAG_PROTO_XDND:
2622               xdnd_send_enter (context);
2623               break;
2624
2625             case GDK_DRAG_PROTO_ROOTWIN:
2626               break;
2627             }
2628           private->old_action = action;
2629           context->suggested_action = action;
2630         }
2631       else
2632         {
2633           context->dest_window = NULL;
2634           context->action = 0;
2635         }
2636
2637       /* Push a status event, to let the client know that
2638        * the drag changed 
2639        */
2640
2641       temp_event.dnd.type = GDK_DRAG_STATUS;
2642       temp_event.dnd.window = context->source_window;
2643       /* We use this to signal a synthetic status. Perhaps
2644        * we should use an extra field...
2645        */
2646       temp_event.dnd.send_event = TRUE;
2647
2648       temp_event.dnd.context = context;
2649       temp_event.dnd.time = time;
2650
2651       gdk_event_put (&temp_event);
2652     }
2653   else
2654     {
2655       private->old_action = context->suggested_action;
2656       context->suggested_action = action;
2657     }
2658
2659   /* Send a drag-motion event */
2660
2661   private->last_x = x_root;
2662   private->last_y = y_root;
2663       
2664   if (context->dest_window)
2665     {
2666       if (private->drag_status == GDK_DRAG_STATUS_DRAG)
2667         {
2668           switch (context->protocol)
2669             {
2670             case GDK_DRAG_PROTO_MOTIF:
2671               motif_send_motion (context, x_root, y_root, action, time);
2672               break;
2673               
2674             case GDK_DRAG_PROTO_XDND:
2675               xdnd_send_motion (context, x_root, y_root, action, time);
2676               break;
2677
2678             case GDK_DRAG_PROTO_ROOTWIN:
2679               {
2680                 GdkEvent temp_event;
2681
2682                 if (g_list_find (context->targets,
2683                                  GUINT_TO_POINTER (gdk_atom_intern ("application/x-rootwin-drop", FALSE))))
2684                   context->action = context->suggested_action;
2685                 else
2686                   context->action = 0;
2687
2688                 temp_event.dnd.type = GDK_DRAG_STATUS;
2689                 temp_event.dnd.window = context->source_window;
2690                 temp_event.dnd.send_event = FALSE;
2691                 temp_event.dnd.context = context;
2692                 temp_event.dnd.time = time;
2693
2694                 gdk_event_put (&temp_event);
2695               }
2696               break;
2697             }
2698         }
2699       else
2700         return TRUE;
2701     }
2702
2703   return FALSE;
2704 }
2705
2706 void
2707 gdk_drag_drop (GdkDragContext *context,
2708                guint32         time)
2709 {
2710   if (context->dest_window)
2711     {
2712       switch (context->protocol)
2713         {
2714         case GDK_DRAG_PROTO_MOTIF:
2715           motif_send_leave (context, time);
2716           motif_send_drop (context, time);
2717           break;
2718           
2719         case GDK_DRAG_PROTO_XDND:
2720           xdnd_send_drop (context, time);
2721           break;
2722
2723         case GDK_DRAG_PROTO_ROOTWIN:
2724           g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
2725           break;
2726         }
2727     }
2728 }
2729
2730 void
2731 gdk_drag_abort (GdkDragContext *context,
2732                 guint32         time)
2733 {
2734   gdk_drag_do_leave (context, time);
2735 }
2736
2737 /* Destination side */
2738
2739 void             
2740 gdk_drag_status (GdkDragContext   *context,
2741                  GdkDragAction     action,
2742                  guint32           time)
2743 {
2744   GdkDragContextPrivate *private;
2745   XEvent xev;
2746
2747   g_return_if_fail (context != 0);
2748
2749   private = (GdkDragContextPrivate *)context;
2750
2751   context->action = action;
2752   
2753   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
2754     {
2755       xev.xclient.type = ClientMessage;
2756       xev.xclient.message_type = gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE);
2757       xev.xclient.format = 8;
2758       xev.xclient.window = GDK_WINDOW_XWINDOW (context->source_window);
2759
2760       if (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT)
2761         {
2762           xev.xclient.data.b[0] = XmOPERATION_CHANGED | 0x80;
2763         }
2764       else
2765         {
2766           if ((action != 0) != (private->old_action != 0))
2767             {
2768               if (action != 0)
2769                 xev.xclient.data.b[0] = XmDROP_SITE_ENTER | 0x80;
2770               else
2771                 xev.xclient.data.b[0] = XmDROP_SITE_LEAVE | 0x80;
2772             }
2773           else
2774             xev.xclient.data.b[0] = XmDRAG_MOTION | 0x80;
2775         }
2776
2777       xev.xclient.data.b[1] = local_byte_order;
2778
2779       switch (action)
2780         {
2781         case GDK_ACTION_MOVE:
2782           xev.xclient.data.s[1] = XmDROP_MOVE;
2783           break;
2784         case GDK_ACTION_COPY:
2785           xev.xclient.data.s[1] = XmDROP_COPY;
2786           break;
2787         case GDK_ACTION_LINK:
2788           xev.xclient.data.s[1] = XmDROP_LINK;
2789           break;
2790         default:
2791           xev.xclient.data.s[1] = XmDROP_NOOP;
2792           break;
2793         }
2794
2795       if (action)
2796         xev.xclient.data.s[1] |= (XmDROP_SITE_VALID << 4);
2797       else
2798         xev.xclient.data.s[1] |= (XmNO_DROP_SITE << 4);
2799
2800       xev.xclient.data.l[1] = time;
2801       xev.xclient.data.s[4] = private->last_x;
2802       xev.xclient.data.s[5] = private->last_y;
2803
2804       if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
2805                        FALSE, 0, &xev))
2806         GDK_NOTE (DND, 
2807                   g_message ("Send event to %lx failed",
2808                              GDK_WINDOW_XWINDOW (context->source_window)));
2809     }
2810   else if (context->protocol == GDK_DRAG_PROTO_XDND)
2811     {
2812       xev.xclient.type = ClientMessage;
2813       xev.xclient.message_type = gdk_atom_intern ("XdndStatus", FALSE);
2814       xev.xclient.format = 32;
2815       xev.xclient.window = GDK_WINDOW_XWINDOW (context->source_window);
2816
2817       xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->dest_window);
2818       xev.xclient.data.l[1] = (action != 0) ? (2 | 1) : 0;
2819       xev.xclient.data.l[2] = 0;
2820       xev.xclient.data.l[3] = 0;
2821       xev.xclient.data.l[4] = xdnd_action_to_atom (action);
2822
2823       if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
2824                        FALSE, 0, &xev))
2825         GDK_NOTE (DND, 
2826                   g_message ("Send event to %lx failed",
2827                              GDK_WINDOW_XWINDOW (context->source_window)));
2828     }
2829
2830   private->old_action = action;
2831 }
2832
2833 void 
2834 gdk_drop_reply (GdkDragContext   *context,
2835                 gboolean          ok,
2836                 guint32           time)
2837 {
2838   GdkDragContextPrivate *private;
2839
2840   g_return_if_fail (context != 0);
2841
2842   private = (GdkDragContextPrivate *)context;
2843   
2844   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
2845     {
2846       XEvent xev;
2847
2848       xev.xclient.type = ClientMessage;
2849       xev.xclient.message_type = gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE);
2850       xev.xclient.format = 8;
2851
2852       xev.xclient.data.b[0] = XmDROP_START | 0x80;
2853       xev.xclient.data.b[1] = local_byte_order;
2854       if (ok)
2855         xev.xclient.data.s[1] = XmDROP_COPY | 
2856                                 (XmDROP_SITE_VALID << 4) |
2857                                 (XmDROP_NOOP << 8) |
2858                                 (XmDROP << 12);
2859       else
2860         xev.xclient.data.s[1] = XmDROP_NOOP | 
2861                                 (XmNO_DROP_SITE << 4) |
2862                                 (XmDROP_NOOP << 8) |
2863                                 (XmDROP_CANCEL << 12);
2864       xev.xclient.data.s[2] = private->last_x;
2865       xev.xclient.data.s[3] = private->last_y;
2866       
2867       gdk_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
2868                        FALSE, 0, &xev);
2869     }
2870 }
2871
2872 void             
2873 gdk_drop_finish (GdkDragContext   *context,
2874                  gboolean          success,
2875                  guint32           time)
2876 {
2877   if (context->protocol == GDK_DRAG_PROTO_XDND)
2878     {
2879       XEvent xev;
2880
2881       xev.xclient.type = ClientMessage;
2882       xev.xclient.message_type = gdk_atom_intern ("XdndFinished", FALSE);
2883       xev.xclient.format = 32;
2884       xev.xclient.window = GDK_WINDOW_XWINDOW (context->source_window);
2885
2886       xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->dest_window);
2887       xev.xclient.data.l[1] = 0;
2888       xev.xclient.data.l[2] = 0;
2889       xev.xclient.data.l[3] = 0;
2890       xev.xclient.data.l[4] = 0;
2891
2892       if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
2893                        FALSE, 0, &xev))
2894         GDK_NOTE (DND, 
2895                   g_message ("Send event to %lx failed",
2896                              GDK_WINDOW_XWINDOW (context->source_window)));
2897     }
2898 }
2899
2900
2901 void            
2902 gdk_window_register_dnd (GdkWindow      *window)
2903 {
2904   static guint32 xdnd_version = 3;
2905   
2906   MotifDragReceiverInfo info;
2907
2908   /* Set Motif drag receiver information property */
2909
2910   if (!motif_drag_receiver_info_atom)
2911     motif_drag_receiver_info_atom = gdk_atom_intern ("_MOTIF_DRAG_RECEIVER_INFO", FALSE);
2912
2913   info.byte_order = local_byte_order;
2914   info.protocol_version = 0;
2915   info.protocol_style = XmDRAG_DYNAMIC;
2916   info.proxy_window = GDK_NONE;
2917   info.num_drop_sites = 0;
2918   info.total_size = sizeof(info);
2919
2920   XChangeProperty (gdk_display, GDK_WINDOW_XWINDOW (window),
2921                    motif_drag_receiver_info_atom,
2922                    motif_drag_receiver_info_atom,
2923                    8, PropModeReplace,
2924                    (guchar *)&info,
2925                    sizeof (info));
2926
2927   /* Set XdndAware */
2928
2929   if (!xdnd_aware_atom)
2930     xdnd_aware_atom = gdk_atom_intern ("XdndAware", FALSE);
2931
2932   /* The property needs to be of type XA_ATOM, not XA_INTEGER. Blech */
2933   XChangeProperty (GDK_WINDOW_XDISPLAY (window), 
2934                    GDK_WINDOW_XWINDOW (window),
2935                    xdnd_aware_atom, XA_ATOM,
2936                    32, PropModeReplace,
2937                    (guchar *)&xdnd_version,
2938                    sizeof (xdnd_version));
2939 }
2940
2941 /*************************************************************
2942  * gdk_drag_get_selection:
2943  *     Returns the selection atom for the current source window
2944  *   arguments:
2945  *     
2946  *   results:
2947  *************************************************************/
2948
2949 GdkAtom       
2950 gdk_drag_get_selection (GdkDragContext *context)
2951 {
2952   g_return_val_if_fail (context != NULL, GDK_NONE);
2953
2954   if (context->protocol == GDK_DRAG_PROTO_MOTIF)
2955     return ((GdkDragContextPrivate *)context)->motif_selection;
2956   else if (context->protocol == GDK_DRAG_PROTO_XDND)
2957     return ((GdkDragContextPrivate *)context)->xdnd_selection;
2958   else 
2959     return GDK_NONE;
2960 }
2961