VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDnDImpl.cpp@ 50609

Last change on this file since 50609 was 50609, checked in by vboxsync, 11 years ago

DnD: A bit of error-to-text resolving.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.7 KB
Line 
1/* $Id: GuestDnDImpl.cpp 50609 2014-02-26 14:32:11Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest Drag and Drop parts
4 */
5
6/*
7 * Copyright (C) 2011-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "GuestImpl.h"
19#include "AutoCaller.h"
20
21#ifdef VBOX_WITH_DRAG_AND_DROP
22# include "ConsoleImpl.h"
23# include "ProgressImpl.h"
24# include "GuestDnDImpl.h"
25
26# include <iprt/dir.h>
27# include <iprt/path.h>
28# include <iprt/stream.h>
29# include <iprt/semaphore.h>
30# include <iprt/cpp/utils.h>
31
32# include <VMMDev.h>
33
34# include <VBox/com/list.h>
35# include <VBox/GuestHost/DragAndDrop.h>
36# include <VBox/HostServices/DragAndDropSvc.h>
37
38# ifdef LOG_GROUP
39 # undef LOG_GROUP
40# endif
41# define LOG_GROUP LOG_GROUP_GUEST_DND
42# include <VBox/log.h>
43
44/* How does this work:
45 *
46 * Drag and Drop is handled over the internal HGCM service for the host <->
47 * guest communication. Beside that we need to map the Drag and Drop protocols
48 * of the various OS's we support to our internal channels, this is also highly
49 * communicative in both directions. Unfortunately HGCM isn't really designed
50 * for that. Next we have to foul some of the components. This includes to
51 * trick X11 on the guest side, but also Qt needs to be tricked on the host
52 * side a little bit.
53 *
54 * The following components are involved:
55 *
56 * 1. GUI: Uses the Qt classes for Drag and Drop and mainly forward the content
57 * of it to the Main IGuest interface (see UIDnDHandler.cpp).
58 * 2. Main: Public interface for doing Drag and Drop. Also manage the IProgress
59 * interfaces for blocking the caller by showing a progress dialog (see
60 * this file).
61 * 3. HGCM service: Handle all messages from the host to the guest at once and
62 * encapsulate the internal communication details (see dndmanager.cpp and
63 * friends).
64 * 4. Guest additions: Split into the platform neutral part (see
65 * VBoxGuestR3LibDragAndDrop.cpp) and the guest OS specific parts.
66 * Receive/send message from/to the HGCM service and does all guest specific
67 * operations. Currently only X11 is supported (see draganddrop.cpp within
68 * VBoxClient).
69 *
70 * Host -> Guest:
71 * 1. There are DnD Enter, Move, Leave events which are send exactly like this
72 * to the guest. The info includes the pos, mimetypes and allowed actions.
73 * The guest has to respond with an action it would accept, so the GUI could
74 * change the cursor.
75 * 2. On drop, first a drop event is send. If this is accepted a drop data
76 * event follows. This blocks the GUI and shows some progress indicator.
77 *
78 * Guest -> Host:
79 * 1. The GUI is asking the guest if a DnD event is pending when the user moves
80 * the cursor out of the view window. If so, this returns the mimetypes and
81 * allowed actions.
82 * (2. On every mouse move this is asked again, to make sure the DnD event is
83 * still valid.)
84 * 3. On drop the host request the data from the guest. This blocks the GUI and
85 * shows some progress indicator.
86 *
87 * Some hints:
88 * m_sstrAllowedMimeTypes here in this file defines the allowed mime-types.
89 * This is necessary because we need special handling for some of the
90 * mime-types. E.g. for URI lists we need to transfer the actual dirs and
91 * files. Text EOL may to be changed. Also unknown mime-types may need special
92 * handling as well, which may lead to undefined behavior in the host/guest, if
93 * not done.
94 *
95 * Dropping of a directory, means recursively transferring _all_ the content.
96 *
97 * Directories and files are placed into the user's temporary directory on the
98 * guest (e.g. /tmp/VirtualBox Dropped Files). We can't delete them after the
99 * DnD operation, because we didn't know what the DnD target does with it. E.g.
100 * it could just be opened in place. This could lead ofc to filling up the disk
101 * within the guest. To inform the user about this, a small app could be
102 * developed which scans this directory regularly and inform the user with a
103 * tray icon hint (and maybe the possibility to clean this up instantly). The
104 * same has to be done in the G->H direction when it is implemented.
105 *
106 * Of course only regularly files are supported. Symlinks are resolved and
107 * transfered as regularly files. First we don't know if the other side support
108 * symlinks at all and second they could point to somewhere in a directory tree
109 * which not exists on the other side.
110 *
111 * The code tries to preserve the file modes of the transfered dirs/files. This
112 * is useful (and maybe necessary) for two things:
113 * 1. If a file is executable, it should be also after the transfer, so the
114 * user can just execute it, without manually tweaking the modes first.
115 * 2. If a dir/file is not accessible by group/others in the host, it shouldn't
116 * be in the guest.
117 * In any case, the user mode is always set to rwx (so that we can access it
118 * ourself, in e.g. for a cleanup case after cancel).
119 *
120 * Cancel is supported in both directions and cleans up all previous steps
121 * (thats is: deleting already transfered dirs/files).
122 *
123 * In general I propose the following changes in the VBox HGCM infrastructure
124 * for the future:
125 * - Currently it isn't really possible to send messages to the guest from the
126 * host. The host informs the guest just that there is something, the guest
127 * than has to ask which message and depending on that send the appropriate
128 * message to the host, which is filled with the right data.
129 * - There is no generic interface for sending bigger memory blocks to/from the
130 * guest. This is now done here, but I guess was also necessary for e.g.
131 * guest execution. So something generic which brake this up into smaller
132 * blocks and send it would be nice (with all the error handling and such
133 * ofc).
134 * - I developed a "protocol" for the DnD communication here. So the host and
135 * the guest have always to match in the revision. This is ofc bad, because
136 * the additions could be outdated easily. So some generic protocol number
137 * support in HGCM for asking the host and the guest of the support version,
138 * would be nice. Ofc at least the host should be able to talk to the guest,
139 * even when the version is below the host one.
140 * All this stuff would be useful for the current services, but also for future
141 * onces.
142 *
143 * Todo:
144 * - Dragging out of the guest (partly done)
145 * - ESC doesn't really work (on Windows guests it's already implemented)
146 * - transfer of URIs (that is the files and patching of the data)
147 * - testing in a multi monitor setup
148 * ... in any case it seems a little bit difficult to handle from the Qt
149 * side. Maybe also a host specific implementation becomes necessary ...
150 * this would be really worst ofc.
151 * - Win guest support (maybe there have to be done a mapping between the
152 * official mime-types and Win Clipboard formats (see QWindowsMime, for an
153 * idea), for VBox internally only mime-types should be used)
154 * - EOL handling on text (should be shared with the clipboard code)
155 * - add configuration (GH, HG, Bidirectional, None), like for the clipboard
156 * - HG->GH and GH->HG-switch: Handle the case the user drags something out of
157 * the guest and than return to the source view (or another window in the
158 * multiple guest screen scenario).
159 * - add support for more mime-types (especially images, csv)
160 * - test unusual behavior:
161 * - DnD service crash in the guest during a DnD op (e.g. crash of VBoxClient or X11)
162 * - not expected order of the events between HGCM and the guest
163 * - Security considerations: We transfer a lot of memory between the guest and
164 * the host and even allow the creation of dirs/files. Maybe there should be
165 * limits introduced to preventing DOS attacks or filling up all the memory
166 * (both in the host and the guest).
167 * - test, test, test ...
168 */
169
170class DnDGuestResponse
171{
172
173public:
174
175 DnDGuestResponse(const ComObjPtr<Guest>& pGuest);
176
177 virtual ~DnDGuestResponse(void);
178
179public:
180
181 int notifyAboutGuestResponse(void);
182 int waitForGuestResponse(RTMSINTERVAL msTimeout = 500);
183
184 void setDefAction(uint32_t a) { m_defAction = a; }
185 uint32_t defAction(void) const { return m_defAction; }
186
187 void setAllActions(uint32_t a) { m_allActions = a; }
188 uint32_t allActions() const { return m_allActions; }
189
190 void setFormat(const Utf8Str &strFormat) { m_strFormat = strFormat; }
191 Utf8Str format(void) const { return m_strFormat; }
192
193 void setDropDir(const Utf8Str &strDropDir) { m_strDropDir = strDropDir; }
194 Utf8Str dropDir(void) const { return m_strDropDir; }
195
196 int dataAdd(const void *pvData, uint32_t cbData, uint32_t *pcbCurSize);
197 int dataSetStatus(size_t cbDataAdd, size_t cbDataTotal = 0);
198 void reset(void);
199 const void *data(void) { return m_pvData; }
200 size_t size(void) const { return m_cbData; }
201
202 int setProgress(unsigned uPercentage, uint32_t uState, int rcOp = VINF_SUCCESS);
203 HRESULT resetProgress(const ComObjPtr<Guest>& pParent);
204 HRESULT queryProgressTo(IProgress **ppProgress);
205
206public:
207
208 Utf8Str errorToString(const ComObjPtr<Guest>& pGuest, int guestRc);
209
210private:
211 RTSEMEVENT m_EventSem;
212 uint32_t m_defAction;
213 uint32_t m_allActions;
214 Utf8Str m_strFormat;
215
216 /** The actual MIME data.*/
217 void *m_pvData;
218 /** Size (in bytes) of MIME data. */
219 uint32_t m_cbData;
220
221 size_t m_cbDataCurrent;
222 size_t m_cbDataTotal;
223 /** Dropped files directory on the host. */
224 Utf8Str m_strDropDir;
225
226 ComObjPtr<Guest> m_parent;
227 ComObjPtr<Progress> m_progress;
228};
229
230/** @todo This class needs a major cleanup. Later. */
231class GuestDnDPrivate
232{
233private:
234
235 /** @todo Currently we only support one response. Maybe this needs to be extended at some time. */
236 GuestDnDPrivate(GuestDnD *q, const ComObjPtr<Guest>& pGuest)
237 : q_ptr(q)
238 , p(pGuest)
239 , m_pDnDResponse(new DnDGuestResponse(pGuest))
240 {}
241 virtual ~GuestDnDPrivate(void) { delete m_pDnDResponse; }
242
243 DnDGuestResponse *response(void) const { return m_pDnDResponse; }
244
245 HRESULT adjustCoords(ULONG uScreenId, ULONG *puX, ULONG *puY) const;
246 int hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const;
247
248 /* Static helpers. */
249 static RTCString toFormatString(ComSafeArrayIn(IN_BSTR, formats));
250 static void toFormatSafeArray(const RTCString &strFormats, ComSafeArrayOut(BSTR, formats));
251
252 static DragAndDropAction_T toMainAction(uint32_t uAction);
253 static void toMainActions(uint32_t uActions, ComSafeArrayOut(DragAndDropAction_T, actions));
254 static uint32_t toHGCMAction(DragAndDropAction_T action);
255 static void toHGCMActions(DragAndDropAction_T inDefAction, uint32_t *pOutDefAction, ComSafeArrayIn(DragAndDropAction_T, inAllowedActions), uint32_t *pOutAllowedActions);
256
257 /* Private q and parent pointer */
258 GuestDnD *q_ptr;
259 ComObjPtr<Guest> p;
260
261 /* Private helper members. */
262 static const RTCList<RTCString> m_sstrAllowedMimeTypes;
263 DnDGuestResponse *m_pDnDResponse;
264
265 friend class GuestDnD;
266};
267
268/* What mime-types are supported by VirtualBox.
269 * Note: If you add something here, make sure you test it with all guest OS's!
270 ** @todo Make this MIME list configurable / extendable (by extra data?). Currently
271 * this is done hardcoded on every guest platform (POSIX/Windows).
272 */
273/* static */
274const RTCList<RTCString> GuestDnDPrivate::m_sstrAllowedMimeTypes = RTCList<RTCString>()
275 /* Uri's */
276 << "text/uri-list"
277 /* Text */
278 << "text/plain;charset=utf-8"
279 << "UTF8_STRING"
280 << "text/plain"
281 << "COMPOUND_TEXT"
282 << "TEXT"
283 << "STRING"
284 /* OpenOffice formates */
285 << "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""
286 << "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"";
287
288DnDGuestResponse::DnDGuestResponse(const ComObjPtr<Guest>& pGuest)
289 : m_EventSem(NIL_RTSEMEVENT)
290 , m_defAction(0)
291 , m_allActions(0)
292 , m_pvData(0)
293 , m_cbData(0)
294 , m_cbDataCurrent(0)
295 , m_cbDataTotal(0)
296 , m_parent(pGuest)
297{
298 int rc = RTSemEventCreate(&m_EventSem);
299 AssertRC(rc);
300}
301
302DnDGuestResponse::~DnDGuestResponse(void)
303{
304 reset();
305 int rc = RTSemEventDestroy(m_EventSem);
306 AssertRC(rc);
307}
308
309int DnDGuestResponse::dataAdd(const void *pvData, uint32_t cbData,
310 uint32_t *pcbCurSize)
311{
312 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
313 AssertReturn(cbData, VERR_INVALID_PARAMETER);
314 /* pcbCurSize is optional. */
315
316 int rc = VINF_SUCCESS;
317
318 /** @todo Make reallocation scheme a bit smarter here. */
319 m_pvData = RTMemRealloc(m_pvData, m_cbData + cbData);
320 if (m_pvData)
321 {
322 memcpy(&static_cast<uint8_t*>(m_pvData)[m_cbData],
323 pvData, cbData);
324 m_cbData += cbData;
325
326 if (pcbCurSize)
327 *pcbCurSize = m_cbData;
328 }
329 else
330 rc = VERR_NO_MEMORY;
331
332 return rc;
333}
334
335/* static */
336Utf8Str DnDGuestResponse::errorToString(const ComObjPtr<Guest>& pGuest, int guestRc)
337{
338 Utf8Str strError;
339
340 switch (guestRc)
341 {
342 case VERR_SHARING_VIOLATION:
343 strError += Utf8StrFmt(pGuest->tr("One or more guest files or directories selected for transferring to the host were locked. "
344 "Please make sure that all selected elements can be accessed and that your guest user has "
345 "the appropriate rights."));
346 break;
347
348 default:
349 strError += Utf8StrFmt("Drag'n drop guest error (%Rrc)", guestRc);
350 break;
351 }
352
353 return strError;
354}
355
356int DnDGuestResponse::notifyAboutGuestResponse(void)
357{
358 return RTSemEventSignal(m_EventSem);
359}
360
361void DnDGuestResponse::reset(void)
362{
363 if (m_pvData)
364 {
365 RTMemFree(m_pvData);
366 m_pvData = NULL;
367 }
368 m_cbData = 0;
369
370 m_cbDataCurrent = 0;
371 m_cbDataTotal = 0;
372}
373
374HRESULT DnDGuestResponse::resetProgress(const ComObjPtr<Guest>& pParent)
375{
376 m_progress.setNull();
377 HRESULT rc = m_progress.createObject();
378 if (SUCCEEDED(rc))
379 {
380 rc = m_progress->init(static_cast<IGuest*>(pParent),
381 Bstr(pParent->tr("Dropping data")).raw(),
382 TRUE);
383 }
384 return rc;
385}
386
387int DnDGuestResponse::setProgress(unsigned uPercentage,
388 uint32_t uState, int rcOp /* = VINF_SUCCESS */)
389{
390 LogFlowFunc(("uPercentage=%RU32, uState=%RU32, rcOp=%Rrc\n",
391 uPercentage, uState, rcOp));
392
393 int vrc = VINF_SUCCESS;
394 if (!m_progress.isNull())
395 {
396 BOOL fCompleted;
397 HRESULT hr = m_progress->COMGETTER(Completed)(&fCompleted);
398 if (!fCompleted)
399 {
400 if (uState == DragAndDropSvc::DND_PROGRESS_ERROR)
401 {
402 hr = m_progress->notifyComplete(VBOX_E_IPRT_ERROR,
403 COM_IIDOF(IGuest),
404 m_parent->getComponentName(),
405 DnDGuestResponse::errorToString(m_parent, rcOp).c_str());
406 reset();
407 }
408 else if (uState == DragAndDropSvc::DND_PROGRESS_CANCELLED)
409 {
410 hr = m_progress->Cancel();
411 if (SUCCEEDED(hr))
412 vrc = VERR_CANCELLED;
413
414 reset();
415 }
416 else /* uState == DragAndDropSvc::DND_PROGRESS_RUNNING */
417 {
418 hr = m_progress->SetCurrentOperationProgress(uPercentage);
419 AssertComRC(hr);
420 if ( uState == DragAndDropSvc::DND_PROGRESS_COMPLETE
421 || uPercentage >= 100)
422 hr = m_progress->notifyComplete(S_OK);
423 }
424 }
425 }
426
427 return vrc;
428}
429
430int DnDGuestResponse::dataSetStatus(size_t cbDataAdd, size_t cbDataTotal /* = 0 */)
431{
432 if (cbDataTotal)
433 {
434#ifndef DEBUG_andy
435 AssertMsg(m_cbDataTotal <= cbDataTotal, ("New data size must not be smaller (%zu) than old value (%zu)\n",
436 cbDataTotal, m_cbDataTotal));
437#endif
438 LogFlowFunc(("Updating total data size from %zu to %zu\n", m_cbDataTotal, cbDataTotal));
439 m_cbDataTotal = cbDataTotal;
440 }
441 AssertMsg(m_cbDataTotal, ("m_cbDataTotal must not be <= 0\n"));
442
443 m_cbDataCurrent += cbDataAdd;
444 unsigned int cPercentage = RT_MIN(m_cbDataCurrent * 100.0 / m_cbDataTotal, 100);
445
446 /** @todo Don't use anonymous enums (uint32_t). */
447 uint32_t uStatus = DragAndDropSvc::DND_PROGRESS_RUNNING;
448 if (m_cbDataCurrent >= m_cbDataTotal)
449 uStatus = DragAndDropSvc::DND_PROGRESS_COMPLETE;
450
451#ifdef DEBUG_andy
452 LogFlowFunc(("Updating transfer status (%zu/%zu), status=%ld\n",
453 m_cbDataCurrent, m_cbDataTotal, uStatus));
454#else
455 AssertMsg(m_cbDataCurrent <= m_cbDataTotal,
456 ("More data transferred (%zu) than initially announced (%zu), cbDataAdd=%zu\n",
457 m_cbDataCurrent, m_cbDataTotal, cbDataAdd));
458#endif
459 int rc = setProgress(cPercentage, uStatus);
460
461 /** @todo For now we instantly confirm the cancel. Check if the
462 * guest should first clean up stuff itself and than really confirm
463 * the cancel request by an extra message. */
464 if (rc == VERR_CANCELLED)
465 rc = setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED);
466
467 LogFlowFuncLeaveRC(rc);
468 return rc;
469}
470
471HRESULT DnDGuestResponse::queryProgressTo(IProgress **ppProgress)
472{
473 return m_progress.queryInterfaceTo(ppProgress);
474}
475
476int DnDGuestResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */)
477{
478 int rc = RTSemEventWait(m_EventSem, msTimeout);
479#ifdef DEBUG_andy
480 LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
481#endif
482 return rc;
483}
484
485///////////////////////////////////////////////////////////////////////////////
486
487HRESULT GuestDnDPrivate::adjustCoords(ULONG uScreenId, ULONG *puX, ULONG *puY) const
488{
489 /* For multi-monitor support we need to add shift values to the coordinates
490 * (depending on the screen number). */
491 ComPtr<IDisplay> pDisplay;
492 HRESULT hr = p->mParent->COMGETTER(Display)(pDisplay.asOutParam());
493 if (FAILED(hr))
494 return hr;
495
496 ComPtr<IFramebuffer> pFramebuffer;
497 LONG xShift, yShift;
498 hr = pDisplay->GetFramebuffer(uScreenId, pFramebuffer.asOutParam(),
499 &xShift, &yShift);
500 if (FAILED(hr))
501 return hr;
502
503 *puX += xShift;
504 *puY += yShift;
505
506 return hr;
507}
508
509int GuestDnDPrivate::hostCall(uint32_t u32Function, uint32_t cParms,
510 PVBOXHGCMSVCPARM paParms) const
511{
512 VMMDev *pVMMDev = NULL;
513 {
514 /* Make sure mParent is valid, so set the read lock while using.
515 * Do not keep this lock while doing the actual call, because in the meanwhile
516 * another thread could request a write lock which would be a bad idea ... */
517 AutoReadLock alock(p COMMA_LOCKVAL_SRC_POS);
518
519 /* Forward the information to the VMM device. */
520 AssertPtr(p->mParent);
521 pVMMDev = p->mParent->getVMMDev();
522 }
523
524 if (!pVMMDev)
525 throw p->setError(VBOX_E_VM_ERROR,
526 p->tr("VMM device is not available (is the VM running?)"));
527
528 LogFlowFunc(("hgcmHostCall uMsg=%RU32, cParms=%RU32\n", u32Function, cParms));
529 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc",
530 u32Function,
531 cParms, paParms);
532 if (RT_FAILURE(rc))
533 {
534 LogFlowFunc(("hgcmHostCall failed with rc=%Rrc\n", rc));
535 throw p->setError(VBOX_E_IPRT_ERROR,
536 p->tr("hgcmHostCall failed (%Rrc)"), rc);
537 }
538
539 return rc;
540}
541
542/* static */
543RTCString GuestDnDPrivate::toFormatString(ComSafeArrayIn(IN_BSTR, formats))
544{
545 const RTCList<Utf8Str> formatList(ComSafeArrayInArg(formats));
546 RTCString strFormat;
547 for (size_t i = 0; i < formatList.size(); ++i)
548 {
549 const RTCString &f = formatList.at(i);
550 /* Only keep allowed format types. */
551 if (m_sstrAllowedMimeTypes.contains(f))
552 strFormat += f + "\r\n";
553 }
554 return strFormat;
555}
556
557/* static */
558void GuestDnDPrivate::toFormatSafeArray(const RTCString &strFormats, ComSafeArrayOut(BSTR, formats))
559{
560 RTCList<RTCString> list = strFormats.split("\r\n");
561 size_t i = 0;
562 while (i < list.size())
563 {
564 /* Only keep allowed format types. */
565 if (!m_sstrAllowedMimeTypes.contains(list.at(i)))
566 list.removeAt(i);
567 else
568 ++i;
569 }
570 /* Create a safe array out of the cleaned list. */
571 com::SafeArray<BSTR> sfaFormats(list.size());
572 for (i = 0; i < list.size(); ++i)
573 {
574 const RTCString &f = list.at(i);
575 if (m_sstrAllowedMimeTypes.contains(f))
576 {
577 Bstr bstr(f);
578 bstr.detachTo(&sfaFormats[i]);
579 }
580 }
581 sfaFormats.detachTo(ComSafeArrayOutArg(formats));
582}
583
584/* static */
585uint32_t GuestDnDPrivate::toHGCMAction(DragAndDropAction_T action)
586{
587 uint32_t a = DND_IGNORE_ACTION;
588 switch (action)
589 {
590 case DragAndDropAction_Copy: a = DND_COPY_ACTION; break;
591 case DragAndDropAction_Move: a = DND_MOVE_ACTION; break;
592 case DragAndDropAction_Link: /* For now it doesn't seems useful to allow a link action between host & guest. Maybe later! */
593 case DragAndDropAction_Ignore: /* Ignored */ break;
594 default: AssertMsgFailed(("Action %u not recognized!\n", action)); break;
595 }
596 return a;
597}
598
599/* static */
600void GuestDnDPrivate::toHGCMActions(DragAndDropAction_T inDefAction,
601 uint32_t *pOutDefAction,
602 ComSafeArrayIn(DragAndDropAction_T, inAllowedActions),
603 uint32_t *pOutAllowedActions)
604{
605 const com::SafeArray<DragAndDropAction_T> sfaInActions(ComSafeArrayInArg(inAllowedActions));
606
607 /* Defaults */
608 *pOutDefAction = toHGCMAction(inDefAction);;
609 *pOutAllowedActions = DND_IGNORE_ACTION;
610
611 /* First convert the allowed actions to a bit array. */
612 for (size_t i = 0; i < sfaInActions.size(); ++i)
613 *pOutAllowedActions |= toHGCMAction(sfaInActions[i]);
614
615 /* Second check if the default action is a valid action and if not so try
616 * to find an allowed action. */
617 if (isDnDIgnoreAction(*pOutDefAction))
618 {
619 if (hasDnDCopyAction(*pOutAllowedActions))
620 *pOutDefAction = DND_COPY_ACTION;
621 else if (hasDnDMoveAction(*pOutAllowedActions))
622 *pOutDefAction = DND_MOVE_ACTION;
623 }
624}
625
626/* static */
627DragAndDropAction_T GuestDnDPrivate::toMainAction(uint32_t uAction)
628{
629 /* For now it doesn't seems useful to allow a
630 * link action between host & guest. Maybe later! */
631 return (isDnDCopyAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Copy :
632 isDnDMoveAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Move :
633 (DragAndDropAction_T)DragAndDropAction_Ignore);
634}
635
636/* static */
637void GuestDnDPrivate::toMainActions(uint32_t uActions,
638 ComSafeArrayOut(DragAndDropAction_T, actions))
639{
640 /* For now it doesn't seems useful to allow a
641 * link action between host & guest. Maybe later! */
642 RTCList<DragAndDropAction_T> list;
643 if (hasDnDCopyAction(uActions))
644 list.append(DragAndDropAction_Copy);
645 if (hasDnDMoveAction(uActions))
646 list.append(DragAndDropAction_Move);
647
648 com::SafeArray<DragAndDropAction_T> sfaActions(list.size());
649 for (size_t i = 0; i < list.size(); ++i)
650 sfaActions[i] = list.at(i);
651 sfaActions.detachTo(ComSafeArrayOutArg(actions));
652}
653
654GuestDnD::GuestDnD(const ComObjPtr<Guest>& pGuest)
655 : d_ptr(new GuestDnDPrivate(this, pGuest))
656{
657}
658
659GuestDnD::~GuestDnD(void)
660{
661 delete d_ptr;
662}
663
664HRESULT GuestDnD::dragHGEnter(ULONG uScreenId, ULONG uX, ULONG uY,
665 DragAndDropAction_T defaultAction,
666 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
667 ComSafeArrayIn(IN_BSTR, formats),
668 DragAndDropAction_T *pResultAction)
669{
670 DPTR(GuestDnD);
671 const ComObjPtr<Guest> &p = d->p;
672
673 /* Default is ignoring */
674 *pResultAction = DragAndDropAction_Ignore;
675
676 /* Check & convert the drag & drop actions */
677 uint32_t uDefAction = 0;
678 uint32_t uAllowedActions = 0;
679 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
680 /* If there is no usable action, ignore this request. */
681 if (isDnDIgnoreAction(uDefAction))
682 return S_OK;
683
684 /* Make a flat data string out of the mime-type list. */
685 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
686 /* If there is no valid mime-type, ignore this request. */
687 if (strFormats.isEmpty())
688 return S_OK;
689
690 HRESULT hr = S_OK;
691
692 try
693 {
694 /* Adjust the coordinates in a multi-monitor setup. */
695 d->adjustCoords(uScreenId, &uX, &uY);
696
697 VBOXHGCMSVCPARM paParms[7];
698 int i = 0;
699 paParms[i++].setUInt32(uScreenId);
700 paParms[i++].setUInt32(uX);
701 paParms[i++].setUInt32(uY);
702 paParms[i++].setUInt32(uDefAction);
703 paParms[i++].setUInt32(uAllowedActions);
704 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
705 paParms[i++].setUInt32(strFormats.length() + 1);
706
707 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_ENTER,
708 i,
709 paParms);
710
711 DnDGuestResponse *pResp = d->response();
712 /* This blocks until the request is answered (or timeout). */
713 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
714 return S_OK;
715
716 /* Copy the response info */
717 *pResultAction = d->toMainAction(pResp->defAction());
718 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
719 }
720 catch (HRESULT hr2)
721 {
722 hr = hr2;
723 }
724
725 return hr;
726}
727
728HRESULT GuestDnD::dragHGMove(ULONG uScreenId, ULONG uX, ULONG uY,
729 DragAndDropAction_T defaultAction,
730 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
731 ComSafeArrayIn(IN_BSTR, formats),
732 DragAndDropAction_T *pResultAction)
733{
734 DPTR(GuestDnD);
735 const ComObjPtr<Guest> &p = d->p;
736
737 /* Default is ignoring */
738 *pResultAction = DragAndDropAction_Ignore;
739
740 /* Check & convert the drag & drop actions */
741 uint32_t uDefAction = 0;
742 uint32_t uAllowedActions = 0;
743 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
744 /* If there is no usable action, ignore this request. */
745 if (isDnDIgnoreAction(uDefAction))
746 return S_OK;
747
748 /* Make a flat data string out of the mime-type list. */
749 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
750 /* If there is no valid mime-type, ignore this request. */
751 if (strFormats.isEmpty())
752 return S_OK;
753
754 HRESULT hr = S_OK;
755
756 try
757 {
758 /* Adjust the coordinates in a multi-monitor setup. */
759 d->adjustCoords(uScreenId, &uX, &uY);
760
761 VBOXHGCMSVCPARM paParms[7];
762 int i = 0;
763 paParms[i++].setUInt32(uScreenId);
764 paParms[i++].setUInt32(uX);
765 paParms[i++].setUInt32(uY);
766 paParms[i++].setUInt32(uDefAction);
767 paParms[i++].setUInt32(uAllowedActions);
768 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
769 paParms[i++].setUInt32(strFormats.length() + 1);
770
771 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_MOVE,
772 i,
773 paParms);
774
775 DnDGuestResponse *pResp = d->response();
776 /* This blocks until the request is answered (or timeout). */
777 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
778 return S_OK;
779
780 /* Copy the response info */
781 *pResultAction = d->toMainAction(pResp->defAction());
782 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
783 }
784 catch (HRESULT hr2)
785 {
786 hr = hr2;
787 }
788
789 return hr;
790}
791
792HRESULT GuestDnD::dragHGLeave(ULONG uScreenId)
793{
794 DPTR(GuestDnD);
795 const ComObjPtr<Guest> &p = d->p;
796
797 HRESULT hr = S_OK;
798
799 try
800 {
801 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_LEAVE,
802 0,
803 NULL);
804
805 DnDGuestResponse *pResp = d->response();
806 /* This blocks until the request is answered (or timeout). */
807 pResp->waitForGuestResponse();
808 }
809 catch (HRESULT hr2)
810 {
811 hr = hr2;
812 }
813
814 return hr;
815}
816
817HRESULT GuestDnD::dragHGDrop(ULONG uScreenId, ULONG uX, ULONG uY,
818 DragAndDropAction_T defaultAction,
819 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
820 ComSafeArrayIn(IN_BSTR, formats),
821 BSTR *pstrFormat,
822 DragAndDropAction_T *pResultAction)
823{
824 DPTR(GuestDnD);
825 const ComObjPtr<Guest> &p = d->p;
826
827 /* Default is ignoring */
828 *pResultAction = DragAndDropAction_Ignore;
829
830 /* Check & convert the drag & drop actions */
831 uint32_t uDefAction = 0;
832 uint32_t uAllowedActions = 0;
833 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
834 /* If there is no usable action, ignore this request. */
835 if (isDnDIgnoreAction(uDefAction))
836 return S_OK;
837
838 /* Make a flat data string out of the mime-type list. */
839 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
840 /* If there is no valid mime-type, ignore this request. */
841 if (strFormats.isEmpty())
842 return S_OK;
843
844 HRESULT hr = S_OK;
845
846 try
847 {
848 /* Adjust the coordinates in a multi-monitor setup. */
849 d->adjustCoords(uScreenId, &uX, &uY);
850
851 VBOXHGCMSVCPARM paParms[7];
852 int i = 0;
853 paParms[i++].setUInt32(uScreenId);
854 paParms[i++].setUInt32(uX);
855 paParms[i++].setUInt32(uY);
856 paParms[i++].setUInt32(uDefAction);
857 paParms[i++].setUInt32(uAllowedActions);
858 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
859 paParms[i++].setUInt32(strFormats.length() + 1);
860
861 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_DROPPED,
862 i,
863 paParms);
864
865 DnDGuestResponse *pResp = d->response();
866 /* This blocks until the request is answered (or timeout). */
867 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
868 return S_OK;
869
870 /* Copy the response info */
871 *pResultAction = d->toMainAction(pResp->defAction());
872 Bstr(pResp->format()).cloneTo(pstrFormat);
873
874 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
875 }
876 catch (HRESULT hr2)
877 {
878 hr = hr2;
879 }
880
881 return hr;
882}
883
884HRESULT GuestDnD::dragHGPutData(ULONG uScreenId, IN_BSTR bstrFormat,
885 ComSafeArrayIn(BYTE, data), IProgress **ppProgress)
886{
887 DPTR(GuestDnD);
888 const ComObjPtr<Guest> &p = d->p;
889
890 HRESULT hr = S_OK;
891
892 try
893 {
894 Utf8Str strFormat(bstrFormat);
895 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(data));
896
897 VBOXHGCMSVCPARM paParms[5];
898 int i = 0;
899 paParms[i++].setUInt32(uScreenId);
900 paParms[i++].setPointer((void*)strFormat.c_str(), (uint32_t)strFormat.length() + 1);
901 paParms[i++].setUInt32((uint32_t)strFormat.length() + 1);
902 paParms[i++].setPointer((void*)sfaData.raw(), (uint32_t)sfaData.size());
903 paParms[i++].setUInt32((uint32_t)sfaData.size());
904
905 DnDGuestResponse *pResp = d->response();
906 /* Reset any old progress status. */
907 pResp->resetProgress(p);
908
909 /* Note: The actual data transfer of files/directoies is performed by the
910 * DnD host service. */
911 d->hostCall(DragAndDropSvc::HOST_DND_HG_SND_DATA,
912 i,
913 paParms);
914
915 /* Query the progress object to the caller. */
916 pResp->queryProgressTo(ppProgress);
917 }
918 catch (HRESULT hr2)
919 {
920 hr = hr2;
921 }
922
923 return hr;
924}
925
926#ifdef VBOX_WITH_DRAG_AND_DROP_GH
927HRESULT GuestDnD::dragGHPending(ULONG uScreenId,
928 ComSafeArrayOut(BSTR, formats),
929 ComSafeArrayOut(DragAndDropAction_T, allowedActions),
930 DragAndDropAction_T *pDefaultAction)
931{
932 DPTR(GuestDnD);
933 const ComObjPtr<Guest> &p = d->p;
934
935 /* Default is ignoring */
936 *pDefaultAction = DragAndDropAction_Ignore;
937
938 HRESULT hr = S_OK;
939
940 try
941 {
942 VBOXHGCMSVCPARM paParms[1];
943 int i = 0;
944 paParms[i++].setUInt32(uScreenId);
945
946 d->hostCall(DragAndDropSvc::HOST_DND_GH_REQ_PENDING,
947 i,
948 paParms);
949
950 /* This blocks until the request is answered (or timed out). */
951 DnDGuestResponse *pResp = d->response();
952 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
953 return S_OK;
954
955 if (isDnDIgnoreAction(pResp->defAction()))
956 return S_OK;
957
958 /* Fetch the default action to use. */
959 *pDefaultAction = d->toMainAction(pResp->defAction());
960 d->toFormatSafeArray(pResp->format(), ComSafeArrayOutArg(formats));
961 d->toMainActions(pResp->allActions(), ComSafeArrayOutArg(allowedActions));
962
963 LogFlowFunc(("*pDefaultAction=0x%x\n", *pDefaultAction));
964 }
965 catch (HRESULT hr2)
966 {
967 hr = hr2;
968 }
969
970 return hr;
971}
972
973HRESULT GuestDnD::dragGHDropped(IN_BSTR bstrFormat, DragAndDropAction_T action,
974 IProgress **ppProgress)
975{
976 DPTR(GuestDnD);
977 const ComObjPtr<Guest> &p = d->p;
978
979 Utf8Str strFormat(bstrFormat);
980 HRESULT hr = S_OK;
981
982 uint32_t uAction = d->toHGCMAction(action);
983 /* If there is no usable action, ignore this request. */
984 if (isDnDIgnoreAction(uAction))
985 return S_OK;
986
987 const char *pcszFormat = strFormat.c_str();
988 bool fNeedsDropDir = DnDMIMENeedsDropDir(pcszFormat, strlen(pcszFormat));
989 LogFlowFunc(("strFormat=%s, uAction=0x%x, fNeedsDropDir=%RTbool\n",
990 pcszFormat, uAction, fNeedsDropDir));
991
992 DnDGuestResponse *pResp = d->response();
993 AssertPtr(pResp);
994
995 pResp->reset();
996
997 if (fNeedsDropDir)
998 {
999 char szDropDir[RTPATH_MAX];
1000 int rc = DnDDirCreateDroppedFiles(szDropDir, sizeof(szDropDir));
1001 if (RT_FAILURE(rc))
1002 return p->setError(VBOX_E_IPRT_ERROR,
1003 p->tr("Unable to create the temporary drag'n drop directory \"%s\" (%Rrc)\n"),
1004 szDropDir, rc);
1005 LogFlowFunc(("Dropped files directory on the host is: %s\n", szDropDir));
1006
1007 pResp->setDropDir(szDropDir);
1008 }
1009
1010 try
1011 {
1012 VBOXHGCMSVCPARM paParms[3];
1013 int i = 0;
1014 paParms[i++].setPointer((void*)strFormat.c_str(), (uint32_t)strFormat.length() + 1);
1015 paParms[i++].setUInt32((uint32_t)strFormat.length() + 1);
1016 paParms[i++].setUInt32(uAction);
1017
1018 /* Reset any old data and the progress status. */
1019 pResp->reset();
1020 pResp->resetProgress(p);
1021
1022 d->hostCall(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED,
1023 i,
1024 paParms);
1025
1026 /* Query the progress object to the caller. */
1027 pResp->queryProgressTo(ppProgress);
1028 }
1029 catch (HRESULT hr2)
1030 {
1031 hr = hr2;
1032 }
1033
1034 return hr;
1035}
1036
1037HRESULT GuestDnD::dragGHGetData(ComSafeArrayOut(BYTE, data))
1038{
1039 DPTR(GuestDnD);
1040 const ComObjPtr<Guest> &p = d->p;
1041
1042 HRESULT hr = S_OK;
1043
1044 DnDGuestResponse *pResp = d->response();
1045 if (pResp)
1046 {
1047 com::SafeArray<BYTE> sfaData;
1048
1049 size_t cbData = pResp->size();
1050 if (cbData)
1051 {
1052 const void *pvData = pResp->data();
1053 AssertPtr(pvData);
1054
1055 Utf8Str strFormat = pResp->format();
1056 LogFlowFunc(("strFormat=%s, cbData=%zu, pvData=0x%p\n",
1057 strFormat.c_str(), cbData, pvData));
1058
1059 if (DnDMIMEHasFileURLs(strFormat.c_str(), strFormat.length()))
1060 {
1061 LogFlowFunc(("strDropDir=%s\n", pResp->dropDir().c_str()));
1062
1063 DnDURIList lstURI;
1064 int rc2 = lstURI.RootFromURIData(pvData, cbData, 0 /* fFlags */);
1065 if (RT_SUCCESS(rc2))
1066 {
1067 Utf8Str strURIs = lstURI.RootToString(pResp->dropDir());
1068 size_t cbURIs = strURIs.length();
1069 if (sfaData.resize(cbURIs + 1 /* Include termination */))
1070 memcpy(sfaData.raw(), strURIs.c_str(), cbURIs);
1071 else
1072 hr = E_OUTOFMEMORY;
1073 }
1074 else
1075 hr = VBOX_E_IPRT_ERROR;
1076
1077 LogFlowFunc(("Found %zu root URIs, rc=%Rrc\n", lstURI.RootCount(), rc2));
1078 }
1079 else
1080 {
1081 /* Copy the data into a safe array of bytes. */
1082 if (sfaData.resize(cbData))
1083 memcpy(sfaData.raw(), pvData, cbData);
1084 else
1085 hr = E_OUTOFMEMORY;
1086 }
1087 }
1088
1089 /* Detach in any case, regardless of data size. */
1090 sfaData.detachTo(ComSafeArrayOutArg(data));
1091
1092 /* Delete the data. */
1093 pResp->reset();
1094 }
1095 else
1096 hr = VBOX_E_INVALID_OBJECT_STATE;
1097
1098 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
1099 return hr;
1100}
1101
1102int GuestDnD::onGHSendData(DnDGuestResponse *pResp,
1103 const void *pvData, size_t cbData,
1104 size_t cbTotalSize)
1105{
1106 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1107 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1108 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1109 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
1110
1111 int rc = pResp->dataAdd(pvData, cbData, NULL /* Current size */);
1112 if (RT_SUCCESS(rc))
1113 rc = pResp->dataSetStatus(cbData, cbTotalSize);
1114
1115 LogFlowFuncLeaveRC(rc);
1116 return rc;
1117}
1118
1119int GuestDnD::onGHSendDir(DnDGuestResponse *pResp,
1120 const char *pszPath, size_t cbPath,
1121 uint32_t fMode)
1122{
1123 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1124 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1125 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
1126
1127 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
1128 pszPath, cbPath, fMode));
1129
1130 int rc;
1131 char *pszDir = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
1132 if (pszDir)
1133 {
1134 rc = RTDirCreateFullPath(pszDir, fMode);
1135 RTStrFree(pszDir);
1136 }
1137 else
1138 rc = VERR_NO_MEMORY;
1139
1140 if (RT_SUCCESS(rc))
1141 rc = pResp->dataSetStatus(cbPath);
1142
1143 LogFlowFuncLeaveRC(rc);
1144 return rc;
1145}
1146
1147int GuestDnD::onGHSendFile(DnDGuestResponse *pResp,
1148 const char *pszPath, size_t cbPath,
1149 void *pvData, size_t cbData, uint32_t fMode)
1150{
1151 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1152 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1153 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
1154
1155 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n",
1156 pszPath, cbPath, fMode));
1157
1158 /** @todo Add file locking between calls! */
1159 int rc;
1160 char *pszFile = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
1161 if (pszFile)
1162 {
1163 RTFILE hFile;
1164 rc = RTFileOpen(&hFile, pszFile,
1165 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE | RTFILE_O_WRITE);
1166 if (RT_SUCCESS(rc))
1167 {
1168 rc = RTFileWrite(hFile, pvData, cbData,
1169 NULL /* No partial writes */);
1170 RTFileClose(hFile);
1171 }
1172 RTStrFree(pszFile);
1173 }
1174 else
1175 rc = VERR_NO_MEMORY;
1176
1177 if (RT_SUCCESS(rc))
1178 rc = pResp->dataSetStatus(cbData);
1179
1180 LogFlowFuncLeaveRC(rc);
1181 return rc;
1182}
1183#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1184
1185/* static */
1186DECLCALLBACK(int) GuestDnD::notifyGuestDragAndDropEvent(void *pvExtension, uint32_t u32Function,
1187 void *pvParms, uint32_t cbParms)
1188{
1189 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
1190 pvExtension, u32Function, pvParms, cbParms));
1191
1192 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest*>(pvExtension);
1193 if (!pGuest->m_pGuestDnD)
1194 return VINF_SUCCESS;
1195
1196 GuestDnD *pGuestDnD = pGuest->m_pGuestDnD;
1197 AssertPtr(pGuestDnD);
1198
1199 GuestDnDPrivate *d = static_cast<GuestDnDPrivate*>(pGuest->m_pGuestDnD->d_ptr);
1200 const ComObjPtr<Guest> &p = d->p;
1201
1202 DnDGuestResponse *pResp = d->response();
1203 if (pResp == NULL)
1204 return VERR_INVALID_PARAMETER;
1205
1206 int rc = VINF_SUCCESS;
1207 switch (u32Function)
1208 {
1209 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
1210 {
1211 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
1212 AssertPtr(pCBData);
1213 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
1214 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1215
1216 pResp->setDefAction(pCBData->uAction);
1217
1218 rc = pResp->notifyAboutGuestResponse();
1219 break;
1220 }
1221
1222 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
1223 {
1224 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
1225 AssertPtr(pCBData);
1226 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1227 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1228
1229 pResp->setFormat(pCBData->pszFormat);
1230
1231 rc = pResp->notifyAboutGuestResponse();
1232 break;
1233 }
1234
1235 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
1236 {
1237 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
1238 AssertPtr(pCBData);
1239 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
1240 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1241
1242 rc = pResp->setProgress(pCBData->uPercentage, pCBData->uState, pCBData->rc);
1243 break;
1244 }
1245
1246#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1247 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
1248 {
1249 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
1250 AssertPtr(pCBData);
1251 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
1252 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1253
1254 pResp->setFormat(pCBData->pszFormat);
1255 pResp->setDefAction(pCBData->uDefAction);
1256 pResp->setAllActions(pCBData->uAllActions);
1257
1258 rc = pResp->notifyAboutGuestResponse();
1259 break;
1260 }
1261
1262 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1263 {
1264 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1265 AssertPtr(pCBData);
1266 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1267 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1268
1269 rc = pGuestDnD->onGHSendData(pResp, pCBData->pvData, pCBData->cbData,
1270 pCBData->cbTotalSize);
1271 break;
1272 }
1273
1274 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1275 {
1276 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1277 AssertPtr(pCBData);
1278 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1279 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1280
1281 rc = pGuestDnD->onGHSendDir(pResp, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1282 break;
1283 }
1284
1285 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
1286 {
1287 DragAndDropSvc::PVBOXDNDCBSNDFILEDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATA>(pvParms);
1288 AssertPtr(pCBData);
1289 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATA) == cbParms, VERR_INVALID_PARAMETER);
1290 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1291
1292 rc = pGuestDnD->onGHSendFile(pResp, pCBData->pszFilePath, pCBData->cbFilePath,
1293 pCBData->pvData, pCBData->cbData, pCBData->fMode);
1294 break;
1295 }
1296
1297 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1298 {
1299 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1300 AssertPtr(pCBData);
1301 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1302 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1303
1304 /* Cleanup. */
1305 pResp->reset();
1306 rc = pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
1307 break;
1308 }
1309#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1310 default:
1311 rc = VERR_NOT_SUPPORTED; /* Tell the guest. */
1312 break;
1313 }
1314
1315 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1316 return rc;
1317}
1318#endif /* VBOX_WITH_DRAG_AND_DROP */
1319
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette