VirtualBox

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

Last change on this file since 55437 was 55424, checked in by vboxsync, 10 years ago

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.0 KB
Line 
1/* $Id: GuestDnDPrivate.cpp 55424 2015-04-24 14:19:53Z 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
162GuestDnDResponse::GuestDnDResponse(const ComObjPtr<Guest>& pGuest)
163 : m_EventSem(NIL_RTSEMEVENT)
164 , m_defAction(0)
165 , m_allActions(0)
166 , m_pvData(0)
167 , m_cbData(0)
168 , m_cbDataCurrent(0)
169 , m_cbDataTotal(0)
170 , m_parent(pGuest)
171{
172 int rc = RTSemEventCreate(&m_EventSem);
173 if (RT_FAILURE(rc))
174 throw rc;
175}
176
177GuestDnDResponse::~GuestDnDResponse(void)
178{
179 reset();
180
181 int rc = RTSemEventDestroy(m_EventSem);
182 AssertRC(rc);
183}
184
185int GuestDnDResponse::dataAdd(const void *pvData, uint32_t cbData,
186 uint32_t *pcbCurSize)
187{
188 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
189 AssertReturn(cbData, VERR_INVALID_PARAMETER);
190 /* pcbCurSize is optional. */
191
192 int rc = VINF_SUCCESS;
193
194 /** @todo Make reallocation scheme a bit smarter here. */
195 m_pvData = RTMemRealloc(m_pvData, m_cbData + cbData);
196 if (m_pvData)
197 {
198 memcpy(&static_cast<uint8_t*>(m_pvData)[m_cbData],
199 pvData, cbData);
200 m_cbData += cbData;
201
202 if (pcbCurSize)
203 *pcbCurSize = m_cbData;
204 }
205 else
206 rc = VERR_NO_MEMORY;
207
208 return 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_strDropDir = "";
258 m_strFormat = "";
259
260 if (m_pvData)
261 {
262 RTMemFree(m_pvData);
263 m_pvData = NULL;
264 }
265
266 m_cbData = 0;
267 m_cbDataCurrent = 0;
268 m_cbDataTotal = 0;
269}
270
271HRESULT GuestDnDResponse::resetProgress(const ComObjPtr<Guest>& pParent)
272{
273 m_progress.setNull();
274 HRESULT rc = m_progress.createObject();
275 if (SUCCEEDED(rc))
276 {
277 rc = m_progress->init(static_cast<IGuest *>(pParent),
278 Bstr(pParent->tr("Dropping data")).raw(),
279 TRUE /* aCancelable */);
280 }
281 return rc;
282}
283
284bool GuestDnDResponse::isProgressCanceled(void) const
285{
286 BOOL fCanceled;
287 if (!m_progress.isNull())
288 {
289 HRESULT hr = m_progress->COMGETTER(Canceled)(&fCanceled);
290 AssertComRC(hr);
291 }
292 else fCanceled = TRUE;
293
294 return RT_BOOL(fCanceled);
295}
296
297int GuestDnDResponse::setCallback(uint32_t uMsg, PFNGUESTDNDCALLBACK pfnCallback, void *pvUser /* = NULL */)
298{
299 GuestDnDCallbackMap::iterator it = m_mapCallbacks.find(uMsg);
300
301 /* Add. */
302 if (pfnCallback)
303 {
304 if (it == m_mapCallbacks.end())
305 {
306 m_mapCallbacks[uMsg] = GuestDnDCallback(pfnCallback, uMsg, pvUser);
307 return VINF_SUCCESS;
308 }
309
310 AssertMsgFailed(("Callback for message %RU32 already registered\n", uMsg));
311 return VERR_ALREADY_EXISTS;
312 }
313
314 /* Remove. */
315 if (it != m_mapCallbacks.end())
316 m_mapCallbacks.erase(it);
317
318 return VINF_SUCCESS;
319}
320
321int GuestDnDResponse::setProgress(unsigned uPercentage,
322 uint32_t uStatus, int rcOp /* = VINF_SUCCESS */)
323{
324 LogFlowFunc(("uStatus=%RU32, uPercentage=%RU32, rcOp=%Rrc\n",
325 uStatus, uPercentage, rcOp));
326
327 int vrc = VINF_SUCCESS;
328 if (!m_progress.isNull())
329 {
330 BOOL fCompleted;
331 HRESULT hr = m_progress->COMGETTER(Completed)(&fCompleted);
332 AssertComRC(hr);
333
334 BOOL fCanceled;
335 hr = m_progress->COMGETTER(Canceled)(&fCanceled);
336 AssertComRC(hr);
337
338 LogFlowFunc(("fCompleted=%RTbool, fCanceled=%RTbool\n", fCompleted, fCanceled));
339
340 if (!fCompleted)
341 {
342 switch (uStatus)
343 {
344 case DragAndDropSvc::DND_PROGRESS_ERROR:
345 {
346 hr = m_progress->i_notifyComplete(VBOX_E_IPRT_ERROR,
347 COM_IIDOF(IGuest),
348 m_parent->getComponentName(),
349 GuestDnDResponse::errorToString(m_parent, rcOp).c_str());
350 reset();
351 break;
352 }
353
354 case DragAndDropSvc::DND_PROGRESS_CANCELLED:
355 {
356 hr = m_progress->i_notifyComplete(S_OK);
357 AssertComRC(hr);
358
359 reset();
360 break;
361 }
362
363 case DragAndDropSvc::DND_PROGRESS_RUNNING:
364 case DragAndDropSvc::DND_PROGRESS_COMPLETE:
365 {
366 if (!fCanceled)
367 {
368 hr = m_progress->SetCurrentOperationProgress(uPercentage);
369 AssertComRC(hr);
370 if ( uStatus == DragAndDropSvc::DND_PROGRESS_COMPLETE
371 || uPercentage >= 100)
372 {
373 hr = m_progress->i_notifyComplete(S_OK);
374 AssertComRC(hr);
375 }
376 }
377 break;
378 }
379
380 default:
381 break;
382 }
383 }
384 }
385
386 LogFlowFuncLeaveRC(vrc);
387 return vrc;
388}
389
390int GuestDnDResponse::dataSetStatus(size_t cbDataAdd, size_t cbDataTotal /* = 0 */)
391{
392 if (cbDataTotal)
393 {
394#ifndef DEBUG_andy
395 AssertMsg(m_cbDataTotal <= cbDataTotal, ("New data size must not be smaller (%zu) than old value (%zu)\n",
396 cbDataTotal, m_cbDataTotal));
397#endif
398 LogFlowFunc(("Updating total data size from %zu to %zu\n", m_cbDataTotal, cbDataTotal));
399 m_cbDataTotal = cbDataTotal;
400 }
401 AssertMsg(m_cbDataTotal, ("m_cbDataTotal must not be <= 0\n"));
402
403 m_cbDataCurrent += cbDataAdd;
404 unsigned int cPercentage = RT_MIN(m_cbDataCurrent * 100.0 / m_cbDataTotal, 100);
405
406 /** @todo Don't use anonymous enums (uint32_t). */
407 uint32_t uStatus = DragAndDropSvc::DND_PROGRESS_RUNNING;
408 Assert(m_cbDataCurrent <= m_cbDataTotal);
409 if (m_cbDataCurrent >= m_cbDataTotal) uStatus = DragAndDropSvc::DND_PROGRESS_COMPLETE;
410
411 LogFlowFunc(("Updating transfer status (%zu/%zu), status=%ld\n", m_cbDataCurrent, m_cbDataTotal, uStatus));
412 AssertMsg(m_cbDataCurrent <= m_cbDataTotal,
413 ("More data transferred (%zu) than initially announced (%zu), cbDataAdd=%zu\n",
414 m_cbDataCurrent, m_cbDataTotal, cbDataAdd));
415
416 int rc = setProgress(cPercentage, uStatus);
417
418 /** @todo For now we instantly confirm the cancel. Check if the
419 * guest should first clean up stuff itself and than really confirm
420 * the cancel request by an extra message. */
421 if (rc == VERR_CANCELLED) rc = setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED);
422
423 LogFlowFuncLeaveRC(rc);
424 return rc;
425}
426
427int GuestDnDResponse::onDispatch(uint32_t u32Function, void *pvParms, uint32_t cbParms)
428{
429 LogFlowFunc(("u32Function=%RU32, pvParms=%p, cbParms=%RU32\n", u32Function, pvParms, cbParms));
430
431 int rc = VERR_WRONG_ORDER; /* Play safe. */
432 bool fTryCallbacks = false;
433
434 switch (u32Function)
435 {
436 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
437 {
438 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
439 AssertPtr(pCBData);
440 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
441 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
442
443 setDefAction(pCBData->uAction);
444 rc = notifyAboutGuestResponse();
445 break;
446 }
447
448 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
449 {
450 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
451 AssertPtr(pCBData);
452 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
453 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
454
455 setFormat(pCBData->pszFormat);
456 rc = notifyAboutGuestResponse();
457 break;
458 }
459
460 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
461 {
462 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData =
463 reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
464 AssertPtr(pCBData);
465 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
466 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
467
468 rc = setProgress(pCBData->uPercentage, pCBData->uStatus, pCBData->rc);
469 if (RT_SUCCESS(rc))
470 rc = notifyAboutGuestResponse();
471 break;
472 }
473#ifdef VBOX_WITH_DRAG_AND_DROP_GH
474 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
475 {
476 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData =
477 reinterpret_cast<DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
478 AssertPtr(pCBData);
479 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
480 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
481
482 setFormat (pCBData->pszFormat);
483 setDefAction (pCBData->uDefAction);
484 setAllActions(pCBData->uAllActions);
485
486 rc = notifyAboutGuestResponse();
487 break;
488 }
489#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
490 default:
491 /* * Try if the event is covered by a registered callback. */
492 fTryCallbacks = true;
493 break;
494 }
495
496 /*
497 * Try the host's installed callbacks (if any).
498 */
499 if (fTryCallbacks)
500 {
501 GuestDnDCallbackMap::const_iterator it = m_mapCallbacks.find(u32Function);
502 if (it != m_mapCallbacks.end())
503 {
504 AssertPtr(it->second.pfnCallback);
505 rc = it->second.pfnCallback(u32Function, pvParms, cbParms, it->second.pvUser);
506 }
507 else
508 rc = VERR_NO_DATA; /* Tell the guest. */
509 }
510
511 LogFlowFunc(("Returning rc=%Rrc\n", rc));
512 return rc;
513}
514
515HRESULT GuestDnDResponse::queryProgressTo(IProgress **ppProgress)
516{
517 return m_progress.queryInterfaceTo(ppProgress);
518}
519
520int GuestDnDResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */) const
521{
522 int rc = RTSemEventWait(m_EventSem, msTimeout);
523#ifdef DEBUG_andy
524 LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
525#endif
526 return rc;
527}
528
529///////////////////////////////////////////////////////////////////////////////
530
531GuestDnD* GuestDnD::s_pInstance = NULL;
532
533GuestDnD::GuestDnD(const ComObjPtr<Guest> &pGuest)
534 : m_pGuest(pGuest)
535{
536 LogFlowFuncEnter();
537
538 m_pResponse = new GuestDnDResponse(pGuest);
539
540 /* List of supported default MIME types. */
541 const com::Utf8Str arrEntries[] = { VBOX_DND_FORMATS_DEFAULT };
542 for (size_t i = 0; i < RT_ELEMENTS(arrEntries); i++)
543 m_strDefaultFormats.push_back(arrEntries[i]);
544}
545
546GuestDnD::~GuestDnD(void)
547{
548 LogFlowFuncEnter();
549
550 if (m_pResponse)
551 delete m_pResponse;
552}
553
554int GuestDnD::adjustScreenCoordinates(ULONG uScreenId, ULONG *puX, ULONG *puY) const
555{
556 /** @todo r=andy Save the current screen's shifting coordinates to speed things up.
557 * Only query for new offsets when the screen ID has changed. */
558
559 /* For multi-monitor support we need to add shift values to the coordinates
560 * (depending on the screen number). */
561 ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
562 ComPtr<IDisplay> pDisplay;
563 HRESULT hr = pConsole->COMGETTER(Display)(pDisplay.asOutParam());
564 if (FAILED(hr))
565 return hr;
566
567 ULONG dummy;
568 LONG xShift, yShift;
569 GuestMonitorStatus_T monitorStatus;
570 hr = pDisplay->GetScreenResolution(uScreenId, &dummy, &dummy, &dummy,
571 &xShift, &yShift, &monitorStatus);
572 if (FAILED(hr))
573 return hr;
574
575 if (puX)
576 *puX += xShift;
577 if (puY)
578 *puY += yShift;
579
580#ifdef DEBUG_andy
581 LogFlowFunc(("uScreenId=%RU32, x=%RU32, y=%RU32\n",
582 uScreenId, puX ? *puX : 0, puY ? *puY : 0));
583#endif
584 return VINF_SUCCESS;
585}
586
587int GuestDnD::hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const
588{
589 Assert(!m_pGuest.isNull());
590 ComObjPtr<Console> pConsole = m_pGuest->i_getConsole();
591
592 /* Forward the information to the VMM device. */
593 Assert(!pConsole.isNull());
594 VMMDev *pVMMDev = pConsole->i_getVMMDev();
595 if (!pVMMDev)
596 return VERR_COM_OBJECT_NOT_FOUND;
597
598 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc", u32Function, cParms, paParms);
599 LogFlowFunc(("uMsg=%RU32, cParms=%RU32, rc=%Rrc\n", u32Function, cParms, rc));
600 return rc;
601}
602
603/* static */
604DECLCALLBACK(int) GuestDnD::notifyDnDDispatcher(void *pvExtension, uint32_t u32Function,
605 void *pvParms, uint32_t cbParms)
606{
607 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
608 pvExtension, u32Function, pvParms, cbParms));
609
610 GuestDnD *pGuestDnD = reinterpret_cast<GuestDnD*>(pvExtension);
611 AssertPtrReturn(pGuestDnD, VERR_INVALID_POINTER);
612
613 /** @todo In case we need to handle multiple guest DnD responses at a time this
614 * would be the place to lookup and dispatch to those. For the moment we
615 * only have one response -- simple. */
616 GuestDnDResponse *pResp = pGuestDnD->m_pResponse;
617 if (pResp)
618 return pResp->onDispatch(u32Function, pvParms, cbParms);
619
620 return VERR_NOT_SUPPORTED;
621}
622
623
624/* static */
625com::Utf8Str GuestDnD::toFormatString(const std::vector<com::Utf8Str> &lstSupportedFormats,
626 const std::vector<com::Utf8Str> &lstFormats)
627{
628 com::Utf8Str strFormat;
629 for (size_t i = 0; i < lstFormats.size(); ++i)
630 {
631 const com::Utf8Str &f = lstFormats.at(i);
632 /* Only keep allowed format types. */
633 if (std::find(lstSupportedFormats.begin(),
634 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
635 strFormat += f + "\r\n";
636 }
637
638 return strFormat;
639}
640
641/* static */
642void GuestDnD::toFormatVector(const std::vector<com::Utf8Str> &lstSupportedFormats,
643 const com::Utf8Str &strFormats,
644 std::vector<com::Utf8Str> &vecformats)
645{
646 RTCList<RTCString> lstFormats = strFormats.split("\r\n");
647 size_t i = 0;
648 while (i < lstFormats.size())
649 {
650 /* Only keep allowed format types. */
651 if (std::find(lstSupportedFormats.begin(),
652 lstSupportedFormats.end(), lstFormats.at(i)) == lstSupportedFormats.end())
653 lstFormats.removeAt(i);
654 else
655 ++i;
656 }
657
658 for (i = 0; i < lstFormats.size(); i++)
659 {
660 const Utf8Str &f = lstFormats.at(i);
661 if (std::find(lstSupportedFormats.begin(),
662 lstSupportedFormats.end(), f) != lstSupportedFormats.end())
663 vecformats.push_back(lstFormats[i]);
664 }
665}
666
667/* static */
668uint32_t GuestDnD::toHGCMAction(DnDAction_T enmAction)
669{
670 uint32_t uAction = DND_IGNORE_ACTION;
671 switch (enmAction)
672 {
673 case DnDAction_Copy:
674 uAction = DND_COPY_ACTION;
675 break;
676 case DnDAction_Move:
677 uAction = DND_MOVE_ACTION;
678 break;
679 case DnDAction_Link:
680 /* For now it doesn't seems useful to allow a link
681 action between host & guest. Later? */
682 case DnDAction_Ignore:
683 /* Ignored. */
684 break;
685 default:
686 AssertMsgFailed(("Action %RU32 not recognized!\n", enmAction));
687 break;
688 }
689
690 return uAction;
691}
692
693/* static */
694void GuestDnD::toHGCMActions(DnDAction_T enmDefAction,
695 uint32_t *puDefAction,
696 const std::vector<DnDAction_T> vecAllowedActions,
697 uint32_t *puAllowedActions)
698{
699 if (puDefAction)
700 *puDefAction = toHGCMAction(enmDefAction);
701 if (puAllowedActions)
702 {
703 *puAllowedActions = DND_IGNORE_ACTION;
704
705 /* First convert the allowed actions to a bit array. */
706 for (size_t i = 0; i < vecAllowedActions.size(); ++i)
707 *puAllowedActions |= toHGCMAction(vecAllowedActions[i]);
708
709 /* Second check if the default action is a valid action and if not so try
710 * to find an allowed action. */
711 if (isDnDIgnoreAction(*puAllowedActions))
712 {
713 if (hasDnDCopyAction(*puAllowedActions))
714 *puAllowedActions = DND_COPY_ACTION;
715 else if (hasDnDMoveAction(*puAllowedActions))
716 *puAllowedActions = DND_MOVE_ACTION;
717 }
718 }
719}
720
721/* static */
722DnDAction_T GuestDnD::toMainAction(uint32_t uAction)
723{
724 /* For now it doesn't seems useful to allow a
725 * link action between host & guest. Maybe later! */
726 return (isDnDCopyAction(uAction) ? (DnDAction_T)DnDAction_Copy :
727 isDnDMoveAction(uAction) ? (DnDAction_T)DnDAction_Move :
728 (DnDAction_T)DnDAction_Ignore);
729}
730
731/* static */
732void GuestDnD::toMainActions(uint32_t uActions,
733 std::vector<DnDAction_T> &vecActions)
734{
735 /* For now it doesn't seems useful to allow a
736 * link action between host & guest. Maybe later! */
737 RTCList<DnDAction_T> lstActions;
738 if (hasDnDCopyAction(uActions))
739 lstActions.append(DnDAction_Copy);
740 if (hasDnDMoveAction(uActions))
741 lstActions.append(DnDAction_Move);
742
743 for (size_t i = 0; i < lstActions.size(); ++i)
744 vecActions.push_back(lstActions.at(i));
745}
746
747///////////////////////////////////////////////////////////////////////////////
748
749GuestDnDBase::GuestDnDBase(void)
750{
751 m_strFormats = GuestDnDInst()->defaultFormats();
752}
753
754HRESULT GuestDnDBase::i_isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
755{
756 *aSupported = std::find(m_strFormats.begin(),
757 m_strFormats.end(), aFormat) != m_strFormats.end()
758 ? TRUE : FALSE;
759 return S_OK;
760}
761
762HRESULT GuestDnDBase::i_getFormats(std::vector<com::Utf8Str> &aFormats)
763{
764 aFormats = m_strFormats;
765
766 return S_OK;
767}
768
769HRESULT GuestDnDBase::i_addFormats(const std::vector<com::Utf8Str> &aFormats)
770{
771 for (size_t i = 0; i < aFormats.size(); ++i)
772 {
773 Utf8Str strFormat = aFormats.at(i);
774 if (std::find(m_strFormats.begin(),
775 m_strFormats.end(), strFormat) == m_strFormats.end())
776 {
777 m_strFormats.push_back(strFormat);
778 }
779 }
780
781 return S_OK;
782}
783
784HRESULT GuestDnDBase::i_removeFormats(const std::vector<com::Utf8Str> &aFormats)
785{
786 for (size_t i = 0; i < aFormats.size(); ++i)
787 {
788 Utf8Str strFormat = aFormats.at(i);
789 std::vector<com::Utf8Str>::iterator itFormat = std::find(m_strFormats.begin(),
790 m_strFormats.end(), strFormat);
791 if (itFormat != m_strFormats.end())
792 m_strFormats.erase(itFormat);
793 }
794
795 return S_OK;
796}
797
798HRESULT GuestDnDBase::i_getProtocolVersion(ULONG *puVersion)
799{
800 int rc = getProtocolVersion((uint32_t *)puVersion);
801 return RT_SUCCESS(rc) ? S_OK : E_FAIL;
802}
803
804int GuestDnDBase::getProtocolVersion(uint32_t *puVersion)
805{
806 AssertPtrReturn(puVersion, VERR_INVALID_POINTER);
807
808 int rc;
809
810 uint32_t uVer, uVerAdditions = 0;
811 if ( m_pGuest
812 && (uVerAdditions = m_pGuest->i_getAdditionsVersion()) > 0)
813 {
814 uint32_t uVBoxMajor = VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions);
815 uint32_t uVBoxMinor = VBOX_FULL_VERSION_GET_MINOR(uVerAdditions);
816
817#if 0 /*def DEBUG_andy*/
818 /* Hardcode the to-used protocol version; nice for testing side effects. */
819 uVer = 2;
820#else
821 uVer = ( uVBoxMajor >= 5)
822 ? 2 /* VBox 5.0 and up: Protocol version 2. */
823 : 1; /* VBox <= 4.3: Protocol version 1. */
824 /* Build revision is ignored. */
825#endif
826
827 LogFlowThisFunc(("uVerAdditions=%RU32 (%RU32.%RU32)\n", uVerAdditions, uVBoxMajor, uVBoxMinor));
828 rc = VINF_SUCCESS;
829 }
830 else
831 {
832 uVer = 1; /* Fallback. */
833 rc = VERR_NOT_FOUND;
834 }
835
836 LogFlowThisFunc(("uVer=%RU32, uVerAdditions=%RU32, rc=%Rrc\n", uVer, uVerAdditions, rc));
837
838 *puVersion = uVer;
839 return rc;
840}
841
842#if 0
843/**
844 * Returns back information (message type + parameter count) of the current message in
845 * the local outgoing message queue.
846 *
847 * @return IPRT status code.
848 * @param pThis
849 * @param puMsg
850 * @param pcParms
851 * @param paParms
852 */
853/* static */
854DECLCALLBACK(int) GuestDnDBase::i_getNextMsgCallback(GuestDnDBase *pThis, uint32_t *puMsg,
855 uint32_t *pcParms, PVBOXHGCMSVCPARM paParms)
856{
857 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
858 AssertPtrReturn(puMsg, VERR_INVALID_POINTER);
859 AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
860 AssertPtrReturn(paParms, VERR_INVALID_POINTER);
861
862 if (pThis->mData.m_lstOutgoing.empty())
863 return VERR_NO_DATA;
864
865 GuestDnDMsg *pMsg = pThis->mData.m_lstOutgoing.front();
866 AssertPtr(pMsg);
867
868 *puMsg = pMsg->uMsg;
869 *pcParms = pMsg->cParms;
870
871 return VINF_SUCCESS;
872}
873#endif
874#endif /* VBOX_WITH_DRAG_AND_DROP */
875
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