VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnD.cpp@ 50460

Last change on this file since 50460 was 50460, checked in by vboxsync, 11 years ago

DnD: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.8 KB
Line 
1/* $Id: VBoxDnD.cpp 50460 2014-02-14 09:46:58Z vboxsync $ */
2/** @file
3 * VBoxDnD.cpp - Windows-specific bits of the drag'n drop service.
4 */
5
6/*
7 * Copyright (C) 2013-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#include <windows.h>
18#include "VBoxTray.h"
19#include "VBoxHelpers.h"
20#include "VBoxDnD.h"
21
22#include <VBox/VBoxGuestLib.h>
23#include "VBox/HostServices/DragAndDropSvc.h"
24
25#include <iprt/asm.h>
26#include <iprt/assert.h>
27#include <iprt/err.h>
28#include <iprt/ldr.h>
29#include <iprt/list.h>
30#include <iprt/mem.h>
31
32#include <iprt/cpp/mtlist.h>
33#include <iprt/cpp/ministring.h>
34
35#include <iprt/cpp/mtlist.h>
36
37#include <VBoxGuestInternal.h>
38
39/* Enable this define to see the proxy window(s) when debugging
40 * their behavior. Don't have this enabled in release builds! */
41#ifdef DEBUG
42//# define VBOX_DND_DEBUG_WND
43#endif
44
45/** @todo Merge this with messages from VBoxTray.h. */
46#define WM_VBOXTRAY_DND_MESSAGE WM_APP + 401
47
48/** Function pointer for SendInput(). This only is available starting
49 * at NT4 SP3+. */
50typedef BOOL (WINAPI *PFNSENDINPUT)(UINT, LPINPUT, int);
51
52/** Static pointer to SendInput() function. */
53static PFNSENDINPUT s_pfnSendInput = NULL;
54
55static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
56static LRESULT CALLBACK vboxDnDWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
57
58VBoxDnDWnd::VBoxDnDWnd(void)
59 : hWnd(NULL),
60 mfMouseButtonDown(false),
61#ifdef VBOX_WITH_DRAG_AND_DROP_GH
62 pDropTarget(NULL),
63#endif
64 mClientID(UINT32_MAX),
65 mMode(Unknown),
66 mState(Uninitialized)
67{
68 RT_ZERO(startupInfo);
69
70 reset();
71}
72
73VBoxDnDWnd::~VBoxDnDWnd(void)
74{
75 /** @todo Shutdown crit sect / event etc! */
76
77 reset();
78}
79
80int VBoxDnDWnd::Initialize(PVBOXDNDCONTEXT pContext)
81{
82 AssertPtrReturn(pContext, VERR_INVALID_POINTER);
83
84 /* Save the context. */
85 this->pContext = pContext;
86
87 int rc = RTSemEventCreate(&mEventSem);
88 if (RT_SUCCESS(rc))
89 rc = RTCritSectInit(&mCritSect);
90
91 if (RT_SUCCESS(rc))
92 {
93 /* Message pump thread for our proxy window. */
94 rc = RTThreadCreate(&gCtx.hEvtQueue, VBoxDnDWnd::Thread, this,
95 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
96 "VBoxTrayDnDWnd");
97 if (RT_FAILURE(rc))
98 LogRel(("DnD: Failed to start proxy window thread, rc=%Rrc\n", rc));
99 /** @todo Wait for thread to be started! */
100 }
101
102 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
103 return rc;
104}
105
106/**
107 * Thread for handling the window's message pump.
108 *
109 * @return IPRT status code.
110 * @param hThread
111 * @param pvUser
112 */
113/* static */
114int VBoxDnDWnd::Thread(RTTHREAD hThread, void *pvUser)
115{
116 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
117
118 VBoxDnDWnd *pThis = (VBoxDnDWnd*)pvUser;
119 AssertPtr(pThis);
120
121 PVBOXDNDCONTEXT pContext = pThis->pContext;
122 AssertPtr(pContext);
123 AssertPtr(pContext->pEnv);
124
125 HINSTANCE hInstance = pContext->pEnv->hInstance;
126 Assert(hInstance != 0);
127
128 /* Create our proxy window. */
129 WNDCLASSEX wndClass;
130 RT_ZERO(wndClass);
131
132 wndClass.cbSize = sizeof(WNDCLASSEX);
133 wndClass.lpfnWndProc = vboxDnDWndProc;
134 wndClass.lpszClassName = "VBoxTrayDnDWnd";
135 wndClass.hInstance = hInstance;
136 wndClass.style = CS_NOCLOSE;
137#ifdef VBOX_DND_DEBUG_WND
138 wndClass.style |= CS_HREDRAW | CS_VREDRAW;
139 wndClass.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(255, 0, 0)));
140#else
141 wndClass.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
142#endif
143
144 int rc = VINF_SUCCESS;
145 if (!RegisterClassEx(&wndClass))
146 {
147 DWORD dwErr = GetLastError();
148 LogFlowFunc(("Unable to register proxy window class, error=%ld\n", dwErr));
149 rc = RTErrConvertFromWin32(dwErr);
150 }
151
152 if (RT_SUCCESS(rc))
153 {
154 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE;
155 DWORD dwStyle = WS_POPUP;
156#ifdef VBOX_DND_DEBUG_WND
157 dwExStyle &= ~WS_EX_TRANSPARENT; /* Remove transparency bit. */
158 dwStyle |= WS_VISIBLE; /* Make the window visible. */
159#endif
160 pThis->hWnd =
161 CreateWindowEx(dwExStyle,
162 "VBoxTrayDnDWnd", "VBoxTrayDnDWnd",
163 dwStyle,
164#ifdef VBOX_DND_DEBUG_WND
165 CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL,
166#else
167 -200, -200, 100, 100, NULL, NULL,
168#endif
169 hInstance, pThis /* lParm */);
170 if (!pThis->hWnd)
171 {
172 DWORD dwErr = GetLastError();
173 LogFlowFunc(("Unable to create proxy window, error=%ld\n", dwErr));
174 rc = RTErrConvertFromWin32(dwErr);
175 }
176 else
177 {
178#ifndef VBOX_DND_DEBUG_WND
179 SetWindowPos(pThis->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
180 SWP_NOACTIVATE | SWP_HIDEWINDOW
181 | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
182 LogFlowFunc(("Proxy window created, hWnd=0x%x\n", pThis->hWnd));
183#else
184 LogFlowFunc(("Debug proxy window created, hWnd=0x%x\n", pThis->hWnd));
185
186 /*
187 * Install some mouse tracking.
188 */
189 TRACKMOUSEEVENT me;
190 RT_ZERO(me);
191 me.cbSize = sizeof(TRACKMOUSEEVENT);
192 me.dwFlags = TME_HOVER | TME_LEAVE | TME_NONCLIENT;
193 me.hwndTrack = pThis->hWnd;
194 BOOL fRc = TrackMouseEvent(&me);
195 Assert(fRc);
196#endif
197 }
198 }
199
200 HRESULT hr = OleInitialize(NULL);
201 if (SUCCEEDED(hr))
202 {
203#ifdef VBOX_WITH_DRAG_AND_DROP_GH
204 rc = pThis->RegisterAsDropTarget();
205#endif
206 }
207 else
208 {
209 LogRelFunc(("Unable to initialize OLE, hr=%Rhrc\n", hr));
210 rc = VERR_COM_UNEXPECTED;
211 }
212
213 if (RT_SUCCESS(rc))
214 {
215 bool fShutdown = false;
216 do
217 {
218 MSG uMsg;
219 while (GetMessage(&uMsg, 0, 0, 0))
220 {
221 TranslateMessage(&uMsg);
222 DispatchMessage(&uMsg);
223 }
224
225 if (ASMAtomicReadBool(&pContext->fShutdown))
226 fShutdown = true;
227
228 if (fShutdown)
229 {
230 LogFlowFunc(("Cancelling ...\n"));
231 break;
232 }
233
234 /** @todo Immediately drop on failure? */
235
236 } while (RT_SUCCESS(rc));
237
238#ifdef VBOX_WITH_DRAG_AND_DROP_GH
239 int rc2 = pThis->UnregisterAsDropTarget();
240 if (RT_SUCCESS(rc))
241 rc = rc2;
242#endif
243 OleUninitialize();
244 }
245
246 LogFlowFuncLeaveRC(rc);
247 return rc;
248}
249
250/* static */
251BOOL CALLBACK VBoxDnDWnd::MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor,
252 LPRECT lprcMonitor, LPARAM lParam)
253{
254 LPRECT pRect = (LPRECT)lParam;
255 AssertPtrReturn(pRect, FALSE);
256
257 AssertPtr(lprcMonitor);
258 LogFlowFunc(("Monitor is %ld,%ld,%ld,%ld\n",
259 lprcMonitor->left, lprcMonitor->top,
260 lprcMonitor->right, lprcMonitor->bottom));
261
262 /* Build up a simple bounding box to hold the entire (virtual) screen. */
263 if (pRect->left > lprcMonitor->left)
264 pRect->left = lprcMonitor->left;
265 if (pRect->right < lprcMonitor->right)
266 pRect->right = lprcMonitor->right;
267 if (pRect->top > lprcMonitor->top)
268 pRect->top = lprcMonitor->top;
269 if (pRect->bottom < lprcMonitor->bottom)
270 pRect->bottom = lprcMonitor->bottom;
271
272 return TRUE;
273}
274
275LRESULT CALLBACK VBoxDnDWnd::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
276{
277 switch (uMsg)
278 {
279 case WM_CREATE:
280 {
281 int rc = OnCreate();
282 if (RT_FAILURE(rc))
283 return FALSE;
284 return TRUE;
285 }
286
287 case WM_CLOSE:
288 {
289 OnDestroy();
290
291 DestroyWindow(hWnd);
292 PostQuitMessage(0);
293 return 0;
294 }
295
296 case WM_LBUTTONDOWN:
297 LogFlowThisFunc(("WM_LBUTTONDOWN\n"));
298 mfMouseButtonDown = true;
299 return 0;
300
301 case WM_LBUTTONUP:
302 LogFlowThisFunc(("WM_LBUTTONUP\n"));
303 mfMouseButtonDown = false;
304 return 0;
305
306 case WM_MOUSELEAVE:
307 LogFlowThisFunc(("WM_MOUSELEAVE\n"));
308 return 0;
309
310 /* Will only be called once; after the first mouse move, this
311 * window will be hidden! */
312 case WM_MOUSEMOVE:
313 {
314 LogFlowThisFunc(("WM_MOUSEMOVE: mfMouseButtonDown=%RTbool, mMode=%ld, mState=%ld\n",
315 mfMouseButtonDown, mMode, mState));
316#ifdef DEBUG_andy
317 POINT p;
318 GetCursorPos(&p);
319 LogFlowThisFunc(("WM_MOUSEMOVE: curX=%ld, curY=%ld\n", p.x, p.y));
320#endif
321 int rc = VINF_SUCCESS;
322 if (mMode == HG) /* Host to guest. */
323 {
324 /* Dragging not started yet? Kick it off ... */
325 if ( mfMouseButtonDown
326 && (mState != Dragging))
327 {
328 mState = Dragging;
329#if 0
330 /* Delay hiding the proxy window a bit when debugging, to see
331 * whether the desired range is covered correctly. */
332 RTThreadSleep(5000);
333#endif
334 hide();
335
336 LogFlowThisFunc(("Starting drag'n drop: uAllActions=0x%x, dwOKEffects=0x%x ...\n",
337 uAllActions, startupInfo.dwOKEffects));
338
339 AssertPtr(startupInfo.pDataObject);
340 AssertPtr(startupInfo.pDropSource);
341 DWORD dwEffect;
342 HRESULT hr = DoDragDrop(startupInfo.pDataObject, startupInfo.pDropSource,
343 startupInfo.dwOKEffects, &dwEffect);
344 LogFlowThisFunc(("hr=%Rhrc, dwEffect=%RI32\n", hr, dwEffect));
345 switch (hr)
346 {
347 case DRAGDROP_S_DROP:
348 mState = Dropped;
349 break;
350
351 case DRAGDROP_S_CANCEL:
352 mState = Canceled;
353 break;
354
355 default:
356 LogFlowThisFunc(("Drag'n drop failed with %Rhrc\n", hr));
357 mState = Canceled;
358 rc = VERR_GENERAL_FAILURE; /** @todo Find a better status code. */
359 break;
360 }
361
362 int rc2 = RTCritSectEnter(&mCritSect);
363 if (RT_SUCCESS(rc2))
364 {
365 startupInfo.pDropSource->Release();
366 startupInfo.pDataObject->Release();
367
368 RT_ZERO(startupInfo);
369
370 rc2 = RTCritSectLeave(&mCritSect);
371 if (RT_SUCCESS(rc))
372 rc = rc2;
373 }
374
375 mMode = Unknown;
376 }
377 }
378 else if (mMode == GH) /* Guest to host. */
379 {
380 //hide();
381
382 /* Starting here VBoxDnDDropTarget should
383 * take over; was instantiated when registering
384 * this proxy window as a (valid) drop target. */
385 }
386 else
387 rc = VERR_NOT_SUPPORTED;
388
389 LogFlowThisFunc(("WM_MOUSEMOVE: mMode=%ld, mState=%ld, rc=%Rrc\n",
390 mMode, mState, rc));
391 return 0;
392 }
393
394 case WM_NCMOUSEHOVER:
395 LogFlowThisFunc(("WM_NCMOUSEHOVER\n"));
396 return 0;
397
398 case WM_NCMOUSELEAVE:
399 LogFlowThisFunc(("WM_NCMOUSELEAVE\n"));
400 return 0;
401
402 case WM_VBOXTRAY_DND_MESSAGE:
403 {
404 VBOXDNDEVENT *pEvent = (PVBOXDNDEVENT)lParam;
405 AssertPtr(pEvent);
406
407 LogFlowThisFunc(("Received uType=%RU32, uScreenID=%RU32\n",
408 pEvent->Event.uType, pEvent->Event.uScreenId));
409
410 int rc;
411 switch (pEvent->Event.uType)
412 {
413 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
414 {
415 LogFlowThisFunc(("HOST_DND_HG_EVT_ENTER\n"));
416
417 reset();
418
419 mMode = HG;
420
421 if (pEvent->Event.cbFormats)
422 {
423 RTCList<RTCString> lstFormats =
424 RTCString(pEvent->Event.pszFormats, pEvent->Event.cbFormats - 1).split("\r\n");
425 rc = OnHgEnter(lstFormats, pEvent->Event.u.a.uAllActions);
426 }
427 else
428 {
429 AssertMsgFailed(("cbFormats is 0\n"));
430 rc = VERR_INVALID_PARAMETER;
431 }
432
433 /* Note: After HOST_DND_HG_EVT_ENTER there immediately is a move
434 * event, so fall through is intentional here. */
435 }
436
437 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
438 {
439 LogFlowThisFunc(("HOST_DND_HG_EVT_MOVE: %d,%d\n",
440 pEvent->Event.u.a.uXpos, pEvent->Event.u.a.uYpos));
441
442 rc = OnHgMove(pEvent->Event.u.a.uXpos, pEvent->Event.u.a.uYpos,
443 pEvent->Event.u.a.uDefAction);
444 break;
445 }
446
447 case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE:
448 {
449 LogFlowThisFunc(("HOST_DND_HG_EVT_LEAVE\n"));
450
451 rc = OnHgLeave();
452 break;
453 }
454
455 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
456 {
457 LogFlowThisFunc(("HOST_DND_HG_EVT_DROPPED\n"));
458
459 rc = OnHgDrop();
460 break;
461 }
462
463 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
464 {
465 LogFlowThisFunc(("HOST_DND_HG_SND_DATA\n"));
466
467 rc = OnHgDataReceived(pEvent->Event.u.b.pvData,
468 pEvent->Event.u.b.cbData);
469 break;
470 }
471
472 case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL:
473 {
474 LogFlowThisFunc(("HOST_DND_HG_EVT_CANCEL\n"));
475
476 rc = OnHgCancel();
477 break;
478 }
479
480 case DragAndDropSvc::HOST_DND_GH_REQ_PENDING:
481 {
482 LogFlowThisFunc(("HOST_DND_GH_REQ_PENDING\n"));
483#ifdef VBOX_WITH_DRAG_AND_DROP_GH
484 if ( mMode == Unknown
485 /* There can be more than one HOST_DND_GH_REQ_PENDING
486 * messages coming in. */
487 || mMode == GH)
488 {
489 mMode = GH;
490 rc = OnGhIsDnDPending(pEvent->Event.uScreenId);
491 }
492 else
493 rc = VERR_WRONG_ORDER;
494#else
495 rc = VERR_NOT_SUPPORTED;
496#endif
497 break;
498 }
499
500 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
501 {
502 LogFlowThisFunc(("HOST_DND_GH_EVT_DROPPED\n"));
503#ifdef VBOX_WITH_DRAG_AND_DROP_GH
504 if (mMode == GH)
505 {
506 rc = OnGhDropped(pEvent->Event.pszFormats,
507 pEvent->Event.cbFormats,
508 pEvent->Event.u.a.uDefAction);
509 }
510 else
511 rc = VERR_WRONG_ORDER;
512#else
513 rc = VERR_NOT_SUPPORTED;
514#endif
515 break;
516 }
517
518 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
519 {
520 LogFlowThisFunc(("GUEST_DND_GH_EVT_ERROR\n"));
521#ifdef VBOX_WITH_DRAG_AND_DROP_GH
522 reset();
523 rc = VINF_SUCCESS; /** @todo GUEST_DND_GH_EVT_ERROR */
524#else
525 rc = VERR_NOT_SUPPORTED;
526#endif
527 break;
528 }
529
530 case DragAndDropSvc::HOST_DND_GH_RECV_DIR:
531 {
532 LogFlowThisFunc(("HOST_DND_GH_RECV_DIR\n"));
533#ifdef VBOX_WITH_DRAG_AND_DROP_GH
534 if (mMode == GH)
535 {
536 rc = OnGhSendDir(pEvent->Event.pszFormats,
537 pEvent->Event.cbFormats,
538 pEvent->Event.u.a.uDefAction);
539 }
540 else
541 rc = VERR_WRONG_ORDER;
542#else
543 rc = VERR_NOT_SUPPORTED;
544#endif
545 break;
546 }
547
548 case DragAndDropSvc::HOST_DND_GH_RECV_FILE:
549 {
550 LogFlowThisFunc(("HOST_DND_GH_RECV_FILE\n"));
551#ifdef VBOX_WITH_DRAG_AND_DROP_GH
552 if (mMode == GH)
553 {
554 rc = OnGhSendFile(pEvent->Event.pszFormats,
555 pEvent->Event.cbFormats,
556 pEvent->Event.u.a.uDefAction);
557 }
558 else
559 rc = VERR_WRONG_ORDER;
560#else
561 rc = VERR_NOT_SUPPORTED;
562#endif
563 break;
564 }
565
566 default:
567 rc = VERR_NOT_SUPPORTED;
568 break;
569 }
570
571 /* Some messages require cleanup. */
572 switch (pEvent->Event.uType)
573 {
574 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
575 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
576 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
577#ifdef VBOX_WITH_DRAG_AND_DROP_GH
578 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
579#endif
580 {
581 if (pEvent->Event.pszFormats)
582 RTMemFree(pEvent->Event.pszFormats);
583 break;
584 }
585
586 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
587 {
588 if (pEvent->Event.pszFormats)
589 RTMemFree(pEvent->Event.pszFormats);
590 if (pEvent->Event.u.b.pvData)
591 RTMemFree(pEvent->Event.u.b.pvData);
592 break;
593 }
594
595 default:
596 /* Ignore. */
597 break;
598 }
599
600 LogFlowThisFunc(("Processing event %RU32 resulted in rc=%Rrc\n",
601 pEvent->Event.uType, rc));
602 if (pEvent)
603 RTMemFree(pEvent);
604 return 0;
605 }
606
607 default:
608 break;
609 }
610
611 return DefWindowProc(hWnd, uMsg, wParam, lParam);
612}
613
614#ifdef VBOX_WITH_DRAG_AND_DROP_GH
615/**
616 * Registers this proxy window as a local drop target.
617 *
618 * @return IPRT status code.
619 */
620int VBoxDnDWnd::RegisterAsDropTarget(void)
621{
622 if (pDropTarget) /* Already registered as drop target? */
623 return VINF_SUCCESS;
624
625 int rc;
626 try
627 {
628 pDropTarget = new VBoxDnDDropTarget(this /* pParent */);
629 AssertPtr(pDropTarget);
630 HRESULT hr = CoLockObjectExternal(pDropTarget, TRUE /* fLock */,
631 FALSE /* fLastUnlockReleases */);
632 if (SUCCEEDED(hr))
633 hr = RegisterDragDrop(hWnd, pDropTarget);
634
635 if (FAILED(hr))
636 {
637 LogRel(("DnD: Creating drop target failed with hr=%Rhrc\n", hr));
638 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
639 }
640 else
641 {
642 rc = VINF_SUCCESS;
643 }
644 }
645 catch (std::bad_alloc)
646 {
647 rc = VERR_NO_MEMORY;
648 }
649
650 LogFlowFuncLeaveRC(rc);
651 return rc;
652}
653
654int VBoxDnDWnd::UnregisterAsDropTarget(void)
655{
656 LogFlowFuncEnter();
657
658 if (!pDropTarget) /* No drop target? Bail out. */
659 return VINF_SUCCESS;
660
661 HRESULT hr = RevokeDragDrop(hWnd);
662 if (SUCCEEDED(hr))
663 hr = CoLockObjectExternal(pDropTarget, FALSE /* fLock */,
664 TRUE /* fLastUnlockReleases */);
665 if (SUCCEEDED(hr))
666 {
667 ULONG cRefs = pDropTarget->Release();
668
669 Assert(cRefs == 0);
670 pDropTarget = NULL;
671 }
672
673 int rc = SUCCEEDED(hr)
674 ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fix this. */
675
676 LogFlowFuncLeaveRC(rc);
677 return rc;
678}
679#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
680
681int VBoxDnDWnd::OnCreate(void)
682{
683 int rc = VbglR3DnDConnect(&mClientID);
684 if (RT_FAILURE(rc))
685 {
686 LogFlowThisFunc(("Connection to host service failed, rc=%Rrc\n", rc));
687 return rc;
688 }
689
690 LogFlowThisFunc(("Client ID=%RU32, rc=%Rrc\n", mClientID, rc));
691 return rc;
692}
693
694void VBoxDnDWnd::OnDestroy(void)
695{
696 VbglR3DnDDisconnect(mClientID);
697 LogFlowThisFuncLeave();
698}
699
700int VBoxDnDWnd::OnHgEnter(const RTCList<RTCString> &lstFormats, uint32_t uAllActions)
701{
702#ifdef DEBUG
703 LogFlowThisFunc(("uActions=0x%x, lstFormats=%zu: ", uAllActions, lstFormats.size()));
704 for (size_t i = 0; i < lstFormats.size(); i++)
705 LogFlow(("'%s' ", lstFormats.at(i).c_str()));
706 LogFlow(("\n"));
707#endif
708
709 /* Save all allowed actions. */
710 this->uAllActions = uAllActions;
711
712 /*
713 * Install our allowed MIME types.
714 ** @todo See todo for m_sstrAllowedMimeTypes in GuestDnDImpl.cpp.
715 */
716 const RTCList<RTCString> lstAllowedMimeTypes = RTCList<RTCString>()
717 /* URI's */
718 << "text/uri-list"
719 /* Text */
720 << "text/plain;charset=utf-8"
721 << "UTF8_STRING"
722 << "text/plain"
723 << "COMPOUND_TEXT"
724 << "TEXT"
725 << "STRING"
726 /* OpenOffice formats */
727 << "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""
728 << "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"";
729 this->lstAllowedFormats = lstAllowedMimeTypes;
730
731 /*
732 * Check MIME compatibility with this client.
733 */
734 LogFlowThisFunc(("Supported MIME types:\n"));
735 for (size_t i = 0; i < lstFormats.size(); i++)
736 {
737 bool fSupported = lstAllowedFormats.contains(lstFormats.at(i));
738 if (fSupported)
739 this->lstFormats.append(lstFormats.at(i));
740 LogFlowThisFunc(("\t%s: %RTbool\n", lstFormats.at(i).c_str(), fSupported));
741 }
742
743 /*
744 * Prepare the startup info for DoDragDrop().
745 */
746 int rc = VINF_SUCCESS;
747 try
748 {
749 /* Translate our drop actions into
750 * allowed Windows drop effects. */
751 startupInfo.dwOKEffects = DROPEFFECT_NONE;
752 if (uAllActions)
753 {
754 if (uAllActions & DND_COPY_ACTION)
755 startupInfo.dwOKEffects |= DROPEFFECT_COPY;
756 if (uAllActions & DND_MOVE_ACTION)
757 startupInfo.dwOKEffects |= DROPEFFECT_MOVE;
758 if (uAllActions & DND_LINK_ACTION)
759 startupInfo.dwOKEffects |= DROPEFFECT_LINK;
760 }
761
762 startupInfo.pDropSource = new VBoxDnDDropSource(this);
763 startupInfo.pDataObject = new VBoxDnDDataObject();
764 }
765 catch (std::bad_alloc)
766 {
767 rc = VERR_NO_MEMORY;
768 }
769
770 if (RT_SUCCESS(rc))
771 rc = makeFullscreen();
772
773 LogFlowFuncLeaveRC(rc);
774 return rc;
775}
776
777int VBoxDnDWnd::OnHgMove(uint32_t u32xPos, uint32_t u32yPos, uint32_t uAction)
778{
779 LogFlowThisFunc(("u32xPos=%RU32, u32yPos=%RU32, uAction=0x%x\n",
780 u32xPos, u32yPos, uAction));
781
782 int rc = mouseMove(u32xPos, u32yPos, MOUSEEVENTF_LEFTDOWN);
783
784 uint32_t uActionNotify = DND_IGNORE_ACTION;
785 if (RT_SUCCESS(rc))
786 rc = RTCritSectEnter(&mCritSect);
787 if (RT_SUCCESS(rc))
788 {
789 if ( (Dragging == mState)
790 && startupInfo.pDropSource)
791 uActionNotify = startupInfo.pDropSource->GetCurrentAction();
792
793 RTCritSectLeave(&mCritSect);
794 }
795
796 if (RT_SUCCESS(rc))
797 {
798 rc = VbglR3DnDHGAcknowledgeOperation(mClientID, uActionNotify);
799 if (RT_FAILURE(rc))
800 LogFlowThisFunc(("Acknowledging operation failed with rc=%Rrc\n", rc));
801 }
802
803 LogFlowThisFunc(("Returning uActionNotify=0x%x, rc=%Rrc\n", uActionNotify, rc));
804 return rc;
805}
806
807int VBoxDnDWnd::OnHgLeave(void)
808{
809 LogFlowThisFunc(("mMode=%ld, mState=%RU32\n", mMode, mState));
810 LogRel(("DnD: Drag'n drop operation aborted\n"));
811
812 reset();
813
814 int rc = VINF_SUCCESS;
815
816 /* Post ESC to our window to officially abort the
817 * drag'n drop operation. */
818 PostMessage(hWnd, WM_KEYDOWN, VK_ESCAPE, 0 /* lParam */);
819
820 LogFlowFuncLeaveRC(rc);
821 return rc;
822}
823
824int VBoxDnDWnd::OnHgDrop(void)
825{
826 LogFlowThisFunc(("mMode=%ld, mState=%RU32\n", mMode, mState));
827
828 int rc = VINF_SUCCESS;
829 if (mState == Dragging)
830 {
831 Assert(lstFormats.size() >= 1);
832
833 /** @todo What to do when multiple formats are available? */
834 mFormatRequested = lstFormats.at(0);
835
836 rc = RTCritSectEnter(&mCritSect);
837 if (RT_SUCCESS(rc))
838 {
839 if (startupInfo.pDataObject)
840 startupInfo.pDataObject->SetStatus(VBoxDnDDataObject::Dropping);
841 else
842 rc = VERR_NOT_FOUND;
843
844 RTCritSectLeave(&mCritSect);
845 }
846
847 if (RT_SUCCESS(rc))
848 {
849 LogRel(("DnD: Requesting data as '%s' ...\n", mFormatRequested.c_str()));
850 rc = VbglR3DnDHGRequestData(mClientID, mFormatRequested.c_str());
851 if (RT_FAILURE(rc))
852 LogFlowThisFunc(("Requesting data failed with rc=%Rrc\n", rc));
853 }
854 }
855
856 LogFlowFuncLeaveRC(rc);
857 return rc;
858}
859
860int VBoxDnDWnd::OnHgDataReceived(const void *pvData, uint32_t cbData)
861{
862 LogFlowThisFunc(("mState=%ld, pvData=%p, cbData=%RU32\n",
863 mState, pvData, cbData));
864
865 mState = Dropped;
866
867 int rc = VINF_SUCCESS;
868 if (pvData)
869 {
870 Assert(cbData);
871 rc = RTCritSectEnter(&mCritSect);
872 if (RT_SUCCESS(rc))
873 {
874 if (startupInfo.pDataObject)
875 rc = startupInfo.pDataObject->Signal(mFormatRequested, pvData, cbData);
876 else
877 rc = VERR_NOT_FOUND;
878
879 RTCritSectLeave(&mCritSect);
880 }
881 }
882
883 int rc2 = mouseRelease();
884 if (RT_SUCCESS(rc))
885 rc = rc2;
886
887 LogFlowFuncLeaveRC(rc);
888 return rc;
889}
890
891int VBoxDnDWnd::OnHgCancel(void)
892{
893 int rc = RTCritSectEnter(&mCritSect);
894 if (RT_SUCCESS(rc))
895 {
896 if (startupInfo.pDataObject)
897 startupInfo.pDataObject->Abort();
898
899 RTCritSectLeave(&mCritSect);
900 }
901
902 int rc2 = mouseRelease();
903 if (RT_SUCCESS(rc))
904 rc = rc2;
905
906 reset();
907
908 return rc;
909}
910
911#ifdef VBOX_WITH_DRAG_AND_DROP_GH
912int VBoxDnDWnd::OnGhIsDnDPending(uint32_t uScreenID)
913{
914 LogFlowThisFunc(("mMode=%ld, mState=%ld, uScreenID=%RU32\n",
915 mMode, mState, uScreenID));
916
917 if (mState == Uninitialized)
918 reset();
919
920 int rc;
921 if (mState == Initialized)
922 {
923 rc = makeFullscreen();
924 if (RT_SUCCESS(rc))
925 {
926 /*
927 * We have to release the left mouse button to
928 * get into our (invisible) proxy window.
929 */
930 mouseRelease();
931
932 /*
933 * Even if we just released the left mouse button
934 * we're still in the dragging state to handle our
935 * own drop target (for the host).
936 */
937 mState = Dragging;
938 }
939 }
940 else
941 rc = VINF_SUCCESS;
942
943 /**
944 * Some notes regarding guest cursor movement:
945 * - The host only sends an HOST_DND_GH_REQ_PENDING message to the guest
946 * if the mouse cursor is outside the VM's window.
947 * - The guest does not know anything about the host's cursor
948 * position / state due to security reasons.
949 * - The guest *only* knows that the host currently is asking whether a
950 * guest DnD operation is in progress.
951 */
952
953 if ( RT_SUCCESS(rc)
954 && (mState == Dragging))
955 {
956 /** @todo Put this block into a function! */
957 POINT p;
958 GetCursorPos(&p);
959 ClientToScreen(hWnd, &p);
960#ifdef DEBUG_andy
961 LogFlowThisFunc(("Client to screen curX=%ld, curY=%ld\n", p.x, p.y));
962#endif
963
964 /** @todo Multi-monitor setups? */
965 int iScreenX = GetSystemMetrics(SM_CXSCREEN) - 1;
966 int iScreenY = GetSystemMetrics(SM_CYSCREEN) - 1;
967
968 static LONG px = p.x;
969 if (px <= 0)
970 px = 1;
971 static LONG py = p.y;
972 if (py <= 0)
973 py = 1;
974
975 rc = mouseMove(px, py, 0 /* dwMouseInputFlags */);
976 }
977
978 if (RT_SUCCESS(rc))
979 {
980 uint32_t uDefAction = DND_IGNORE_ACTION;
981
982 AssertPtr(pDropTarget);
983 RTCString strFormats = pDropTarget->Formats();
984 if (!strFormats.isEmpty())
985 {
986 uDefAction = DND_COPY_ACTION;
987 uAllActions = uDefAction;
988
989 LogFlowFunc(("Acknowledging pDropTarget=0x%p, uDefAction=0x%x, uAllActions=0x%x, strFormats=%s\n",
990 pDropTarget, uDefAction, uAllActions, strFormats.c_str()));
991 rc = VbglR3DnDGHAcknowledgePending(mClientID,
992 uDefAction, uAllActions, strFormats.c_str());
993 }
994 else
995 LogFlowFunc(("No format data available yet\n"));
996 }
997
998 LogFlowFuncLeaveRC(rc);
999 return rc;
1000}
1001
1002int VBoxDnDWnd::OnGhDropped(const char *pszFormat, uint32_t cbFormats,
1003 uint32_t uDefAction)
1004{
1005 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1006 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
1007
1008 LogFlowThisFunc(("mMode=%ld, mState=%ld, pDropTarget=0x%p, uDefAction=0x%x\n",
1009 mMode, mState, pDropTarget, uDefAction));
1010
1011 int rc;
1012 if (mState == Dragging)
1013 {
1014 AssertPtr(pDropTarget);
1015 rc = pDropTarget->WaitForDrop(30 * 1000 /* Timeout in ms */);
1016 if (RT_SUCCESS(rc))
1017 {
1018 /** @todo Respect uDefAction. */
1019 /** @todo Do data checking / conversion based on pszFormats. */
1020
1021 void *pvData = pDropTarget->DataMutableRaw();
1022 AssertPtr(pvData);
1023 uint32_t cbData = pDropTarget->DataSize();
1024 Assert(cbData);
1025
1026 rc = VbglR3DnDGHSendData(mClientID, pvData, cbData);
1027 LogFlowFunc(("Sent pvData=0x%p, cbData=%RU32, rc=%Rrc\n",
1028 pvData, cbData, rc));
1029 }
1030
1031 reset();
1032 }
1033 else
1034 rc = VERR_WRONG_ORDER;
1035
1036 LogFlowFuncLeaveRC(rc);
1037 return rc;
1038}
1039
1040int VBoxDnDWnd::OnGhSendDir(const char *pszFormats, uint32_t cbFormats,
1041 uint32_t uDefAction)
1042{
1043 int rc = 0;
1044 LogFlowFuncLeaveRC(rc);
1045 return rc;
1046}
1047
1048int VBoxDnDWnd::OnGhSendFile(const char *pszFormats, uint32_t cbFormats,
1049 uint32_t uDefAction)
1050{
1051 int rc = 0;
1052 LogFlowFuncLeaveRC(rc);
1053 return rc;
1054}
1055#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1056
1057int VBoxDnDWnd::ProcessEvent(PVBOXDNDEVENT pEvent)
1058{
1059 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1060
1061 PostMessage(hWnd, WM_VBOXTRAY_DND_MESSAGE,
1062 0 /* wParm */, (LPARAM)pEvent /* lParm */);
1063
1064 return VINF_SUCCESS;
1065}
1066
1067int VBoxDnDWnd::hide(void)
1068{
1069#ifdef DEBUG_andy
1070 LogFlowFunc(("\n"));
1071#endif
1072 ShowWindow(hWnd, SW_HIDE);
1073
1074 return VINF_SUCCESS;
1075}
1076
1077int VBoxDnDWnd::makeFullscreen(void)
1078{
1079 int rc = VINF_SUCCESS;
1080
1081 RECT r;
1082 RT_ZERO(r);
1083
1084 BOOL fRc;
1085 HDC hDC = GetDC(NULL /* Entire screen */);
1086 if (hDC)
1087 {
1088 fRc = EnumDisplayMonitors(hDC, NULL, VBoxDnDWnd::MonitorEnumProc,
1089 (LPARAM)&r);
1090 if (!fRc)
1091 rc = VERR_NOT_FOUND;
1092 ReleaseDC(NULL, hDC);
1093 }
1094 else
1095 rc = VERR_ACCESS_DENIED;
1096
1097 if (RT_FAILURE(rc))
1098 {
1099 /* If multi-monitor enumeration failed above, try getting at least the
1100 * primary monitor as a fallback. */
1101 MONITORINFO monitor_info;
1102 monitor_info.cbSize = sizeof(monitor_info);
1103 if (GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST),
1104 &monitor_info))
1105 {
1106
1107 r = monitor_info.rcMonitor;
1108 rc = VINF_SUCCESS;
1109 }
1110 }
1111
1112 if (RT_SUCCESS(rc))
1113 {
1114 LONG lStyle = GetWindowLong(hWnd, GWL_STYLE);
1115 SetWindowLong(hWnd, GWL_STYLE,
1116 lStyle & ~(WS_CAPTION | WS_THICKFRAME));
1117 LONG lExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
1118 SetWindowLong(hWnd, GWL_EXSTYLE,
1119 lExStyle & ~( WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE
1120 | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
1121
1122 fRc = SetWindowPos(hWnd, HWND_TOPMOST,
1123 r.left,
1124 r.top,
1125 r.right - r.left,
1126 r.bottom - r.top,
1127#ifdef VBOX_DND_DEBUG_WND
1128 SWP_SHOWWINDOW | SWP_FRAMECHANGED);
1129#else
1130 SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
1131#endif
1132 if (fRc)
1133 {
1134 LogFlowFunc(("Virtual screen is %ld,%ld,%ld,%ld (%ld x %ld)\n",
1135 r.left, r.top, r.right, r.bottom,
1136 r.right - r.left, r.bottom - r.top));
1137 }
1138 else
1139 {
1140 DWORD dwErr = GetLastError();
1141 LogRel(("DnD: Failed to set proxy window position, rc=%Rrc\n",
1142 RTErrConvertFromWin32(dwErr)));
1143 }
1144 }
1145 else
1146 LogRel(("DnD: Failed to determine virtual screen size, rc=%Rrc\n", rc));
1147
1148 LogFlowFuncLeaveRC(rc);
1149 return rc;
1150}
1151
1152int VBoxDnDWnd::mouseMove(int x, int y, DWORD dwMouseInputFlags)
1153{
1154 int iScreenX = GetSystemMetrics(SM_CXSCREEN) - 1;
1155 int iScreenY = GetSystemMetrics(SM_CYSCREEN) - 1;
1156
1157 INPUT Input[1] = { 0 };
1158 Input[0].type = INPUT_MOUSE;
1159 Input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE
1160 | dwMouseInputFlags;
1161 Input[0].mi.dx = x * (65535 / iScreenX);
1162 Input[0].mi.dy = y * (65535 / iScreenY);
1163
1164 int rc;
1165 if (s_pfnSendInput(1 /* Number of inputs */,
1166 Input, sizeof(INPUT)))
1167 {
1168#ifdef DEBUG_andy
1169 CURSORINFO ci;
1170 RT_ZERO(ci);
1171 ci.cbSize = sizeof(ci);
1172 BOOL fRc = GetCursorInfo(&ci);
1173 if (fRc)
1174 LogFlowThisFunc(("Cursor shown=%RTbool, cursor=0x%p, x=%d, y=%d\n",
1175 (ci.flags & CURSOR_SHOWING) ? true : false,
1176 ci.hCursor, ci.ptScreenPos.x, ci.ptScreenPos.y));
1177#endif
1178 rc = VINF_SUCCESS;
1179 }
1180 else
1181 {
1182 DWORD dwErr = GetLastError();
1183 rc = RTErrConvertFromWin32(dwErr);
1184 LogFlowFunc(("SendInput failed with rc=%Rrc\n", rc));
1185 }
1186
1187 return rc;
1188}
1189
1190int VBoxDnDWnd::mouseRelease(void)
1191{
1192#ifdef DEBUG_andy
1193 LogFlowFunc(("\n"));
1194#endif
1195 int rc;
1196
1197 /* Release mouse button in the guest to start the "drop"
1198 * action at the current mouse cursor position. */
1199 INPUT Input[1] = { 0 };
1200 Input[0].type = INPUT_MOUSE;
1201 Input[0].mi.dwFlags = MOUSEEVENTF_LEFTUP;
1202 if (!s_pfnSendInput(1, Input, sizeof(INPUT)))
1203 {
1204 DWORD dwErr = GetLastError();
1205 rc = RTErrConvertFromWin32(dwErr);
1206 LogFlowFunc(("SendInput failed with rc=%Rrc\n", rc));
1207 }
1208 else
1209 rc = VINF_SUCCESS;
1210
1211 return rc;
1212}
1213
1214void VBoxDnDWnd::reset(void)
1215{
1216 LogFlowThisFunc(("Old mState=%ld\n", mState));
1217
1218 lstAllowedFormats.clear();
1219 lstFormats.clear();
1220 uAllActions = DND_IGNORE_ACTION;
1221
1222 mMode = Unknown;
1223 mState = Initialized;
1224}
1225
1226static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1227{
1228 LONG_PTR pUserData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
1229 AssertPtrReturn(pUserData, 0);
1230
1231 VBoxDnDWnd *pWnd = reinterpret_cast<VBoxDnDWnd *>(pUserData);
1232 if (pWnd)
1233 return pWnd->WndProc(hWnd, uMsg, wParam, lParam);
1234
1235 return 0;
1236}
1237
1238static LRESULT CALLBACK vboxDnDWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1239{
1240 /* Note: WM_NCCREATE is not the first ever message which arrives, but
1241 * early enough for us. */
1242 if (uMsg == WM_NCCREATE)
1243 {
1244 LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam;
1245 AssertPtr(pCS);
1246 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pCS->lpCreateParams);
1247 SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)vboxDnDWndProcInstance);
1248
1249 return vboxDnDWndProcInstance(hWnd, uMsg, wParam, lParam);
1250 }
1251
1252 /* No window associated yet. */
1253 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1254}
1255
1256/**
1257 * Initializes drag'n drop.
1258 *
1259 * @return IPRT status code.
1260 * @param pEnv The DnD service's environment.
1261 * @param ppInstance The instance pointer which refer to this object.
1262 * @param pfStartThread Pointer to flag whether the DnD service can be started or not.
1263 */
1264int VBoxDnDInit(const VBOXSERVICEENV *pEnv, void **ppInstance, bool *pfStartThread)
1265{
1266 AssertPtrReturn(pEnv, VERR_INVALID_POINTER);
1267 /** ppInstance not used here. */
1268 AssertPtrReturn(pfStartThread, VERR_INVALID_POINTER);
1269
1270 LogFlowFuncEnter();
1271
1272 *pfStartThread = false;
1273
1274 PVBOXDNDCONTEXT pCtx = &gCtx;
1275
1276 int rc;
1277 bool fSupportedOS = true;
1278
1279 s_pfnSendInput = (PFNSENDINPUT)
1280 RTLdrGetSystemSymbol("User32.dll", "SendInput");
1281 fSupportedOS = !RT_BOOL(s_pfnSendInput == NULL);
1282
1283 if (!fSupportedOS)
1284 {
1285 LogRel(("DnD: Not supported Windows version, disabling drag'n drop support\n"));
1286 rc = VERR_NOT_SUPPORTED;
1287 }
1288 else
1289 rc = VINF_SUCCESS;
1290
1291 if (RT_SUCCESS(rc))
1292 {
1293 /* Create the proxy window. At the moment we
1294 * only support one window at a time. */
1295 VBoxDnDWnd *pWnd = NULL;
1296 try
1297 {
1298 pWnd = new VBoxDnDWnd();
1299 rc = pWnd->Initialize(pCtx);
1300
1301 /* Add proxy window to our proxy windows list. */
1302 if (RT_SUCCESS(rc))
1303 pCtx->lstWnd.append(pWnd);
1304 }
1305 catch (std::bad_alloc)
1306 {
1307 rc = VERR_NO_MEMORY;
1308 }
1309 }
1310
1311 if (RT_SUCCESS(rc))
1312 rc = RTSemEventCreate(&pCtx->hEvtQueueSem);
1313 if (RT_SUCCESS(rc))
1314 {
1315 /* Assign service environment to our context. */
1316 gCtx.pEnv = pEnv;
1317 }
1318
1319 if (RT_SUCCESS(rc))
1320 {
1321 *ppInstance = pCtx;
1322 *pfStartThread = true;
1323
1324 LogRel(("DnD: Drag'n drop service successfully started\n"));
1325 return VINF_SUCCESS;
1326 }
1327
1328 LogRel(("DnD: Initializing drag'n drop service failed with rc=%Rrc\n", rc));
1329 return rc;
1330}
1331
1332void VBoxDnDStop(const VBOXSERVICEENV *pEnv, void *pInstance)
1333{
1334 AssertPtrReturnVoid(pEnv);
1335 AssertPtrReturnVoid(pInstance);
1336
1337 LogFunc(("Stopping pInstance=%p\n", pInstance));
1338
1339 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1340 AssertPtr(pCtx);
1341
1342 /* Set shutdown indicator. */
1343 ASMAtomicWriteBool(&pCtx->fShutdown, true);
1344
1345 /** @todo Notify / wait for HGCM thread! */
1346}
1347
1348void VBoxDnDDestroy(const VBOXSERVICEENV *pEnv, void *pInstance)
1349{
1350 AssertPtr(pEnv);
1351 AssertPtr(pInstance);
1352
1353 LogFunc(("Destroying pInstance=%p\n", pInstance));
1354
1355 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1356 AssertPtr(pCtx);
1357
1358 int rc = VINF_SUCCESS;
1359
1360 /** @todo At the moment we only have one DnD proxy window. */
1361 Assert(pCtx->lstWnd.size() == 1);
1362 VBoxDnDWnd *pWnd = pCtx->lstWnd.first();
1363 if (pWnd)
1364 delete pWnd;
1365
1366 if (pCtx->hEvtQueueSem != NIL_RTSEMEVENT)
1367 RTSemEventDestroy(pCtx->hEvtQueueSem);
1368
1369 LogFunc(("Destroyed pInstance=%p, rc=%Rrc\n",
1370 pInstance, rc));
1371}
1372
1373unsigned __stdcall VBoxDnDThread(void *pInstance)
1374{
1375 LogFlowFunc(("pInstance=%p\n", pInstance));
1376
1377 PVBOXDNDCONTEXT pCtx = (PVBOXDNDCONTEXT)pInstance;
1378 AssertPtr(pCtx);
1379
1380 uint32_t uClientID;
1381 int rc = VbglR3DnDConnect(&uClientID);
1382 if (RT_FAILURE(rc))
1383 return rc;
1384
1385 /** @todo At the moment we only have one DnD proxy window. */
1386 Assert(pCtx->lstWnd.size() == 1);
1387 VBoxDnDWnd *pWnd = pCtx->lstWnd.first();
1388 AssertPtr(pWnd);
1389
1390 /* Number of invalid messages skipped in a row. */
1391 int cMsgSkippedInvalid = 0;
1392
1393 do
1394 {
1395 PVBOXDNDEVENT pEvent = (PVBOXDNDEVENT)RTMemAlloc(sizeof(VBOXDNDEVENT));
1396 if (!pEvent)
1397 {
1398 rc = VERR_NO_MEMORY;
1399 break;
1400 }
1401 /* Note: pEvent will be free'd by the consumer later. */
1402
1403 rc = VbglR3DnDProcessNextMessage(uClientID, &pEvent->Event);
1404 LogFlowFunc(("VbglR3DnDProcessNextMessage returned uType=%RU32, rc=%Rrc\n",
1405 pEvent->Event.uType, rc));
1406
1407 if (ASMAtomicReadBool(&pCtx->fShutdown))
1408 break;
1409
1410 if (RT_SUCCESS(rc))
1411 {
1412 cMsgSkippedInvalid = 0; /* Reset skipped messages count. */
1413
1414 LogFlowFunc(("Received new event, type=%RU32\n", pEvent->Event.uType));
1415
1416 int rc2 = pWnd->ProcessEvent(pEvent);
1417 if (RT_FAILURE(rc2))
1418 LogFlowFunc(("Processing event failed with rc=%Rrc\n", rc2));
1419 }
1420 else if (rc == VERR_CANCELLED)
1421 {
1422 int rc2 = pWnd->OnHgCancel();
1423 if (RT_FAILURE(rc2))
1424 LogFlowFunc(("Cancelling failed with rc=%Rrc\n", rc2));
1425 }
1426 else
1427 {
1428 LogFlowFunc(("Processing next message failed with rc=%Rrc\n", rc));
1429
1430 /* Old(er) hosts either are broken regarding DnD support or otherwise
1431 * don't support the stuff we do on the guest side, so make sure we
1432 * don't process invalid messages forever. */
1433 if (rc == VERR_INVALID_PARAMETER)
1434 cMsgSkippedInvalid++;
1435 if (cMsgSkippedInvalid > 3)
1436 {
1437 LogFlowFunc(("Too many invalid/skipped messages from host, exiting ...\n"));
1438 break;
1439 }
1440 }
1441
1442 if (ASMAtomicReadBool(&pCtx->fShutdown))
1443 break;
1444
1445 } while (true);
1446
1447 LogFlowFunc(("Shutting down ...\n"));
1448
1449 VbglR3DnDDisconnect(uClientID);
1450
1451 LogFlowFuncLeaveRC(rc);
1452 return rc;
1453}
1454
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette