[gtk/wip/otte/dnd] GDK W32: Adapt to GdkDrop and GdkDragContext changes



commit 2517520d981332bc739e1b34891faa427f22525e
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Tue Jun 5 23:03:51 2018 +0000

    GDK W32: Adapt to GdkDrop and GdkDragContext changes
    
    * Remove clipdrop->dnd_target_state, it's not used anymore
    * Remove non-functioning _gdk_dropfiles_store(), store dropfiles
      list in GdkWin32Drop instead
    * Fix multiple comment typos
    * Fix _gdk_win32_get_clipboard_format_name_as_interned_mimetype() to
      leave names that look like mime/types alone
    * Refactor _gdk_win32_add_w32format_to_pairs() to populate
      GdkContentFormatsBuilder directly, instead of making a GList
    * Rename context -> drag (still using GdkDragContext type,
      but [almost?] all variables and comments say "drag" now)
    * Rename GdkDropContext -> GdkDrop
    * Rename some parameter names for clarity
    * Rewrite local protocol to look more like OLE2 protocol
      instead of mirroring the structure of the X11 API.
    * Add handle_events field to GdkWin32DragContext,
      to shut off event handling (temporary fix until GTK is patched up)
    * Remove _gdk_win32_drag_context_find() - the drag object is stored
      in GdkDrop instead. Use _gdk_win32_find_drag_for_dest_surface()
      to get it initially.
    * Remove target_ctx_for_window, droptarget context is stored
      in the surface instead.
    * Call gdk_drag_context_set_cursor() just like wayland backend does
      (slightly broken for now)
    * Clean up the action choosing code (filter source actions by using
      keyboard state, pass that to GTK, get all actions supported by GTK in
      response, match them up with filtered source actions, return the
      result, falling back to COPY in case of multiple actions)
    * Check drag_win32->protocol instead of the use_ole2_dnd variable where
      possible
    * Remove protocol checks from functions that are only used by the local
      protocol
    * Use event state to manufacture the keyboard state for WM_MOUSEMOVE
    * Change function names printed by GDK_NOTE to name the actual
      functions, not their theoretical generic GDK stack ancestors
    * Consistently use drag_win32 and drop_win32 variables instead of a mix
      of that and win32_drag/win32_drop
    * Return FALSE from button handler to ensure that GTK gets the button
      event to break implicit grab
    * Emit leave event on failed idroptarget_drop() calls

 gdk/win32/gdkclipdrop-win32.c   |   85 ++-
 gdk/win32/gdkclipdrop-win32.h   |   80 ++-
 gdk/win32/gdkdrag-win32.c       | 1123 ++++++++++++++++-----------------------
 gdk/win32/gdkdrop-win32.c       | 1122 ++++++++++++++++++++++----------------
 gdk/win32/gdkprivate-win32.h    |    4 +-
 gdk/win32/gdksurface-win32.c    |    3 +
 gdk/win32/gdksurface-win32.h    |   12 +
 gdk/win32/gdkwin32dnd-private.h |   95 ++--
 gdk/win32/gdkwin32dnd.h         |   17 -
 9 files changed, 1274 insertions(+), 1267 deletions(-)
---
diff --git a/gdk/win32/gdkclipdrop-win32.c b/gdk/win32/gdkclipdrop-win32.c
index 6774d6cb3b..d8118d7f58 100644
--- a/gdk/win32/gdkclipdrop-win32.c
+++ b/gdk/win32/gdkclipdrop-win32.c
@@ -1541,8 +1541,6 @@ gdk_win32_clipdrop_init (GdkWin32Clipdrop *win32_clipdrop)
   win32_clipdrop->GetUpdatedClipboardFormats = GetProcAddress (user32, "GetUpdatedClipboardFormats");
   FreeLibrary (user32);
 
-  win32_clipdrop->dnd_target_state = GDK_WIN32_DND_NONE;
-
   atoms = g_array_sized_new (FALSE, TRUE, sizeof (GdkAtom), GDK_WIN32_ATOM_INDEX_LAST);
   g_array_set_size (atoms, GDK_WIN32_ATOM_INDEX_LAST);
   cfs = g_array_sized_new (FALSE, TRUE, sizeof (UINT), GDK_WIN32_CF_INDEX_LAST);
@@ -1876,34 +1874,6 @@ gdk_win32_clipdrop_init (GdkWin32Clipdrop *win32_clipdrop)
   win32_clipdrop->dnd_thread_id = GPOINTER_TO_UINT (g_async_queue_pop (win32_clipdrop->dnd_queue));
 }
 
-void
-_gdk_dropfiles_store (gchar *data)
-{
-  GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
-
-/* FIXME: REMOVE ALL THAT STUFF
-  if (data != NULL)
-    {
-      g_assert (clipdrop->dropfiles_prop == NULL);
-
-      clipdrop->dropfiles_prop = g_new (GdkSelProp, 1);
-      clipdrop->dropfiles_prop->data = (guchar *) data;
-      clipdrop->dropfiles_prop->length = strlen (data) + 1;
-      clipdrop->dropfiles_prop->bitness = 8;
-      clipdrop->dropfiles_prop->target = _gdk_win32_clipdrop_atom (GDK_WIN32_ATOM_INDEX_TEXT_URI_LIST);
-    }
-  else
-    {
-      if (clipdrop->dropfiles_prop != NULL)
-       {
-         g_free (clipdrop->dropfiles_prop->data);
-         g_free (clipdrop->dropfiles_prop);
-       }
-      clipdrop->dropfiles_prop = NULL;
-    }
-*/
-}
-
 #define CLIPBOARD_IDLE_ABORT_TIME 30
 
 static const gchar *
