/* $Id: dndmanager.cpp 44102 2012-12-12 09:36:49Z vboxsync $ */ /** @file * Drag and Drop manager. */ /* * Copyright (C) 2011-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /****************************************************************************** * Header Files * ******************************************************************************/ #define LOG_GROUP LOG_GROUP_HGCM #include "dndmanager.h" #include #include #include #include #include #define VERBOSE 1 #if defined(VERBOSE) && defined(DEBUG_poetzsch) # include # define DO(s) RTPrintf s #else # define DO(s) do {} while(0) //# define DO(s) Log s #endif /****************************************************************************** * Private declarations * ******************************************************************************/ typedef DECLCALLBACK(int) FNDNDPRIVATEPROGRESS(size_t cbDone, void *pvUser); typedef FNDNDPRIVATEPROGRESS *PFNDNDPRIVATEPROGRESS; /** * Internal DnD message class for informing the guest about a new directory. * * @see DnDHGSendDataMessage */ class DnDHGSendDirPrivate: public DnDMessage { public: DnDHGSendDirPrivate(const RTCString &strPath, uint32_t fMode, uint64_t cbSize, PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser) : m_strPath(strPath) , m_cbSize(cbSize) , m_pfnProgressCallback(pfnProgressCallback) , m_pvProgressUser(pvProgressUser) { VBOXHGCMSVCPARM paTmpParms[3]; paTmpParms[0].setString(m_strPath.c_str()); paTmpParms[1].setUInt32(m_strPath.length() + 1); paTmpParms[2].setUInt32(fMode); m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_DIR, 3, paTmpParms); } int currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { int rc = DnDMessage::currentMessage(uMsg, cParms, paParms); /* Advance progress info */ if ( RT_SUCCESS(rc) && m_pfnProgressCallback) rc = m_pfnProgressCallback(m_cbSize, m_pvProgressUser); return rc; } protected: RTCString m_strPath; /* Progress stuff */ size_t m_cbSize; PFNDNDPRIVATEPROGRESS m_pfnProgressCallback; void *m_pvProgressUser; }; /** * Internal DnD message class for informing the guest about a new file. * * @see DnDHGSendDataMessage */ class DnDHGSendFilePrivate: public DnDMessage { public: DnDHGSendFilePrivate(const RTCString &strHostPath, const RTCString &strGuestPath, uint32_t fMode, uint64_t cbSize, PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser); ~DnDHGSendFilePrivate(); int currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); protected: RTCString m_strHostPath; RTCString m_strGuestPath; uint64_t m_cbSize; uint64_t m_cbDone; RTFILE m_hCurFile; VBOXHGCMSVCPARM m_paSkelParms[5]; /* Progress stuff */ PFNDNDPRIVATEPROGRESS m_pfnProgressCallback; void *m_pvProgressUser; }; /** * Internal DnD message class for informing the guest about new drag & drop * data. * * @see DnDHGSendDataMessage */ class DnDHGSendDataMessagePrivate: public DnDMessage { public: DnDHGSendDataMessagePrivate(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser); int currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); protected: size_t m_cbSize; size_t m_cbDone; /* Progress stuff */ PFNDNDPRIVATEPROGRESS m_pfnProgressCallback; void *m_pvProgressUser; }; /****************************************************************************** * Implementation * ******************************************************************************/ /****************************************************************************** * DnDHGSendFilePrivate * ******************************************************************************/ DnDHGSendFilePrivate::DnDHGSendFilePrivate(const RTCString &strHostPath, const RTCString &strGuestPath, uint32_t fMode, uint64_t cbSize, PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser) : m_strHostPath(strHostPath) , m_strGuestPath(strGuestPath) , m_cbSize(cbSize) , m_cbDone(0) , m_hCurFile(0) , m_pfnProgressCallback(pfnProgressCallback) , m_pvProgressUser(pvProgressUser) { m_paSkelParms[0].setString(m_strGuestPath.c_str()); m_paSkelParms[1].setUInt32(m_strGuestPath.length() + 1); m_paSkelParms[2].setPointer(NULL, 0); m_paSkelParms[3].setUInt32(0); m_paSkelParms[4].setUInt32(fMode); m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_FILE, 5, m_paSkelParms); } DnDHGSendFilePrivate::~DnDHGSendFilePrivate() { if (m_hCurFile) RTFileClose(m_hCurFile); } int DnDHGSendFilePrivate::currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { if (!m_pNextMsg) return VERR_NO_DATA; int rc = m_pNextMsg->getData(uMsg, cParms, paParms); clearNextMsg(); if (RT_FAILURE(rc)) return rc; if (!m_hCurFile) { rc = RTFileOpen(&m_hCurFile, m_strHostPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_ALL); if (RT_FAILURE(rc)) return rc; } /* How big is the pointer provided by the guest? */ uint32_t cbToRead = paParms[2].u.pointer.size; size_t cbRead; rc = RTFileRead(m_hCurFile, paParms[2].u.pointer.addr, cbToRead, &cbRead); if (RT_FAILURE(rc)) { /* On error, immediately close the file. */ RTFileClose(m_hCurFile); m_hCurFile = 0; return rc; } m_cbDone += cbRead; /* Tell the guest the actual size. */ paParms[3].setUInt32(cbRead); /* Check if we are done. */ if (m_cbSize == m_cbDone) { RTFileClose(m_hCurFile); m_hCurFile = 0; } else { /* More data! Prepare the next message. */ m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_FILE, 5, m_paSkelParms); } /* Advance progress info */ if ( RT_SUCCESS(rc) && m_pfnProgressCallback) rc = m_pfnProgressCallback(cbRead, m_pvProgressUser); return rc; } /****************************************************************************** * DnDHGSendDataMessagePrivate * ******************************************************************************/ DnDHGSendDataMessagePrivate::DnDHGSendDataMessagePrivate(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser) : m_cbSize(paParms[4].u.uint32) , m_cbDone(0) , m_pfnProgressCallback(pfnProgressCallback) , m_pvProgressUser(pvProgressUser) { /* Create the initial data message. */ m_pNextMsg = new HGCM::Message(uMsg, cParms, paParms); } int DnDHGSendDataMessagePrivate::currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { /* Todo: don't copy the data parts ... just move the data pointer in * the original data ptr. */ if (!m_pNextMsg) return VERR_NO_DATA; int rc = VINF_SUCCESS; HGCM::Message *pCurMsg = m_pNextMsg; m_pNextMsg = 0; rc = pCurMsg->getData(uMsg, cParms, paParms); /* Depending on the current message, the data pointer is on a * different position (HOST_DND_HG_SND_DATA=3; * HOST_DND_HG_SND_MORE_DATA=0). */ int iPos = uMsg == DragAndDropSvc::HOST_DND_HG_SND_DATA ? 3 : 0; m_cbDone += paParms[iPos + 1].u.uint32; /* Info & data send already? */ if (rc == VERR_BUFFER_OVERFLOW) { paParms[iPos + 1].u.uint32 = paParms[iPos].u.pointer.size; VBOXHGCMSVCPARM paTmpParms[2]; void *pvOldData; uint32_t cOldData; pCurMsg->getParmPtrInfo(iPos, &pvOldData, &cOldData); paTmpParms[0].setPointer(static_cast(pvOldData) + paParms[iPos].u.pointer.size, cOldData - paParms[iPos].u.pointer.size); paTmpParms[1].setUInt32(cOldData - paParms[iPos].u.pointer.size); m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_MORE_DATA, 2, paTmpParms); } delete pCurMsg; /* Advance progress info */ if ( RT_SUCCESS(rc) && m_pfnProgressCallback) rc = m_pfnProgressCallback(m_cbDone, m_pvProgressUser); return rc; } /****************************************************************************** * DnDHGSendDataMessage * ******************************************************************************/ /* * This class is a meta message class. It doesn't consist of any own message * data, but handle the meta info, the data itself as well any files or * directories which have to be transfered to the guest. */ DnDHGSendDataMessage::DnDHGSendDataMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], PFNDNDPROGRESS pfnProgressCallback, void *pvProgressUser) : m_cbAll(0) , m_cbTransfered(0) , m_pfnProgressCallback(pfnProgressCallback) , m_pvProgressUser(pvProgressUser) { RTCString strNewUris; /* Check the format for any uri type. */ if (hasFileUrls(static_cast(paParms[1].u.pointer.addr), paParms[1].u.pointer.size)) { DO(("old data '%s'\n", (char*)paParms[3].u.pointer.addr)); /* The list is separated by newline (Even if only one file is * listed). */ RTCList oldUriList = RTCString(static_cast(paParms[3].u.pointer.addr), paParms[3].u.pointer.size).split("\r\n"); if (!oldUriList.isEmpty()) { RTCList newUriList; for (size_t i = 0; i < oldUriList.size(); ++i) { const RTCString &strUri = oldUriList.at(i); /* Query the path component of a file URI. If this hasn't a * file scheme null is returned. */ if (char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO)) { /* Add the path to our internal file list (recursive in * the case of a directory). */ if (char *pszFilename = RTPathFilename(pszFilePath)) { char *pszNewUri = RTUriFileCreate(pszFilename); if (pszNewUri) { newUriList.append(pszNewUri); RTStrFree(pszNewUri); buildFileTree(pszFilePath, pszFilename - pszFilePath); } } RTStrFree(pszFilePath); } else newUriList.append(strUri); } /* We have to change the actual DnD data. Remove any host paths and * just decode the filename into the new data. The guest tools will * add the correct path again, before sending the DnD drop event to * some window. */ strNewUris = RTCString::join(newUriList, "\r\n") + "\r\n"; /* Remark: We don't delete the old pointer here, cause this is done * by the caller. We just use the RTString data, which has the * scope of this ctor. This is enough cause the data is copied in * the DnDHGSendDataMessagePrivate anyway. */ paParms[3].u.pointer.addr = (void*)strNewUris.c_str(); paParms[3].u.pointer.size = strNewUris.length() + 1; paParms[4].u.uint32 = strNewUris.length() + 1; } } /* Add the size of the data to the todo list. */ m_cbAll += paParms[4].u.uint32; /* The first message is the meta info for the data and the data itself. */ m_pNextPathMsg = new DnDHGSendDataMessagePrivate(uMsg, cParms, paParms, &DnDHGSendDataMessage::progressCallback, this); DO(("new data '%s'\n", (char*)paParms[3].u.pointer.addr)); DO(("cbAll: %u\n", m_cbAll)); DO(("cbData: %u\n", paParms[4].u.uint32)); for (size_t i = 0; i < m_uriList.size(); ++i) DO(("file: %s : %s - %o - %ld\n", m_uriList.at(i).m_strHostPath.c_str(), m_uriList.at(i).m_strGuestPath.c_str(), m_uriList.at(i).m_fMode, m_uriList.at(i).m_cbSize)); } DnDHGSendDataMessage::~DnDHGSendDataMessage() { if (m_pNextPathMsg) delete m_pNextPathMsg; } HGCM::Message* DnDHGSendDataMessage::nextHGCMMessage() { if (!m_pNextPathMsg) return NULL; return m_pNextPathMsg->nextHGCMMessage(); } int DnDHGSendDataMessage::currentMessageInfo(uint32_t *puMsg, uint32_t *pcParms) { if (!m_pNextPathMsg) return VERR_NO_DATA; return m_pNextPathMsg->currentMessageInfo(puMsg, pcParms); } int DnDHGSendDataMessage::currentMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { if (!m_pNextPathMsg) return VERR_NO_DATA; /* Fill the data out of our current queued message. */ int rc = m_pNextPathMsg->currentMessage(uMsg, cParms, paParms); /* Has this message more data to deliver? */ if (!m_pNextPathMsg->isMessageWaiting()) { delete m_pNextPathMsg; m_pNextPathMsg = NULL; } /* File data to send? */ if (!m_pNextPathMsg) { if (m_uriList.isEmpty()) return rc; /* Create new messages based on our internal path list. Currently * this could be directories or regular files. */ PathEntry nextPath = m_uriList.first(); if (RTFS_IS_DIRECTORY(nextPath.m_fMode)) m_pNextPathMsg = new DnDHGSendDirPrivate(nextPath.m_strGuestPath, nextPath.m_fMode, nextPath.m_cbSize, &DnDHGSendDataMessage::progressCallback, this); else if (RTFS_IS_FILE(nextPath.m_fMode)) m_pNextPathMsg = new DnDHGSendFilePrivate(nextPath.m_strHostPath, nextPath.m_strGuestPath, nextPath.m_fMode, nextPath.m_cbSize, &DnDHGSendDataMessage::progressCallback, this); else AssertMsgFailedReturn(("type '%d' is not supported for path '%s'", nextPath.m_fMode, nextPath.m_strHostPath.c_str()), VERR_NO_DATA); m_uriList.removeFirst(); } return rc; } bool DnDHGSendDataMessage::hasFileUrls(const char *pcszFormat, size_t cbMax) const { DO(("format %s\n", pcszFormat)); /* text/uri also an official variant? */ return RTStrNICmp(pcszFormat, "text/uri-list", cbMax) == 0 || RTStrNICmp(pcszFormat, "x-special/gnome-icon-list", cbMax) == 0; } int DnDHGSendDataMessage::buildFileTree(const char *pcszPath, size_t cbBaseLen) { RTFSOBJINFO objInfo; int rc = RTPathQueryInfo(pcszPath, &objInfo, RTFSOBJATTRADD_NOTHING); if (RT_FAILURE(rc)) return rc; /* These are the types we currently support. Symlinks are not directly * supported. First the guest could be an OS which doesn't support it and * second the symlink could point to a file which is out of the base tree. * Both things are hard to support. For now we just copy the target file in * this case. */ if (!( RTFS_IS_DIRECTORY(objInfo.Attr.fMode) || RTFS_IS_FILE(objInfo.Attr.fMode) || RTFS_IS_SYMLINK(objInfo.Attr.fMode))) return VINF_SUCCESS; uint64_t cbSize = 0; rc = RTFileQuerySize(pcszPath, &cbSize); if (rc == VERR_IS_A_DIRECTORY) rc = VINF_SUCCESS; if (RT_FAILURE(rc)) return rc; m_uriList.append(PathEntry(pcszPath, &pcszPath[cbBaseLen], objInfo.Attr.fMode, cbSize)); m_cbAll += cbSize; DO(("cbFile: %u\n", cbSize)); PRTDIR hDir; /* We have to try to open even symlinks, cause they could be symlinks * to directories. */ rc = RTDirOpen(&hDir, pcszPath); /* The following error happens when this was a symlink to an file or a * regular file. */ if (rc == VERR_PATH_NOT_FOUND) return VINF_SUCCESS; if (RT_FAILURE(rc)) return rc; while (RT_SUCCESS(rc)) { RTDIRENTRY DirEntry; rc = RTDirRead(hDir, &DirEntry, NULL); if (RT_FAILURE(rc)) { if (rc == VERR_NO_MORE_FILES) rc = VINF_SUCCESS; break; } switch (DirEntry.enmType) { case RTDIRENTRYTYPE_DIRECTORY: { /* Skip "." and ".." entries. */ if ( RTStrCmp(DirEntry.szName, ".") == 0 || RTStrCmp(DirEntry.szName, "..") == 0) break; if (char *pszRecDir = RTStrAPrintf2("%s%c%s", pcszPath, RTPATH_DELIMITER, DirEntry.szName)) { rc = buildFileTree(pszRecDir, cbBaseLen); RTStrFree(pszRecDir); } else rc = VERR_NO_MEMORY; break; } case RTDIRENTRYTYPE_SYMLINK: case RTDIRENTRYTYPE_FILE: { if (char *pszNewFile = RTStrAPrintf2("%s%c%s", pcszPath, RTPATH_DELIMITER, DirEntry.szName)) { /* We need the size and the mode of the file. */ RTFSOBJINFO objInfo1; rc = RTPathQueryInfo(pszNewFile, &objInfo1, RTFSOBJATTRADD_NOTHING); if (RT_FAILURE(rc)) return rc; rc = RTFileQuerySize(pszNewFile, &cbSize); if (RT_FAILURE(rc)) break; m_uriList.append(PathEntry(pszNewFile, &pszNewFile[cbBaseLen], objInfo1.Attr.fMode, cbSize)); m_cbAll += cbSize; RTStrFree(pszNewFile); } else rc = VERR_NO_MEMORY; break; } default: break; } } RTDirClose(hDir); return rc; } int DnDHGSendDataMessage::progressCallback(size_t cbDone, void *pvUser) { AssertPtrReturn(pvUser, VERR_INVALID_POINTER); DnDHGSendDataMessage *pSelf = static_cast(pvUser); /* How many bytes are transfered already. */ pSelf->m_cbTransfered += cbDone; /* Advance progress info */ int rc = VINF_SUCCESS; if ( pSelf->m_pfnProgressCallback && pSelf->m_cbAll) rc = pSelf->m_pfnProgressCallback((uint64_t)pSelf->m_cbTransfered * 100 / pSelf->m_cbAll, DragAndDropSvc::DND_PROGRESS_RUNNING, pSelf->m_pvProgressUser); return rc; } /****************************************************************************** * DnDManager * ******************************************************************************/ int DnDManager::addMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { int rc = VINF_SUCCESS; switch (uMsg) { case DragAndDropSvc::HOST_DND_HG_EVT_ENTER: { clear(); LogFlowFunc(("HOST_DND_HG_EVT_ENTER\n")); DO(("HOST_DND_HG_EVT_ENTER\n")); /* Verify parameter count and types. */ if ( cParms != 7 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* screen id */ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* x-pos */ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* y-pos */ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* default action */ || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* allowed actions */ || paParms[5].type != VBOX_HGCM_SVC_PARM_PTR /* data */ || paParms[6].type != VBOX_HGCM_SVC_PARM_32BIT /* size */) rc = VERR_INVALID_PARAMETER; else { m_fOpInProcess = true; DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms); m_dndMessageQueue.append(pMessage); } break; } case DragAndDropSvc::HOST_DND_HG_EVT_MOVE: { LogFlowFunc(("HOST_DND_HG_EVT_MOVE\n")); DO(("HOST_DND_HG_EVT_MOVE\n")); /* Verify parameter count and types. */ if ( cParms != 7 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* screen id */ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* x-pos */ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* y-pos */ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* default action */ || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* allowed actions */ || paParms[5].type != VBOX_HGCM_SVC_PARM_PTR /* data */ || paParms[6].type != VBOX_HGCM_SVC_PARM_32BIT /* size */) rc = VERR_INVALID_PARAMETER; else { m_fOpInProcess = true; DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms); m_dndMessageQueue.append(pMessage); } break; } case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE: { LogFlowFunc(("HOST_DND_HG_EVT_LEAVE\n")); DO(("HOST_DND_HG_EVT_LEAVE\n")); /* Verify parameter count and types. */ if (cParms != 0) rc = VERR_INVALID_PARAMETER; else { DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms); m_dndMessageQueue.append(pMessage); } m_fOpInProcess = false; break; } case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED: { LogFlowFunc(("HOST_DND_HG_EVT_DROPPED\n")); DO(("HOST_DND_HG_EVT_DROPPED\n")); /* Verify parameter count and types. */ if ( cParms != 7 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* screen id */ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* x-pos */ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* y-pos */ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* default action */ || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* allowed actions */ || paParms[5].type != VBOX_HGCM_SVC_PARM_PTR /* data */ || paParms[6].type != VBOX_HGCM_SVC_PARM_32BIT /* size */) rc = VERR_INVALID_PARAMETER; else { DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms); m_dndMessageQueue.append(pMessage); } break; } case DragAndDropSvc::HOST_DND_HG_SND_DATA: { LogFlowFunc(("HOST_DND_HG_SND_DATA\n")); DO(("HOST_DND_HG_SND_DATA\n")); /* Verify parameter count and types. */ if ( cParms != 5 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* screen id */ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* format */ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* format size */ || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* data */ || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* data size */) rc = VERR_INVALID_PARAMETER; else { DnDHGSendDataMessage *pMessage = new DnDHGSendDataMessage(uMsg, cParms, paParms, m_pfnProgressCallback, m_pvProgressUser); m_dndMessageQueue.append(pMessage); } break; } #ifdef VBOX_WITH_DRAG_AND_DROP_GH case DragAndDropSvc::HOST_DND_GH_REQ_PENDING: { LogFlowFunc(("HOST_DND_GH_REQ_PENDING\n")); DO(("HOST_DND_GH_REQ_PENDING\n")); /* Verify parameter count and types. */ if ( cParms != 1 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* screen id */) rc = VERR_INVALID_PARAMETER; else { DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms); m_dndMessageQueue.append(pMessage); } break; } case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED: { LogFlowFunc(("HOST_DND_GH_EVT_DROPPED\n")); DO(("HOST_DND_GH_EVT_DROPPED\n")); /* Verify parameter count and types. */ if ( cParms != 3 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* format */ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* format size */ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* action */) rc = VERR_INVALID_PARAMETER; else { DnDGenericMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms); m_dndMessageQueue.append(pMessage); } break; } #endif default: rc = VERR_NOT_IMPLEMENTED; break; } return rc; } HGCM::Message* DnDManager::nextHGCMMessage() { if (m_pCurMsg) return m_pCurMsg->nextHGCMMessage(); else { if (m_dndMessageQueue.isEmpty()) return 0; return m_dndMessageQueue.first()->nextHGCMMessage(); } } int DnDManager::nextMessageInfo(uint32_t *puMsg, uint32_t *pcParms) { AssertPtrReturn(puMsg, VERR_INVALID_POINTER); AssertPtrReturn(pcParms, VERR_INVALID_POINTER); int rc = VINF_SUCCESS; if (m_pCurMsg) rc = m_pCurMsg->currentMessageInfo(puMsg, pcParms); else { if (m_dndMessageQueue.isEmpty()) { rc = VERR_NO_DATA; // if (m_pfnProgressCallback) // m_pfnProgressCallback(100.0, DragAndDropSvc::DND_OP_CANCELLED, m_pvProgressUser); } else rc = m_dndMessageQueue.first()->currentMessageInfo(puMsg, pcParms); } DO(("next msg info: %d %d %Rrc\n", *puMsg, *pcParms, rc)); return rc; } int DnDManager::nextMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) { if (!m_pCurMsg) { /* Check for pending messages in our queue. */ if (m_dndMessageQueue.isEmpty()) return VERR_NO_DATA; m_pCurMsg = m_dndMessageQueue.first(); m_dndMessageQueue.removeFirst(); } /* Fetch the current message info */ int rc = m_pCurMsg->currentMessage(uMsg, cParms, paParms); /* If this message not provide any additional sub messages, clear it. */ if (!m_pCurMsg->isMessageWaiting()) { delete m_pCurMsg; m_pCurMsg = 0; } /* If the user has canceled the operation, we need to cleanup all pending * events and inform the progress callback about our successful cleanup. */ if ( rc == VERR_CANCELLED && m_pfnProgressCallback) { /* Clear any pending messages */ clear(); /* Create a new cancel message to inform the guest. */ m_pCurMsg = new DnDHGCancelMessage(); m_pfnProgressCallback(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, m_pvProgressUser); } DO(("next msg: %d %d %Rrc\n", uMsg, cParms, rc)); return rc; } void DnDManager::clear() { if (m_pCurMsg) { delete m_pCurMsg; m_pCurMsg = 0; } while (!m_dndMessageQueue.isEmpty()) { delete m_dndMessageQueue.last(); m_dndMessageQueue.removeLast(); } }