diff options
Diffstat (limited to 'src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp')
-rw-r--r-- | src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp | 1352 |
1 files changed, 1352 insertions, 0 deletions
diff --git a/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp new file mode 100644 index 00000000..c9ac0119 --- /dev/null +++ b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp @@ -0,0 +1,1352 @@ +/* $Id: VBoxDragAndDropSvc.cpp $ */ +/** @file + * Drag and Drop Service. + */ + +/* + * Copyright (C) 2011-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +/** @page pg_svc_dnd Drag and drop HGCM Service + * + * @sa See src/VBox/Main/src-client/GuestDnDPrivate.cpp for more information. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_GUEST_DND +#include <VBox/GuestHost/DragAndDrop.h> +#include <VBox/GuestHost/DragAndDropDefs.h> +#include <VBox/HostServices/Service.h> +#include <VBox/HostServices/DragAndDropSvc.h> +#include <VBox/AssertGuest.h> + +#include <VBox/err.h> + +#include <algorithm> +#include <list> +#include <map> + +#include "dndmanager.h" + +using namespace DragAndDropSvc; + + +/********************************************************************************************************************************* +* Service class declaration * +*********************************************************************************************************************************/ + +class DragAndDropClient : public HGCM::Client +{ +public: + + DragAndDropClient(uint32_t idClient) + : HGCM::Client(idClient) + , uProtocolVerDeprecated(0) + , fGuestFeatures0(VBOX_DND_GF_NONE) + , fGuestFeatures1(VBOX_DND_GF_NONE) + { + RT_ZERO(m_SvcCtx); + } + + virtual ~DragAndDropClient(void) + { + disconnect(); + } + +public: + + void disconnect(void) RT_NOEXCEPT; + +public: + + /** Protocol version used by this client. + * Deprecated; only used for keeping backwards compatibility. */ + uint32_t uProtocolVerDeprecated; + /** Guest feature flags, VBOX_DND_GF_0_XXX. */ + uint64_t fGuestFeatures0; + /** Guest feature flags, VBOX_DND_GF_1_XXX. */ + uint64_t fGuestFeatures1; +}; + +/** Map holding pointers to drag and drop clients. Key is the (unique) HGCM client ID. */ +typedef std::map<uint32_t, DragAndDropClient*> DnDClientMap; + +/** Simple queue (list) which holds deferred (waiting) clients. */ +typedef std::list<uint32_t> DnDClientQueue; + +/** + * Specialized drag & drop service class. + */ +class DragAndDropService : public HGCM::AbstractService<DragAndDropService> +{ +public: + explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers) + : HGCM::AbstractService<DragAndDropService>(pHelpers) + , m_pManager(NULL) + , m_u32Mode(VBOX_DRAG_AND_DROP_MODE_OFF) + {} + +protected: + int init(VBOXHGCMSVCFNTABLE *pTable) RT_NOEXCEPT RT_OVERRIDE; + int uninit(void) RT_NOEXCEPT RT_OVERRIDE; + int clientConnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT RT_OVERRIDE; + int clientDisconnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT RT_OVERRIDE; + int clientQueryFeatures(uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT; + int clientReportFeatures(DragAndDropClient *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT; + void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t idClient, void *pvClient, uint32_t u32Function, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT RT_OVERRIDE; + int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT RT_OVERRIDE; + +private: + int modeSet(uint32_t u32Mode) RT_NOEXCEPT; + inline uint32_t modeGet(void) const RT_NOEXCEPT + { return m_u32Mode; }; + + static DECLCALLBACK(int) progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser); + +private: + /** Pointer to our DnD manager instance. */ + DnDManager *m_pManager; + /** Map of all connected clients. + * The primary key is the (unique) client ID, the secondary value + * an allocated pointer to the DragAndDropClient class, managed + * by this service class. */ + DnDClientMap m_clientMap; + /** List of all clients which are queued up (deferred return) and ready + * to process new commands. The key is the (unique) client ID. */ + DnDClientQueue m_clientQueue; + /** Current drag and drop mode, VBOX_DRAG_AND_DROP_MODE_XXX. */ + uint32_t m_u32Mode; + /** Host feature mask (VBOX_DND_HF_0_XXX) for DND_GUEST_REPORT_FEATURES + * and DND_GUEST_QUERY_FEATURES. */ + uint64_t m_fHostFeatures0; +}; + + +/********************************************************************************************************************************* +* Client implementation * +*********************************************************************************************************************************/ + +/** + * Called when the HGCM client disconnected on the guest side. + * + * This function takes care of the client's data cleanup and also lets the host + * know that the client has been disconnected. + */ +void DragAndDropClient::disconnect(void) RT_NOEXCEPT +{ + LogFlowThisFunc(("uClient=%RU32, fDeferred=%RTbool\n", m_idClient, IsDeferred())); + + /* + * If the client still is waiting for a message (i.e in deferred mode), + * complete the call with a VERR_CANCELED status so that the client (VBoxTray / VBoxClient) knows + * it should bail out. + */ + if (IsDeferred()) + CompleteDeferred(VERR_CANCELLED); + + /* + * Let the host know. + */ + VBOXDNDCBDISCONNECTMSGDATA data; + RT_ZERO(data); + /** @todo Magic needed? */ + /** @todo Add context ID. */ + + if (m_SvcCtx.pfnHostCallback) + { + int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, GUEST_DND_FN_DISCONNECT, &data, sizeof(data)); + if (RT_FAILURE(rc2)) + LogFlowFunc(("Warning: Unable to notify host about client %RU32 disconnect, rc=%Rrc\n", m_idClient, rc2)); + /* Not fatal. */ + } +} + + +/********************************************************************************************************************************* +* Service class implementation * +*********************************************************************************************************************************/ + +int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable) RT_NOEXCEPT +{ + /* Legacy clients map to the root category. */ + pTable->idxLegacyClientCategory = HGCM_CLIENT_CATEGORY_ROOT; + + /* Limit to 255 clients (see also DragAndDropService::clientConnect). */ + for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++) + pTable->acMaxClients[i] = UINT8_MAX; + + /* Limit the number of concurrent calls to 256 (playing safe). */ + /** @todo Properly determin the max number of pending/concurrent calls for DnD. */ + for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++) + pTable->acMaxCallsPerClient[i] = 256; + + /* Register functions. */ + pTable->pfnHostCall = svcHostCall; + pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */ + pTable->pfnLoadState = NULL; /* construction done before restoring suffices */ + pTable->pfnRegisterExtension = svcRegisterExtension; + pTable->pfnNotify = NULL; + + /* Drag'n drop mode is disabled by default. */ + modeSet(VBOX_DRAG_AND_DROP_MODE_OFF); + + /* Set host features. */ + m_fHostFeatures0 = VBOX_DND_HF_NONE; + + int rc = VINF_SUCCESS; + + try + { + m_pManager = new DnDManager(&DragAndDropService::progressCallback, this); + } + catch (std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int DragAndDropService::uninit(void) RT_NOEXCEPT +{ + LogFlowFuncEnter(); + + if (m_pManager) + { + delete m_pManager; + m_pManager = NULL; + } + + DnDClientMap::iterator itClient = m_clientMap.begin(); + while (itClient != m_clientMap.end()) + { + delete itClient->second; + m_clientMap.erase(itClient); + itClient = m_clientMap.begin(); + } + + LogFlowFuncLeave(); + return VINF_SUCCESS; +} + +int DragAndDropService::clientConnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT +{ + RT_NOREF1(pvClient); + if (m_clientMap.size() >= UINT8_MAX) /* Don't allow too much clients at the same time. */ + { + AssertMsgFailed(("Maximum number of clients reached\n")); + return VERR_MAX_PROCS_REACHED; + } + + /* + * Add client to our client map. + */ + if (m_clientMap.find(idClient) != m_clientMap.end()) + { + LogFunc(("Client %RU32 is already connected!\n", idClient)); + return VERR_ALREADY_EXISTS; + } + + try + { + DragAndDropClient *pClient = new DragAndDropClient(idClient); + pClient->SetSvcContext(m_SvcCtx); + m_clientMap[idClient] = pClient; + } + catch (std::bad_alloc &) + { + LogFunc(("Client %RU32 - VERR_NO_MEMORY!\n", idClient)); + return VERR_NO_MEMORY; + } + + LogFlowFunc(("Client %RU32 connected (VINF_SUCCESS)\n", idClient)); + return VINF_SUCCESS; +} + +int DragAndDropService::clientDisconnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT +{ + RT_NOREF1(pvClient); + + /* Client not found? Bail out early. */ + DnDClientMap::iterator itClient = m_clientMap.find(idClient); + if (itClient == m_clientMap.end()) + { + LogFunc(("Client %RU32 not found!\n", idClient)); + return VERR_NOT_FOUND; + } + + /* + * Remove from waiters queue. + */ + m_clientQueue.remove(idClient); + + /* + * Remove from client map and deallocate. + */ + AssertPtr(itClient->second); + delete itClient->second; + + m_clientMap.erase(itClient); + + LogFlowFunc(("Client %RU32 disconnected\n", idClient)); + return VINF_SUCCESS; +} + +/** + * Implements GUEST_DND_FN_REPORT_FEATURES. + * + * @returns VBox status code. + * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here). + * @retval VERR_ACCESS_DENIED if not master + * @retval VERR_INVALID_PARAMETER if bit 63 in the 2nd parameter isn't set. + * @retval VERR_WRONG_PARAMETER_COUNT + * + * @param pClient The client state. + * @param cParms Number of parameters. + * @param paParms Array of parameters. + */ +int DragAndDropService::clientReportFeatures(DragAndDropClient *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT +{ + RT_NOREF(pClient); + + /* + * Validate the request. + */ + ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT); + ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE); + uint64_t const fFeatures0 = paParms[0].u.uint64; + ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE); + uint64_t const fFeatures1 = paParms[1].u.uint64; + ASSERT_GUEST_RETURN(fFeatures1 & VBOX_DND_GF_1_MUST_BE_ONE, VERR_INVALID_PARAMETER); + + /* + * Report back the host features. + */ + paParms[0].u.uint64 = m_fHostFeatures0; + paParms[1].u.uint64 = 0; + + pClient->fGuestFeatures0 = fFeatures0; + pClient->fGuestFeatures1 = fFeatures1; + + Log(("[Client %RU32] features: %#RX64 %#RX64\n", pClient->GetClientID(), fFeatures0, fFeatures1)); + + return VINF_SUCCESS; +} + +/** + * Implements GUEST_DND_FN_QUERY_FEATURES. + * + * @returns VBox status code. + * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here). + * @retval VERR_WRONG_PARAMETER_COUNT + * + * @param cParms Number of parameters. + * @param paParms Array of parameters. + */ +int DragAndDropService::clientQueryFeatures(uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT +{ + /* + * Validate the request. + */ + ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT); + ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE); + ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE); + ASSERT_GUEST(paParms[1].u.uint64 & RT_BIT_64(63)); + + /* + * Report back the host features. + */ + paParms[0].u.uint64 = m_fHostFeatures0; + paParms[1].u.uint64 = 0; + + return VINF_SUCCESS; +} + +int DragAndDropService::modeSet(uint32_t u32Mode) RT_NOEXCEPT +{ +#ifndef VBOX_WITH_DRAG_AND_DROP_GH + if ( u32Mode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST + || u32Mode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL) + { + m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF; + return VERR_NOT_SUPPORTED; + } +#endif + + switch (u32Mode) + { + case VBOX_DRAG_AND_DROP_MODE_OFF: + case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST: + case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST: + case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL: + m_u32Mode = u32Mode; + break; + + default: + m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF; + break; + } + + return VINF_SUCCESS; +} + +void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t idClient, + void *pvClient, uint32_t u32Function, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT +{ + RT_NOREF1(pvClient); + LogFlowFunc(("idClient=%RU32, u32Function=%s (%#x), cParms=%RU32\n", + idClient, DnDGuestMsgToStr(u32Function), u32Function, cParms)); + + /* Check if we've the right mode set. */ + int rc = VERR_ACCESS_DENIED; /* Play safe. */ + switch (u32Function) + { + case GUEST_DND_FN_GET_NEXT_HOST_MSG: + { + if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF) + rc = VINF_SUCCESS; + else + { + LogRel(("DnD: Feature is disabled, ignoring request from guest\n")); + rc = VINF_HGCM_ASYNC_EXECUTE; + } + break; + } + + /* New since protocol v2. */ + case GUEST_DND_FN_CONNECT: + RT_FALL_THROUGH(); + /* New since VBox 6.1.x. */ + case GUEST_DND_FN_REPORT_FEATURES: + RT_FALL_THROUGH(); + /* New since VBox 6.1.x. */ + case GUEST_DND_FN_QUERY_FEATURES: + { + /* + * Never block these calls, as the clients issues those when + * initializing and might get stuck if drag and drop is set to "disabled" at + * that time. + */ + rc = VINF_SUCCESS; + break; + } + + /* New since VBOx 7.0.x. See define for details. */ + case GUEST_DND_FN_EVT_ERROR: + { + rc = VINF_SUCCESS; + break; + } + + case GUEST_DND_FN_HG_ACK_OP: + case GUEST_DND_FN_HG_REQ_DATA: + case GUEST_DND_FN_HG_EVT_PROGRESS: + { + if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL + || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST) + rc = VINF_SUCCESS; + else + LogRel(("DnD: Host -> Guest mode disabled, ignoring request from guest\n")); + break; + } + + case GUEST_DND_FN_GH_ACK_PENDING: + case GUEST_DND_FN_GH_SND_DATA_HDR: + case GUEST_DND_FN_GH_SND_DATA: + case GUEST_DND_FN_GH_SND_DIR: + case GUEST_DND_FN_GH_SND_FILE_HDR: + case GUEST_DND_FN_GH_SND_FILE_DATA: + { +#ifdef VBOX_WITH_DRAG_AND_DROP_GH + if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL + || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST) + rc = VINF_SUCCESS; + else +#endif + LogRel(("DnD: Guest -> Host mode disabled, ignoring request from guest\n")); + break; + } + + default: + /* Reach through to DnD manager. */ + rc = VINF_SUCCESS; + break; + } + +#define DO_HOST_CALLBACK(); \ + if ( RT_SUCCESS(rc) \ + && m_SvcCtx.pfnHostCallback) \ + { \ + rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); \ + } + + /* + * Lookup client. + */ + DragAndDropClient *pClient = NULL; + + DnDClientMap::iterator itClient = m_clientMap.find(idClient); + if (itClient != m_clientMap.end()) + { + pClient = itClient->second; + AssertPtr(pClient); + } + else + { + LogFunc(("Client %RU32 was not found\n", idClient)); + rc = VERR_NOT_FOUND; + } + +/* Verifies that an uint32 parameter has the expected buffer size set. + * Will set rc to VERR_INVALID_PARAMETER otherwise. See #9777. */ +#define VERIFY_BUFFER_SIZE_UINT32(a_ParmUInt32, a_SizeExpected) \ +do { \ + uint32_t cbTemp = 0; \ + rc = HGCMSvcGetU32(&a_ParmUInt32, &cbTemp); \ + ASSERT_GUEST_BREAK(RT_SUCCESS(rc) && cbTemp == a_SizeExpected); \ +} while (0) + +/* Gets the context ID from the first parameter and store it into the data header. + * Then increments idxParm by one if more than one parameter is available. */ +#define GET_CONTEXT_ID_PARM0() \ + if (fHasCtxID) \ + { \ + ASSERT_GUEST_BREAK(cParms >= 1); \ + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); \ + ASSERT_GUEST_BREAK(RT_SUCCESS(rc)); \ + if (cParms > 1) \ + idxParm++; \ + } + + if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */ + { + rc = VERR_INVALID_PARAMETER; /* Play safe by default. */ + + /* Whether the client's advertised protocol sends context IDs with commands. */ + const bool fHasCtxID = pClient->uProtocolVerDeprecated >= 3; + + /* Current parameter index to process. */ + unsigned idxParm = 0; + + switch (u32Function) + { + /* + * Note: Older VBox versions with enabled DnD guest->host support (< 5.0) + * used the same message ID (300) for GUEST_DND_FN_GET_NEXT_HOST_MSG and + * HOST_DND_FN_GH_REQ_PENDING, which led this service returning + * VERR_INVALID_PARAMETER when the guest wanted to actually + * handle HOST_DND_FN_GH_REQ_PENDING. + */ + case GUEST_DND_FN_GET_NEXT_HOST_MSG: + { + if (cParms == 3) + { + /* Make sure to increase the reference count so that the next message doesn't get removed between + * the guest's GUEST_DND_FN_GET_NEXT_HOST_MSG call and the actual message retrieval call. */ + rc = m_pManager->GetNextMsgInfo(true /* fAddRef */, + &paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */); + if (RT_FAILURE(rc)) /* No queued messages available? */ + { + if (m_SvcCtx.pfnHostCallback) /* Try asking the host. */ + { + VBOXDNDCBHGGETNEXTHOSTMSG data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG; + rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); + if (RT_SUCCESS(rc)) + { + paParms[0].u.uint32 = data.uMsg; /* uMsg */ + paParms[1].u.uint32 = data.cParms; /* cParms */ + /* Note: paParms[2] was set by the guest as blocking flag. */ + } + + LogFlowFunc(("Host callback returned %Rrc\n", rc)); + } + else /* No host callback in place, so drag and drop is not supported by the host. */ + rc = VERR_NOT_SUPPORTED; + + if (RT_FAILURE(rc)) + rc = m_pManager->GetNextMsg(u32Function, cParms, paParms); + + /* Some error occurred or no (new) messages available? */ + if (RT_FAILURE(rc)) + { + uint32_t fFlags = 0; + int rc2 = HGCMSvcGetU32(&paParms[2], &fFlags); + if ( RT_SUCCESS(rc2) + && fFlags) /* Blocking flag set? */ + { + /* Defer client returning. */ + rc = VINF_HGCM_ASYNC_EXECUTE; + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFunc(("Message queue is empty, returning %Rrc to guest\n", rc)); + } + } + } + break; + } + case GUEST_DND_FN_CONNECT: + { + ASSERT_GUEST_BREAK(cParms >= 2); + + VBOXDNDCBCONNECTDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_CONNECT; + + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.hdr.uContextID); \ + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uProtocolVersion); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm], &data.fFlags); + ASSERT_GUEST_RC_BREAK(rc); + + unsigned uProtocolVer = 3; /* The protocol version we're going to use. */ + + /* Make sure we're only setting a protocl version we're supporting on the host. */ + if (data.uProtocolVersion > uProtocolVer) + data.uProtocolVersion = uProtocolVer; + + pClient->uProtocolVerDeprecated = data.uProtocolVersion; + + /* Return the highest protocol version we're supporting. */ + AssertBreak(idxParm); + ASSERT_GUEST_BREAK(idxParm); + paParms[idxParm - 1].u.uint32 = data.uProtocolVersion; + + LogFlowFunc(("Client %RU32 is now using protocol v%RU32\n", + pClient->GetClientID(), pClient->uProtocolVerDeprecated)); + + DO_HOST_CALLBACK(); + break; + } + case GUEST_DND_FN_REPORT_FEATURES: + { + rc = clientReportFeatures(pClient, cParms, paParms); + if (RT_SUCCESS(rc)) + { + VBOXDNDCBREPORTFEATURESDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_REPORT_FEATURES; + + data.fGuestFeatures0 = pClient->fGuestFeatures0; + /* fGuestFeatures1 is not used yet. */ + + /* Don't touch initial rc. */ + int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); + AssertRC(rc2); + } + break; + } + case GUEST_DND_FN_QUERY_FEATURES: + { + rc = clientQueryFeatures(cParms, paParms); + break; + } + case GUEST_DND_FN_HG_ACK_OP: + { + ASSERT_GUEST_BREAK(cParms >= 2); + + VBOXDNDCBHGACKOPDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_HG_ACK_OP; + + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetU32(&paParms[idxParm], &data.uAction); /* Get drop action. */ + ASSERT_GUEST_RC_BREAK(rc); + + DO_HOST_CALLBACK(); + break; + } + case GUEST_DND_FN_HG_REQ_DATA: + { + VBOXDNDCBHGREQDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_HG_REQ_DATA; + + switch (pClient->uProtocolVerDeprecated) + { + case 3: + { + ASSERT_GUEST_BREAK(cParms == 3); + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetPv(&paParms[idxParm++], (void **)&data.pszFormat, &data.cbFormat); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.cbFormat); + break; + } + + case 2: + RT_FALL_THROUGH(); + default: + { + ASSERT_GUEST_BREAK(cParms == 1); + rc = HGCMSvcGetPv(&paParms[idxParm], (void**)&data.pszFormat, &data.cbFormat); + ASSERT_GUEST_RC_BREAK(rc); + break; + } + } + + DO_HOST_CALLBACK(); + break; + } + case GUEST_DND_FN_HG_EVT_PROGRESS: + { + ASSERT_GUEST_BREAK(cParms >= 3); + + VBOXDNDCBHGEVTPROGRESSDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS; + + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uStatus); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uPercentage); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm], &data.rc); + ASSERT_GUEST_RC_BREAK(rc); + + DO_HOST_CALLBACK(); + break; + } +#ifdef VBOX_WITH_DRAG_AND_DROP_GH + case GUEST_DND_FN_GH_ACK_PENDING: + { + VBOXDNDCBGHACKPENDINGDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_ACK_PENDING; + + switch (pClient->uProtocolVerDeprecated) + { + case 3: + { + ASSERT_GUEST_BREAK(cParms == 5); + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uDefAction); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uAllActions); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pszFormat, &data.cbFormat); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.cbFormat); + break; + } + + case 2: + default: + { + ASSERT_GUEST_BREAK(cParms == 3); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uDefAction); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uAllActions); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetPv(&paParms[idxParm], (void**)&data.pszFormat, &data.cbFormat); + ASSERT_GUEST_RC_BREAK(rc); + break; + } + } + + DO_HOST_CALLBACK(); + break; + } + /* New since protocol v3. */ + case GUEST_DND_FN_GH_SND_DATA_HDR: + { + ASSERT_GUEST_BREAK(cParms == 12); + + VBOXDNDCBSNDDATAHDRDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA_HDR; + + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.uFlags); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.uScreenId); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU64(&paParms[idxParm++], &data.data.cbTotal); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.cbMeta); + ASSERT_GUEST_RC_BREAK(rc); + ASSERT_GUEST_BREAK(data.data.cbMeta <= data.data.cbTotal); + rc = HGCMSvcGetPv(&paParms[idxParm++], &data.data.pvMetaFmt, &data.data.cbMetaFmt); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.data.cbMetaFmt); + rc = HGCMSvcGetU64(&paParms[idxParm++], &data.data.cObjects); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.enmCompression); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm++], (uint32_t *)&data.data.enmChecksumType); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetPv(&paParms[idxParm++], &data.data.pvChecksum, &data.data.cbChecksum); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.data.cbChecksum); + + DO_HOST_CALLBACK(); + break; + } + case GUEST_DND_FN_GH_SND_DATA: + { + switch (pClient->uProtocolVerDeprecated) + { + case 3: + { + ASSERT_GUEST_BREAK(cParms == 5); + + VBOXDNDCBSNDDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA; + + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.data.u.v3.pvData, &data.data.u.v3.cbData); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.data.u.v3.cbData); + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.data.u.v3.pvChecksum, &data.data.u.v3.cbChecksum); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.data.u.v3.cbChecksum); + + DO_HOST_CALLBACK(); + break; + } + + case 2: + RT_FALL_THROUGH(); + default: + { + ASSERT_GUEST_BREAK(cParms == 2); + + VBOXDNDCBSNDDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA; + + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.data.u.v1.pvData, &data.data.u.v1.cbData); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm], &data.data.u.v1.cbTotalSize); + ASSERT_GUEST_RC_BREAK(rc); + + DO_HOST_CALLBACK(); + break; + } + } + break; + } + case GUEST_DND_FN_GH_SND_DIR: + { + ASSERT_GUEST_BREAK(cParms >= 3); + + VBOXDNDCBSNDDIRDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DIR; + + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pszPath, &data.cbPath); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbPath); + rc = HGCMSvcGetU32(&paParms[idxParm], &data.fMode); + ASSERT_GUEST_RC_BREAK(rc); + + DO_HOST_CALLBACK(); + break; + } + /* New since protocol v2 (>= VBox 5.0). */ + case GUEST_DND_FN_GH_SND_FILE_HDR: + { + ASSERT_GUEST_BREAK(cParms == 6); + + VBOXDNDCBSNDFILEHDRDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_HDR; + + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pszFilePath, &data.cbFilePath); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbFilePath); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.fFlags); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU32(&paParms[idxParm++], &data.fMode); + ASSERT_GUEST_RC_BREAK(rc); + rc = HGCMSvcGetU64(&paParms[idxParm], &data.cbSize); + ASSERT_GUEST_RC_BREAK(rc); + + DO_HOST_CALLBACK(); + break; + } + case GUEST_DND_FN_GH_SND_FILE_DATA: + { + switch (pClient->uProtocolVerDeprecated) + { + /* Protocol v3 adds (optional) checksums. */ + case 3: + { + ASSERT_GUEST_BREAK(cParms == 5); + + VBOXDNDCBSNDFILEDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA; + + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pvData, &data.cbData); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbData); + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.u.v3.pvChecksum, &data.u.v3.cbChecksum); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.u.v3.cbChecksum); + + DO_HOST_CALLBACK(); + break; + } + /* Protocol v2 only sends the next data chunks to reduce traffic. */ + case 2: + { + ASSERT_GUEST_BREAK(cParms == 3); + + VBOXDNDCBSNDFILEDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA; + + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pvData, &data.cbData); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.cbData); + + DO_HOST_CALLBACK(); + break; + } + /* Protocol v1 sends the file path and attributes for every file chunk (!). */ + default: + { + ASSERT_GUEST_BREAK(cParms == 5); + + VBOXDNDCBSNDFILEDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA; + + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.u.v1.pszFilePath, &data.u.v1.cbFilePath); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.u.v1.cbFilePath); + rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pvData, &data.cbData); + ASSERT_GUEST_RC_BREAK(rc); + VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbData); + rc = HGCMSvcGetU32(&paParms[idxParm], &data.u.v1.fMode); + ASSERT_GUEST_RC_BREAK(rc); + + DO_HOST_CALLBACK(); + break; + } + } + break; + } + case GUEST_DND_FN_EVT_ERROR: + { + ASSERT_GUEST_BREAK(cParms >= 1); + + VBOXDNDCBEVTERRORDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_EVT_ERROR; + + GET_CONTEXT_ID_PARM0(); + rc = HGCMSvcGetU32(&paParms[idxParm], (uint32_t *)&data.rc); + ASSERT_GUEST_RC_BREAK(rc); + + DO_HOST_CALLBACK(); + break; + } +#endif /* VBOX_WITH_DRAG_AND_DROP_GH */ + + default: + { + LogFlowFunc(("u32Function=%s (%#x), cParms=%RU32\n", DnDHostMsgToStr(u32Function), u32Function, cParms)); + + /* All other messages are handled by the DnD manager. */ + rc = m_pManager->GetNextMsg(u32Function, cParms, paParms); + if (rc == VERR_NO_DATA) /* Manager has no new messsages? Try asking the host. */ + { + if (m_SvcCtx.pfnHostCallback) + { + VBOXDNDCBHGGETNEXTHOSTMSGDATA data; + RT_ZERO(data); + + data.hdr.uMagic = VBOX_DND_CB_MAGIC_MAKE(0 /* uFn */, 0 /* uVer */); + + data.uMsg = u32Function; + data.cParms = cParms; + data.paParms = paParms; + + rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); + if (RT_SUCCESS(rc)) + { + cParms = data.cParms; + paParms = data.paParms; + } + else + { + if (rc == VERR_CANCELLED) + { + /* Host indicated that the current operation was cancelled. Tell the guest. */ + LogFunc(("Host indicated that operation was cancelled\n")); + } + else + { + /* + * In case the guest is too fast asking for the next message + * and the host did not supply it yet, just defer the client's + * return until a response from the host available. + */ + LogFunc(("No new messages from the host (%Rrc), deferring request\n", rc)); + rc = VINF_HGCM_ASYNC_EXECUTE; + } + } + } + else /* No host callback in place, so drag and drop is not supported by the host. */ + rc = VERR_NOT_SUPPORTED; + } + break; + } + } + } + +#undef VERIFY_BUFFER_SIZE_UINT32 + + /* + * If async execution is requested, we didn't notify the guest yet about + * completion. The client is queued into the waiters list and will be + * notified as soon as a new event is available. + */ + if (rc == VINF_HGCM_ASYNC_EXECUTE) + { + LogFunc(("Deferring client %RU32\n", idClient)); + + try + { + AssertPtr(pClient); + pClient->SetDeferred(callHandle, u32Function, cParms, paParms); + m_clientQueue.push_back(idClient); + } + catch (std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + /* Don't report to guest. */ + } + } + else if (pClient) + { + /* Complete the call on the guest side. */ + pClient->Complete(callHandle, rc); + } + else + { + AssertMsgFailed(("Guest call failed with %Rrc\n", rc)); + rc = VERR_NOT_IMPLEMENTED; + } + + LogFunc(("Returning %Rrc to guest\n", rc)); +} + +int DragAndDropService::hostCall(uint32_t u32Function, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT +{ + LogFlowFunc(("u32Function=%s (%#x), cParms=%RU32, cClients=%zu, cQueue=%zu\n", + DnDHostMsgToStr(u32Function), u32Function, cParms, m_clientMap.size(), m_clientQueue.size())); + + uint32_t const uMode = modeGet(); + + /* Check if we've the right mode set. */ + int rc = VERR_ACCESS_DENIED; /* Play safe. */ + switch (u32Function) + { + /* + * Host -> Guest mode + */ + case HOST_DND_FN_HG_EVT_ENTER: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_EVT_MOVE: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_EVT_LEAVE: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_EVT_DROPPED: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_DATA_HDR: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_DATA: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_MORE_DATA: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_DIR: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_FILE_DATA: + RT_FALL_THROUGH(); + case HOST_DND_FN_HG_SND_FILE_HDR: + { + if ( uMode == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST + || uMode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL) + rc = VINF_SUCCESS; + else + { + LogRel2(("DnD: Host to guest mode is disabled, ignoring request from host\n")); + } + break; + } + + /* + * Guest -> Host mode + */ + case HOST_DND_FN_GH_REQ_PENDING: + RT_FALL_THROUGH(); + case HOST_DND_FN_GH_EVT_DROPPED: + { + if ( uMode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST + || uMode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL) + rc = VINF_SUCCESS; + else + { + LogRel2(("DnD: Guest to host mode is disabled, ignoring request from host\n")); + } + break; + } + + /* + * Both modes + */ + case HOST_DND_FN_CANCEL: + if (uMode != VBOX_DRAG_AND_DROP_MODE_OFF) + rc = VINF_SUCCESS; + break; + + /* + * Functions that always work. + */ + case HOST_DND_FN_SET_MODE: + rc = VINF_SUCCESS; + break; + + /* + * Forbid everything else not explicitly allowed. + */ + default: + break; + } + + if (RT_FAILURE(rc)) + return rc; + + bool fSendToGuest = false; /* Whether to send the message down to the guest side or not. */ + + switch (u32Function) + { + case HOST_DND_FN_SET_MODE: + { + if (cParms != 1) + rc = VERR_INVALID_PARAMETER; + else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) + rc = VERR_INVALID_PARAMETER; + else + rc = modeSet(paParms[0].u.uint32); + break; + } + + case HOST_DND_FN_CANCEL: + { + LogFlowFunc(("Cancelling all waiting clients ...\n")); + + /* Forcefully reset the message queue, as the host has cancelled the current operation. */ + m_pManager->Reset(true /* fForce */); + + /* + * Wake up all deferred clients and tell them to process + * the cancelling message next. + */ + DnDClientQueue::iterator itQueue = m_clientQueue.begin(); + while (itQueue != m_clientQueue.end()) + { + DnDClientMap::iterator itClient = m_clientMap.find(*itQueue); + Assert(itClient != m_clientMap.end()); + + DragAndDropClient *pClient = itClient->second; + AssertPtr(pClient); + + int rc2 = pClient->SetDeferredMsgInfo(HOST_DND_FN_CANCEL, + /* Protocol v3+ also contains the context ID. */ + pClient->uProtocolVerDeprecated >= 3 ? 1 : 0); + AssertRC(rc2); + + /* Return VERR_CANCELLED when waking up the guest side. */ + pClient->CompleteDeferred(VERR_CANCELLED); + + m_clientQueue.erase(itQueue); + itQueue = m_clientQueue.begin(); + } + + Assert(m_clientQueue.empty()); + + /* Tell the host that everything went well. */ + rc = VINF_SUCCESS; + break; + } + + case HOST_DND_FN_HG_EVT_ENTER: + { + /* Reset the message queue as a new DnD operation just began. */ + m_pManager->Reset(false /* fForce */); + + fSendToGuest = true; + rc = VINF_SUCCESS; + break; + } + + default: + { + fSendToGuest = true; + rc = VINF_SUCCESS; + break; + } + } + + do /* goto avoidance break-loop. */ + { + if (fSendToGuest) + { + if (m_clientMap.empty()) /* At least one client on the guest connected? */ + { + /* + * Tell the host that the guest does not support drag'n drop. + * This might happen due to not installed Guest Additions or + * not running VBoxTray/VBoxClient. + */ + rc = VERR_NOT_SUPPORTED; + break; + } + + rc = m_pManager->AddMsg(u32Function, cParms, paParms, true /* fAppend */); + if (RT_FAILURE(rc)) + { + AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n", u32Function, rc)); + break; + } + + /* Any clients in our queue ready for processing the next command? */ + if (m_clientQueue.empty()) + { + LogFlowFunc(("All clients (%zu) busy -- delaying execution\n", m_clientMap.size())); + break; + } + + uint32_t uClientNext = m_clientQueue.front(); + DnDClientMap::iterator itClientNext = m_clientMap.find(uClientNext); + Assert(itClientNext != m_clientMap.end()); + + DragAndDropClient *pClient = itClientNext->second; + AssertPtr(pClient); + + /* + * Check if this was a request for getting the next host + * message. If so, return the message ID and the parameter + * count. The message itself has to be queued. + */ + uint32_t uMsgClient = pClient->GetMsgType(); + + uint32_t uMsgNext = 0; + uint32_t cParmsNext = 0; + /* Note: We only want to peek for the next message, hence fAddRef is false. */ + int rcNext = m_pManager->GetNextMsgInfo(false /* fAddRef */, &uMsgNext, &cParmsNext); + + LogFlowFunc(("uMsgClient=%s (%#x), uMsgNext=%s (%#x), cParmsNext=%RU32, rcNext=%Rrc\n", + DnDGuestMsgToStr(uMsgClient), uMsgClient, DnDHostMsgToStr(uMsgNext), uMsgNext, cParmsNext, rcNext)); + + if (RT_SUCCESS(rcNext)) + { + if (uMsgClient == GUEST_DND_FN_GET_NEXT_HOST_MSG) + { + rc = pClient->SetDeferredMsgInfo(uMsgNext, cParmsNext); + + /* Note: Report the current rc back to the guest. */ + pClient->CompleteDeferred(rc); + } + /* + * Does the message the client is waiting for match the message + * next in the queue? Process it right away then. + */ + else if (uMsgClient == uMsgNext) + { + rc = m_pManager->GetNextMsg(u32Function, cParms, paParms); + + /* Note: Report the current rc back to the guest. */ + pClient->CompleteDeferred(rc); + } + else /* Should not happen; cancel the operation on the guest. */ + { + LogFunc(("Client ID=%RU32 in wrong state with uMsg=%RU32 (next message in queue: %RU32), cancelling\n", + pClient->GetClientID(), uMsgClient, uMsgNext)); + + pClient->CompleteDeferred(VERR_CANCELLED); + } + + m_clientQueue.pop_front(); + } + + } /* fSendToGuest */ + + } while (0); /* To use breaks. */ + + LogFlowFuncLeaveRC(rc); + return rc; +} + +DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser) +{ + AssertPtrReturn(pvUser, VERR_INVALID_POINTER); + + DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser); + AssertPtr(pSelf); + + if (pSelf->m_SvcCtx.pfnHostCallback) + { + LogFlowFunc(("GUEST_DND_FN_HG_EVT_PROGRESS: uStatus=%RU32, uPercentage=%RU32, rc=%Rrc\n", + uStatus, uPercentage, rc)); + + VBOXDNDCBHGEVTPROGRESSDATA data; + data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS; + data.uPercentage = RT_MIN(uPercentage, 100); + data.uStatus = uStatus; + data.rc = rc; /** @todo uin32_t vs. int. */ + + return pSelf->m_SvcCtx.pfnHostCallback(pSelf->m_SvcCtx.pvHostData, + GUEST_DND_FN_HG_EVT_PROGRESS, + &data, sizeof(data)); + } + + return VINF_SUCCESS; +} + +/** + * @copydoc FNVBOXHGCMSVCLOAD + */ +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable) +{ + return DragAndDropService::svcLoad(pTable); +} + |