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