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