VirtualBox

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

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

DnD: API overhaul; now using IDnDTarget + IDnDSource. Renamed DragAndDrop* enumerations to DnD*. Also rewrote some internal code.

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