VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp@ 56268

Last change on this file since 56268 was 55963, checked in by vboxsync, 10 years ago

DnD: Fixed hang when reporting host errors such as inaccessible (locked) files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.9 KB
Line 
1/* $Id: GuestDnDPrivate.cpp 55963 2015-05-20 11:18:14Z vboxsync $ */
2/** @file
3 * Private guest drag and drop code, used by GuestDnDTarget +
4 * GuestDnDSource.
5 */
6
7/*
8 * Copyright (C) 2011-2015 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "GuestImpl.h"
20#include "AutoCaller.h"
21
22#ifdef VBOX_WITH_DRAG_AND_DROP
23# include "ConsoleImpl.h"
24# include "ProgressImpl.h"
25# include "GuestDnDPrivate.h"
26
27# include <algorithm>
28
29# include <iprt/dir.h>
30# include <iprt/path.h>
31# include <iprt/stream.h>
32# include <iprt/semaphore.h>
33# include <iprt/cpp/utils.h>
34
35# include <VMMDev.h>
36
37# include <VBox/GuestHost/DragAndDrop.h>
38# include <VBox/HostServices/DragAndDropSvc.h>
39# include <VBox/version.h>
40
41# ifdef LOG_GROUP
42# undef LOG_GROUP
43# endif
44# define LOG_GROUP LOG_GROUP_GUEST_DND
45# include <VBox/log.h>
46
47/**
48 * Overview:
49 *
50 * Drag and Drop is handled over the internal HGCM service for the host <->
51 * guest communication. Beside that we need to map the Drag and Drop protocols
52 * of the various OS's we support to our internal channels, this is also highly
53 * communicative in both directions. Unfortunately HGCM isn't really designed
54 * for that. Next we have to foul some of the components. This includes to
55 * trick X11 on the guest side, but also Qt needs to be tricked on the host
56 * side a little bit.
57 *
58 * The following components are involved:
59 *
60 * 1. GUI: Uses the Qt classes for Drag and Drop and mainly forward the content
61 * of it to the Main IGuest interface (see UIDnDHandler.cpp).
62 * 2. Main: Public interface for doing Drag and Drop. Also manage the IProgress
63 * interfaces for blocking the caller by showing a progress dialog (see
64 * this file).
65 * 3. HGCM service: Handle all messages from the host to the guest at once and
66 * encapsulate the internal communication details (see dndmanager.cpp and
67 * friends).
68 * 4. Guest additions: Split into the platform neutral part (see
69 * VBoxGuestR3LibDragAndDrop.cpp) and the guest OS specific parts.
70 * Receive/send message from/to the HGCM service and does all guest specific
71 * operations. Currently only X11 is supported (see draganddrop.cpp within
72 * VBoxClient).
73 *
74 * Host -> Guest:
75 * 1. There are DnD Enter, Move, Leave events which are send exactly like this
76 * to the guest. The info includes the pos, mimetypes and allowed actions.
77 * The guest has to respond with an action it would accept, so the GUI could
78 * change the cursor.
79 * 2. On drop, first a drop event is sent. If this is accepted a drop data
80 * event follows. This blocks the GUI and shows some progress indicator.
81 *
82 * Guest -> Host:
83 * 1. The GUI is asking the guest if a DnD event is pending when the user moves
84 * the cursor out of the view window. If so, this returns the mimetypes and
85 * allowed actions.
86 * (2. On every mouse move this is asked again, to make sure the DnD event is
87 * still valid.)
88 * 3. On drop the host request the data from the guest. This blocks the GUI and
89 * shows some progress indicator.
90 *
91 * Some hints:
92 * m_strSupportedFormats here in this file defines the allowed mime-types.
93 * This is necessary because we need special handling for some of the
94 * mime-types. E.g. for URI lists we need to transfer the actual dirs and
95 * files. Text EOL may to be changed. Also unknown mime-types may need special
96 * handling as well, which may lead to undefined behavior in the host/guest, if
97 * not done.
98 *
99 * Dropping of a directory, means recursively transferring _all_ the content.
100 *
101 * Directories and files are placed into the user's temporary directory on the
102 * guest (e.g. /tmp/VirtualBox Dropped Files). We can't delete them after the
103 * DnD operation, because we didn't know what the DnD target does with it. E.g.
104 * it could just be opened in place. This could lead ofc to filling up the disk
105 * within the guest. To inform the user about this, a small app could be
106 * developed which scans this directory regularly and inform the user with a
107 * tray icon hint (and maybe the possibility to clean this up instantly). The
108 * same has to be done in the G->H direction when it is implemented.
109 *
110 * Of course only regularly files are supported. Symlinks are resolved and
111 * transfered as regularly files. First we don't know if the other side support
112 * symlinks at all and second they could point to somewhere in a directory tree
113 * which not exists on the other side.
114 *
115 * The code tries to preserve the file modes of the transfered dirs/files. This
116 * is useful (and maybe necessary) for two things:
117 * 1. If a file is executable, it should be also after the transfer, so the
118 * user can just execute it, without manually tweaking the modes first.
119 * 2. If a dir/file is not accessible by group/others in the host, it shouldn't
120 * be in the guest.
121 * In any case, the user mode is always set to rwx (so that we can access it
122 * ourself, in e.g. for a cleanup case after cancel).
123 *
124 * Cancel is supported in both directions and cleans up all previous steps
125 * (thats is: deleting already transfered dirs/files).
126 *
127 * In general I propose the following changes in the VBox HGCM infrastructure
128 * for the future:
129 * - Currently it isn't really possible to send messages to the guest from the
130 * host. The host informs the guest just that there is something, the guest
131 * than has to ask which message and depending on that send the appropriate
132 * message to the host, which is filled with the right data.
133 * - There is no generic interface for sending bigger memory blocks to/from the
134 * guest. This is now done here, but I guess was also necessary for e.g.
135 * guest execution. So something generic which brake this up into smaller
136 * blocks and send it would be nice (with all the error handling and such
137 * ofc).
138 * - I developed a "protocol" for the DnD communication here. So the host and
139 * the guest have always to match in the revision. This is ofc bad, because
140 * the additions could be outdated easily. So some generic protocol number
141 * support in HGCM for asking the host and the guest of the support version,
142 * would be nice. Ofc at least the host should be able to talk to the guest,
143 * even when the version is below the host one.
144 * All this stuff would be useful for the current services, but also for future
145 * onces.
146 *
147 ** @todo
148 * - ESC doesn't really work (on Windows guests it's already implemented)
149 * ... in any case it seems a little bit difficult to handle from the Qt
150 * side. Maybe also a host specific implementation becomes necessary ...
151 * this would be really worst ofc.
152 * - Add support for more mime-types (especially images, csv)
153 * - Test unusual behavior:
154 * - DnD service crash in the guest during a DnD op (e.g. crash of VBoxClient or X11)
155 * - Not expected order of the events between HGCM and the guest
156 * - Security considerations: We transfer a lot of memory between the guest and
157 * the host and even allow the creation of dirs/files. Maybe there should be
158 * limits introduced to preventing DOS attacks or filling up all the memory
159 * (both in the host and the guest).
160 */
161
162GuestDnDCallbackEvent::~GuestDnDCallbackEvent(void)
163{
164 if (NIL_RTSEMEVENT != mSemEvent)
165 RTSemEventDestroy(mSemEvent);
166}
167
168int GuestDnDCallbackEvent::Reset(void)
169{
170 int rc = VINF_SUCCESS;
171
172 if (NIL_RTSEMEVENT == mSemEvent)
173 rc = RTSemEventCreate(&mSemEvent);
174
175 mRc = VINF_SUCCESS;
176 return rc;
177}
178
179int GuestDnDCallbackEvent::Notify(int rc /* = VINF_SUCCESS */)
180{
181 mRc = rc;
182 return RTSemEventSignal(mSemEvent);
183}
184
185int GuestDnDCallbackEvent::Wait(RTMSINTERVAL msTimeout)
186{
187 return RTSemEventWait(mSemEvent, msTimeout);
188}
189
190///////////////////////////////////////////////////////////////////////////////
191
192GuestDnDResponse::GuestDnDResponse(const ComObjPtr<Guest>& pGuest)
193 : m_EventSem(NIL_RTSEMEVENT)
194 , m_defAction(0)
195 , m_allActions(0)
196 , m_parent(pGuest)
197{
198 int rc = RTSemEventCreate(&m_EventSem);
199 if (RT_FAILURE(rc))
200 throw rc;
201}
202
203GuestDnDResponse::~GuestDnDResponse(void)
204{
205 reset();
206
207 int rc = RTSemEventDestroy(m_EventSem);
208 AssertRC(rc);
209}
210
211int GuestDnDResponse::notifyAboutGuestResponse(void) const
212{
213 return RTSemEventSignal(m_EventSem);
214}
215
216void GuestDnDResponse::reset(void)
217{
218 LogFlowThisFuncEnter();
219
220 m_defAction = 0;
221 m_allActions = 0;
222
223 m_strFormat = "";
224}
225
226HRESULT GuestDnDResponse::resetProgress(const ComObjPtr<Guest>& pParent)
227{
228 m_progress.setNull();
229
230 HRESULT hr = m_progress.createObject();
231 if (SUCCEEDED(hr))
232 {
233 hr = m_progress->init(static_cast<IGuest *>(pParent),
234 Bstr(pParent->tr("Dropping data")).raw(),
235 TRUE /* aCancelable */);
236 }
237
238 return hr;
239}
240
241bool GuestDnDResponse::isProgressCanceled(void) const
242{
243 BOOL fCanceled;
244 if (!m_progress.isNull())
245 {
246 HRESULT hr = m_progress->COMGETTER(Canceled)(&fCanceled);
247 AssertComRC(hr);
248 }
249 else
250 fCanceled = TRUE;
251
252 return RT_BOOL(fCanceled);
253}
254
255int GuestDnDResponse::setCallback(uint32_t uMsg, PFNGUESTDNDCALLBACK pfnCallback, void *pvUser /* = NULL */)
256{
257 GuestDnDCallbackMap::iterator it = m_mapCallbacks.find(uMsg);
258
259 /* Add. */
260 if (pfnCallback)
261 {
262 if (it == m_mapCallbacks.end())
263 {
264 m_mapCallbacks[uMsg] = GuestDnDCallback(pfnCallback, uMsg, pvUser);
265 return VINF_SUCCESS;
266 }
267
268 AssertMsgFailed(("Callback for message %RU32 already registered\n", uMsg));
269 return VERR_ALREADY_EXISTS;
270 }
271
272 /* Remove. */
273 if (it != m_mapCallbacks.end())
274 m_mapCallbacks.erase(it);
275
276 return VINF_SUCCESS;
277}
278
279int GuestDnDResponse::setProgress(unsigned uPercentage,
280 uint32_t uStatus,
281 int rcOp /* = VINF_SUCCESS */, const Utf8Str &strMsg /* = "" */)
282{
283 LogFlowFunc(("uStatus=%RU32, uPercentage=%RU32, rcOp=%Rrc, strMsg=%s\n",
284 uStatus, uPercentage, rcOp, strMsg.c_str()));
285
286 int rc = VINF_SUCCESS;
287 if (!m_progress.isNull())
288 {
289 BOOL fCompleted;
290 HRESULT hr = m_progress->COMGETTER(Completed)(&fCompleted);
291 AssertComRC(hr);
292
293 BOOL fCanceled;
294 hr = m_progress->COMGETTER(Canceled)(&fCanceled);
295 AssertComRC(hr);
296
297 LogFlowFunc(("Current: fCompleted=%RTbool, fCanceled=%RTbool\n", fCompleted, fCanceled));
298
299 if (!fCompleted)
300 {
301 switch (uStatus)
302 {
303 case DragAndDropSvc::DND_PROGRESS_ERROR:
304 {
305 hr = m_progress->i_notifyComplete(VBOX_E_IPRT_ERROR,
306 COM_IIDOF(IGuest),
307 m_parent->getComponentName(), strMsg.c_str());
308 reset();
309 break;
310 }
311
312 case DragAndDropSvc::DND_PROGRESS_CANCELLED:
313 {
314 hr = m_progress->i_notifyComplete(S_OK);
315 AssertComRC(hr);
316
317 reset();
318 break;
319 }
320
321 case DragAndDropSvc::DND_PROGRESS_RUNNING:
322 case DragAndDropSvc::DND_PROGRESS_COMPLETE:
323 {
324 if (!fCanceled)
325 {
326 hr = m_progress->SetCurrentOperationProgress(uPercentage);
327 AssertComRC(hr);
328 if ( uStatus == DragAndDropSvc::DND_PROGRESS_COMPLETE
329 || uPercentage >= 100)
330 {
331 hr = m_progress->i_notifyComplete(S_OK);
332 AssertComRC(hr);
333 }
334 }
335 break;
336 }
337
338 default:
339 break;
340 }
341 }
342
343 hr = m_progress->COMGETTER(Completed)(&fCompleted);
344 AssertComRC(hr);
345 hr = m_progress->COMGETTER(Canceled)(&fCanceled);
346 AssertComRC(hr);
347
348 LogFlowFunc(("New: fCompleted=%RTbool, fCanceled=%RTbool\n", fCompleted, fCanceled));
349 }
350
351 LogFlowFuncLeaveRC(rc);
352 return rc;
353}
354
355int GuestDnDResponse::onDispatch(uint32_t u32Function, void *pvParms, uint32_t cbParms)
356{
357 LogFlowFunc(("u32Function=%RU32, pvParms=%p, cbParms=%RU32\n", u32Function, pvParms, cbParms));
358
359 int rc = VERR_WRONG_ORDER; /* Play safe. */
360 bool fTryCallbacks = false;
361
362 switch (u32Function)
363 {
364 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
365 {
366 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
367 AssertPtr(pCBData);
368 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
369 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
370
371 setDefAction(pCBData->uAction);
372 rc = notifyAboutGuestResponse();
373 break;
374 }
375
376 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
377 {
378 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
379 AssertPtr(pCBData);
380 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
381 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
382
383 if ( pCBData->cbFormat == 0
384 || pCBData->cbFormat > _64K)
385 {
386 rc = VERR_INVALID_PARAMETER;
387 }
388 else
389 {
390 setFormat(pCBData->pszFormat);
391
392 rc = VINF_SUCCESS;
393 }
394
395 int rc2 = notifyAboutGuestResponse();
396 if (RT_SUCCESS(rc))
397 rc = rc2;
398 break;
399 }
400
401 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
402 {
403 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData =
404 reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
405 AssertPtr(pCBData);
406 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
407 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
408
409 rc = setProgress(pCBData->uPercentage, pCBData->uStatus, pCBData->rc);
410 if (RT_SUCCESS(rc))
411 rc = notifyAboutGuestResponse();
412 break;
413 }
414#ifdef VBOX_WITH_DRAG_AND_DROP_GH
415 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
416 {
417 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData =
418 reinterpret_cast<DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
419 AssertPtr(pCBData);
420 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
421 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
422
423 if ( pCBData->cbFormat == 0
424 || pCBData->cbFormat > _64K)
425 {
426 rc = VERR_INVALID_PARAMETER;
427 }
428 else
429 {
430 setFormat (pCBData->pszFormat);
431 setDefAction (pCBData->uDefAction);
432 setAllActions(pCBData->uAllActions);
433
434 rc = VINF_SUCCESS;
435 }
436
437 int rc2 = notifyAboutGuestResponse();
438 if (RT_SUCCESS(rc))
439 rc = rc2;
440 break;
441 }
442#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
443 default:
444 /* * Try if the event is covered by a registered callback. */
445 fTryCallbacks = true;
446 break;
447 }
448
449 /*
450 * Try the host's installed callbacks (if any).
451 */
452 if (fTryCallbacks)
453 {
454 GuestDnDCallbackMap::const_iterator it = m_mapCallbacks.find(u32Function);
455 if (it != m_mapCallbacks.end())
456 {
457 AssertPtr(it->second.pfnCallback);
458 rc = it->second.pfnCallback(u32Function, pvParms, cbParms, it->second.pvUser);
459 }
460 else
461 rc = VERR_NO_DATA; /* Tell the guest. */
462 }
463
464 LogFlowFunc(("Returning rc=%Rrc\n", rc));
465 return rc;
466}
467
468HRESULT GuestDnDResponse::queryProgressTo(IProgress **ppProgress)
469{
470 return m_progress.queryInterfaceTo(ppProgress);
471}
472
473int GuestDnDResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */) const
474{
475 int rc = RTSemEventWait(m_EventSem, msTimeout);
476#ifdef DEBUG_andy
477 LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
478#endif
479 return rc;
480}
481
482///////////////////////////////////////////////////////////////////////////////
483
484GuestDnD* GuestDnD::s_pInstance = NULL;
485
486GuestDnD::GuestDnD(const ComObjPtr<Guest> &pGuest)
487 : m_pGuest(pGuest)
488{
489 LogFlowFuncEnter();
490
491 m_pResponse = new GuestDnDResponse(pGuest);
492
493 /* List of supported default MIME types. */
494 const com::Utf8Str arrEntries[] = { VBOX_DND_FORMATS_DEFAULT };
495 for (size_t i = 0; i < RT_ELEMENTS(arrEntries); i++)
496 m_strDefaultFormats.push_back(arrEntries[i]);
497}
498
499GuestDnD::~GuestDnD(void)
500{
501 LogFlowFuncEnter();
502
503 if (m_pResponse)
504 delete m_pResponse;
505}
506
507int GuestDnD::adjustScreenCoordinates(ULONG uScreenId, ULONG *puX, ULONG *puY) const
508{
509 /** @todo r=andy Save the current screen's shifting coordinates to speed things up.
510 * Only query for new offsets when the screen ID has changed. */
511
512 /* For multi-monitor support we need to add shift values to the coordinates
513 * (depending on the screen number). */
514 ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
515 ComPtr<IDisplay> pDisplay;
516 HRESULT hr = pConsole->COMGETTER(Display)(pDisplay.asOutParam());
517 if (FAILED(hr))
518 return hr;
519
520 ULONG dummy;
521 LONG xShift, yShift;
522 GuestMonitorStatus_T monitorStatus;
523 hr = pDisplay->GetScreenResolution(uScreenId, &dummy, &dummy, &dummy,
524 &xShift, &yShift, &monitorStatus);
525 if (FAILED(hr))
526 return hr;
527
528 if (puX)
529 *puX += xShift;
530 if (puY)
531 *puY += yShift;
532
533#ifdef DEBUG_andy
534 LogFlowFunc(("uScreenId=%RU32, x=%RU32, y=%RU32\n",
535 uScreenId, puX ? *puX : 0, puY ? *puY : 0));
536#endif
537 return VINF_SUCCESS;
538}
539
540int GuestDnD::hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const
541{
542 Assert(!m_pGuest.isNull());
543 ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
544
545 /* Forward the information to the VMM device. */
546 Assert(!pConsole.isNull());
547 VMMDev *pVMMDev = pConsole->i_getVMMDev();
548 if (!pVMMDev)
549 return VERR_COM_OBJECT_NOT_FOUND;
550
551 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", u32Function, cParms, paParms);
552 LogFlowFunc(("uMsg=%RU32, cParms=%RU32, rc=%Rrc\n", u32Function, cParms, rc));
553 return rc;
554}
555
556/* static */
557DECLCALLBACK(int) GuestDnD::notifyDnDDispatcher(void *pvExtension, uint32_t u32Function,
558 void *pvParms, uint32_t cbParms)
559{
560 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
561 pvExtension, u32Function, pvParms, cbParms));
562
563 GuestDnD *pGuestDnD = reinterpret_cast<GuestDnD*>(pvExtension);
564 AssertPtrReturn(pGuestDnD, VERR_INVALID_POINTER);
565
566 /** @todo In case we need to handle multiple guest DnD responses at a time this
567 * would be the place to lookup and dispatch to those. For the moment we
568 * only have one response -- simple. */
569 GuestDnDResponse *pResp = pGuestDnD->m_pResponse;
570 if (pResp)
571 return pResp->onDispatch(u32Function, pvParms, cbParms);
572
573 return VERR_NOT_SUPPORTED;
574}
575
576
577/* static */
578com::Utf8Str GuestDnD::toFormatString(const std::vector<com::Utf8Str> &lstSupportedFormats,
579 const std::vector<com::Utf8Str> &lstFormats)
580{
581 com::Utf8Str strFormat;
582 for (size_t i = 0; i < lstFormats.size(); ++i)
583 {
584 const com::Utf8Str &f = lstFormats.at(i);
585 /* Only keep allowed format types. */
586 if (std::find(lstSupportedFormats.begin(),
587 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
588 strFormat += f + "\r\n";
589 }
590
591 return strFormat;
592}
593
594/* static */
595void GuestDnD::toFormatVector(const std::vector<com::Utf8Str> &lstSupportedFormats,
596 const com::Utf8Str &strFormats,
597 std::vector<com::Utf8Str> &vecformats)
598{
599 RTCList<RTCString> lstFormats = strFormats.split("\r\n");
600 size_t i = 0;
601 while (i < lstFormats.size())
602 {
603 /* Only keep allowed format types. */
604 if (std::find(lstSupportedFormats.begin(),
605 lstSupportedFormats.end(), lstFormats.at(i)) == lstSupportedFormats.end())
606 lstFormats.removeAt(i);
607 else
608 ++i;
609 }
610
611 for (i = 0; i < lstFormats.size(); i++)
612 {
613 const Utf8Str &f = lstFormats.at(i);
614 if (std::find(lstSupportedFormats.begin(),
615 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
616 vecformats.push_back(lstFormats[i]);
617 }
618}
619
620/* static */
621uint32_t GuestDnD::toHGCMAction(DnDAction_T enmAction)
622{
623 uint32_t uAction = DND_IGNORE_ACTION;
624 switch (enmAction)
625 {
626 case DnDAction_Copy:
627 uAction = DND_COPY_ACTION;
628 break;
629 case DnDAction_Move:
630 uAction = DND_MOVE_ACTION;
631 break;
632 case DnDAction_Link:
633 /* For now it doesn't seems useful to allow a link
634 action between host & guest. Later? */
635 case DnDAction_Ignore:
636 /* Ignored. */
637 break;
638 default:
639 AssertMsgFailed(("Action %RU32 not recognized!\n", enmAction));
640 break;
641 }
642
643 return uAction;
644}
645
646/* static */
647void GuestDnD::toHGCMActions(DnDAction_T enmDefAction,
648 uint32_t *puDefAction,
649 const std::vector<DnDAction_T> vecAllowedActions,
650 uint32_t *puAllowedActions)
651{
652 if (puDefAction)
653 *puDefAction = toHGCMAction(enmDefAction);
654 if (puAllowedActions)
655 {
656 *puAllowedActions = DND_IGNORE_ACTION;
657
658 /* First convert the allowed actions to a bit array. */
659 for (size_t i = 0; i < vecAllowedActions.size(); ++i)
660 *puAllowedActions |= toHGCMAction(vecAllowedActions[i]);
661
662 /* Second check if the default action is a valid action and if not so try
663 * to find an allowed action. */
664 if (isDnDIgnoreAction(*puAllowedActions))
665 {
666 if (hasDnDCopyAction(*puAllowedActions))
667 *puAllowedActions = DND_COPY_ACTION;
668 else if (hasDnDMoveAction(*puAllowedActions))
669 *puAllowedActions = DND_MOVE_ACTION;
670 }
671 }
672}
673
674/* static */
675DnDAction_T GuestDnD::toMainAction(uint32_t uAction)
676{
677 /* For now it doesn't seems useful to allow a
678 * link action between host & guest. Maybe later! */
679 return (isDnDCopyAction(uAction) ? (DnDAction_T)DnDAction_Copy :
680 isDnDMoveAction(uAction) ? (DnDAction_T)DnDAction_Move :
681 (DnDAction_T)DnDAction_Ignore);
682}
683
684/* static */
685void GuestDnD::toMainActions(uint32_t uActions,
686 std::vector<DnDAction_T> &vecActions)
687{
688 /* For now it doesn't seems useful to allow a
689 * link action between host & guest. Maybe later! */
690 RTCList<DnDAction_T> lstActions;
691 if (hasDnDCopyAction(uActions))
692 lstActions.append(DnDAction_Copy);
693 if (hasDnDMoveAction(uActions))
694 lstActions.append(DnDAction_Move);
695
696 for (size_t i = 0; i < lstActions.size(); ++i)
697 vecActions.push_back(lstActions.at(i));
698}
699
700///////////////////////////////////////////////////////////////////////////////
701
702GuestDnDBase::GuestDnDBase(void)
703{
704 mDataBase.mfTransferIsPending = false;
705
706 /*
707 * Initialize public attributes.
708 */
709 m_strFormats = GuestDnDInst()->defaultFormats();
710}
711
712HRESULT GuestDnDBase::i_isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
713{
714 *aSupported = std::find(m_strFormats.begin(),
715 m_strFormats.end(), aFormat) != m_strFormats.end()
716 ? TRUE : FALSE;
717 return S_OK;
718}
719
720HRESULT GuestDnDBase::i_getFormats(std::vector<com::Utf8Str> &aFormats)
721{
722 aFormats = m_strFormats;
723
724 return S_OK;
725}
726
727HRESULT GuestDnDBase::i_addFormats(const std::vector<com::Utf8Str> &aFormats)
728{
729 for (size_t i = 0; i < aFormats.size(); ++i)
730 {
731 Utf8Str strFormat = aFormats.at(i);
732 if (std::find(m_strFormats.begin(),
733 m_strFormats.end(), strFormat) == m_strFormats.end())
734 {
735 m_strFormats.push_back(strFormat);
736 }
737 }
738
739 return S_OK;
740}
741
742HRESULT GuestDnDBase::i_removeFormats(const std::vector<com::Utf8Str> &aFormats)
743{
744 for (size_t i = 0; i < aFormats.size(); ++i)
745 {
746 Utf8Str strFormat = aFormats.at(i);
747 std::vector<com::Utf8Str>::iterator itFormat = std::find(m_strFormats.begin(),
748 m_strFormats.end(), strFormat);
749 if (itFormat != m_strFormats.end())
750 m_strFormats.erase(itFormat);
751 }
752
753 return S_OK;
754}
755
756HRESULT GuestDnDBase::i_getProtocolVersion(ULONG *puVersion)
757{
758 int rc = getProtocolVersion((uint32_t *)puVersion);
759 return RT_SUCCESS(rc) ? S_OK : E_FAIL;
760}
761
762int GuestDnDBase::getProtocolVersion(uint32_t *puVersion)
763{
764 AssertPtrReturn(puVersion, VERR_INVALID_POINTER);
765
766 int rc;
767
768 uint32_t uVer, uVerAdditions = 0;
769 if ( m_pGuest
770 && (uVerAdditions = m_pGuest->i_getAdditionsVersion()) > 0)
771 {
772 uint32_t uVBoxMajor = VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions);
773 uint32_t uVBoxMinor = VBOX_FULL_VERSION_GET_MINOR(uVerAdditions);
774
775#if 0 /*def DEBUG_andy*/
776 /* Hardcode the to-used protocol version; nice for testing side effects. */
777 uVer = 2;
778#else
779 uVer = ( uVBoxMajor >= 5)
780 ? 2 /* VBox 5.0 and up: Protocol version 2. */
781 : 1; /* VBox <= 4.3: Protocol version 1. */
782 /* Build revision is ignored. */
783#endif
784
785 LogFlowThisFunc(("uVerAdditions=%RU32 (%RU32.%RU32)\n", uVerAdditions, uVBoxMajor, uVBoxMinor));
786 rc = VINF_SUCCESS;
787 }
788 else
789 {
790 uVer = 1; /* Fallback. */
791 rc = VERR_NOT_FOUND;
792 }
793
794 LogFlowThisFunc(("uVer=%RU32, uVerAdditions=%RU32, rc=%Rrc\n", uVer, uVerAdditions, rc));
795
796 *puVersion = uVer;
797 return rc;
798}
799
800int GuestDnDBase::msgQueueAdd(GuestDnDMsg *pMsg)
801{
802 mDataBase.mListOutgoing.push_back(pMsg);
803 return VINF_SUCCESS;
804}
805
806GuestDnDMsg *GuestDnDBase::msgQueueGetNext(void)
807{
808 if (mDataBase.mListOutgoing.empty())
809 return NULL;
810 return mDataBase.mListOutgoing.front();
811}
812
813void GuestDnDBase::msgQueueRemoveNext(void)
814{
815 if (!mDataBase.mListOutgoing.empty())
816 {
817 GuestDnDMsg *pMsg = mDataBase.mListOutgoing.front();
818 if (pMsg)
819 delete pMsg;
820 mDataBase.mListOutgoing.pop_front();
821 }
822}
823
824void GuestDnDBase::msgQueueClear(void)
825{
826 GuestDnDMsgList::iterator itMsg = mDataBase.mListOutgoing.begin();
827 while (itMsg != mDataBase.mListOutgoing.end())
828 {
829 delete *itMsg;
830 }
831
832 mDataBase.mListOutgoing.clear();
833}
834
835int GuestDnDBase::sendCancel(void)
836{
837 LogFlowFunc(("Generating cancel request ...\n"));
838
839 int rc;
840 try
841 {
842 GuestDnDMsg *pMsgCancel = new GuestDnDMsg();
843 pMsgCancel->setType(DragAndDropSvc::HOST_DND_HG_EVT_CANCEL);
844
845 rc = msgQueueAdd(pMsgCancel);
846 }
847 catch(std::bad_alloc & /*e*/)
848 {
849 rc = VERR_NO_MEMORY;
850 }
851
852 return rc;
853}
854
855/** @todo GuestDnDResponse *pResp needs to go. */
856int GuestDnDBase::waitForEvent(RTMSINTERVAL msTimeout, GuestDnDCallbackEvent &Event, GuestDnDResponse *pResp)
857{
858 int rc;
859
860 uint64_t tsStart = RTTimeMilliTS();
861 do
862 {
863 /*
864 * Wait until our desired callback triggered the
865 * wait event. As we don't want to block if the guest does not
866 * respond, do busy waiting here.
867 */
868 rc = Event.Wait(500 /* ms */);
869 if (RT_SUCCESS(rc))
870 {
871 rc = Event.Result();
872 LogFlowFunc(("Callback done, result is %Rrc\n", rc));
873 break;
874 }
875 else if (rc == VERR_TIMEOUT) /* Continue waiting. */
876 continue;
877
878 if ( msTimeout != RT_INDEFINITE_WAIT
879 && RTTimeMilliTS() - tsStart > msTimeout)
880 {
881 rc = VERR_TIMEOUT;
882 LogFlowFunc(("Guest did not respond within time\n"));
883 }
884 else if (pResp->isProgressCanceled()) /** @todo GuestDnDResponse *pResp needs to go. */
885 {
886 LogFlowFunc(("Canceled by user\n"));
887 rc = VERR_CANCELLED;
888 }
889
890 } while (RT_SUCCESS(rc));
891
892 LogFlowFuncLeaveRC(rc);
893 return rc;
894}
895#endif /* VBOX_WITH_DRAG_AND_DROP */
896
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