VirtualBox

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

Last change on this file since 57194 was 56734, checked in by vboxsync, 9 years ago

DnD/Main: Fixed setting the default action based on the allowed actions.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.2 KB
Line 
1/* $Id: GuestDnDPrivate.cpp 56734 2015-07-01 14:09:26Z 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_strFmtReq = "";
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 setFmtReq(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 setFmtReq (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 LogRel2(("DnD: Supported default host formats:\n"));
495 const com::Utf8Str arrEntries[] = { VBOX_DND_FORMATS_DEFAULT };
496 for (size_t i = 0; i < RT_ELEMENTS(arrEntries); i++)
497 {
498 m_strDefaultFormats.push_back(arrEntries[i]);
499 LogRel2(("DnD: \t%s\n", arrEntries[i].c_str()));
500 }
501}
502
503GuestDnD::~GuestDnD(void)
504{
505 LogFlowFuncEnter();
506
507 if (m_pResponse)
508 delete m_pResponse;
509}
510
511int GuestDnD::adjustScreenCoordinates(ULONG uScreenId, ULONG *puX, ULONG *puY) const
512{
513 /** @todo r=andy Save the current screen's shifting coordinates to speed things up.
514 * Only query for new offsets when the screen ID has changed. */
515
516 /* For multi-monitor support we need to add shift values to the coordinates
517 * (depending on the screen number). */
518 ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
519 ComPtr<IDisplay> pDisplay;
520 HRESULT hr = pConsole->COMGETTER(Display)(pDisplay.asOutParam());
521 if (FAILED(hr))
522 return hr;
523
524 ULONG dummy;
525 LONG xShift, yShift;
526 GuestMonitorStatus_T monitorStatus;
527 hr = pDisplay->GetScreenResolution(uScreenId, &dummy, &dummy, &dummy,
528 &xShift, &yShift, &monitorStatus);
529 if (FAILED(hr))
530 return hr;
531
532 if (puX)
533 *puX += xShift;
534 if (puY)
535 *puY += yShift;
536
537#ifdef DEBUG_andy
538 LogFlowFunc(("uScreenId=%RU32, x=%RU32, y=%RU32\n",
539 uScreenId, puX ? *puX : 0, puY ? *puY : 0));
540#endif
541 return VINF_SUCCESS;
542}
543
544int GuestDnD::hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const
545{
546 Assert(!m_pGuest.isNull());
547 ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
548
549 /* Forward the information to the VMM device. */
550 Assert(!pConsole.isNull());
551 VMMDev *pVMMDev = pConsole->i_getVMMDev();
552 if (!pVMMDev)
553 return VERR_COM_OBJECT_NOT_FOUND;
554
555 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", u32Function, cParms, paParms);
556 LogFlowFunc(("uMsg=%RU32, cParms=%RU32, rc=%Rrc\n", u32Function, cParms, rc));
557 return rc;
558}
559
560/* static */
561DECLCALLBACK(int) GuestDnD::notifyDnDDispatcher(void *pvExtension, uint32_t u32Function,
562 void *pvParms, uint32_t cbParms)
563{
564 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
565 pvExtension, u32Function, pvParms, cbParms));
566
567 GuestDnD *pGuestDnD = reinterpret_cast<GuestDnD*>(pvExtension);
568 AssertPtrReturn(pGuestDnD, VERR_INVALID_POINTER);
569
570 /** @todo In case we need to handle multiple guest DnD responses at a time this
571 * would be the place to lookup and dispatch to those. For the moment we
572 * only have one response -- simple. */
573 GuestDnDResponse *pResp = pGuestDnD->m_pResponse;
574 if (pResp)
575 return pResp->onDispatch(u32Function, pvParms, cbParms);
576
577 return VERR_NOT_SUPPORTED;
578}
579
580
581/* static */
582com::Utf8Str GuestDnD::toFormatString(const std::vector<com::Utf8Str> &lstSupportedFormats,
583 const std::vector<com::Utf8Str> &lstWantedFormats)
584{
585 com::Utf8Str strFormat;
586 for (size_t i = 0; i < lstWantedFormats.size(); ++i)
587 {
588 const com::Utf8Str &f = lstWantedFormats.at(i);
589 /* Only keep allowed format types. */
590 if (std::find(lstSupportedFormats.begin(),
591 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
592 strFormat += f + "\r\n";
593 }
594
595 return strFormat;
596}
597
598/* static */
599void GuestDnD::toFormatVector(const std::vector<com::Utf8Str> &lstSupportedFormats,
600 const com::Utf8Str &strFormats,
601 std::vector<com::Utf8Str> &vecformats)
602{
603 RTCList<RTCString> lstFormats = strFormats.split("\r\n");
604 size_t i = 0;
605 while (i < lstFormats.size())
606 {
607 /* Only keep allowed format types. */
608 if (std::find(lstSupportedFormats.begin(),
609 lstSupportedFormats.end(), lstFormats.at(i)) == lstSupportedFormats.end())
610 lstFormats.removeAt(i);
611 else
612 ++i;
613 }
614
615 for (i = 0; i < lstFormats.size(); i++)
616 {
617 const Utf8Str &f = lstFormats.at(i);
618 if (std::find(lstSupportedFormats.begin(),
619 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
620 vecformats.push_back(lstFormats[i]);
621 }
622}
623
624/* static */
625uint32_t GuestDnD::toHGCMAction(DnDAction_T enmAction)
626{
627 uint32_t uAction = DND_IGNORE_ACTION;
628 switch (enmAction)
629 {
630 case DnDAction_Copy:
631 uAction = DND_COPY_ACTION;
632 break;
633 case DnDAction_Move:
634 uAction = DND_MOVE_ACTION;
635 break;
636 case DnDAction_Link:
637 /* For now it doesn't seems useful to allow a link
638 action between host & guest. Later? */
639 case DnDAction_Ignore:
640 /* Ignored. */
641 break;
642 default:
643 AssertMsgFailed(("Action %RU32 not recognized!\n", enmAction));
644 break;
645 }
646
647 return uAction;
648}
649
650/* static */
651void GuestDnD::toHGCMActions(DnDAction_T enmDefAction,
652 uint32_t *puDefAction,
653 const std::vector<DnDAction_T> vecAllowedActions,
654 uint32_t *puAllowedActions)
655{
656 uint32_t uAllowedActions = DND_IGNORE_ACTION;
657 uint32_t uDefAction = toHGCMAction(enmDefAction);
658
659 if (!vecAllowedActions.empty())
660 {
661 /* First convert the allowed actions to a bit array. */
662 for (size_t i = 0; i < vecAllowedActions.size(); i++)
663 uAllowedActions |= toHGCMAction(vecAllowedActions[i]);
664
665 /*
666 * If no default action is set (ignoring), try one of the
667 * set allowed actions, preferring copy, move (in that order).
668 */
669 if (isDnDIgnoreAction(uDefAction))
670 {
671 if (hasDnDCopyAction(uAllowedActions))
672 uDefAction = DND_COPY_ACTION;
673 else if (hasDnDMoveAction(uAllowedActions))
674 uDefAction = DND_MOVE_ACTION;
675 }
676 }
677
678 if (puDefAction)
679 *puDefAction = uDefAction;
680 if (puAllowedActions)
681 *puAllowedActions = uAllowedActions;
682}
683
684/* static */
685DnDAction_T GuestDnD::toMainAction(uint32_t uAction)
686{
687 /* For now it doesn't seems useful to allow a
688 * link action between host & guest. Maybe later! */
689 return (isDnDCopyAction(uAction) ? (DnDAction_T)DnDAction_Copy :
690 isDnDMoveAction(uAction) ? (DnDAction_T)DnDAction_Move :
691 (DnDAction_T)DnDAction_Ignore);
692}
693
694/* static */
695void GuestDnD::toMainActions(uint32_t uActions,
696 std::vector<DnDAction_T> &vecActions)
697{
698 /* For now it doesn't seems useful to allow a
699 * link action between host & guest. Maybe later! */
700 RTCList<DnDAction_T> lstActions;
701 if (hasDnDCopyAction(uActions))
702 lstActions.append(DnDAction_Copy);
703 if (hasDnDMoveAction(uActions))
704 lstActions.append(DnDAction_Move);
705
706 for (size_t i = 0; i < lstActions.size(); ++i)
707 vecActions.push_back(lstActions.at(i));
708}
709
710///////////////////////////////////////////////////////////////////////////////
711
712GuestDnDBase::GuestDnDBase(void)
713{
714 mDataBase.mfTransferIsPending = false;
715
716 /*
717 * Initialize public attributes.
718 */
719 m_vecFmtSup = GuestDnDInst()->defaultFormats();
720}
721
722HRESULT GuestDnDBase::i_isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
723{
724 *aSupported = std::find(m_vecFmtSup.begin(),
725 m_vecFmtSup.end(), aFormat) != m_vecFmtSup.end()
726 ? TRUE : FALSE;
727 return S_OK;
728}
729
730HRESULT GuestDnDBase::i_getFormats(std::vector<com::Utf8Str> &aFormats)
731{
732 aFormats = m_vecFmtSup;
733
734 return S_OK;
735}
736
737HRESULT GuestDnDBase::i_addFormats(const std::vector<com::Utf8Str> &aFormats)
738{
739 for (size_t i = 0; i < aFormats.size(); ++i)
740 {
741 Utf8Str strFormat = aFormats.at(i);
742 if (std::find(m_vecFmtSup.begin(),
743 m_vecFmtSup.end(), strFormat) == m_vecFmtSup.end())
744 {
745 m_vecFmtSup.push_back(strFormat);
746 }
747 }
748
749 return S_OK;
750}
751
752HRESULT GuestDnDBase::i_removeFormats(const std::vector<com::Utf8Str> &aFormats)
753{
754 for (size_t i = 0; i < aFormats.size(); ++i)
755 {
756 Utf8Str strFormat = aFormats.at(i);
757 std::vector<com::Utf8Str>::iterator itFormat = std::find(m_vecFmtSup.begin(),
758 m_vecFmtSup.end(), strFormat);
759 if (itFormat != m_vecFmtSup.end())
760 m_vecFmtSup.erase(itFormat);
761 }
762
763 return S_OK;
764}
765
766HRESULT GuestDnDBase::i_getProtocolVersion(ULONG *puVersion)
767{
768 int rc = getProtocolVersion((uint32_t *)puVersion);
769 return RT_SUCCESS(rc) ? S_OK : E_FAIL;
770}
771
772int GuestDnDBase::getProtocolVersion(uint32_t *puVersion)
773{
774 AssertPtrReturn(puVersion, VERR_INVALID_POINTER);
775
776 int rc;
777
778 uint32_t uVer, uVerAdditions = 0;
779 if ( m_pGuest
780 && (uVerAdditions = m_pGuest->i_getAdditionsVersion()) > 0)
781 {
782 uint32_t uVBoxMajor = VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions);
783 uint32_t uVBoxMinor = VBOX_FULL_VERSION_GET_MINOR(uVerAdditions);
784
785#if 0 /*def DEBUG_andy*/
786 /* Hardcode the to-used protocol version; nice for testing side effects. */
787 uVer = 2;
788#else
789 uVer = ( uVBoxMajor >= 5)
790 ? 2 /* VBox 5.0 and up: Protocol version 2. */
791 : 1; /* VBox <= 4.3: Protocol version 1. */
792 /* Build revision is ignored. */
793#endif
794
795 LogFlowThisFunc(("uVerAdditions=%RU32 (%RU32.%RU32)\n", uVerAdditions, uVBoxMajor, uVBoxMinor));
796 rc = VINF_SUCCESS;
797 }
798 else
799 {
800 uVer = 1; /* Fallback. */
801 rc = VERR_NOT_FOUND;
802 }
803
804 LogFlowThisFunc(("uVer=%RU32, uVerAdditions=%RU32, rc=%Rrc\n", uVer, uVerAdditions, rc));
805
806 *puVersion = uVer;
807 return rc;
808}
809
810int GuestDnDBase::msgQueueAdd(GuestDnDMsg *pMsg)
811{
812 mDataBase.mListOutgoing.push_back(pMsg);
813 return VINF_SUCCESS;
814}
815
816GuestDnDMsg *GuestDnDBase::msgQueueGetNext(void)
817{
818 if (mDataBase.mListOutgoing.empty())
819 return NULL;
820 return mDataBase.mListOutgoing.front();
821}
822
823void GuestDnDBase::msgQueueRemoveNext(void)
824{
825 if (!mDataBase.mListOutgoing.empty())
826 {
827 GuestDnDMsg *pMsg = mDataBase.mListOutgoing.front();
828 if (pMsg)
829 delete pMsg;
830 mDataBase.mListOutgoing.pop_front();
831 }
832}
833
834void GuestDnDBase::msgQueueClear(void)
835{
836 GuestDnDMsgList::iterator itMsg = mDataBase.mListOutgoing.begin();
837 while (itMsg != mDataBase.mListOutgoing.end())
838 {
839 delete *itMsg;
840 }
841
842 mDataBase.mListOutgoing.clear();
843}
844
845int GuestDnDBase::sendCancel(void)
846{
847 int rc;
848 try
849 {
850 GuestDnDMsg *pMsgCancel = new GuestDnDMsg();
851 pMsgCancel->setType(DragAndDropSvc::HOST_DND_HG_EVT_CANCEL);
852
853 rc = msgQueueAdd(pMsgCancel);
854 }
855 catch(std::bad_alloc & /*e*/)
856 {
857 rc = VERR_NO_MEMORY;
858 }
859
860 LogFlowFunc(("Generated cancelling request, rc=%Rrc\n", rc));
861 return rc;
862}
863
864/** @todo GuestDnDResponse *pResp needs to go. */
865int GuestDnDBase::waitForEvent(RTMSINTERVAL msTimeout, GuestDnDCallbackEvent &Event, GuestDnDResponse *pResp)
866{
867 int rc;
868
869 uint64_t tsStart = RTTimeMilliTS();
870 do
871 {
872 /*
873 * Wait until our desired callback triggered the
874 * wait event. As we don't want to block if the guest does not
875 * respond, do busy waiting here.
876 */
877 rc = Event.Wait(500 /* ms */);
878 if (RT_SUCCESS(rc))
879 {
880 rc = Event.Result();
881 LogFlowFunc(("Callback done, result is %Rrc\n", rc));
882 break;
883 }
884 else if (rc == VERR_TIMEOUT) /* Continue waiting. */
885 rc = VINF_SUCCESS;
886
887 if ( msTimeout != RT_INDEFINITE_WAIT
888 && RTTimeMilliTS() - tsStart > msTimeout)
889 {
890 rc = VERR_TIMEOUT;
891 LogRel2(("DnD: Error: Guest did not respond within time\n"));
892 }
893 else if (pResp->isProgressCanceled()) /** @todo GuestDnDResponse *pResp needs to go. */
894 {
895 LogRel2(("DnD: Operation was canceled by user\n"));
896 rc = VERR_CANCELLED;
897 }
898
899 } while (RT_SUCCESS(rc));
900
901 LogFlowFuncLeaveRC(rc);
902 return rc;
903}
904#endif /* VBOX_WITH_DRAG_AND_DROP */
905
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