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