1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 2001 Archaeopteryx Software Inc.
4  * Copyright (C) 1998-2002 Tor Lillqvist
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26 
27 #include "config.h"
28 #include <string.h>
29 
30 #include "gdk-private.h"
31 
32 #include <io.h>
33 #include <fcntl.h>
34 #include <math.h>
35 
36 /*
37  * Support for OLE-2 drag and drop added at Archaeopteryx Software, 2001
38  * For more information, do not contact Stephan R.A. Deibel (sdeibel@archaeopteryx.com),
39  * because the code went through multiple modifications since then.
40  *
41  * Notes on the implementation:
42  *
43  * Source drag context, IDropSource and IDataObject for it are created
44  * (almost) simultaneously, whereas target drag context and IDropTarget
45  * are separated in time - IDropTarget is created when a window is made
46  * to accept drops, while target drag context is created when a dragging
47  * cursor enters the window and is destroyed when that cursor leaves
48  * the window.
49  *
50  * There's a mismatch between data types supported by W32 (W32 formats)
51  * and by GTK (GDK contentformats).
52  * To account for it the data is transmuted back and forth. There are two
53  * main points of transmutation:
54  * * GdkWin32HDATAOutputStream: transmutes GTK data to W32 data
55  * * GdkWin32Drop: transmutes W32 data to GTK data
56  *
57  * There are also two points where data formats are considered:
58  * * When source drag context is created, it gets a list of GDK contentformats
59  *   that it supports, these are matched to the W32 formats they
60  *   correspond to (possibly with transmutation). New W32 formats for
61  *   GTK-specific contentformats are also created here (see below).
62  * * When target drop context is created, it queries the IDataObject
63  *   for the list of W32 formats it supports and matches these to
64  *   corresponding GDK contentformats that it will be able to provide
65  *   (possibly with transmutation) later. Missing GDK contentformats for
66  *   W32-specific formats are also created here (see below).
67  *
68  * W32 formats are integers (CLIPFORMAT), while GTK contentformats
69  * are mime/type strings, and cannot be used interchangeably.
70  *
71  * To accommodate advanced GTK applications the code allows them to
72  * register drop targets that accept W32 data formats, and to register
73  * drag sources that provide W32 data formats. To do that they must
74  * register with the mime/type "application/x.windows.ZZZ", where
75  * ZZZ is the string name of the format in question
76  * (for example, "Shell IDList Array") or, for unnamed pre-defined
77  * formats, register with the stringified constant name of the format
78  * in question (for example, "CF_UNICODETEXT").
79  * If such contentformat is accepted/provided, GDK will not try to
80  * transmute it to/from something else. Otherwise GDK will do the following
81  * transmutation:
82  * * If GTK application provides image/png, image/gif or image/jpeg,
83  *   GDK will claim to also provide "PNG", "GIF" or "JFIF" respectively,
84  *   and will pass these along verbatim.
85  * * If GTK application provides any GdkPixbuf-compatible contentformat,
86  *   GDK will also offer "PNG" and CF_DIB W32 formats.
87  * * If GTK application provides text/plain;charset=utf8, GDK will also offer
88  *   CF_UNICODETEXT (UTF-16-encoded) and CF_TEXT (encoded with thread-
89  *   and locale-dependent codepage), and will do the conversion when such
90  *   data is requested.
91  * * If GTK application accepts image/png, image/gif or image/jpeg,
92  *   GDK will claim to also accept "PNG", "GIF" or "JFIF" respectively,
93  *   and will pass these along verbatim.
94  * * If GTK application accepts image/bmp, GDK will
95  *   claim to accept CF_DIB W32 format, and will convert
96  *   it, changing the header, when such data is provided.
97  * * If GTK application accepts text/plain;charset=utf8, GDK will
98  *   claim to accept CF_UNICODETEXT and CF_TEXT, and will do
99  *   the conversion when such data is provided.
100  * * If GTK application accepts text/uri-list, GDK will
101  *   claim to accept "Shell IDList Array", and will do the
102  *   conversion when such data is provided.
103  *
104  * Currently the conversion from text/uri-list to "Shell IDList Array" is not
105  * implemented, so it's not possible to drag & drop files from GTK
106  * applications to non-GTK applications the same way one can drag files
107  * from Windows Explorer.
108  *
109  * To increase inter-GTK compatibility, GDK will register GTK-specific
110  * formats by their mime/types, as-is (i.e "text/plain;charset=utf-8", for example).
111  * That way two GTK applications can exchange data in their native formats
112  * (both well-known ones, such as text/plain;charset=utf8, and special,
113  * known only to specific applications). This will work just
114  * fine as long as both applications agree on what kind of data is stored
115  * under such format exactly.
116  *
117  * Note that clipboard format space is limited, there can only be 16384
118  * of them for a particular user session. Therefore it is highly inadvisable
119  * to create and register such formats out of the whole cloth, dynamically.
120  * If more flexibility is needed, register one format that has some
121  * internal indicators of the kind of data it contains, then write the application
122  * in such a way that it requests the data and inspects its header before deciding
123  * whether to accept it or not. For details see GTK drag & drop documentation
124  * on the "drag-motion" and "drag-data-received" signals.
125  *
126  * How managed DnD works:
127  * GTK widget detects a drag gesture and calls
128  * S: gdk_drag_begin_from_point() -> backend:drag_begin()
129  * which creates the source drag context and the drag window,
130  * and grabs the pointing device. GDK layer adds the context
131  * to a list of currently-active contexts.
132  *
133  * From that point forward the context gets any events emitted
134  * by GDK, and can prevent these events from going anywhere else.
135  * They are all handled in
136  * S: gdk_drag_handle_source_event() -> backend:handle_event()
137  * (except for wayland backend - it doesn't have that function).
138  *
139  * That function catches the following events:
140  * GDK_MOTION_NOTIFY
141  * GDK_BUTTON_RELEASE
142  * GDK_KEY_PRESS
143  * GDK_KEY_RELEASE
144  * GDK_GRAB_BROKEN
145  *
146  * GDK_MOTION_NOTIFY is emitted by the backend in response to
147  * mouse movement.
148  * Drag context handles it by calling a bunch of functions to
149  * determine the state of the drag actions from the keys being
150  * pressed, finding the drag window (another backend function
151  * routed through GDK layer) and finally calls
152  * S: gdk_drag_motion -> backend:drag_motion()
153  * to notify the backend (i.e. itself) that the drag cursor
154  * moved.
155  * The response to that is to move the drag window and
156  * do various bookkeeping.
157  * W32: OLE2 protocol does nothing (other than moving the
158  * drag window) in response to this, as all the functions
159  * that GDK could perform here are already handled by the
160  * OS driving the DnD process via DoDragDrop() call.
161  *
162  * GDK_BUTTON_RELEASE checks the
163  * released button - if it's the button that was used to
164  * initiate the drag, the "drop-performed" signal is emitted,
165  * otherwise the drag is cancelled.
166  *
167  * GDK_KEY_PRESS and GDK_KEY_RELEASE handler does exactly the same thing as
168  * GDK_MOTION_NOTIFY handler, but only after it checks the pressed
169  * keys to emit "drop-performed" signal (on Space, Enter etc),
170  * cancel the drag (on Escape) or move the drag cursor (arrow keys).
171  *
172  * GDK_GRAB_BROKEN handler cancels the drag for most broken grabs
173  * (except for some special cases where the backend itself does
174  *  temporary grabs as part of DnD, such as changing the cursor).
175  *
176  * GDK_DRAG_ENTER, GDK_DRAG_LEAVE, GDK_DRAG_MOTION and GDK_DROP_START
177  * events are emitted when
178  * the OS notifies the process about these things happening.
179  * For X11 backend that is done in Xdnd event filters,
180  * for W32 backend this is done in IDropSource/IDropTarget
181  * object methods for the OLE2 protocol.
182  *
183  */
184 
185 /* The mingw.org compiler does not export GUIDS in it's import library. To work
186  * around that, define INITGUID to have the GUIDS declared. */
187 #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
188 #define INITGUID
189 #endif
190 
191 /* For C-style COM wrapper macros */
192 #define COBJMACROS
193 
194 #include "gdkdrag.h"
195 #include "gdkinternals.h"
196 #include "gdkprivate-win32.h"
197 #include "gdkwin32.h"
198 #include "gdkwin32dnd.h"
199 #include "gdkdisplayprivate.h"
200 #include "gdk/gdkdragprivate.h"
201 #include "gdkwin32dnd-private.h"
202 #include "gdkdisplay-win32.h"
203 #include "gdkdevice-win32.h"
204 #include "gdkdeviceprivate.h"
205 #include "gdkhdataoutputstream-win32.h"
206 
207 #include <ole2.h>
208 
209 #include <shlobj.h>
210 #include <shlguid.h>
211 #include <objidl.h>
212 #include "gdkintl.h"
213 
214 #include <gdk/gdk.h>
215 #include <glib/gstdio.h>
216 
217 /* Just to avoid calling RegisterWindowMessage() every time */
218 static UINT thread_wakeup_message;
219 
220 typedef struct
221 {
222   IDropSource                     ids;
223   IDropSourceNotify               idsn;
224   int                             ref_count;
225   GdkDrag                        *drag;
226 
227   /* These are thread-local
228    * copies of the similar fields from GdkWin32Drag
229    */
230   GdkWin32DragUtilityData  util_data;
231 
232   /* Cached here, so that we don't have to look in
233    * the context every time.
234    */
235   HWND                            source_window_handle;
236   guint                           scale;
237 
238   /* We get this from the OS via IDropSourceNotify and pass it to the
239    * main thread.
240    * Will be INVALID_HANDLE_VALUE (not NULL!) when unset.
241    */
242   HWND                            dest_window_handle;
243 } source_drag_context;
244 
245 typedef struct {
246   IDataObject                     ido;
247   int                             ref_count;
248   GdkDrag                        *drag;
249   GArray                         *formats;
250 } data_object;
251 
252 typedef struct {
253   IEnumFORMATETC                  ief;
254   int                             ref_count;
255   int                             ix;
256   GArray                         *formats;
257 } enum_formats;
258 
259 typedef enum _GdkWin32DnDThreadQueueItemType GdkWin32DnDThreadQueueItemType;
260 
261 enum _GdkWin32DnDThreadQueueItemType
262 {
263   GDK_WIN32_DND_THREAD_QUEUE_ITEM_GIVE_FEEDBACK = 1,
264   GDK_WIN32_DND_THREAD_QUEUE_ITEM_DRAG_INFO = 2,
265   GDK_WIN32_DND_THREAD_QUEUE_ITEM_DO_DRAG_DROP = 3,
266   GDK_WIN32_DND_THREAD_QUEUE_ITEM_GET_DATA = 4,
267   GDK_WIN32_DND_THREAD_QUEUE_ITEM_UPDATE_DRAG_STATE = 5,
268 };
269 
270 typedef struct _GdkWin32DnDThreadQueueItem GdkWin32DnDThreadQueueItem;
271 
272 struct _GdkWin32DnDThreadQueueItem
273 {
274   GdkWin32DnDThreadQueueItemType  item_type;
275 
276   /* This is used by the DnD thread to communicate the identity
277    * of the drag context to the main thread. This is thread-safe
278    * because DnD thread holds a reference to the context.
279    */
280   gpointer                        opaque_context;
281 };
282 
283 typedef struct _GdkWin32DnDThreadDoDragDrop GdkWin32DnDThreadDoDragDrop;
284 
285 /* This is used both to signal the DnD thread that it needs
286  * to call DoDragDrop(), *and* to signal the main thread
287  * that the DoDragDrop() call returned.
288  */
289 struct _GdkWin32DnDThreadDoDragDrop
290 {
291   GdkWin32DnDThreadQueueItem  base;
292 
293   source_drag_context        *src_context;
294   data_object                *src_object;
295   DWORD                       allowed_drop_effects;
296 
297   DWORD                       received_drop_effect;
298   HRESULT                     received_result;
299 };
300 
301 typedef struct _GdkWin32DnDThreadGetData GdkWin32DnDThreadGetData;
302 
303 /* This is used both to signal the main thread that the DnD thread
304  * needs DnD data, and to give that data to the DnD thread.
305  */
306 struct _GdkWin32DnDThreadGetData
307 {
308   GdkWin32DnDThreadQueueItem  base;
309 
310   GdkWin32ContentFormatPair   pair;
311   GdkWin32HDataOutputStream  *stream;
312 
313   STGMEDIUM                   produced_data_medium;
314 };
315 
316 typedef struct _GdkWin32DnDThreadGiveFeedback GdkWin32DnDThreadGiveFeedback;
317 
318 struct _GdkWin32DnDThreadGiveFeedback
319 {
320   GdkWin32DnDThreadQueueItem base;
321 
322   DWORD                      received_drop_effect;
323 };
324 
325 typedef struct _GdkWin32DnDThreadDragInfo GdkWin32DnDThreadDragInfo;
326 
327 struct _GdkWin32DnDThreadDragInfo
328 {
329   GdkWin32DnDThreadQueueItem base;
330 
331   BOOL                       received_escape_pressed;
332   DWORD                      received_keyboard_mods;
333 };
334 
335 typedef struct _GdkWin32DnDThreadUpdateDragState GdkWin32DnDThreadUpdateDragState;
336 
337 struct _GdkWin32DnDThreadUpdateDragState
338 {
339   GdkWin32DnDThreadQueueItem base;
340 
341   gpointer                opaque_ddd;
342   GdkWin32DragUtilityData produced_util_data;
343 };
344 
345 typedef struct _GdkWin32DnDThread GdkWin32DnDThread;
346 
347 struct _GdkWin32DnDThread
348 {
349   /* We receive instructions from the main thread in this queue */
350   GAsyncQueue *input_queue;
351 
352   /* We can't peek the queue or "unpop" queue items,
353    * so the items that we can't act upon (yet) got
354    * to be stored *somewhere*.
355    */
356   GList       *dequeued_items;
357 
358   source_drag_context *src_context;
359   data_object         *src_object;
360 };
361 
362 /* The code is much more secure if we don't rely on the OS to keep
363  * this around for us.
364  */
365 static GdkWin32DnDThread *dnd_thread_data = NULL;
366 
367 static gboolean
dnd_queue_is_empty()368 dnd_queue_is_empty ()
369 {
370   return g_atomic_int_get (&_win32_clipdrop->dnd_queue_counter) == 0;
371 }
372 
373 static void
decrement_dnd_queue_counter()374 decrement_dnd_queue_counter ()
375 {
376   g_atomic_int_dec_and_test (&_win32_clipdrop->dnd_queue_counter);
377 }
378 
379 static void
increment_dnd_queue_counter()380 increment_dnd_queue_counter ()
381 {
382   g_atomic_int_inc (&_win32_clipdrop->dnd_queue_counter);
383 }
384 
385 static void
free_queue_item(GdkWin32DnDThreadQueueItem * item)386 free_queue_item (GdkWin32DnDThreadQueueItem *item)
387 {
388   GdkWin32DnDThreadGetData *getdata;
389 
390   switch (item->item_type)
391     {
392     case GDK_WIN32_DND_THREAD_QUEUE_ITEM_DO_DRAG_DROP:
393       /* Don't unref anything, it's all done in the main thread,
394        * when it receives a DoDragDrop reply.
395        */
396       break;
397     case GDK_WIN32_DND_THREAD_QUEUE_ITEM_UPDATE_DRAG_STATE:
398     case GDK_WIN32_DND_THREAD_QUEUE_ITEM_GIVE_FEEDBACK:
399     case GDK_WIN32_DND_THREAD_QUEUE_ITEM_DRAG_INFO:
400       /* These have no data to clean up */
401       break;
402     case GDK_WIN32_DND_THREAD_QUEUE_ITEM_GET_DATA:
403       getdata = (GdkWin32DnDThreadGetData *) item;
404 
405       switch (getdata->produced_data_medium.tymed)
406         {
407         case TYMED_FILE:
408         case TYMED_ISTREAM:
409         case TYMED_ISTORAGE:
410         case TYMED_GDI:
411         case TYMED_MFPICT:
412         case TYMED_ENHMF:
413           g_critical ("Unsupported STGMEDIUM type");
414           break;
415         case TYMED_NULL:
416           break;
417         case TYMED_HGLOBAL:
418           GlobalFree (getdata->produced_data_medium.hGlobal);
419           break;
420         }
421     }
422 
423   g_free (item);
424 }
425 
426 static gboolean
process_dnd_queue(gboolean timed,guint64 end_time,GdkWin32DnDThreadGetData * getdata_check)427 process_dnd_queue (gboolean                   timed,
428                    guint64                    end_time,
429                    GdkWin32DnDThreadGetData  *getdata_check)
430 {
431   GdkWin32DnDThreadQueueItem *item;
432   GdkWin32DnDThreadUpdateDragState *updatestate;
433   GdkWin32DnDThreadDoDragDrop *ddd;
434 
435   while (TRUE)
436     {
437       if (timed)
438         {
439           guint64 current_time = g_get_monotonic_time ();
440 
441           if (current_time >= end_time)
442             break;
443 
444           item = g_async_queue_timeout_pop (dnd_thread_data->input_queue, end_time - current_time);
445         }
446       else
447         {
448           item = g_async_queue_try_pop (dnd_thread_data->input_queue);
449         }
450 
451       if (item == NULL)
452         break;
453 
454       decrement_dnd_queue_counter ();
455 
456       switch (item->item_type)
457         {
458         case GDK_WIN32_DND_THREAD_QUEUE_ITEM_DO_DRAG_DROP:
459           /* We don't support more than one DnD at a time */
460           free_queue_item (item);
461           break;
462         case GDK_WIN32_DND_THREAD_QUEUE_ITEM_UPDATE_DRAG_STATE:
463           updatestate = (GdkWin32DnDThreadUpdateDragState *) item;
464           ddd = (GdkWin32DnDThreadDoDragDrop *) updatestate->opaque_ddd;
465           ddd->src_context->util_data = updatestate->produced_util_data;
466           free_queue_item (item);
467           break;
468         case GDK_WIN32_DND_THREAD_QUEUE_ITEM_GET_DATA:
469           if (item == (GdkWin32DnDThreadQueueItem *) getdata_check)
470             return TRUE;
471 
472           free_queue_item (item);
473           break;
474         case GDK_WIN32_DND_THREAD_QUEUE_ITEM_GIVE_FEEDBACK:
475         case GDK_WIN32_DND_THREAD_QUEUE_ITEM_DRAG_INFO:
476           g_assert_not_reached ();
477         }
478     }
479 
480   return FALSE;
481 }
482 
483 static gboolean
do_drag_drop_response(gpointer user_data)484 do_drag_drop_response (gpointer user_data)
485 {
486   GdkWin32DnDThreadDoDragDrop *ddd = (GdkWin32DnDThreadDoDragDrop *) user_data;
487   HRESULT hr = ddd->received_result;
488   GdkDrag *drag = GDK_DRAG (ddd->base.opaque_context);
489   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
490   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
491   gpointer table_value = g_hash_table_lookup (clipdrop->active_source_drags, drag);
492 
493   if (ddd == table_value)
494     {
495       GDK_NOTE (DND, g_print ("DoDragDrop returned %s with effect %lu\n",
496                               (hr == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
497                                (hr == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
498                                 (hr == E_UNEXPECTED ? "E_UNEXPECTED" :
499                                  g_strdup_printf ("%#.8lx", hr)))), ddd->received_drop_effect));
500 
501       drag_win32->drop_failed = !(SUCCEEDED (hr) || hr == DRAGDROP_S_DROP);
502 
503       /* We used to delete the selection here,
504        * now GTK does that automatically in response to
505        * the "dnd-finished" signal,
506        * if the operation was successful and was a move.
507        */
508       GDK_NOTE (DND, g_print ("gdk_dnd_handle_drop_finished: 0x%p\n",
509                               drag));
510 
511       g_signal_emit_by_name (drag, "dnd-finished");
512       gdk_drag_drop_done (drag, !drag_win32->drop_failed);
513     }
514   else
515     {
516       if (!table_value)
517         g_critical ("Did not find drag 0x%p in the active drags table", drag);
518       else
519         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);
520     }
521 
522   /* 3rd parties could keep a reference to this object,
523    * but we won't keep the drag alive that long.
524    * Neutralize it (attempts to get its data will fail)
525    * by nullifying the drag pointer (it doesn't hold
526    * a reference, so no unreffing).
527    */
528   g_clear_object (&ddd->src_object->drag);
529 
530   IDropSource_Release (&ddd->src_context->ids);
531   IDataObject_Release (&ddd->src_object->ido);
532 
533   g_hash_table_remove (clipdrop->active_source_drags, drag);
534   free_queue_item (&ddd->base);
535 
536   return G_SOURCE_REMOVE;
537 }
538 
539 static void
received_drag_context_data(GObject * drag,GAsyncResult * result,gpointer user_data)540 received_drag_context_data (GObject      *drag,
541                             GAsyncResult *result,
542                             gpointer      user_data)
543 {
544   GError *error = NULL;
545   GdkWin32DnDThreadGetData *getdata = (GdkWin32DnDThreadGetData *) user_data;
546   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
547 
548   if (!gdk_drag_write_finish (GDK_DRAG (drag), result, &error))
549     {
550       HANDLE handle;
551       gboolean is_hdata;
552 
553       GDK_NOTE (DND, g_printerr ("%p: failed to write HData-backed stream: %s\n", drag, error->message));
554       g_error_free (error);
555       g_output_stream_close (G_OUTPUT_STREAM (getdata->stream), NULL, NULL);
556       handle = gdk_win32_hdata_output_stream_get_handle (getdata->stream, &is_hdata);
557 
558       if (is_hdata)
559         API_CALL (GlobalFree, (handle));
560       else
561         API_CALL (CloseHandle, (handle));
562     }
563   else
564     {
565       g_output_stream_close (G_OUTPUT_STREAM (getdata->stream), NULL, NULL);
566       getdata->produced_data_medium.tymed = TYMED_HGLOBAL;
567       getdata->produced_data_medium.hGlobal = gdk_win32_hdata_output_stream_get_handle (getdata->stream, NULL);
568     }
569 
570   g_clear_object (&getdata->stream);
571   increment_dnd_queue_counter ();
572   g_async_queue_push (clipdrop->dnd_queue, getdata);
573   API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id, thread_wakeup_message, 0, 0));
574 }
575 
576 static gboolean
get_data_response(gpointer user_data)577 get_data_response (gpointer user_data)
578 {
579   GdkWin32DnDThreadGetData *getdata = (GdkWin32DnDThreadGetData *) user_data;
580   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
581   GdkDrag *drag = GDK_DRAG (getdata->base.opaque_context);
582   gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
583 
584   GDK_NOTE (DND, g_print ("idataobject_getdata will request target 0x%p (%s)",
585                           getdata->pair.contentformat, getdata->pair.contentformat));
586 
587   /* This just verifies that we got the right drag,
588    * we don't need the ddd struct itself.
589    */
590   if (ddd)
591     {
592       GError *error = NULL;
593       GOutputStream *stream = gdk_win32_hdata_output_stream_new (&getdata->pair, &error);
594 
595       if (stream)
596         {
597           getdata->stream = GDK_WIN32_HDATA_OUTPUT_STREAM (stream);
598           gdk_drag_write_async (drag,
599                                         getdata->pair.contentformat,
600                                         stream,
601                                         G_PRIORITY_DEFAULT,
602                                         NULL,
603                                         received_drag_context_data,
604                                         getdata);
605 
606           return G_SOURCE_REMOVE;
607         }
608     }
609 
610   increment_dnd_queue_counter ();
611   g_async_queue_push (clipdrop->dnd_queue, getdata);
612   API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id, thread_wakeup_message, 0, 0));
613 
614   return G_SOURCE_REMOVE;
615 }
616 
617 static void
do_drag_drop(GdkWin32DnDThreadDoDragDrop * ddd)618 do_drag_drop (GdkWin32DnDThreadDoDragDrop *ddd)
619 {
620   HRESULT hr;
621 
622   dnd_thread_data->src_object = ddd->src_object;
623   dnd_thread_data->src_context = ddd->src_context;
624 
625   hr = DoDragDrop (&dnd_thread_data->src_object->ido,
626                    &dnd_thread_data->src_context->ids,
627                    ddd->allowed_drop_effects,
628                    &ddd->received_drop_effect);
629 
630   ddd->received_result = hr;
631 
632   g_idle_add_full (G_PRIORITY_DEFAULT, do_drag_drop_response, ddd, NULL);
633 }
634 
635 gpointer
_gdk_win32_dnd_thread_main(gpointer data)636 _gdk_win32_dnd_thread_main (gpointer data)
637 {
638   GAsyncQueue *queue = (GAsyncQueue *) data;
639   GdkWin32DnDThreadQueueItem *item;
640   MSG msg;
641   HRESULT hr;
642 
643   g_assert (dnd_thread_data == NULL);
644 
645   dnd_thread_data = g_new0 (GdkWin32DnDThread, 1);
646   dnd_thread_data->input_queue = queue;
647 
648   CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
649 
650   hr = OleInitialize (NULL);
651 
652   if (!SUCCEEDED (hr))
653     g_error ("OleInitialize failed");
654 
655   /* Create a message queue */
656   PeekMessage (&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
657 
658   thread_wakeup_message = RegisterWindowMessage ("GDK_WORKER_THREAD_WEAKEUP");
659 
660   /* Signal the main thread that we're ready.
661    * This is the only time the queue works in reverse.
662    */
663   g_async_queue_push (queue, GUINT_TO_POINTER (GetCurrentThreadId ()));
664 
665   while (GetMessage (&msg, NULL, 0, 0))
666     {
667       if (!dnd_queue_is_empty ())
668         {
669           while ((item = g_async_queue_try_pop (queue)) != NULL)
670             {
671               decrement_dnd_queue_counter ();
672 
673               if (item->item_type != GDK_WIN32_DND_THREAD_QUEUE_ITEM_DO_DRAG_DROP)
674                 {
675                   free_queue_item (item);
676                   continue;
677                 }
678 
679               do_drag_drop ((GdkWin32DnDThreadDoDragDrop *) item);
680               API_CALL (PostThreadMessage, (GetCurrentThreadId (), thread_wakeup_message, 0, 0));
681               break;
682             }
683         }
684 
685       /* Just to be safe, although this mostly does nothing */
686       TranslateMessage (&msg);
687       DispatchMessage (&msg);
688     }
689 
690   g_async_queue_unref (queue);
691   g_clear_pointer (&dnd_thread_data, g_free);
692 
693   OleUninitialize ();
694   CoUninitialize ();
695 
696   return NULL;
697 }
698 
699 static gboolean drag_context_grab (GdkDrag *drag);
700 
G_DEFINE_TYPE(GdkWin32Drag,gdk_win32_drag,GDK_TYPE_DRAG)701 G_DEFINE_TYPE (GdkWin32Drag, gdk_win32_drag, GDK_TYPE_DRAG)
702 
703 static void
704 move_drag_surface (GdkDrag *drag,
705                    guint    x_root,
706                    guint    y_root)
707 {
708   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
709 
710   g_assert (_win32_main_thread == NULL ||
711             _win32_main_thread == g_thread_self ());
712 
713   gdk_win32_surface_move (drag_win32->drag_surface,
714                           x_root - drag_win32->hot_x,
715                           y_root - drag_win32->hot_y);
716   gdk_win32_surface_raise (drag_win32->drag_surface);
717 }
718 
719 static void
gdk_win32_drag_init(GdkWin32Drag * drag)720 gdk_win32_drag_init (GdkWin32Drag *drag)
721 {
722   g_assert (_win32_main_thread == NULL ||
723             _win32_main_thread == g_thread_self ());
724 
725   drag->handle_events = TRUE;
726   drag->dest_window = INVALID_HANDLE_VALUE;
727 
728   GDK_NOTE (DND, g_print ("gdk_win32_drag_init %p\n", drag));
729 }
730 
731 static void
gdk_win32_drag_finalize(GObject * object)732 gdk_win32_drag_finalize (GObject *object)
733 {
734   GdkDrag *drag;
735   GdkWin32Drag *drag_win32;
736   GdkSurface *drag_surface;
737 
738   g_assert (_win32_main_thread == NULL ||
739             _win32_main_thread == g_thread_self ());
740 
741   GDK_NOTE (DND, g_print ("gdk_win32_drag_finalize %p\n", object));
742 
743   g_return_if_fail (GDK_IS_WIN32_DRAG (object));
744 
745   drag = GDK_DRAG (object);
746   drag_win32 = GDK_WIN32_DRAG (drag);
747 
748   gdk_drag_set_cursor (drag, NULL);
749 
750   g_set_object (&drag_win32->grab_surface, NULL);
751   drag_surface = drag_win32->drag_surface;
752 
753   G_OBJECT_CLASS (gdk_win32_drag_parent_class)->finalize (object);
754 
755   if (drag_surface)
756     gdk_surface_destroy (drag_surface);
757 }
758 
759 /* Drag Contexts */
760 
761 static GdkDrag *
gdk_drag_new(GdkDisplay * display,GdkSurface * surface,GdkContentProvider * content,GdkDragAction actions,GdkDevice * device)762 gdk_drag_new (GdkDisplay         *display,
763               GdkSurface         *surface,
764               GdkContentProvider *content,
765               GdkDragAction       actions,
766               GdkDevice          *device)
767 {
768   GdkWin32Drag *drag_win32;
769   GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (display);
770   GdkDrag *drag;
771 
772   drag_win32 = g_object_new (GDK_TYPE_WIN32_DRAG,
773                              "device", device,
774                              "content", content,
775                              "surface", surface,
776                              "actions", actions,
777                              NULL);
778 
779   drag = GDK_DRAG (drag_win32);
780 
781   if (display_win32->has_fixed_scale)
782     drag_win32->scale = display_win32->surface_scale;
783   else
784     drag_win32->scale = gdk_win32_display_get_monitor_scale_factor (display_win32, NULL, NULL);
785 
786   return drag;
787 }
788 
789 #define PRINT_GUID(guid) \
790   g_print ("%.08lx-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
791            ((gulong *)  guid)[0], \
792            ((gushort *) guid)[2], \
793            ((gushort *) guid)[3], \
794            ((guchar *)  guid)[8], \
795            ((guchar *)  guid)[9], \
796            ((guchar *)  guid)[10], \
797            ((guchar *)  guid)[11], \
798            ((guchar *)  guid)[12], \
799            ((guchar *)  guid)[13], \
800            ((guchar *)  guid)[14], \
801            ((guchar *)  guid)[15]);
802 
803 static enum_formats *enum_formats_new (GArray *formats);
804 
805 /* Finds a GdkDrag object that corresponds to a DnD operation
806  * which is currently targeting the dest_window
807  * Does not give a reference.
808  */
809 GdkDrag *
_gdk_win32_find_drag_for_dest_window(HWND dest_window)810 _gdk_win32_find_drag_for_dest_window (HWND dest_window)
811 {
812   GHashTableIter               iter;
813   GdkWin32Drag                *drag_win32;
814   GdkWin32DnDThreadDoDragDrop *ddd;
815   GdkWin32Clipdrop            *clipdrop = _gdk_win32_clipdrop_get ();
816 
817   g_hash_table_iter_init (&iter, clipdrop->active_source_drags);
818 
819   while (g_hash_table_iter_next (&iter, (gpointer *) &drag_win32, (gpointer *) &ddd))
820     if (ddd->src_context->dest_window_handle == dest_window)
821       return GDK_DRAG (drag_win32);
822 
823   return NULL;
824 }
825 
826 static GdkDragAction
action_for_drop_effect(DWORD effect)827 action_for_drop_effect (DWORD effect)
828 {
829   GdkDragAction action = 0;
830 
831   if (effect & DROPEFFECT_MOVE)
832     action |= GDK_ACTION_MOVE;
833   if (effect & DROPEFFECT_LINK)
834     action |= GDK_ACTION_LINK;
835   if (effect & DROPEFFECT_COPY)
836     action |= GDK_ACTION_COPY;
837 
838   return action;
839 }
840 
841 static ULONG STDMETHODCALLTYPE
idropsource_addref(LPDROPSOURCE This)842 idropsource_addref (LPDROPSOURCE This)
843 {
844   source_drag_context *ctx = (source_drag_context *) This;
845 
846   int ref_count = ++ctx->ref_count;
847 
848   GDK_NOTE (DND, g_print ("idropsource_addref %p %d\n", This, ref_count));
849 
850   return ref_count;
851 }
852 
853 typedef struct _GdkWin32DnDEnterLeaveNotify GdkWin32DnDEnterLeaveNotify;
854 
855 struct _GdkWin32DnDEnterLeaveNotify
856 {
857   gpointer opaque_context;
858   HWND     target_window_handle;
859 };
860 
861 static gboolean
notify_dnd_enter(gpointer user_data)862 notify_dnd_enter (gpointer user_data)
863 {
864   GdkWin32DnDEnterLeaveNotify *notify = (GdkWin32DnDEnterLeaveNotify *) user_data;
865   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (notify->opaque_context);
866 
867   drag_win32->dest_window = notify->target_window_handle;
868 
869   g_free (notify);
870 
871   return G_SOURCE_REMOVE;
872 }
873 
874 static gboolean
notify_dnd_leave(gpointer user_data)875 notify_dnd_leave (gpointer user_data)
876 {
877   GdkWin32DnDEnterLeaveNotify *notify = (GdkWin32DnDEnterLeaveNotify *) user_data;
878   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (notify->opaque_context);
879 
880   if (notify->target_window_handle != drag_win32->dest_window)
881     g_warning ("DnD leave says that the window handle is 0x%p, but drag has 0x%p", notify->target_window_handle, drag_win32->dest_window);
882 
883   drag_win32->dest_window = INVALID_HANDLE_VALUE;
884 
885   g_free (notify);
886 
887   return G_SOURCE_REMOVE;
888 }
889 
890 static HRESULT STDMETHODCALLTYPE
idropsourcenotify_dragentertarget(IDropSourceNotify * This,HWND hwndTarget)891 idropsourcenotify_dragentertarget (IDropSourceNotify *This,
892                                    HWND               hwndTarget)
893 {
894   source_drag_context *ctx = (source_drag_context *) (((char *) This) - G_STRUCT_OFFSET (source_drag_context, idsn));
895   GdkWin32DnDEnterLeaveNotify *notify;
896 
897   if (!dnd_queue_is_empty ())
898     process_dnd_queue (FALSE, 0, NULL);
899 
900   GDK_NOTE (DND, g_print ("idropsourcenotify_dragentertarget %p (SDC %p) 0x%p\n", This, ctx, hwndTarget));
901 
902   ctx->dest_window_handle = hwndTarget;
903 
904   notify = g_new0 (GdkWin32DnDEnterLeaveNotify, 1);
905   notify->target_window_handle = hwndTarget;
906   notify->opaque_context = ctx->drag;
907   g_idle_add_full (G_PRIORITY_DEFAULT, notify_dnd_enter, notify, NULL);
908 
909   return S_OK;
910 }
911 
912 static HRESULT STDMETHODCALLTYPE
idropsourcenotify_dragleavetarget(IDropSourceNotify * This)913 idropsourcenotify_dragleavetarget (IDropSourceNotify *This)
914 {
915   source_drag_context *ctx = (source_drag_context *) (((char *) This) - G_STRUCT_OFFSET (source_drag_context, idsn));
916   GdkWin32DnDEnterLeaveNotify *notify;
917 
918   if (!dnd_queue_is_empty ())
919     process_dnd_queue (FALSE, 0, NULL);
920 
921   GDK_NOTE (DND, g_print ("idropsourcenotify_dragleavetarget %p (SDC %p) 0x%p\n", This, ctx, ctx->dest_window_handle));
922 
923   notify = g_new0 (GdkWin32DnDEnterLeaveNotify, 1);
924   notify->target_window_handle = ctx->dest_window_handle;
925   ctx->dest_window_handle = INVALID_HANDLE_VALUE;
926   notify->opaque_context = ctx->drag;
927   g_idle_add_full (G_PRIORITY_DEFAULT, notify_dnd_leave, notify, NULL);
928 
929   return S_OK;
930 }
931 
932 static HRESULT STDMETHODCALLTYPE
idropsource_queryinterface(LPDROPSOURCE This,REFIID riid,LPVOID * ppvObject)933 idropsource_queryinterface (LPDROPSOURCE This,
934                             REFIID       riid,
935                             LPVOID      *ppvObject)
936 {
937   GDK_NOTE (DND, {
938       g_print ("idropsource_queryinterface %p ", This);
939       PRINT_GUID (riid);
940     });
941 
942   *ppvObject = NULL;
943 
944   if (IsEqualGUID (riid, &IID_IUnknown))
945     {
946       GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
947       idropsource_addref (This);
948       *ppvObject = This;
949       return S_OK;
950     }
951   else if (IsEqualGUID (riid, &IID_IDropSource))
952     {
953       GDK_NOTE (DND, g_print ("...IDropSource S_OK\n"));
954       idropsource_addref (This);
955       *ppvObject = This;
956       return S_OK;
957     }
958   else if (IsEqualGUID (riid, &IID_IDropSourceNotify))
959     {
960       GDK_NOTE (DND, g_print ("...IDropSourceNotify S_OK\n"));
961       idropsource_addref (This);
962       *ppvObject = &((source_drag_context *) This)->idsn;
963       return S_OK;
964     }
965   else
966     {
967       GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
968       return E_NOINTERFACE;
969     }
970 }
971 
972 static gboolean
unref_context_in_main_thread(gpointer opaque_context)973 unref_context_in_main_thread (gpointer opaque_context)
974 {
975   GdkDrag *drag = GDK_DRAG (opaque_context);
976 
977   g_clear_object (&drag);
978 
979   return G_SOURCE_REMOVE;
980 }
981 
982 static ULONG STDMETHODCALLTYPE
idropsource_release(LPDROPSOURCE This)983 idropsource_release (LPDROPSOURCE This)
984 {
985   source_drag_context *ctx = (source_drag_context *) This;
986 
987   int ref_count = --ctx->ref_count;
988 
989   GDK_NOTE (DND, g_print ("idropsource_release %p %d\n", This, ref_count));
990 
991   if (ref_count == 0)
992   {
993     g_idle_add (unref_context_in_main_thread, ctx->drag);
994     g_free (This);
995   }
996 
997   return ref_count;
998 }
999 
1000 /* NOTE: This method is called continuously, even if nothing is
1001  * happening, as long as the drag operation is in progress.
1002  * It is OK to return a "safe" value (S_OK, to keep the drag
1003  * operation going) even if something notable happens, because
1004  * we will have another opportunity to return the "right" value
1005  * (once we know what it is, after GTK processes the events we
1006  *  send out) very soon.
1007  * Note that keyboard-related state in this function is nonsense,
1008  * as DoDragDrop doesn't get precise information about the keyboard,
1009  * especially the fEscapePressed argument.
1010  */
1011 static HRESULT STDMETHODCALLTYPE
idropsource_querycontinuedrag(LPDROPSOURCE This,BOOL fEscapePressed,DWORD grfKeyState)1012 idropsource_querycontinuedrag (LPDROPSOURCE This,
1013                                BOOL         fEscapePressed,
1014                                DWORD        grfKeyState)
1015 {
1016   source_drag_context *ctx = (source_drag_context *) This;
1017 
1018   GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p esc=%d keystate=0x%lx with state %d\n", This, fEscapePressed, grfKeyState, ctx->util_data.state));
1019 
1020   if (!dnd_queue_is_empty ())
1021     process_dnd_queue (FALSE, 0, NULL);
1022 
1023   GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag state %d\n", ctx->util_data.state));
1024 
1025   if (ctx->util_data.state == GDK_WIN32_DND_DROPPED)
1026     {
1027       GDK_NOTE (DND, g_print ("DRAGDROP_S_DROP\n"));
1028       return DRAGDROP_S_DROP;
1029     }
1030   else if (ctx->util_data.state == GDK_WIN32_DND_NONE)
1031     {
1032       GDK_NOTE (DND, g_print ("DRAGDROP_S_CANCEL\n"));
1033       return DRAGDROP_S_CANCEL;
1034     }
1035   else
1036     {
1037       GDK_NOTE (DND, g_print ("S_OK\n"));
1038       return S_OK;
1039     }
1040 }
1041 
1042 static void
maybe_emit_action_changed(GdkWin32Drag * drag_win32,GdkDragAction actions)1043 maybe_emit_action_changed (GdkWin32Drag        *drag_win32,
1044                            GdkDragAction        actions)
1045 {
1046   if (actions != drag_win32->current_action)
1047     {
1048       drag_win32->current_action = actions;
1049       gdk_drag_set_selected_action (GDK_DRAG (drag_win32), actions);
1050     }
1051 }
1052 
1053 static gboolean
give_feedback(gpointer user_data)1054 give_feedback (gpointer user_data)
1055 {
1056   GdkWin32DnDThreadGiveFeedback *feedback = (GdkWin32DnDThreadGiveFeedback *) user_data;
1057   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
1058   gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, feedback->base.opaque_context);
1059 
1060   if (ddd)
1061     {
1062       GdkDrag *drag = GDK_DRAG (feedback->base.opaque_context);
1063       GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
1064 
1065       GDK_NOTE (DND, g_print ("gdk_dnd_handle_drag_status: 0x%p\n",
1066                               drag));
1067 
1068       maybe_emit_action_changed (drag_win32, action_for_drop_effect (feedback->received_drop_effect));
1069     }
1070 
1071   free_queue_item (&feedback->base);
1072 
1073   return G_SOURCE_REMOVE;
1074 }
1075 
1076 static HRESULT STDMETHODCALLTYPE
idropsource_givefeedback(LPDROPSOURCE This,DWORD dwEffect)1077 idropsource_givefeedback (LPDROPSOURCE This,
1078                           DWORD        dwEffect)
1079 {
1080   source_drag_context *ctx = (source_drag_context *) This;
1081   GdkWin32DnDThreadGiveFeedback *feedback;
1082 
1083   GDK_NOTE (DND, g_print ("idropsource_givefeedback %p with drop effect %lu S_OK\n", This, dwEffect));
1084 
1085   if (!dnd_queue_is_empty ())
1086     process_dnd_queue (FALSE, 0, NULL);
1087 
1088   feedback = g_new0 (GdkWin32DnDThreadGiveFeedback, 1);
1089   feedback->base.item_type = GDK_WIN32_DND_THREAD_QUEUE_ITEM_GIVE_FEEDBACK;
1090   feedback->base.opaque_context = ctx->drag;
1091   feedback->received_drop_effect = dwEffect;
1092 
1093   g_idle_add_full (G_PRIORITY_DEFAULT, give_feedback, feedback, NULL);
1094 
1095   GDK_NOTE (DND, g_print ("idropsource_givefeedback %p returns\n", This));
1096 
1097   return S_OK;
1098 }
1099 
1100 static ULONG STDMETHODCALLTYPE
idataobject_addref(LPDATAOBJECT This)1101 idataobject_addref (LPDATAOBJECT This)
1102 {
1103   data_object *dobj = (data_object *) This;
1104   int ref_count = ++dobj->ref_count;
1105 
1106   GDK_NOTE (DND, g_print ("idataobject_addref %p %d\n", This, ref_count));
1107 
1108   return ref_count;
1109 }
1110 
1111 static HRESULT STDMETHODCALLTYPE
idataobject_queryinterface(LPDATAOBJECT This,REFIID riid,LPVOID * ppvObject)1112 idataobject_queryinterface (LPDATAOBJECT This,
1113                             REFIID       riid,
1114                             LPVOID      *ppvObject)
1115 {
1116   GDK_NOTE (DND, {
1117       g_print ("idataobject_queryinterface %p ", This);
1118       PRINT_GUID (riid);
1119     });
1120 
1121   *ppvObject = NULL;
1122 
1123   if (IsEqualGUID (riid, &IID_IUnknown))
1124     {
1125       GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
1126       idataobject_addref (This);
1127       *ppvObject = This;
1128       return S_OK;
1129     }
1130   else if (IsEqualGUID (riid, &IID_IDataObject))
1131     {
1132       GDK_NOTE (DND, g_print ("...IDataObject S_OK\n"));
1133       idataobject_addref (This);
1134       *ppvObject = This;
1135       return S_OK;
1136     }
1137   else
1138     {
1139       GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
1140       return E_NOINTERFACE;
1141     }
1142 }
1143 
1144 static ULONG STDMETHODCALLTYPE
idataobject_release(LPDATAOBJECT This)1145 idataobject_release (LPDATAOBJECT This)
1146 {
1147   data_object *dobj = (data_object *) This;
1148   int ref_count = --dobj->ref_count;
1149 
1150   GDK_NOTE (DND, g_print ("idataobject_release %p %d\n", This, ref_count));
1151 
1152   if (ref_count == 0)
1153     {
1154       g_array_free (dobj->formats, TRUE);
1155       g_free (This);
1156     }
1157 
1158   return ref_count;
1159 }
1160 
1161 static HRESULT
query(LPDATAOBJECT This,LPFORMATETC pFormatEtc,GdkWin32ContentFormatPair ** pair)1162 query (LPDATAOBJECT                This,
1163        LPFORMATETC                 pFormatEtc,
1164        GdkWin32ContentFormatPair **pair)
1165 {
1166   data_object *ctx = (data_object *) This;
1167   int i;
1168 
1169   if (pair)
1170     *pair = NULL;
1171 
1172   if (!pFormatEtc)
1173     return DV_E_FORMATETC;
1174 
1175   if (pFormatEtc->lindex != -1)
1176     return DV_E_LINDEX;
1177 
1178   if ((pFormatEtc->tymed & TYMED_HGLOBAL) == 0)
1179     return DV_E_TYMED;
1180 
1181   if ((pFormatEtc->dwAspect & DVASPECT_CONTENT) == 0)
1182     return DV_E_DVASPECT;
1183 
1184   for (i = 0; i < ctx->formats->len; i++)
1185     {
1186       GdkWin32ContentFormatPair *p = &g_array_index (ctx->formats, GdkWin32ContentFormatPair, i);
1187       if (pFormatEtc->cfFormat == p->w32format)
1188         {
1189           if (pair)
1190             *pair = p;
1191 
1192           return S_OK;
1193         }
1194     }
1195 
1196   return DV_E_FORMATETC;
1197 }
1198 
1199 static HRESULT STDMETHODCALLTYPE
idataobject_getdata(LPDATAOBJECT This,LPFORMATETC pFormatEtc,LPSTGMEDIUM pMedium)1200 idataobject_getdata (LPDATAOBJECT This,
1201                      LPFORMATETC  pFormatEtc,
1202                      LPSTGMEDIUM  pMedium)
1203 {
1204   data_object *ctx = (data_object *) This;
1205   HRESULT hr;
1206   GdkWin32DnDThreadGetData *getdata;
1207   GdkWin32ContentFormatPair *pair;
1208 
1209   if (ctx->drag == NULL)
1210     return E_FAIL;
1211 
1212   GDK_NOTE (DND, g_print ("idataobject_getdata %p %s ",
1213                           This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
1214 
1215   /* Check whether we can provide requested format */
1216   hr = query (This, pFormatEtc, &pair);
1217 
1218   if (hr != S_OK)
1219     {
1220       GDK_NOTE (DND, g_print ("Unsupported format, returning 0x%lx\n", hr));
1221       return hr;
1222     }
1223 
1224   if (!dnd_queue_is_empty ())
1225     process_dnd_queue (FALSE, 0, NULL);
1226 
1227   getdata = g_new0 (GdkWin32DnDThreadGetData, 1);
1228   getdata->base.item_type = GDK_WIN32_DND_THREAD_QUEUE_ITEM_GET_DATA;
1229   getdata->base.opaque_context = (gpointer) ctx->drag;
1230   getdata->pair = *pair;
1231   g_idle_add_full (G_PRIORITY_DEFAULT, get_data_response, getdata, NULL);
1232 
1233   if (!process_dnd_queue (TRUE, g_get_monotonic_time () + G_USEC_PER_SEC * 30, getdata))
1234     return E_FAIL;
1235 
1236   if (getdata->produced_data_medium.tymed == TYMED_NULL)
1237     {
1238       free_queue_item (&getdata->base);
1239 
1240       return E_FAIL;
1241     }
1242 
1243   memcpy (pMedium, &getdata->produced_data_medium, sizeof (*pMedium));
1244 
1245   /* To ensure that the data isn't freed */
1246   getdata->produced_data_medium.tymed = TYMED_NULL;
1247 
1248   free_queue_item (&getdata->base);
1249 
1250   return S_OK;
1251 }
1252 
1253 static HRESULT STDMETHODCALLTYPE
idataobject_getdatahere(LPDATAOBJECT This,LPFORMATETC pFormatEtc,LPSTGMEDIUM pMedium)1254 idataobject_getdatahere (LPDATAOBJECT This,
1255                          LPFORMATETC  pFormatEtc,
1256                          LPSTGMEDIUM  pMedium)
1257 {
1258   GDK_NOTE (DND, g_print ("idataobject_getdatahere %p E_NOTIMPL\n", This));
1259 
1260   return E_NOTIMPL;
1261 }
1262 
1263 static HRESULT STDMETHODCALLTYPE
idataobject_querygetdata(LPDATAOBJECT This,LPFORMATETC pFormatEtc)1264 idataobject_querygetdata (LPDATAOBJECT This,
1265                           LPFORMATETC  pFormatEtc)
1266 {
1267   HRESULT hr;
1268 
1269   g_assert (_win32_main_thread == NULL ||
1270             _win32_main_thread != g_thread_self ());
1271 
1272   hr = query (This, pFormatEtc, NULL);
1273 
1274   GDK_NOTE (DND,
1275       g_print ("idataobject_querygetdata %p 0x%08x fmt, %p ptd, %lu aspect, %ld lindex, %0lx tymed - %s, return %#lx (%s)\n",
1276                This, pFormatEtc->cfFormat, pFormatEtc->ptd, pFormatEtc->dwAspect, pFormatEtc->lindex, pFormatEtc->tymed, _gdk_win32_cf_to_string (pFormatEtc->cfFormat),
1277                hr, (hr == S_OK) ? "S_OK" : (hr == DV_E_FORMATETC) ? "DV_E_FORMATETC" : (hr == DV_E_LINDEX) ? "DV_E_LINDEX" : (hr == DV_E_TYMED) ? "DV_E_TYMED" : (hr == DV_E_DVASPECT) ? "DV_E_DVASPECT" : "unknown meaning"));
1278 
1279   return hr;
1280 }
1281 
1282 static HRESULT STDMETHODCALLTYPE
idataobject_getcanonicalformatetc(LPDATAOBJECT This,LPFORMATETC pFormatEtcIn,LPFORMATETC pFormatEtcOut)1283 idataobject_getcanonicalformatetc (LPDATAOBJECT This,
1284                                    LPFORMATETC  pFormatEtcIn,
1285                                    LPFORMATETC  pFormatEtcOut)
1286 {
1287   GDK_NOTE (DND, g_print ("idataobject_getcanonicalformatetc %p E_NOTIMPL\n", This));
1288 
1289   return E_NOTIMPL;
1290 }
1291 
1292 static HRESULT STDMETHODCALLTYPE
idataobject_setdata(LPDATAOBJECT This,LPFORMATETC pFormatEtc,LPSTGMEDIUM pMedium,BOOL fRelease)1293 idataobject_setdata (LPDATAOBJECT This,
1294                      LPFORMATETC  pFormatEtc,
1295                      LPSTGMEDIUM  pMedium,
1296                      BOOL         fRelease)
1297 {
1298   GDK_NOTE (DND, g_print ("idataobject_setdata %p %s E_NOTIMPL\n",
1299                           This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
1300 
1301   return E_NOTIMPL;
1302 }
1303 
1304 static HRESULT STDMETHODCALLTYPE
idataobject_enumformatetc(LPDATAOBJECT This,DWORD dwDirection,LPENUMFORMATETC * ppEnumFormatEtc)1305 idataobject_enumformatetc (LPDATAOBJECT     This,
1306                            DWORD            dwDirection,
1307                            LPENUMFORMATETC *ppEnumFormatEtc)
1308 {
1309   g_assert (_win32_main_thread == NULL ||
1310             _win32_main_thread != g_thread_self ());
1311 
1312   if (dwDirection != DATADIR_GET)
1313     {
1314       GDK_NOTE (DND, g_print ("idataobject_enumformatetc %p E_NOTIMPL", This));
1315 
1316       return E_NOTIMPL;
1317     }
1318 
1319   *ppEnumFormatEtc = &enum_formats_new (((data_object *) This)->formats)->ief;
1320 
1321   GDK_NOTE (DND, g_print ("idataobject_enumformatetc %p -> %p S_OK", This, *ppEnumFormatEtc));
1322 
1323   return S_OK;
1324 }
1325 
1326 static HRESULT STDMETHODCALLTYPE
idataobject_dadvise(LPDATAOBJECT This,LPFORMATETC pFormatetc,DWORD advf,LPADVISESINK pAdvSink,DWORD * pdwConnection)1327 idataobject_dadvise (LPDATAOBJECT This,
1328                      LPFORMATETC  pFormatetc,
1329                      DWORD        advf,
1330                      LPADVISESINK pAdvSink,
1331                      DWORD       *pdwConnection)
1332 {
1333   GDK_NOTE (DND, g_print ("idataobject_dadvise %p E_NOTIMPL\n", This));
1334 
1335   return E_NOTIMPL;
1336 }
1337 
1338 static HRESULT STDMETHODCALLTYPE
idataobject_dunadvise(LPDATAOBJECT This,DWORD dwConnection)1339 idataobject_dunadvise (LPDATAOBJECT This,
1340                        DWORD         dwConnection)
1341 {
1342   GDK_NOTE (DND, g_print ("idataobject_dunadvise %p E_NOTIMPL\n", This));
1343 
1344   return E_NOTIMPL;
1345 }
1346 
1347 static HRESULT STDMETHODCALLTYPE
idataobject_enumdadvise(LPDATAOBJECT This,LPENUMSTATDATA * ppenumAdvise)1348 idataobject_enumdadvise (LPDATAOBJECT    This,
1349                          LPENUMSTATDATA *ppenumAdvise)
1350 {
1351   GDK_NOTE (DND, g_print ("idataobject_enumdadvise %p OLE_E_ADVISENOTSUPPORTED\n", This));
1352 
1353   return OLE_E_ADVISENOTSUPPORTED;
1354 }
1355 
1356 static ULONG STDMETHODCALLTYPE
ienumformatetc_addref(LPENUMFORMATETC This)1357 ienumformatetc_addref (LPENUMFORMATETC This)
1358 {
1359   enum_formats *en = (enum_formats *) This;
1360   int ref_count = ++en->ref_count;
1361 
1362   GDK_NOTE (DND, g_print ("ienumformatetc_addref %p %d\n", This, ref_count));
1363 
1364   return ref_count;
1365 }
1366 
1367 static HRESULT STDMETHODCALLTYPE
ienumformatetc_queryinterface(LPENUMFORMATETC This,REFIID riid,LPVOID * ppvObject)1368 ienumformatetc_queryinterface (LPENUMFORMATETC This,
1369                                REFIID          riid,
1370                                LPVOID         *ppvObject)
1371 {
1372   GDK_NOTE (DND, {
1373       g_print ("ienumformatetc_queryinterface %p", This);
1374       PRINT_GUID (riid);
1375     });
1376 
1377   *ppvObject = NULL;
1378 
1379   if (IsEqualGUID (riid, &IID_IUnknown))
1380     {
1381       GDK_NOTE (DND, g_print ("...IUnknown S_OK\n"));
1382       ienumformatetc_addref (This);
1383       *ppvObject = This;
1384       return S_OK;
1385     }
1386   else if (IsEqualGUID (riid, &IID_IEnumFORMATETC))
1387     {
1388       GDK_NOTE (DND, g_print ("...IEnumFORMATETC S_OK\n"));
1389       ienumformatetc_addref (This);
1390       *ppvObject = This;
1391       return S_OK;
1392     }
1393   else
1394     {
1395       GDK_NOTE (DND, g_print ("...E_NOINTERFACE\n"));
1396       return E_NOINTERFACE;
1397     }
1398 }
1399 
1400 static ULONG STDMETHODCALLTYPE
ienumformatetc_release(LPENUMFORMATETC This)1401 ienumformatetc_release (LPENUMFORMATETC This)
1402 {
1403   enum_formats *en = (enum_formats *) This;
1404   int ref_count = --en->ref_count;
1405 
1406   GDK_NOTE (DND, g_print ("ienumformatetc_release %p %d\n", This, ref_count));
1407 
1408   if (ref_count == 0)
1409     {
1410       g_array_unref (en->formats);
1411       g_free (This);
1412     }
1413 
1414   return ref_count;
1415 }
1416 
1417 static HRESULT STDMETHODCALLTYPE
ienumformatetc_next(LPENUMFORMATETC This,ULONG celt,LPFORMATETC elts,ULONG * nelt)1418 ienumformatetc_next (LPENUMFORMATETC This,
1419                      ULONG             celt,
1420                      LPFORMATETC     elts,
1421                      ULONG            *nelt)
1422 {
1423   enum_formats *en = (enum_formats *) This;
1424   ULONG i, n;
1425   ULONG formats_to_get = celt;
1426 
1427   GDK_NOTE (DND, g_print ("ienumformatetc_next %p %d %ld ", This, en->ix, celt));
1428 
1429   n = 0;
1430   for (i = 0; i < formats_to_get; i++)
1431     {
1432       UINT fmt;
1433       if (en->ix >= en->formats->len)
1434         break;
1435       fmt = g_array_index (en->formats, GdkWin32ContentFormatPair, en->ix++).w32format;
1436       /* skip internals */
1437       if (fmt == 0 || fmt > 0xFFFF)
1438         {
1439           formats_to_get += 1;
1440           continue;
1441         }
1442       elts[n].cfFormat = fmt;
1443       elts[n].ptd = NULL;
1444       elts[n].dwAspect = DVASPECT_CONTENT;
1445       elts[n].lindex = -1;
1446       elts[n].tymed = TYMED_HGLOBAL;
1447 
1448       n++;
1449     }
1450 
1451   if (nelt != NULL)
1452     *nelt = n;
1453 
1454   GDK_NOTE (DND, g_print ("%s\n", (n == celt) ? "S_OK" : "S_FALSE"));
1455 
1456   if (n == celt)
1457     return S_OK;
1458   else
1459     return S_FALSE;
1460 }
1461 
1462 static HRESULT STDMETHODCALLTYPE
ienumformatetc_skip(LPENUMFORMATETC This,ULONG celt)1463 ienumformatetc_skip (LPENUMFORMATETC This,
1464                      ULONG             celt)
1465 {
1466   enum_formats *en = (enum_formats *) This;
1467 
1468   GDK_NOTE (DND, g_print ("ienumformatetc_skip %p %d %ld S_OK\n", This, en->ix, celt));
1469 
1470   en->ix += celt;
1471 
1472   return S_OK;
1473 }
1474 
1475 static HRESULT STDMETHODCALLTYPE
ienumformatetc_reset(LPENUMFORMATETC This)1476 ienumformatetc_reset (LPENUMFORMATETC This)
1477 {
1478   enum_formats *en = (enum_formats *) This;
1479 
1480   GDK_NOTE (DND, g_print ("ienumformatetc_reset %p S_OK\n", This));
1481 
1482   en->ix = 0;
1483 
1484   return S_OK;
1485 }
1486 
1487 static HRESULT STDMETHODCALLTYPE
ienumformatetc_clone(LPENUMFORMATETC This,LPENUMFORMATETC * ppEnumFormatEtc)1488 ienumformatetc_clone (LPENUMFORMATETC  This,
1489                       LPENUMFORMATETC *ppEnumFormatEtc)
1490 {
1491   enum_formats *en = (enum_formats *) This;
1492   enum_formats *new;
1493 
1494   GDK_NOTE (DND, g_print ("ienumformatetc_clone %p S_OK\n", This));
1495 
1496   new = enum_formats_new (en->formats);
1497 
1498   new->ix = en->ix;
1499 
1500   *ppEnumFormatEtc = &new->ief;
1501 
1502   return S_OK;
1503 }
1504 
1505 static IDropSourceVtbl ids_vtbl = {
1506   idropsource_queryinterface,
1507   idropsource_addref,
1508   idropsource_release,
1509   idropsource_querycontinuedrag,
1510   idropsource_givefeedback
1511 };
1512 
1513 static IDropSourceNotifyVtbl idsn_vtbl = {
1514   (HRESULT (STDMETHODCALLTYPE *) (IDropSourceNotify *, REFIID , LPVOID *)) idropsource_queryinterface,
1515   (ULONG (STDMETHODCALLTYPE *) (IDropSourceNotify *)) idropsource_addref,
1516   (ULONG (STDMETHODCALLTYPE *) (IDropSourceNotify *)) idropsource_release,
1517   idropsourcenotify_dragentertarget,
1518   idropsourcenotify_dragleavetarget
1519 };
1520 
1521 static IDataObjectVtbl ido_vtbl = {
1522   idataobject_queryinterface,
1523   idataobject_addref,
1524   idataobject_release,
1525   idataobject_getdata,
1526   idataobject_getdatahere,
1527   idataobject_querygetdata,
1528   idataobject_getcanonicalformatetc,
1529   idataobject_setdata,
1530   idataobject_enumformatetc,
1531   idataobject_dadvise,
1532   idataobject_dunadvise,
1533   idataobject_enumdadvise
1534 };
1535 
1536 static IEnumFORMATETCVtbl ief_vtbl = {
1537   ienumformatetc_queryinterface,
1538   ienumformatetc_addref,
1539   ienumformatetc_release,
1540   ienumformatetc_next,
1541   ienumformatetc_skip,
1542   ienumformatetc_reset,
1543   ienumformatetc_clone
1544 };
1545 
1546 static source_drag_context *
source_context_new(GdkDrag * drag,GdkContentFormats * formats)1547 source_context_new (GdkDrag           *drag,
1548                     GdkContentFormats *formats)
1549 {
1550   GdkWin32Drag *drag_win32;
1551   source_drag_context *result;
1552   GdkSurface *surface;
1553 
1554   drag_win32 = GDK_WIN32_DRAG (drag);
1555 
1556   g_object_get (drag, "surface", &surface, NULL);
1557 
1558   result = g_new0 (source_drag_context, 1);
1559   result->drag = g_object_ref (drag);
1560   result->ids.lpVtbl = &ids_vtbl;
1561   result->idsn.lpVtbl = &idsn_vtbl;
1562   result->ref_count = 1;
1563   result->source_window_handle = GDK_SURFACE_HWND (surface);
1564   result->scale = drag_win32->scale;
1565   result->util_data.state = GDK_WIN32_DND_PENDING; /* Implicit */
1566   result->dest_window_handle = INVALID_HANDLE_VALUE;
1567 
1568   g_object_unref (surface);
1569 
1570   GDK_NOTE (DND, g_print ("source_context_new: %p (drag %p)\n", result, result->drag));
1571 
1572   return result;
1573 }
1574 
1575 static data_object *
data_object_new(GdkDrag * drag)1576 data_object_new (GdkDrag *drag)
1577 {
1578   data_object *result;
1579   const char * const *mime_types;
1580   gsize n_mime_types, i;
1581 
1582   result = g_new0 (data_object, 1);
1583 
1584   result->ido.lpVtbl = &ido_vtbl;
1585   result->ref_count = 1;
1586   result->drag = drag;
1587   result->formats = g_array_new (FALSE, FALSE, sizeof (GdkWin32ContentFormatPair));
1588 
1589   mime_types = gdk_content_formats_get_mime_types (gdk_drag_get_formats (drag), &n_mime_types);
1590 
1591   for (i = 0; i < n_mime_types; i++)
1592     {
1593       int added_count = 0;
1594       int j;
1595 
1596       GDK_NOTE (DND, g_print ("DataObject supports contentformat 0x%p (%s)\n", mime_types[i], mime_types[i]));
1597 
1598       added_count = _gdk_win32_add_contentformat_to_pairs (mime_types[i], result->formats);
1599 
1600       for (j = 0; j < added_count && result->formats->len - 1 - j >= 0; j++)
1601         GDK_NOTE (DND, g_print ("DataObject will support w32format 0x%x\n", g_array_index (result->formats, GdkWin32ContentFormatPair, j).w32format));
1602     }
1603 
1604   GDK_NOTE (DND, g_print ("data_object_new: %p\n", result));
1605 
1606   return result;
1607 }
1608 
1609 static enum_formats *
enum_formats_new(GArray * formats)1610 enum_formats_new (GArray *formats)
1611 {
1612   enum_formats *result;
1613 
1614   result = g_new0 (enum_formats, 1);
1615 
1616   result->ief.lpVtbl = &ief_vtbl;
1617   result->ref_count = 1;
1618   result->ix = 0;
1619   result->formats = g_array_ref (formats);
1620 
1621   return result;
1622 }
1623 
1624 void
_gdk_drag_init(void)1625 _gdk_drag_init (void)
1626 {
1627   HRESULT hr;
1628 
1629   CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
1630 
1631   hr = OleInitialize (NULL);
1632 
1633   if (! SUCCEEDED (hr))
1634     g_error ("OleInitialize failed");
1635 }
1636 
1637 void
_gdk_win32_dnd_exit(void)1638 _gdk_win32_dnd_exit (void)
1639 {
1640   OleUninitialize ();
1641   CoUninitialize ();
1642 }
1643 
1644 static GdkSurface *
create_drag_surface(GdkDisplay * display)1645 create_drag_surface (GdkDisplay *display)
1646 {
1647   GdkSurface *surface;
1648 
1649   surface = _gdk_win32_display_create_surface (display,
1650                                                GDK_SURFACE_TEMP,
1651                                                NULL,
1652                                                0, 0, 100, 100);
1653 
1654   return surface;
1655 }
1656 
1657 GdkDrag *
_gdk_win32_surface_drag_begin(GdkSurface * surface,GdkDevice * device,GdkContentProvider * content,GdkDragAction actions,double dx,double dy)1658 _gdk_win32_surface_drag_begin (GdkSurface         *surface,
1659                                GdkDevice          *device,
1660                                GdkContentProvider *content,
1661                                GdkDragAction       actions,
1662                                double              dx,
1663                                double              dy)
1664 {
1665   GdkDrag *drag;
1666   GdkWin32Drag *drag_win32;
1667   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
1668   double px, py;
1669   int x_root, y_root;
1670   GdkWin32DnDThreadDoDragDrop *ddd;
1671   source_drag_context *source_ctx;
1672   data_object         *data_obj;
1673 
1674   g_return_val_if_fail (surface != NULL, NULL);
1675 
1676   drag = gdk_drag_new (gdk_surface_get_display (surface),
1677                        surface,
1678                        content,
1679                        actions,
1680                        device);
1681   drag_win32 = GDK_WIN32_DRAG (drag);
1682 
1683   GDK_NOTE (DND, g_print ("_gdk_win32_surface_drag_begin\n"));
1684 
1685   _gdk_device_win32_query_state (device, NULL, NULL, &px, &py, NULL);
1686   x_root = round (px + dx);
1687   y_root = round (py + dy);
1688 
1689   drag_win32->start_x = x_root;
1690   drag_win32->start_y = y_root;
1691   drag_win32->util_data.last_x = drag_win32->start_x;
1692   drag_win32->util_data.last_y = drag_win32->start_y;
1693 
1694   g_set_object (&drag_win32->grab_surface, surface);
1695 
1696   drag_win32->drag_surface = create_drag_surface (gdk_surface_get_display (surface));
1697 
1698   if (!drag_context_grab (drag))
1699     {
1700       g_object_unref (drag);
1701       return FALSE;
1702     }
1703 
1704   ddd = g_new0 (GdkWin32DnDThreadDoDragDrop, 1);
1705 
1706   source_ctx = source_context_new (drag, gdk_drag_get_formats (drag));
1707   data_obj = data_object_new (drag);
1708 
1709   ddd->base.item_type = GDK_WIN32_DND_THREAD_QUEUE_ITEM_DO_DRAG_DROP;
1710   ddd->base.opaque_context = drag_win32;
1711   ddd->src_context = source_ctx;
1712   ddd->src_object = data_obj;
1713   ddd->allowed_drop_effects = 0;
1714   if (actions & GDK_ACTION_COPY)
1715     ddd->allowed_drop_effects |= DROPEFFECT_COPY;
1716   if (actions & GDK_ACTION_MOVE)
1717     ddd->allowed_drop_effects |= DROPEFFECT_MOVE;
1718   if (actions & GDK_ACTION_LINK)
1719     ddd->allowed_drop_effects |= DROPEFFECT_LINK;
1720 
1721   g_hash_table_replace (clipdrop->active_source_drags, g_object_ref (drag), ddd);
1722   increment_dnd_queue_counter ();
1723   g_async_queue_push (clipdrop->dnd_queue, ddd);
1724   API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id, thread_wakeup_message, 0, 0));
1725 
1726   drag_win32->util_data.state = GDK_WIN32_DND_PENDING;
1727 
1728   move_drag_surface (drag, x_root, y_root);
1729 
1730   return drag;
1731 }
1732 
1733 static DWORD
manufacture_keystate_from_GMT(GdkModifierType state)1734 manufacture_keystate_from_GMT (GdkModifierType state)
1735 {
1736   DWORD key_state = 0;
1737 
1738   if (state & GDK_ALT_MASK)
1739     key_state |= MK_ALT;
1740   if (state & GDK_CONTROL_MASK)
1741     key_state |= MK_CONTROL;
1742   if (state & GDK_SHIFT_MASK)
1743     key_state |= MK_SHIFT;
1744   if (state & GDK_BUTTON1_MASK)
1745     key_state |= MK_LBUTTON;
1746   if (state & GDK_BUTTON2_MASK)
1747     key_state |= MK_MBUTTON;
1748   if (state & GDK_BUTTON3_MASK)
1749     key_state |= MK_RBUTTON;
1750 
1751   return key_state;
1752 }
1753 
1754 static void
send_source_state_update(GdkWin32Clipdrop * clipdrop,GdkWin32Drag * drag_win32,gpointer * ddd)1755 send_source_state_update (GdkWin32Clipdrop    *clipdrop,
1756                           GdkWin32Drag        *drag_win32,
1757                           gpointer            *ddd)
1758 {
1759   GdkWin32DnDThreadUpdateDragState *status = g_new0 (GdkWin32DnDThreadUpdateDragState, 1);
1760   status->base.item_type = GDK_WIN32_DND_THREAD_QUEUE_ITEM_UPDATE_DRAG_STATE;
1761   status->opaque_ddd = ddd;
1762   status->produced_util_data = drag_win32->util_data;
1763   increment_dnd_queue_counter ();
1764   g_async_queue_push (clipdrop->dnd_queue, status);
1765   API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id, thread_wakeup_message, 0, 0));
1766 }
1767 
1768 static void
gdk_win32_drag_drop(GdkDrag * drag,guint32 time_)1769 gdk_win32_drag_drop (GdkDrag *drag,
1770                      guint32  time_)
1771 {
1772   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
1773   GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
1774   gpointer ddd;
1775 
1776   g_assert (_win32_main_thread == NULL ||
1777             _win32_main_thread == g_thread_self ());
1778 
1779   g_return_if_fail (drag != NULL);
1780 
1781   GDK_NOTE (DND, g_print ("gdk_win32_drag_drop\n"));
1782 
1783   ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
1784 
1785   drag_win32->util_data.state = GDK_WIN32_DND_DROPPED;
1786 
1787   if (ddd)
1788     send_source_state_update (clipdrop, drag_win32, ddd);
1789 }
1790 
1791 static void
gdk_win32_drag_set_cursor(GdkDrag * drag,GdkCursor * cursor)1792 gdk_win32_drag_set_cursor (GdkDrag *drag,
1793                            GdkCursor      *cursor)
1794 {
1795   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
1796 
1797   GDK_NOTE (DND, g_print ("gdk_win32_drag_set_cursor: 0x%p 0x%p\n", drag, cursor));
1798 
1799   if (!g_set_object (&drag_win32->cursor, cursor))
1800     return;
1801 
1802   if (drag_win32->grab_seat)
1803     {
1804       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1805       gdk_device_grab (gdk_seat_get_pointer (drag_win32->grab_seat),
1806                        drag_win32->grab_surface,
1807                        FALSE,
1808                        GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
1809                        cursor, GDK_CURRENT_TIME);
1810       G_GNUC_END_IGNORE_DEPRECATIONS;
1811     }
1812 }
1813 
1814 static double
ease_out_cubic(double t)1815 ease_out_cubic (double t)
1816 {
1817   double p = t - 1;
1818   return p * p * p + 1;
1819 }
1820 
1821 #define ANIM_TIME 500000 /* half a second */
1822 
1823 typedef struct _GdkDragAnim GdkDragAnim;
1824 struct _GdkDragAnim {
1825   GdkWin32Drag *drag;
1826   GdkFrameClock *frame_clock;
1827   gint64 start_time;
1828 };
1829 
1830 static void
gdk_drag_anim_destroy(GdkDragAnim * anim)1831 gdk_drag_anim_destroy (GdkDragAnim *anim)
1832 {
1833   g_object_unref (anim->drag);
1834   g_slice_free (GdkDragAnim, anim);
1835 }
1836 
1837 static gboolean
gdk_drag_anim_timeout(gpointer data)1838 gdk_drag_anim_timeout (gpointer data)
1839 {
1840   GdkDragAnim *anim = data;
1841   GdkWin32Drag *drag = anim->drag;
1842   GdkFrameClock *frame_clock = anim->frame_clock;
1843   gint64 current_time;
1844   double f;
1845   double t;
1846   int x, y;
1847 
1848   if (!frame_clock)
1849     return G_SOURCE_REMOVE;
1850 
1851   current_time = gdk_frame_clock_get_frame_time (frame_clock);
1852 
1853   f = (current_time - anim->start_time) / (double) ANIM_TIME;
1854 
1855   if (f >= 1.0)
1856     return G_SOURCE_REMOVE;
1857 
1858   t = ease_out_cubic (f);
1859 
1860   gdk_win32_surface_show (drag->drag_surface, FALSE);
1861   x = (drag->util_data.last_x +
1862        (drag->start_x - drag->util_data.last_x) * t -
1863        drag->hot_x);
1864   y = (drag->util_data.last_y +
1865        (drag->start_y - drag->util_data.last_y) * t -
1866        drag->hot_y);
1867   gdk_win32_surface_move (drag->drag_surface, x, y);
1868 
1869   return G_SOURCE_CONTINUE;
1870 }
1871 
1872 static void
gdk_win32_drag_drop_done(GdkDrag * drag,gboolean success)1873 gdk_win32_drag_drop_done (GdkDrag  *drag,
1874                           gboolean  success)
1875 {
1876   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
1877   GdkDragAnim *anim;
1878   GdkWin32Clipdrop *clipdrop;
1879   gpointer ddd;
1880 /*
1881   cairo_surface_t *win_surface;
1882   cairo_surface_t *surface;
1883   cairo_t *cr;
1884 */
1885   guint id;
1886 
1887   GDK_NOTE (DND, g_print ("gdk_win32_drag_drop_done: 0x%p %s\n",
1888                           drag,
1889                           success ? "dropped successfully" : "dropped unsuccessfully"));
1890 
1891   /* FIXME: This is temporary, until the code is fixed to ensure that
1892    * gdk_drag_finish () is called by GTK.
1893    */
1894   clipdrop = _gdk_win32_clipdrop_get ();
1895   ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
1896 
1897   if (success)
1898     drag_win32->util_data.state = GDK_WIN32_DND_DROPPED;
1899   else
1900     drag_win32->util_data.state = GDK_WIN32_DND_NONE;
1901 
1902   if (ddd)
1903     send_source_state_update (clipdrop, drag_win32, ddd);
1904 
1905   drag_win32->handle_events = FALSE;
1906 
1907   if (success)
1908     {
1909       gdk_surface_hide (drag_win32->drag_surface);
1910 
1911       return;
1912     }
1913 
1914 /*
1915   win_surface = _gdk_surface_ref_cairo_surface (drag_win32->drag_surface);
1916   surface = gdk_surface_create_similar_surface (drag_win32->drag_surface,
1917                                                 cairo_surface_get_content (win_surface),
1918                                                 gdk_surface_get_width (drag_win32->drag_surface),
1919                                                 gdk_surface_get_height (drag_win32->drag_surface));
1920   cr = cairo_create (surface);
1921   cairo_set_source_surface (cr, win_surface, 0, 0);
1922   cairo_paint (cr);
1923   cairo_destroy (cr);
1924   cairo_surface_destroy (win_surface);
1925 
1926   pattern = cairo_pattern_create_for_surface (surface);
1927 
1928   gdk_surface_set_background_pattern (drag_win32->drag_surface, pattern);
1929 
1930   cairo_pattern_destroy (pattern);
1931   cairo_surface_destroy (surface);
1932 */
1933 
1934   anim = g_slice_new0 (GdkDragAnim);
1935   g_set_object (&anim->drag, drag_win32);
1936   anim->frame_clock = gdk_surface_get_frame_clock (drag_win32->drag_surface);
1937   anim->start_time = gdk_frame_clock_get_frame_time (anim->frame_clock);
1938 
1939   GDK_NOTE (DND, g_print ("gdk_win32_drag_drop_done: animate the drag window from %d : %d to %d : %d\n",
1940                           drag_win32->util_data.last_x, drag_win32->util_data.last_y,
1941                           drag_win32->start_x, drag_win32->start_y));
1942 
1943   id = g_timeout_add_full (G_PRIORITY_DEFAULT, 17,
1944                            gdk_drag_anim_timeout, anim,
1945                            (GDestroyNotify) gdk_drag_anim_destroy);
1946   gdk_source_set_static_name_by_id (id, "[gtk] gdk_drag_anim_timeout");
1947 }
1948 
1949 static gboolean
drag_context_grab(GdkDrag * drag)1950 drag_context_grab (GdkDrag *drag)
1951 {
1952   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
1953   GdkSeatCapabilities capabilities;
1954   GdkSeat *seat;
1955   GdkCursor *cursor;
1956 
1957   GDK_NOTE (DND, g_print ("drag_context_grab: 0x%p with grab surface 0x%p\n",
1958                           drag,
1959                           drag_win32->grab_surface));
1960 
1961   if (!drag_win32->grab_surface)
1962     return FALSE;
1963 
1964   seat = gdk_device_get_seat (gdk_drag_get_device (drag));
1965 
1966   capabilities = GDK_SEAT_CAPABILITY_ALL;
1967 
1968   cursor = gdk_drag_get_cursor (drag, gdk_drag_get_selected_action (drag));
1969   g_set_object (&drag_win32->cursor, cursor);
1970 
1971   if (gdk_seat_grab (seat, drag_win32->grab_surface,
1972                      capabilities, FALSE,
1973                      drag_win32->cursor, NULL, NULL, NULL) != GDK_GRAB_SUCCESS)
1974     return FALSE;
1975 
1976   g_set_object (&drag_win32->grab_seat, seat);
1977 
1978   /* TODO: Should be grabbing keys here, to support keynav. SetWindowsHookEx()? */
1979 
1980   return TRUE;
1981 }
1982 
1983 static void
drag_context_ungrab(GdkDrag * drag)1984 drag_context_ungrab (GdkDrag *drag)
1985 {
1986   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
1987 
1988   GDK_NOTE (DND, g_print ("drag_context_ungrab: 0x%p 0x%p\n",
1989                           drag,
1990                           drag_win32->grab_seat));
1991 
1992   if (!drag_win32->grab_seat)
1993     return;
1994 
1995   gdk_seat_ungrab (drag_win32->grab_seat);
1996 
1997   g_clear_object (&drag_win32->grab_seat);
1998 
1999   /* TODO: Should be ungrabbing keys here */
2000 }
2001 
2002 static void
gdk_win32_drag_cancel(GdkDrag * drag,GdkDragCancelReason reason)2003 gdk_win32_drag_cancel (GdkDrag             *drag,
2004                        GdkDragCancelReason  reason)
2005 {
2006   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
2007 
2008   const char *reason_str = NULL;
2009   switch (reason)
2010     {
2011     case GDK_DRAG_CANCEL_NO_TARGET:
2012       reason_str = "no target";
2013       break;
2014     case GDK_DRAG_CANCEL_USER_CANCELLED:
2015       reason_str = "user cancelled";
2016       break;
2017     case GDK_DRAG_CANCEL_ERROR:
2018       reason_str = "error";
2019       break;
2020     default:
2021       reason_str = "<unknown>";
2022       break;
2023     }
2024 
2025   GDK_NOTE (DND, g_print ("gdk_win32_drag_cancel: 0x%p %s\n",
2026                           drag,
2027                           reason_str));
2028 
2029   gdk_drag_set_cursor (drag, NULL);
2030   drag_context_ungrab (drag);
2031   gdk_drag_drop_done (drag, FALSE);
2032 }
2033 
2034 static void
gdk_win32_drag_drop_performed(GdkDrag * drag,guint32 time_)2035 gdk_win32_drag_drop_performed (GdkDrag *drag,
2036                                guint32  time_)
2037 {
2038   GDK_NOTE (DND, g_print ("gdk_win32_drag_drop_performed: 0x%p %u\n",
2039                           drag,
2040                           time_));
2041 
2042   gdk_win32_drag_drop (drag, time_);
2043   gdk_drag_set_cursor (drag, NULL);
2044   drag_context_ungrab (drag);
2045 }
2046 
2047 #define BIG_STEP 20
2048 #define SMALL_STEP 1
2049 
2050 static gboolean
gdk_dnd_handle_motion_event(GdkDrag * drag,GdkEvent * event)2051 gdk_dnd_handle_motion_event (GdkDrag  *drag,
2052                              GdkEvent *event)
2053 {
2054   GdkModifierType state;
2055   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
2056   DWORD key_state;
2057   double x, y;
2058   double x_root, y_root;
2059   GdkWin32Clipdrop *clipdrop;
2060 
2061   GDK_NOTE (DND, g_print ("gdk_dnd_handle_motion_event: 0x%p\n", drag));
2062 
2063   state = gdk_event_get_modifier_state (event);
2064   gdk_event_get_position (event, &x, &y);
2065 
2066   x_root = event->surface->x + x;
2067   y_root = event->surface->y + y;
2068 
2069   if (drag_win32->drag_surface)
2070     move_drag_surface (drag, x_root, y_root);
2071 
2072   key_state = manufacture_keystate_from_GMT (state);
2073 
2074   clipdrop = _gdk_win32_clipdrop_get ();
2075 
2076   GDK_NOTE (DND, g_print ("Post WM_MOUSEMOVE keystate=%lu\n", key_state));
2077 
2078   drag_win32->util_data.last_x = x_root;
2079   drag_win32->util_data.last_y = y_root;
2080 
2081   API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id,
2082                                 WM_MOUSEMOVE,
2083                                 key_state,
2084                                 MAKELPARAM (x_root * drag_win32->scale - _gdk_offset_x,
2085                                             y_root * drag_win32->scale - _gdk_offset_y)));
2086 
2087   return TRUE;
2088 }
2089 
2090 static gboolean
gdk_dnd_handle_key_event(GdkDrag * drag,GdkEvent * event)2091 gdk_dnd_handle_key_event (GdkDrag  *drag,
2092                           GdkEvent *event)
2093 {
2094   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
2095   GdkModifierType state;
2096   GdkDevice *pointer;
2097   GdkSeat *seat;
2098   int dx, dy;
2099 
2100   GDK_NOTE (DND, g_print ("gdk_dnd_handle_key_event: 0x%p\n", drag));
2101 
2102   state = gdk_event_get_modifier_state (event);
2103 
2104   dx = dy = 0;
2105   seat = gdk_event_get_seat (event);
2106   pointer = gdk_seat_get_pointer (seat);
2107 
2108   if (gdk_event_get_event_type (event) == GDK_KEY_PRESS)
2109     {
2110       switch (gdk_key_event_get_keyval (event))
2111         {
2112         case GDK_KEY_Escape:
2113           gdk_drag_cancel (drag, GDK_DRAG_CANCEL_USER_CANCELLED);
2114           return TRUE;
2115 
2116         case GDK_KEY_space:
2117         case GDK_KEY_Return:
2118         case GDK_KEY_ISO_Enter:
2119         case GDK_KEY_KP_Enter:
2120         case GDK_KEY_KP_Space:
2121           if ((gdk_drag_get_selected_action (drag) != 0) &&
2122               (drag_win32->dest_window != INVALID_HANDLE_VALUE))
2123             {
2124               g_signal_emit_by_name (drag, "drop-performed");
2125             }
2126           else
2127             gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
2128 
2129           return TRUE;
2130 
2131         case GDK_KEY_Up:
2132         case GDK_KEY_KP_Up:
2133           dy = (state & GDK_ALT_MASK) ? -BIG_STEP : -SMALL_STEP;
2134           break;
2135 
2136         case GDK_KEY_Down:
2137         case GDK_KEY_KP_Down:
2138           dy = (state & GDK_ALT_MASK) ? BIG_STEP : SMALL_STEP;
2139           break;
2140 
2141         case GDK_KEY_Left:
2142         case GDK_KEY_KP_Left:
2143           dx = (state & GDK_ALT_MASK) ? -BIG_STEP : -SMALL_STEP;
2144           break;
2145 
2146         case GDK_KEY_Right:
2147         case GDK_KEY_KP_Right:
2148           dx = (state & GDK_ALT_MASK) ? BIG_STEP : SMALL_STEP;
2149           break;
2150         }
2151     }
2152 
2153   /* The state is not yet updated in the event, so we need
2154    * to query it here.
2155    */
2156   _gdk_device_win32_query_state (pointer, NULL, NULL, NULL, NULL, &state);
2157 
2158   if (dx != 0 || dy != 0)
2159     {
2160       drag_win32->util_data.last_x += dx;
2161       drag_win32->util_data.last_y += dy;
2162     }
2163 
2164   if (drag_win32->drag_surface)
2165     move_drag_surface (drag, drag_win32->util_data.last_x, drag_win32->util_data.last_y);
2166 
2167   return TRUE;
2168 }
2169 
2170 static gboolean
gdk_dnd_handle_grab_broken_event(GdkDrag * drag,GdkEvent * event)2171 gdk_dnd_handle_grab_broken_event (GdkDrag  *drag,
2172                                   GdkEvent *event)
2173 {
2174   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
2175 
2176   GDK_NOTE (DND, g_print ("gdk_dnd_handle_grab_broken_event: 0x%p\n", drag));
2177 
2178   /* Don't cancel if we break the implicit grab from the initial button_press.
2179    * Also, don't cancel if we re-grab on the widget or on our grab window, for
2180    * example, when changing the drag cursor.
2181    */
2182   if (/* FIXME: event->implicit || */
2183       gdk_grab_broken_event_get_grab_surface (event) == drag_win32->drag_surface ||
2184       gdk_grab_broken_event_get_grab_surface (event) == drag_win32->grab_surface)
2185     return FALSE;
2186 
2187   if (gdk_event_get_device (event) != gdk_drag_get_device (drag))
2188     return FALSE;
2189 
2190   gdk_drag_cancel (drag, GDK_DRAG_CANCEL_ERROR);
2191   return TRUE;
2192 }
2193 
2194 static gboolean
gdk_dnd_handle_button_event(GdkDrag * drag,GdkEvent * event)2195 gdk_dnd_handle_button_event (GdkDrag  *drag,
2196                              GdkEvent *event)
2197 {
2198   GDK_NOTE (DND, g_print ("gdk_dnd_handle_button_event: 0x%p\n", drag));
2199 
2200 #if 0
2201   /* FIXME: Check the button matches */
2202   if (event->button != drag_win32->button)
2203     return FALSE;
2204 #endif
2205 
2206   if ((gdk_drag_get_selected_action (drag) != 0))
2207     {
2208       g_signal_emit_by_name (drag, "drop-performed");
2209     }
2210   else
2211     gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
2212 
2213   /* Make sure GTK gets mouse release button event */
2214   return FALSE;
2215 }
2216 
2217 gboolean
gdk_win32_drag_handle_event(GdkDrag * drag,GdkEvent * event)2218 gdk_win32_drag_handle_event (GdkDrag  *drag,
2219                              GdkEvent *event)
2220 {
2221   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
2222 
2223   if (!drag_win32->grab_seat)
2224     return FALSE;
2225   if (!drag_win32->handle_events)
2226     {
2227       /* FIXME: remove this functionality once gtk no longer calls DnD after drag_done() */
2228       g_warning ("Got an event %d for drag context %p, even though it's done!",
2229                  event->event_type, drag);
2230       return FALSE;
2231     }
2232 
2233   switch (gdk_event_get_event_type (event))
2234     {
2235     case GDK_MOTION_NOTIFY:
2236       return gdk_dnd_handle_motion_event (drag, event);
2237     case GDK_BUTTON_RELEASE:
2238       return gdk_dnd_handle_button_event (drag, event);
2239     case GDK_KEY_PRESS:
2240     case GDK_KEY_RELEASE:
2241       return gdk_dnd_handle_key_event (drag, event);
2242     case GDK_GRAB_BROKEN:
2243       return gdk_dnd_handle_grab_broken_event (drag, event);
2244     default:
2245       break;
2246     }
2247 
2248   return FALSE;
2249 }
2250 
2251 static GdkSurface *
gdk_win32_drag_get_drag_surface(GdkDrag * drag)2252 gdk_win32_drag_get_drag_surface (GdkDrag *drag)
2253 {
2254   return GDK_WIN32_DRAG (drag)->drag_surface;
2255 }
2256 
2257 static void
gdk_win32_drag_set_hotspot(GdkDrag * drag,int hot_x,int hot_y)2258 gdk_win32_drag_set_hotspot (GdkDrag *drag,
2259                             int      hot_x,
2260                             int      hot_y)
2261 {
2262   GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
2263 
2264   GDK_NOTE (DND, g_print ("gdk_drag_set_hotspot: 0x%p %d:%d\n",
2265                           drag,
2266                           hot_x, hot_y));
2267 
2268   drag_win32->hot_x = hot_x;
2269   drag_win32->hot_y = hot_y;
2270 
2271   if (drag_win32->grab_seat)
2272     {
2273       /* DnD is managed, update current position */
2274       move_drag_surface (drag, drag_win32->util_data.last_x, drag_win32->util_data.last_y);
2275     }
2276 }
2277 
2278 static void
gdk_win32_drag_class_init(GdkWin32DragClass * klass)2279 gdk_win32_drag_class_init (GdkWin32DragClass *klass)
2280 {
2281   GObjectClass *object_class = G_OBJECT_CLASS (klass);
2282   GdkDragClass *drag_class = GDK_DRAG_CLASS (klass);
2283 
2284   object_class->finalize = gdk_win32_drag_finalize;
2285 
2286   drag_class->get_drag_surface = gdk_win32_drag_get_drag_surface;
2287   drag_class->set_hotspot = gdk_win32_drag_set_hotspot;
2288   drag_class->drop_done = gdk_win32_drag_drop_done;
2289   drag_class->set_cursor = gdk_win32_drag_set_cursor;
2290   drag_class->cancel = gdk_win32_drag_cancel;
2291   drag_class->drop_performed = gdk_win32_drag_drop_performed;
2292   drag_class->handle_event = gdk_win32_drag_handle_event;
2293 }
2294