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