VirtualBox

source: vbox/trunk/src/VBox/HostServices/DragAndDrop/dndmanager.cpp@ 49891

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

Merged private draganddrop branch into trunk.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.3 KB
Line 
1/* $Id: dndmanager.cpp 49891 2013-12-12 20:09:20Z vboxsync $ */
2/** @file
3 * Drag and Drop manager.
4 */
5
6/*
7 * Copyright (C) 2011-2013 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/******************************************************************************
19 * Header Files *
20 ******************************************************************************/
21
22#ifdef LOG_GROUP
23 #undef LOG_GROUP
24#endif
25#define LOG_GROUP LOG_GROUP_GUEST_DND
26
27#include "dndmanager.h"
28
29#include <VBox/log.h>
30#include <iprt/file.h>
31#include <iprt/dir.h>
32#include <iprt/path.h>
33#include <iprt/uri.h>
34
35/******************************************************************************
36 * Private declarations *
37 ******************************************************************************/
38
39typedef DECLCALLBACK(int) FNDNDPRIVATEPROGRESS(size_t cbDone, void *pvUser);
40typedef FNDNDPRIVATEPROGRESS *PFNDNDPRIVATEPROGRESS;
41
42/**
43 * Internal DnD message class for informing the guest about a new directory.
44 *
45 * @see DnDHGSendDataMessage
46 */
47class DnDHGSendDirPrivate: public DnDMessage
48{
49public:
50 DnDHGSendDirPrivate(const RTCString &strPath, uint32_t fMode, uint64_t cbSize, PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser)
51 : m_strPath(strPath)
52 , m_cbSize(cbSize)
53 , m_pfnProgressCallback(pfnProgressCallback)
54 , m_pvProgressUser(pvProgressUser)
55 {
56 VBOXHGCMSVCPARM paTmpParms[3];
57 paTmpParms[0].setString(m_strPath.c_str());
58 paTmpParms[1].setUInt32(m_strPath.length() + 1);
59 paTmpParms[2].setUInt32(fMode);
60
61 m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_DIR, 3, paTmpParms);
62 }
63
64 int currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
65 {
66 int rc = DnDMessage::currentMessage(uMsg, cParms, paParms);
67 /* Advance progress info */
68 if ( RT_SUCCESS(rc)
69 && m_pfnProgressCallback)
70 rc = m_pfnProgressCallback(m_cbSize, m_pvProgressUser);
71
72 return rc;
73 }
74
75protected:
76 RTCString m_strPath;
77
78 /* Progress stuff */
79 size_t m_cbSize;
80 PFNDNDPRIVATEPROGRESS m_pfnProgressCallback;
81 void *m_pvProgressUser;
82};
83
84/**
85 * Internal DnD message class for informing the guest about a new file.
86 *
87 * @see DnDHGSendDataMessage
88 */
89class DnDHGSendFilePrivate: public DnDMessage
90{
91public:
92 DnDHGSendFilePrivate(const RTCString &strHostPath, const RTCString &strGuestPath, uint32_t fMode, uint64_t cbSize, PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser);
93 virtual ~DnDHGSendFilePrivate();
94
95 int currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
96
97protected:
98 RTCString m_strHostPath;
99 RTCString m_strGuestPath;
100 uint64_t m_cbFileSize;
101 uint64_t m_cbFileProcessed;
102 RTFILE m_hCurFile;
103 VBOXHGCMSVCPARM m_paSkelParms[5];
104
105 /* Progress stuff */
106 PFNDNDPRIVATEPROGRESS m_pfnProgressCallback;
107 void *m_pvProgressUser;
108};
109
110/**
111 * Internal DnD message class for informing the guest about new drag & drop
112 * data.
113 *
114 * @see DnDHGSendDataMessage
115 */
116class DnDHGSendDataMessagePrivate: public DnDMessage
117{
118public:
119 DnDHGSendDataMessagePrivate(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser);
120 int currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
121
122protected:
123 size_t m_cbSize;
124 size_t m_cbDone;
125
126 /* Progress stuff */
127 PFNDNDPRIVATEPROGRESS m_pfnProgressCallback;
128 void *m_pvProgressUser;
129};
130
131/******************************************************************************
132 * Implementation *
133 ******************************************************************************/
134
135/******************************************************************************
136 * DnDHGSendFilePrivate *
137 ******************************************************************************/
138
139DnDHGSendFilePrivate::DnDHGSendFilePrivate(const RTCString &strHostPath, const RTCString &strGuestPath, uint32_t fMode, uint64_t cbSize, PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser)
140 : m_strHostPath(strHostPath)
141 , m_strGuestPath(strGuestPath)
142 , m_cbFileSize(cbSize)
143 , m_cbFileProcessed(0)
144 , m_hCurFile(0)
145 , m_pfnProgressCallback(pfnProgressCallback)
146 , m_pvProgressUser(pvProgressUser)
147{
148 m_paSkelParms[0].setString(m_strGuestPath.c_str());
149 m_paSkelParms[1].setUInt32(m_strGuestPath.length() + 1);
150 m_paSkelParms[2].setPointer(NULL, 0);
151 m_paSkelParms[3].setUInt32(0);
152 m_paSkelParms[4].setUInt32(fMode);
153
154 m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_FILE, 5, m_paSkelParms);
155}
156
157DnDHGSendFilePrivate::~DnDHGSendFilePrivate()
158{
159 if (m_hCurFile)
160 RTFileClose(m_hCurFile);
161}
162
163int DnDHGSendFilePrivate::currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
164{
165 if (!m_pNextMsg)
166 return VERR_NO_DATA;
167
168 int rc = m_pNextMsg->getData(uMsg, cParms, paParms);
169 clearNextMsg();
170 if (RT_FAILURE(rc))
171 return rc;
172
173 if (!m_hCurFile)
174 {
175 /* Open files on the host with RTFILE_O_DENY_WRITE to prevent races where the host
176 * writes to the file while the guest transfers it over. */
177 rc = RTFileOpen(&m_hCurFile, m_strHostPath.c_str(),
178 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
179 }
180
181 size_t cbRead;
182 if (RT_SUCCESS(rc))
183 {
184 /* Get buffer size + pointer to buffer from guest side. */
185 uint32_t cbToRead = paParms[2].u.pointer.size;
186 Assert(cbToRead);
187 void *pvBuf = paParms[2].u.pointer.addr;
188 AssertPtr(pvBuf);
189 rc = RTFileRead(m_hCurFile, pvBuf, cbToRead, &cbRead);
190 if (RT_LIKELY(RT_SUCCESS(rc)))
191 {
192 /* Advance. */
193 m_cbFileProcessed += cbRead;
194 Assert(m_cbFileProcessed <= m_cbFileSize);
195
196 /* Tell the guest the actual size. */
197 paParms[3].setUInt32(cbRead);
198 }
199 }
200
201 if (RT_SUCCESS(rc))
202 {
203 /* Check if we are done. */
204 bool fDone = m_cbFileSize == m_cbFileProcessed;
205 if (!fDone)
206 {
207 try
208 {
209 /* More data! Prepare the next message. */
210 m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_FILE, 5 /* cParms */,
211 m_paSkelParms);
212 }
213 catch(std::bad_alloc &)
214 {
215 rc = VERR_NO_MEMORY;
216 }
217 }
218
219 /* Advance progress info. */
220 if ( RT_SUCCESS(rc)
221 && m_pfnProgressCallback)
222 rc = m_pfnProgressCallback(cbRead, m_pvProgressUser);
223
224 if ( fDone
225 || RT_FAILURE(rc))
226 {
227 RTFileClose(m_hCurFile);
228 m_hCurFile = 0;
229 }
230 }
231
232 return rc;
233}
234
235/******************************************************************************
236 * DnDHGSendDataMessagePrivate *
237 ******************************************************************************/
238
239DnDHGSendDataMessagePrivate::DnDHGSendDataMessagePrivate(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser)
240 : m_cbSize(paParms[4].u.uint32)
241 , m_cbDone(0)
242 , m_pfnProgressCallback(pfnProgressCallback)
243 , m_pvProgressUser(pvProgressUser)
244{
245 /* Create the initial data message. */
246 m_pNextMsg = new HGCM::Message(uMsg, cParms, paParms);
247}
248
249int DnDHGSendDataMessagePrivate::currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
250{
251 /* Todo: don't copy the data parts ... just move the data pointer in
252 * the original data ptr. */
253 if (!m_pNextMsg)
254 return VERR_NO_DATA;
255
256 int rc = VINF_SUCCESS;
257
258 HGCM::Message *pCurMsg = m_pNextMsg;
259 AssertPtr(pCurMsg);
260
261 m_pNextMsg = 0;
262 rc = pCurMsg->getData(uMsg, cParms, paParms);
263 /* Depending on the current message, the data pointer is on a
264 * different position (HOST_DND_HG_SND_DATA=3;
265 * HOST_DND_HG_SND_MORE_DATA=0). */
266 int iPos = uMsg == DragAndDropSvc::HOST_DND_HG_SND_DATA ? 3 : 0;
267 m_cbDone += paParms[iPos + 1].u.uint32;
268 /* Info & data send already? */
269 if (rc == VERR_BUFFER_OVERFLOW)
270 {
271 paParms[iPos + 1].u.uint32 = paParms[iPos].u.pointer.size;
272 VBOXHGCMSVCPARM paTmpParms[2];
273 void *pvOldData;
274 uint32_t cOldData;
275 pCurMsg->getParmPtrInfo(iPos, &pvOldData, &cOldData);
276 paTmpParms[0].setPointer(static_cast<uint8_t*>(pvOldData) + paParms[iPos].u.pointer.size, cOldData - paParms[iPos].u.pointer.size);
277 paTmpParms[1].setUInt32(cOldData - paParms[iPos].u.pointer.size);
278
279 try
280 {
281 m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_MORE_DATA, 2, paTmpParms);
282 }
283 catch(std::bad_alloc &)
284 {
285 rc = VERR_NO_MEMORY;
286 }
287 }
288
289 delete pCurMsg;
290
291 /* Advance progress info */
292 if ( RT_SUCCESS(rc)
293 && m_pfnProgressCallback)
294 rc = m_pfnProgressCallback(m_cbDone, m_pvProgressUser);
295
296 return rc;
297}
298
299/******************************************************************************
300 * DnDHGSendDataMessage *
301 ******************************************************************************/
302
303/*
304 * This class is a meta message class. It doesn't consist of any own message
305 * data, but handle the meta info, the data itself as well any files or
306 * directories which have to be transfered to the guest.
307 */
308DnDHGSendDataMessage::DnDHGSendDataMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], PFNDNDPROGRESS pfnProgressCallback, void *pvProgressUser)
309 : m_cbAll(0)
310 , m_cbTransfered(0)
311 , m_pfnProgressCallback(pfnProgressCallback)
312 , m_pvProgressUser(pvProgressUser)
313{
314 RTCString strNewUris;
315 /* Check the format for any uri type. */
316 if (hasFileUrls(static_cast<const char*>(paParms[1].u.pointer.addr), paParms[1].u.pointer.size))
317 {
318 LogFlowFunc(("Old data: '%s'\n", (char*)paParms[3].u.pointer.addr));
319 /* The list is separated by newline (Even if only one file is
320 * listed). */
321 RTCList<RTCString> oldUriList = RTCString(static_cast<const char*>(paParms[3].u.pointer.addr),
322 paParms[3].u.pointer.size).split("\r\n");
323 if (!oldUriList.isEmpty())
324 {
325 RTCList<RTCString> newUriList;
326 for (size_t i = 0; i < oldUriList.size(); ++i)
327 {
328 const RTCString &strUri = oldUriList.at(i);
329 /* Query the path component of a file URI. If this hasn't a
330 * file scheme null is returned. */
331 if (char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO))
332 {
333 /* Add the path to our internal file list (recursive in
334 * the case of a directory). */
335 if (char *pszFilename = RTPathFilename(pszFilePath))
336 {
337 char *pszNewUri = RTUriFileCreate(pszFilename);
338 if (pszNewUri)
339 {
340 newUriList.append(pszNewUri);
341 RTStrFree(pszNewUri);
342 buildFileTree(pszFilePath, pszFilename - pszFilePath);
343 }
344 }
345 RTStrFree(pszFilePath);
346 }
347 else
348 newUriList.append(strUri);
349 }
350 /* We have to change the actual DnD data. Remove any host paths and
351 * just decode the filename into the new data. The guest tools will
352 * add the correct path again, before sending the DnD drop event to
353 * some window. */
354 strNewUris = RTCString::join(newUriList, "\r\n") + "\r\n";
355 /* Remark: We don't delete the old pointer here, cause this is done
356 * by the caller. We just use the RTString data, which has the
357 * scope of this ctor. This is enough cause the data is copied in
358 * the DnDHGSendDataMessagePrivate anyway. */
359 paParms[3].u.pointer.addr = (void*)strNewUris.c_str();
360 paParms[3].u.pointer.size = strNewUris.length() + 1;
361 paParms[4].u.uint32 = strNewUris.length() + 1;
362 }
363 }
364 /* Add the size of the data to the todo list. */
365 m_cbAll += paParms[4].u.uint32;
366 /* The first message is the meta info for the data and the data itself. */
367 m_pNextPathMsg = new DnDHGSendDataMessagePrivate(uMsg, cParms, paParms,
368 &DnDHGSendDataMessage::progressCallback, this);
369
370 LogFlowFunc(("new data '%s'\n", (char*)paParms[3].u.pointer.addr));
371 LogFlowFunc(("cbAll: %zu\n", m_cbAll));
372 LogFlowFunc(("cbData: %RU32\n", paParms[4].u.uint32));
373
374 for (size_t i = 0; i < m_uriList.size(); ++i)
375 LogFlowFunc(("file: %s : %s - %o - %ld\n",
376 m_uriList.at(i).m_strHostPath.c_str(), m_uriList.at(i).m_strGuestPath.c_str(),
377 m_uriList.at(i).m_fMode, m_uriList.at(i).m_cbSize));
378}
379
380DnDHGSendDataMessage::~DnDHGSendDataMessage()
381{
382 if (m_pNextPathMsg)
383 delete m_pNextPathMsg;
384}
385
386HGCM::Message* DnDHGSendDataMessage::nextHGCMMessage()
387{
388 if (!m_pNextPathMsg)
389 return NULL;
390
391 return m_pNextPathMsg->nextHGCMMessage();
392}
393
394int DnDHGSendDataMessage::currentMessageInfo(uint32_t *puMsg, uint32_t *pcParms)
395{
396 if (!m_pNextPathMsg)
397 return VERR_NO_DATA;
398
399 return m_pNextPathMsg->currentMessageInfo(puMsg, pcParms);
400}
401
402int DnDHGSendDataMessage::currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
403{
404 if (!m_pNextPathMsg)
405 return VERR_NO_DATA;
406
407 /* Fill the data out of our current queued message. */
408 int rc = m_pNextPathMsg->currentMessage(uMsg, cParms, paParms);
409 /* Has this message more data to deliver? */
410 if (!m_pNextPathMsg->isMessageWaiting())
411 {
412 delete m_pNextPathMsg;
413 m_pNextPathMsg = NULL;
414 }
415
416 /* File data to send? */
417 if (!m_pNextPathMsg)
418 {
419 if (m_uriList.isEmpty())
420 return rc;
421 /* Create new messages based on our internal path list. Currently
422 * this could be directories or regular files. */
423 PathEntry nextPath = m_uriList.first();
424 try
425 {
426 if (RTFS_IS_DIRECTORY(nextPath.m_fMode))
427 m_pNextPathMsg = new DnDHGSendDirPrivate(nextPath.m_strGuestPath,
428 nextPath.m_fMode, nextPath.m_cbSize,
429 &DnDHGSendDataMessage::progressCallback, this);
430 else if (RTFS_IS_FILE(nextPath.m_fMode))
431 m_pNextPathMsg = new DnDHGSendFilePrivate(nextPath.m_strHostPath, nextPath.m_strGuestPath,
432 nextPath.m_fMode, nextPath.m_cbSize,
433 &DnDHGSendDataMessage::progressCallback, this);
434 else
435 AssertMsgFailedReturn(("type '%d' is not supported for path '%s'",
436 nextPath.m_fMode, nextPath.m_strHostPath.c_str()), VERR_NO_DATA);
437
438 m_uriList.removeFirst();
439 }
440 catch(std::bad_alloc &)
441 {
442 rc = VERR_NO_MEMORY;
443 }
444 }
445
446 return rc;
447}
448
449bool DnDHGSendDataMessage::hasFileUrls(const char *pcszFormat, size_t cbMax) const
450{
451 LogFlowFunc(("format %s\n", pcszFormat));
452 /** @todo text/uri also an official variant? */
453 return RTStrNICmp(pcszFormat, "text/uri-list", cbMax) == 0
454 || RTStrNICmp(pcszFormat, "x-special/gnome-icon-list", cbMax) == 0;
455}
456
457int DnDHGSendDataMessage::buildFileTree(const char *pcszPath, size_t cbBaseLen)
458{
459 RTFSOBJINFO objInfo;
460 int rc = RTPathQueryInfo(pcszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
461 if (RT_FAILURE(rc))
462 return rc;
463
464 /* These are the types we currently support. Symlinks are not directly
465 * supported. First the guest could be an OS which doesn't support it and
466 * second the symlink could point to a file which is out of the base tree.
467 * Both things are hard to support. For now we just copy the target file in
468 * this case. */
469 if (!( RTFS_IS_DIRECTORY(objInfo.Attr.fMode)
470 || RTFS_IS_FILE(objInfo.Attr.fMode)
471 || RTFS_IS_SYMLINK(objInfo.Attr.fMode)))
472 return VINF_SUCCESS;
473
474 uint64_t cbSize = 0;
475 rc = RTFileQuerySize(pcszPath, &cbSize);
476 if (rc == VERR_IS_A_DIRECTORY)
477 rc = VINF_SUCCESS;
478 if (RT_FAILURE(rc))
479 return rc;
480 m_uriList.append(PathEntry(pcszPath, &pcszPath[cbBaseLen], objInfo.Attr.fMode, cbSize));
481 m_cbAll += cbSize;
482 LogFlowFunc(("cbFile: %RU64\n", cbSize));
483
484 PRTDIR hDir;
485 /* We have to try to open even symlinks, cause they could be symlinks
486 * to directories. */
487 rc = RTDirOpen(&hDir, pcszPath);
488 /* The following error happens when this was a symlink to an file or a
489 * regular file. */
490 if (rc == VERR_PATH_NOT_FOUND)
491 return VINF_SUCCESS;
492 if (RT_FAILURE(rc))
493 return rc;
494
495 while (RT_SUCCESS(rc))
496 {
497 RTDIRENTRY DirEntry;
498 rc = RTDirRead(hDir, &DirEntry, NULL);
499 if (RT_FAILURE(rc))
500 {
501 if (rc == VERR_NO_MORE_FILES)
502 rc = VINF_SUCCESS;
503 break;
504 }
505 switch (DirEntry.enmType)
506 {
507 case RTDIRENTRYTYPE_DIRECTORY:
508 {
509 /* Skip "." and ".." entries. */
510 if ( RTStrCmp(DirEntry.szName, ".") == 0
511 || RTStrCmp(DirEntry.szName, "..") == 0)
512 break;
513 if (char *pszRecDir = RTStrAPrintf2("%s%c%s", pcszPath, RTPATH_DELIMITER, DirEntry.szName))
514 {
515 rc = buildFileTree(pszRecDir, cbBaseLen);
516 RTStrFree(pszRecDir);
517 }
518 else
519 rc = VERR_NO_MEMORY;
520 break;
521 }
522 case RTDIRENTRYTYPE_SYMLINK:
523 case RTDIRENTRYTYPE_FILE:
524 {
525 if (char *pszNewFile = RTStrAPrintf2("%s%c%s", pcszPath, RTPATH_DELIMITER, DirEntry.szName))
526 {
527 /* We need the size and the mode of the file. */
528 RTFSOBJINFO objInfo1;
529 rc = RTPathQueryInfo(pszNewFile, &objInfo1, RTFSOBJATTRADD_NOTHING);
530 if (RT_FAILURE(rc))
531 return rc;
532 rc = RTFileQuerySize(pszNewFile, &cbSize);
533 if (RT_FAILURE(rc))
534 break;
535 m_uriList.append(PathEntry(pszNewFile, &pszNewFile[cbBaseLen], objInfo1.Attr.fMode, cbSize));
536 m_cbAll += cbSize;
537 RTStrFree(pszNewFile);
538 }
539 else
540 rc = VERR_NO_MEMORY;
541 break;
542 }
543 default: break;
544 }
545 }
546 RTDirClose(hDir);
547
548 return rc;
549}
550
551int DnDHGSendDataMessage::progressCallback(size_t cbDone, void *pvUser)
552{
553 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
554
555 DnDHGSendDataMessage *pSelf = static_cast<DnDHGSendDataMessage *>(pvUser);
556
557 /* How many bytes are transfered already. */
558 pSelf->m_cbTransfered += cbDone;
559
560 /* Advance progress info */
561 int rc = VINF_SUCCESS;
562 if ( pSelf->m_pfnProgressCallback
563 && pSelf->m_cbAll)
564 rc = pSelf->m_pfnProgressCallback((uint64_t)pSelf->m_cbTransfered * 100 / pSelf->m_cbAll,
565 DragAndDropSvc::DND_PROGRESS_RUNNING, VINF_SUCCESS /* rc */, pSelf->m_pvProgressUser);
566
567 return rc;
568}
569
570/******************************************************************************
571 * DnDManager *
572 ******************************************************************************/
573
574int DnDManager::addMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
575{
576 int rc = VINF_SUCCESS;
577
578 try
579 {
580 switch (uMsg)
581 {
582 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
583 {
584 clear();
585 LogFlowFunc(("HOST_DND_HG_EVT_ENTER\n"));
586
587 /* Verify parameter count and types. */
588 if ( cParms != 7
589 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* screen id */
590 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* x-pos */
591 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* y-pos */
592 || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* default action */
593 || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* allowed actions */
594 || paParms[5].type != VBOX_HGCM_SVC_PARM_PTR /* data */
595 || paParms[6].type != VBOX_HGCM_SVC_PARM_32BIT /* size */)
596 rc = VERR_INVALID_PARAMETER;
597 else
598 {
599 m_fOpInProcess = true;
600 DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms);
601 m_dndMessageQueue.append(pMessage);
602 }
603 break;
604 }
605 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
606 {
607 LogFlowFunc(("HOST_DND_HG_EVT_MOVE\n"));
608
609 /* Verify parameter count and types. */
610 if ( cParms != 7
611 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* screen id */
612 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* x-pos */
613 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* y-pos */
614 || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* default action */
615 || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* allowed actions */
616 || paParms[5].type != VBOX_HGCM_SVC_PARM_PTR /* data */
617 || paParms[6].type != VBOX_HGCM_SVC_PARM_32BIT /* size */)
618 rc = VERR_INVALID_PARAMETER;
619 else
620 {
621 m_fOpInProcess = true;
622 DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms);
623 m_dndMessageQueue.append(pMessage);
624 }
625 break;
626 }
627 case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE:
628 {
629 LogFlowFunc(("HOST_DND_HG_EVT_LEAVE\n"));
630
631 /* Verify parameter count and types. */
632 if (cParms != 0)
633 rc = VERR_INVALID_PARAMETER;
634 else
635 {
636 DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms);
637 m_dndMessageQueue.append(pMessage);
638 }
639 m_fOpInProcess = false;
640 break;
641 }
642 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
643 {
644 LogFlowFunc(("HOST_DND_HG_EVT_DROPPED\n"));
645
646 /* Verify parameter count and types. */
647 if ( cParms != 7
648 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* screen id */
649 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* x-pos */
650 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* y-pos */
651 || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* default action */
652 || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* allowed actions */
653 || paParms[5].type != VBOX_HGCM_SVC_PARM_PTR /* data */
654 || paParms[6].type != VBOX_HGCM_SVC_PARM_32BIT /* size */)
655 rc = VERR_INVALID_PARAMETER;
656 else
657 {
658 DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms);
659 m_dndMessageQueue.append(pMessage);
660 }
661 break;
662 }
663 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
664 {
665 LogFlowFunc(("HOST_DND_HG_SND_DATA\n"));
666
667 /* Verify parameter count and types. */
668 if ( cParms != 5
669 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* screen id */
670 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* format */
671 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* format size */
672 || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* data */
673 || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* data size */)
674 rc = VERR_INVALID_PARAMETER;
675 else
676 {
677 DnDHGSendDataMessage *pMessage = new DnDHGSendDataMessage(uMsg, cParms, paParms, m_pfnProgressCallback, m_pvProgressUser);
678 m_dndMessageQueue.append(pMessage);
679 }
680 break;
681 }
682 #ifdef VBOX_WITH_DRAG_AND_DROP_GH
683 case DragAndDropSvc::HOST_DND_GH_REQ_PENDING:
684 {
685 LogFlowFunc(("HOST_DND_GH_REQ_PENDING\n"));
686
687 /* Verify parameter count and types. */
688 if ( cParms != 1
689 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* screen id */)
690 rc = VERR_INVALID_PARAMETER;
691 else
692 {
693 DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms);
694 m_dndMessageQueue.append(pMessage);
695 }
696 break;
697 }
698 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
699 {
700 LogFlowFunc(("HOST_DND_GH_EVT_DROPPED\n"));
701
702 /* Verify parameter count and types. */
703 if ( cParms != 3
704 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* format */
705 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* format size */
706 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* action */)
707 rc = VERR_INVALID_PARAMETER;
708 else
709 {
710 try
711 {
712 DnDGenericMessage *pMessage
713 = new DnDGenericMessage(uMsg, cParms, paParms);
714 m_dndMessageQueue.append(pMessage);
715 }
716 catch(std::bad_alloc &)
717 {
718 rc = VERR_NO_MEMORY;
719 }
720 }
721 break;
722 }
723 #endif
724 default:
725 rc = VERR_NOT_IMPLEMENTED;
726 break;
727 }
728 }
729 catch(std::bad_alloc &)
730 {
731 rc = VERR_NO_MEMORY;
732 }
733
734 return rc;
735}
736
737HGCM::Message* DnDManager::nextHGCMMessage()
738{
739 if (m_pCurMsg)
740 return m_pCurMsg->nextHGCMMessage();
741 else
742 {
743 if (m_dndMessageQueue.isEmpty())
744 return 0;
745
746 return m_dndMessageQueue.first()->nextHGCMMessage();
747 }
748}
749
750int DnDManager::nextMessageInfo(uint32_t *puMsg, uint32_t *pcParms)
751{
752 AssertPtrReturn(puMsg, VERR_INVALID_POINTER);
753 AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
754
755 int rc = VINF_SUCCESS;
756
757 if (m_pCurMsg)
758 rc = m_pCurMsg->currentMessageInfo(puMsg, pcParms);
759 else
760 {
761 if (m_dndMessageQueue.isEmpty())
762 {
763 rc = VERR_NO_DATA;
764// if (m_pfnProgressCallback)
765// m_pfnProgressCallback(100.0, DragAndDropSvc::DND_OP_CANCELLED, m_pvProgressUser);
766 }
767 else
768 rc = m_dndMessageQueue.first()->currentMessageInfo(puMsg, pcParms);
769 }
770
771 LogFlowFunc(("Returning puMsg=%RU32, pcParms=%RU32, rc=%Rrc\n", *puMsg, *pcParms, rc));
772 return rc;
773}
774
775int DnDManager::nextMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
776{
777 LogFlowFunc(("uMsg=%RU32, cParms=%RU32\n", uMsg, cParms));
778
779 if (!m_pCurMsg)
780 {
781 /* Check for pending messages in our queue. */
782 if (m_dndMessageQueue.isEmpty())
783 {
784 LogFlowFunc(("Message queue is empty, returning\n"));
785 return VERR_NO_DATA;
786 }
787
788 m_pCurMsg = m_dndMessageQueue.first();
789 m_dndMessageQueue.removeFirst();
790 }
791
792 /* Fetch the current message info */
793 int rc = m_pCurMsg->currentMessage(uMsg, cParms, paParms);
794 /* If this message doesn't provide any additional sub messages, clear it. */
795 if (!m_pCurMsg->isMessageWaiting())
796 {
797 delete m_pCurMsg;
798 m_pCurMsg = NULL;
799 }
800
801 /*
802 * If there was an error handling the current message or the user has canceled
803 * the operation, we need to cleanup all pending events and inform the progress
804 * callback about our exit.
805 */
806 if ( RT_FAILURE(rc)
807 && m_pfnProgressCallback)
808 {
809 /* Clear any pending messages. */
810 clear();
811
812 /* Create a new cancel message to inform the guest + call
813 * the host whether the current transfer was canceled or aborted
814 * due to an error. */
815 try
816 {
817 Assert(!m_pCurMsg);
818 m_pCurMsg = new DnDHGCancelMessage();
819 m_pfnProgressCallback(100 /* Percent */,
820 rc == VERR_CANCELLED
821 ? DragAndDropSvc::DND_PROGRESS_CANCELLED
822 : DragAndDropSvc::DND_PROGRESS_ERROR, rc, m_pvProgressUser);
823 }
824 catch(std::bad_alloc &)
825 {
826 rc = VERR_NO_MEMORY;
827 }
828 }
829
830 LogFlowFunc(("Message processed with rc=%Rrc\n", rc));
831 return rc;
832}
833
834void DnDManager::clear()
835{
836 if (m_pCurMsg)
837 {
838 delete m_pCurMsg;
839 m_pCurMsg = 0;
840 }
841 while (!m_dndMessageQueue.isEmpty())
842 {
843 delete m_dndMessageQueue.last();
844 m_dndMessageQueue.removeLast();
845 }
846}
847
Note: See TracBrowser for help on using the repository browser.

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