VirtualBox

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

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

DnD: Update, bugfixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.8 KB
Line 
1/* $Id: GuestDnDImpl.cpp 50561 2014-02-24 21:07:22Z 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.215389.xyz. 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
206private:
207 RTSEMEVENT m_EventSem;
208 uint32_t m_defAction;
209 uint32_t m_allActions;
210 Utf8Str m_strFormat;
211
212 /** The actual MIME data.*/
213 void *m_pvData;
214 /** Size (in bytes) of MIME data. */
215 uint32_t m_cbData;
216
217 size_t m_cbDataCurrent;
218 size_t m_cbDataTotal;
219 /** Dropped files directory on the host. */
220 Utf8Str m_strDropDir;
221
222 ComObjPtr<Guest> m_parent;
223 ComObjPtr<Progress> m_progress;
224};
225
226/** @todo This class needs a major cleanup. Later. */
227class GuestDnDPrivate
228{
229private:
230
231 /** @todo Currently we only support one response. Maybe this needs to be extended at some time. */
232 GuestDnDPrivate(GuestDnD *q, const ComObjPtr<Guest>& pGuest)
233 : q_ptr(q)
234 , p(pGuest)
235 , m_pDnDResponse(new DnDGuestResponse(pGuest))
236 {}
237 virtual ~GuestDnDPrivate(void) { delete m_pDnDResponse; }
238
239 DnDGuestResponse *response(void) const { return m_pDnDResponse; }
240
241 HRESULT adjustCoords(ULONG uScreenId, ULONG *puX, ULONG *puY) const;
242 int hostCall(uint32_t u32Function, uint32_t cParms, PVBOXHGCMSVCPARM paParms) const;
243
244 /* Static helpers. */
245 static RTCString toFormatString(ComSafeArrayIn(IN_BSTR, formats));
246 static void toFormatSafeArray(const RTCString &strFormats, ComSafeArrayOut(BSTR, formats));
247
248 static DragAndDropAction_T toMainAction(uint32_t uAction);
249 static void toMainActions(uint32_t uActions, ComSafeArrayOut(DragAndDropAction_T, actions));
250 static uint32_t toHGCMAction(DragAndDropAction_T action);
251 static void toHGCMActions(DragAndDropAction_T inDefAction, uint32_t *pOutDefAction, ComSafeArrayIn(DragAndDropAction_T, inAllowedActions), uint32_t *pOutAllowedActions);
252
253 /* Private q and parent pointer */
254 GuestDnD *q_ptr;
255 ComObjPtr<Guest> p;
256
257 /* Private helper members. */
258 static const RTCList<RTCString> m_sstrAllowedMimeTypes;
259 DnDGuestResponse *m_pDnDResponse;
260
261 friend class GuestDnD;
262};
263
264/* What mime-types are supported by VirtualBox.
265 * Note: If you add something here, make sure you test it with all guest OS's!
266 ** @todo Make this MIME list configurable / extendable (by extra data?). Currently
267 * this is done hardcoded on every guest platform (POSIX/Windows).
268 */
269/* static */
270const RTCList<RTCString> GuestDnDPrivate::m_sstrAllowedMimeTypes = RTCList<RTCString>()
271 /* Uri's */
272 << "text/uri-list"
273 /* Text */
274 << "text/plain;charset=utf-8"
275 << "UTF8_STRING"
276 << "text/plain"
277 << "COMPOUND_TEXT"
278 << "TEXT"
279 << "STRING"
280 /* OpenOffice formates */
281 << "application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\""
282 << "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"";
283
284DnDGuestResponse::DnDGuestResponse(const ComObjPtr<Guest>& pGuest)
285 : m_EventSem(NIL_RTSEMEVENT)
286 , m_defAction(0)
287 , m_allActions(0)
288 , m_pvData(0)
289 , m_cbData(0)
290 , m_cbDataCurrent(0)
291 , m_cbDataTotal(0)
292 , m_parent(pGuest)
293{
294 int rc = RTSemEventCreate(&m_EventSem);
295 AssertRC(rc);
296}
297
298DnDGuestResponse::~DnDGuestResponse()
299{
300 reset();
301 int rc = RTSemEventDestroy(m_EventSem);
302 AssertRC(rc);
303}
304
305int DnDGuestResponse::notifyAboutGuestResponse()
306{
307 return RTSemEventSignal(m_EventSem);
308}
309
310int DnDGuestResponse::waitForGuestResponse(RTMSINTERVAL msTimeout /*= 500 */)
311{
312 int rc = RTSemEventWait(m_EventSem, msTimeout);
313#ifdef DEBUG_andy
314 LogFlowFunc(("msTimeout=%RU32, rc=%Rrc\n", msTimeout, rc));
315#endif
316 return rc;
317}
318
319int DnDGuestResponse::dataAdd(const void *pvData, uint32_t cbData,
320 uint32_t *pcbCurSize)
321{
322 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
323 AssertReturn(cbData, VERR_INVALID_PARAMETER);
324 /* pcbCurSize is optional. */
325
326 int rc = VINF_SUCCESS;
327
328 /** @todo Make reallocation scheme a bit smarter here. */
329 m_pvData = RTMemRealloc(m_pvData, m_cbData + cbData);
330 if (m_pvData)
331 {
332 memcpy(&static_cast<uint8_t*>(m_pvData)[m_cbData],
333 pvData, cbData);
334 m_cbData += cbData;
335
336 if (pcbCurSize)
337 *pcbCurSize = m_cbData;
338 }
339 else
340 rc = VERR_NO_MEMORY;
341
342 return rc;
343}
344
345void DnDGuestResponse::reset(void)
346{
347 if (m_pvData)
348 {
349 RTMemFree(m_pvData);
350 m_pvData = NULL;
351 }
352 m_cbData = 0;
353
354 m_cbDataCurrent = 0;
355 m_cbDataTotal = 0;
356}
357
358HRESULT DnDGuestResponse::resetProgress(const ComObjPtr<Guest>& pParent)
359{
360 m_progress.setNull();
361 HRESULT rc = m_progress.createObject();
362 if (SUCCEEDED(rc))
363 {
364 rc = m_progress->init(static_cast<IGuest*>(pParent),
365 Bstr(pParent->tr("Dropping data")).raw(),
366 TRUE);
367 }
368 return rc;
369}
370
371int DnDGuestResponse::setProgress(unsigned uPercentage,
372 uint32_t uState, int rcOp /* = VINF_SUCCESS */)
373{
374 LogFlowFunc(("uPercentage=%RU32, uState=%ld, rcOp=%Rrc\n",
375 uPercentage, uState, rcOp));
376
377 int vrc = VINF_SUCCESS;
378 if (!m_progress.isNull())
379 {
380 BOOL fCompleted;
381 HRESULT hr = m_progress->COMGETTER(Completed)(&fCompleted);
382 if (!fCompleted)
383 {
384 if (uState == DragAndDropSvc::DND_PROGRESS_ERROR)
385 {
386 hr = m_progress->notifyComplete(E_FAIL,
387 COM_IIDOF(IGuest),
388 m_parent->getComponentName(),
389 m_parent->tr("Drag'n drop guest error (%Rrc)"), rcOp);
390 reset();
391 }
392 else if (uState == DragAndDropSvc::DND_PROGRESS_CANCELLED)
393 {
394 hr = m_progress->Cancel();
395 if (SUCCEEDED(hr))
396 vrc = VERR_CANCELLED;
397
398 reset();
399 }
400 else /* uState == DragAndDropSvc::DND_PROGRESS_RUNNING */
401 {
402 hr = m_progress->SetCurrentOperationProgress(uPercentage);
403 AssertComRC(hr);
404 if ( uState == DragAndDropSvc::DND_PROGRESS_COMPLETE
405 || uPercentage >= 100)
406 hr = m_progress->notifyComplete(S_OK);
407 }
408 }
409 }
410
411 return vrc;
412}
413
414int DnDGuestResponse::dataSetStatus(size_t cbDataAdd, size_t cbDataTotal /* = 0 */)
415{
416 if (cbDataTotal)
417 {
418#ifndef DEBUG_andy
419 AssertMsg(m_cbDataTotal <= cbDataTotal, ("New data size must not be smaller (%zu) than old value (%zu)\n",
420 cbDataTotal, m_cbDataTotal));
421#endif
422 LogFlowFunc(("Updating total data size from %zu to %zu\n", m_cbDataTotal, cbDataTotal));
423 m_cbDataTotal = cbDataTotal;
424 }
425 AssertMsg(m_cbDataTotal, ("m_cbDataTotal must not be <= 0\n"));
426
427 m_cbDataCurrent += cbDataAdd;
428 unsigned int cPercentage = RT_MIN(m_cbDataCurrent * 100.0 / m_cbDataTotal, 100);
429
430 /** @todo Don't use anonymous enums (uint32_t). */
431 uint32_t uStatus = DragAndDropSvc::DND_PROGRESS_RUNNING;
432 if (m_cbDataCurrent >= m_cbDataTotal)
433 uStatus = DragAndDropSvc::DND_PROGRESS_COMPLETE;
434
435#ifdef DEBUG_andy
436 LogFlowFunc(("Updating transfer status (%zu/%zu), status=%ld\n",
437 m_cbDataCurrent, m_cbDataTotal, uStatus));
438#else
439 AssertMsg(m_cbDataCurrent <= m_cbDataTotal,
440 ("More data transferred (%RU32) than initially announced (%RU32)\n",
441 m_cbDataCurrent, m_cbDataTotal));
442#endif
443 int rc = setProgress(cPercentage, uStatus);
444
445 /** @todo For now we instantly confirm the cancel. Check if the
446 * guest should first clean up stuff itself and than really confirm
447 * the cancel request by an extra message. */
448 if (rc == VERR_CANCELLED)
449 rc = setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED);
450
451 return rc;
452}
453
454HRESULT DnDGuestResponse::queryProgressTo(IProgress **ppProgress)
455{
456 return m_progress.queryInterfaceTo(ppProgress);
457}
458
459HRESULT GuestDnDPrivate::adjustCoords(ULONG uScreenId, ULONG *puX, ULONG *puY) const
460{
461 /* For multi-monitor support we need to add shift values to the coordinates
462 * (depending on the screen number). */
463 ComPtr<IDisplay> pDisplay;
464 HRESULT hr = p->mParent->COMGETTER(Display)(pDisplay.asOutParam());
465 if (FAILED(hr))
466 return hr;
467
468 ComPtr<IFramebuffer> pFramebuffer;
469 LONG xShift, yShift;
470 hr = pDisplay->GetFramebuffer(uScreenId, pFramebuffer.asOutParam(),
471 &xShift, &yShift);
472 if (FAILED(hr))
473 return hr;
474
475 *puX += xShift;
476 *puY += yShift;
477
478 return hr;
479}
480
481int GuestDnDPrivate::hostCall(uint32_t u32Function, uint32_t cParms,
482 PVBOXHGCMSVCPARM paParms) const
483{
484 VMMDev *pVMMDev = NULL;
485 {
486 /* Make sure mParent is valid, so set the read lock while using.
487 * Do not keep this lock while doing the actual call, because in the meanwhile
488 * another thread could request a write lock which would be a bad idea ... */
489 AutoReadLock alock(p COMMA_LOCKVAL_SRC_POS);
490
491 /* Forward the information to the VMM device. */
492 AssertPtr(p->mParent);
493 pVMMDev = p->mParent->getVMMDev();
494 }
495
496 if (!pVMMDev)
497 throw p->setError(VBOX_E_VM_ERROR,
498 p->tr("VMM device is not available (is the VM running?)"));
499
500 LogFlowFunc(("hgcmHostCall msg=%RU32, numParms=%RU32\n", u32Function, cParms));
501 int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc",
502 u32Function,
503 cParms, paParms);
504 if (RT_FAILURE(rc))
505 {
506 LogFlowFunc(("hgcmHostCall error: %Rrc\n", rc));
507 throw p->setError(VBOX_E_VM_ERROR,
508 p->tr("hgcmHostCall failed (%Rrc)"), rc);
509 }
510
511 return rc;
512}
513
514/* static */
515RTCString GuestDnDPrivate::toFormatString(ComSafeArrayIn(IN_BSTR, formats))
516{
517 const RTCList<Utf8Str> formatList(ComSafeArrayInArg(formats));
518 RTCString strFormat;
519 for (size_t i = 0; i < formatList.size(); ++i)
520 {
521 const RTCString &f = formatList.at(i);
522 /* Only keep allowed format types. */
523 if (m_sstrAllowedMimeTypes.contains(f))
524 strFormat += f + "\r\n";
525 }
526 return strFormat;
527}
528
529/* static */
530void GuestDnDPrivate::toFormatSafeArray(const RTCString &strFormats, ComSafeArrayOut(BSTR, formats))
531{
532 RTCList<RTCString> list = strFormats.split("\r\n");
533 size_t i = 0;
534 while (i < list.size())
535 {
536 /* Only keep allowed format types. */
537 if (!m_sstrAllowedMimeTypes.contains(list.at(i)))
538 list.removeAt(i);
539 else
540 ++i;
541 }
542 /* Create a safe array out of the cleaned list. */
543 com::SafeArray<BSTR> sfaFormats(list.size());
544 for (i = 0; i < list.size(); ++i)
545 {
546 const RTCString &f = list.at(i);
547 if (m_sstrAllowedMimeTypes.contains(f))
548 {
549 Bstr bstr(f);
550 bstr.detachTo(&sfaFormats[i]);
551 }
552 }
553 sfaFormats.detachTo(ComSafeArrayOutArg(formats));
554}
555
556/* static */
557uint32_t GuestDnDPrivate::toHGCMAction(DragAndDropAction_T action)
558{
559 uint32_t a = DND_IGNORE_ACTION;
560 switch (action)
561 {
562 case DragAndDropAction_Copy: a = DND_COPY_ACTION; break;
563 case DragAndDropAction_Move: a = DND_MOVE_ACTION; break;
564 case DragAndDropAction_Link: /* For now it doesn't seems useful to allow a link action between host & guest. Maybe later! */
565 case DragAndDropAction_Ignore: /* Ignored */ break;
566 default: AssertMsgFailed(("Action %u not recognized!\n", action)); break;
567 }
568 return a;
569}
570
571/* static */
572void GuestDnDPrivate::toHGCMActions(DragAndDropAction_T inDefAction,
573 uint32_t *pOutDefAction,
574 ComSafeArrayIn(DragAndDropAction_T, inAllowedActions),
575 uint32_t *pOutAllowedActions)
576{
577 const com::SafeArray<DragAndDropAction_T> sfaInActions(ComSafeArrayInArg(inAllowedActions));
578
579 /* Defaults */
580 *pOutDefAction = toHGCMAction(inDefAction);;
581 *pOutAllowedActions = DND_IGNORE_ACTION;
582
583 /* First convert the allowed actions to a bit array. */
584 for (size_t i = 0; i < sfaInActions.size(); ++i)
585 *pOutAllowedActions |= toHGCMAction(sfaInActions[i]);
586
587 /* Second check if the default action is a valid action and if not so try
588 * to find an allowed action. */
589 if (isDnDIgnoreAction(*pOutDefAction))
590 {
591 if (hasDnDCopyAction(*pOutAllowedActions))
592 *pOutDefAction = DND_COPY_ACTION;
593 else if (hasDnDMoveAction(*pOutAllowedActions))
594 *pOutDefAction = DND_MOVE_ACTION;
595 }
596}
597
598/* static */
599DragAndDropAction_T GuestDnDPrivate::toMainAction(uint32_t uAction)
600{
601 /* For now it doesn't seems useful to allow a
602 * link action between host & guest. Maybe later! */
603 return (isDnDCopyAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Copy :
604 isDnDMoveAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Move :
605 (DragAndDropAction_T)DragAndDropAction_Ignore);
606}
607
608/* static */
609void GuestDnDPrivate::toMainActions(uint32_t uActions,
610 ComSafeArrayOut(DragAndDropAction_T, actions))
611{
612 /* For now it doesn't seems useful to allow a
613 * link action between host & guest. Maybe later! */
614 RTCList<DragAndDropAction_T> list;
615 if (hasDnDCopyAction(uActions))
616 list.append(DragAndDropAction_Copy);
617 if (hasDnDMoveAction(uActions))
618 list.append(DragAndDropAction_Move);
619
620 com::SafeArray<DragAndDropAction_T> sfaActions(list.size());
621 for (size_t i = 0; i < list.size(); ++i)
622 sfaActions[i] = list.at(i);
623 sfaActions.detachTo(ComSafeArrayOutArg(actions));
624}
625
626GuestDnD::GuestDnD(const ComObjPtr<Guest>& pGuest)
627 : d_ptr(new GuestDnDPrivate(this, pGuest))
628{
629}
630
631GuestDnD::~GuestDnD(void)
632{
633 delete d_ptr;
634}
635
636HRESULT GuestDnD::dragHGEnter(ULONG uScreenId, ULONG uX, ULONG uY,
637 DragAndDropAction_T defaultAction,
638 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
639 ComSafeArrayIn(IN_BSTR, formats),
640 DragAndDropAction_T *pResultAction)
641{
642 DPTR(GuestDnD);
643 const ComObjPtr<Guest> &p = d->p;
644
645 /* Default is ignoring */
646 *pResultAction = DragAndDropAction_Ignore;
647
648 /* Check & convert the drag & drop actions */
649 uint32_t uDefAction = 0;
650 uint32_t uAllowedActions = 0;
651 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
652 /* If there is no usable action, ignore this request. */
653 if (isDnDIgnoreAction(uDefAction))
654 return S_OK;
655
656 /* Make a flat data string out of the mime-type list. */
657 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
658 /* If there is no valid mime-type, ignore this request. */
659 if (strFormats.isEmpty())
660 return S_OK;
661
662 HRESULT hr = S_OK;
663
664 try
665 {
666 /* Adjust the coordinates in a multi-monitor setup. */
667 d->adjustCoords(uScreenId, &uX, &uY);
668
669 VBOXHGCMSVCPARM paParms[7];
670 int i = 0;
671 paParms[i++].setUInt32(uScreenId);
672 paParms[i++].setUInt32(uX);
673 paParms[i++].setUInt32(uY);
674 paParms[i++].setUInt32(uDefAction);
675 paParms[i++].setUInt32(uAllowedActions);
676 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
677 paParms[i++].setUInt32(strFormats.length() + 1);
678
679 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_ENTER,
680 i,
681 paParms);
682
683 DnDGuestResponse *pResp = d->response();
684 /* This blocks until the request is answered (or timeout). */
685 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
686 return S_OK;
687
688 /* Copy the response info */
689 *pResultAction = d->toMainAction(pResp->defAction());
690 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
691 }
692 catch (HRESULT hr2)
693 {
694 hr = hr2;
695 }
696
697 return hr;
698}
699
700HRESULT GuestDnD::dragHGMove(ULONG uScreenId, ULONG uX, ULONG uY,
701 DragAndDropAction_T defaultAction,
702 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
703 ComSafeArrayIn(IN_BSTR, formats),
704 DragAndDropAction_T *pResultAction)
705{
706 DPTR(GuestDnD);
707 const ComObjPtr<Guest> &p = d->p;
708
709 /* Default is ignoring */
710 *pResultAction = DragAndDropAction_Ignore;
711
712 /* Check & convert the drag & drop actions */
713 uint32_t uDefAction = 0;
714 uint32_t uAllowedActions = 0;
715 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
716 /* If there is no usable action, ignore this request. */
717 if (isDnDIgnoreAction(uDefAction))
718 return S_OK;
719
720 /* Make a flat data string out of the mime-type list. */
721 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
722 /* If there is no valid mime-type, ignore this request. */
723 if (strFormats.isEmpty())
724 return S_OK;
725
726 HRESULT hr = S_OK;
727
728 try
729 {
730 /* Adjust the coordinates in a multi-monitor setup. */
731 d->adjustCoords(uScreenId, &uX, &uY);
732
733 VBOXHGCMSVCPARM paParms[7];
734 int i = 0;
735 paParms[i++].setUInt32(uScreenId);
736 paParms[i++].setUInt32(uX);
737 paParms[i++].setUInt32(uY);
738 paParms[i++].setUInt32(uDefAction);
739 paParms[i++].setUInt32(uAllowedActions);
740 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
741 paParms[i++].setUInt32(strFormats.length() + 1);
742
743 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_MOVE,
744 i,
745 paParms);
746
747 DnDGuestResponse *pResp = d->response();
748 /* This blocks until the request is answered (or timeout). */
749 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
750 return S_OK;
751
752 /* Copy the response info */
753 *pResultAction = d->toMainAction(pResp->defAction());
754 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
755 }
756 catch (HRESULT hr2)
757 {
758 hr = hr2;
759 }
760
761 return hr;
762}
763
764HRESULT GuestDnD::dragHGLeave(ULONG uScreenId)
765{
766 DPTR(GuestDnD);
767 const ComObjPtr<Guest> &p = d->p;
768
769 HRESULT hr = S_OK;
770
771 try
772 {
773 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_LEAVE,
774 0,
775 NULL);
776
777 DnDGuestResponse *pResp = d->response();
778 /* This blocks until the request is answered (or timeout). */
779 pResp->waitForGuestResponse();
780 }
781 catch (HRESULT hr2)
782 {
783 hr = hr2;
784 }
785
786 return hr;
787}
788
789HRESULT GuestDnD::dragHGDrop(ULONG uScreenId, ULONG uX, ULONG uY,
790 DragAndDropAction_T defaultAction,
791 ComSafeArrayIn(DragAndDropAction_T, allowedActions),
792 ComSafeArrayIn(IN_BSTR, formats),
793 BSTR *pstrFormat,
794 DragAndDropAction_T *pResultAction)
795{
796 DPTR(GuestDnD);
797 const ComObjPtr<Guest> &p = d->p;
798
799 /* Default is ignoring */
800 *pResultAction = DragAndDropAction_Ignore;
801
802 /* Check & convert the drag & drop actions */
803 uint32_t uDefAction = 0;
804 uint32_t uAllowedActions = 0;
805 d->toHGCMActions(defaultAction, &uDefAction, ComSafeArrayInArg(allowedActions), &uAllowedActions);
806 /* If there is no usable action, ignore this request. */
807 if (isDnDIgnoreAction(uDefAction))
808 return S_OK;
809
810 /* Make a flat data string out of the mime-type list. */
811 RTCString strFormats = d->toFormatString(ComSafeArrayInArg(formats));
812 /* If there is no valid mime-type, ignore this request. */
813 if (strFormats.isEmpty())
814 return S_OK;
815
816 HRESULT hr = S_OK;
817
818 try
819 {
820 /* Adjust the coordinates in a multi-monitor setup. */
821 d->adjustCoords(uScreenId, &uX, &uY);
822
823 VBOXHGCMSVCPARM paParms[7];
824 int i = 0;
825 paParms[i++].setUInt32(uScreenId);
826 paParms[i++].setUInt32(uX);
827 paParms[i++].setUInt32(uY);
828 paParms[i++].setUInt32(uDefAction);
829 paParms[i++].setUInt32(uAllowedActions);
830 paParms[i++].setPointer((void*)strFormats.c_str(), strFormats.length() + 1);
831 paParms[i++].setUInt32(strFormats.length() + 1);
832
833 d->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_DROPPED,
834 i,
835 paParms);
836
837 DnDGuestResponse *pResp = d->response();
838 /* This blocks until the request is answered (or timeout). */
839 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
840 return S_OK;
841
842 /* Copy the response info */
843 *pResultAction = d->toMainAction(pResp->defAction());
844 Bstr(pResp->format()).cloneTo(pstrFormat);
845
846 LogFlowFunc(("*pResultAction=%ld\n", *pResultAction));
847 }
848 catch (HRESULT hr2)
849 {
850 hr = hr2;
851 }
852
853 return hr;
854}
855
856HRESULT GuestDnD::dragHGPutData(ULONG uScreenId, IN_BSTR bstrFormat,
857 ComSafeArrayIn(BYTE, data), IProgress **ppProgress)
858{
859 DPTR(GuestDnD);
860 const ComObjPtr<Guest> &p = d->p;
861
862 HRESULT hr = S_OK;
863
864 try
865 {
866 Utf8Str strFormat(bstrFormat);
867 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(data));
868
869 VBOXHGCMSVCPARM paParms[5];
870 int i = 0;
871 paParms[i++].setUInt32(uScreenId);
872 paParms[i++].setPointer((void*)strFormat.c_str(), (uint32_t)strFormat.length() + 1);
873 paParms[i++].setUInt32((uint32_t)strFormat.length() + 1);
874 paParms[i++].setPointer((void*)sfaData.raw(), (uint32_t)sfaData.size());
875 paParms[i++].setUInt32((uint32_t)sfaData.size());
876
877 DnDGuestResponse *pResp = d->response();
878 /* Reset any old progress status. */
879 pResp->resetProgress(p);
880
881 /* Note: The actual data transfer of files/directoies is performed by the
882 * DnD host service. */
883 d->hostCall(DragAndDropSvc::HOST_DND_HG_SND_DATA,
884 i,
885 paParms);
886
887 /* Query the progress object to the caller. */
888 pResp->queryProgressTo(ppProgress);
889 }
890 catch (HRESULT hr2)
891 {
892 hr = hr2;
893 }
894
895 return hr;
896}
897
898#ifdef VBOX_WITH_DRAG_AND_DROP_GH
899HRESULT GuestDnD::dragGHPending(ULONG uScreenId,
900 ComSafeArrayOut(BSTR, formats),
901 ComSafeArrayOut(DragAndDropAction_T, allowedActions),
902 DragAndDropAction_T *pDefaultAction)
903{
904 DPTR(GuestDnD);
905 const ComObjPtr<Guest> &p = d->p;
906
907 /* Default is ignoring */
908 *pDefaultAction = DragAndDropAction_Ignore;
909
910 HRESULT hr = S_OK;
911
912 try
913 {
914 VBOXHGCMSVCPARM paParms[1];
915 int i = 0;
916 paParms[i++].setUInt32(uScreenId);
917
918 d->hostCall(DragAndDropSvc::HOST_DND_GH_REQ_PENDING,
919 i,
920 paParms);
921
922 /* This blocks until the request is answered (or timed out). */
923 DnDGuestResponse *pResp = d->response();
924 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
925 return S_OK;
926
927 if (isDnDIgnoreAction(pResp->defAction()))
928 return S_OK;
929
930 /* Fetch the default action to use. */
931 *pDefaultAction = d->toMainAction(pResp->defAction());
932 d->toFormatSafeArray(pResp->format(), ComSafeArrayOutArg(formats));
933 d->toMainActions(pResp->allActions(), ComSafeArrayOutArg(allowedActions));
934
935 LogFlowFunc(("*pDefaultAction=0x%x\n", *pDefaultAction));
936 }
937 catch (HRESULT hr2)
938 {
939 hr = hr2;
940 }
941
942 return hr;
943}
944
945HRESULT GuestDnD::dragGHDropped(IN_BSTR bstrFormat, DragAndDropAction_T action,
946 IProgress **ppProgress)
947{
948 DPTR(GuestDnD);
949 const ComObjPtr<Guest> &p = d->p;
950
951 Utf8Str strFormat(bstrFormat);
952 HRESULT hr = S_OK;
953
954 uint32_t uAction = d->toHGCMAction(action);
955 /* If there is no usable action, ignore this request. */
956 if (isDnDIgnoreAction(uAction))
957 return S_OK;
958
959 const char *pcszFormat = strFormat.c_str();
960 bool fNeedsDropDir = DnDMIMENeedsDropDir(pcszFormat, strlen(pcszFormat));
961 LogFlowFunc(("strFormat=%s, uAction=0x%x, fNeedsDropDir=%RTbool\n",
962 pcszFormat, uAction, fNeedsDropDir));
963
964 DnDGuestResponse *pResp = d->response();
965 AssertPtr(pResp);
966
967 pResp->reset();
968
969 if (fNeedsDropDir)
970 {
971 char szDropDir[RTPATH_MAX];
972 int rc = DnDDirCreateDroppedFiles(szDropDir, sizeof(szDropDir));
973 if (RT_FAILURE(rc))
974 return p->setError(VBOX_E_IPRT_ERROR,
975 p->tr("Unable to create the temporary drag'n drop directory \"%s\" (%Rrc)\n"),
976 szDropDir, rc);
977 LogFlowFunc(("Dropped files directory on the host is: %s\n", szDropDir));
978
979 pResp->setDropDir(szDropDir);
980 }
981
982 try
983 {
984 VBOXHGCMSVCPARM paParms[3];
985 int i = 0;
986 paParms[i++].setPointer((void*)strFormat.c_str(), (uint32_t)strFormat.length() + 1);
987 paParms[i++].setUInt32((uint32_t)strFormat.length() + 1);
988 paParms[i++].setUInt32(uAction);
989
990 /* Reset any old data and the progress status. */
991 pResp->reset();
992 pResp->resetProgress(p);
993
994 d->hostCall(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED,
995 i,
996 paParms);
997
998 /* Query the progress object to the caller. */
999 pResp->queryProgressTo(ppProgress);
1000 }
1001 catch (HRESULT hr2)
1002 {
1003 hr = hr2;
1004 }
1005
1006 return hr;
1007}
1008
1009HRESULT GuestDnD::dragGHGetData(ComSafeArrayOut(BYTE, data))
1010{
1011 DPTR(GuestDnD);
1012 const ComObjPtr<Guest> &p = d->p;
1013
1014 HRESULT hr = S_OK;
1015
1016 DnDGuestResponse *pResp = d->response();
1017 if (pResp)
1018 {
1019 com::SafeArray<BYTE> sfaData;
1020
1021 size_t cbData = pResp->size();
1022 if (cbData)
1023 {
1024 const void *pvData = pResp->data();
1025 AssertPtr(pvData);
1026
1027 Utf8Str strFormat = pResp->format();
1028 LogFlowFunc(("strFormat=%s, cbData=%zu, pvData=0x%p\n",
1029 strFormat.c_str(), cbData, pvData));
1030
1031 if (DnDMIMEHasFileURLs(strFormat.c_str(), strFormat.length()))
1032 {
1033 LogFlowFunc(("strDropDir=%s\n", pResp->dropDir().c_str()));
1034
1035 DnDURIList lstURI;
1036 int rc2 = lstURI.RootFromURIData(pvData, cbData, 0 /* fFlags */);
1037 if (RT_SUCCESS(rc2))
1038 {
1039 Utf8Str strURIs = lstURI.RootToString(pResp->dropDir());
1040 size_t cbURIs = strURIs.length();
1041 if (sfaData.resize(cbURIs + 1 /* Include termination */))
1042 memcpy(sfaData.raw(), strURIs.c_str(), cbURIs);
1043 else
1044 hr = E_OUTOFMEMORY;
1045 }
1046 else
1047 hr = VBOX_E_IPRT_ERROR;
1048
1049 LogFlowFunc(("Found %zu root URIs, rc=%Rrc\n", lstURI.RootCount(), rc2));
1050 }
1051 else
1052 {
1053 /* Copy the data into a safe array of bytes. */
1054 if (sfaData.resize(cbData))
1055 memcpy(sfaData.raw(), pvData, cbData);
1056 else
1057 hr = E_OUTOFMEMORY;
1058 }
1059 }
1060
1061 /* Detach in any case, regardless of data size. */
1062 sfaData.detachTo(ComSafeArrayOutArg(data));
1063
1064 /* Delete the data. */
1065 pResp->reset();
1066 }
1067 else
1068 hr = VBOX_E_INVALID_OBJECT_STATE;
1069
1070 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
1071 return hr;
1072}
1073
1074int GuestDnD::onGHSendData(DnDGuestResponse *pResp,
1075 const void *pvData, size_t cbData,
1076 size_t cbTotalSize)
1077{
1078 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1079 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1080 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1081 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
1082
1083 int rc = pResp->dataAdd(pvData, cbData, NULL /* Current size */);
1084 if (RT_SUCCESS(rc))
1085 rc = pResp->dataSetStatus(cbData, cbTotalSize);
1086
1087 LogFlowFuncLeaveRC(rc);
1088 return rc;
1089}
1090
1091int GuestDnD::onGHSendDir(DnDGuestResponse *pResp,
1092 const char *pszPath, size_t cbPath,
1093 uint32_t fMode)
1094{
1095 AssertPtrReturn(pResp, VERR_INVALID_POINTER);
1096 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1097 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
1098
1099 LogFlowFunc(("strDir=%s, cbPath=%zu, fMode=0x%x\n",
1100 pszPath, cbPath, fMode));
1101
1102 int rc;
1103 char *pszDir = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
1104 if (pszDir)
1105 {
1106 rc = RTDirCreateFullPath(pszDir, fMode);
1107 RTStrFree(pszDir);
1108 }
1109 else
1110 rc = VERR_NO_MEMORY;
1111
1112 if (RT_SUCCESS(rc))
1113 rc = pResp->dataSetStatus(cbPath);
1114
1115 LogFlowFuncLeaveRC(rc);
1116 return rc;
1117}
1118
1119int GuestDnD::onGHSendFile(DnDGuestResponse *pResp,
1120 const char *pszPath, size_t cbPath,
1121 void *pvData, size_t cbData, 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(("strFile=%s, cbPath=%zu, fMode=0x%x\n",
1128 pszPath, cbPath, fMode));
1129
1130 /** @todo Add file locking between calls! */
1131 int rc;
1132 char *pszFile = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
1133 if (pszFile)
1134 {
1135 RTFILE hFile;
1136 rc = RTFileOpen(&hFile, pszFile,
1137 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE | RTFILE_O_WRITE);
1138 if (RT_SUCCESS(rc))
1139 {
1140 rc = RTFileWrite(hFile, pvData, cbData,
1141 NULL /* No partial writes */);
1142 RTFileClose(hFile);
1143 }
1144 RTStrFree(pszFile);
1145 }
1146 else
1147 rc = VERR_NO_MEMORY;
1148
1149 if (RT_SUCCESS(rc))
1150 rc = pResp->dataSetStatus(cbData);
1151
1152 LogFlowFuncLeaveRC(rc);
1153 return rc;
1154}
1155#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1156
1157/* static */
1158DECLCALLBACK(int) GuestDnD::notifyGuestDragAndDropEvent(void *pvExtension, uint32_t u32Function,
1159 void *pvParms, uint32_t cbParms)
1160{
1161 LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
1162 pvExtension, u32Function, pvParms, cbParms));
1163
1164 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest*>(pvExtension);
1165 if (!pGuest->m_pGuestDnD)
1166 return VINF_SUCCESS;
1167
1168 GuestDnD *pGuestDnD = pGuest->m_pGuestDnD;
1169 AssertPtr(pGuestDnD);
1170
1171 GuestDnDPrivate *d = static_cast<GuestDnDPrivate*>(pGuest->m_pGuestDnD->d_ptr);
1172 const ComObjPtr<Guest> &p = d->p;
1173
1174 DnDGuestResponse *pResp = d->response();
1175 if (pResp == NULL)
1176 return VERR_INVALID_PARAMETER;
1177
1178 int rc = VINF_SUCCESS;
1179 switch (u32Function)
1180 {
1181 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
1182 {
1183 DragAndDropSvc::PVBOXDNDCBHGACKOPDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGACKOPDATA>(pvParms);
1184 AssertPtr(pCBData);
1185 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
1186 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1187
1188 pResp->setDefAction(pCBData->uAction);
1189
1190 rc = pResp->notifyAboutGuestResponse();
1191 break;
1192 }
1193
1194 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
1195 {
1196 DragAndDropSvc::PVBOXDNDCBHGREQDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGREQDATADATA>(pvParms);
1197 AssertPtr(pCBData);
1198 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1199 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1200
1201 pResp->setFormat(pCBData->pszFormat);
1202
1203 rc = pResp->notifyAboutGuestResponse();
1204 break;
1205 }
1206
1207 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
1208 {
1209 DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGEVTPROGRESSDATA>(pvParms);
1210 AssertPtr(pCBData);
1211 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
1212 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1213
1214 rc = pResp->setProgress(pCBData->uPercentage, pCBData->uState, pCBData->rc);
1215 break;
1216 }
1217
1218#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1219 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
1220 {
1221 DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBGHACKPENDINGDATA>(pvParms);
1222 AssertPtr(pCBData);
1223 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
1224 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1225
1226 pResp->setFormat(pCBData->pszFormat);
1227 pResp->setDefAction(pCBData->uDefAction);
1228 pResp->setAllActions(pCBData->uAllActions);
1229
1230 rc = pResp->notifyAboutGuestResponse();
1231 break;
1232 }
1233
1234 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1235 {
1236 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1237 AssertPtr(pCBData);
1238 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1239 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1240
1241 rc = pGuestDnD->onGHSendData(pResp, pCBData->pvData, pCBData->cbData,
1242 pCBData->cbTotalSize);
1243 break;
1244 }
1245
1246 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1247 {
1248 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1249 AssertPtr(pCBData);
1250 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1251 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1252
1253 rc = pGuestDnD->onGHSendDir(pResp, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1254 break;
1255 }
1256
1257 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
1258 {
1259 DragAndDropSvc::PVBOXDNDCBSNDFILEDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATA>(pvParms);
1260 AssertPtr(pCBData);
1261 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATA) == cbParms, VERR_INVALID_PARAMETER);
1262 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1263
1264 rc = pGuestDnD->onGHSendFile(pResp, pCBData->pszFilePath, pCBData->cbFilePath,
1265 pCBData->pvData, pCBData->cbData, pCBData->fMode);
1266 break;
1267 }
1268
1269 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1270 {
1271 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1272 AssertPtr(pCBData);
1273 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1274 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1275
1276 /* Cleanup. */
1277 pResp->reset();
1278 rc = pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
1279 break;
1280 }
1281#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1282 default:
1283 rc = VERR_NOT_SUPPORTED; /* Tell the guest. */
1284 break;
1285 }
1286
1287 LogFlowFunc(("Returning rc=%Rrc\n", rc));
1288 return rc;
1289}
1290#endif /* VBOX_WITH_DRAG_AND_DROP */
1291
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