]> Pileus Git - ~andy/gtk/blobdiff - gdk/x11/gdkdnd-x11.c
[ Merges from gtk-1-2 ]
[~andy/gtk] / gdk / x11 / gdkdnd-x11.c
index b2653628a5d5c0232485123267aed9c487a32a66..a2be5e160c7cd9b7d9edb49416e9c8d7941c5616 100644 (file)
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+
+/*
+ * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
 #include <string.h>
@@ -56,10 +64,15 @@ struct _GdkDragContextPrivate {
 
   guint16 last_x;              /* Coordinates from last event */
   guint16 last_y;
-  GdkDragAction old_action;    /* The last action we sent to the source */
+  GdkDragAction old_action;      /* The last action we sent to the source */
+  GdkDragAction old_actions;     /* The last actions we sent to the source */
+  GdkDragAction xdnd_actions;     /* What is currently set in XdndActionList */
 
-  Window  dest_xid;
+  Window dest_xid;              /* The last window we looked up */
+  Window drop_xid;            /* The (non-proxied) window that is receiving drops */
   guint xdnd_targets_set : 1;   /* Whether we've already set XdndTypeList */
+  guint xdnd_actions_set : 1;   /* Whether we've already set XdndActionList */
+  guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
   guint motif_targets_set : 1;  /* Whether we've already set motif initiator info */
   guint drag_status : 4;       /* current status of drag */
 
@@ -101,6 +114,10 @@ static GdkFilterReturn xdnd_drop_filter (GdkXEvent *xev,
                                         GdkEvent  *event,
                                         gpointer   data);
 
+static void   xdnd_manage_source_filter (GdkDragContext *context,
+                                        GdkWindow      *window,
+                                        gboolean        add_filter);
+
 /* Drag Contexts */
 
 static GList *contexts;
@@ -133,6 +150,7 @@ gdk_drag_context_unref (GdkDragContext *context)
   GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
 
   g_return_if_fail (context != NULL);
+  g_return_if_fail (private->ref_count > 0);
 
   private->ref_count--;
   
@@ -143,7 +161,13 @@ gdk_drag_context_unref (GdkDragContext *context)
       g_list_free (context->targets);
 
       if (context->source_window)
-       gdk_window_unref (context->source_window);
+       {
+         if ((context->protocol == GDK_DRAG_PROTO_XDND) &&
+             !context->is_source)
+           xdnd_manage_source_filter (context, context->source_window, FALSE);
+
+         gdk_window_unref (context->source_window);
+       }
 
       if (context->dest_window)
        gdk_window_unref (context->dest_window);