@@ -1970,7 +1940,7 @@ _gdk_win32_get_clipboard_format_name (UINT      fmt,
         }
 
       /* If GetClipboardFormatNameW() used up all the space, it means that
-       * we probably need a bigger buffer, but cap this at 1 kilobyte.
+       * we probably need a bigger buffer, but cap this at 1 megabyte.
        */
       if (gcfn_result == 0 || registered_name_w_len > 1024 * 1024)
         {
@@ -1997,12 +1967,22 @@ _gdk_win32_get_clipboard_format_name (UINT      fmt,
 /* This turns an arbitrary string into a string that
  * *looks* like a mime/type, such as:
  * "application/x.windows.FOO_BAR" from "FOO_BAR".
+ * Does nothing for strings that already look like a mime/type
+ * (no spaces, one slash, with at least one char on each side of the slash).
  */
 const gchar *
 _gdk_win32_get_clipboard_format_name_as_interned_mimetype (gchar *w32format_name)
 {
   gchar *mime_type;
   const gchar *result;
+  gchar *space = strchr (w32format_name, ' ');
+  gchar *slash = strchr (w32format_name, '/');
+
+  if (space == NULL &&
+      slash > w32format_name &&
+      slash[1] != '\0' &&
+      strchr (&slash[1], '/') == NULL)
+    return g_intern_string (w32format_name);
 
   mime_type = g_strdup_printf ("application/x.windows.%s", w32format_name);
   result = g_intern_string (mime_type);
@@ -2061,15 +2041,16 @@ _gdk_win32_get_compatibility_contentformats_for_w32format (UINT w32format)
 
 /* Turn W32 format into a GDK content format and add it
  * to the array of W32 format <-> GDK content format pairs
- * and/or to a list of GDK content formats.
+ * and/or to a GDK contentformat builder.
  * Also add compatibility GDK content formats for that W32 format.
  * The added content format string is always interned.
- * Ensures that duplicates are not added.
+ * Ensures that duplicates are not added to the pairs array
+ * (builder already takes care of that for itself).
  */
 void
-_gdk_win32_add_w32format_to_pairs (UINT     w32format,
-                                   GArray  *array,
-                                   GList  **list)
+_gdk_win32_add_w32format_to_pairs (UINT                      w32format,
+                                   GArray                   *pairs,
+                                   GdkContentFormatsBuilder *builder)
 {
   gboolean predef;
   gchar *w32format_name = _gdk_win32_get_clipboard_format_name (w32format, &predef);
@@ -2084,47 +2065,45 @@ _gdk_win32_add_w32format_to_pairs (UINT     w32format,
       GDK_NOTE (DND, g_print ("Maybe add as-is format %s (%s) (0x%p)\n", w32format_name, 
interned_w32format_name, interned_w32format_name));
       g_free (w32format_name);
 
-      if (array && interned_w32format_name != 0)
+      if (pairs && interned_w32format_name != 0)
         {
-          for (j = 0; j < array->len; j++)
-            if (g_array_index (array, GdkWin32ContentFormatPair, j).contentformat == interned_w32format_name)
+          for (j = 0; j < pairs->len; j++)
+            if (g_array_index (pairs, GdkWin32ContentFormatPair, j).contentformat == interned_w32format_name)
               break;
-          if (j == array->len)
+          if (j == pairs->len)
             {
               pair.w32format = w32format;
               pair.contentformat = interned_w32format_name;
               pair.transmute = FALSE;
-              g_array_append_val (array, pair);
+              g_array_append_val (pairs, pair);
             }
         }
-
-      if (list && interned_w32format_name != 0 && g_list_find (*list, interned_w32format_name) == NULL)
-        *list = g_list_prepend (*list, interned_w32format_name);
+      if (builder != NULL && interned_w32format_name != 0)
+        gdk_content_formats_builder_add_mime_type (builder, interned_w32format_name);
     }
 
   comp_pairs = _gdk_win32_get_compatibility_contentformats_for_w32format (w32format);
 
- if (array && comp_pairs != NULL)
+ if (pairs != NULL && comp_pairs != NULL)
    for (i = 0; i < comp_pairs->len; i++)
      {
        pair = g_array_index (comp_pairs, GdkWin32ContentFormatPair, i);
 
-       for (j = 0; j < array->len; j++)
-         if (g_array_index (array, GdkWin32ContentFormatPair, j).contentformat == pair.contentformat &&
-             g_array_index (array, GdkWin32ContentFormatPair, j).w32format == pair.w32format)
+       for (j = 0; j < pairs->len; j++)
+         if (g_array_index (pairs, GdkWin32ContentFormatPair, j).contentformat == pair.contentformat &&
+             g_array_index (pairs, GdkWin32ContentFormatPair, j).w32format == pair.w32format)
            break;
 
-       if (j == array->len)
-         g_array_append_val (array, pair);
+       if (j == pairs->len)
+         g_array_append_val (pairs, pair);
      }
 
- if (list && comp_pairs != NULL)
+ if (builder != NULL && comp_pairs != NULL)
    for (i = 0; i < comp_pairs->len; i++)
      {
        pair = g_array_index (comp_pairs, GdkWin32ContentFormatPair, i);
 
-       if (g_list_find (*list, pair.contentformat) == NULL)
-         *list = g_list_prepend (*list, pair.contentformat);
+       gdk_content_formats_builder_add_mime_type (builder, pair.contentformat);
      }
 }
 
diff --git a/gdk/win32/gdkclipdrop-win32.h b/gdk/win32/gdkclipdrop-win32.h
index 570f407dc4..b7e7b2422f 100644
--- a/gdk/win32/gdkclipdrop-win32.h
+++ b/gdk/win32/gdkclipdrop-win32.h
@@ -142,8 +142,6 @@ struct _GdkWin32Clipdrop
    */
   GArray *known_clipboard_formats;
 
-  GdkWin32DndState  dnd_target_state;
-
   /* A target-keyed hash table of GArrays of GdkWin32ContentFormatPairs describing compatibility w32formats 
for a contentformat */
   GHashTable       *compatibility_w32formats;
   /* A format-keyed hash table of GArrays of GdkAtoms describing compatibility contentformats for a 
w32format */
@@ -213,12 +211,12 @@ struct _GdkWin32Clipdrop
   /* We don't actually support multiple simultaneous drags,
    * for obvious reasons (though this might change with
    * the advent of multitouch support?), but there may be
-   * circumstances where we have two drag contexts at
+   * circumstances where we have two drag objects at
    * the same time (one of them will grab the cursor
    * and thus cancel the other drag operation, but
-   * there will be a point of time when both contexts
+   * there will be a point of time when both objects
    * are out there). Thus we keep them around in this hash table.
-   * Key is the context object (which is safe, because the main
+   * Key is the GdkDragContext object (which is safe, because the main
    * thread keeps a reference on each one of those), value
    * is a pointer to a GdkWin32DnDThreadDoDragDrop struct,
    * which we can only examine when we're sure that the
@@ -236,42 +234,42 @@ GType    gdk_win32_clipdrop_get_type                               (void) G_GNUC
 
 void     _gdk_win32_clipdrop_init                                  (void);
 
-gboolean _gdk_win32_format_uses_hdata                              (UINT               w32format);
-
-gchar  * _gdk_win32_get_clipboard_format_name                      (UINT               fmt,
-                                                                    gboolean          *is_predefined);
-void     _gdk_win32_add_w32format_to_pairs                         (UINT               format,
-                                                                    GArray            *array,
-                                                                    GList            **list);
-gint     _gdk_win32_add_contentformat_to_pairs                     (GdkAtom            target,
-                                                                    GArray            *array);
-
-void     _gdk_win32_clipboard_default_output_done                  (GObject           *clipboard,
-                                                                    GAsyncResult      *result,
-                                                                    gpointer           user_data);
-gboolean _gdk_win32_transmute_contentformat                        (const gchar       *from_contentformat,
-                                                                    UINT               to_w32format,
-                                                                    const guchar      *data,
-                                                                    gint               length,
-                                                                    guchar           **set_data,
-                                                                    gint              *set_data_length);
-
-gboolean _gdk_win32_transmute_windows_data                         (UINT          from_w32format,
-                                                                    const gchar  *to_contentformat,
-                                                                    HANDLE        hdata,
-                                                                    guchar      **set_data,
-                                                                    gsize        *set_data_length);
-
-
-gboolean _gdk_win32_store_clipboard_contentformats                 (GdkClipboard      *cb,
-                                                                    GTask             *task,
-                                                                    GdkContentFormats *contentformats);
-
-void     _gdk_win32_retrieve_clipboard_contentformats              (GTask             *task,
-                                                                    GdkContentFormats *contentformats);
-
-void     _gdk_win32_advertise_clipboard_contentformats             (GTask             *task,
-                                                                    GdkContentFormats *contentformats);
+gboolean _gdk_win32_format_uses_hdata                              (UINT                      w32format);
+
+gchar  * _gdk_win32_get_clipboard_format_name                      (UINT                      w32format,
+                                                                    gboolean                 *is_predefined);
+void     _gdk_win32_add_w32format_to_pairs                         (UINT                      w32format,
+                                                                    GArray                   *pairs,
+                                                                    GdkContentFormatsBuilder *builder);
+gint     _gdk_win32_add_contentformat_to_pairs                     (GdkAtom                   target,
+                                                                    GArray                   *array);
+
+void     _gdk_win32_clipboard_default_output_done                  (GObject                  *clipboard,
+                                                                    GAsyncResult             *result,
+                                                                    gpointer                  user_data);
+gboolean _gdk_win32_transmute_contentformat                        (const gchar              
*from_contentformat,
+                                                                    UINT                      to_w32format,
+                                                                    const guchar             *data,
+                                                                    gint                      length,
+                                                                    guchar                  **set_data,
+                                                                    gint                     
*set_data_length);
+
+gboolean _gdk_win32_transmute_windows_data                         (UINT                      from_w32format,
+                                                                    const gchar              
*to_contentformat,
+                                                                    HANDLE                    hdata,
+                                                                    guchar                  **set_data,
+                                                                    gsize                    
*set_data_length);
+
+
+gboolean _gdk_win32_store_clipboard_contentformats                 (GdkClipboard             *cb,
+                                                                    GTask                    *task,
+                                                                    GdkContentFormats        
*contentformats);
+
+void     _gdk_win32_retrieve_clipboard_contentformats              (GTask                    *task,
+                                                                    GdkContentFormats        
*contentformats);
+
+void     _gdk_win32_advertise_clipboard_contentformats             (GTask                    *task,
+                                                                    GdkContentFormats        
*contentformats);
 
 
 
diff --git a/gdk/win32/gdkdrag-win32.c b/gdk/win32/gdkdrag-win32.c
index 3a24d72d89..53cd19731c 100644
--- a/gdk/win32/gdkdrag-win32.c
+++ b/gdk/win32/gdkdrag-win32.c
@@ -37,7 +37,7 @@
  *
  * Notes on the implementation:
  *
- * Source drag context, IDragSource and IDataObject for it are created
+ * Source drag context, IDropSource and IDataObject for it are created
  * (almost) simultaneously, whereas target drag context and IDropTarget
  * are separated in time - IDropTarget is created when a window is made
  * to accept drops, while target drag context is created when a dragging
@@ -49,7 +49,7 @@
  * To account for it the data is transmuted back and forth. There are two
  * main points of transmutation:
  * * GdkWin32HDATAOutputStream: transmutes GTK+ data to W32 data
- * * GdkWin32DropContext: transmutes W32 data to GTK+ data
+ * * GdkWin32Drop: transmutes W32 data to GTK+ data
  *
  * There are also two points where data formats are considered:
  * * When source drag context is created, it gets a list of GDK contentformats
@@ -226,7 +226,7 @@ typedef struct
   IDropSource                     ids;
   IDropSourceNotify               idsn;
   gint                            ref_count;
-  GdkDragContext                 *context;
+  GdkDragContext                 *drag;
 
   /* These are thread-local
    * copies of the similar fields from GdkWin32DragContext
@@ -248,7 +248,7 @@ typedef struct
 typedef struct {
   IDataObject                     ido;
   int                             ref_count;
-  GdkDragContext                 *context;
+  GdkDragContext                 *drag;
   GArray                         *formats;
 } data_object;
 
@@ -400,6 +400,7 @@ free_queue_item (GdkWin32DnDThreadQueueItem *item)
     case GDK_WIN32_DND_THREAD_QUEUE_ITEM_UPDATE_DRAG_STATE:
     case GDK_WIN32_DND_THREAD_QUEUE_ITEM_GIVE_FEEDBACK:
     case GDK_WIN32_DND_THREAD_QUEUE_ITEM_DRAG_INFO:
+      /* These have no data to clean up */
       break;
     case GDK_WIN32_DND_THREAD_QUEUE_ITEM_GET_DATA:
       getdata = (GdkWin32DnDThreadGetData *) item;
@@ -482,18 +483,27 @@ process_dnd_queue (gboolean                   timed,
   return FALSE;
 }
 
+void
+_gdk_win32_local_drag_context_drop_response (GdkDragContext *drag,
+                                             GdkDragAction   action)
+{
+  GDK_NOTE (DND, g_print ("_gdk_win32_local_drag_context_drop_response: 0x%p\n",
+                          drag));
+
+  g_signal_emit_by_name (drag, "dnd-finished");
+  gdk_drag_drop_done (drag, action != 0);
+}
+
 static gboolean
 do_drag_drop_response (gpointer user_data)
 {
   GdkWin32DnDThreadDoDragDrop *ddd = (GdkWin32DnDThreadDoDragDrop *) user_data;
   HRESULT hr = ddd->received_result;
-  GdkDragContext *context = GDK_DRAG_CONTEXT (ddd->base.opaque_context);
+  GdkDragContext *drag = GDK_DRAG_CONTEXT (ddd->base.opaque_context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
-  gpointer table_value = g_hash_table_lookup (clipdrop->active_source_drags, context);
+  gpointer table_value = g_hash_table_lookup (clipdrop->active_source_drags, drag);
 
-  /* This just verifies that we got the right context,
-   * we don't need the ddd struct itself.
-   */
   if (ddd == table_value)
     {
       GDK_NOTE (DND, g_print ("DoDragDrop returned %s with effect %lu\n",
@@ -502,46 +512,46 @@ do_drag_drop_response (gpointer user_data)
                                 (hr == E_UNEXPECTED ? "E_UNEXPECTED" :
                                  g_strdup_printf ("%#.8lx", hr)))), ddd->received_drop_effect));
 
-      GDK_WIN32_DRAG_CONTEXT (context)->drop_failed = !(SUCCEEDED (hr) || hr == DRAGDROP_S_DROP);
+      drag_win32->drop_failed = !(SUCCEEDED (hr) || hr == DRAGDROP_S_DROP);
 
       /* We used to delete the selection here,
        * now GTK does that automatically in response to 
        * the "dnd-finished" signal,
        * if the operation was successful and was a move.
        */
-      GDK_NOTE (DND, g_print ("gdk_dnd_handle_drop_finihsed: 0x%p\n",
-                              context));
+      GDK_NOTE (DND, g_print ("gdk_dnd_handle_drop_finished: 0x%p\n",
+                              drag));
 
-      g_signal_emit_by_name (context, "dnd-finished");
-      gdk_drag_drop_done (context, !(GDK_WIN32_DRAG_CONTEXT (context))->drop_failed);
+      g_signal_emit_by_name (drag, "dnd-finished");
+      gdk_drag_drop_done (drag, !drag_win32->drop_failed);
     }
   else
     {
       if (!table_value)
-        g_critical ("Did not find context 0x%p in the active contexts table", context);
+        g_critical ("Did not find drag 0x%p in the active drags table", drag);
       else
-        g_critical ("Found context 0x%p in the active contexts table, but the record doesn't match (0x%p != 
0x%p)", context, ddd, table_value);
+        g_critical ("Found drag 0x%p in the active drags table, but the record doesn't match (0x%p != 
0x%p)", drag, ddd, table_value);
     }
 
   /* 3rd parties could keep a reference to this object,
-   * but we won't keep the context alive that long.
+   * but we won't keep the drag alive that long.
    * Neutralize it (attempts to get its data will fail)
-   * by nullifying the context pointer (it doesn't hold
+   * by nullifying the drag pointer (it doesn't hold
    * a reference, so no unreffing).
    */
-  ddd->src_object->context = NULL;
+  g_clear_object (&ddd->src_object->drag);
 
   IDropSource_Release (&ddd->src_context->ids);
   IDataObject_Release (&ddd->src_object->ido);
 
-  g_hash_table_remove (clipdrop->active_source_drags, context);
+  g_hash_table_remove (clipdrop->active_source_drags, drag);
   free_queue_item (&ddd->base);
 
   return G_SOURCE_REMOVE;
 }
 
 static void
-received_drag_context_data (GObject      *context,
+received_drag_context_data (GObject      *drag,
                             GAsyncResult *result,
                             gpointer      user_data)
 {
@@ -549,12 +559,12 @@ received_drag_context_data (GObject      *context,
   GdkWin32DnDThreadGetData *getdata = (GdkWin32DnDThreadGetData *) user_data;
   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
 
-  if (!gdk_drag_context_write_finish (GDK_DRAG_CONTEXT (context), result, &error))
+  if (!gdk_drag_context_write_finish (GDK_DRAG_CONTEXT (drag), result, &error))
     {
       HANDLE handle;
       gboolean is_hdata;
 
-      GDK_NOTE (DND, g_printerr ("%p: failed to write HData-backed stream: %s\n", context, error->message));
+      GDK_NOTE (DND, g_printerr ("%p: failed to write HData-backed stream: %s\n", drag, error->message));
       g_error_free (error);
       g_output_stream_close (G_OUTPUT_STREAM (getdata->stream), NULL, NULL);
       handle = gdk_win32_hdata_output_stream_get_handle (getdata->stream, &is_hdata);
@@ -582,13 +592,13 @@ get_data_response (gpointer user_data)
 {
   GdkWin32DnDThreadGetData *getdata = (GdkWin32DnDThreadGetData *) user_data;
   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
-  GdkDragContext *context = GDK_DRAG_CONTEXT (getdata->base.opaque_context);
-  gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, context);
+  GdkDragContext *drag = GDK_DRAG_CONTEXT (getdata->base.opaque_context);
+  gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
 
   GDK_NOTE (DND, g_print ("idataobject_getdata will request target 0x%p (%s)",
                           getdata->pair.contentformat, getdata->pair.contentformat));
 
-  /* This just verifies that we got the right context,
+  /* This just verifies that we got the right drag,
    * we don't need the ddd struct itself.
    */
   if (ddd)
@@ -599,7 +609,7 @@ get_data_response (gpointer user_data)
       if (stream)
         {
           getdata->stream = GDK_WIN32_HDATA_OUTPUT_STREAM (stream);
-          gdk_drag_context_write_async (context,
+          gdk_drag_context_write_async (drag,
                                         getdata->pair.contentformat,
                                         stream,
                                         G_PRIORITY_DEFAULT,
@@ -708,75 +718,60 @@ typedef enum {
   GDK_DRAG_STATUS_DROP
 } GdkDragStatus;
 
-static GList *local_source_contexts;
-static GdkDragContext *current_dest_drag = NULL;
-
 static gboolean use_ole2_dnd = TRUE;
 
-static gboolean drag_context_grab (GdkDragContext *context);
+static gboolean drag_context_grab (GdkDragContext *drag);
 
 G_DEFINE_TYPE (GdkWin32DragContext, gdk_win32_drag_context, GDK_TYPE_DRAG_CONTEXT)
 
 static void
-move_drag_surface (GdkDragContext *context,
+move_drag_surface (GdkDragContext *drag,
                    guint           x_root,
                    guint           y_root)
 {
-  GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
   g_assert (_win32_main_thread == NULL ||
             _win32_main_thread == g_thread_self ());
 
-  gdk_surface_move (context_win32->drag_surface,
-                    x_root - context_win32->hot_x,
-                    y_root - context_win32->hot_y);
-  gdk_surface_raise (context_win32->drag_surface);
+  gdk_surface_move (drag_win32->drag_surface,
+                    x_root - drag_win32->hot_x,
+                    y_root - drag_win32->hot_y);
+  gdk_surface_raise (drag_win32->drag_surface);
 }
 
 static void
-gdk_win32_drag_context_init (GdkWin32DragContext *context)
+gdk_win32_drag_context_init (GdkWin32DragContext *drag)
 {
   g_assert (_win32_main_thread == NULL ||
             _win32_main_thread == g_thread_self ());
 
-  if (!use_ole2_dnd)
-    {
-      local_source_contexts = g_list_prepend (local_source_contexts, context);
-    }
-  else
-    {
-    }
+  drag->handle_events = TRUE;
 
-  GDK_NOTE (DND, g_print ("gdk_drag_context_init %p\n", context));
+  GDK_NOTE (DND, g_print ("gdk_win32_drag_context_init %p\n", drag));
 }
 
 static void
 gdk_win32_drag_context_finalize (GObject *object)
 {
-  GdkDragContext *context;
-  GdkWin32DragContext *context_win32;
+  GdkDragContext *drag;
+  GdkWin32DragContext *drag_win32;
   GdkSurface *drag_surface;
 
   g_assert (_win32_main_thread == NULL ||
             _win32_main_thread == g_thread_self ());
 
-  GDK_NOTE (DND, g_print ("gdk_drag_context_finalize %p\n", object));
+  GDK_NOTE (DND, g_print ("gdk_win32_drag_context_finalize %p\n", object));
 
   g_return_if_fail (GDK_IS_WIN32_DRAG_CONTEXT (object));
 
-  context = GDK_DRAG_CONTEXT (object);
-  context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+  drag = GDK_DRAG_CONTEXT (object);
+  drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
-  if (!use_ole2_dnd)
-    {
-      local_source_contexts = g_list_remove (local_source_contexts, context);
-
-      if (context == current_dest_drag)
-        current_dest_drag = NULL;
-    }
+  gdk_drag_context_set_cursor (drag, NULL);
 
-  g_set_object (&context_win32->ipc_window, NULL);
-  drag_surface = context_win32->drag_surface;
+  g_set_object (&drag_win32->ipc_window, NULL);
+  drag_surface = drag_win32->drag_surface;
 
   G_OBJECT_CLASS (gdk_win32_drag_context_parent_class)->finalize (object);
 
@@ -789,62 +784,30 @@ gdk_win32_drag_context_finalize (GObject *object)
 static GdkDragContext *
 gdk_drag_context_new (GdkDisplay         *display,
                       GdkContentProvider *content,
-                      GdkSurface         *source_surface,
-                      GdkContentFormats  *formats,
                       GdkDragAction       actions,
                       GdkDevice          *device,
                       GdkDragProtocol     protocol)
 {
-  GdkWin32DragContext *context_win32;
+  GdkWin32DragContext *drag_win32;
   GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
-  GdkDragContext *context;
+  GdkDragContext *drag;
 
-  context_win32 = g_object_new (GDK_TYPE_WIN32_DRAG_CONTEXT,
-                                "device", device ? device : gdk_seat_get_pointer 
(gdk_display_get_default_seat (display)),
+  drag_win32 = g_object_new (GDK_TYPE_WIN32_DRAG_CONTEXT,
+                                "device", device,
                                 "content", content,
-                                "formats", formats,
                                 NULL);
 
-  context = GDK_DRAG_CONTEXT (context_win32);
+  drag = GDK_DRAG_CONTEXT (drag_win32);
 
   if (win32_display->has_fixed_scale)
-    context_win32->scale = win32_display->surface_scale;
+    drag_win32->scale = win32_display->surface_scale;
   else
-    context_win32->scale = _gdk_win32_display_get_monitor_scale_factor (win32_display, NULL, NULL, NULL);
-
-  context->is_source = TRUE;
-  g_set_object (&context->source_surface, source_surface);
-  gdk_drag_context_set_actions (context, actions, actions);
-  context_win32->protocol = protocol;
+    drag_win32->scale = _gdk_win32_display_get_monitor_scale_factor (win32_display, NULL, NULL, NULL);
 
-  gdk_content_formats_unref (formats);
+  gdk_drag_context_set_actions (drag, actions, actions);
+  drag_win32->protocol = protocol;
 
-  return context;
-}
-
-GdkDragContext *
-_gdk_win32_drag_context_find (GdkSurface *source,
-                              GdkSurface *dest)
-{
-  GList *tmp_list = local_source_contexts;
-  GdkDragContext *context;
-
-  g_assert (_win32_main_thread == NULL ||
-            _win32_main_thread == g_thread_self ());
-
-  while (tmp_list)
-    {
-      context = (GdkDragContext *)tmp_list->data;
-
-      if (context->is_source &&
-          ((source == NULL) || (context->source_surface && (context->source_surface == source))) &&
-          ((dest == NULL) || (context->dest_surface && (context->dest_surface == dest))))
-        return context;
-
-      tmp_list = tmp_list->next;
-    }
-
-  return NULL;
+  return drag;
 }
 
 #define PRINT_GUID(guid) \
@@ -863,19 +826,23 @@ _gdk_win32_drag_context_find (GdkSurface *source,
 
 static enum_formats *enum_formats_new (GArray *formats);
 
+/* Finds a GdkDragContext object that corresponds to a DnD operation
+ * which is currently targetting the dest_surface
+ * Does not give a reference.
+ */
 GdkDragContext *
-_gdk_win32_find_source_context_for_dest_surface (GdkSurface *dest_surface)
+_gdk_win32_find_drag_for_dest_surface (GdkSurface *dest_surface)
 {
   GHashTableIter               iter;
-  GdkWin32DragContext         *win32_context;
+  GdkWin32DragContext         *drag_win32;
   GdkWin32DnDThreadDoDragDrop *ddd;
   GdkWin32Clipdrop            *clipdrop = _gdk_win32_clipdrop_get ();
 
   g_hash_table_iter_init (&iter, clipdrop->active_source_drags);
 
-  while (g_hash_table_iter_next (&iter, (gpointer *) &win32_context, (gpointer *) &ddd))
+  while (g_hash_table_iter_next (&iter, (gpointer *) &drag_win32, (gpointer *) &ddd))
     if (ddd->src_context->dest_window_handle == GDK_SURFACE_HWND (dest_surface))
-      return GDK_DRAG_CONTEXT (win32_context);
+      return GDK_DRAG_CONTEXT (drag_win32);
 
   return NULL;
 }
@@ -919,7 +886,7 @@ static gboolean
 notify_dnd_enter (gpointer user_data)
 {
   GdkWin32DnDEnterLeaveNotify *notify = (GdkWin32DnDEnterLeaveNotify *) user_data;
-  GdkDragContext *context = GDK_DRAG_CONTEXT (notify->opaque_context);
+  GdkDragContext *drag = GDK_DRAG_CONTEXT (notify->opaque_context);
   GdkSurface *dest_surface, *dw;
 
   dw = gdk_win32_handle_table_lookup (notify->target_window_handle);
@@ -927,10 +894,10 @@ notify_dnd_enter (gpointer user_data)
   if (dw)
     dest_surface = g_object_ref (dw);
   else
-    dest_surface = gdk_win32_surface_foreign_new_for_display (gdk_drag_context_get_display (context), 
notify->target_window_handle);
+    dest_surface = gdk_win32_surface_foreign_new_for_display (gdk_drag_context_get_display (drag), 
notify->target_window_handle);
 
-  g_clear_object (&context->dest_surface);
-  context->dest_surface = dest_surface;
+  g_clear_object (&drag->dest_surface);
+  drag->dest_surface = dest_surface;
 
   g_free (notify);
 
@@ -941,7 +908,7 @@ static gboolean
 notify_dnd_leave (gpointer user_data)
 {
   GdkWin32DnDEnterLeaveNotify *notify = (GdkWin32DnDEnterLeaveNotify *) user_data;
-  GdkDragContext *context = GDK_DRAG_CONTEXT (notify->opaque_context);
+  GdkDragContext *drag = GDK_DRAG_CONTEXT (notify->opaque_context);
   GdkSurface *dest_surface, *dw;
 
   dw = gdk_win32_handle_table_lookup (notify->target_window_handle);
@@ -950,10 +917,10 @@ notify_dnd_leave (gpointer user_data)
     {
       dest_surface = gdk_surface_get_toplevel (dw);
 
-      if (dest_surface == context->dest_surface)
-        g_clear_object (&context->dest_surface);
+      if (dest_surface == drag->dest_surface)
+        g_clear_object (&drag->dest_surface);
       else
-        g_warning ("Destination window for handle 0x%p is 0x%p, but context has 0x%p", 
notify->target_window_handle, dest_surface, context->dest_surface);
+        g_warning ("Destination window for handle 0x%p is 0x%p, but drag has 0x%p", 
notify->target_window_handle, dest_surface, drag->dest_surface);
     }
   else
     g_warning ("Failed to find destination window for handle 0x%p", notify->target_window_handle);
@@ -979,7 +946,7 @@ idropsourcenotify_dragentertarget (IDropSourceNotify *This,
 
   notify = g_new0 (GdkWin32DnDEnterLeaveNotify, 1);
   notify->target_window_handle = hwndTarget;
-  notify->opaque_context = ctx->context;
+  notify->opaque_context = ctx->drag;
   g_idle_add_full (G_PRIORITY_DEFAULT, notify_dnd_enter, notify, NULL);
 
   return S_OK;
@@ -999,7 +966,7 @@ idropsourcenotify_dragleavetarget (IDropSourceNotify *This)
   notify = g_new0 (GdkWin32DnDEnterLeaveNotify, 1);
   notify->target_window_handle = ctx->dest_window_handle;
   ctx->dest_window_handle = NULL;
-  notify->opaque_context = ctx->context;
+  notify->opaque_context = ctx->drag;
   g_idle_add_full (G_PRIORITY_DEFAULT, notify_dnd_leave, notify, NULL);
 
   return S_OK;
@@ -1048,9 +1015,9 @@ idropsource_queryinterface (LPDROPSOURCE This,
 static gboolean
 unref_context_in_main_thread (gpointer opaque_context)
 {
-  GdkDragContext *context = GDK_DRAG_CONTEXT (opaque_context);
+  GdkDragContext *drag = GDK_DRAG_CONTEXT (opaque_context);
 
-  g_clear_object (&context);
+  g_clear_object (&drag);
 
   return G_SOURCE_REMOVE;
 }
@@ -1066,7 +1033,7 @@ idropsource_release (LPDROPSOURCE This)
 
   if (ref_count == 0)
   {
-    g_idle_add (unref_context_in_main_thread, ctx->context);
+    g_idle_add (unref_context_in_main_thread, ctx->drag);
     g_free (This);
   }
 
@@ -1091,11 +1058,13 @@ idropsource_querycontinuedrag (LPDROPSOURCE This,
 {
   source_drag_context *ctx = (source_drag_context *) This;
 
-  GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p esc=%d keystate=0x%lx with state %d", This, 
fEscapePressed, grfKeyState, ctx->util_data.state));
+  GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p esc=%d keystate=0x%lx with state %d\n", This, 
fEscapePressed, grfKeyState, ctx->util_data.state));
 
   if (!dnd_queue_is_empty ())
     process_dnd_queue (FALSE, 0, NULL);
 
+  GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag state %d\n", ctx->util_data.state));
+
   if (ctx->util_data.state == GDK_WIN32_DND_DROPPED)
     {
       GDK_NOTE (DND, g_print ("DRAGDROP_S_DROP\n"));
@@ -1113,6 +1082,33 @@ idropsource_querycontinuedrag (LPDROPSOURCE This,
     }
 }
 
+static void
+maybe_emit_action_changed (GdkWin32DragContext *drag_win32,
+                           GdkDragAction        actions)
+{
+  if (actions != drag_win32->current_action)
+    {
+      drag_win32->current_action = actions;
+      g_signal_emit_by_name (GDK_DRAG_CONTEXT (drag_win32), "action-changed", actions);
+    }
+}
+
+void
+_gdk_win32_local_drag_give_feedback (GdkDragContext *drag,
+                                     GdkDragAction   actions)
+{
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
+
+  if (drag_win32->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
+    drag_win32->drag_status = GDK_DRAG_STATUS_DRAG;
+
+  GDK_NOTE (DND, g_print ("_gdk_win32_local_drag_give_feedback: 0x%p\n",
+                          drag));
+
+  drag->action = actions;
+  maybe_emit_action_changed (drag_win32, actions);
+}
+
 static gboolean
 give_feedback (gpointer user_data)
 {
@@ -1122,19 +1118,14 @@ give_feedback (gpointer user_data)
 
   if (ddd)
     {
-      GdkDragContext *context = GDK_DRAG_CONTEXT (feedback->base.opaque_context);
-      GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+      GdkDragContext *drag = GDK_DRAG_CONTEXT (feedback->base.opaque_context);
+      GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
       GDK_NOTE (DND, g_print ("gdk_dnd_handle_drag_status: 0x%p\n",
-                              context));
-
-      context->action = action_for_drop_effect (feedback->received_drop_effect);
+                              drag));
 
-      if (context->action != win32_context->current_action)
-        {
-          win32_context->current_action = context->action;
-          g_signal_emit_by_name (context, "action-changed", context->action);
-        }
+      drag->action = action_for_drop_effect (feedback->received_drop_effect);
+      maybe_emit_action_changed (drag_win32, drag->action);
     }
 
   free_queue_item (&feedback->base);
@@ -1155,7 +1146,8 @@ idropsource_givefeedback (LPDROPSOURCE This,
     process_dnd_queue (FALSE, 0, NULL);
 
   feedback = g_new0 (GdkWin32DnDThreadGiveFeedback, 1);
-  feedback->base.opaque_context = ctx->context;
+  feedback->base.item_type = GDK_WIN32_DND_THREAD_QUEUE_ITEM_GIVE_FEEDBACK;
+  feedback->base.opaque_context = ctx->drag;
   feedback->received_drop_effect = dwEffect;
 
   g_idle_add_full (G_PRIORITY_DEFAULT, give_feedback, feedback, NULL);
@@ -1274,7 +1266,7 @@ idataobject_getdata (LPDATAOBJECT This,
   GdkWin32DnDThreadGetData *getdata;
   GdkWin32ContentFormatPair *pair;
 
-  if (ctx->context == NULL)
+  if (ctx->drag == NULL)
     return E_FAIL;
 
   GDK_NOTE (DND, g_print ("idataobject_getdata %p %s ",
@@ -1294,7 +1286,7 @@ idataobject_getdata (LPDATAOBJECT This,
 
   getdata = g_new0 (GdkWin32DnDThreadGetData, 1);
   getdata->base.item_type = GDK_WIN32_DND_THREAD_QUEUE_ITEM_GET_DATA;
-  getdata->base.opaque_context = (gpointer) ctx->context;
+  getdata->base.opaque_context = (gpointer) ctx->drag;
   getdata->pair = *pair;
   g_idle_add_full (G_PRIORITY_DEFAULT, get_data_response, getdata, NULL);
 
@@ -1612,31 +1604,31 @@ static IEnumFORMATETCVtbl ief_vtbl = {
 };
 
 static source_drag_context *
-source_context_new (GdkDragContext    *context,
+source_context_new (GdkDragContext    *drag,
                     GdkSurface        *window,
                     GdkContentFormats *formats)
 {
-  GdkWin32DragContext *context_win32;
+  GdkWin32DragContext *drag_win32;
   source_drag_context *result;
 
-  context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+  drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
   result = g_new0 (source_drag_context, 1);
-  result->context = g_object_ref (context);
+  result->drag = g_object_ref (drag);
   result->ids.lpVtbl = &ids_vtbl;
   result->idsn.lpVtbl = &idsn_vtbl;
   result->ref_count = 1;
-  result->source_window_handle = GDK_SURFACE_HWND (context->source_surface);
-  result->scale = context_win32->scale;
+  result->source_window_handle = GDK_SURFACE_HWND (drag->source_surface);
+  result->scale = drag_win32->scale;
   result->util_data.state = GDK_WIN32_DND_PENDING; /* Implicit */
 
-  GDK_NOTE (DND, g_print ("source_context_new: %p (drag context %p)\n", result, result->context));
+  GDK_NOTE (DND, g_print ("source_context_new: %p (drag %p)\n", result, result->drag));
 
   return result;
 }
 
 static data_object *
-data_object_new (GdkDragContext *context)
+data_object_new (GdkDragContext *drag)
 {
   data_object *result;
   const char * const *mime_types;
@@ -1646,10 +1638,10 @@ data_object_new (GdkDragContext *context)
 
   result->ido.lpVtbl = &ido_vtbl;
   result->ref_count = 1;
-  result->context = context;
+  result->drag = drag;
   result->formats = g_array_new (FALSE, FALSE, sizeof (GdkWin32ContentFormatPair));
 
-  mime_types = gdk_content_formats_get_mime_types (gdk_drag_context_get_formats (context), &n_mime_types);
+  mime_types = gdk_content_formats_get_mime_types (gdk_drag_context_get_formats (drag), &n_mime_types);
 
   for (i = 0; i < n_mime_types; i++)
     {
@@ -1689,8 +1681,8 @@ _gdk_drag_init (void)
 {
   CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
 
-  if (g_strcmp0 (getenv ("GDK_WIN32_OLE2_DND"), "0") != 0)
-    use_ole2_dnd = TRUE;
+  if (g_strcmp0 (getenv ("GDK_WIN32_OLE2_DND"), "0") == 0)
+    use_ole2_dnd = FALSE;
 
   if (use_ole2_dnd)
     {
@@ -1714,123 +1706,6 @@ _gdk_win32_dnd_exit (void)
   CoUninitialize ();
 }
 
-/* Source side */
-
-void
-_gdk_win32_drag_context_send_local_status_event (GdkDragContext *src_context,
-                                                 GdkDragAction   action)
-{
-  GdkWin32DragContext *src_context_win32 = GDK_WIN32_DRAG_CONTEXT (src_context);
-
-  if (src_context_win32->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
-    src_context_win32->drag_status = GDK_DRAG_STATUS_DRAG;
-
-  src_context->action = action;
-
-  GDK_NOTE (DND, g_print ("gdk_dnd_handle_drag_status: 0x%p\n",
-                          src_context));
-
-  if (action != src_context_win32->current_action)
-    {
-      src_context_win32->current_action = action;
-      g_signal_emit_by_name (src_context, "action-changed", action);
-    }
-}
-
-static void
-local_send_leave (GdkDragContext *context,
-                  guint32         time)
-{
-  GdkEvent *tmp_event;
-
-  GDK_NOTE (DND, g_print ("local_send_leave: context=%p current_dest_drag=%p\n",
-                          context,
-                          current_dest_drag));
-
-  if ((current_dest_drag != NULL) &&
-      (GDK_WIN32_DRAG_CONTEXT (current_dest_drag)->protocol == GDK_DRAG_PROTO_LOCAL) &&
-      (current_dest_drag->source_surface == context->source_surface))
-    {
-      gdk_drop_emit_leave_event (GDK_DROP (current_dest_drag), FALSE, GDK_CURRENT_TIME);
-
-      current_dest_drag = NULL;
-    }
-}
-
-static void
-local_send_motion (GdkDragContext *context,
-                   gint            x_root,
-                   gint            y_root,
-                   GdkDragAction   action,
-                   guint32         time)
-{
-  GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
-
-  GDK_NOTE (DND, g_print ("local_send_motion: context=%p (%d,%d) current_dest_drag=%p\n",
-                          context, x_root, y_root,
-                          current_dest_drag));
-
-  if ((current_dest_drag != NULL) &&
-      (GDK_WIN32_DRAG_CONTEXT (current_dest_drag)->protocol == GDK_DRAG_PROTO_LOCAL) &&
-      (current_dest_drag->source_surface == context->source_surface))
-    {
-      GdkWin32DragContext *current_dest_drag_win32;
-
-      gdk_drag_context_set_actions (current_dest_drag, action, action);
-
-      current_dest_drag_win32 = GDK_WIN32_DRAG_CONTEXT (current_dest_drag);
-      current_dest_drag_win32->util_data.last_x = x_root;
-      current_dest_drag_win32->util_data.last_y = y_root;
-
-      context_win32->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
-
-      gdk_drop_emit_motion_event (GDK_DROP (current_dest_drag), FALSE, x_root, y_root, time);
-    }
-}
-
-static void
-local_send_drop (GdkDragContext *context,
-                 guint32         time)
-{
-  GDK_NOTE (DND, g_print ("local_send_drop: context=%p current_dest_drag=%p\n",
-                          context,
-                          current_dest_drag));
-
-  if ((current_dest_drag != NULL) &&
-      (GDK_WIN32_DRAG_CONTEXT (current_dest_drag)->protocol == GDK_DRAG_PROTO_LOCAL) &&
-      (current_dest_drag->source_surface == context->source_surface))
-    {
-      GdkWin32DragContext *context_win32;
-
-      context_win32 = GDK_WIN32_DRAG_CONTEXT (current_dest_drag);
-
-      gdk_drop_emit_motion_event (GDK_DROP (current_dest_drag),
-                                  FALSE,
-                                  context_win32->util_data.last_x, context_win32->util_data.last_y,
-                                  GDK_CURRENT_TIME);
-
-      current_dest_drag = NULL;
-    }
-}
-
-void
-_gdk_win32_drag_do_leave (GdkDragContext *context,
-                          guint32         time)
-{
-  if (context->dest_surface)
-    {
-      GDK_NOTE (DND, g_print ("gdk_drag_do_leave\n"));
-
-      if (!use_ole2_dnd)
-        {
-          if (GDK_WIN32_DRAG_CONTEXT (context)->protocol == GDK_DRAG_PROTO_LOCAL)
-            local_send_leave (context, time);
-        }
-
-      g_clear_object (&context->dest_surface);
-    }
-}
-
 static GdkSurface *
 create_drag_surface (GdkDisplay *display)
 {
@@ -1844,64 +1719,64 @@ create_drag_surface (GdkDisplay *display)
 }
 
 GdkDragContext *
-_gdk_win32_surface_drag_begin (GdkSurface        *window,
-                              GdkDevice          *device,
-                              GdkContentProvider *content,
-                              GdkDragAction       actions,
-                              gint                dx,
-                              gint                dy)
-{
-  GdkDragContext *context;
-  GdkWin32DragContext *context_win32;
+_gdk_win32_surface_drag_begin (GdkSurface         *window,
+                               GdkDevice          *device,
+                               GdkContentProvider *content,
+                               GdkDragAction       actions,
+                               gint                dx,
+                               gint                dy)
+{
+  GdkDragContext *drag;
+  GdkWin32DragContext *drag_win32;
   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
   int x_root, y_root;
 
   g_return_val_if_fail (window != NULL, NULL);
 
-  context = gdk_drag_context_new (gdk_surface_get_display (window),
-                                  content,
-                                  window,
-                                  gdk_content_formats_union_serialize_mime_types 
(gdk_content_provider_ref_storable_formats (content)),
-                                  actions,
-                                  device,
-                                  use_ole2_dnd ? GDK_DRAG_PROTO_OLE2 : GDK_DRAG_PROTO_LOCAL);
+  drag = gdk_drag_context_new (gdk_surface_get_display (window),
+                               content,
+                               actions,
+                               device,
+                               use_ole2_dnd ? GDK_DRAG_PROTO_OLE2 : GDK_DRAG_PROTO_LOCAL);
+  drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
-  context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+  g_set_object (&drag->source_surface, window);
+  drag->is_source = TRUE;
 
-  GDK_NOTE (DND, g_print ("gdk_drag_begin\n"));
+  GDK_NOTE (DND, g_print ("_gdk_win32_surface_drag_begin\n"));
 
   gdk_device_get_position (device, &x_root, &y_root);
   x_root += dx;
   y_root += dy;
 
-  context_win32->start_x = x_root;
-  context_win32->start_y = y_root;
-  context_win32->util_data.last_x = context_win32->start_x;
-  context_win32->util_data.last_y = context_win32->start_y;
+  drag_win32->start_x = x_root;
+  drag_win32->start_y = y_root;
+  drag_win32->util_data.last_x = drag_win32->start_x;
+  drag_win32->util_data.last_y = drag_win32->start_y;
 
-  g_set_object (&context_win32->ipc_window, window);
+  g_set_object (&drag_win32->ipc_window, window);
 
-  context_win32->drag_surface = create_drag_surface (gdk_surface_get_display (window));
+  drag_win32->drag_surface = create_drag_surface (gdk_surface_get_display (window));
 
-  if (!drag_context_grab (context))
+  if (!drag_context_grab (drag))
     {
-      g_object_unref (context);
+      g_object_unref (drag);
       return FALSE;
     }
 
-  if (use_ole2_dnd)
+  if (drag_win32->protocol == GDK_DRAG_PROTO_OLE2)
     {
       GdkWin32DnDThreadDoDragDrop *ddd = g_new0 (GdkWin32DnDThreadDoDragDrop, 1);
       source_drag_context *source_ctx;
       data_object         *data_obj;
 
-      source_ctx = source_context_new (context, 
+      source_ctx = source_context_new (drag, 
                                        window,
-                                       gdk_drag_context_get_formats (context));
-      data_obj = data_object_new (context);
+                                       gdk_drag_context_get_formats (drag));
+      data_obj = data_object_new (drag);
 
       ddd->base.item_type = GDK_WIN32_DND_THREAD_QUEUE_ITEM_DO_DRAG_DROP;
-      ddd->base.opaque_context = context_win32;
+      ddd->base.opaque_context = drag_win32;
       ddd->src_context = source_ctx;
       ddd->src_object = data_obj;
       ddd->allowed_drop_effects = 0;
@@ -1912,17 +1787,17 @@ _gdk_win32_surface_drag_begin (GdkSurface        *window,
       if (actions & GDK_ACTION_LINK)
         ddd->allowed_drop_effects |= DROPEFFECT_LINK;
 
-      g_hash_table_replace (clipdrop->active_source_drags, g_object_ref (context), ddd);
+      g_hash_table_replace (clipdrop->active_source_drags, g_object_ref (drag), ddd);
       increment_dnd_queue_counter ();
       g_async_queue_push (clipdrop->dnd_queue, ddd);
       API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id, thread_wakeup_message, 0, 0));
 
-      context_win32->util_data.state = GDK_WIN32_DND_PENDING;
+      drag_win32->util_data.state = GDK_WIN32_DND_PENDING;
     }
 
-  move_drag_surface (context, x_root, y_root);
+  move_drag_surface (drag, x_root, y_root);
 
-  return context;
+  return drag;
 }
 
 /* TODO: remove this?
@@ -1968,22 +1843,26 @@ find_window_enum_proc (HWND   hwnd,
     return TRUE;
 }
 
+/* Finds the GdkSurface under cursor. Local DnD protcol
+ * uses this function, since local protocol is implemented
+ * entirely in GDK and cannot rely on the OS to notify
+ * drop targets about drags that move over them.
+ */
 static GdkSurface *
-gdk_win32_drag_context_find_surface (GdkDragContext  *context,
+gdk_win32_drag_context_find_surface (GdkDragContext  *drag,
                                      GdkSurface      *drag_surface,
                                      gint             x_root,
-                                     gint             y_root,
-                                     GdkDragProtocol *protocol)
+                                     gint             y_root)
 {
-  GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
   GdkSurface *dest_surface, *dw;
   find_window_enum_arg a;
 
   g_assert (_win32_main_thread == NULL ||
             _win32_main_thread == g_thread_self ());
 
-  a.x = x_root * context_win32->scale - _gdk_offset_x;
-  a.y = y_root * context_win32->scale - _gdk_offset_y;
+  a.x = x_root * drag_win32->scale - _gdk_offset_x;
+  a.y = y_root * drag_win32->scale - _gdk_offset_y;
   a.ignore = drag_surface ? GDK_SURFACE_HWND (drag_surface) : NULL;
   a.result = NULL;
 
@@ -2005,157 +1884,114 @@ gdk_win32_drag_context_find_surface (GdkDragContext  *context,
           g_object_ref (dest_surface);
         }
       else
-        dest_surface = gdk_win32_surface_foreign_new_for_display (gdk_drag_context_get_display (context), 
a.result);
-
-      if (use_ole2_dnd)
-        *protocol = GDK_DRAG_PROTO_OLE2;
-      else if (context->source_surface)
-        *protocol = GDK_DRAG_PROTO_LOCAL;
-      else
-        *protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
+        dest_surface = gdk_win32_surface_foreign_new_for_display (gdk_drag_context_get_display (drag), 
a.result);
     }
 
   GDK_NOTE (DND,
-            g_print ("gdk_win32_drag_context_find_surface: %p %+d%+d: %p: %p %s\n",
+            g_print ("gdk_win32_drag_context_find_surface: %p %+d%+d: %p: %p\n",
                      (drag_surface ? GDK_SURFACE_HWND (drag_surface) : NULL),
                      x_root, y_root,
                      a.result,
-                     (dest_surface ? GDK_SURFACE_HWND (dest_surface) : NULL),
-                     _gdk_win32_drag_protocol_to_string (*protocol)));
+                     (dest_surface ? GDK_SURFACE_HWND (dest_surface) : NULL)));
 
   return dest_surface;
 }
 
-static gboolean
-gdk_win32_drag_context_drag_motion (GdkDragContext  *context,
-                                    GdkSurface      *dest_surface,
-                                    GdkDragProtocol  protocol,
-                                    gint             x_root,
-                                    gint             y_root,
-                                    GdkDragAction    suggested_action,
-                                    GdkDragAction    possible_actions,
-                                    guint32          time)
+static DWORD
+manufacture_keystate_from_GMT (GdkModifierType state)
 {
-  GdkWin32DragContext *context_win32;
+  DWORD key_state = 0;
 
-  g_assert (_win32_main_thread == NULL ||
-            _win32_main_thread == g_thread_self ());
+  if (state & GDK_MOD1_MASK)
+    key_state |= MK_ALT;
+  if (state & GDK_CONTROL_MASK)
+    key_state |= MK_CONTROL;
+  if (state & GDK_SHIFT_MASK)
+    key_state |= MK_SHIFT;
+  if (state & GDK_BUTTON1_MASK)
+    key_state |= MK_LBUTTON;
+  if (state & GDK_BUTTON2_MASK)
+    key_state |= MK_MBUTTON;
+  if (state & GDK_BUTTON3_MASK)
+    key_state |= MK_RBUTTON;
 
-  g_return_val_if_fail (context != NULL, FALSE);
+  return key_state;
+}
 
-  GDK_NOTE (DND, g_print ("gdk_win32_drag_context_drag_motion: @ %+d:%+d %s suggested=%s, possible=%s\n"
-                          " context=%p:{actions=%s,suggested=%s,action=%s}\n",
-                          x_root, y_root,
-                          _gdk_win32_drag_protocol_to_string (protocol),
-                          _gdk_win32_drag_action_to_string (suggested_action),
-                          _gdk_win32_drag_action_to_string (possible_actions),
-                          context,
-                          _gdk_win32_drag_action_to_string (gdk_drag_context_get_actions (context)),
-                          _gdk_win32_drag_action_to_string (gdk_drag_context_get_suggested_action (context)),
-                          _gdk_win32_drag_action_to_string (context->action)));
+static gboolean
+gdk_win32_local_drag_motion (GdkDragContext  *drag,
+                             GdkSurface      *dest_surface,
+                             gint             x_root,
+                             gint             y_root,
+                             GdkDragAction    possible_actions,
+                             DWORD            key_state,
+                             guint32          time_)
+{
+  GdkWin32DragContext *drag_win32;
+  GdkDrop *drop;
+  GdkDragAction actions;
 
-  context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+  g_assert (_win32_main_thread == NULL ||
+            _win32_main_thread == g_thread_self ());
 
-  if (context_win32->drag_surface)
-    move_drag_surface (context, x_root, y_root);
+  g_return_val_if_fail (drag != NULL, FALSE);
 
-  if (!use_ole2_dnd)
-    {
-      if (context->dest_surface == dest_surface)
-        {
-          GdkDragContext *dest_context;
+  drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
-          dest_context = _gdk_win32_drop_context_find (context->source_surface,
-                                                       dest_surface);
+  drop = _gdk_win32_get_drop_for_dest_surface (drag->dest_surface);
 
-          if (dest_context)
-            gdk_drag_context_set_actions (dest_context, possible_actions, suggested_action);
+  actions = gdk_drag_context_get_actions (drag);
 
-          gdk_drag_context_set_actions (context, possible_actions, suggested_action);
-        }
-      else
-        {
-          /* Send a leave to the last destination */
-          _gdk_win32_drag_do_leave (context, time);
+  GDK_NOTE (DND, g_print ("gdk_win32_local_drag_motion: @ %+d:%+d possible=%s\n"
+                          " dest=%p (current %p) drop=%p drag=%p:{actions=%s,suggested=%s,action=%s}\n",
+                          x_root, y_root,
+                          _gdk_win32_drag_action_to_string (possible_actions),
+                          dest_surface, drag->dest_surface, drop, drag,
+                          _gdk_win32_drag_action_to_string (actions),
+                          _gdk_win32_drag_action_to_string (gdk_drag_context_get_suggested_action (drag)),
+                          _gdk_win32_drag_action_to_string (drag->action)));
 
-          context_win32->drag_status = GDK_DRAG_STATUS_DRAG;
+  if (drag->dest_surface != dest_surface)
+    {
+      /* Send a leave to the last destination */
+      if (drop)
+        _gdk_win32_local_drop_target_dragleave (drop, time_);
 
-          /* Check if new destination accepts drags, and which protocol */
-          if (dest_surface)
-            {
-              g_set_object (&context->dest_surface, dest_surface);
-              context_win32->protocol = protocol;
+      g_set_object (&drag->dest_surface, dest_surface);
 
-              switch (protocol)
-                {
-                case GDK_DRAG_PROTO_LOCAL:
-                  _gdk_win32_local_send_enter (context, time);
-                  break;
+      drag_win32->drag_status = GDK_DRAG_STATUS_DRAG;
 
-                default:
-                  break;
-                }
-              gdk_drag_context_set_actions (context, possible_actions, suggested_action);
-            }
-          else
-            {
-              context->dest_surface = NULL;
-              gdk_drag_context_set_actions (context, 0, 0);
-            }
+      _gdk_win32_local_drop_target_dragenter (drag, dest_surface, x_root, y_root, key_state, time_, 
&actions);
 
-          GDK_NOTE (DND, g_print ("gdk_dnd_handle_drag_status: 0x%p\n",
-                                  context));
-
-          if (context->action != context_win32->current_action)
-            {
-              context_win32->current_action = context->action;
-              g_signal_emit_by_name (context, "action-changed", context->action);
-            }
-        }
+      drop = _gdk_win32_get_drop_for_dest_surface (drag->dest_surface);
+      maybe_emit_action_changed (drag_win32, actions);
+    }
 
-      /* Send a drag-motion event */
+  /* Send a drag-motion event */
 
-      context_win32->util_data.last_x = x_root;
-      context_win32->util_data.last_y = y_root;
+  drag_win32->util_data.last_x = x_root;
+  drag_win32->util_data.last_y = y_root;
 
-      if (context->dest_surface)
-        {
-          if (context_win32->drag_status == GDK_DRAG_STATUS_DRAG)
-            {
-              switch (context_win32->protocol)
-                {
-                case GDK_DRAG_PROTO_LOCAL:
-                  local_send_motion (context, x_root, y_root, suggested_action, time);
-                  break;
+  if (drop != NULL &&
+      drag_win32->drag_status == GDK_DRAG_STATUS_DRAG &&
+      _gdk_win32_local_drop_target_will_emit_motion (drop, x_root, y_root, key_state))
+    {
+      actions = gdk_drag_context_get_actions (drag);
+      drag_win32->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
 
-                case GDK_DRAG_PROTO_NONE:
-                  g_warning ("GDK_DRAG_PROTO_NONE is not valid in gdk_win32_drag_context_drag_motion()");
-                  break;
+      _gdk_win32_local_drop_target_dragover (drop, drag, x_root, y_root, key_state, time_, &actions);
 
-                default:
-                  break;
-                }
-            }
-          else
-            {
-              GDK_NOTE (DND, g_print (" returning TRUE\n"
-                                      " context=%p:{actions=%s,suggested=%s,action=%s}\n",
-                                      context,
-                                      _gdk_win32_drag_action_to_string (gdk_drag_context_get_actions 
(context)),
-                                      _gdk_win32_drag_action_to_string 
(gdk_drag_context_get_suggested_action (context)),
-                                      _gdk_win32_drag_action_to_string (context->action)));
-              return TRUE;
-            }
-        }
+      maybe_emit_action_changed (drag_win32, actions);
     }
 
-  GDK_NOTE (DND, g_print (" returning FALSE\n"
-                          " context=%p:{actions=%s,suggested=%s,action=%s}\n",
-                          context,
-                          _gdk_win32_drag_action_to_string (gdk_drag_context_get_actions (context)),
-                          _gdk_win32_drag_action_to_string (gdk_drag_context_get_suggested_action (context)),
-                          _gdk_win32_drag_action_to_string (context->action)));
-  return FALSE;
+  GDK_NOTE (DND, g_print (" returning %s\n"
+                          " drag=%p:{actions=%s,suggested=%s,action=%s}\n",
+                          (drop != NULL && drag_win32->drag_status == GDK_DRAG_STATUS_DRAG) ? "TRUE" : 
"FALSE",
+                          drag,
+                          _gdk_win32_drag_action_to_string (gdk_drag_context_get_actions (drag)),
+                          _gdk_win32_drag_action_to_string (gdk_drag_context_get_suggested_action (drag)),
+                          _gdk_win32_drag_action_to_string (drag->action)));
+  return (drop != NULL && drag_win32->drag_status == GDK_DRAG_STATUS_DRAG);
 }
 
 static void
@@ -2173,75 +2009,86 @@ send_source_state_update (GdkWin32Clipdrop    *clipdrop,
 }
 
 static void
-gdk_win32_drag_context_drag_drop (GdkDragContext *context,
-                                  guint32         time)
+gdk_win32_drag_context_drag_drop (GdkDragContext *drag,
+                                  guint32         time_)
 {
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
 
   g_assert (_win32_main_thread == NULL ||
             _win32_main_thread == g_thread_self ());
 
-  g_return_if_fail (context != NULL);
+  g_return_if_fail (drag != NULL);
 
-  GDK_NOTE (DND, g_print ("gdk_drag_drop\n"));
+  GDK_NOTE (DND, g_print ("gdk_win32_drag_context_drag_drop\n"));
 
-  if (!use_ole2_dnd)
+  if (drag_win32->protocol == GDK_DRAG_PROTO_LOCAL)
     {
-      if (context->dest_surface &&
-          GDK_WIN32_DRAG_CONTEXT (context)->protocol == GDK_DRAG_PROTO_LOCAL)
-        local_send_drop (context, time);
+      GdkDrop *drop;
+      drop = _gdk_win32_get_drop_for_dest_surface (drag->dest_surface);
+
+      if (drop)
+        {
+          GdkDragAction actions;
+
+          actions = gdk_drag_context_get_actions (drag);
+          _gdk_win32_local_drop_target_drop (drop, drag, time_, &actions);
+          maybe_emit_action_changed (drag_win32, actions);
+          _gdk_win32_local_drag_context_drop_response (drag, actions);
+        }
     }
-  else
+  else if (drag_win32->protocol == GDK_DRAG_PROTO_OLE2)
     {
-      gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, context);
+      gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
 
-      GDK_WIN32_DRAG_CONTEXT (context)->util_data.state = GDK_WIN32_DND_DROPPED;
+      drag_win32->util_data.state = GDK_WIN32_DND_DROPPED;
 
       if (ddd)
-        send_source_state_update (clipdrop, GDK_WIN32_DRAG_CONTEXT (context), ddd);
+        send_source_state_update (clipdrop, drag_win32, ddd);
     }
 }
 
 static void
-gdk_win32_drag_context_drag_abort (GdkDragContext *context,
-                                   guint32         time)
+gdk_win32_drag_context_drag_abort (GdkDragContext *drag,
+                                   guint32         time_)
 {
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
 
   g_assert (_win32_main_thread == NULL ||
             _win32_main_thread == g_thread_self ());
 
-  g_return_if_fail (context != NULL);
+  g_return_if_fail (drag != NULL);
 
-  GDK_NOTE (DND, g_print ("gdk_drag_abort\n"));
+  GDK_NOTE (DND, g_print ("gdk_win32_drag_context_drag_abort\n"));
 
-  if (use_ole2_dnd)
+  if (drag_win32->protocol == GDK_DRAG_PROTO_OLE2)
     {
-      gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, context);
+      gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
 
-      GDK_WIN32_DRAG_CONTEXT (context)->util_data.state = GDK_WIN32_DND_NONE;
+      drag_win32->util_data.state = GDK_WIN32_DND_NONE;
 
       if (ddd)
-        send_source_state_update (clipdrop, GDK_WIN32_DRAG_CONTEXT (context), ddd);
+        send_source_state_update (clipdrop, drag_win32, ddd);
     }
 }
 
 static void
-gdk_win32_drag_context_set_cursor (GdkDragContext *context,
+gdk_win32_drag_context_set_cursor (GdkDragContext *drag,
                                    GdkCursor      *cursor)
 {
-  GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
-  GDK_NOTE (DND, g_print ("gdk_drag_context_set_cursor: 0x%p 0x%p\n", context, cursor));
+  GDK_NOTE (DND, g_print ("gdk_win32_drag_context_set_cursor: 0x%p 0x%p\n", drag, cursor));
 
-  if (!g_set_object (&context_win32->cursor, cursor))
+  if (!g_set_object (&drag_win32->cursor, cursor))
     return;
 
-  if (context_win32->grab_seat)
+  if (drag_win32->grab_seat)
     {
       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
-      gdk_device_grab (gdk_seat_get_pointer (context_win32->grab_seat),
-                       context_win32->ipc_window,
+      gdk_device_grab (gdk_seat_get_pointer (drag_win32->grab_seat),
+                       drag_win32->ipc_window,
                        GDK_OWNERSHIP_APPLICATION, FALSE,
                        GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
                        cursor, GDK_CURRENT_TIME);
@@ -2260,7 +2107,7 @@ ease_out_cubic (double t)
 
 typedef struct _GdkDragAnim GdkDragAnim;
 struct _GdkDragAnim {
-  GdkWin32DragContext *context;
+  GdkWin32DragContext *drag;
   GdkFrameClock *frame_clock;
   gint64 start_time;
 };
@@ -2268,7 +2115,7 @@ struct _GdkDragAnim {
 static void
 gdk_drag_anim_destroy (GdkDragAnim *anim)
 {
-  g_object_unref (anim->context);
+  g_object_unref (anim->drag);
   g_slice_free (GdkDragAnim, anim);
 }
 
@@ -2276,7 +2123,7 @@ static gboolean
 gdk_drag_anim_timeout (gpointer data)
 {
   GdkDragAnim *anim = data;
-  GdkWin32DragContext *context = anim->context;
+  GdkWin32DragContext *drag = anim->drag;
   GdkFrameClock *frame_clock = anim->frame_clock;
   gint64 current_time;
   double f;
@@ -2294,20 +2141,20 @@ gdk_drag_anim_timeout (gpointer data)
 
   t = ease_out_cubic (f);
 
-  gdk_surface_show (context->drag_surface);
-  gdk_surface_move (context->drag_surface,
-                    context->util_data.last_x + (context->start_x - context->util_data.last_x) * t - 
context->hot_x,
-                    context->util_data.last_y + (context->start_y - context->util_data.last_y) * t - 
context->hot_y);
-  gdk_surface_set_opacity (context->drag_surface, 1.0 - f);
+  gdk_surface_show (drag->drag_surface);
+  gdk_surface_move (drag->drag_surface,
+                    drag->util_data.last_x + (drag->start_x - drag->util_data.last_x) * t - drag->hot_x,
+                    drag->util_data.last_y + (drag->start_y - drag->util_data.last_y) * t - drag->hot_y);
+  gdk_surface_set_opacity (drag->drag_surface, 1.0 - f);
 
   return G_SOURCE_CONTINUE;
 }
 
 static void
-gdk_win32_drag_context_drop_done (GdkDragContext *context,
+gdk_win32_drag_context_drop_done (GdkDragContext *drag,
                                   gboolean        success)
 {
-  GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
   GdkDragAnim *anim;
 /*
   cairo_surface_t *win_surface;
@@ -2316,40 +2163,46 @@ gdk_win32_drag_context_drop_done (GdkDragContext *context,
 */
   guint id;
 
-  GDK_NOTE (DND, g_print ("gdk_drag_context_drop_done: 0x%p %s\n",
-                          context,
+  GDK_NOTE (DND, g_print ("gdk_win32_drag_context_drop_done: 0x%p %s\n",
+                          drag,
                           success ? "dropped successfully" : "dropped unsuccessfully"));
 
   /* FIXME: This is temporary, until the code is fixed to ensure that
    * gdk_drag_finish () is called by GTK.
    */
-  if (use_ole2_dnd)
+  if (drag_win32->protocol == GDK_DRAG_PROTO_OLE2)
     {
       GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
-      gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, context);
+      gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
 
       if (success)
-        win32_context->util_data.state = GDK_WIN32_DND_DROPPED;
+        drag_win32->util_data.state = GDK_WIN32_DND_DROPPED;
       else
-        win32_context->util_data.state = GDK_WIN32_DND_NONE;
+        drag_win32->util_data.state = GDK_WIN32_DND_NONE;
 
       if (ddd)
-        send_source_state_update (clipdrop, win32_context, ddd);
+        send_source_state_update (clipdrop, drag_win32, ddd);
+    }
+  else if (drag_win32->protocol == GDK_DRAG_PROTO_LOCAL)
+    {
+      
     }
 
+  drag_win32->handle_events = FALSE;
+
   if (success)
     {
-      gdk_surface_hide (win32_context->drag_surface);
+      gdk_surface_hide (drag_win32->drag_surface);
 
       return;
     }
 
 /*
-  win_surface = _gdk_surface_ref_cairo_surface (win32_context->drag_surface);
-  surface = gdk_surface_create_similar_surface (win32_context->drag_surface,
+  win_surface = _gdk_surface_ref_cairo_surface (drag_win32->drag_surface);
+  surface = gdk_surface_create_similar_surface (drag_win32->drag_surface,
                                                 cairo_surface_get_content (win_surface),
-                                                gdk_surface_get_width (win32_context->drag_surface),
-                                                gdk_surface_get_height (win32_context->drag_surface));
+                                                gdk_surface_get_width (drag_win32->drag_surface),
+                                                gdk_surface_get_height (drag_win32->drag_surface));
   cr = cairo_create (surface);
   cairo_set_source_surface (cr, win_surface, 0, 0);
   cairo_paint (cr);
@@ -2358,20 +2211,20 @@ gdk_win32_drag_context_drop_done (GdkDragContext *context,
 
   pattern = cairo_pattern_create_for_surface (surface);
 
-  gdk_surface_set_background_pattern (win32_context->drag_surface, pattern);
+  gdk_surface_set_background_pattern (drag_win32->drag_surface, pattern);
 
   cairo_pattern_destroy (pattern);
   cairo_surface_destroy (surface);
 */
 
   anim = g_slice_new0 (GdkDragAnim);
-  g_set_object (&anim->context, win32_context);
-  anim->frame_clock = gdk_surface_get_frame_clock (win32_context->drag_surface);
+  g_set_object (&anim->drag, drag_win32);
+  anim->frame_clock = gdk_surface_get_frame_clock (drag_win32->drag_surface);
   anim->start_time = gdk_frame_clock_get_frame_time (anim->frame_clock);
 
-  GDK_NOTE (DND, g_print ("gdk_drag_context_drop_done: animate the drag window from %d : %d to %d : %d\n",
-                          win32_context->util_data.last_x, win32_context->util_data.last_y,
-                          win32_context->start_x, win32_context->start_y));
+  GDK_NOTE (DND, g_print ("gdk_win32_drag_context_drop_done: animate the drag window from %d : %d to %d : 
%d\n",
+                          drag_win32->util_data.last_x, drag_win32->util_data.last_y,
+                          drag_win32->start_x, drag_win32->start_y));
 
   id = g_timeout_add_full (G_PRIORITY_DEFAULT, 17,
                            gdk_drag_anim_timeout, anim,
@@ -2380,29 +2233,33 @@ gdk_win32_drag_context_drop_done (GdkDragContext *context,
 }
 
 static gboolean
-drag_context_grab (GdkDragContext *context)
+drag_context_grab (GdkDragContext *drag)
 {
-  GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
   GdkSeatCapabilities capabilities;
   GdkSeat *seat;
   GdkCursor *cursor;
 
-  if (!context_win32->ipc_window)
+  GDK_NOTE (DND, g_print ("drag_context_grab: 0x%p with ipc window 0x%p\n",
+                          drag,
+                          drag_win32->ipc_window));
+
+  if (!drag_win32->ipc_window)
     return FALSE;
 
-  seat = gdk_device_get_seat (gdk_drag_context_get_device (context));
+  seat = gdk_device_get_seat (gdk_drag_context_get_device (drag));
 
   capabilities = GDK_SEAT_CAPABILITY_ALL;
 
-  cursor = gdk_drag_get_cursor (context, gdk_drag_context_get_selected_action (context));
-  g_set_object (&context_win32->cursor, cursor);
+  cursor = gdk_drag_get_cursor (drag, gdk_drag_context_get_selected_action (drag));
+  g_set_object (&drag_win32->cursor, cursor);
 
-  if (gdk_seat_grab (seat, context_win32->ipc_window,
+  if (gdk_seat_grab (seat, drag_win32->ipc_window,
                      capabilities, FALSE,
-                     context_win32->cursor, NULL, NULL, NULL) != GDK_GRAB_SUCCESS)
+                     drag_win32->cursor, NULL, NULL, NULL) != GDK_GRAB_SUCCESS)
     return FALSE;
 
-  g_set_object (&context_win32->grab_seat, seat);
+  g_set_object (&drag_win32->grab_seat, seat);
 
   /* TODO: Should be grabbing keys here, to support keynav. SetWindowsHookEx()? */
 
@@ -2410,24 +2267,30 @@ drag_context_grab (GdkDragContext *context)
 }
 
 static void
-drag_context_ungrab (GdkDragContext *context)
+drag_context_ungrab (GdkDragContext *drag)
 {
-  GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
-  if (!context_win32->grab_seat)
+  GDK_NOTE (DND, g_print ("drag_context_ungrab: 0x%p 0x%p\n",
+                          drag,
+                          drag_win32->grab_seat));
+
+  if (!drag_win32->grab_seat)
     return;
 
-  gdk_seat_ungrab (context_win32->grab_seat);
+  gdk_seat_ungrab (drag_win32->grab_seat);
 
-  g_clear_object (&context_win32->grab_seat);
+  g_clear_object (&drag_win32->grab_seat);
 
   /* TODO: Should be ungrabbing keys here */
 }
 
 static void
-gdk_win32_drag_context_cancel (GdkDragContext      *context,
+gdk_win32_drag_context_cancel (GdkDragContext      *drag,
                                GdkDragCancelReason  reason)
 {
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
+
   const gchar *reason_str = NULL;
   switch (reason)
     {
@@ -2445,185 +2308,119 @@ gdk_win32_drag_context_cancel (GdkDragContext      *context,
       break;
     }
 
-  GDK_NOTE (DND, g_print ("gdk_drag_context_cancel: 0x%p %s\n",
-                          context,
+  GDK_NOTE (DND, g_print ("gdk_win32_drag_context_cancel: 0x%p %s\n",
+                          drag,
                           reason_str));
-  drag_context_ungrab (context);
-  gdk_drag_drop_done (context, FALSE);
+
+  if (drag_win32->protocol == GDK_DRAG_PROTO_LOCAL)
+    {
+      GdkDrop *drop = _gdk_win32_get_drop_for_dest_surface (drag->dest_surface);
+      if (drop)
+        _gdk_win32_local_drop_target_dragleave (drop, GDK_CURRENT_TIME);
+      drop = NULL;
+    }
+
+  gdk_drag_context_set_cursor (drag, NULL);
+  drag_context_ungrab (drag);
+  gdk_drag_drop_done (drag, FALSE);
 }
 
 static void
-gdk_win32_drag_context_drop_performed (GdkDragContext *context,
+gdk_win32_drag_context_drop_performed (GdkDragContext *drag,
                                        guint32         time_)
 {
-  GDK_NOTE (DND, g_print ("gdk_drag_context_drop_performed: 0x%p %u\n",
-                          context,
+  GDK_NOTE (DND, g_print ("gdk_win32_drag_context_drop_performed: 0x%p %u\n",
+                          drag,
                           time_));
-  gdk_drag_drop (context, time_);
-  drag_context_ungrab (context);
+  gdk_drag_drop (drag, time_);
+  gdk_drag_context_set_cursor (drag, NULL);
+  drag_context_ungrab (drag);
 }
 
 #define BIG_STEP 20
 #define SMALL_STEP 1
 
 static void
-gdk_drag_get_current_actions (GdkModifierType  state,
-                              gint             button,
-                              GdkDragAction    actions,
-                              GdkDragAction   *suggested_action,
-                              GdkDragAction   *possible_actions)
+gdk_local_drag_update (GdkDragContext  *drag,
+                       gdouble          x_root,
+                       gdouble          y_root,
+                       DWORD            grfKeyState,
+                       guint32          evtime)
 {
-  *suggested_action = 0;
-  *possible_actions = 0;
-
-  if ((button == GDK_BUTTON_MIDDLE || button == GDK_BUTTON_SECONDARY) && (actions & GDK_ACTION_ASK))
-    {
-      *suggested_action = GDK_ACTION_ASK;
-      *possible_actions = actions;
-    }
-  else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
-    {
-      if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
-        {
-          if (actions & GDK_ACTION_LINK)
-            {
-              *suggested_action = GDK_ACTION_LINK;
-              *possible_actions = GDK_ACTION_LINK;
-            }
-        }
-      else if (state & GDK_CONTROL_MASK)
-        {
-          if (actions & GDK_ACTION_COPY)
-            {
-              *suggested_action = GDK_ACTION_COPY;
-              *possible_actions = GDK_ACTION_COPY;
-            }
-        }
-      else
-        {
-          if (actions & GDK_ACTION_MOVE)
-            {
-              *suggested_action = GDK_ACTION_MOVE;
-              *possible_actions = GDK_ACTION_MOVE;
-            }
-        }
-    }
-  else
-    {
-      *possible_actions = actions;
-
-      if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
-        *suggested_action = GDK_ACTION_ASK;
-      else if (actions & GDK_ACTION_COPY)
-        *suggested_action =  GDK_ACTION_COPY;
-      else if (actions & GDK_ACTION_MOVE)
-        *suggested_action = GDK_ACTION_MOVE;
-      else if (actions & GDK_ACTION_LINK)
-        *suggested_action = GDK_ACTION_LINK;
-    }
-}
-
-static void
-gdk_drag_update (GdkDragContext  *context,
-                 gdouble          x_root,
-                 gdouble          y_root,
-                 GdkModifierType  mods,
-                 guint32          evtime)
-{
-  GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
-  GdkDragAction action, possible_actions;
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
   GdkSurface *dest_surface;
-  GdkDragProtocol protocol;
 
   g_assert (_win32_main_thread == NULL ||
             _win32_main_thread == g_thread_self ());
 
-  gdk_drag_get_current_actions (mods, GDK_BUTTON_PRIMARY, win32_context->actions,
-                                &action, &possible_actions);
-
-  dest_surface = gdk_win32_drag_context_find_surface (context,
-                                                      win32_context->drag_surface,
-                                                      x_root, y_root, &protocol);
+  dest_surface = gdk_win32_drag_context_find_surface (drag,
+                                                      drag_win32->drag_surface,
+                                                      x_root, y_root);
 
-  gdk_win32_drag_context_drag_motion (context, dest_surface, protocol, x_root, y_root,
-                                      action, possible_actions, evtime);
+  gdk_win32_local_drag_motion (drag, dest_surface, x_root, y_root,
+                               gdk_drag_context_get_actions (drag),
+                               grfKeyState, evtime);
 }
 
 static gboolean
-gdk_dnd_handle_motion_event (GdkDragContext       *context,
+gdk_dnd_handle_motion_event (GdkDragContext       *drag,
                              const GdkEventMotion *event)
 {
   GdkModifierType state;
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
+  DWORD key_state;
 
   if (!gdk_event_get_state ((GdkEvent *) event, &state))
     return FALSE;
 
   GDK_NOTE (DND, g_print ("gdk_dnd_handle_motion_event: 0x%p\n",
-                          context));
+                          drag));
 
-  gdk_drag_update (context, event->x_root, event->y_root, state,
-                   gdk_event_get_time ((GdkEvent *) event));
+  if (drag_win32->drag_surface)
+    move_drag_surface (drag, event->x_root, event->y_root);
 
+  key_state = manufacture_keystate_from_GMT (state);
 
-  if (use_ole2_dnd)
+  if (drag_win32->protocol == GDK_DRAG_PROTO_LOCAL)
+    {
+      gdk_local_drag_update (drag, event->x_root, event->y_root, key_state,
+                             gdk_event_get_time ((GdkEvent *) event));
+    }
+  else if (drag_win32->protocol == GDK_DRAG_PROTO_OLE2)
     {
       GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
-      GdkWin32DragContext *context_win32;
-      DWORD key_state = 0;
-      BYTE kbd_state[256];
-
-      /* TODO: is this correct? We get the state at current moment,
-       * not at the moment when the event was emitted.
-       * I don't think that it ultimately serves any purpose,
-       * as our IDropSource does not react to the keyboard
-       * state changes (rather, it reacts to our status updates),
-       * but there's no way to tell what goes inside DoDragDrop(),
-       * so we should send at least *something* that looks right.
-       */
-      API_CALL (GetKeyboardState, (kbd_state));
-
-      if (kbd_state[VK_CONTROL] & 0x80)
-        key_state |= MK_CONTROL;
-      if (kbd_state[VK_SHIFT] & 0x80)
-        key_state |= MK_SHIFT;
-      if (kbd_state[VK_LBUTTON] & 0x80)
-        key_state |= MK_LBUTTON;
-      if (kbd_state[VK_MBUTTON] & 0x80)
-        key_state |= MK_MBUTTON;
-      if (kbd_state[VK_RBUTTON] & 0x80)
-        key_state |= MK_RBUTTON;
 
       GDK_NOTE (DND, g_print ("Post WM_MOUSEMOVE keystate=%lu\n", key_state));
 
-      context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
-
-      context_win32->util_data.last_x = event->x_root;
-      context_win32->util_data.last_y = event->y_root;
+      drag_win32->util_data.last_x = event->x_root;
+      drag_win32->util_data.last_y = event->y_root;
 
       API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id,
                                     WM_MOUSEMOVE,
                                     key_state,
-                                    MAKELPARAM ((event->x_root - _gdk_offset_x) * context_win32->scale,
-                                                (event->y_root - _gdk_offset_y) * context_win32->scale)));
+                                    MAKELPARAM ((event->x_root - _gdk_offset_x) * drag_win32->scale,
+                                                (event->y_root - _gdk_offset_y) * drag_win32->scale)));
     }
 
   return TRUE;
 }
 
 static gboolean
-gdk_dnd_handle_key_event (GdkDragContext    *context,
+gdk_dnd_handle_key_event (GdkDragContext    *drag,
                           const GdkEventKey *event)
 {
-  GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
   GdkModifierType state;
   GdkDevice *pointer;
   gint dx, dy;
 
+  if (!gdk_event_get_state ((GdkEvent *) event, &state))
+    return FALSE;
+
   GDK_NOTE (DND, g_print ("gdk_dnd_handle_key_event: 0x%p\n",
-                          context));
+                          drag));
 
   dx = dy = 0;
-  state = event->state;
   pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
 
   if (event->any.type == GDK_KEY_PRESS)
@@ -2631,7 +2428,7 @@ gdk_dnd_handle_key_event (GdkDragContext    *context,
       switch (event->keyval)
         {
         case GDK_KEY_Escape:
-          gdk_drag_context_cancel (context, GDK_DRAG_CANCEL_USER_CANCELLED);
+          gdk_drag_context_cancel (drag, GDK_DRAG_CANCEL_USER_CANCELLED);
           return TRUE;
 
         case GDK_KEY_space:
@@ -2639,14 +2436,14 @@ gdk_dnd_handle_key_event (GdkDragContext    *context,
         case GDK_KEY_ISO_Enter:
         case GDK_KEY_KP_Enter:
         case GDK_KEY_KP_Space:
-          if ((gdk_drag_context_get_selected_action (context) != 0) &&
-              (gdk_drag_context_get_dest_surface (context) != NULL))
+          if ((gdk_drag_context_get_selected_action (drag) != 0) &&
+              (gdk_drag_context_get_dest_surface (drag) != NULL))
             {
-              g_signal_emit_by_name (context, "drop-performed",
+              g_signal_emit_by_name (drag, "drop-performed",
                                      gdk_event_get_time ((GdkEvent *) event));
             }
           else
-            gdk_drag_context_cancel (context, GDK_DRAG_CANCEL_NO_TARGET);
+            gdk_drag_context_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
 
           return TRUE;
 
@@ -2679,89 +2476,101 @@ gdk_dnd_handle_key_event (GdkDragContext    *context,
 
   if (dx != 0 || dy != 0)
     {
-      win32_context->util_data.last_x += dx;
-      win32_context->util_data.last_y += dy;
-      gdk_device_warp (pointer, win32_context->util_data.last_x, win32_context->util_data.last_y);
+      drag_win32->util_data.last_x += dx;
+      drag_win32->util_data.last_y += dy;
+      gdk_device_warp (pointer, drag_win32->util_data.last_x, drag_win32->util_data.last_y);
     }
 
-  gdk_drag_update (context, win32_context->util_data.last_x, win32_context->util_data.last_y, state,
-                   gdk_event_get_time ((GdkEvent *) event));
+  if (drag_win32->drag_surface)
+    move_drag_surface (drag, drag_win32->util_data.last_x, drag_win32->util_data.last_y);
+
+  if (drag_win32->protocol == GDK_DRAG_PROTO_LOCAL)
+    gdk_local_drag_update (drag, drag_win32->util_data.last_x, drag_win32->util_data.last_y,
+                           manufacture_keystate_from_GMT (state),
+                           gdk_event_get_time ((GdkEvent *) event));
 
   return TRUE;
 }
 
 static gboolean
-gdk_dnd_handle_grab_broken_event (GdkDragContext           *context,
+gdk_dnd_handle_grab_broken_event (GdkDragContext           *drag,
                                   const GdkEventGrabBroken *event)
 {
-  GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
   GDK_NOTE (DND, g_print ("gdk_dnd_handle_grab_broken_event: 0x%p\n",
-                          context));
+                          drag));
 
   /* Don't cancel if we break the implicit grab from the initial button_press.
    * Also, don't cancel if we re-grab on the widget or on our IPC window, for
    * example, when changing the drag cursor.
    */
   if (event->implicit ||
-      event->grab_surface == win32_context->drag_surface ||
-      event->grab_surface == win32_context->ipc_window)
+      event->grab_surface == drag_win32->drag_surface ||
+      event->grab_surface == drag_win32->ipc_window)
     return FALSE;
 
   if (gdk_event_get_device ((GdkEvent *) event) !=
-      gdk_drag_context_get_device (context))
+      gdk_drag_context_get_device (drag))
     return FALSE;
 
-  gdk_drag_context_cancel (context, GDK_DRAG_CANCEL_ERROR);
+  gdk_drag_context_cancel (drag, GDK_DRAG_CANCEL_ERROR);
   return TRUE;
 }
 
 static gboolean
-gdk_dnd_handle_button_event (GdkDragContext       *context,
+gdk_dnd_handle_button_event (GdkDragContext       *drag,
                              const GdkEventButton *event)
 {
   GDK_NOTE (DND, g_print ("gdk_dnd_handle_button_event: 0x%p\n",
-                          context));
+                          drag));
 
 #if 0
   /* FIXME: Check the button matches */
-  if (event->button != win32_context->button)
+  if (event->button != drag_win32->button)
     return FALSE;
 #endif
 
-  if ((gdk_drag_context_get_selected_action (context) != 0))
+  if ((gdk_drag_context_get_selected_action (drag) != 0))
     {
-      g_signal_emit_by_name (context, "drop-performed",
+      g_signal_emit_by_name (drag, "drop-performed",
                              gdk_event_get_time ((GdkEvent *) event));
     }
   else
-    gdk_drag_context_cancel (context, GDK_DRAG_CANCEL_NO_TARGET);
+    gdk_drag_context_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
 
-  return TRUE;
+  /* Make sure GTK gets mouse release button event */
+  return FALSE;
 }
 
 gboolean
-gdk_win32_drag_context_handle_event (GdkDragContext *context,
+gdk_win32_drag_context_handle_event (GdkDragContext *drag,
                                      const GdkEvent *event)
 {
-  GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
-  if (!context->is_source)
+  if (!drag->is_source)
     return FALSE;
-  if (!win32_context->grab_seat)
+  if (!drag_win32->grab_seat)
     return FALSE;
+  if (!drag_win32->handle_events)
+    {
+      /* FIXME: remove this functionality once gtk no longer calls DnD after drag_done() */
+      g_warning ("Got an event %d for drag context %p, even though it's done!", event->any.type, drag);
+      return FALSE;
+    }
 
   switch (event->any.type)
     {
     case GDK_MOTION_NOTIFY:
-      return gdk_dnd_handle_motion_event (context, &event->motion);
+      return gdk_dnd_handle_motion_event (drag, &event->motion);
     case GDK_BUTTON_RELEASE:
-      return gdk_dnd_handle_button_event (context, &event->button);
+      return gdk_dnd_handle_button_event (drag, &event->button);
     case GDK_KEY_PRESS:
     case GDK_KEY_RELEASE:
-      return gdk_dnd_handle_key_event (context, &event->key);
+      return gdk_dnd_handle_key_event (drag, &event->key);
     case GDK_GRAB_BROKEN:
-      return gdk_dnd_handle_grab_broken_event (context, &event->grab_broken);
+      return gdk_dnd_handle_grab_broken_event (drag, &event->grab_broken);
     default:
       break;
     }
@@ -2770,39 +2579,39 @@ gdk_win32_drag_context_handle_event (GdkDragContext *context,
 }
 
 void
-gdk_win32_drag_context_action_changed (GdkDragContext *context,
+gdk_win32_drag_context_action_changed (GdkDragContext *drag,
                                        GdkDragAction   action)
 {
   GdkCursor *cursor;
 
-  cursor = gdk_drag_get_cursor (context, action);
-  gdk_drag_context_set_cursor (context, cursor);
+  cursor = gdk_drag_get_cursor (drag, action);
+  gdk_drag_context_set_cursor (drag, cursor);
 }
 
 static GdkSurface *
-gdk_win32_drag_context_get_drag_surface (GdkDragContext *context)
+gdk_win32_drag_context_get_drag_surface (GdkDragContext *drag)
 {
-  return GDK_WIN32_DRAG_CONTEXT (context)->drag_surface;
+  return GDK_WIN32_DRAG_CONTEXT (drag)->drag_surface;
 }
 
 static void
-gdk_win32_drag_context_set_hotspot (GdkDragContext *context,
+gdk_win32_drag_context_set_hotspot (GdkDragContext *drag,
                                     gint            hot_x,
                                     gint            hot_y)
 {
-  GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+  GdkWin32DragContext *drag_win32 = GDK_WIN32_DRAG_CONTEXT (drag);
 
   GDK_NOTE (DND, g_print ("gdk_drag_context_set_hotspot: 0x%p %d:%d\n",
-                          context,
+                          drag,
                           hot_x, hot_y));
 
-  win32_context->hot_x = hot_x;
-  win32_context->hot_y = hot_y;
+  drag_win32->hot_x = hot_x;
+  drag_win32->hot_y = hot_y;
 
-  if (win32_context->grab_seat)
+  if (drag_win32->grab_seat)
     {
       /* DnD is managed, update current position */
-      move_drag_surface (context, win32_context->util_data.last_x, win32_context->util_data.last_y);
+      move_drag_surface (drag, drag_win32->util_data.last_x, drag_win32->util_data.last_y);
     }
 }
 
diff --git a/gdk/win32/gdkdrop-win32.c b/gdk/win32/gdkdrop-win32.c
index 1bd89cc711..6d7ff78e7a 100644
--- a/gdk/win32/gdkdrop-win32.c
+++ b/gdk/win32/gdkdrop-win32.c
@@ -62,133 +62,161 @@
 #include <gdk/gdk.h>
 #include <glib/gstdio.h>
 
-typedef struct
+#define GDK_TYPE_WIN32_DROP              (gdk_win32_drop_get_type ())
+#define GDK_WIN32_DROP(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WIN32_DROP, 
GdkWin32Drop))
+#define GDK_WIN32_DROP_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WIN32_DROP, 
GdkWin32DropClass))
+#define GDK_IS_WIN32_DROP(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WIN32_DROP))
+#define GDK_IS_WIN32_DROP_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WIN32_DROP))
+#define GDK_WIN32_DROP_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WIN32_DROP, 
GdkWin32DropClass))
+
+typedef struct _GdkWin32Drop GdkWin32Drop;
+typedef struct _GdkWin32DropClass GdkWin32DropClass;
+
+struct _GdkWin32Drop
+{
+  GdkDrop          drop;
+
+  /* The drag protocol being in use */
+  GdkDragProtocol  protocol;
+
+  /* The actions supported at GTK level. Set in gdk_win32_drop_status(). */
+  GdkDragAction    actions;
+
+  guint            scale;          /* Temporarily caches the HiDPI scale */
+  gint             last_x;         /* Coordinates from last event, in GDK space */
+  gint             last_y;
+  DWORD            last_key_state; /* Key state from last event */
+
+  /* Just like GdkDrop->formats, but an array, and with format IDs
+   * stored inside.
+   */
+  GArray          *droptarget_w32format_contentformat_map;
+
+  /* The list from WM_DROPFILES is store here temporarily,
+   * until the next gdk_win32_drop_read_async ()
+   */
+  gchar           *dropfiles_list;
+
+  guint drop_finished : 1; /* FALSE until gdk_drop_finish() is called */
+  guint drop_failed : 1;   /* Whether the drop was unsuccessful */
+};
+
+struct _GdkWin32DropClass
+{
+  GdkDropClass parent_class;
+};
+
+GType    gdk_win32_drop_get_type (void);
+
+G_DEFINE_TYPE (GdkWin32Drop, gdk_win32_drop, GDK_TYPE_DROP)
+
+/* This structure is presented to COM as an object that
+ * implements IDropTarget interface. Every surface that
+ * can be a droptarget has one of these.
+ */
+struct _drop_target_context
 {
   IDropTarget                     idt;
   gint                            ref_count;
-  GdkDragContext                 *context;
-  /* We get this at the object creation time and keep
-   * it indefinitely. Contexts come and go, but this window
-   * remains the same.
+
+  /* The drop object we create when a drag enters our surface.
+   * The drop obj�ct is destroyed when the drag leaves.
    */
-  GdkSurface                     *dest_surface;
+  GdkDrop                        *drop;
+  /* We get this at the object at creation time and keep
+   * it indefinitely. Drops (see above) come and go, but
+   * this surface remains the same.
+   * This is not a reference, as drop_target_context must not
+   * outlive the surface it's attached to.
+   * drop_target_context is not folded into GdkSurfaceImplWin32
+   * only because it's easier to present it to COM as a separate
+   * object when it's allocated separately.
+   */
+  GdkSurface                     *surface;
   /* This is given to us by the OS, we store it here
-   * until the drag leaves our window.
+   * until the drag leaves our window. It is referenced
+   * (using COM reference counting).
    */
   IDataObject                    *data_object;
-} target_drag_context;
-
-static GList *dnd_target_contexts;
-static GdkDragContext *current_dest_drag = NULL;
+};
 
+/* TRUE to use OLE2 protocol, FALSE to use local protocol */
 static gboolean use_ole2_dnd = TRUE;
 
-G_DEFINE_TYPE (GdkWin32DropContext, gdk_win32_drop_context, GDK_TYPE_DRAG_CONTEXT)
-
 static void
-gdk_win32_drop_context_init (GdkWin32DropContext *context)
+gdk_win32_drop_init (GdkWin32Drop *drop)
 {
-  if (!use_ole2_dnd)
-    {
-      dnd_target_contexts = g_list_prepend (dnd_target_contexts, context);
-    }
-  else
-    {
-    }
+  drop->droptarget_w32format_contentformat_map = g_array_new (FALSE, FALSE, sizeof 
(GdkWin32ContentFormatPair));
 
-  GDK_NOTE (DND, g_print ("gdk_drop_context_init %p\n", context));
+  GDK_NOTE (DND, g_print ("gdk_win32_drop_init %p\n", drop));
 }
 
 static void
-gdk_win32_drop_context_finalize (GObject *object)
+gdk_win32_drop_finalize (GObject *object)
 {
-  GdkDragContext *context;
-  GdkWin32DropContext *context_win32;
+  GdkDrop *drop;
+  GdkWin32Drop *drop_win32;
 
-  GDK_NOTE (DND, g_print ("gdk_drop_context_finalize %p\n", object));
+  GDK_NOTE (DND, g_print ("gdk_win32_drop_finalize %p\n", object));
 
-  g_return_if_fail (GDK_IS_WIN32_DROP_CONTEXT (object));
+  drop = GDK_DROP (object);
 
-  context = GDK_DRAG_CONTEXT (object);
-  context_win32 = GDK_WIN32_DROP_CONTEXT (context);
+  drop_win32 = GDK_WIN32_DROP (drop);
 
-  if (!use_ole2_dnd)
-    {
-      dnd_target_contexts = g_list_remove (dnd_target_contexts, context);
-
-      if (context == current_dest_drag)
-        current_dest_drag = NULL;
-    }
-
-  g_clear_object (&context_win32->local_source_context);
+  g_array_unref (drop_win32->droptarget_w32format_contentformat_map);
 
-  g_array_unref (context_win32->droptarget_w32format_contentformat_map);
-
-  G_OBJECT_CLASS (gdk_win32_drop_context_parent_class)->finalize (object);
+  G_OBJECT_CLASS (gdk_win32_drop_parent_class)->finalize (object);
 }
 
-/* Drag Contexts */
-
-static GdkDragContext *
-gdk_drop_context_new (GdkDisplay        *display,
-                      GdkSurface        *source_surface,
-                      GdkSurface        *dest_surface,
-                      GdkContentFormats *formats,
-                      GdkDragAction      actions,
-                      GdkDragProtocol    protocol)
+static GdkDrop *
+gdk_drop_new (GdkDisplay        *display,
+              GdkDevice         *device,
+              GdkDragContext    *drag,
+              GdkContentFormats *formats,
+              GdkSurface        *surface,
+              GdkDragProtocol    protocol)
 {
-  GdkWin32DropContext *context_win32;
+  GdkWin32Drop *drop_win32;
   GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
-  GdkDragContext *context;
 
-  context_win32 = g_object_new (GDK_TYPE_WIN32_DROP_CONTEXT,
-                                "device", gdk_seat_get_pointer (gdk_display_get_default_seat (display)),
-                                "formats", formats,
-                                "surface", dest_surface,
-                                NULL);
-
-  context = GDK_DRAG_CONTEXT (context_win32);
+  drop_win32 = g_object_new (GDK_TYPE_WIN32_DROP,
+                             "device", device,
+                             "drag", drag,
+                             "formats", formats,
+                             "surface", surface,
+                             NULL);
 
   if (win32_display->has_fixed_scale)
-    context_win32->scale = win32_display->surface_scale;
+    drop_win32->scale = win32_display->surface_scale;
   else
-    context_win32->scale = _gdk_win32_display_get_monitor_scale_factor (win32_display, NULL, NULL, NULL);
-
-  context_win32->droptarget_w32format_contentformat_map = g_array_new (FALSE, FALSE, sizeof 
(GdkWin32ContentFormatPair));
+    drop_win32->scale = _gdk_win32_display_get_monitor_scale_factor (win32_display, NULL, NULL, NULL);
 
-  context->is_source = FALSE;
-  g_set_object (&context->source_surface, source_surface);
-  g_set_object (&context->dest_surface, dest_surface);
-  gdk_drag_context_set_actions (context, actions, actions);
-  context_win32->protocol = protocol;
+  drop_win32->protocol = protocol;
 
-  gdk_content_formats_unref (formats);
-
-  return context;
+  return GDK_DROP (drop_win32);
 }
 
-GdkDragContext *
-_gdk_win32_drop_context_find (GdkSurface *source,
-                              GdkSurface *dest)
+/* Gets the GdkDrop that corresponds to a particular GdkSurface.
+ * Will be NULL for surfaces that are not registered as drop targets,
+ * or for surfaces that are currently not under the drag cursor.
+ */
+GdkDrop *
+_gdk_win32_get_drop_for_dest_surface (GdkSurface *dest)
 {
-  GList *tmp_list = dnd_target_contexts;
-  GdkDragContext *context;
+  GdkSurfaceImplWin32 *impl;
 
-  while (tmp_list)
-    {
-      context = (GdkDragContext *) tmp_list->data;
+  if (dest == NULL)
+    return NULL;
 
-      if (!context->is_source &&
-          ((source == NULL) || (context->source_surface && (context->source_surface == source))) &&
-          ((dest == NULL) || (context->dest_surface && (context->dest_surface == dest))))
-        return context;
+  impl = GDK_SURFACE_IMPL_WIN32 (dest->impl);
 
-      tmp_list = tmp_list->next;
-    }
+  if (impl->drop_target != NULL)
+    return impl->drop_target->drop;
 
-  return NULL;
+  return impl->drop;
 }
 
+
 #define PRINT_GUID(guid) \
   g_print ("%.08lx-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
            ((gulong *)  guid)[0], \
@@ -203,21 +231,10 @@ _gdk_win32_drop_context_find (GdkSurface *source,
            ((guchar *)  guid)[14], \
            ((guchar *)  guid)[15]);
 
-/* map windows -> target drag contexts. The table
- * owns a ref to each context.
- */
-static GHashTable* target_ctx_for_window = NULL;
-
-static target_drag_context *
-find_droptarget_for_target_context (GdkDragContext *context)
-{
-  return g_hash_table_lookup (target_ctx_for_window, GDK_SURFACE_HWND (context->dest_surface));
-}
-
 static ULONG STDMETHODCALLTYPE
 idroptarget_addref (LPDROPTARGET This)
 {
-  target_drag_context *ctx = (target_drag_context *) This;
+  drop_target_context *ctx = (drop_target_context *) This;
 
   int ref_count = ++ctx->ref_count;
 
@@ -262,7 +279,7 @@ idroptarget_queryinterface (LPDROPTARGET This,
 static ULONG STDMETHODCALLTYPE
 idroptarget_release (LPDROPTARGET This)
 {
-  target_drag_context *ctx = (target_drag_context *) This;
+  drop_target_context *ctx = (drop_target_context *) This;
 
   int ref_count = --ctx->ref_count;
 
@@ -270,66 +287,24 @@ idroptarget_release (LPDROPTARGET This)
 
   if (ref_count == 0)
     {
-      g_object_unref (ctx->context);
-      g_clear_object (&ctx->dest_surface);
+      g_clear_object (&ctx->drop);
       g_free (This);
     }
 
   return ref_count;
 }
 
-static GdkDragAction
-get_suggested_action (GdkWin32DropContext *win32_context,
-                      DWORD                grfKeyState)
-{
-  /* This is the yucky Windows standard: Force link action if both
-   * Control and Alt are down, copy if Control is down alone, move if
-   * Alt is down alone, or use default of move within the app or copy
-   * when origin of the drag is in another app.
-   */
-  if (grfKeyState & MK_CONTROL && grfKeyState & MK_SHIFT)
-    return GDK_ACTION_LINK; /* Link action not supported */
-  else if (grfKeyState & MK_CONTROL)
-    return GDK_ACTION_COPY;
-  else if (grfKeyState & MK_ALT)
-    return GDK_ACTION_MOVE;
-  else if (win32_context->local_source_context &&
-           win32_context->local_source_context->util_data.state == GDK_WIN32_DND_DRAGGING)
-    return GDK_ACTION_MOVE;
-  else
-    return GDK_ACTION_COPY;
-  /* Any way to determine when to add in DROPEFFECT_SCROLL? */
-}
-
-static DWORD
-drop_effect_for_action (GdkDragAction action)
-{
-  DWORD effect = 0;
-
-  if (action & GDK_ACTION_MOVE)
-    effect |= DROPEFFECT_MOVE;
-  if (action & GDK_ACTION_LINK)
-    effect |= DROPEFFECT_LINK;
-  if (action & GDK_ACTION_COPY)
-    effect |= DROPEFFECT_COPY;
-
-  if (effect == 0)
-    effect = DROPEFFECT_NONE;
-
-  return effect;
-}
-
 static GdkContentFormats *
-query_targets (LPDATAOBJECT  pDataObj,
-               GArray       *format_target_map)
+query_object_formats (LPDATAOBJECT  pDataObj,
+                      GArray       *w32format_contentformat_map)
 {
   IEnumFORMATETC *pfmt = NULL;
   FORMATETC fmt;
-  GList *result = NULL;
   HRESULT hr;
   GdkContentFormatsBuilder *builder;
   GdkContentFormats *result_formats;
-  GList *p;
+
+  builder = gdk_content_formats_builder_new ();
 
   hr = IDataObject_EnumFormatEtc (pDataObj, DATADIR_GET, &pfmt);
 
@@ -349,20 +324,14 @@ query_targets (LPDATAOBJECT  pDataObj,
         GDK_NOTE (DND, g_print ("supported unnamed? source format 0x%x\n", fmt.cfFormat));
 
       g_free (registered_name);
-      _gdk_win32_add_w32format_to_pairs (fmt.cfFormat, format_target_map, &result);
+      _gdk_win32_add_w32format_to_pairs (fmt.cfFormat, w32format_contentformat_map, builder);
       hr = IEnumFORMATETC_Next (pfmt, 1, &fmt, NULL);
     }
 
   if (pfmt)
     IEnumFORMATETC_Release (pfmt);
 
-  builder = gdk_content_formats_builder_new ();
-
-  for (p = g_list_reverse (result); p; p = p->next)
-    gdk_content_formats_builder_add_mime_type (builder, (const gchar *) p->data);
-
   result_formats = gdk_content_formats_builder_free_to_formats (builder);
-  g_list_free (result);
 
   return result_formats;
 }
@@ -379,62 +348,276 @@ set_data_object (LPDATAOBJECT *location, LPDATAOBJECT data_object)
     IDataObject_AddRef (*location);
 }
 
+/* Figures out an action that the user forces onto us by
+ * pressing some modifier keys.
+ */
+static GdkDragAction
+get_user_action (DWORD grfKeyState)
+{
+  /* Windows explorer does this:
+   * 'C'ontrol for 'C'opy
+   * a'L't (or Contro'L' + Shift) for 'L'ink
+   * Shift for Move
+   * Control + Alt or Shift + Alt or Control + Alt + Shift for Default action (see below).
+   *
+   * Default action is 'Copy' when dragging between drives, 'Move' otherwise.
+   * For GTK 'between drives' turns into 'between applications'.
+   */
+  if (((grfKeyState & (MK_CONTROL | MK_ALT)) == (MK_CONTROL | MK_ALT)) ||
+      ((grfKeyState & (MK_ALT | MK_SHIFT)) == (MK_ALT | MK_SHIFT)) ||
+      ((grfKeyState & (MK_CONTROL | MK_ALT | MK_SHIFT)) == (MK_CONTROL | MK_ALT | MK_SHIFT)))
+    {
+      return 0;
+    }
+  else if ((grfKeyState & (MK_CONTROL | MK_SHIFT)) == (MK_CONTROL | MK_SHIFT))
+    return GDK_ACTION_LINK;
+  else if (grfKeyState & MK_CONTROL)
+    return GDK_ACTION_COPY;
+  else if (grfKeyState & MK_ALT)
+    return GDK_ACTION_MOVE;
+
+  return 0;
+}
+
+static DWORD
+drop_effect_for_actions (GdkDragAction actions)
+{
+  DWORD effects = 0;
+  int effect_count = 0;
+
+  if (actions & GDK_ACTION_MOVE)
+    {
+      effects |= DROPEFFECT_MOVE;
+      effect_count++;
+    }
+  if (actions & GDK_ACTION_LINK)
+    {
+      effects |= DROPEFFECT_LINK;
+      effect_count++;
+    }
+  if (actions & GDK_ACTION_COPY)
+    {
+      effects |= DROPEFFECT_COPY;
+      effect_count++;
+    }
+
+  if (effect_count == 0)
+    effects = DROPEFFECT_NONE;
+  else if (effect_count > 1)
+    /* Actually it should be DROPEFECT_ASK, but Windows doesn't support that */
+    effects = DROPEFFECT_COPY;
+
+  return effects;
+}
+
+static GdkDragAction
+actions_for_drop_effects (DWORD effects)
+{
+  GdkDragAction actions = 0;
+
+  if (effects & DROPEFFECT_MOVE)
+    actions |= GDK_ACTION_MOVE;
+  if (effects & DROPEFFECT_LINK)
+    actions |= GDK_ACTION_LINK;
+  if (effects & DROPEFFECT_COPY)
+    actions |= GDK_ACTION_COPY;
+
+  return actions;
+}
+
+static GdkDragAction
+filter_actions (GdkDragAction actions,
+                GdkDragAction filter)
+{
+  return actions & filter;
+}
+
+static GdkDragAction
+set_source_actions_helper (GdkDrop       *drop,
+                           GdkDragAction  actions,
+                           DWORD          grfKeyState)
+{
+  GdkDragAction user_action;
+
+  user_action = get_user_action (grfKeyState);
+
+  if (user_action != 0)
+    gdk_drop_set_actions (drop, user_action);
+  else
+    gdk_drop_set_actions (drop, actions);
+
+  return actions;
+}
+
+void
+_gdk_win32_local_drop_target_dragenter (GdkDragContext *drag,
+                                        GdkSurface     *dest_surface,
+                                        gint            x_root,
+                                        gint            y_root,
+                                        DWORD           grfKeyState,
+                                        guint32         time_,
+                                        GdkDragAction  *actions)
+{
+  GdkDrop *drop;
+  GdkWin32Drop *drop_win32;
+  GdkDisplay *display;
+  GdkDragAction source_actions;
+  GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (dest_surface->impl);
+
+  GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_dragenter %p @ %d : %d"
+                          " for dest window 0x%p"
+                          ". actions = %s\n",
+                          drag, x_root, y_root,
+                          dest_surface,
+                          _gdk_win32_drag_action_to_string (*actions)));
+
+  display = gdk_surface_get_display (dest_surface);
+  drop = gdk_drop_new (display,
+                       gdk_seat_get_pointer (gdk_display_get_default_seat (display)),
+                       drag,
+                       gdk_content_formats_ref (gdk_drag_context_get_formats (drag)),
+                       dest_surface,
+                       GDK_DRAG_PROTO_LOCAL);
+  drop_win32 = GDK_WIN32_DROP (drop);
+
+  impl->drop = drop;
+
+  source_actions = set_source_actions_helper (drop, *actions, grfKeyState);
+
+  gdk_drop_emit_enter_event (drop, TRUE, time_);
+  gdk_drop_emit_motion_event (drop, TRUE, x_root, y_root, time_);
+  drop_win32->last_key_state = grfKeyState;
+  drop_win32->last_x = x_root;
+  drop_win32->last_y = y_root;
+  *actions = filter_actions (drop_win32->actions, source_actions);
+
+  GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_dragenter returns with actions %s\n",
+                          _gdk_win32_drag_action_to_string (*actions)));
+}
+
+/* The pdwEffect here initially points
+ * to a DWORD that contains the value of dwOKEffects argument in DoDragDrop,
+ * i.e. the drag action that the drag source deems acceptable.
+ * On return it should point to the effect value that denotes the
+ * action that is going to happen on drop, and that is what DoDragDrop will
+ * put into the DWORD that pdwEffect was pointing to.
+ */
 static HRESULT STDMETHODCALLTYPE
 idroptarget_dragenter (LPDROPTARGET This,
                        LPDATAOBJECT pDataObj,
                        DWORD        grfKeyState,
                        POINTL       pt,
-                       LPDWORD      pdwEffect)
+                       LPDWORD      pdwEffect_and_dwOKEffects)
 {
-  target_drag_context *ctx = (target_drag_context *) This;
-  GdkDragContext *context;
-  GdkWin32DropContext *context_win32;
+  drop_target_context *ctx = (drop_target_context *) This;
+  GdkDrop *drop;
+  GdkWin32Drop *drop_win32;
   GdkDisplay *display;
   gint pt_x;
   gint pt_y;
-  GdkDragContext *source_context;
-
-  GDK_NOTE (DND, g_print ("idroptarget_dragenter %p @ %ld : %ld for dest window 0x%p S_OK\n", This, pt.x, 
pt.y, ctx->dest_surface));
-
-  g_clear_object (&ctx->context);
-
-  source_context = _gdk_win32_find_source_context_for_dest_surface (ctx->dest_surface);
-
-  display = gdk_surface_get_display (ctx->dest_surface);
-  context = gdk_drop_context_new (display,
-                                  /* OLE2 DnD does not allow us to get the source window,
-                                   * but we *can* find it if it's ours. This is needed to
-                                   * support DnD within the same widget, for example.
-                                   */
-                                  source_context ? source_context->source_surface : NULL,
-                                  ctx->dest_surface,
-                                  query_targets (pDataObj, 
context_win32->droptarget_w32format_contentformat_map),
-                                  GDK_ACTION_COPY | GDK_ACTION_MOVE,
-                                  GDK_DRAG_PROTO_OLE2);
-  context_win32 = GDK_WIN32_DROP_CONTEXT (context);
-  g_array_set_size (context_win32->droptarget_w32format_contentformat_map, 0);
-  g_set_object (&context_win32->local_source_context, GDK_WIN32_DRAG_CONTEXT (source_context));
-
-  ctx->context = context;
-  context->action = GDK_ACTION_MOVE;
-  gdk_drag_context_set_actions (context, 
-                                GDK_ACTION_COPY | GDK_ACTION_MOVE,
-                                get_suggested_action (context_win32, grfKeyState));
-  set_data_object (&ctx->data_object, pDataObj);
-  pt_x = pt.x / context_win32->scale + _gdk_offset_x;
-  pt_y = pt.y / context_win32->scale + _gdk_offset_y;
-  gdk_drop_emit_enter_event (GDK_DROP (context), TRUE, GDK_CURRENT_TIME);
-  gdk_drop_emit_motion_event (GDK_DROP (context), TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
-  context_win32->last_key_state = grfKeyState;
-  context_win32->last_x = pt_x;
-  context_win32->last_y = pt_y;
-  *pdwEffect = drop_effect_for_action (context->action);
+  GdkDragContext *drag;
+  GdkDragAction source_actions;
+  GdkDragAction dest_actions;
+
+  GDK_NOTE (DND, g_print ("idroptarget_dragenter %p @ %ld : %ld"
+                          " for dest window 0x%p"
+                          ". dwOKEffects = %lu\n",
+                          This, pt.x, pt.y,
+                          ctx->surface,
+                          *pdwEffect_and_dwOKEffects));
+
+  g_clear_object (&ctx->drop);
+
+  /* Try to find the GdkDragContext object for this DnD operation,
+   * if it originated in our own application.
+   */
+  drag = _gdk_win32_find_drag_for_dest_surface (ctx->surface);
+
+  display = gdk_surface_get_display (ctx->surface);
+  drop = gdk_drop_new (display,
+                       gdk_seat_get_pointer (gdk_display_get_default_seat (display)),
+                       drag,
+                       query_object_formats (pDataObj, NULL),
+                       ctx->surface,
+                       GDK_DRAG_PROTO_OLE2);
+  drop_win32 = GDK_WIN32_DROP (drop);
+  g_array_set_size (drop_win32->droptarget_w32format_contentformat_map, 0);
+  gdk_content_formats_unref (query_object_formats (pDataObj, 
drop_win32->droptarget_w32format_contentformat_map));
 
-  GDK_NOTE (DND, g_print ("idroptarget_dragenter returns with action %d and drop effect %lu\n", 
context->action, *pdwEffect));
+  ctx->drop = drop;
+
+  source_actions = set_source_actions_helper (drop,
+                                              actions_for_drop_effects (*pdwEffect_and_dwOKEffects),
+                                              grfKeyState);
+
+  set_data_object (&ctx->data_object, pDataObj);
+  pt_x = pt.x / drop_win32->scale + _gdk_offset_x;
+  pt_y = pt.y / drop_win32->scale + _gdk_offset_y;
+  gdk_drop_emit_enter_event (drop, TRUE, GDK_CURRENT_TIME);
+  gdk_drop_emit_motion_event (drop, TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
+  drop_win32->last_key_state = grfKeyState;
+  drop_win32->last_x = pt_x;
+  drop_win32->last_y = pt_y;
+  dest_actions = filter_actions (drop_win32->actions, source_actions);
+  *pdwEffect_and_dwOKEffects = drop_effect_for_actions (dest_actions);
+
+  GDK_NOTE (DND, g_print ("idroptarget_dragenter returns S_OK with actions %s"
+                          " and drop effect %lu\n",
+                          _gdk_win32_drag_action_to_string (dest_actions),
+                          *pdwEffect_and_dwOKEffects));
 
   return S_OK;
 }
 
+gboolean
+_gdk_win32_local_drop_target_will_emit_motion (GdkDrop *drop,
+                                               gint     x_root,
+                                               gint     y_root,
+                                               DWORD    grfKeyState)
+{
+  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (drop);
+
+  if (x_root != drop_win32->last_x ||
+      y_root != drop_win32->last_y ||
+      grfKeyState != drop_win32->last_key_state)
+    return TRUE;
+
+  return FALSE;
+}
+
+void
+_gdk_win32_local_drop_target_dragover (GdkDrop        *drop,
+                                       GdkDragContext *drag,
+                                       gint            x_root,
+                                       gint            y_root,
+                                       DWORD           grfKeyState,
+                                       guint32         time_,
+                                       GdkDragAction  *actions)
+{
+  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (drop);
+  GdkDragAction source_actions;
+
+  source_actions = set_source_actions_helper (drop, *actions, grfKeyState);
+
+  GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_dragover %p @ %d : %d"
+                          ", actions = %s\n",
+                          drop, x_root, y_root,
+                          _gdk_win32_drag_action_to_string (*actions)));
+
+  if (_gdk_win32_local_drop_target_will_emit_motion (drop, x_root, y_root, grfKeyState))
+    {
+      gdk_drop_emit_motion_event (drop, TRUE, x_root, y_root, time_);
+      drop_win32->last_key_state = grfKeyState;
+      drop_win32->last_x = x_root;
+      drop_win32->last_y = y_root;
+    }
+
+  *actions = filter_actions (drop_win32->actions, source_actions);
+
+  GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_dragover returns with actions %s\n",
+                          _gdk_win32_drag_action_to_string (*actions)));
+}
+
 /* NOTE: This method is called continuously, even if nothing is
  * happening, as long as the drag operation is in progress and
  * the cursor is above our window.
@@ -449,88 +632,151 @@ static HRESULT STDMETHODCALLTYPE
 idroptarget_dragover (LPDROPTARGET This,
                       DWORD        grfKeyState,
                       POINTL       pt,
-                      LPDWORD      pdwEffect)
+                      LPDWORD      pdwEffect_and_dwOKEffects)
 {
-  target_drag_context *ctx = (target_drag_context *) This;
-  GdkWin32DropContext *context_win32 = GDK_WIN32_DROP_CONTEXT (ctx->context);
-  gint pt_x = pt.x / context_win32->scale + _gdk_offset_x;
-  gint pt_y = pt.y / context_win32->scale + _gdk_offset_y;
-
-  gdk_drag_context_set_actions (ctx->context,
-                                gdk_drag_context_get_actions (ctx->context),
-                                get_suggested_action (context_win32, grfKeyState));
-
-  GDK_NOTE (DND, g_print ("idroptarget_dragover %p @ %d : %d (raw %ld : %ld), suggests %d action S_OK\n", 
This, pt_x, pt_y, pt.x, pt.y, gdk_drag_context_get_suggested_action (ctx->context)));
-
-  if (pt_x != context_win32->last_x ||
-      pt_y != context_win32->last_y ||
-      grfKeyState != context_win32->last_key_state)
+  drop_target_context *ctx = (drop_target_context *) This;
+  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (ctx->drop);
+  gint pt_x = pt.x / drop_win32->scale + _gdk_offset_x;
+  gint pt_y = pt.y / drop_win32->scale + _gdk_offset_y;
+  GdkDragAction source_actions;
+  GdkDragAction dest_actions;
+
+  source_actions = set_source_actions_helper (ctx->drop,
+                                              actions_for_drop_effects (*pdwEffect_and_dwOKEffects),
+                                              grfKeyState);
+
+  GDK_NOTE (DND, g_print ("idroptarget_dragover %p @ %d : %d"
+                          " (raw %ld : %ld)"
+                          ", dwOKEffects = %lu"
+                          ", suggests %d action\n",
+                          This, pt_x, pt_y,
+                          pt.x, pt.y,
+                          *pdwEffect_and_dwOKEffects,
+                          source_actions));
+
+  if (pt_x != drop_win32->last_x ||
+      pt_y != drop_win32->last_y ||
+      grfKeyState != drop_win32->last_key_state)
     {
-      gdk_drop_emit_motion_event (GDK_DROP (ctx->context), TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
-      context_win32->last_key_state = grfKeyState;
-      context_win32->last_x = pt_x;
-      context_win32->last_y = pt_y;
+      gdk_drop_emit_motion_event (ctx->drop, TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
+      drop_win32->last_key_state = grfKeyState;
+      drop_win32->last_x = pt_x;
+      drop_win32->last_y = pt_y;
     }
 
-  *pdwEffect = drop_effect_for_action (ctx->context->action);
+  dest_actions = filter_actions (drop_win32->actions, source_actions);
+  *pdwEffect_and_dwOKEffects = drop_effect_for_actions (dest_actions);
 
-  GDK_NOTE (DND, g_print ("idroptarget_dragover returns with action %d and effect %lu\n", 
ctx->context->action, *pdwEffect));
+  GDK_NOTE (DND, g_print ("idroptarget_dragover returns S_OK with actions %s"
+                          " and effect %lu\n",
+                          _gdk_win32_drag_action_to_string (dest_actions),
+                          *pdwEffect_and_dwOKEffects));
 
   return S_OK;
 }
 
+void
+_gdk_win32_local_drop_target_dragleave (GdkDrop *drop,
+                                        guint32  time_)
+{
+  GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (gdk_drop_get_surface (drop)->impl);
+  GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_dragleave %p\n", drop));
+
+  gdk_drop_emit_leave_event (drop, TRUE, time_);
+
+  g_clear_object (&impl->drop);
+}
+
 static HRESULT STDMETHODCALLTYPE
 idroptarget_dragleave (LPDROPTARGET This)
 {
-  target_drag_context *ctx = (target_drag_context *) This;
+  drop_target_context *ctx = (drop_target_context *) This;
 
   GDK_NOTE (DND, g_print ("idroptarget_dragleave %p S_OK\n", This));
 
-  gdk_drop_emit_leave_event (GDK_DROP (ctx->context), TRUE, GDK_CURRENT_TIME);
+  gdk_drop_emit_leave_event (GDK_DROP (ctx->drop), TRUE, GDK_CURRENT_TIME);
 
-  g_clear_object (&ctx->context);
+  g_clear_object (&ctx->drop);
   set_data_object (&ctx->data_object, NULL);
 
   return S_OK;
 }
 
+void
+_gdk_win32_local_drop_target_drop (GdkDrop        *drop,
+                                   GdkDragContext *drag,
+                                   guint32         time_,
+                                   GdkDragAction  *actions)
+{
+  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (drop);
+
+  GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_drop %p ", drop));
+
+  set_source_actions_helper (drop,
+                             *actions,
+                             drop_win32->last_key_state);
+
+  drop_win32->drop_finished = FALSE;
+  gdk_drop_emit_drop_event (drop, TRUE, drop_win32->last_x, drop_win32->last_y, time_);
+
+  while (!drop_win32->drop_finished)
+    g_main_context_iteration (NULL, FALSE);
+
+  /* Notify local source of the DnD result
+   * Special case:
+   * drop_win32->actions is guaranteed to contain 1 action after gdk_drop_finish ()
+   */
+  *actions = drop_win32->actions;
+
+  GDK_NOTE (DND, g_print ("drop with action %s\n", _gdk_win32_drag_action_to_string (*actions)));
+}
+
 static HRESULT STDMETHODCALLTYPE
 idroptarget_drop (LPDROPTARGET This,
                   LPDATAOBJECT pDataObj,
                   DWORD        grfKeyState,
                   POINTL       pt,
-                  LPDWORD      pdwEffect)
+                  LPDWORD      pdwEffect_and_dwOKEffects)
 {
-  target_drag_context *ctx = (target_drag_context *) This;
-  GdkWin32DropContext *context_win32 = GDK_WIN32_DROP_CONTEXT (ctx->context);
-  GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
-  gint pt_x = pt.x / context_win32->scale + _gdk_offset_x;
-  gint pt_y = pt.y / context_win32->scale + _gdk_offset_y;
+  drop_target_context *ctx = (drop_target_context *) This;
+  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (ctx->drop);
+  gint pt_x = pt.x / drop_win32->scale + _gdk_offset_x;
+  gint pt_y = pt.y / drop_win32->scale + _gdk_offset_y;
+  GdkDragAction dest_action;
 
   GDK_NOTE (DND, g_print ("idroptarget_drop %p ", This));
 
   if (pDataObj == NULL)
     {
       GDK_NOTE (DND, g_print ("E_POINTER\n"));
-      g_clear_object (&ctx->context);
+      gdk_drop_emit_leave_event (ctx->drop, TRUE, GDK_CURRENT_TIME);
+      g_clear_object (&ctx->drop);
       set_data_object (&ctx->data_object, NULL);
 
       return E_POINTER;
     }
 
-  gdk_drag_context_set_actions (ctx->context,
-                                gdk_drag_context_get_actions (ctx->context),
-                                get_suggested_action (context_win32, grfKeyState));
+  set_source_actions_helper (ctx->drop,
+                             actions_for_drop_effects (*pdwEffect_and_dwOKEffects),
+                             grfKeyState);
 
-  gdk_drop_emit_drop_event (GDK_DROP (ctx->context), TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
+  drop_win32->drop_finished = FALSE;
+  gdk_drop_emit_drop_event (ctx->drop, TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
 
-  /* Notify OLE of copy or move */
-  *pdwEffect = drop_effect_for_action (ctx->context->action);
+  while (!drop_win32->drop_finished)
+    g_main_context_iteration (NULL, FALSE);
+
+  /* Notify OLE of the DnD result
+   * Special case:
+   * drop_win32->actions is guaranteed to contain 1 action after gdk_drop_finish ()
+   */
+  dest_action = drop_win32->actions;
+  *pdwEffect_and_dwOKEffects = drop_effect_for_actions (dest_action);
 
-  g_clear_object (&ctx->context);
+  g_clear_object (&ctx->drop);
   set_data_object (&ctx->data_object, NULL);
 
-  GDK_NOTE (DND, g_print ("drop S_OK with effect %lx\n", *pdwEffect));
+  GDK_NOTE (DND, g_print ("drop S_OK with effect %lx\n", *pdwEffect_and_dwOKEffects));
 
   return S_OK;
 }
@@ -545,20 +791,20 @@ static IDropTargetVtbl idt_vtbl = {
   idroptarget_drop
 };
 
-static target_drag_context *
+static drop_target_context *
 target_context_new (GdkSurface *window)
 {
-  target_drag_context *result;
+  drop_target_context *result;
 
-  result = g_new0 (target_drag_context, 1);
+  result = g_new0 (drop_target_context, 1);
   result->idt.lpVtbl = &idt_vtbl;
   result->ref_count = 0;
 
-  result->dest_surface = g_object_ref (window);
+  result->surface = window;
 
   idroptarget_addref (&result->idt);
 
-  GDK_NOTE (DND, g_print ("target_context_new: %p (window %p)\n", result, result->dest_surface));
+  GDK_NOTE (DND, g_print ("target_context_new: %p (window %p)\n", result, result->surface));
 
   return result;
 }
@@ -685,8 +931,8 @@ gdk_dropfiles_filter (GdkWin32Display *display,
                       gpointer         data)
 {
   GdkSurface *window;
-  GdkDragContext *context;
-  GdkWin32DropContext *context_win32;
+  GdkDrop *drop;
+  GdkWin32Drop *drop_win32;
   GString *result;
   HANDLE hdrop;
   POINT pt;
@@ -696,190 +942,167 @@ gdk_dropfiles_filter (GdkWin32Display *display,
   if (msg->message != WM_DROPFILES)
     return GDK_WIN32_MESSAGE_FILTER_CONTINUE;
 
-      GDK_NOTE (DND, g_print ("WM_DROPFILES: %p\n", msg->hwnd));
+  GDK_NOTE (DND, g_print ("WM_DROPFILES: %p\n", msg->hwnd));
 
-      window = gdk_win32_handle_table_lookup (msg->hwnd);
+  window = gdk_win32_handle_table_lookup (msg->hwnd);
 
-      context = gdk_drop_context_new (GDK_DISPLAY (display),
-                                      NULL,
-                                      window,
-                                      gdk_content_formats_new ((const char *[2]) {
-                                                                 "text/uri-list",
-                                                                 NULL
-                                                               }, 1),
-                                      GDK_ACTION_COPY,
-                                      GDK_DRAG_PROTO_WIN32_DROPFILES);
-      context_win32 = GDK_WIN32_DROP_CONTEXT (context);
-      /* WM_DROPFILES drops are always file names */
+  drop = gdk_drop_new (GDK_DISPLAY (display),
+                       gdk_seat_get_pointer (gdk_display_get_default_seat (GDK_DISPLAY (display))),
+                       NULL,
+                       /* WM_DROPFILES drops are always file names */
+                       gdk_content_formats_new ((const char *[2]) {
+                                                  "text/uri-list",
+                                                  NULL
+                                                }, 1),
+                       window,
+                       GDK_DRAG_PROTO_WIN32_DROPFILES);
+  drop_win32 = GDK_WIN32_DROP (drop);
 
-      gdk_drag_context_set_actions (context, GDK_ACTION_COPY, GDK_ACTION_COPY);
-      current_dest_drag = context;
+  gdk_drop_set_actions (drop, GDK_ACTION_COPY);
 
-      hdrop = (HANDLE) msg->wParam;
-      DragQueryPoint (hdrop, &pt);
-      ClientToScreen (msg->hwnd, &pt);
+  hdrop = (HANDLE) msg->wParam;
+  DragQueryPoint (hdrop, &pt);
+  ClientToScreen (msg->hwnd, &pt);
 
-      nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
+  nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
 
-      result = g_string_new (NULL);
-      for (i = 0; i < nfiles; i++)
-        {
-          gchar *uri;
-          wchar_t wfn[MAX_PATH];
+  result = g_string_new (NULL);
+  for (i = 0; i < nfiles; i++)
+    {
+      gchar *uri;
+      wchar_t wfn[MAX_PATH];
 
-          DragQueryFileW (hdrop, i, wfn, MAX_PATH);
-          fileName = g_utf16_to_utf8 (wfn, -1, NULL, NULL, NULL);
+      DragQueryFileW (hdrop, i, wfn, MAX_PATH);
+      fileName = g_utf16_to_utf8 (wfn, -1, NULL, NULL, NULL);
 
-          /* Resolve shortcuts */
-          if (resolve_link (msg->hwnd, wfn, &linkedFile))
+      /* Resolve shortcuts */
+      if (resolve_link (msg->hwnd, wfn, &linkedFile))
+        {
+          uri = g_filename_to_uri (linkedFile, NULL, NULL);
+          if (uri != NULL)
             {
-              uri = g_filename_to_uri (linkedFile, NULL, NULL);
-              if (uri != NULL)
-                {
-                  g_string_append (result, uri);
-                  GDK_NOTE (DND, g_print ("... %s link to %s: %s\n",
-                                          fileName, linkedFile, uri));
-                  g_free (uri);
-                }
-              g_free (fileName);
-              fileName = linkedFile;
+              g_string_append (result, uri);
+              GDK_NOTE (DND, g_print ("... %s link to %s: %s\n",
+                                      fileName, linkedFile, uri));
+              g_free (uri);
             }
-          else
+          g_free (fileName);
+          fileName = linkedFile;
+        }
+      else
+        {
+          uri = g_filename_to_uri (fileName, NULL, NULL);
+          if (uri != NULL)
             {
-              uri = g_filename_to_uri (fileName, NULL, NULL);
-              if (uri != NULL)
-                {
-                  g_string_append (result, uri);
-                  GDK_NOTE (DND, g_print ("... %s: %s\n", fileName, uri));
-                  g_free (uri);
-                }
+              g_string_append (result, uri);
+              GDK_NOTE (DND, g_print ("... %s: %s\n", fileName, uri));
+              g_free (uri);
             }
+        }
 
 #if 0
-          /* Awful hack to recognize temp files corresponding to
-           * images dragged from Firefox... Open the file right here
-           * so that it is less likely that Firefox manages to delete
-           * it before the GTK+-using app (typically GIMP) has opened
-           * it.
-           *
-           * Not compiled in for now, because it means images dragged
-           * from Firefox would stay around in the temp folder which
-           * is not what Firefox intended. I don't feel comfortable
-           * with that, both from a geenral sanity point of view, and
-           * from a privacy point of view. It's better to wait for
-           * Firefox to fix the problem, for instance by deleting the
-           * temp file after a longer delay, or to wait until we
-           * implement the OLE2_DND...
-           */
-          if (filename_looks_tempish (fileName))
+      /* Awful hack to recognize temp files corresponding to
+       * images dragged from Firefox... Open the file right here
+       * so that it is less likely that Firefox manages to delete
+       * it before the GTK+-using app (typically GIMP) has opened
+       * it.
+       *
+       * Not compiled in for now, because it means images dragged
+       * from Firefox would stay around in the temp folder which
+       * is not what Firefox intended. I don't feel comfortable
+       * with that, both from a geenral sanity point of view, and
+       * from a privacy point of view. It's better to wait for
+       * Firefox to fix the problem, for instance by deleting the
+       * temp file after a longer delay, or to wait until we
+       * implement the OLE2_DND...
+       */
+      if (filename_looks_tempish (fileName))
+        {
+          int fd = g_open (fileName, _O_RDONLY|_O_BINARY, 0);
+          if (fd == -1)
             {
-              int fd = g_open (fileName, _O_RDONLY|_O_BINARY, 0);
-              if (fd == -1)
-                {
-                  GDK_NOTE (DND, g_print ("Could not open %s, maybe an image dragged from Firefox that it 
already deleted\n", fileName));
-                }
-              else
-                {
-                  GDK_NOTE (DND, g_print ("Opened %s as %d so that Firefox won't delete it\n", fileName, 
fd));
-                  g_timeout_add_seconds (1, close_it, GINT_TO_POINTER (fd));
-                }
+              GDK_NOTE (DND, g_print ("Could not open %s, maybe an image dragged from Firefox that it 
already deleted\n", fileName));
             }
+          else
+            {
+              GDK_NOTE (DND, g_print ("Opened %s as %d so that Firefox won't delete it\n", fileName, fd));
+              g_timeout_add_seconds (1, close_it, GINT_TO_POINTER (fd));
+            }
+        }
 #endif
 
-          g_free (fileName);
-          g_string_append (result, "\015\012");
-        }
+      g_free (fileName);
+      g_string_append (result, "\015\012");
+    }
 
-      /* FIXME: this call is currently a no-op, but it should
-       * stash the string somewhere, and later produce it,
-       * maybe in response to gdk_win32_drop_context_read_async()?
-       */
-      _gdk_dropfiles_store (result->str);
-      g_string_free (result, FALSE);
+  g_clear_pointer (&drop_win32->dropfiles_list, g_free);
+  drop_win32->dropfiles_list = result->str;
+  g_string_free (result, FALSE);
+  if (drop_win32->dropfiles_list == NULL)
+    drop_win32->dropfiles_list = g_strdup ("");
 
-      gdk_drop_emit_drop_event (GDK_DROP (context),
-                                FALSE, 
-                                pt.x / context_win32->scale + _gdk_offset_x,
-                                pt.y / context_win32->scale + _gdk_offset_y,
-                                _gdk_win32_get_next_tick (msg->time));
+  gdk_drop_emit_drop_event (drop,
+                            FALSE, 
+                            pt.x / drop_win32->scale + _gdk_offset_x,
+                            pt.y / drop_win32->scale + _gdk_offset_y,
+                            _gdk_win32_get_next_tick (msg->time));
 
-      DragFinish (hdrop);
+  DragFinish (hdrop);
 
   *ret_valp = 0;
 
   return GDK_WIN32_MESSAGE_FILTER_REMOVE;
 }
 
-/* Destination side */
-
 static void
-gdk_win32_drop_context_status (GdkDrop       *drop,
-                               GdkDragAction  action)
+gdk_win32_drop_status (GdkDrop       *drop,
+                       GdkDragAction  actions)
 {
-  GdkDragContext *context = GDK_DRAG_CONTEXT (drop);
-  GdkDragContext *src_context;
+  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (drop);
+  GdkDragContext *drag;
 
-  g_return_if_fail (context != NULL);
+  g_return_if_fail (drop != NULL);
 
-  GDK_NOTE (DND, g_print ("gdk_drag_status: %s\n"
-                          " context=%p:{actions=%s,suggested=%s,action=%s}\n",
-                          _gdk_win32_drag_action_to_string (action),
-                          context,
-                          _gdk_win32_drag_action_to_string (gdk_drag_context_get_actions (context)),
-                          _gdk_win32_drag_action_to_string (gdk_drag_context_get_suggested_action (context)),
-                          _gdk_win32_drag_action_to_string (context->action)));
+  GDK_NOTE (DND, g_print ("gdk_win32_drop_status: %s\n"
+                          " context=%p:{source_actions=%s}\n",
+                          _gdk_win32_drag_action_to_string (actions),
+                          drop,
+                          _gdk_win32_drag_action_to_string (gdk_drop_get_actions (drop))));
 
-  context->action = action;
+  drop_win32->actions = actions;
 
-  if (!use_ole2_dnd)
-    {
-      src_context = _gdk_win32_drag_context_find (context->source_surface,
-                                                  context->dest_surface);
+  if (drop_win32->protocol == GDK_DRAG_PROTO_OLE2)
+    return;
 
-      if (src_context)
-        {
-          _gdk_win32_drag_context_send_local_status_event (src_context, action);
-        }
-    }
+  drag = gdk_drop_get_drag (drop);
+
+  if (drag != NULL)
+    _gdk_win32_local_drag_give_feedback (drag, actions);
 }
 
 static void
-gdk_win32_drop_context_finish (GdkDrop       *drop,
-                               GdkDragAction  action)
+gdk_win32_drop_finish (GdkDrop       *drop,
+                       GdkDragAction  action)
 {
-  GdkDragContext *context = GDK_DRAG_CONTEXT (drop);
-  GdkDragContext *src_context;
-  GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
-
-  g_return_if_fail (context != NULL);
+  GdkDragContext *drag;
+  GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (drop);
 
-  GDK_NOTE (DND, g_print ("gdk_drag_finish\n"));
+  g_return_if_fail (drop != NULL);
 
-  if (context->action != action)
-    gdk_win32_drop_context_status (context, action);
+  GDK_NOTE (DND, g_print ("gdk_win32_drop_finish with action %s\n",
+                          _gdk_win32_drag_action_to_string (action)));
 
-  if (!use_ole2_dnd)
-    {
-      src_context = _gdk_win32_drag_context_find (context->source_surface,
-                                                  context->dest_surface);
-      if (src_context)
-        {
-          GDK_NOTE (DND, g_print ("gdk_dnd_handle_drop_finihsed: 0x%p\n",
-                                  context));
+  drop_win32->actions = action;
+  drop_win32->drop_finished = TRUE;
 
-          g_signal_emit_by_name (context, "dnd-finished");
-          gdk_drag_drop_done (context, !GDK_WIN32_DROP_CONTEXT (context)->drop_failed);
-        }
-    }
-  else
-    {
-      _gdk_win32_drag_do_leave (context, GDK_CURRENT_TIME);
+  if (drop_win32->protocol == GDK_DRAG_PROTO_OLE2)
+    return;
+/* FIXME: remove?
+  drag = gdk_drop_get_drag (drop);
 
-      if (action)
-        clipdrop->dnd_target_state = GDK_WIN32_DND_DROPPED;
-      else
-        clipdrop->dnd_target_state = GDK_WIN32_DND_FAILED;
-    }
+  if (drag != NULL)
+    _gdk_win32_local_drag_context_drop_response (drag, action);
+*/
 }
 
 #if 0
@@ -910,7 +1133,7 @@ gdk_destroy_filter (GdkXEvent *xev,
 void
 _gdk_win32_surface_register_dnd (GdkSurface *window)
 {
-  target_drag_context *ctx;
+  drop_target_context *ctx;
   HRESULT hr;
 
   g_return_if_fail (window != NULL);
@@ -920,7 +1143,7 @@ _gdk_win32_surface_register_dnd (GdkSurface *window)
   else
     g_object_set_data (G_OBJECT (window), "gdk-dnd-registered", GINT_TO_POINTER (TRUE));
 
-  GDK_NOTE (DND, g_print ("gdk_surface_register_dnd: %p\n", GDK_SURFACE_HWND (window)));
+  GDK_NOTE (DND, g_print ("gdk_win32_surface_register_dnd: %p\n", GDK_SURFACE_HWND (window)));
 
   if (!use_ole2_dnd)
     {
@@ -929,13 +1152,15 @@ _gdk_win32_surface_register_dnd (GdkSurface *window)
        * whether the window (widget) in question actually accepts files
        * (in gtk, data of type text/uri-list) or not.
        */
-      gdk_win32_display_add_filter (gdk_display_get_default (), gdk_dropfiles_filter, NULL);
+      gdk_win32_display_add_filter (GDK_WIN32_DISPLAY (gdk_display_get_default ()), gdk_dropfiles_filter, 
NULL);
       DragAcceptFiles (GDK_SURFACE_HWND (window), TRUE);
     }
   else
     {
+      GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl);
+
       /* Return if window is already setup for DND. */
-      if (g_hash_table_lookup (target_ctx_for_window, GDK_SURFACE_HWND (window)) != NULL)
+      if (impl->drop_target != NULL)
         return;
 
       ctx = target_context_new (window);
@@ -955,13 +1180,21 @@ _gdk_win32_surface_register_dnd (GdkSurface *window)
             OTHER_API_FAILED ("RegisterDragDrop");
           else
             {
-              g_object_ref (window);
-              g_hash_table_insert (target_ctx_for_window, GDK_SURFACE_HWND (window), ctx);
+              impl->drop_target = ctx;
             }
         }
     }
 }
 
+void
+_gdk_win32_surface_unregister_dnd (GdkSurface *window)
+{
+  GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl);
+
+  if (impl->drop_target)
+    idroptarget_release (&impl->drop_target->idt);
+}
+
 static gpointer
 grab_data_from_hdata (GTask  *task,
                       HANDLE  hdata,
@@ -1009,16 +1242,16 @@ grab_data_from_hdata (GTask  *task,
 }
 
 static void
-gdk_win32_drop_context_read_async (GdkDrop             *drop,
-                                   GdkContentFormats   *formats,
-                                   int                  io_priority,
-                                   GCancellable        *cancellable,
-                                   GAsyncReadyCallback  callback,
-                                   gpointer             user_data)
+gdk_win32_drop_read_async (GdkDrop             *drop,
+                           GdkContentFormats   *formats,
+                           int                  io_priority,
+                           GCancellable        *cancellable,
+                           GAsyncReadyCallback  callback,
+                           gpointer             user_data)
 {
-  GdkWin32DropContext       *context_win32 = GDK_WIN32_DROP_CONTEXT (drop);
+  GdkWin32Drop              *drop_win32 = GDK_WIN32_DROP (drop);
   GTask                     *task;
-  target_drag_context       *tctx;
+  drop_target_context       *tctx;
   const char * const        *mime_types;
   gsize                      i, j, n_mime_types;
   GdkWin32ContentFormatPair *pair;
@@ -1031,14 +1264,38 @@ gdk_win32_drop_context_read_async (GdkDrop             *drop,
 
   task = g_task_new (drop, cancellable, callback, user_data);
   g_task_set_priority (task, io_priority);
-  g_task_set_source_tag (task, gdk_win32_drop_context_read_async);
+  g_task_set_source_tag (task, gdk_win32_drop_read_async);
+
+  mime_types = gdk_content_formats_get_mime_types (formats, &n_mime_types);
 
-  tctx = find_droptarget_for_target_context (GDK_DRAG_CONTEXT (drop));
+  if (drop_win32->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
+    {
+      for (i = 0; i < n_mime_types; i++)
+        if (g_strcmp0 (mime_types[i], "text/uri-list") == 0)
+          break;
+      if (i >= n_mime_types)
+        {
+          g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                                   _("No compatible transfer format found"));
+          g_clear_pointer (&drop_win32->dropfiles_list, g_free);
+
+          return;
+        }
+
+      stream = g_memory_input_stream_new_from_data (drop_win32->dropfiles_list, strlen 
(drop_win32->dropfiles_list), g_free);
+      drop_win32->dropfiles_list = NULL;
+      g_object_set_data (G_OBJECT (stream), "gdk-dnd-stream-contenttype", (gpointer) "text/uri-list");
+      g_task_return_pointer (task, stream, g_object_unref);
+
+      return;
+    }
+
+  tctx = GDK_SURFACE_IMPL_WIN32 (gdk_drop_get_surface (drop)->impl)->drop_target;
 
   if (tctx == NULL)
     {
       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
-                               _("Failed to find target context record for context 0x%p"), drop);
+                               _("GDK surface 0x%p is not registered as a drop target"), 
gdk_drop_get_surface (drop));
       return;
     }
 
@@ -1049,13 +1306,11 @@ gdk_win32_drop_context_read_async (GdkDrop             *drop,
       return;
     }
 
-  mime_types = gdk_content_formats_get_mime_types (formats, &n_mime_types);
-
   for (pair = NULL, i = 0; i < n_mime_types; i++)
     {
-      for (j = 0; j < context_win32->droptarget_w32format_contentformat_map->len; j++)
+      for (j = 0; j < drop_win32->droptarget_w32format_contentformat_map->len; j++)
         {
-          pair = &g_array_index (context_win32->droptarget_w32format_contentformat_map, 
GdkWin32ContentFormatPair, j);
+          pair = &g_array_index (drop_win32->droptarget_w32format_contentformat_map, 
GdkWin32ContentFormatPair, j);
           if (pair->contentformat == mime_types[i])
             break;
 
@@ -1127,17 +1382,17 @@ gdk_win32_drop_context_read_async (GdkDrop             *drop,
 }
 
 static GInputStream *
-gdk_win32_drop_context_read_finish (GdkDrop       *drop,
-                                    const char   **out_mime_type,
-                                    GAsyncResult  *result,
-                                    GError       **error)
+gdk_win32_drop_read_finish (GdkDrop       *drop,
+                            const char   **out_mime_type,
+                            GAsyncResult  *result,
+                            GError       **error)
 {
   GTask *task;
   GInputStream *stream;
 
   g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (drop)), NULL);
   task = G_TASK (result);
-  g_return_val_if_fail (g_task_get_source_tag (task) == gdk_win32_drop_context_read_async, NULL);
+  g_return_val_if_fail (g_task_get_source_tag (task) == gdk_win32_drop_read_async, NULL);
 
   stream = g_task_propagate_pointer (task, error);
 
@@ -1148,57 +1403,22 @@ gdk_win32_drop_context_read_finish (GdkDrop       *drop,
 }
 
 static void
-gdk_win32_drop_context_class_init (GdkWin32DropContextClass *klass)
+gdk_win32_drop_class_init (GdkWin32DropClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GdkDropClass *drop_class = GDK_DROP_CLASS (klass);
-  GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass);
 
-  object_class->finalize = gdk_win32_drop_context_finalize;
+  object_class->finalize = gdk_win32_drop_finalize;
 
-  drop_class->status = gdk_win32_drop_context_status;
-  drop_class->finish = gdk_win32_drop_context_finish;
-  drop_class->read_async = gdk_win32_drop_context_read_async;
-  drop_class->read_finish = gdk_win32_drop_context_read_finish;
-}
-
-void
-_gdk_win32_local_send_enter (GdkDragContext *context,
-                             guint32         time)
-{
-  GdkDragContext *new_context;
-
-  GDK_NOTE (DND, g_print ("local_send_enter: context=%p current_dest_drag=%p\n",
-                          context,
-                          current_dest_drag));
-
-  if (current_dest_drag != NULL)
-    {
-      g_object_unref (G_OBJECT (current_dest_drag));
-      current_dest_drag = NULL;
-    }
-
-  new_context = gdk_drop_context_new (gdk_surface_get_display (context->source_surface),
-                                      context->source_surface,
-                                      context->dest_surface,
-                                      gdk_content_formats_ref (gdk_drag_context_get_formats (context)),
-                                      gdk_drag_context_get_actions (context),
-                                      GDK_DRAG_PROTO_LOCAL);
-
-  gdk_surface_set_events (new_context->source_surface,
-                          gdk_surface_get_events (new_context->source_surface) |
-                          GDK_PROPERTY_CHANGE_MASK);
-
-  current_dest_drag = new_context;
-
-  gdk_drop_emit_enter_event (GDK_DROP (new_context), FALSE, GDK_CURRENT_TIME);
+  drop_class->status = gdk_win32_drop_status;
+  drop_class->finish = gdk_win32_drop_finish;
+  drop_class->read_async = gdk_win32_drop_read_async;
+  drop_class->read_finish = gdk_win32_drop_read_finish;
 }
 
 void
 _gdk_drop_init (void)
 {
-  if (use_ole2_dnd)
-    {
-      target_ctx_for_window = g_hash_table_new (g_direct_hash, g_direct_equal);
-    }
+  if (g_strcmp0 (getenv ("GDK_WIN32_OLE2_DND"), "0") == 0)
+    use_ole2_dnd = FALSE;
 }
diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h
index 57905f998e..95e1259cd0 100644
--- a/gdk/win32/gdkprivate-win32.h
+++ b/gdk/win32/gdkprivate-win32.h
@@ -202,8 +202,6 @@ void    _gdk_win32_adjust_client_rect   (GdkSurface *window,
 
 void    _gdk_selection_property_delete (GdkSurface *);
 
-void    _gdk_dropfiles_store (gchar *data);
-
 void       _gdk_push_modal_window   (GdkSurface *window);
 void       _gdk_remove_modal_window (GdkSurface *window);
 GdkSurface *_gdk_modal_current       (void);
@@ -424,6 +422,8 @@ void       _gdk_win32_display_create_surface_impl   (GdkDisplay    *display,
 
 /* stray GdkSurfaceImplWin32 members */
 void _gdk_win32_surface_register_dnd (GdkSurface *window);
+void _gdk_win32_surface_unregister_dnd (GdkSurface *window);
+
 GdkDragContext *_gdk_win32_surface_drag_begin (GdkSurface         *window,
                                                GdkDevice          *device,
                                                GdkContentProvider *content,
diff --git a/gdk/win32/gdksurface-win32.c b/gdk/win32/gdksurface-win32.c
index f0e6def13d..105ef1772b 100644
--- a/gdk/win32/gdksurface-win32.c
+++ b/gdk/win32/gdksurface-win32.c
@@ -254,6 +254,9 @@ gdk_surface_impl_win32_finalize (GObject *object)
       surface_impl->cache_surface = NULL;
     }
 
+  _gdk_win32_surface_unregister_dnd (wrapper);
+  g_clear_object (&surface_impl->drop);
+
   g_assert (surface_impl->transient_owner == NULL);
   g_assert (surface_impl->transient_children == NULL);
 
diff --git a/gdk/win32/gdksurface-win32.h b/gdk/win32/gdksurface-win32.h
index a0e7309eab..2a14578197 100644
--- a/gdk/win32/gdksurface-win32.h
+++ b/gdk/win32/gdksurface-win32.h
@@ -216,6 +216,9 @@ struct _GdkW32DragMoveResizeContext
 
 typedef struct _GdkW32DragMoveResizeContext GdkW32DragMoveResizeContext;
 
+/* defined in gdkdrop-win32.c */
+typedef struct _drop_target_context drop_target_context;
+
 struct _GdkSurfaceImplWin32
 {
   GdkSurfaceImpl parent_instance;
@@ -243,6 +246,15 @@ struct _GdkSurfaceImplWin32
 
   GdkEventMask native_event_mask;
 
+  /* Non-NULL for any window that is registered as a drop target.
+   * For OLE2 protocol only.
+   */
+  drop_target_context *drop_target;
+  /* Temporarily holds the GdkDrop currently associated with this window.
+   * For LOCAL protocol only.
+   */
+  GdkDrop             *drop;
+
   GdkSurfaceTypeHint type_hint;
 
   GdkSurface *transient_owner;
diff --git a/gdk/win32/gdkwin32dnd-private.h b/gdk/win32/gdkwin32dnd-private.h
index 1492313322..443cc78448 100644
--- a/gdk/win32/gdkwin32dnd-private.h
+++ b/gdk/win32/gdkwin32dnd-private.h
@@ -38,13 +38,12 @@ struct _GdkWin32DragContextUtilityData
 
 struct _GdkWin32DragContext
 {
-  GdkDragContext context;
+  GdkDragContext drag;
   GdkDragProtocol protocol;
   GdkSurface *ipc_window;
   GdkSurface *drag_surface;
   GdkCursor *cursor;
   GdkSeat *grab_seat;
-  GdkDragAction actions;
   GdkDragAction current_action;
 
   GdkWin32DragContextUtilityData util_data;
@@ -57,6 +56,7 @@ struct _GdkWin32DragContext
 
   guint drag_status : 4;             /* Current status of drag */
   guint drop_failed : 1;             /* Whether the drop was unsuccessful */
+  guint handle_events : 1;           /* Whether handle_event() should do anything */
 };
 
 struct _GdkWin32DragContextClass
@@ -64,51 +64,54 @@ struct _GdkWin32DragContextClass
   GdkDragContextClass parent_class;
 };
 
-struct _GdkWin32DropContext
-{
-  GdkDragContext context;
-  GdkDragProtocol protocol;
-  GdkDragAction actions;
-  GdkDragAction current_action;
-
-  guint scale;          /* Temporarily caches the HiDPI scale */
-  gint             last_x;         /* Coordinates from last event, in GDK space */
-  gint             last_y;
-  DWORD            last_key_state; /* Key state from last event */
-
-  /* Just like context->formats, but an array, and with format IDs
-   * stored inside.
-   */
-  GArray *droptarget_w32format_contentformat_map;
-
-  GdkWin32DragContext *local_source_context;
-
-  guint drag_status : 4;             /* Current status of drag */
-  guint drop_failed : 1;             /* Whether the drop was unsuccessful */
-};
-
-struct _GdkWin32DropContextClass
-{
-  GdkDragContextClass parent_class;
-};
-
-gpointer _gdk_win32_dnd_thread_main (gpointer data);
-
-GdkDragContext *_gdk_win32_find_source_context_for_dest_surface  (GdkSurface      *dest_surface);
-
-void            _gdk_win32_drag_context_send_local_status_event (GdkDragContext *src_context,
-                                                                 GdkDragAction   action);
-void            _gdk_win32_local_send_enter                     (GdkDragContext *context,
-                                                                 guint32         time);
-
-GdkDragContext *_gdk_win32_drag_context_find                    (GdkSurface      *source,
-                                                                 GdkSurface      *dest);
-GdkDragContext *_gdk_win32_drop_context_find                    (GdkSurface      *source,
-                                                                 GdkSurface      *dest);
-
 
-void            _gdk_win32_drag_do_leave                        (GdkDragContext *context,
-                                                                 guint32         time);
+gpointer _gdk_win32_dnd_thread_main                      (gpointer         data);
+
+GdkDragContext *_gdk_win32_find_drag_for_dest_surface    (GdkSurface      *dest_surface);
+
+void     _gdk_win32_drag_send_local_status_event         (GdkDragContext  *drag,
+                                                          GdkDragAction    action);
+void     _gdk_win32_local_send_enter                     (GdkDragContext  *drag,
+                                                          GdkSurface      *dest_surface,
+                                                          guint32          time);
+
+GdkDragContext *_gdk_win32_drag_context_find             (GdkSurface      *source,
+                                                          GdkSurface      *dest);
+GdkDrop        *_gdk_win32_get_drop_for_dest_surface     (GdkSurface      *dest);
+
+void     _gdk_win32_drag_do_leave                        (GdkDragContext  *context,
+                                                          guint32          time);
+
+gboolean _gdk_win32_local_drop_target_will_emit_motion (GdkDrop *drop,
+                                                        gint     x_root,
+                                                        gint     y_root,
+                                                        DWORD    grfKeyState);
+
+void     _gdk_win32_local_drop_target_dragenter (GdkDragContext *drag,
+                                                 GdkSurface     *dest_surface,
+                                                 gint            x_root,
+                                                 gint            y_root,
+                                                 DWORD           grfKeyState,
+                                                 guint32         time_,
+                                                 GdkDragAction  *actions);
+void     _gdk_win32_local_drop_target_dragover  (GdkDrop        *drop,
+                                                 GdkDragContext *drag,
+                                                 gint            x_root,
+                                                 gint            y_root,
+                                                 DWORD           grfKeyState,
+                                                 guint32         time_,
+                                                 GdkDragAction  *actions);
+void     _gdk_win32_local_drop_target_dragleave (GdkDrop *drop,
+                                                 guint32  time_);
+void     _gdk_win32_local_drop_target_drop      (GdkDrop        *drop,
+                                                 GdkDragContext *drag,
+                                                 guint32         time_,
+                                                 GdkDragAction  *actions);
+
+void     _gdk_win32_local_drag_give_feedback    (GdkDragContext *drag,
+                                                 GdkDragAction   actions);
+void     _gdk_win32_local_drag_context_drop_response (GdkDragContext *drag,
+                                                      GdkDragAction   action);
 
 
 G_END_DECLS
diff --git a/gdk/win32/gdkwin32dnd.h b/gdk/win32/gdkwin32dnd.h
index a59aefa9b3..31a0ee85e4 100644
--- a/gdk/win32/gdkwin32dnd.h
+++ b/gdk/win32/gdkwin32dnd.h
@@ -43,23 +43,6 @@ typedef struct _GdkWin32DragContextClass GdkWin32DragContextClass;
 GDK_AVAILABLE_IN_ALL
 GType    gdk_win32_drag_context_get_type (void);
 
-#define GDK_TYPE_WIN32_DROP_CONTEXT              (gdk_win32_drop_context_get_type ())
-#define GDK_WIN32_DROP_CONTEXT(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), 
GDK_TYPE_WIN32_DROP_CONTEXT, GdkWin32DropContext))
-#define GDK_WIN32_DROP_CONTEXT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), 
GDK_TYPE_WIN32_DROP_CONTEXT, GdkWin32DropContextClass))
-#define GDK_IS_WIN32_DROP_CONTEXT(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), 
GDK_TYPE_WIN32_DROP_CONTEXT))
-#define GDK_IS_WIN32_DROP_CONTEXT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GDK_TYPE_WIN32_DROP_CONTEXT))
-#define GDK_WIN32_DROP_CONTEXT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GDK_TYPE_WIN32_DROP_CONTEXT, GdkWin32DropContextClass))
-
-#ifdef GDK_COMPILATION
-typedef struct _GdkWin32DropContext GdkWin32DropContext;
-#else
-typedef GdkDragContext GdkWin32DropContext;
-#endif
-typedef struct _GdkWin32DropContextClass GdkWin32DropContextClass;
-
-GDK_AVAILABLE_IN_ALL
-GType    gdk_win32_drop_context_get_type (void);
-
 G_END_DECLS
 
 #endif /* __GDK_WIN32_DRAG_CONTEXT_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]