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