VirtualBox

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

Last change on this file since 55631 was 55571, checked in by vboxsync, 10 years ago

DnD: Simplified cancellation logic.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.4 KB
Line 
1/* $Id: GuestDnDPrivate.cpp 55571 2015-04-30 17:04:37Z 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)
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
211/* static */
212Utf8Str GuestDnDResponse::errorToString(const ComObjPtr<Guest>& pGuest, int guestRc)
213{
214 Utf8Str strError;
215
216 switch (guestRc)
217 {
218 case VERR_ACCESS_DENIED:
219 strError += Utf8StrFmt(pGuest->tr("For one or more guest files or directories selected for transferring to the host your guest "
220 "user does not have the appropriate access rights for. Please make sure that all selected "
221 "elements can be accessed and that your guest user has the appropriate rights."));
222 break;
223
224 case VERR_NOT_FOUND:
225 /* Should not happen due to file locking on the guest, but anyway ... */
226 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were not"
227 "found on the guest anymore. This can be the case if the guest files were moved and/or"
228 "altered while the drag and drop operation was in progress."));
229 break;
230
231 case VERR_SHARING_VIOLATION:
232 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were locked. "
233 "Please make sure that all selected elements can be accessed and that your guest user has "
234 "the appropriate rights."));
235 break;
236
237 default:
238 strError += Utf8StrFmt("Drag and drop guest error (%Rrc)", guestRc);
239 break;
240 }
241
242 return strError;
243}
244
245int GuestDnDResponse::notifyAboutGuestResponse(void) const
246{
247 return RTSemEventSignal(m_EventSem);
248}
249
250void GuestDnDResponse::reset(void)
251{
252 LogFlowThisFuncEnter();
253
254 m_defAction = 0;
255 m_allActions = 0;
256
257 m_strFormat = "";
258}
259
260HRESULT GuestDnDResponse::resetProgress(const ComObjPtr<Guest>& pParent)
261{
262 m_progress.setNull();
263
264 HRESULT hr = m_progress.createObject();
265 if (SUCCEEDED(hr))
266 {
267 hr = m_progress->init(static_cast<IGuest *>(pParent),
268 Bstr(pParent->tr("Dropping data")).raw(),
269 TRUE /* aCancelable */);
270 }
271
272 return hr;
273}
274
275bool GuestDnDResponse::isProgressCanceled(void) const
276{
277 BOOL fCanceled;
278 if (!m_progress.isNull())
279 {
280 HRESULT hr = m_progress->COMGETTER(Canceled)(&fCanceled);
281 AssertComRC(hr);
282 }
283 else
284 fCanceled = TRUE;
285
286 return RT_BOOL(fCanceled);
287}
288
289int GuestDnDResponse::setCallback(uint32_t uMsg, PFNGUESTDNDCALLBACK pfnCallback, void *pvUser /* = NULL */)
290{
291 GuestDnDCallbackMap::iterator it = m_mapCallbacks.find(uMsg);
292
293 /* Add. */
294 if (pfnCallback)
295 {
296 if (it == m_mapCallbacks.end())
297 {
298 m_mapCallbacks[uMsg] = GuestDnDCallback(pfnCallback, uMsg, pvUser);
299 return VINF_SUCCESS;
300 }
301
302 AssertMsgFailed(("Callback for message %RU32 already registered\n", uMsg));
303 return VERR_ALREADY_EXISTS;
304 }
305
306 /* Remove. */
307 if (it != m_mapCallbacks.end())
308 m_mapCallbacks.erase(it);
309
310 return VINF_SUCCESS;
311}
312
313int GuestDnDResponse::setProgress(unsigned uPercentage,
314 uint32_t uStatus, int rcOp /* = VINF_SUCCESS */)
315{
316 LogFlowFunc(("uStatus=%RU32, uPercentage=%RU32, rcOp=%Rrc\n",
317 uStatus, uPercentage, rcOp));
318
319 int rc = VINF_SUCCESS;
320 if (!m_progress.isNull())
321 {
322 BOOL fCompleted;
323 HRESULT hr = m_progress->COMGETTER(Completed)(&fCompleted);
324 AssertComRC(hr);
325
326 BOOL fCanceled;
327 hr = m_progress->COMGETTER(Canceled)(&fCanceled);
328 AssertComRC(hr);
329
330 LogFlowFunc(("Current: fCompleted=%RTbool, fCanceled=%RTbool\n", fCompleted, fCanceled));
331
332 if (!fCompleted)
333 {
334 switch (uStatus)
335 {
336 case DragAndDropSvc::DND_PROGRESS_ERROR:
337 {
338 hr = m_progress->i_notifyComplete(VBOX_E_IPRT_ERROR,
339 COM_IIDOF(IGuest),
340 m_parent->getComponentName(),
341 GuestDnDResponse::errorToString(m_parent, rcOp).c_str());
342 reset();
343 break;
344 }
345
346 case DragAndDropSvc::DND_PROGRESS_CANCELLED:
347 {
348 hr = m_progress->i_notifyComplete(S_OK);
349 AssertComRC(hr);
350
351 reset();
352 break;
353 }
354
355 case DragAndDropSvc::DND_PROGRESS_RUNNING:
356 case DragAndDropSvc::DND_PROGRESS_COMPLETE:
357 {
358 if (!fCanceled)
359 {
360 hr = m_progress->SetCurrentOperationProgress(uPercentage);
361 AssertComRC(hr);
362 if ( uStatus == DragAndDropSvc::DND_PROGRESS_COMPLETE
363 || uPercentage >= 100)
364 {
365 hr = m_progress->i_notifyComplete(S_OK);
366 AssertComRC(hr);
367 }
368 }
369 break;
370 }
371
372 default:
373 break;
374 }
375 }
376
377 hr = m_progress->COMGETTER(Completed)(&fCompleted);
378 AssertComRC(hr);
379 hr = m_progress->COMGETTER(Canceled)(&fCanceled);
380 AssertComRC(hr);
381
382 LogFlowFunc(("New: fCompleted=%RTbool, fCanceled=%RTbool\n", fCompleted, fCanceled));
383 }
384
385 LogFlowFuncLeaveRC(rc);
386 return rc;
387}
388
389int GuestDnDResponse::onDispatch(uint32_t u32Function, void *pvParms, uint32_t cbParms)
390{
391 LogFlowFunc(("u32Function=%RU32, pvParms=%p, cbParms=%RU32\n", u32Function, pvParms, cbParms));
392
393 int rc = VERR_WRONG_ORDER; /* Play safe. */
394 bool fTryCallbacks = false;
395
396 switch (u32Function)
397 {
398 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
399 {
400 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
401 AssertPtr(pCBData);
402 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
403 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
404
405 setDefAction(pCBData->uAction);
406 rc = notifyAboutGuestResponse();
407 break;
408 }
409
410 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
411 {
412 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
413 AssertPtr(pCBData);
414 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
415 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
416
417 if ( pCBData->cbFormat == 0
418 || pCBData->cbFormat > _64K)
419 {
420 rc = VERR_INVALID_PARAMETER;
421 }
422 else
423 {
424 setFormat(pCBData->pszFormat);
425
426 rc = VINF_SUCCESS;
427 }
428
429 int rc2 = notifyAboutGuestResponse();
430 if (RT_SUCCESS(rc))
431 rc = rc2;
432 break;
433 }
434
435 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
436 {
437 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData =
438 reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
439 AssertPtr(pCBData);
440 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
441 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
442
443 rc = setProgress(pCBData->uPercentage, pCBData->uStatus, pCBData->rc);
444 if (RT_SUCCESS(rc))
445 rc = notifyAboutGuestResponse();
446 break;
447 }
448#ifdef VBOX_WITH_DRAG_AND_DROP_GH
449 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
450 {
451 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData =
452 reinterpret_cast<DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
453 AssertPtr(pCBData);
454 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
455 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
456
457 if ( pCBData->cbFormat == 0
458 || pCBData->cbFormat > _64K)
459 {
460 rc = VERR_INVALID_PARAMETER;
461 }
462 else
463 {
464 setFormat (pCBData->pszFormat);
465 setDefAction (pCBData->uDefAction);
466 setAllActions(pCBData->uAllActions);
467
468 rc = VINF_SUCCESS;
469 }
470
471 int rc2 = notifyAboutGuestResponse();
472 if (RT_SUCCESS(rc))
473 rc = rc2;
474 break;
475 }
476#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
477 default:
478 /* * Try if the event is covered by a registered callback. */
479 fTryCallbacks = true;
480 break;
481 }
482
483 /*
484 * Try the host's installed callbacks (if any).
485 */
486 if (fTryCallbacks)
487 {
488 GuestDnDCallbackMap::const_iterator it = m_mapCallbacks.find(u32Function);
489 if (it != m_mapCallbacks.end())
490 {
491 AssertPtr(it->second.pfnCallback);
492 rc = it->second.pfnCallback(u32Function, pvParms, cbParms, it->second.pvUser);
493 }
494 else
495 rc = VERR_NO_DATA; /* Tell the guest. */
496 }
497
498 LogFlowFunc(("Returning rc=%Rrc\n", rc));
499 return rc;
500}
501
502HRESULT GuestDnDResponse::queryProgressTo(IProgress **ppProgress)
503{
504 return m_progress.queryInterfaceTo(ppProgress);
505}
506
507int GuestDnDResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */) const
508{
509 int rc = RTSemEventWait(m_EventSem, msTimeout);
510#ifdef DEBUG_andy
511 LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
512#endif
513 return rc;
514}
515
516///////////////////////////////////////////////////////////////////////////////
517
518GuestDnD* GuestDnD::s_pInstance = NULL;
519
520GuestDnD::GuestDnD(const ComObjPtr<Guest> &pGuest)
521 : m_pGuest(pGuest)
522{
523 LogFlowFuncEnter();
524
525 m_pResponse = new GuestDnDResponse(pGuest);
526
527 /* List of supported default MIME types. */
528 const com::Utf8Str arrEntries[] = { VBOX_DND_FORMATS_DEFAULT };
529 for (size_t i = 0; i < RT_ELEMENTS(arrEntries); i++)
530 m_strDefaultFormats.push_back(arrEntries[i]);
531}
532
533GuestDnD::~GuestDnD(void)
534{
535 LogFlowFuncEnter();
536
537 if (m_pResponse)
538 delete m_pResponse;
539}
540
541int GuestDnD::adjustScreenCoordinates(ULONG uScreenId, ULONG *puX, ULONG *puY) const
542{
543 /** @todo r=andy Save the current screen's shifting coordinates to speed things up.
544 * Only query for new offsets when the screen ID has changed. */
545
546 /* For multi-monitor support we need to add shift values to the coordinates
547 * (depending on the screen number). */
548 ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
549 ComPtr<IDisplay> pDisplay;
550 HRESULT hr = pConsole->COMGETTER(Display)(pDisplay.asOutParam());
551 if (FAILED(hr))
552 return hr;
553
554 ULONG dummy;
555 LONG xShift, yShift;
556 GuestMonitorStatus_T monitorStatus;
557 hr = pDisplay->GetScreenResolution(uScreenId, &dummy, &dummy, &dummy,
558 &xShift, &yShift, &monitorStatus);
559 if (FAILED(hr))
560 return hr;
561
562 if (puX)
563 *puX += xShift;
564 if (puY)
565 *puY += yShift;
566
567#ifdef DEBUG_andy
568 LogFlowFunc(("uScreenId=%RU32, x=%RU32, y=%RU32\n",
569 uScreenId, puX ? *puX : 0, puY ? *puY : 0));
570#endif
571 return VINF_SUCCESS;
572}
573
574int GuestDnD::hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const
575{
576 Assert(!m_pGuest.isNull());
577 ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
578
579 /* Forward the information to the VMM device. */
580 Assert(!pConsole.isNull());
581 VMMDev *pVMMDev = pConsole->i_getVMMDev();
582 if (!pVMMDev)
583 return VERR_COM_OBJECT_NOT_FOUND;
584
585 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", u32Function, cParms, paParms);
586 LogFlowFunc(("uMsg=%RU32, cParms=%RU32, rc=%Rrc\n", u32Function, cParms, rc));
587 return rc;
588}
589
590/* static */
591DECLCALLBACK(int) GuestDnD::notifyDnDDispatcher(void *pvExtension, uint32_t u32Function,
592 void *pvParms, uint32_t cbParms)
593{
594 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
595 pvExtension, u32Function, pvParms, cbParms));
596
597 GuestDnD *pGuestDnD = reinterpret_cast<GuestDnD*>(pvExtension);
598 AssertPtrReturn(pGuestDnD, VERR_INVALID_POINTER);
599
600 /** @todo In case we need to handle multiple guest DnD responses at a time this
601 * would be the place to lookup and dispatch to those. For the moment we
602 * only have one response -- simple. */
603 GuestDnDResponse *pResp = pGuestDnD->m_pResponse;
604 if (pResp)
605 return pResp->onDispatch(u32Function, pvParms, cbParms);
606
607 return VERR_NOT_SUPPORTED;
608}
609
610
611/* static */
612com::Utf8Str GuestDnD::toFormatString(const std::vector<com::Utf8Str> &lstSupportedFormats,
613 const std::vector<com::Utf8Str> &lstFormats)
614{
615 com::Utf8Str strFormat;
616 for (size_t i = 0; i < lstFormats.size(); ++i)
617 {
618 const com::Utf8Str &f = lstFormats.at(i);
619 /* Only keep allowed format types. */
620 if (std::find(lstSupportedFormats.begin(),
621 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
622 strFormat += f + "\r\n";
623 }
624
625 return strFormat;
626}
627
628/* static */
629void GuestDnD::toFormatVector(const std::vector<com::Utf8Str> &lstSupportedFormats,
630 const com::Utf8Str &strFormats,
631 std::vector<com::Utf8Str> &vecformats)
632{
633 RTCList<RTCString> lstFormats = strFormats.split("\r\n");
634 size_t i = 0;
635 while (i < lstFormats.size())
636 {
637 /* Only keep allowed format types. */
638 if (std::find(lstSupportedFormats.begin(),
639 lstSupportedFormats.end(), lstFormats.at(i)) == lstSupportedFormats.end())
640 lstFormats.removeAt(i);
641 else
642 ++i;
643 }
644
645 for (i = 0; i < lstFormats.size(); i++)
646 {
647 const Utf8Str &f = lstFormats.at(i);
648 if (std::find(lstSupportedFormats.begin(),
649 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
650 vecformats.push_back(lstFormats[i]);
651 }
652}
653
654/* static */
655uint32_t GuestDnD::toHGCMAction(DnDAction_T enmAction)
656{
657 uint32_t uAction = DND_IGNORE_ACTION;
658 switch (enmAction)
659 {
660 case DnDAction_Copy:
661 uAction = DND_COPY_ACTION;
662 break;
663 case DnDAction_Move:
664 uAction = DND_MOVE_ACTION;
665 break;
666 case DnDAction_Link:
667 /* For now it doesn't seems useful to allow a link
668 action between host & guest. Later? */
669 case DnDAction_Ignore:
670 /* Ignored. */
671 break;
672 default:
673 AssertMsgFailed(("Action %RU32 not recognized!\n", enmAction));
674 break;
675 }
676
677 return uAction;
678}
679
680/* static */
681void GuestDnD::toHGCMActions(DnDAction_T enmDefAction,
682 uint32_t *puDefAction,
683 const std::vector<DnDAction_T> vecAllowedActions,
684 uint32_t *puAllowedActions)
685{
686 if (puDefAction)
687 *puDefAction = toHGCMAction(enmDefAction);
688 if (puAllowedActions)
689 {
690 *puAllowedActions = DND_IGNORE_ACTION;
691
692 /* First convert the allowed actions to a bit array. */
693 for (size_t i = 0; i < vecAllowedActions.size(); ++i)
694 *puAllowedActions |= toHGCMAction(vecAllowedActions[i]);
695
696 /* Second check if the default action is a valid action and if not so try
697 * to find an allowed action. */
698 if (isDnDIgnoreAction(*puAllowedActions))
699 {
700 if (hasDnDCopyAction(*puAllowedActions))
701 *puAllowedActions = DND_COPY_ACTION;
702 else if (hasDnDMoveAction(*puAllowedActions))
703 *puAllowedActions = DND_MOVE_ACTION;
704 }
705 }
706}
707
708/* static */
709DnDAction_T GuestDnD::toMainAction(uint32_t uAction)
710{
711 /* For now it doesn't seems useful to allow a
712 * link action between host & guest. Maybe later! */
713 return (isDnDCopyAction(uAction) ? (DnDAction_T)DnDAction_Copy :
714 isDnDMoveAction(uAction) ? (DnDAction_T)DnDAction_Move :
715 (DnDAction_T)DnDAction_Ignore);
716}
717
718/* static */
719void GuestDnD::toMainActions(uint32_t uActions,
720 std::vector<DnDAction_T> &vecActions)
721{
722 /* For now it doesn't seems useful to allow a
723 * link action between host & guest. Maybe later! */
724 RTCList<DnDAction_T> lstActions;
725 if (hasDnDCopyAction(uActions))
726 lstActions.append(DnDAction_Copy);
727 if (hasDnDMoveAction(uActions))
728 lstActions.append(DnDAction_Move);
729
730 for (size_t i = 0; i < lstActions.size(); ++i)
731 vecActions.push_back(lstActions.at(i));
732}
733
734///////////////////////////////////////////////////////////////////////////////
735
736GuestDnDBase::GuestDnDBase(void)
737{
738 mDataBase.mfTransferIsPending = false;
739
740 /*
741 * Initialize public attributes.
742 */
743 m_strFormats = GuestDnDInst()->defaultFormats();
744}
745
746HRESULT GuestDnDBase::i_isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
747{
748 *aSupported = std::find(m_strFormats.begin(),
749 m_strFormats.end(), aFormat) != m_strFormats.end()
750 ? TRUE : FALSE;
751 return S_OK;
752}
753
754HRESULT GuestDnDBase::i_getFormats(std::vector<com::Utf8Str> &aFormats)
755{
756 aFormats = m_strFormats;
757
758 return S_OK;
759}
760
761HRESULT GuestDnDBase::i_addFormats(const std::vector<com::Utf8Str> &aFormats)
762{
763 for (size_t i = 0; i < aFormats.size(); ++i)
764 {
765 Utf8Str strFormat = aFormats.at(i);
766 if (std::find(m_strFormats.begin(),
767 m_strFormats.end(), strFormat) == m_strFormats.end())
768 {
769 m_strFormats.push_back(strFormat);
770 }
771 }
772
773 return S_OK;
774}
775
776HRESULT GuestDnDBase::i_removeFormats(const std::vector<com::Utf8Str> &aFormats)
777{
778 for (size_t i = 0; i < aFormats.size(); ++i)
779 {
780 Utf8Str strFormat = aFormats.at(i);
781 std::vector<com::Utf8Str>::iterator itFormat = std::find(m_strFormats.begin(),
782 m_strFormats.end(), strFormat);
783 if (itFormat != m_strFormats.end())
784 m_strFormats.erase(itFormat);
785 }
786
787 return S_OK;
788}
789
790HRESULT GuestDnDBase::i_getProtocolVersion(ULONG *puVersion)
791{
792 int rc = getProtocolVersion((uint32_t *)puVersion);
793 return RT_SUCCESS(rc) ? S_OK : E_FAIL;
794}
795
796int GuestDnDBase::getProtocolVersion(uint32_t *puVersion)
797{
798 AssertPtrReturn(puVersion, VERR_INVALID_POINTER);
799
800 int rc;
801
802 uint32_t uVer, uVerAdditions = 0;
803 if ( m_pGuest
804 && (uVerAdditions = m_pGuest->i_getAdditionsVersion()) > 0)
805 {
806 uint32_t uVBoxMajor = VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions);
807 uint32_t uVBoxMinor = VBOX_FULL_VERSION_GET_MINOR(uVerAdditions);
808
809#if 0 /*def DEBUG_andy*/
810 /* Hardcode the to-used protocol version; nice for testing side effects. */
811 uVer = 2;
812#else
813 uVer = ( uVBoxMajor >= 5)
814 ? 2 /* VBox 5.0 and up: Protocol version 2. */
815 : 1; /* VBox <= 4.3: Protocol version 1. */
816 /* Build revision is ignored. */
817#endif
818
819 LogFlowThisFunc(("uVerAdditions=%RU32 (%RU32.%RU32)\n", uVerAdditions, uVBoxMajor, uVBoxMinor));
820 rc = VINF_SUCCESS;
821 }
822 else
823 {
824 uVer = 1; /* Fallback. */
825 rc = VERR_NOT_FOUND;
826 }
827
828 LogFlowThisFunc(("uVer=%RU32, uVerAdditions=%RU32, rc=%Rrc\n", uVer, uVerAdditions, rc));
829
830 *puVersion = uVer;
831 return rc;
832}
833
834int GuestDnDBase::msgQueueAdd(GuestDnDMsg *pMsg)
835{
836 mDataBase.mListOutgoing.push_back(pMsg);
837 return VINF_SUCCESS;
838}
839
840GuestDnDMsg *GuestDnDBase::msgQueueGetNext(void)
841{
842 if (mDataBase.mListOutgoing.empty())
843 return NULL;
844 return mDataBase.mListOutgoing.front();
845}
846
847void GuestDnDBase::msgQueueRemoveNext(void)
848{
849 if (!mDataBase.mListOutgoing.empty())
850 {
851 GuestDnDMsg *pMsg = mDataBase.mListOutgoing.front();
852 if (pMsg)
853 delete pMsg;
854 mDataBase.mListOutgoing.pop_front();
855 }
856}
857
858void GuestDnDBase::msgQueueClear(void)
859{
860 GuestDnDMsgList::iterator itMsg = mDataBase.mListOutgoing.begin();
861 while (itMsg != mDataBase.mListOutgoing.end())
862 {
863 delete *itMsg;
864 }
865
866 mDataBase.mListOutgoing.clear();
867}
868
869int GuestDnDBase::sendCancel(void)
870{
871 LogFlowFunc(("Sending cancelation request to guest ...\n"));
872
873 GuestDnDMsg MsgCancel;
874 MsgCancel.setType(DragAndDropSvc::HOST_DND_HG_EVT_CANCEL);
875
876 return GuestDnDInst()->hostCall(MsgCancel.getType(), MsgCancel.getCount(), MsgCancel.getParms());
877}
878
879/** @todo GuestDnDResponse *pResp needs to go. */
880int GuestDnDBase::waitForEvent(RTMSINTERVAL msTimeout, GuestDnDCallbackEvent &Event, GuestDnDResponse *pResp)
881{
882 int rc;
883
884 uint64_t tsStart = RTTimeMilliTS();
885 do
886 {
887 /*
888 * Wait until our desired callback triggered the
889 * wait event. As we don't want to block if the guest does not
890 * respond, so do busy waiting here.
891 */
892 rc = Event.Wait(500 /* ms */);
893 if (RT_SUCCESS(rc))
894 {
895 rc = Event.Result();
896 LogFlowFunc(("Callback done, result is %Rrc\n", rc));
897 break;
898 }
899 else if (rc == VERR_TIMEOUT) /* Continue waiting. */
900 rc = VINF_SUCCESS;
901
902 if ( msTimeout != RT_INDEFINITE_WAIT
903 && RTTimeMilliTS() - tsStart > msTimeout)
904 {
905 rc = VERR_TIMEOUT;
906 }
907 else if (pResp->isProgressCanceled())
908 {
909 pResp->setProgress(100 /* Percent */, DragAndDropSvc::DND_PROGRESS_CANCELLED);
910 rc = VERR_CANCELLED;
911 }
912
913 } while (RT_SUCCESS(rc));
914
915 LogFlowFuncLeaveRC(rc);
916 return rc;
917}
918#endif /* VBOX_WITH_DRAG_AND_DROP */
919
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