VirtualBox

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

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

DnD: Bugfixes for Linux hosts.

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