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