VirtualBox

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

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