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