GDK W32: Adapt to GdkDrop and GdkDragContext changes
authorРуслан Ижбулатов <lrn1986@gmail.com>
Tue, 5 Jun 2018 23:03:51 +0000 (23:03 +0000)
committerBenjamin Otte <otte@redhat.com>
Mon, 18 Jun 2018 21:49:52 +0000 (23:49 +0200)
* 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
gdk/win32/gdkclipdrop-win32.h
gdk/win32/gdkdrag-win32.c
gdk/win32/gdkdrop-win32.c
gdk/win32/gdkprivate-win32.h
gdk/win32/gdksurface-win32.c
gdk/win32/gdksurface-win32.h
gdk/win32/gdkwin32dnd-private.h
gdk/win32/gdkwin32dnd.h

index 2cefb583eae0cd9908e582c8b6a28b7cb61a8110..f3d4925fb52040f714eca6525e8150c4398354e7 100644 (file)
@@ -1540,8 +1540,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);
@@ -1875,32 +1873,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)
-{
-/* 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 *
@@ -1967,7 +1939,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)
         {
@@ -1994,12 +1966,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);
@@ -2058,15 +2040,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);
@@ -2081,47 +2064,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, (gpointer) 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, (gpointer) pair.contentformat);
+       gdk_content_formats_builder_add_mime_type (builder, pair.contentformat);
      }
 }
 
index c281c8d4865d28aaf8240d25975411ffaeaa5b2d..d7b88c6061af0b78c4c7d872762a0d2f79db68d8 100644 (file)
@@ -236,42 +236,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,
-                                                                    gsize             *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                         fmt,
+                                                                    gboolean                    *is_predefined);
+void     _gdk_win32_add_w32format_to_pairs                         (UINT                         format,
+                                                                    GArray                      *array,
+                                                                    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,
+                                                                    gsize                       *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);
 
 
 
index 2da97003060c6f33635ba32d5746bcb21a9a7da6..8ad7322f86f1ca9416c2ef1089beedfea9aa58e5 100644 (file)
@@ -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;
 
@@ -2007,157 +1886,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
@@ -2175,75 +2011,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);
@@ -2262,7 +2109,7 @@ ease_out_cubic (double t)
 
 typedef struct _GdkDragAnim GdkDragAnim;
 struct _GdkDragAnim {
-  GdkWin32DragContext *context;
+  GdkWin32DragContext *drag;
   GdkFrameClock *frame_clock;
   gint64 start_time;
 };
@@ -2270,7 +2117,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);
 }
 
@@ -2278,7 +2125,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;
@@ -2296,20 +2143,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;
@@ -2318,40 +2165,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);
@@ -2360,20 +2213,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,
@@ -2382,29 +2235,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()? */
 
@@ -2412,24 +2269,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)
     {
@@ -2447,185 +2310,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)
@@ -2633,7 +2430,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:
@@ -2641,14 +2438,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;
 
@@ -2681,89 +2478,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;
     }
@@ -2772,39 +2581,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);
     }
 }
 
index 17f0738d88e0097289d24b056b12b6a6e099a83e..6d7ff78e7a73c25e919465a4e37a1140fba01523 100644 (file)
 #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,63 +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, NULL),
-                                  GDK_ACTION_COPY | GDK_ACTION_MOVE,
-                                  GDK_DRAG_PROTO_OLE2);
-  context_win32 = GDK_WIN32_DROP_CONTEXT (context);
-  gdk_content_formats_unref (query_targets (pDataObj, context_win32->droptarget_w32format_contentformat_map));
-  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.
@@ -450,87 +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);
-  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)
     {
@@ -934,8 +1157,10 @@ _gdk_win32_surface_register_dnd (GdkSurface *window)
     }
   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;
 }
index b494816a0072f0a228789fba09d27681ea421e7c..1d1970872b0721a4e66c5ab5e28eace40489ab01 100644 (file)
@@ -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,
index 932ca4a66389d377a236d7f7d6fca11e175605f5..ddd117eb67369c7ccd45846255212114c0512efd 100644 (file)
@@ -250,6 +250,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);
 
index a0e7309eab4756d39d9dae7c774cabe60123aab5..2a145781970cd8dca0b24f53e75f8f2bd3fdccaf 100644 (file)
@@ -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;
index 1492313322ebd5e409c0ea0e0d61843971726ea9..443cc78448528b06f054bf93d551f6bb69e6859a 100644 (file)
@@ -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
index a59aefa9b39d14047a765b341c3df196102a2cd4..31a0ee85e433869aa538e3a87b14040fdde23a42 100644 (file)
@@ -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__ */