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