VirtualBox

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

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

DnD: Update.

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