@@ -163,16 +187,24 @@ gdk_drag_context_find (gboolean is_source,
 {
   GList *tmp_list = contexts;
   GdkDragContext *context;
+  GdkDragContextPrivate *private;
+  Window context_dest_xid;
 
   while (tmp_list)
     {
       context = (GdkDragContext *)tmp_list->data;
+      private = (GdkDragContextPrivate *)context;
+
+      context_dest_xid = context->dest_window ? 
+                          (private->drop_xid ?
+                             private->drop_xid :
+                             GDK_WINDOW_XWINDOW (context->dest_window)) :
+                          None;
 
       if ((!context->is_source == !is_source) &&
          ((source_xid == None) || (context->source_window &&
            (GDK_WINDOW_XWINDOW (context->source_window) == source_xid))) &&
-         ((dest_xid == None) || (context->dest_window &&
-           (GDK_WINDOW_XWINDOW (context->dest_window) == dest_xid))))
+         ((dest_xid == None) || (context_dest_xid == dest_xid)))
        return context;
       
       tmp_list = tmp_list->next;
@@ -355,7 +387,7 @@ gdk_window_cache_new (void)
                            xwa.x, xwa.y, xwa.width, xwa.height,
                            xwa.map_state != IsUnmapped);
 
-      if (gdk_error_code != 0)
+      if (gdk_error_code)
        gdk_error_code = 0;
       else
        {
@@ -409,9 +441,10 @@ get_client_window_at_coords_recurse (Window  win,
                      wm_state_atom, 0, 0, False, AnyPropertyType,
                      &type, &format, &nitems, &after, &data);
   
-  if (gdk_error_code != 0)
+  if (gdk_error_code)
     {
       gdk_error_code = 0;
+
       return None;
     }
 
@@ -426,9 +459,10 @@ get_client_window_at_coords_recurse (Window  win,
   XTranslateCoordinates (gdk_display, gdk_root_window, win,
                         x_root, y_root, &dest_x, &dest_y, &child);
 
-  if (gdk_error_code != 0)
+  if (gdk_error_code)
     {
       gdk_error_code = 0;
+
       return None;
     }
   
@@ -437,7 +471,7 @@ get_client_window_at_coords_recurse (Window  win,
                 &root, &tmp_parent, &children, &nchildren) == 0)
     return 0;
 
-  if (gdk_error_code == 0)
+  if (!gdk_error_code)
     {
       for (i = nchildren - 1; (i >= 0) && (child == None); i--)
        {
@@ -445,7 +479,7 @@ get_client_window_at_coords_recurse (Window  win,
          
          XGetWindowAttributes (gdk_display, children[i], &xwa);
          
-         if (gdk_error_code != 0)
+         if (gdk_error_code)
            gdk_error_code = 0;
          else if ((xwa.map_state == IsViewable) && (xwa.class == InputOutput) &&
                   (x >= xwa.x) && (x < xwa.x + (gint)xwa.width) &&
@@ -534,9 +568,10 @@ get_client_window_at_coords_recurse (Window  win,
                      wm_state_atom, 0, 0, False, AnyPropertyType,
                      &type, &format, &nitems, &after, &data);
   
-  if (gdk_error_code != 0)
+  if (gdk_error_code)
     {
       gdk_error_code = 0;
+
       return None;
     }
 
@@ -549,9 +584,10 @@ get_client_window_at_coords_recurse (Window  win,
   XTranslateCoordinates (gdk_display, gdk_root_window, win,
                         x_root, y_root, &dest_x, &dest_y, &child);
 
-  if (gdk_error_code != 0)
+  if (gdk_error_code)
     {
       gdk_error_code = 0;
+
       return None;
     }
 
@@ -588,7 +624,7 @@ get_client_window_at_coords (Window  ignore,
 
          XGetWindowAttributes (gdk_display, children[i], &xwa);
 
-         if (gdk_error_code != 0)
+         if (gdk_error_code)
            gdk_error_code = 0;
          else if ((xwa.map_state == IsViewable) &&
                   (x_root >= xwa.x) && (x_root < xwa.x + (gint)xwa.width) &&
@@ -709,6 +745,24 @@ card32_to_host (guint32 x, gchar byte_order) {
     return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
 }
 
+/* Motif packs together fields of varying length into the
+ * client message. We can't rely on accessing these
+ * through data.s[], data.l[], etc, because on some architectures
+ * (i.e., Alpha) these won't be valid for format == 8. 
+ */
+
+#define MOTIF_XCLIENT_BYTE(xevent,i) \
+  (xevent)->xclient.data.b[i]
+#define MOTIF_XCLIENT_SHORT(xevent,i) \
+  ((gint16 *)&((xevent)->xclient.data.b[0]))[i]
+#define MOTIF_XCLIENT_LONG(xevent,i) \
+  ((gint32 *)&((xevent)->xclient.data.b[0]))[i]
+
+#define MOTIF_UNPACK_BYTE(xevent,i) MOTIF_XCLIENT_BYTE(xevent,i)
+#define MOTIF_UNPACK_SHORT(xevent,i) \
+  card16_to_host (MOTIF_XCLIENT_SHORT(xevent,i), MOTIF_XCLIENT_BYTE(xevent, 1))
+#define MOTIF_UNPACK_LONG(xevent,i) \
+  card32_to_host (MOTIF_XCLIENT_LONG(xevent,i), MOTIF_XCLIENT_BYTE(xevent, 1))
 
 /***** Dest side ***********/
 
@@ -771,7 +825,7 @@ motif_drag_window_filter (GdkXEvent *xevent,
   return GDK_FILTER_REMOVE;
 }
 
-static GdkAtom motif_drag_window_atom = GDK_NONE;
+static Atom motif_drag_window_atom = GDK_NONE;
 
 static Window
 motif_lookup_drag_window (Display *display)
@@ -783,11 +837,11 @@ motif_lookup_drag_window (Display *display)
   guchar *data;
 
   XGetWindowProperty (gdk_display, gdk_root_window, motif_drag_window_atom,
-                     0, sizeof(Window)/4, FALSE,
+                     0, 1, FALSE,
                      XA_WINDOW, &type, &format, &nitems, &bytes_after,
                      &data);
   
-  if ((format == 8*sizeof(Window)) && (nitems == 1) && (bytes_after == 0))
+  if ((format == 32) && (nitems == 1) && (bytes_after == 0))
     {
       retval = *(Window *)data;
       GDK_NOTE(DND, 
@@ -819,7 +873,7 @@ motif_find_drag_window (gboolean create)
          
          Display *display;
          XSetWindowAttributes attr;
-         display = XOpenDisplay (NULL);
+         display = XOpenDisplay (gdk_display_name);
          XSetCloseDownMode (display, RetainPermanent);
 
          XGrabServer (display);
@@ -908,7 +962,7 @@ motif_read_target_table (void)
 
       XGetWindowProperty (gdk_display, motif_drag_window, motif_drag_targets_atom,
                          (sizeof(MotifTargetTableHeader)+3)/4, 
-                         (header->total_size sizeof(MotifTargetTableHeader) + 3)/4,
+                         (header->total_size + 3)/4 - (sizeof(MotifTargetTableHeader) + 3)/4,
                          FALSE,
                          motif_drag_targets_atom, &type, &format, &nitems, 
                          &bytes_after, &target_bytes);
@@ -1278,16 +1332,16 @@ motif_send_enter (GdkDragContext  *context,
   xev.xclient.format = 8;
   xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
 
-  xev.xclient.data.b[0] = XmTOP_LEVEL_ENTER;
-  xev.xclient.data.b[1] = local_byte_order;
-  xev.xclient.data.s[1] = 0;
-  xev.xclient.data.l[1] = time;
-  xev.xclient.data.l[2] = GDK_WINDOW_XWINDOW (context->source_window);
+  MOTIF_XCLIENT_BYTE (&xev, 0) = XmTOP_LEVEL_ENTER;
+  MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
+  MOTIF_XCLIENT_SHORT (&xev, 1) = 0;
+  MOTIF_XCLIENT_LONG (&xev, 1) = time;
+  MOTIF_XCLIENT_LONG (&xev, 2) = GDK_WINDOW_XWINDOW (context->source_window);
 
   if (!private->motif_targets_set)
     motif_set_targets (context);
 
-  xev.xclient.data.l[3] = private->motif_selection;
+  MOTIF_XCLIENT_LONG (&xev, 3) = private->motif_selection;
 
   if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
                        FALSE, 0, &xev))
@@ -1307,12 +1361,12 @@ motif_send_leave (GdkDragContext  *context,
   xev.xclient.format = 8;
   xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
 
-  xev.xclient.data.b[0] = XmTOP_LEVEL_LEAVE;
-  xev.xclient.data.b[1] = local_byte_order;
-  xev.xclient.data.s[1] = 0;
-  xev.xclient.data.l[1] = time;
-  xev.xclient.data.l[2] = GDK_WINDOW_XWINDOW (context->source_window);
-  xev.xclient.data.l[3] = 0;
+  MOTIF_XCLIENT_BYTE (&xev, 0) = XmTOP_LEVEL_LEAVE;
+  MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
+  MOTIF_XCLIENT_SHORT (&xev, 1) = 0;
+  MOTIF_XCLIENT_LONG (&xev, 1) = time;
+  MOTIF_XCLIENT_LONG (&xev, 2) = GDK_WINDOW_XWINDOW (context->source_window);
+  MOTIF_XCLIENT_LONG (&xev, 3) = 0;
 
   if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
                        FALSE, 0, &xev))
@@ -1337,23 +1391,24 @@ motif_send_motion (GdkDragContext  *context,
   xev.xclient.format = 8;
   xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
 
-  xev.xclient.data.b[1] = local_byte_order;
-  xev.xclient.data.s[1] = motif_dnd_get_flags (context);
-  xev.xclient.data.l[1] = time;
+  MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
+  MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
+  MOTIF_XCLIENT_LONG (&xev, 1) = time;
 
-  if (context->suggested_action != private->old_action)
+  if ((context->suggested_action != private->old_action) ||
+      (context->actions != private->old_actions))
     {
-      xev.xclient.data.b[0] = XmOPERATION_CHANGED;
+      MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED;
 
-      private->drag_status = GDK_DRAG_STATUS_ACTION_WAIT;
+      /* private->drag_status = GDK_DRAG_STATUS_ACTION_WAIT; */
       retval = TRUE;
     }
   else
     {
-      xev.xclient.data.b[0] = XmDRAG_MOTION;
+      MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION;
 
-      xev.xclient.data.s[4] = x_root;
-      xev.xclient.data.s[5] = y_root;
+      MOTIF_XCLIENT_SHORT (&xev, 4) = x_root;
+      MOTIF_XCLIENT_SHORT (&xev, 5) = y_root;
       
       private->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
       retval = FALSE;
@@ -1379,16 +1434,16 @@ motif_send_drop (GdkDragContext *context, guint32 time)
   xev.xclient.format = 8;
   xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
 
-  xev.xclient.data.b[0] = XmDROP_START;
-  xev.xclient.data.b[1] = local_byte_order;
-  xev.xclient.data.s[1] = motif_dnd_get_flags (context);
-  xev.xclient.data.l[1] = time;
+  MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START;
+  MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
+  MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
+  MOTIF_XCLIENT_LONG (&xev, 1)  = time;
 
-  xev.xclient.data.s[4] = private->last_x;
-  xev.xclient.data.s[5] = private->last_y;
+  MOTIF_XCLIENT_SHORT (&xev, 4) = private->last_x;
+  MOTIF_XCLIENT_SHORT (&xev, 5) = private->last_y;
 
-  xev.xclient.data.l[3] = private->motif_selection;
-  xev.xclient.data.l[4] = GDK_WINDOW_XWINDOW (context->source_window);
+  MOTIF_XCLIENT_LONG (&xev, 3)  = private->motif_selection;
+  MOTIF_XCLIENT_LONG (&xev, 4)  = GDK_WINDOW_XWINDOW (context->source_window);
 
   if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
                        FALSE, 0, &xev))
@@ -1431,6 +1486,11 @@ motif_read_initiator_info (Window source_window,
 
   motif_read_target_table ();
 
+  initiator_info->targets_index = 
+    card16_to_host (initiator_info->targets_index, initiator_info->byte_order);
+  initiator_info->selection_atom = 
+    card32_to_host (initiator_info->selection_atom, initiator_info->byte_order);
+  
   if (initiator_info->targets_index >= motif_n_target_lists)
     {
       g_warning ("Invalid target index in TOP_LEVEL_ENTER MESSAGE");
@@ -1699,7 +1759,8 @@ motif_drag_status (GdkEvent *event,
   if (context)
     {
       GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
-      if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
+      if ((private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT) ||
+         (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT))
        private->drag_status = GDK_DRAG_STATUS_DRAG;
       
       event->dnd.type = GDK_DRAG_STATUS;
@@ -1741,10 +1802,8 @@ motif_dnd_filter (GdkXEvent *xev,
                  gpointer data)
 {
   XEvent *xevent = (XEvent *)xev;
-  XClientMessageEvent *xce = &xevent->xclient;
 
   guint8 reason;
-  gchar byte_order;
   guint16 flags;
   guint32 timestamp;
   guint32 source_window;
@@ -1754,25 +1813,24 @@ motif_dnd_filter (GdkXEvent *xev,
   
   /* First read some fields common to all Motif DND messages */
 
-  reason = xce->data.b[0];
-  byte_order = xce->data.b[1];
-  flags = card16_to_host (xce->data.s[1], byte_order);
-  timestamp = card32_to_host (xce->data.l[1], byte_order);
+  reason = MOTIF_UNPACK_BYTE (xevent, 0);
+  flags = MOTIF_UNPACK_SHORT (xevent, 1);
+  timestamp = MOTIF_UNPACK_LONG (xevent, 1);
 
   is_reply = ((reason & 0x80) != 0);
 
   switch (reason & 0x7f)
     {
     case XmTOP_LEVEL_ENTER:
-      source_window = card32_to_host (xce->data.l[2], byte_order);
-      atom = card32_to_host (xce->data.l[3], byte_order);
+      source_window = MOTIF_UNPACK_LONG (xevent, 2);
+      atom = MOTIF_UNPACK_LONG (xevent, 3);
       return motif_top_level_enter (event, flags, timestamp, source_window, atom);
     case XmTOP_LEVEL_LEAVE:
       return motif_top_level_leave (event, flags, timestamp);
 
     case XmDRAG_MOTION:
-      x_root = card16_to_host (xce->data.s[4], byte_order);
-      y_root = card16_to_host (xce->data.s[5], byte_order);
+      x_root = MOTIF_UNPACK_SHORT (xevent, 4);
+      y_root = MOTIF_UNPACK_SHORT (xevent, 5);
       
       if (!is_reply)
        return motif_motion (event, flags, timestamp, x_root, y_root);
@@ -1787,10 +1845,10 @@ motif_dnd_filter (GdkXEvent *xev,
                                XmNO_DROP_SITE << 8 | XmDROP_NOOP, 
                                timestamp);
     case XmDROP_START:
-      x_root = card16_to_host (xce->data.s[4], byte_order);
-      y_root = card16_to_host (xce->data.s[5], byte_order);
-      atom = card32_to_host (xce->data.l[3], byte_order);
-      source_window = card32_to_host (xce->data.l[4], byte_order);
+      x_root = MOTIF_UNPACK_SHORT (xevent, 4);
+      y_root = MOTIF_UNPACK_SHORT (xevent, 5);
+      atom = MOTIF_UNPACK_LONG (xevent, 3);
+      source_window = MOTIF_UNPACK_LONG (xevent, 4);
 
       if (!is_reply)
        return motif_drop_start (event, flags, timestamp, source_window, atom, x_root, y_root);
@@ -1953,16 +2011,16 @@ static void
 xdnd_set_targets (GdkDragContext *context)
 {
   GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
-  GdkAtom *typelist;
+  GdkAtom *atomlist;
   GList *tmp_list = context->targets;
   gint i;
-  gint n_targets = g_list_length (context->targets);
+  gint n_atoms = g_list_length (context->targets);
 
-  typelist = g_new (GdkAtom, n_targets);
+  atomlist = g_new (GdkAtom, n_atoms);
   i = 0;
   while (tmp_list)
     {
-      typelist[i] = GPOINTER_TO_INT (tmp_list->data);
+      atomlist[i] = GPOINTER_TO_INT (tmp_list->data);
       tmp_list = tmp_list->next;
       i++;
     }
@@ -1971,11 +2029,82 @@ xdnd_set_targets (GdkDragContext *context)
                   GDK_WINDOW_XWINDOW (context->source_window),
                   gdk_atom_intern ("XdndTypeList", FALSE),
                   XA_ATOM, 32, PropModeReplace,
-                  (guchar *)typelist, n_targets);
+                  (guchar *)atomlist, n_atoms);
+
+  g_free (atomlist);
 
   private->xdnd_targets_set = 1;
 }
 
+static void
+xdnd_set_actions (GdkDragContext *context)
+{
+  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
+  GdkAtom *atomlist;
+  gint i;
+  gint n_atoms;
+  guint actions;
+
+  if (!xdnd_actions_initialized)
+    xdnd_initialize_actions();
+  
+  actions = context->actions;
+  n_atoms = 0;
+  for (i=0; i<xdnd_n_actions; i++)
+    {
+      if (actions & xdnd_actions_table[i].action)
+       {
+         actions &= ~xdnd_actions_table[i].action;
+         n_atoms++;
+       }
+    }
+
+  atomlist = g_new (GdkAtom, n_atoms);
+
+  actions = context->actions;
+  n_atoms = 0;
+  for (i=0; i<xdnd_n_actions; i++)
+    {
+      if (actions & xdnd_actions_table[i].action)
+       {
+         actions &= ~xdnd_actions_table[i].action;
+         atomlist[n_atoms] = xdnd_actions_table[i].atom;
+         n_atoms++;
+       }
+    }
+
+  XChangeProperty (GDK_WINDOW_XDISPLAY (context->source_window),
+                  GDK_WINDOW_XWINDOW (context->source_window),
+                  gdk_atom_intern ("XdndActionList", FALSE),
+                  XA_ATOM, 32, PropModeReplace,
+                  (guchar *)atomlist, n_atoms);
+
+  g_free (atomlist);
+
+  private->xdnd_actions_set = 1;
+  private->xdnd_actions = context->actions;
+}
+
+/*************************************************************
+ * xdnd_send_xevent:
+ *     Like gdk_send_event, but if the target is the root
+ *     window, sets an event mask of ButtonPressMask, otherwise
+ *     an event mask of 0.
+ *   arguments:
+ *     
+ *   results:
+ *************************************************************/
+
+gint
+xdnd_send_xevent (Window window, gboolean propagate, 
+                 XEvent *event_send)
+{
+  if (window == gdk_root_window)
+    return gdk_send_xevent (window, propagate, ButtonPressMask, event_send);
+  else
+    return gdk_send_xevent (window, propagate, 0, event_send);
+}
+
 static void
 xdnd_send_enter (GdkDragContext *context)
 {
@@ -1985,7 +2114,9 @@ xdnd_send_enter (GdkDragContext *context)
   xev.xclient.type = ClientMessage;
   xev.xclient.message_type = gdk_atom_intern ("XdndEnter", FALSE);
   xev.xclient.format = 32;
-  xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
+  xev.xclient.window = private->drop_xid ? 
+                           private->drop_xid : 
+                           GDK_WINDOW_XWINDOW (context->dest_window);
   xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->source_window);
   xev.xclient.data.l[1] = (3 << 24); /* version */
   xev.xclient.data.l[2] = 0;
@@ -2014,8 +2145,8 @@ xdnd_send_enter (GdkDragContext *context)
        }
     }
 
-  if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
-                       FALSE, 0, &xev))
+  if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
+                        FALSE, &xev))
     {
       GDK_NOTE (DND, 
                g_message ("Send event to %lx failed",
@@ -2030,18 +2161,22 @@ xdnd_send_leave (GdkDragContext *context)
 {
   XEvent xev;
 
+  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
+
   xev.xclient.type = ClientMessage;
   xev.xclient.message_type = gdk_atom_intern ("XdndLeave", FALSE);
   xev.xclient.format = 32;
-  xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
+  xev.xclient.window = private->drop_xid ? 
+                           private->drop_xid : 
+                           GDK_WINDOW_XWINDOW (context->dest_window);
   xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->source_window);
   xev.xclient.data.l[1] = 0;
   xev.xclient.data.l[2] = 0;
   xev.xclient.data.l[3] = 0;
   xev.xclient.data.l[4] = 0;
 
-  if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
-                       FALSE, 0, &xev))
+  if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
+                        FALSE, &xev))
     {
       GDK_NOTE (DND, 
                g_message ("Send event to %lx failed",
@@ -2054,20 +2189,23 @@ xdnd_send_leave (GdkDragContext *context)
 static void
 xdnd_send_drop (GdkDragContext *context, guint32 time)
 {
+  GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
   XEvent xev;
 
   xev.xclient.type = ClientMessage;
   xev.xclient.message_type = gdk_atom_intern ("XdndDrop", FALSE);
   xev.xclient.format = 32;
-  xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
+  xev.xclient.window = private->drop_xid ? 
+                           private->drop_xid : 
+                           GDK_WINDOW_XWINDOW (context->dest_window);
   xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->source_window);
   xev.xclient.data.l[1] = 0;
   xev.xclient.data.l[2] = time;
   xev.xclient.data.l[3] = 0;
   xev.xclient.data.l[4] = 0;
 
-  if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
-                       FALSE, 0, &xev))
+  if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
+                        FALSE, &xev))
     {
       GDK_NOTE (DND, 
                g_message ("Send event to %lx failed",
@@ -2090,15 +2228,17 @@ xdnd_send_motion (GdkDragContext *context,
   xev.xclient.type = ClientMessage;
   xev.xclient.message_type = gdk_atom_intern ("XdndPosition", FALSE);
   xev.xclient.format = 32;
-  xev.xclient.window = GDK_WINDOW_XWINDOW (context->dest_window);
+  xev.xclient.window = private->drop_xid ? 
+                           private->drop_xid : 
+                           GDK_WINDOW_XWINDOW (context->dest_window);
   xev.xclient.data.l[0] = GDK_WINDOW_XWINDOW (context->source_window);
   xev.xclient.data.l[1] = 0;
   xev.xclient.data.l[2] = (x_root << 16) | y_root;
   xev.xclient.data.l[3] = time;
   xev.xclient.data.l[4] = xdnd_action_to_atom (action);
 
-  if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
-                       FALSE, 0, &xev))
+  if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
+                        FALSE, &xev))
     {
       GDK_NOTE (DND, 
                g_message ("Send event to %lx failed",
@@ -2185,15 +2325,147 @@ xdnd_check_dest (Window win)
 
 /* Target side */
 
+static void
+xdnd_read_actions (GdkDragContext *context)
+{
+  Atom type;
+  int format;
+  gulong nitems, after;
+  Atom *data;
+
+  gint i;
+
+  gint old_warnings = gdk_error_warnings;
+
+  gdk_error_code = 0;
+  gdk_error_warnings = 0;
+
+  /* Get the XdndActionList, if set */
+
+  XGetWindowProperty (GDK_WINDOW_XDISPLAY (context->source_window),
+                     GDK_WINDOW_XWINDOW (context->source_window),
+                     gdk_atom_intern ("XdndActionList", FALSE), 0, 65536,
+                     False, XA_ATOM, &type, &format, &nitems,
+                     &after, (guchar **)&data);
+  
+  if (!gdk_error_code && (format == 32) && (type == XA_ATOM))
+    {
+      context->actions = 0;
+
+      for (i=0; i<nitems; i++)
+       context->actions |= xdnd_action_from_atom (data[i]);
+
+      ((GdkDragContextPrivate *)context)->xdnd_have_actions = TRUE;
+
+#ifdef G_ENABLE_DEBUG
+      if (gdk_debug_flags & GDK_DEBUG_DND)
+       {
+         GString *action_str = g_string_new (NULL);
+         if (context->actions & GDK_ACTION_MOVE)
+           g_string_append(action_str, "MOVE ");
+         if (context->actions & GDK_ACTION_COPY)
+           g_string_append(action_str, "COPY ");
+         if (context->actions & GDK_ACTION_LINK)
+           g_string_append(action_str, "LINK ");
+         if (context->actions & GDK_ACTION_ASK)
+           g_string_append(action_str, "ASK ");
+         
+         g_message("Xdnd actions = %s", action_str->str);
+         g_string_free (action_str, TRUE);
+       }
+#endif /* G_ENABLE_DEBUG */
+
+      XFree(data);
+    }
+
+  gdk_error_warnings = old_warnings;
+  gdk_error_code = 0;
+}
+
+/* We have to make sure that the XdndActionList we keep internally
+ * is up to date with the XdndActionList on the source window
+ * because we get no notification, because Xdnd wasn't meant
+ * to continually send actions. So we select on PropertyChangeMask
+ * and add this filter.
+ */
+static GdkFilterReturn 
+xdnd_source_window_filter (GdkXEvent *xev,
+                          GdkEvent  *event,
+                          gpointer   cb_data)
+{
+  XEvent *xevent = (XEvent *)xev;
+  GdkDragContext *context = cb_data;
+
+  if ((xevent->xany.type == PropertyNotify) &&
+      (xevent->xproperty.atom == gdk_atom_intern ("XdndActionList", FALSE)))
+    {
+      xdnd_read_actions (context);
+
+      return GDK_FILTER_REMOVE;
+    }
+
+  return GDK_FILTER_CONTINUE;
+}
+
+static void
+xdnd_manage_source_filter (GdkDragContext *context,
+                          GdkWindow      *window,
+                          gboolean        add_filter)
+{
+  gint old_warnings = 0;       /* quiet gcc */
+  GdkWindowPrivate *private = (GdkWindowPrivate *)window;
+                              
+  gboolean is_foreign = (private->window_type == GDK_WINDOW_FOREIGN);
+
+  if (is_foreign)
+    {
+      old_warnings = gdk_error_warnings;
+      gdk_error_warnings = 0;
+    }
+
+  if (!private->destroyed)
+    {
+      if (add_filter)
+       {
+         gdk_window_set_events (window,
+                                gdk_window_get_events (window) |
+                                GDK_PROPERTY_CHANGE_MASK);
+         gdk_window_add_filter (window, xdnd_source_window_filter, context);
+
+       }
+      else
+       {
+         gdk_window_remove_filter (window,
+                                   xdnd_source_window_filter,
+                                   context);
+         /* Should we remove the GDK_PROPERTY_NOTIFY mask?
+          * but we might want it for other reasons. (Like
+          * INCR selection transactions).
+          */
+       }
+    }
+
+  if (is_foreign)
+    {
+      gdk_flush();
+      gdk_error_warnings = old_warnings;
+    }
+}
+
 static GdkFilterReturn 
 xdnd_enter_filter (GdkXEvent *xev,
                   GdkEvent  *event,
-                  gpointer   data)
+                  gpointer   cb_data)
 {
   XEvent *xevent = (XEvent *)xev;
   GdkDragContext *new_context;
   gint i;
   
+  Atom type;
+  int format;
+  gulong nitems, after;
+  Atom *data;
+
   guint32 source_window = xevent->xclient.data.l[0];
   gboolean get_types = ((xevent->xclient.data.l[1] & 1) != 0);
   gint version = (xevent->xclient.data.l[1] & 0xff000000) >> 24;
@@ -2237,11 +2509,6 @@ xdnd_enter_filter (GdkXEvent *xev,
   new_context->targets = NULL;
   if (get_types)
     {
-      Atom type;
-      int format;
-      gulong nitems, after;
-      Atom *data;
-
       XGetWindowProperty (GDK_WINDOW_XDISPLAY (event->any.window), 
                          source_window, 
                          gdk_atom_intern ("XdndTypeList", FALSE), 0, 65536,
@@ -2273,6 +2540,9 @@ xdnd_enter_filter (GdkXEvent *xev,
     print_target_list (new_context->targets);
 #endif /* G_ENABLE_DEBUG */
 
+  xdnd_manage_source_filter (new_context, new_context->source_window, TRUE);
+  xdnd_read_actions (new_context);
+
   event->dnd.type = GDK_DRAG_ENTER;
   event->dnd.context = new_context;
   gdk_drag_context_ref (new_context);
@@ -2340,6 +2610,8 @@ xdnd_position_filter (GdkXEvent *xev,
       event->dnd.time = time;
 
       current_dest_drag->suggested_action = xdnd_action_from_atom (action);
+      if (!((GdkDragContextPrivate *)current_dest_drag)->xdnd_have_actions)
+       current_dest_drag->actions = current_dest_drag->suggested_action;
 
       event->dnd.x_root = x_root;
       event->dnd.y_root = y_root;
@@ -2436,6 +2708,7 @@ gdk_drag_do_leave (GdkDragContext *context, guint32 time)
          xdnd_send_leave (context);
          break;
        case GDK_DRAG_PROTO_ROOTWIN:
+       case GDK_DRAG_PROTO_NONE:
          break;
        }
 
@@ -2446,8 +2719,7 @@ gdk_drag_do_leave (GdkDragContext *context, guint32 time)
 
 GdkDragContext * 
 gdk_drag_begin (GdkWindow     *window,
-               GList         *targets,
-               GdkDragAction  actions)
+               GList         *targets)
 {
   GList *tmp_list;
   GdkDragContext *new_context;
@@ -2468,7 +2740,7 @@ gdk_drag_begin (GdkWindow     *window,
       tmp_list = tmp_list->prev;
     }
 
-  new_context->actions = actions;
+  new_context->actions = 0;
 
   return new_context;
 }
@@ -2579,6 +2851,12 @@ gdk_drag_find_window (GdkDragContext  *context,
 
       /* Check if new destination accepts drags, and which protocol */
 
+      /* There is some ugliness here. We actually need to pass
+       * _three_ pieces of information to drag_motion - dest_window,
+       * protocol, and the XID of the unproxied window. The first
+       * two are passed explicitely, the third implicitly through
+       * protocol->dest_xid.
+       */
       if ((recipient = gdk_drag_get_protocol (dest, protocol)))
        {
          *dest_window = gdk_window_lookup (recipient);
@@ -2605,13 +2883,25 @@ gdk_drag_motion (GdkDragContext *context,
                 GdkDragProtocol protocol,
                 gint            x_root, 
                 gint            y_root,
-                GdkDragAction   action,
+                GdkDragAction   suggested_action,
+                GdkDragAction   possible_actions,
                 guint32         time)
 {
   GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
 
   g_return_val_if_fail (context != NULL, FALSE);
 
+  /* When we have a Xdnd target, make sure our XdndActionList
+   * matches the current actions;
+   */
+  private->old_actions = context->actions;
+  context->actions = possible_actions;
+  
+  if ((protocol == GDK_DRAG_PROTO_XDND) &&
+      (!private->xdnd_actions_set ||
+       private->xdnd_actions != possible_actions))
+    xdnd_set_actions (context);
+
   if (context->dest_window != dest_window)
     {
       GdkEvent temp_event;
@@ -2625,6 +2915,7 @@ gdk_drag_motion (GdkDragContext *context,
       if (dest_window)
        {
          context->dest_window = dest_window;
+         private->drop_xid = private->dest_xid;
          gdk_window_ref (context->dest_window);
          context->protocol = protocol;
 
@@ -2639,14 +2930,17 @@ gdk_drag_motion (GdkDragContext *context,
              break;
 
            case GDK_DRAG_PROTO_ROOTWIN:
+           case GDK_DRAG_PROTO_NONE:
              break;
            }
-         private->old_action = action;
-         context->suggested_action = action;
+         private->old_action = suggested_action;
+         context->suggested_action = suggested_action;
+         private->old_actions = possible_actions;
        }
       else
        {
          context->dest_window = NULL;
+         private->drop_xid = None;
          context->action = 0;
        }
 
@@ -2669,7 +2963,7 @@ gdk_drag_motion (GdkDragContext *context,
   else
     {
       private->old_action = context->suggested_action;
-      context->suggested_action = action;
+      context->suggested_action = suggested_action;
     }
 
   /* Send a drag-motion event */
@@ -2684,11 +2978,11 @@ gdk_drag_motion (GdkDragContext *context,
          switch (context->protocol)
            {
            case GDK_DRAG_PROTO_MOTIF:
-             motif_send_motion (context, x_root, y_root, action, time);
+             motif_send_motion (context, x_root, y_root, suggested_action, time);
              break;
              
            case GDK_DRAG_PROTO_XDND:
-             xdnd_send_motion (context, x_root, y_root, action, time);
+             xdnd_send_motion (context, x_root, y_root, suggested_action, time);
              break;
 
            case GDK_DRAG_PROTO_ROOTWIN:
@@ -2710,6 +3004,9 @@ gdk_drag_motion (GdkDragContext *context,
                gdk_event_put (&temp_event);
              }
              break;
+           case GDK_DRAG_PROTO_NONE:
+             g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_motion()");
+             break;
            }
        }
       else
@@ -2741,6 +3038,9 @@ gdk_drag_drop (GdkDragContext *context,
        case GDK_DRAG_PROTO_ROOTWIN:
          g_warning ("Drops for GDK_DRAG_PROTO_ROOTWIN must be handled internally");
          break;
+       case GDK_DRAG_PROTO_NONE:
+         g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_drag_drop()");
+         break;
        }
     }
 }
@@ -2779,47 +3079,47 @@ gdk_drag_status (GdkDragContext   *context,
 
       if (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT)
        {
-         xev.xclient.data.b[0] = XmOPERATION_CHANGED | 0x80;
+         MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED | 0x80;
        }
       else
        {
          if ((action != 0) != (private->old_action != 0))
            {
              if (action != 0)
-               xev.xclient.data.b[0] = XmDROP_SITE_ENTER | 0x80;
+               MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_ENTER | 0x80;
              else
-               xev.xclient.data.b[0] = XmDROP_SITE_LEAVE | 0x80;
+               MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_SITE_LEAVE | 0x80;
            }
          else
-           xev.xclient.data.b[0] = XmDRAG_MOTION | 0x80;
+           MOTIF_XCLIENT_BYTE (&xev, 0) = XmDRAG_MOTION | 0x80;
        }
 
-      xev.xclient.data.b[1] = local_byte_order;
+      MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
 
       switch (action)
        {
        case GDK_ACTION_MOVE:
-         xev.xclient.data.s[1] = XmDROP_MOVE;
+         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_MOVE;
          break;
        case GDK_ACTION_COPY:
-         xev.xclient.data.s[1] = XmDROP_COPY;
+         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY;
          break;
        case GDK_ACTION_LINK:
-         xev.xclient.data.s[1] = XmDROP_LINK;
+         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_LINK;
          break;
        default:
-         xev.xclient.data.s[1] = XmDROP_NOOP;
+         MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP;
          break;
        }
 
       if (action)
-       xev.xclient.data.s[1] |= (XmDROP_SITE_VALID << 4);
+       MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmDROP_SITE_VALID << 4);
       else
-       xev.xclient.data.s[1] |= (XmNO_DROP_SITE << 4);
+       MOTIF_XCLIENT_SHORT (&xev, 1) |= (XmNO_DROP_SITE << 4);
 
-      xev.xclient.data.l[1] = time;
-      xev.xclient.data.s[4] = private->last_x;
-      xev.xclient.data.s[5] = private->last_y;
+      MOTIF_XCLIENT_LONG (&xev, 1) = time;
+      MOTIF_XCLIENT_SHORT (&xev, 4) = private->last_x;
+      MOTIF_XCLIENT_SHORT (&xev, 5) = private->last_y;
 
       if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
                       FALSE, 0, &xev))
@@ -2840,8 +3140,8 @@ gdk_drag_status (GdkDragContext   *context,
       xev.xclient.data.l[3] = 0;
       xev.xclient.data.l[4] = xdnd_action_to_atom (action);
 
-      if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
-                      FALSE, 0, &xev))
+      if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
+                            FALSE, &xev))
        GDK_NOTE (DND, 
                  g_message ("Send event to %lx failed",
                             GDK_WINDOW_XWINDOW (context->source_window)));
@@ -2869,20 +3169,20 @@ gdk_drop_reply (GdkDragContext   *context,
       xev.xclient.message_type = gdk_atom_intern ("_MOTIF_DRAG_AND_DROP_MESSAGE", FALSE);
       xev.xclient.format = 8;
 
-      xev.xclient.data.b[0] = XmDROP_START | 0x80;
-      xev.xclient.data.b[1] = local_byte_order;
+      MOTIF_XCLIENT_BYTE (&xev, 0) = XmDROP_START | 0x80;
+      MOTIF_XCLIENT_BYTE (&xev, 1) = local_byte_order;
       if (ok)
-       xev.xclient.data.s[1] = XmDROP_COPY | 
-                               (XmDROP_SITE_VALID << 4) |
-                               (XmDROP_NOOP << 8) |
-                               (XmDROP << 12);
+       MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_COPY | 
+                                      (XmDROP_SITE_VALID << 4) |
+                                      (XmDROP_NOOP << 8) |
+                                      (XmDROP << 12);
       else
-       xev.xclient.data.s[1] = XmDROP_NOOP | 
-                               (XmNO_DROP_SITE << 4) |
-                               (XmDROP_NOOP << 8) |
-                               (XmDROP_CANCEL << 12);
-      xev.xclient.data.s[2] = private->last_x;
-      xev.xclient.data.s[3] = private->last_y;
+       MOTIF_XCLIENT_SHORT (&xev, 1) = XmDROP_NOOP | 
+                                      (XmNO_DROP_SITE << 4) |
+                                      (XmDROP_NOOP << 8) |
+                                      (XmDROP_CANCEL << 12);
+      MOTIF_XCLIENT_SHORT (&xev, 2) = private->last_x;
+      MOTIF_XCLIENT_SHORT (&xev, 3) = private->last_y;
       
       gdk_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
                       FALSE, 0, &xev);
@@ -2911,8 +3211,8 @@ gdk_drop_finish (GdkDragContext   *context,
       xev.xclient.data.l[3] = 0;
       xev.xclient.data.l[4] = 0;
 
-      if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
-                      FALSE, 0, &xev))
+      if (!xdnd_send_xevent (GDK_WINDOW_XWINDOW (context->source_window),
+                            FALSE, &xev))
        GDK_NOTE (DND, 
                  g_message ("Send event to %lx failed",
                             GDK_WINDOW_XWINDOW (context->source_window)));
@@ -2923,7 +3223,7 @@ gdk_drop_finish (GdkDragContext   *context,
 void            
 gdk_window_register_dnd (GdkWindow      *window)
 {
-  static guint32 xdnd_version = 3;
+  static gulong xdnd_version = 3;
   MotifDragReceiverInfo info;
 
   g_return_if_fail (window != NULL);