VirtualBox

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

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

DnD/HostServices: A bit of cleanup.

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