VirtualBox

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

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

DnD: Update, bugfixes.

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