diff options
Diffstat (limited to '')
194 files changed, 71403 insertions, 0 deletions
diff --git a/src/VBox/HostServices/.scm-settings b/src/VBox/HostServices/.scm-settings new file mode 100644 index 00000000..9dc579d1 --- /dev/null +++ b/src/VBox/HostServices/.scm-settings @@ -0,0 +1,19 @@ +# $Id: .scm-settings $ +## @file +# Source code massager settings for the host HGCM services. +# + +# +# Copyright (C) 2019 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. +# + +/*.h: --guard-relative-to-dir . + diff --git a/src/VBox/HostServices/DragAndDrop/Makefile.kmk b/src/VBox/HostServices/DragAndDrop/Makefile.kmk new file mode 100644 index 00000000..2889e61f --- /dev/null +++ b/src/VBox/HostServices/DragAndDrop/Makefile.kmk @@ -0,0 +1,57 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Guest Control Host Service. +# + +# +# Copyright (C) 2011-2019 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. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# Include sub-makefile(s). +# include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk + +# +# The drag and drop service DLL. +# +DLLS += VBoxDragAndDropSvc +VBoxDragAndDropSvc_TEMPLATE = VBOXR3 +VBoxDragAndDropSvc_NAME.os2 = VBoxDnD +VBoxDragAndDropSvc_DEFS = \ + VBOX_WITH_HGCM \ + $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,) +VBoxDragAndDropSvc_INCS = $(PATH_ROOT)/src/VBox/Main/include ./ +VBoxDragAndDropSvc_INCS.win = \ + $(VBOX_PATH_SDK) + +VBoxDragAndDropSvc_SOURCES = \ + VBoxDragAndDropSvc.cpp \ + dndmanager.cpp + +VBoxDragAndDropSvc_SOURCES += \ + ../common/client.cpp \ + ../common/message.cpp + +VBoxDragAndDropSvc_SOURCES.win = \ + VBoxDragAndDropSvc.rc + +VBoxDragAndDropSvc_LIBS = \ + $(LIB_VMM) \ + $(LIB_RUNTIME) \ + $(LIB_REM) \ + $(PATH_STAGE_LIB)/VBoxDnDHostR3Lib$(VBOX_SUFF_LIB) + +VBoxDragAndDropSvc_LDFLAGS.darwin = \ + -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxDragAndDropSvc.dylib + +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp new file mode 100644 index 00000000..6a46b0f6 --- /dev/null +++ b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp @@ -0,0 +1,1219 @@ +/* $Id: VBoxDragAndDropSvc.cpp $ */ +/** @file + * Drag and Drop Service. + */ + +/* + * Copyright (C) 2011-2019 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. + */ + +/** @page pg_svc_guest_control Drag and drop HGCM Service + * + * TODO + */ + + +/********************************************************************************************************************************* +* 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/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 uClientID) + : HGCM::Client(uClientID) + { + RT_ZERO(m_SvcCtx); + } + + virtual ~DragAndDropClient(void) + { + disconnect(); + } + +public: + + void disconnect(void); +}; + +/** 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) {} + +protected: + + int init(VBOXHGCMSVCFNTABLE *pTable); + int uninit(void); + int clientConnect(uint32_t u32ClientID, void *pvClient); + int clientDisconnect(uint32_t u32ClientID, void *pvClient); + void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + + int modeSet(uint32_t u32Mode); + inline uint32_t modeGet(void) const { return m_u32Mode; }; + +protected: + + static DECLCALLBACK(int) progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser); + +protected: + + /** 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. */ + uint32_t m_u32Mode; +}; + + +/********************************************************************************************************************************* +* 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) +{ + LogFlowThisFunc(("uClient=%RU32\n", m_uClientID)); + + if (IsDeferred()) + CompleteDeferred(VERR_INTERRUPTED); + + /* + * 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_DISCONNECT, &data, sizeof(data)); + if (RT_FAILURE(rc2)) + LogFlowFunc(("Warning: Unable to notify host about client %RU32 disconnect, rc=%Rrc\n", m_uClientID, rc2)); + /* Not fatal. */ + } +} + + +/********************************************************************************************************************************* +* Service class implementation * +*********************************************************************************************************************************/ + +int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable) +{ + /* 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); + + 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) +{ + 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 u32ClientID, void *pvClient) +{ + 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; + } + + int rc = VINF_SUCCESS; + + /* + * Add client to our client map. + */ + if (m_clientMap.find(u32ClientID) != m_clientMap.end()) + rc = VERR_ALREADY_EXISTS; + + if (RT_SUCCESS(rc)) + { + try + { + DragAndDropClient *pClient = new DragAndDropClient(u32ClientID); + pClient->SetSvcContext(m_SvcCtx); + m_clientMap[u32ClientID] = pClient; + } + catch(std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + } + + if (RT_SUCCESS(rc)) + { + /* + * Reset the message queue as soon as a new clients connect + * to ensure that every client has the same state. + */ + if (m_pManager) + m_pManager->Reset(); + } + } + + LogFlowFunc(("Client %RU32 connected, rc=%Rrc\n", u32ClientID, rc)); + return rc; +} + +int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient) +{ + RT_NOREF1(pvClient); + + /* Client not found? Bail out early. */ + DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID); + if (itClient == m_clientMap.end()) + return VERR_NOT_FOUND; + + /* + * Remove from waiters queue. + */ + m_clientQueue.remove(u32ClientID); + + /* + * Remove from client map and deallocate. + */ + AssertPtr(itClient->second); + delete itClient->second; + + m_clientMap.erase(itClient); + + LogFlowFunc(("Client %RU32 disconnected\n", u32ClientID)); + return VINF_SUCCESS; +} + +int DragAndDropService::modeSet(uint32_t u32Mode) +{ +#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 u32ClientID, + void *pvClient, uint32_t u32Function, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + RT_NOREF1(pvClient); + LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n", + u32ClientID, u32Function, cParms)); + + /* Check if we've the right mode set. */ + int rc = VERR_ACCESS_DENIED; /* Play safe. */ + switch (u32Function) + { + case GUEST_DND_GET_NEXT_HOST_MSG: + { + if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF) + { + rc = VINF_SUCCESS; + } + else + { + LogFlowFunc(("DnD disabled, deferring request\n")); + rc = VINF_HGCM_ASYNC_EXECUTE; + } + break; + } + + /* New since protocol v2. */ + case GUEST_DND_CONNECT: + { + /* + * Never block the initial connect call, as the clients do this when + * initializing and might get stuck if drag and drop is set to "disabled" at + * that time. + */ + rc = VINF_SUCCESS; + break; + } + case GUEST_DND_HG_ACK_OP: + case GUEST_DND_HG_REQ_DATA: + case GUEST_DND_HG_EVT_PROGRESS: + { + if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL + || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST) + { + rc = VINF_SUCCESS; + } + else + LogFlowFunc(("Host -> Guest DnD mode disabled, ignoring request\n")); + break; + } + + case GUEST_DND_GH_ACK_PENDING: + case GUEST_DND_GH_SND_DATA_HDR: + case GUEST_DND_GH_SND_DATA: + case GUEST_DND_GH_SND_DIR: + case GUEST_DND_GH_SND_FILE_HDR: + case GUEST_DND_GH_SND_FILE_DATA: + case GUEST_DND_GH_EVT_ERROR: + { +#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 + LogFlowFunc(("Guest -> Host DnD mode disabled, ignoring request\n")); + break; + } + + default: + /* Reach through to DnD manager. */ + rc = VINF_SUCCESS; + break; + } + +#ifdef DEBUG_andy + LogFlowFunc(("Mode (%RU32) check rc=%Rrc\n", modeGet(), rc)); +#endif + +#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(u32ClientID); + if (itClient != m_clientMap.end()) + { + pClient = itClient->second; + AssertPtr(pClient); + } + else + { + LogFunc(("Client %RU32 was not found\n", u32ClientID)); + rc = VERR_NOT_FOUND; + } + + if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */ + { + LogFlowFunc(("Client %RU32: Protocol v%RU32\n", pClient->GetClientID(), pClient->GetProtocolVer())); + + rc = VERR_INVALID_PARAMETER; /* Play safe. */ + + switch (u32Function) + { + /* + * Note: Older VBox versions with enabled DnD guest->host support (< 5.0) + * used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and + * HOST_DND_GH_REQ_PENDING, which led this service returning + * VERR_INVALID_PARAMETER when the guest wanted to actually + * handle HOST_DND_GH_REQ_PENDING. + */ + case GUEST_DND_GET_NEXT_HOST_MSG: + { + LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n")); + if (cParms == 3) + { + rc = m_pManager->GetNextMsgInfo(&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. */ + } + } + 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_CONNECT: + { + LogFlowFunc(("GUEST_DND_CONNECT\n")); + if (cParms >= 2) + { + const uint8_t idxProto = cParms >= 3 ? 1 : 0; + + VBOXDNDCBCONNECTMSGDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_CONNECT; + if (cParms >= 3) + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + else /* Older protocols don't have a context ID. */ + rc = VINF_SUCCESS; + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[idxProto], &data.uProtocol); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[idxProto + 1], &data.uFlags); + if (RT_SUCCESS(rc)) + pClient->SetProtocolVer(data.uProtocol); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("Client %RU32 is now using protocol v%RU32\n", pClient->GetClientID(), pClient->GetProtocolVer())); + DO_HOST_CALLBACK(); + } + } + break; + } + case GUEST_DND_HG_ACK_OP: + { + LogFlowFunc(("GUEST_DND_HG_ACK_OP\n")); + + VBOXDNDCBHGACKOPDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_HG_ACK_OP; + + switch (pClient->GetProtocolVer()) + { + case 3: + { + if (cParms == 2) + { + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[1], &data.uAction); /* Get drop action. */ + } + break; + } + + case 2: + default: + { + if (cParms == 1) + rc = HGCMSvcGetU32(&paParms[0], &data.uAction); /* Get drop action. */ + break; + } + } + + DO_HOST_CALLBACK(); + break; + } + case GUEST_DND_HG_REQ_DATA: + { + LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n")); + + VBOXDNDCBHGREQDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_HG_REQ_DATA; + + switch (pClient->GetProtocolVer()) + { + case 3: + { + if (cParms == 3) + { + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[1], (void **)&data.pszFormat, &data.cbFormat); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.cbFormat); + } + break; + } + + case 2: + default: + { + if (cParms == 1) + rc = HGCMSvcGetPv(&paParms[0], (void**)&data.pszFormat, &data.cbFormat); + break; + } + } + + DO_HOST_CALLBACK(); + break; + } + case GUEST_DND_HG_EVT_PROGRESS: + { + LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS\n")); + + VBOXDNDCBHGEVTPROGRESSDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS; + + switch (pClient->GetProtocolVer()) + { + case 3: + { + if (cParms == 4) + { + rc = HGCMSvcGetU32(&paParms[0], &data.uStatus); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[1], &data.uStatus); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.uPercentage); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[3], &data.rc); + } + break; + } + + case 2: + default: + { + if (cParms == 3) + { + rc = HGCMSvcGetU32(&paParms[0], &data.uStatus); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[1], &data.uPercentage); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.rc); + } + break; + } + } + + DO_HOST_CALLBACK(); + break; + } +#ifdef VBOX_WITH_DRAG_AND_DROP_GH + case GUEST_DND_GH_ACK_PENDING: + { + LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n")); + + VBOXDNDCBGHACKPENDINGDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_ACK_PENDING; + + switch (pClient->GetProtocolVer()) + { + case 3: + { + if (cParms == 5) + { + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[1], &data.uDefAction); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.uAllActions); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[3], (void**)&data.pszFormat, &data.cbFormat); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[4], &data.cbFormat); + } + break; + } + + case 2: + default: + { + if (cParms == 3) + { + rc = HGCMSvcGetU32(&paParms[0], &data.uDefAction); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[1], &data.uAllActions); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[2], (void**)&data.pszFormat, &data.cbFormat); + } + break; + } + } + + DO_HOST_CALLBACK(); + break; + } + /* New since protocol v3. */ + case GUEST_DND_GH_SND_DATA_HDR: + { + LogFlowFunc(("GUEST_DND_GH_SND_DATA_HDR\n")); + if (cParms == 12) + { + VBOXDNDCBSNDDATAHDRDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA_HDR; + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[1], &data.data.uFlags); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.data.uScreenId); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU64(&paParms[3], &data.data.cbTotal); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[4], &data.data.cbMeta); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[5], &data.data.pvMetaFmt, &data.data.cbMetaFmt); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[6], &data.data.cbMetaFmt); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU64(&paParms[7], &data.data.cObjects); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[8], &data.data.enmCompression); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[9], (uint32_t *)&data.data.enmChecksumType); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[10], &data.data.pvChecksum, &data.data.cbChecksum); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[11], &data.data.cbChecksum); + + LogFlowFunc(("fFlags=0x%x, cbTotalSize=%RU64, cObj=%RU64\n", + data.data.uFlags, data.data.cbTotal, data.data.cObjects)); + DO_HOST_CALLBACK(); + } + break; + } + case GUEST_DND_GH_SND_DATA: + { + LogFlowFunc(("GUEST_DND_GH_SND_DATA\n")); + switch (pClient->GetProtocolVer()) + { + case 3: + { + if (cParms == 5) + { + VBOXDNDCBSNDDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA; + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[1], (void**)&data.data.u.v3.pvData, &data.data.u.v3.cbData); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.data.u.v3.cbData); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[3], (void**)&data.data.u.v3.pvChecksum, &data.data.u.v3.cbChecksum); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[4], &data.data.u.v3.cbChecksum); + DO_HOST_CALLBACK(); + } + break; + } + + case 2: + default: + { + if (cParms == 2) + { + VBOXDNDCBSNDDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA; + rc = HGCMSvcGetPv(&paParms[0], (void**)&data.data.u.v1.pvData, &data.data.u.v1.cbData); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[1], &data.data.u.v1.cbTotalSize); + DO_HOST_CALLBACK(); + } + break; + } + } + break; + } + case GUEST_DND_GH_SND_DIR: + { + LogFlowFunc(("GUEST_DND_GH_SND_DIR\n")); + + VBOXDNDCBSNDDIRDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DIR; + + switch (pClient->GetProtocolVer()) + { + case 3: + { + if (cParms == 4) + { + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pszPath, &data.cbPath); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.cbPath); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[3], &data.fMode); + } + break; + } + + case 2: + default: + { + if (cParms == 3) + { + rc = HGCMSvcGetPv(&paParms[0], (void**)&data.pszPath, &data.cbPath); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[1], &data.cbPath); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.fMode); + } + break; + } + } + + DO_HOST_CALLBACK(); + break; + } + /* New since protocol v2 (>= VBox 5.0). */ + case GUEST_DND_GH_SND_FILE_HDR: + { + LogFlowFunc(("GUEST_DND_GH_SND_FILE_HDR\n")); + if (cParms == 6) + { + VBOXDNDCBSNDFILEHDRDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_HDR; + + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pszFilePath, &data.cbFilePath); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.cbFilePath); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[3], &data.fFlags); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[4], &data.fMode); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU64(&paParms[5], &data.cbSize); + + LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x, cbSize=%RU64\n", + data.pszFilePath, data.cbFilePath, data.fMode, data.cbSize)); + DO_HOST_CALLBACK(); + } + break; + } + case GUEST_DND_GH_SND_FILE_DATA: + { + LogFlowFunc(("GUEST_DND_GH_SND_FILE_DATA\n")); + + switch (pClient->GetProtocolVer()) + { + /* Protocol v3 adds (optional) checksums. */ + case 3: + { + if (cParms == 5) + { + VBOXDNDCBSNDFILEDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA; + + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pvData, &data.cbData); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.cbData); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[3], (void**)&data.u.v3.pvChecksum, &data.u.v3.cbChecksum); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[4], &data.u.v3.cbChecksum); + + LogFlowFunc(("pvData=0x%p, cbData=%RU32\n", data.pvData, data.cbData)); + DO_HOST_CALLBACK(); + } + break; + } + /* Protocol v2 only sends the next data chunks to reduce traffic. */ + case 2: + { + if (cParms == 3) + { + VBOXDNDCBSNDFILEDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA; + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pvData, &data.cbData); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[2], &data.cbData); + + LogFlowFunc(("cbData=%RU32, pvData=0x%p\n", data.cbData, data.pvData)); + DO_HOST_CALLBACK(); + } + break; + } + /* Protocol v1 sends the file path and attributes for every file chunk (!). */ + default: + { + if (cParms == 5) + { + VBOXDNDCBSNDFILEDATADATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA; + uint32_t cTmp; + rc = HGCMSvcGetPv(&paParms[0], (void**)&data.u.v1.pszFilePath, &cTmp); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[1], &data.u.v1.cbFilePath); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&paParms[2], (void**)&data.pvData, &cTmp); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[3], &data.cbData); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&paParms[4], &data.u.v1.fMode); + + LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n", + data.u.v1.pszFilePath, data.cbData, data.pvData, data.u.v1.fMode)); + DO_HOST_CALLBACK(); + } + break; + } + } + break; + } + case GUEST_DND_GH_EVT_ERROR: + { + LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n")); + + VBOXDNDCBEVTERRORDATA data; + RT_ZERO(data); + data.hdr.uMagic = CB_MAGIC_DND_GH_EVT_ERROR; + + switch (pClient->GetProtocolVer()) + { + case 3: + { + if (cParms == 2) + { + rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); + if (RT_SUCCESS(rc)) + { + uint32_t rcOp; + rc = HGCMSvcGetU32(&paParms[1], &rcOp); + if (RT_SUCCESS(rc)) + data.rc = rcOp; + } + } + break; + } + + case 2: + default: + { + if (cParms == 1) + { + uint32_t rcOp; + rc = HGCMSvcGetU32(&paParms[0], &rcOp); + if (RT_SUCCESS(rc)) + data.rc = (int32_t)rcOp; + } + break; + } + } + + DO_HOST_CALLBACK(); + break; + } +#endif /* VBOX_WITH_DRAG_AND_DROP_GH */ + + default: + { + /* 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 + { + /* + * 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. + */ + LogFlowFunc(("No new messages from the host (yet), deferring request: %Rrc\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; + } + } + } + + /* + * 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) + { + try + { + AssertPtr(pClient); + pClient->SetDeferred(callHandle, u32Function, cParms, paParms); + m_clientQueue.push_back(u32ClientID); + } + catch (std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + /* Don't report to guest. */ + } + } + else if (pClient) + pClient->Complete(callHandle, rc); + else + { + AssertMsgFailed(("Guest call failed with %Rrc\n", rc)); + rc = VERR_NOT_IMPLEMENTED; + } + + LogFlowFunc(("Returning rc=%Rrc\n", rc)); +} + +int DragAndDropService::hostCall(uint32_t u32Function, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + LogFlowFunc(("u32Function=%RU32, cParms=%RU32, cClients=%zu, cQueue=%zu\n", + u32Function, cParms, m_clientMap.size(), m_clientQueue.size())); + + int rc; + + do + { + bool fSendToGuest = false; /* Whether to send the message down to the guest side or not. */ + + switch (u32Function) + { + case HOST_DND_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_CANCEL: + { + LogFlowFunc(("Cancelling all waiting clients ...\n")); + + /* Reset the message queue as the host cancelled the whole operation. */ + m_pManager->Reset(); + + 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; + } + + /* + * 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_CANCEL, + /* Protocol v3+ also contains the context ID. */ + pClient->GetProtocolVer() >= 3 ? 1 : 0); + pClient->CompleteDeferred(rc2); + + 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_HG_EVT_ENTER: + { + /* Reset the message queue as a new DnD operation just began. */ + m_pManager->Reset(); + + fSendToGuest = true; + rc = VINF_SUCCESS; + break; + } + + default: + { + fSendToGuest = true; + rc = VINF_SUCCESS; + break; + } + } + + if (fSendToGuest) + { + if (modeGet() == VBOX_DRAG_AND_DROP_MODE_OFF) + { + /* Tell the host that a wrong drag'n drop mode is set. */ + rc = VERR_ACCESS_DENIED; + break; + } + + 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; + int rcNext = m_pManager->GetNextMsgInfo(&uMsgNext, &cParmsNext); + + LogFlowFunc(("uMsgClient=%RU32, uMsgNext=%RU32, cParmsNext=%RU32, rcNext=%Rrc\n", + uMsgClient, uMsgNext, cParmsNext, rcNext)); + + if (RT_SUCCESS(rcNext)) + { + if (uMsgClient == GUEST_DND_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_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_HG_EVT_PROGRESS, + &data, sizeof(data)); + } + + return VINF_SUCCESS; +} + +/** + * @copydoc VBOXHGCMSVCLOAD + */ +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable) +{ + return DragAndDropService::svcLoad(pTable); +} + diff --git a/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc new file mode 100644 index 00000000..f4059f39 --- /dev/null +++ b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxDragAndDropSvc.rc $ */ +/** @file + * VBoxDragAndDropSvc - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox Drag and Drop Host Service\0" + VALUE "InternalName", "VBoxDragAndDropSvc\0" + VALUE "OriginalFilename", "VBoxDragAndDropSvc.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/DragAndDrop/dndmanager.cpp b/src/VBox/HostServices/DragAndDrop/dndmanager.cpp new file mode 100644 index 00000000..efc79280 --- /dev/null +++ b/src/VBox/HostServices/DragAndDrop/dndmanager.cpp @@ -0,0 +1,206 @@ +/* $Id: dndmanager.cpp $ */ +/** @file + * Drag and Drop manager: Handling of DnD messages on the host side. + */ + +/* + * Copyright (C) 2011-2019 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 * +*********************************************************************************************************************************/ + +#ifdef LOG_GROUP + #undef LOG_GROUP +#endif +#define LOG_GROUP LOG_GROUP_GUEST_DND + +#include "dndmanager.h" + +#include <VBox/log.h> +#include <iprt/file.h> +#include <iprt/dir.h> +#include <iprt/path.h> +#include <iprt/uri.h> + + +/********************************************************************************************************************************* +* DnDManager * +*********************************************************************************************************************************/ + +/** + * Adds a DnD message to the manager's queue. + * + * @returns IPRT status code. + * @param pMsg Pointer to DnD message to add. The queue then owns the pointer. + * @param fAppend Whether to append or prepend the message to the queue. + */ +int DnDManager::AddMsg(DnDMessage *pMsg, bool fAppend /* = true */) +{ + AssertPtrReturn(pMsg, VERR_INVALID_POINTER); + + LogFlowFunc(("uMsg=%RU32, cParms=%RU32, fAppend=%RTbool\n", pMsg->GetType(), pMsg->GetParamCount(), fAppend)); + + if (fAppend) + m_queueMsg.append(pMsg); + else + m_queueMsg.prepend(pMsg); + + /** @todo Catch / handle OOM? */ + + return VINF_SUCCESS; +} + +/** + * Adds a DnD message to the manager's queue. + * + * @returns IPRT status code. + * @param uMsg Type (function number) of message to add. + * @param cParms Number of parameters of message to add. + * @param paParms Array of parameters of message to add. + * @param fAppend Whether to append or prepend the message to the queue. + */ +int DnDManager::AddMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fAppend /* = true */) +{ + int rc; + + try + { + DnDMessage *pMsg = new DnDGenericMessage(uMsg, cParms, paParms); + rc = AddMsg(pMsg, fAppend); + } + catch(std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Retrieves information about the next message in the queue. + * + * @returns IPRT status code. VERR_NO_DATA if no next message is available. + * @param puType Where to store the message type. + * @param pcParms Where to store the message parameter count. + */ +int DnDManager::GetNextMsgInfo(uint32_t *puType, uint32_t *pcParms) +{ + AssertPtrReturn(puType, VERR_INVALID_POINTER); + AssertPtrReturn(pcParms, VERR_INVALID_POINTER); + + int rc; + + if (m_queueMsg.isEmpty()) + { + rc = VERR_NO_DATA; + } + else + { + DnDMessage *pMsg = m_queueMsg.first(); + AssertPtr(pMsg); + + *puType = pMsg->GetType(); + *pcParms = pMsg->GetParamCount(); + + rc = VINF_SUCCESS; + } + + LogFlowFunc(("Returning puMsg=%RU32, pcParms=%RU32, rc=%Rrc\n", *puType, *pcParms, rc)); + return rc; +} + +/** + * Retrieves the next queued up message and removes it from the queue on success. + * Will return VERR_NO_DATA if no next message is available. + * + * @returns IPRT status code. + * @param uMsg Message type to retrieve. + * @param cParms Number of parameters the \@a paParms array can store. + * @param paParms Where to store the message parameters. + */ +int DnDManager::GetNextMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + LogFlowFunc(("uMsg=%RU32, cParms=%RU32\n", uMsg, cParms)); + + /* Check for pending messages in our queue. */ + if (m_queueMsg.isEmpty()) + return VERR_NO_DATA; + + /* Get the current message. */ + DnDMessage *pMsg = m_queueMsg.first(); + AssertPtr(pMsg); + + m_queueMsg.removeFirst(); /* Remove the current message from the queue. */ + + /* Fetch the current message info. */ + int rc = pMsg->GetData(uMsg, cParms, paParms); + + /* + * If there was an error handling the current message or the user has canceled + * the operation, we need to cleanup all pending events and inform the progress + * callback about our exit. + */ + if (RT_FAILURE(rc)) + { + /* Clear any pending messages. */ + Reset(); + + /* Create a new cancel message to inform the guest + call + * the host whether the current transfer was canceled or aborted + * due to an error. */ + try + { + if (rc == VERR_CANCELLED) + LogFlowFunc(("Operation was cancelled\n")); + + DnDHGCancelMessage *pMsgCancel = new DnDHGCancelMessage(); + + int rc2 = AddMsg(pMsgCancel, false /* Prepend */); + AssertRC(rc2); + + if (m_pfnProgressCallback) + { + LogFlowFunc(("Notifying host about aborting operation (%Rrc) ...\n", rc)); + m_pfnProgressCallback( rc == VERR_CANCELLED + ? DragAndDropSvc::DND_PROGRESS_CANCELLED + : DragAndDropSvc::DND_PROGRESS_ERROR, + 100 /* Percent */, rc, + m_pvProgressUser); + } + } + catch(std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + } + } + + LogFlowFunc(("Message processed with rc=%Rrc\n", rc)); + return rc; +} + +/** + * Resets the manager by clearing the message queue and internal state. + */ +void DnDManager::Reset(void) +{ + LogFlowFuncEnter(); + + while (!m_queueMsg.isEmpty()) + { + delete m_queueMsg.last(); + m_queueMsg.removeLast(); + } +} + diff --git a/src/VBox/HostServices/DragAndDrop/dndmanager.h b/src/VBox/HostServices/DragAndDrop/dndmanager.h new file mode 100644 index 00000000..be5986e6 --- /dev/null +++ b/src/VBox/HostServices/DragAndDrop/dndmanager.h @@ -0,0 +1,113 @@ +/** @file + * Drag and Drop manager. + */ + +/* + * Copyright (C) 2011-2019 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. + */ + +#ifndef VBOX_INCLUDED_SRC_DragAndDrop_dndmanager_h +#define VBOX_INCLUDED_SRC_DragAndDrop_dndmanager_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/GuestHost/DragAndDrop.h> +#include <VBox/HostServices/Service.h> +#include <VBox/HostServices/DragAndDropSvc.h> + +#include <iprt/cpp/ministring.h> +#include <iprt/cpp/list.h> + +typedef DECLCALLBACK(int) FNDNDPROGRESS(uint32_t uState, uint32_t uPercentage, int rc, void *pvUser); +typedef FNDNDPROGRESS *PFNDNDPROGRESS; + +/** + * DnD message class. This class forms the base of all other more specialized + * message classes. + */ +class DnDMessage : public HGCM::Message +{ +public: + + DnDMessage(void) + { + } + + DnDMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[]) + : Message(uMsg, cParms, aParms) { } + + virtual ~DnDMessage(void) { } +}; + +/** + * DnD message class for generic messages which didn't need any special + * handling. + */ +class DnDGenericMessage: public DnDMessage +{ +public: + DnDGenericMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) + : DnDMessage(uMsg, cParms, paParms) { } +}; + +/** + * DnD message class for informing the guest to cancel any current (and pending) activities. + */ +class DnDHGCancelMessage: public DnDMessage +{ +public: + + DnDHGCancelMessage(void) + { + int rc2 = initData(DragAndDropSvc::HOST_DND_CANCEL, + 0 /* cParms */, 0 /* aParms */); + AssertRC(rc2); + } +}; + +/** + * DnD manager. Manage creation and queuing of messages for the various DnD + * messages types. + */ +class DnDManager +{ +public: + + DnDManager(PFNDNDPROGRESS pfnProgressCallback, void *pvProgressUser) + : m_pfnProgressCallback(pfnProgressCallback) + , m_pvProgressUser(pvProgressUser) + {} + + virtual ~DnDManager(void) + { + Reset(); + } + + int AddMsg(DnDMessage *pMessage, bool fAppend = true); + int AddMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fAppend = true); + + int GetNextMsgInfo(uint32_t *puType, uint32_t *pcParms); + int GetNextMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + + void Reset(void); + +protected: + + /** DnD message queue (FIFO). */ + RTCList<DnDMessage *> m_queueMsg; + /** Pointer to host progress callback. Optional, can be NULL. */ + PFNDNDPROGRESS m_pfnProgressCallback; + /** Pointer to progress callback user context. Can be NULL if not used. */ + void *m_pvProgressUser; +}; +#endif /* !VBOX_INCLUDED_SRC_DragAndDrop_dndmanager_h */ + diff --git a/src/VBox/HostServices/GuestControl/Makefile.kmk b/src/VBox/HostServices/GuestControl/Makefile.kmk new file mode 100644 index 00000000..f36d6438 --- /dev/null +++ b/src/VBox/HostServices/GuestControl/Makefile.kmk @@ -0,0 +1,49 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Guest Control Host Service. +# + +# +# Copyright (C) 2011-2019 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. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# Include sub-makefile(s). +include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk + +# +# The guest control service DLL. +# +DLLS += VBoxGuestControlSvc +VBoxGuestControlSvc_TEMPLATE = VBOXR3 +VBoxGuestControlSvc_NAME.os2 = VBoxGCTL +VBoxGuestControlSvc_DEFS = VBOX_WITH_HGCM +VBoxGuestControlSvc_INCS = $(PATH_ROOT)/src/VBox/Main/include +VBoxGuestControlSvc_INCS.win = \ + $(VBOX_PATH_SDK) + +VBoxGuestControlSvc_SOURCES = \ + VBoxGuestControlSvc.cpp + +VBoxGuestControlSvc_SOURCES.win = \ + VBoxGuestControlSvc.rc + +VBoxGuestControlSvc_LIBS = \ + $(LIB_VMM) \ + $(LIB_RUNTIME) \ + $(LIB_REM) + +VBoxGuestControlSvc_LDFLAGS.darwin = \ + -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxGuestControlSvc.dylib + +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp b/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp new file mode 100644 index 00000000..1996a885 --- /dev/null +++ b/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp @@ -0,0 +1,2434 @@ +/* $Id: VBoxGuestControlSvc.cpp $ */ +/** @file + * Guest Control Service: Controlling the guest. + */ + +/* + * Copyright (C) 2011-2019 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. + */ + +/** @page pg_svc_guest_control Guest Control HGCM Service + * + * This service acts as a proxy for handling and buffering host message requests + * and clients on the guest. It tries to be as transparent as possible to let + * the guest (client) and host side do their protocol handling as desired. + * + * The following terms are used: + * - Host: A host process (e.g. VBoxManage or another tool utilizing the Main API) + * which wants to control something on the guest. + * - Client: A client (e.g. VBoxService) running inside the guest OS waiting for + * new host messages to perform. There can be multiple clients connected + * to this service. A client is represented by its unique HGCM client ID. + * - Context ID: An (almost) unique ID automatically generated on the host (Main API) + * to not only distinguish clients but individual requests. Because + * the host does not know anything about connected clients it needs + * an indicator which it can refer to later. This context ID gets + * internally bound by the service to a client which actually processes + * the message in order to have a relationship between client<->context ID(s). + * + * The host can trigger messages which get buffered by the service (with full HGCM + * parameter info). As soon as a client connects (or is ready to do some new work) + * it gets a buffered host message to process it. This message then will be immediately + * removed from the message list. If there are ready clients but no new messages to be + * processed, these clients will be set into a deferred state (that is being blocked + * to return until a new host message is available). + * + * If a client needs to inform the host that something happened, it can send a + * message to a low level HGCM callback registered in Main. This callback contains + * the actual data as well as the context ID to let the host do the next necessary + * steps for this context. This context ID makes it possible to wait for an event + * inside the host's Main API function (like starting a process on the guest and + * wait for getting its PID returned by the client) as well as cancelling blocking + * host calls in order the client terminated/crashed (HGCM detects disconnected + * clients and reports it to this service's callback). + * + * Starting at VBox 4.2 the context ID itself consists of a session ID, an object + * ID (for example a process or file ID) and a count. This is necessary to not break + * compatibility between older hosts and to manage guest session on the host. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_GUEST_CONTROL +#include <VBox/HostServices/GuestControlSvc.h> +#include <VBox/GuestHost/GuestControl.h> /** @todo r=bird: Why two headers??? */ + +#include <VBox/err.h> +#include <VBox/log.h> +#include <VBox/AssertGuest.h> +#include <VBox/VMMDev.h> +#include <VBox/vmm/ssm.h> +#include <iprt/assert.h> +#include <iprt/cpp/autores.h> +#include <iprt/cpp/utils.h> +#include <iprt/mem.h> +#include <iprt/list.h> +#include <iprt/req.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <iprt/time.h> + +#include <map> +#include <new> /* for std::nothrow*/ + + +using namespace guestControl; + + +/** + * Structure for maintaining a request. + */ +typedef struct ClientRequest +{ + /** The call handle */ + VBOXHGCMCALLHANDLE mHandle; + /** Number of parameters */ + uint32_t mNumParms; + /** The call parameters */ + VBOXHGCMSVCPARM *mParms; + /** The default constructor. */ + ClientRequest(void) + : mHandle(0), mNumParms(0), mParms(NULL) + {} +} ClientRequest; + +/** + * Structure for holding a buffered host message which has + * not been processed yet. + */ +typedef struct HostMsg +{ + /** Entry on the ClientState::m_HostMsgList list. */ + RTLISTNODE m_ListEntry; + union + { + /** The top two twomost bits are exploited for message destination. + * See VBOX_GUESTCTRL_DST_XXX. */ + uint64_t m_idContextAndDst; + /** The context ID this message belongs to (extracted from the first parameter). */ + uint32_t m_idContext; + }; + /** Dynamic structure for holding the HGCM parms */ + uint32_t mType; + /** Number of HGCM parameters. */ + uint32_t mParmCount; + /** Array of HGCM parameters. */ + PVBOXHGCMSVCPARM mpParms; + /** Set if we detected the message skipping hack from r121400. */ + bool m_f60BetaHackInPlay; + + HostMsg() + : m_idContextAndDst(0) + , mType(UINT32_MAX) + , mParmCount(0) + , mpParms(NULL) + , m_f60BetaHackInPlay(false) + { + RTListInit(&m_ListEntry); + } + + /** + * Releases the host message, properly deleting it if no further references. + */ + void Delete(void) + { + LogFlowThisFunc(("[Msg %RU32 (%s)] destroying\n", mType, GstCtrlHostMsgtoStr((eHostMsg)mType))); + Assert(m_ListEntry.pNext == NULL); + if (mpParms) + { + for (uint32_t i = 0; i < mParmCount; i++) + if (mpParms[i].type == VBOX_HGCM_SVC_PARM_PTR) + { + RTMemFree(mpParms[i].u.pointer.addr); + mpParms[i].u.pointer.addr = NULL; + } + RTMemFree(mpParms); + mpParms = NULL; + } + mParmCount = 0; + delete this; + } + + + /** + * Initializes the message. + * + * The specified parameters are copied and any buffers referenced by it + * duplicated as well. + * + * @returns VBox status code. + * @param idMsg The host message number, eHostMsg. + * @param cParms Number of parameters in the HGCM request. + * @param paParms Array of parameters. + */ + int Init(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) + { + LogFlowThisFunc(("[Msg %RU32 (%s)] Allocating cParms=%RU32, paParms=%p\n", + idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), cParms, paParms)); + Assert(mpParms == NULL); + Assert(mParmCount == 0); + Assert(RTListIsEmpty(&m_ListEntry)); + + /* + * Fend of bad stuff. + */ + AssertReturn(cParms > 0, VERR_WRONG_PARAMETER_COUNT); /* At least one parameter (context ID) must be present. */ + AssertReturn(cParms < VMMDEV_MAX_HGCM_PARMS, VERR_WRONG_PARAMETER_COUNT); + AssertPtrReturn(paParms, VERR_INVALID_POINTER); + + /* + * The first parameter is the context ID and the message destination mask. + */ + if (paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT) + { + m_idContextAndDst = paParms[0].u.uint64; + AssertReturn(m_idContextAndDst & VBOX_GUESTCTRL_DST_BOTH, VERR_INTERNAL_ERROR_3); + } + else if (paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT) + { + AssertMsgFailed(("idMsg=%u %s - caller must set dst!\n", idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg))); + m_idContextAndDst = paParms[0].u.uint32 | VBOX_GUESTCTRL_DST_BOTH; + } + else + AssertFailedReturn(VERR_WRONG_PARAMETER_TYPE); + + /* + * Just make a copy of the parameters and any buffers. + */ + mType = idMsg; + mParmCount = cParms; + mpParms = (VBOXHGCMSVCPARM *)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * mParmCount); + AssertReturn(mpParms, VERR_NO_MEMORY); + + for (uint32_t i = 0; i < cParms; i++) + { + mpParms[i].type = paParms[i].type; + switch (paParms[i].type) + { + case VBOX_HGCM_SVC_PARM_32BIT: + mpParms[i].u.uint32 = paParms[i].u.uint32; + break; + + case VBOX_HGCM_SVC_PARM_64BIT: + mpParms[i].u.uint64 = paParms[i].u.uint64; + break; + + case VBOX_HGCM_SVC_PARM_PTR: + mpParms[i].u.pointer.size = paParms[i].u.pointer.size; + if (mpParms[i].u.pointer.size > 0) + { + mpParms[i].u.pointer.addr = RTMemDup(paParms[i].u.pointer.addr, mpParms[i].u.pointer.size); + AssertReturn(mpParms[i].u.pointer.addr, VERR_NO_MEMORY); + } + /* else: structure is zeroed by allocator. */ + break; + + default: + AssertMsgFailedReturn(("idMsg=%u (%s) parameter #%u: type=%u\n", + idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), i, paParms[i].type), + VERR_WRONG_PARAMETER_TYPE); + } + } + + /* + * Morph the first parameter back to 32-bit. + */ + mpParms[0].type = VBOX_HGCM_SVC_PARM_32BIT; + mpParms[0].u.uint32 = (uint32_t)paParms[0].u.uint64; + + return VINF_SUCCESS; + } + + + /** + * Sets the GUEST_MSG_PEEK_WAIT GUEST_MSG_PEEK_NOWAIT return parameters. + * + * @param paDstParms The peek parameter vector. + * @param cDstParms The number of peek parameters (at least two). + * @remarks ASSUMES the parameters has been cleared by clientMsgPeek. + */ + inline void setPeekReturn(PVBOXHGCMSVCPARM paDstParms, uint32_t cDstParms) + { + Assert(cDstParms >= 2); + if (paDstParms[0].type == VBOX_HGCM_SVC_PARM_32BIT) + paDstParms[0].u.uint32 = mType; + else + paDstParms[0].u.uint64 = mType; + paDstParms[1].u.uint32 = mParmCount; + + uint32_t i = RT_MIN(cDstParms, mParmCount + 2); + while (i-- > 2) + switch (mpParms[i - 2].type) + { + case VBOX_HGCM_SVC_PARM_32BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint32_t); break; + case VBOX_HGCM_SVC_PARM_64BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint64_t); break; + case VBOX_HGCM_SVC_PARM_PTR: paDstParms[i].u.uint32 = mpParms[i - 2].u.pointer.size; break; + } + } + + + /** @name Support for old-style (GUEST_MSG_WAIT) operation. + * @{ + */ + + /** + * Worker for Assign() that opies data from the buffered HGCM request to the + * current HGCM request. + * + * @returns VBox status code. + * @param paDstParms Array of parameters of HGCM request to fill the data into. + * @param cDstParms Number of parameters the HGCM request can handle. + */ + int CopyTo(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms) const + { + LogFlowThisFunc(("[Msg %RU32] mParmCount=%RU32, m_idContext=%RU32 (Session %RU32)\n", + mType, mParmCount, m_idContext, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(m_idContext))); + + int rc = VINF_SUCCESS; + if (cDstParms != mParmCount) + { + LogFlowFunc(("Parameter count does not match (got %RU32, expected %RU32)\n", + cDstParms, mParmCount)); + rc = VERR_INVALID_PARAMETER; + } + + if (RT_SUCCESS(rc)) + { + for (uint32_t i = 0; i < mParmCount; i++) + { + if (paDstParms[i].type != mpParms[i].type) + { + LogFunc(("Parameter %RU32 type mismatch (got %RU32, expected %RU32)\n", i, paDstParms[i].type, mpParms[i].type)); + rc = VERR_INVALID_PARAMETER; + } + else + { + switch (mpParms[i].type) + { + case VBOX_HGCM_SVC_PARM_32BIT: +#ifdef DEBUG_andy + LogFlowFunc(("\tmpParms[%RU32] = %RU32 (uint32_t)\n", + i, mpParms[i].u.uint32)); +#endif + paDstParms[i].u.uint32 = mpParms[i].u.uint32; + break; + + case VBOX_HGCM_SVC_PARM_64BIT: +#ifdef DEBUG_andy + LogFlowFunc(("\tmpParms[%RU32] = %RU64 (uint64_t)\n", + i, mpParms[i].u.uint64)); +#endif + paDstParms[i].u.uint64 = mpParms[i].u.uint64; + break; + + case VBOX_HGCM_SVC_PARM_PTR: + { +#ifdef DEBUG_andy + LogFlowFunc(("\tmpParms[%RU32] = %p (ptr), size = %RU32\n", + i, mpParms[i].u.pointer.addr, mpParms[i].u.pointer.size)); +#endif + if (!mpParms[i].u.pointer.size) + continue; /* Only copy buffer if there actually is something to copy. */ + + if (!paDstParms[i].u.pointer.addr) + rc = VERR_INVALID_PARAMETER; + else if (paDstParms[i].u.pointer.size < mpParms[i].u.pointer.size) + rc = VERR_BUFFER_OVERFLOW; + else + memcpy(paDstParms[i].u.pointer.addr, + mpParms[i].u.pointer.addr, + mpParms[i].u.pointer.size); + break; + } + + default: + LogFunc(("Parameter %RU32 of type %RU32 is not supported yet\n", i, mpParms[i].type)); + rc = VERR_NOT_SUPPORTED; + break; + } + } + + if (RT_FAILURE(rc)) + { + LogFunc(("Parameter %RU32 invalid (%Rrc), refusing\n", i, rc)); + break; + } + } + } + + LogFlowFunc(("Returned with rc=%Rrc\n", rc)); + return rc; + } + + int Assign(const ClientRequest *pReq) + { + AssertPtrReturn(pReq, VERR_INVALID_POINTER); + + int rc; + + LogFlowThisFunc(("[Msg %RU32] mParmCount=%RU32, mpParms=%p\n", mType, mParmCount, mpParms)); + + /* Does the current host message need more parameter space which + * the client does not provide yet? */ + if (mParmCount > pReq->mNumParms) + { + LogFlowThisFunc(("[Msg %RU32] Requires %RU32 parms, only got %RU32 from client\n", + mType, mParmCount, pReq->mNumParms)); + /* + * So this call apparently failed because the guest wanted to peek + * how much parameters it has to supply in order to successfully retrieve + * this message. Let's tell him so! + */ + rc = VERR_TOO_MUCH_DATA; + } + else + { + rc = CopyTo(pReq->mParms, pReq->mNumParms); + + /* + * Has there been enough parameter space but the wrong parameter types + * were submitted -- maybe the client was just asking for the next upcoming + * host message? + * + * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA + * in every case. + */ + if (RT_FAILURE(rc)) + rc = VERR_TOO_MUCH_DATA; + } + + return rc; + } + + int Peek(const ClientRequest *pReq) + { + AssertPtrReturn(pReq, VERR_INVALID_POINTER); + + LogFlowThisFunc(("[Msg %RU32] mParmCount=%RU32, mpParms=%p\n", mType, mParmCount, mpParms)); + + if (pReq->mNumParms >= 2) + { + HGCMSvcSetU32(&pReq->mParms[0], mType); /* Message ID */ + HGCMSvcSetU32(&pReq->mParms[1], mParmCount); /* Required parameters for message */ + } + else + LogFlowThisFunc(("Warning: Client has not (yet) submitted enough parameters (%RU32, must be at least 2) to at least peak for the next message\n", + pReq->mNumParms)); + + /* + * Always return VERR_TOO_MUCH_DATA data here to + * keep it compatible with older clients and to + * have correct accounting (mHostRc + mHostMsgTries). + */ + return VERR_TOO_MUCH_DATA; + } + + /** @} */ +} HostMsg; + +/** + * Per-client structure used for book keeping/state tracking a + * certain host message. + */ +typedef struct ClientContext +{ + /* Pointer to list node of this message. */ + HostMsg *mpHostMsg; + /** The standard constructor. */ + ClientContext(void) : mpHostMsg(NULL) {} + /** Internal constrcutor. */ + ClientContext(HostMsg *pHostMsg) : mpHostMsg(pHostMsg) {} +} ClientContext; +typedef std::map< uint32_t, ClientContext > ClientContextMap; + +/** + * Structure for holding a connected guest client state. + */ +typedef struct ClientState +{ + PVBOXHGCMSVCHELPERS m_pSvcHelpers; + /** Host message list to process (HostMsg). */ + RTLISTANCHOR m_HostMsgList; + /** The HGCM client ID. */ + uint32_t m_idClient; + /** The session ID for this client, UINT32_MAX if not set or master. */ + uint32_t m_idSession; + /** Set if master. */ + bool m_fIsMaster; + /** Set if restored (needed for shutting legacy mode assert on non-masters). */ + bool m_fRestored; + + /** Set if we've got a pending wait cancel. */ + bool m_fPendingCancel; + /** Pending client call (GUEST_MSG_PEEK_WAIT or GUEST_MSG_WAIT), zero if none pending. + * + * This means the client waits for a new host message to reply and won't return + * from the waiting call until a new host message is available. */ + guestControl::eGuestMsg m_enmPendingMsg; + /** Pending peek/wait request details. */ + ClientRequest m_PendingReq; + + + ClientState(void) + : m_pSvcHelpers(NULL) + , m_idClient(0) + , m_idSession(UINT32_MAX) + , m_fIsMaster(false) + , m_fRestored(false) + , m_fPendingCancel(false) + , m_enmPendingMsg((guestControl::eGuestMsg)0) + , mHostMsgRc(VINF_SUCCESS) + , mHostMsgTries(0) + , mPeekCount(0) + { + RTListInit(&m_HostMsgList); + } + + ClientState(PVBOXHGCMSVCHELPERS pSvcHelpers, uint32_t idClient) + : m_pSvcHelpers(pSvcHelpers) + , m_idClient(idClient) + , m_idSession(UINT32_MAX) + , m_fIsMaster(false) + , m_fRestored(false) + , m_fPendingCancel(false) + , m_enmPendingMsg((guestControl::eGuestMsg)0) + , mHostMsgRc(VINF_SUCCESS) + , mHostMsgTries(0) + , mPeekCount(0) + { + RTListInit(&m_HostMsgList); + } + + /** + * Used by for Service::hostProcessMessage(). + */ + void EnqueueMessage(HostMsg *pHostMsg) + { + AssertPtr(pHostMsg); + RTListAppend(&m_HostMsgList, &pHostMsg->m_ListEntry); + } + + /** + * Used by for Service::hostProcessMessage(). + * + * @note This wakes up both GUEST_MSG_WAIT and GUEST_MSG_PEEK_WAIT sleepers. + */ + int Wakeup(void) + { + int rc = VINF_NO_CHANGE; + + if (m_enmPendingMsg != 0) + { + LogFlowFunc(("[Client %RU32] Waking up ...\n", m_idClient)); + + rc = VINF_SUCCESS; + + HostMsg *pFirstMsg = RTListGetFirstCpp(&m_HostMsgList, HostMsg, m_ListEntry); + if (pFirstMsg) + { + LogFlowThisFunc(("[Client %RU32] Current host message is %RU32 (CID=%#RX32, cParms=%RU32)\n", + m_idClient, pFirstMsg->mType, pFirstMsg->m_idContext, pFirstMsg->mParmCount)); + + if (m_enmPendingMsg == GUEST_MSG_PEEK_WAIT) + { + pFirstMsg->setPeekReturn(m_PendingReq.mParms, m_PendingReq.mNumParms); + rc = m_pSvcHelpers->pfnCallComplete(m_PendingReq.mHandle, VINF_SUCCESS); + + m_PendingReq.mHandle = NULL; + m_PendingReq.mParms = NULL; + m_PendingReq.mNumParms = 0; + m_enmPendingMsg = (guestControl::eGuestMsg)0; + } + else if (m_enmPendingMsg == GUEST_MSG_WAIT) + rc = OldRun(&m_PendingReq, pFirstMsg); + else + AssertMsgFailed(("m_enmIsPending=%d\n", m_enmPendingMsg)); + } + else + AssertMsgFailed(("Waking up client ID=%RU32 with no host message in queue is a bad idea\n", m_idClient)); + + return rc; + } + + return VINF_NO_CHANGE; + } + + /** + * Used by Service::call() to handle GUEST_MSG_CANCEL. + * + * @note This cancels both GUEST_MSG_WAIT and GUEST_MSG_PEEK_WAIT sleepers. + */ + int CancelWaiting() + { + LogFlowFunc(("[Client %RU32] Cancelling waiting thread, isPending=%d, pendingNumParms=%RU32, m_idSession=%x\n", + m_idClient, m_enmPendingMsg, m_PendingReq.mNumParms, m_idSession)); + + /* + * The PEEK call is simple: At least two parameters, all set to zero before sleeping. + */ + int rcComplete; + if (m_enmPendingMsg == GUEST_MSG_PEEK_WAIT) + { + HGCMSvcSetU32(&m_PendingReq.mParms[0], HOST_MSG_CANCEL_PENDING_WAITS); + rcComplete = VINF_TRY_AGAIN; + } + /* + * The GUEST_MSG_WAIT call is complicated, though we're generally here + * to wake up someone who is peeking and have two parameters. If there + * aren't two parameters, fail the call. + */ + else if (m_enmPendingMsg != 0) + { + Assert(m_enmPendingMsg == GUEST_MSG_WAIT); + if (m_PendingReq.mNumParms > 0) + HGCMSvcSetU32(&m_PendingReq.mParms[0], HOST_MSG_CANCEL_PENDING_WAITS); + if (m_PendingReq.mNumParms > 1) + HGCMSvcSetU32(&m_PendingReq.mParms[1], 0); + rcComplete = m_PendingReq.mNumParms == 2 ? VINF_SUCCESS : VERR_TRY_AGAIN; + } + /* + * If nobody is waiting, flag the next wait call as cancelled. + */ + else + { + m_fPendingCancel = true; + return VINF_SUCCESS; + } + + m_pSvcHelpers->pfnCallComplete(m_PendingReq.mHandle, rcComplete); + + m_PendingReq.mHandle = NULL; + m_PendingReq.mParms = NULL; + m_PendingReq.mNumParms = 0; + m_enmPendingMsg = (guestControl::eGuestMsg)0; + m_fPendingCancel = false; + return VINF_SUCCESS; + } + + + /** @name The GUEST_MSG_WAIT state and helpers. + * + * @note Don't try understand this, it is certificable! + * + * @{ + */ + + /** Last (most recent) rc after handling the host message. */ + int mHostMsgRc; + /** How many GUEST_MSG_WAIT calls the client has issued to retrieve one message. + * + * This is used as a heuristic to remove a message that the client appears not + * to be able to successfully retrieve. */ + uint32_t mHostMsgTries; + /** Number of times we've peeked at a pending message. + * + * This is necessary for being compatible with older Guest Additions. In case + * there are messages which only have two (2) parameters and therefore would fit + * into the GUEST_MSG_WAIT reply immediately, we now can make sure that the + * client first gets back the GUEST_MSG_WAIT results first. + */ + uint32_t mPeekCount; + + /** + * Ditches the first host message and crazy GUEST_MSG_WAIT state. + * + * @note Only used by GUEST_MSG_WAIT scenarios. + */ + void OldDitchFirstHostMsg() + { + HostMsg *pFirstMsg = RTListGetFirstCpp(&m_HostMsgList, HostMsg, m_ListEntry); + Assert(pFirstMsg); + RTListNodeRemove(&pFirstMsg->m_ListEntry); + pFirstMsg->Delete(); + + /* Reset state else. */ + mHostMsgRc = VINF_SUCCESS; + mHostMsgTries = 0; + mPeekCount = 0; + } + + /** + * Used by Wakeup() and OldRunCurrent(). + * + * @note Only used by GUEST_MSG_WAIT scenarios. + */ + int OldRun(ClientRequest const *pReq, HostMsg *pHostMsg) + { + AssertPtrReturn(pReq, VERR_INVALID_POINTER); + AssertPtrReturn(pHostMsg, VERR_INVALID_POINTER); + Assert(RTListNodeIsFirst(&m_HostMsgList, &pHostMsg->m_ListEntry)); + + LogFlowFunc(("[Client %RU32] pReq=%p, mHostMsgRc=%Rrc, mHostMsgTries=%RU32, mPeekCount=%RU32\n", + m_idClient, pReq, mHostMsgRc, mHostMsgTries, mPeekCount)); + + int rc = mHostMsgRc = OldSendReply(pReq, pHostMsg); + + LogFlowThisFunc(("[Client %RU32] Processing host message %RU32 ended with rc=%Rrc\n", + m_idClient, pHostMsg->mType, mHostMsgRc)); + + bool fRemove = false; + if (RT_FAILURE(rc)) + { + mHostMsgTries++; + + /* + * If the client understood the message but supplied too little buffer space + * don't send this message again and drop it after 6 unsuccessful attempts. + * + * Note: Due to legacy reasons this the retry counter has to be even because on + * every peek there will be the actual message retrieval from the client side. + * To not get the actual message if the client actually only wants to peek for + * the next message, there needs to be two rounds per try, e.g. 3 rounds = 6 tries. + */ + /** @todo Fix the mess stated above. GUEST_MSG_WAIT should be become GUEST_MSG_PEEK, *only* + * (and every time) returning the next upcoming host message (if any, blocking). Then + * it's up to the client what to do next, either peeking again or getting the actual + * host message via an own GUEST_ type message. + */ + if ( rc == VERR_TOO_MUCH_DATA + || rc == VERR_CANCELLED) + { + if (mHostMsgTries == 6) + fRemove = true; + } + /* Client did not understand the message or something else weird happened. Try again one + * more time and drop it if it didn't get handled then. */ + else if (mHostMsgTries > 1) + fRemove = true; + } + else + fRemove = true; /* Everything went fine, remove it. */ + + LogFlowThisFunc(("[Client %RU32] Tried host message %RU32 for %RU32 times, (last result=%Rrc, fRemove=%RTbool)\n", + m_idClient, pHostMsg->mType, mHostMsgTries, rc, fRemove)); + + if (fRemove) + { + Assert(RTListNodeIsFirst(&m_HostMsgList, &pHostMsg->m_ListEntry)); + OldDitchFirstHostMsg(); + } + + LogFlowFunc(("[Client %RU32] Returned with rc=%Rrc\n", m_idClient, rc)); + return rc; + } + + /** + * @note Only used by GUEST_MSG_WAIT scenarios. + */ + int OldRunCurrent(const ClientRequest *pReq) + { + AssertPtrReturn(pReq, VERR_INVALID_POINTER); + + /* + * If the host message list is empty, the request must wait for one to be posted. + */ + HostMsg *pFirstMsg = RTListGetFirstCpp(&m_HostMsgList, HostMsg, m_ListEntry); + if (!pFirstMsg) + { + if (!m_fPendingCancel) + { + /* Go to sleep. */ + ASSERT_GUEST_RETURN(m_enmPendingMsg == 0, VERR_WRONG_ORDER); + m_PendingReq = *pReq; + m_enmPendingMsg = GUEST_MSG_WAIT; + LogFlowFunc(("[Client %RU32] Is now in pending mode\n", m_idClient)); + return VINF_HGCM_ASYNC_EXECUTE; + } + + /* Wait was cancelled. */ + m_fPendingCancel = false; + if (pReq->mNumParms > 0) + HGCMSvcSetU32(&pReq->mParms[0], HOST_MSG_CANCEL_PENDING_WAITS); + if (pReq->mNumParms > 1) + HGCMSvcSetU32(&pReq->mParms[1], 0); + return pReq->mNumParms == 2 ? VINF_SUCCESS : VERR_TRY_AGAIN; + } + + /* + * Return first host message. + */ + return OldRun(pReq, pFirstMsg); + } + + /** + * Internal worker for OldRun(). + * @note Only used for GUEST_MSG_WAIT. + */ + int OldSendReply(ClientRequest const *pReq, + HostMsg *pHostMsg) + { + AssertPtrReturn(pReq, VERR_INVALID_POINTER); + AssertPtrReturn(pHostMsg, VERR_INVALID_POINTER); + + /* In case of VERR_CANCELLED. */ + uint32_t const cSavedPeeks = mPeekCount; + + int rc; + /* If the client is in pending mode, always send back + * the peek result first. */ + if (m_enmPendingMsg) + { + Assert(m_enmPendingMsg == GUEST_MSG_WAIT); + rc = pHostMsg->Peek(pReq); + mPeekCount++; + } + else + { + /* If this is the very first peek, make sure to *always* give back the peeking answer + * instead of the actual message, even if this message would fit into the current + * connection buffer. */ + if (!mPeekCount) + { + rc = pHostMsg->Peek(pReq); + mPeekCount++; + } + else + { + /* Try assigning the host message to the client and store the + * result code for later use. */ + rc = pHostMsg->Assign(pReq); + if (RT_FAILURE(rc)) /* If something failed, let the client peek (again). */ + { + rc = pHostMsg->Peek(pReq); + mPeekCount++; + } + else + mPeekCount = 0; + } + } + + /* Reset pending status. */ + m_enmPendingMsg = (guestControl::eGuestMsg)0; + + /* In any case the client did something, so complete + * the pending call with the result we just got. */ + AssertPtr(m_pSvcHelpers); + int rc2 = m_pSvcHelpers->pfnCallComplete(pReq->mHandle, rc); + + /* Rollback in case the guest cancelled the call. */ + if (rc2 == VERR_CANCELLED && RT_SUCCESS(rc)) + { + mPeekCount = cSavedPeeks; + rc = VERR_CANCELLED; + } + + LogFlowThisFunc(("[Client %RU32] Message %RU32 ended with %Rrc (mPeekCount=%RU32, pReq=%p)\n", + m_idClient, pHostMsg->mType, rc, mPeekCount, pReq)); + return rc; + } + + /** @} */ +} ClientState; +typedef std::map< uint32_t, ClientState *> ClientStateMap; + +/** + * Prepared session (GUEST_SESSION_PREPARE). + */ +typedef struct GstCtrlPreparedSession +{ + /** List entry. */ + RTLISTNODE ListEntry; + /** The session ID. */ + uint32_t idSession; + /** The key size. */ + uint32_t cbKey; + /** The key bytes. */ + uint8_t abKey[RT_FLEXIBLE_ARRAY]; +} GstCtrlPreparedSession; + + +/** + * Class containing the shared information service functionality. + */ +class GstCtrlService : public RTCNonCopyable +{ + +private: + + /** Type definition for use in callback functions. */ + typedef GstCtrlService SELF; + /** HGCM helper functions. */ + PVBOXHGCMSVCHELPERS mpHelpers; + /** Callback function supplied by the host for notification of updates to properties. */ + PFNHGCMSVCEXT mpfnHostCallback; + /** User data pointer to be supplied to the host callback function. */ + void *mpvHostData; + /** Map containing all connected clients, key is HGCM client ID. */ + ClientStateMap m_ClientStateMap; + /** Session ID -> client state. */ + ClientStateMap m_SessionIdMap; + /** The current master client, NULL if none. */ + ClientState *m_pMasterClient; + /** The master HGCM client ID, UINT32_MAX if none. */ + uint32_t m_idMasterClient; + /** Set if we're in legacy mode (pre 6.0). */ + bool m_fLegacyMode; + /** Number of prepared sessions. */ + uint32_t m_cPreparedSessions; + /** List of prepared session (GstCtrlPreparedSession). */ + RTLISTANCHOR m_PreparedSessions; + +public: + explicit GstCtrlService(PVBOXHGCMSVCHELPERS pHelpers) + : mpHelpers(pHelpers) + , mpfnHostCallback(NULL) + , mpvHostData(NULL) + , m_pMasterClient(NULL) + , m_idMasterClient(UINT32_MAX) + , m_fLegacyMode(true) + , m_cPreparedSessions(0) + { + RTListInit(&m_PreparedSessions); + } + + static DECLCALLBACK(int) svcUnload(void *pvService); + static DECLCALLBACK(int) svcConnect(void *pvService, uint32_t idClient, void *pvClient, + uint32_t fRequestor, bool fRestoring); + static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t idClient, void *pvClient); + static DECLCALLBACK(void) svcCall(void *pvService, VBOXHGCMCALLHANDLE hCall, uint32_t idClient, void *pvClient, + uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival); + static DECLCALLBACK(int) svcHostCall(void *pvService, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + static DECLCALLBACK(int) svcSaveState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM); + static DECLCALLBACK(int) svcLoadState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion); + static DECLCALLBACK(int) svcRegisterExtension(void *pvService, PFNHGCMSVCEXT pfnExtension, void *pvExtension); + +private: + int clientMakeMeMaster(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms); + int clientMsgPeek(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fWait); + int clientMsgGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientMsgCancel(ClientState *pClient, uint32_t cParms); + int clientMsgSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientSessionPrepare(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientSessionCancelPrepared(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientSessionAccept(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientSessionCloseOther(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientToMain(ClientState *pClient, uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + + int clientMsgOldGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientMsgOldFilterSet(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int clientMsgOldSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms); + + int hostCallback(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int hostProcessMessage(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(GstCtrlService); +}; + + +/** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnUnload, + * Simply deletes the GstCtrlService object} + */ +/*static*/ DECLCALLBACK(int) +GstCtrlService::svcUnload(void *pvService) +{ + AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER); + SELF *pThis = reinterpret_cast<SELF *>(pvService); + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + + delete pThis; + + return VINF_SUCCESS; +} + + + +/** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect, + * Initializes the state for a new client.} + */ +/*static*/ DECLCALLBACK(int) +GstCtrlService::svcConnect(void *pvService, uint32_t idClient, void *pvClient, uint32_t fRequestor, bool fRestoring) +{ + LogFlowFunc(("[Client %RU32] Connected\n", idClient)); + + RT_NOREF(fRestoring, pvClient); + AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER); + SELF *pThis = reinterpret_cast<SELF *>(pvService); + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + + AssertMsg(pThis->m_ClientStateMap.find(idClient) == pThis->m_ClientStateMap.end(), + ("Client with ID=%RU32 already connected when it should not\n", idClient)); + + /* + * Create client state. + */ + ClientState *pClient = NULL; + try + { + pClient = new (pvClient) ClientState(pThis->mpHelpers, idClient); + pThis->m_ClientStateMap[idClient] = pClient; + } + catch (std::bad_alloc &) + { + if (pClient) + pClient->~ClientState(); + return VERR_NO_MEMORY; + } + + /* + * For legacy compatibility reasons we have to pick a master client at some + * point, so if the /dev/vboxguest requirements checks out we pick the first + * one through the door. + */ +/** @todo make picking the master more dynamic/flexible? */ + if ( pThis->m_fLegacyMode + && pThis->m_idMasterClient == UINT32_MAX) + { + if ( fRequestor == VMMDEV_REQUESTOR_LEGACY + || !(fRequestor & VMMDEV_REQUESTOR_USER_DEVICE)) + { + LogFunc(("Picking %u as master for now.\n", idClient)); + pThis->m_pMasterClient = pClient; + pThis->m_idMasterClient = idClient; + pClient->m_fIsMaster = true; + } + } + + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect, + * Handles a client which disconnected.} + * + * This functiond does some internal cleanup as well as sends notifications to + * the host so that the host can do the same (if required). + */ +/*static*/ DECLCALLBACK(int) +GstCtrlService::svcDisconnect(void *pvService, uint32_t idClient, void *pvClient) +{ + SELF *pThis = reinterpret_cast<SELF *>(pvService); + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + ClientState *pClient = reinterpret_cast<ClientState *>(pvClient); + AssertPtrReturn(pClient, VERR_INVALID_POINTER); + LogFlowFunc(("[Client %RU32] Disconnected (%zu clients total)\n", idClient, pThis->m_ClientStateMap.size())); + + /* + * Cancel all pending host messages, replying with GUEST_DISCONNECTED if final recipient. + */ + HostMsg *pCurMsg, *pNextMsg; + RTListForEachSafeCpp(&pClient->m_HostMsgList, pCurMsg, pNextMsg, HostMsg, m_ListEntry) + { + RTListNodeRemove(&pCurMsg->m_ListEntry); + + VBOXHGCMSVCPARM Parm; + HGCMSvcSetU32(&Parm, pCurMsg->m_idContext); + int rc2 = pThis->hostCallback(GUEST_MSG_DISCONNECTED, 1, &Parm); + LogFlowFunc(("Cancelled host message %u (%s) with idContext=%#x -> %Rrc\n", + pCurMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pCurMsg->mType), pCurMsg->m_idContext, rc2)); + RT_NOREF(rc2); + + pCurMsg->Delete(); + } + + /* + * Delete the client state. + */ + pThis->m_ClientStateMap.erase(idClient); + if (pClient->m_idSession != UINT32_MAX) + pThis->m_SessionIdMap.erase(pClient->m_idSession); + pClient->~ClientState(); + + /* + * If it's the master disconnecting, we need to reset related globals. + */ + if (idClient == pThis->m_idMasterClient) + { + pThis->m_pMasterClient = NULL; + pThis->m_idMasterClient = UINT32_MAX; + + GstCtrlPreparedSession *pCur, *pNext; + RTListForEachSafe(&pThis->m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry) + { + RTListNodeRemove(&pCur->ListEntry); + RTMemFree(pCur); + } + pThis->m_cPreparedSessions = 0; + } + else + Assert(pClient != pThis->m_pMasterClient); + + if (pThis->m_ClientStateMap.empty()) + pThis->m_fLegacyMode = true; + + return VINF_SUCCESS; +} + + +/** + * A client asks for the next message to process. + * + * This either fills in a pending host message into the client's parameter space + * or defers the guest call until we have something from the host. + * + * @returns VBox status code. + * @param pClient The client state. + * @param hCall The client's call handle. + * @param cParms Number of parameters. + * @param paParms Array of parameters. + */ +int GstCtrlService::clientMsgOldGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + ASSERT_GUEST(pClient->m_idSession != UINT32_MAX || pClient->m_fIsMaster || pClient->m_fRestored); + + /* Use the current (inbound) connection. */ + ClientRequest thisCon; + thisCon.mHandle = hCall; + thisCon.mNumParms = cParms; + thisCon.mParms = paParms; + + return pClient->OldRunCurrent(&thisCon); +} + + +/** + * Implements GUEST_MAKE_ME_MASTER. + * + * @returns VBox status code. + * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here). + * @retval VERR_ACCESS_DENIED if not using main VBoxGuest device not + * @retval VERR_RESOURCE_BUSY if there is already a master. + * @retval VERR_VERSION_MISMATCH if VBoxGuest didn't supply requestor info. + * @retval VERR_WRONG_PARAMETER_COUNT + * + * @param pClient The client state. + * @param hCall The client's call handle. + * @param cParms Number of parameters. + */ +int GstCtrlService::clientMakeMeMaster(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms) +{ + /* + * Validate the request. + */ + ASSERT_GUEST_RETURN(cParms == 0, VERR_WRONG_PARAMETER_COUNT); + + uint32_t fRequestor = mpHelpers->pfnGetRequestor(hCall); + ASSERT_GUEST_LOGREL_MSG_RETURN(fRequestor != VMMDEV_REQUESTOR_LEGACY, + ("Outdated VBoxGuest w/o requestor support. Please update!\n"), + VERR_VERSION_MISMATCH); + ASSERT_GUEST_LOGREL_MSG_RETURN(!(fRequestor & VMMDEV_REQUESTOR_USER_DEVICE), ("fRequestor=%#x\n", fRequestor), + VERR_ACCESS_DENIED); + + /* + * Do the work. + */ + ASSERT_GUEST_MSG_RETURN(m_idMasterClient == pClient->m_idClient || m_idMasterClient == UINT32_MAX, + ("Already have master session %RU32, refusing %RU32.\n", m_idMasterClient, pClient->m_idClient), + VERR_RESOURCE_BUSY); + int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS); + if (RT_SUCCESS(rc)) + { + m_pMasterClient = pClient; + m_idMasterClient = pClient->m_idClient; + m_fLegacyMode = false; + pClient->m_fIsMaster = true; + Log(("[Client %RU32] is master.\n", pClient->m_idClient)); + } + else + LogFunc(("pfnCallComplete -> %Rrc\n", rc)); + + return VINF_HGCM_ASYNC_EXECUTE; +} + +/** + * Implements GUEST_MSG_PEEK_WAIT and GUEST_MSG_PEEK_NOWAIT. + * + * @returns VBox status code. + * @retval VINF_SUCCESS if a message was pending and is being returned. + * @retval VERR_TRY_AGAIN if no message pending and not blocking. + * @retval VERR_RESOURCE_BUSY if another read already made a waiting call. + * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending. + * + * @param pClient The client state. + * @param hCall The client's call handle. + * @param cParms Number of parameters. + * @param paParms Array of parameters. + * @param fWait Set if we should wait for a message, clear if to return + * immediately. + */ +int GstCtrlService::clientMsgPeek(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fWait) +{ + /* + * Validate the request. + */ + ASSERT_GUEST_MSG_RETURN(cParms >= 2, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT); + + uint64_t idRestoreCheck = 0; + uint32_t i = 0; + if (paParms[i].type == VBOX_HGCM_SVC_PARM_64BIT) + { + idRestoreCheck = paParms[0].u.uint64; + paParms[0].u.uint64 = 0; + i++; + } + for (; i < cParms; i++) + { + ASSERT_GUEST_MSG_RETURN(paParms[i].type == VBOX_HGCM_SVC_PARM_32BIT, ("#%u type=%u\n", i, paParms[i].type), + VERR_WRONG_PARAMETER_TYPE); + paParms[i].u.uint32 = 0; + } + + /* + * Check restore session ID. + */ + if (idRestoreCheck != 0) + { + uint64_t idRestore = mpHelpers->pfnGetVMMDevSessionId(mpHelpers); + if (idRestoreCheck != idRestore) + { + paParms[0].u.uint64 = idRestore; + LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_XXXX -> VERR_VM_RESTORED (%#RX64 -> %#RX64)\n", + pClient->m_idClient, idRestoreCheck, idRestore)); + return VERR_VM_RESTORED; + } + Assert(!mpHelpers->pfnIsCallRestored(hCall)); + } + + /* + * Return information about the first message if one is pending in the list. + */ + HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry); + if (pFirstMsg) + { + pFirstMsg->setPeekReturn(paParms, cParms); + LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_XXXX -> VINF_SUCCESS (idMsg=%u (%s), cParms=%u)\n", + pClient->m_idClient, pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType), pFirstMsg->mParmCount)); + return VINF_SUCCESS; + } + + /* + * If we cannot wait, fail the call. + */ + if (!fWait) + { + LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT -> VERR_TRY_AGAIN\n", pClient->m_idClient)); + return VERR_TRY_AGAIN; + } + + /* + * Wait for the host to queue a message for this client. + */ + ASSERT_GUEST_MSG_RETURN(pClient->m_enmPendingMsg == 0, ("Already pending! (idClient=%RU32)\n", pClient->m_idClient), + VERR_RESOURCE_BUSY); + pClient->m_PendingReq.mHandle = hCall; + pClient->m_PendingReq.mNumParms = cParms; + pClient->m_PendingReq.mParms = paParms; + pClient->m_enmPendingMsg = GUEST_MSG_PEEK_WAIT; + LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->m_idClient)); + return VINF_HGCM_ASYNC_EXECUTE; +} + +/** + * Implements GUEST_MSG_GET. + * + * @returns VBox status code. + * @retval VINF_SUCCESS if message retrieved and removed from the pending queue. + * @retval VERR_TRY_AGAIN if no message pending. + * @retval VERR_BUFFER_OVERFLOW if a parmeter buffer is too small. The buffer + * size was updated to reflect the required size, though this isn't yet + * forwarded to the guest. (The guest is better of using peek with + * parameter count + 2 parameters to get the sizes.) + * @retval VERR_MISMATCH if the incoming message ID does not match the pending. + * @retval VINF_HGCM_ASYNC_EXECUTE if message was completed already. + * + * @param pClient The client state. + * @param hCall The client's call handle. + * @param cParms Number of parameters. + * @param paParms Array of parameters. + */ +int GstCtrlService::clientMsgGet(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * Validate the request. + * + * The weird first parameter logic is due to GUEST_MSG_WAIT compatibility + * (don't want to rewrite all the message structures). + */ + uint32_t const idMsgExpected = cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT ? paParms[0].u.uint32 + : cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT ? paParms[0].u.uint64 + : UINT32_MAX; + + /* + * Return information about the first message if one is pending in the list. + */ + HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry); + if (pFirstMsg) + { + + ASSERT_GUEST_MSG_RETURN(pFirstMsg->mType == idMsgExpected || idMsgExpected == UINT32_MAX, + ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n", + pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType), pFirstMsg->mParmCount, + idMsgExpected, GstCtrlHostMsgtoStr((eHostMsg)idMsgExpected), cParms), + VERR_MISMATCH); + ASSERT_GUEST_MSG_RETURN(pFirstMsg->mParmCount == cParms, + ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n", + pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType), pFirstMsg->mParmCount, + idMsgExpected, GstCtrlHostMsgtoStr((eHostMsg)idMsgExpected), cParms), + VERR_WRONG_PARAMETER_COUNT); + + /* Check the parameter types. */ + for (uint32_t i = 0; i < cParms; i++) + ASSERT_GUEST_MSG_RETURN(pFirstMsg->mpParms[i].type == paParms[i].type, + ("param #%u: type %u, caller expected %u (idMsg=%u %s)\n", i, pFirstMsg->mpParms[i].type, + paParms[i].type, pFirstMsg->mType, GstCtrlHostMsgtoStr((eHostMsg)pFirstMsg->mType)), + VERR_WRONG_PARAMETER_TYPE); + + /* + * Copy out the parameters. + * + * No assertions on buffer overflows, and keep going till the end so we can + * communicate all the required buffer sizes. + */ + int rc = VINF_SUCCESS; + for (uint32_t i = 0; i < cParms; i++) + switch (pFirstMsg->mpParms[i].type) + { + case VBOX_HGCM_SVC_PARM_32BIT: + paParms[i].u.uint32 = pFirstMsg->mpParms[i].u.uint32; + break; + + case VBOX_HGCM_SVC_PARM_64BIT: + paParms[i].u.uint64 = pFirstMsg->mpParms[i].u.uint64; + break; + + case VBOX_HGCM_SVC_PARM_PTR: + { + uint32_t const cbSrc = pFirstMsg->mpParms[i].u.pointer.size; + uint32_t const cbDst = paParms[i].u.pointer.size; + paParms[i].u.pointer.size = cbSrc; /** @todo Check if this is safe in other layers... + * Update: Safe, yes, but VMMDevHGCM doesn't pass it along. */ + if (cbSrc <= cbDst) + memcpy(paParms[i].u.pointer.addr, pFirstMsg->mpParms[i].u.pointer.addr, cbSrc); + else + rc = VERR_BUFFER_OVERFLOW; + break; + } + + default: + AssertMsgFailed(("#%u: %u\n", i, pFirstMsg->mpParms[i].type)); + rc = VERR_INTERNAL_ERROR; + break; + } + if (RT_SUCCESS(rc)) + { + /* + * Complete the message and remove the pending message unless the + * guest raced us and cancelled this call in the meantime. + */ + AssertPtr(mpHelpers); + rc = mpHelpers->pfnCallComplete(hCall, rc); + if (rc != VERR_CANCELLED) + { + RTListNodeRemove(&pFirstMsg->m_ListEntry); + pFirstMsg->Delete(); + } + else + LogFunc(("pfnCallComplete -> %Rrc\n", rc)); + return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */ + } + return rc; + } + + paParms[0].u.uint32 = 0; + paParms[1].u.uint32 = 0; + LogFlowFunc(("[Client %RU32] GUEST_MSG_GET -> VERR_TRY_AGAIN\n", pClient->m_idClient)); + return VERR_TRY_AGAIN; +} + +/** + * Implements GUEST_MSG_CANCEL. + * + * @returns VBox status code. + * @retval VINF_SUCCESS if cancelled any calls. + * @retval VWRN_NOT_FOUND if no callers. + * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending. + * + * @param pClient The client state. + * @param cParms Number of parameters. + */ +int GstCtrlService::clientMsgCancel(ClientState *pClient, uint32_t cParms) +{ + /* + * Validate the request. + */ + ASSERT_GUEST_MSG_RETURN(cParms == 0, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT); + + /* + * Execute. + */ + if (pClient->m_enmPendingMsg != 0) + { + pClient->CancelWaiting(); + return VINF_SUCCESS; + } + return VWRN_NOT_FOUND; +} + + +/** + * Implements GUEST_MSG_SKIP. + * + * @returns VBox status code. + * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message. + * @retval VERR_NOT_FOUND if no message pending. + * + * @param pClient The client state. + * @param hCall The call handle for completing it. + * @param cParms Number of parameters. + * @param paParms The parameters. + */ +int GstCtrlService::clientMsgSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * Validate the call. + */ + ASSERT_GUEST_RETURN(cParms <= 2, VERR_WRONG_PARAMETER_COUNT); + + int32_t rcSkip = VERR_NOT_SUPPORTED; + if (cParms >= 1) + { + ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); + rcSkip = (int32_t)paParms[0].u.uint32; + } + + uint32_t idMsg = UINT32_MAX; + if (cParms >= 2) + { + ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); + idMsg = paParms[1].u.uint32; + } + + /* + * Do the job. + */ + HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry); + if (pFirstMsg) + { + if ( pFirstMsg->mType == idMsg + || idMsg == UINT32_MAX) + { + int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS); + if (RT_SUCCESS(rc)) + { + /* + * Remove the message from the queue. + */ + Assert(RTListNodeIsFirst(&pClient->m_HostMsgList, &pFirstMsg->m_ListEntry) ); + RTListNodeRemove(&pFirstMsg->m_ListEntry); + + /* + * Compose a reply to the host service. + */ + VBOXHGCMSVCPARM aReplyParams[5]; + HGCMSvcSetU32(&aReplyParams[0], pFirstMsg->m_idContext); + switch (pFirstMsg->mType) + { + case HOST_MSG_EXEC_CMD: + HGCMSvcSetU32(&aReplyParams[1], 0); /* pid */ + HGCMSvcSetU32(&aReplyParams[2], PROC_STS_ERROR); /* status */ + HGCMSvcSetU32(&aReplyParams[3], rcSkip); /* flags / whatever */ + HGCMSvcSetPv(&aReplyParams[4], NULL, 0); /* data buffer */ + hostCallback(GUEST_MSG_EXEC_STATUS, 5, aReplyParams); + break; + + case HOST_MSG_SESSION_CREATE: + HGCMSvcSetU32(&aReplyParams[1], GUEST_SESSION_NOTIFYTYPE_ERROR); /* type */ + HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* result */ + hostCallback(GUEST_MSG_SESSION_NOTIFY, 3, aReplyParams); + break; + + case HOST_MSG_EXEC_SET_INPUT: + HGCMSvcSetU32(&aReplyParams[1], pFirstMsg->mParmCount >= 2 ? pFirstMsg->mpParms[1].u.uint32 : 0); + HGCMSvcSetU32(&aReplyParams[2], INPUT_STS_ERROR); /* status */ + HGCMSvcSetU32(&aReplyParams[3], rcSkip); /* flags / whatever */ + HGCMSvcSetU32(&aReplyParams[4], 0); /* bytes consumed */ + hostCallback(GUEST_MSG_EXEC_INPUT_STATUS, 5, aReplyParams); + break; + + case HOST_MSG_FILE_OPEN: + HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_OPEN); /* type*/ + HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */ + HGCMSvcSetU32(&aReplyParams[3], VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pFirstMsg->m_idContext)); /* handle */ + hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams); + break; + case HOST_MSG_FILE_CLOSE: + HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_ERROR); /* type*/ + HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */ + hostCallback(GUEST_MSG_FILE_NOTIFY, 3, aReplyParams); + break; + case HOST_MSG_FILE_READ: + case HOST_MSG_FILE_READ_AT: + HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_READ); /* type */ + HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */ + HGCMSvcSetPv(&aReplyParams[3], NULL, 0); /* data buffer */ + hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams); + break; + case HOST_MSG_FILE_WRITE: + case HOST_MSG_FILE_WRITE_AT: + HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_WRITE); /* type */ + HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */ + HGCMSvcSetU32(&aReplyParams[3], 0); /* bytes written */ + hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams); + break; + case HOST_MSG_FILE_SEEK: + HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_SEEK); /* type */ + HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */ + HGCMSvcSetU64(&aReplyParams[3], 0); /* actual */ + hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams); + break; + case HOST_MSG_FILE_TELL: + HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_TELL); /* type */ + HGCMSvcSetU32(&aReplyParams[2], rcSkip); /* rc */ + HGCMSvcSetU64(&aReplyParams[3], 0); /* actual */ + hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams); + break; + + case HOST_MSG_EXEC_GET_OUTPUT: /** @todo This can't be right/work. */ + case HOST_MSG_EXEC_TERMINATE: /** @todo This can't be right/work. */ + case HOST_MSG_EXEC_WAIT_FOR: /** @todo This can't be right/work. */ + case HOST_MSG_PATH_USER_DOCUMENTS: + case HOST_MSG_PATH_USER_HOME: + case HOST_MSG_PATH_RENAME: + case HOST_MSG_DIR_REMOVE: + default: + HGCMSvcSetU32(&aReplyParams[1], pFirstMsg->mType); + HGCMSvcSetU32(&aReplyParams[2], (uint32_t)rcSkip); + HGCMSvcSetPv(&aReplyParams[3], NULL, 0); + hostCallback(GUEST_MSG_REPLY, 4, aReplyParams); + break; + } + + /* + * Free the message. + */ + pFirstMsg->Delete(); + } + else + LogFunc(("pfnCallComplete -> %Rrc\n", rc)); + return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */ + } + LogFunc(("Warning: GUEST_MSG_SKIP mismatch! Found %u, caller expected %u!\n", pFirstMsg->mType, idMsg)); + return VERR_MISMATCH; + } + return VERR_NOT_FOUND; +} + + +/** + * Implements GUEST_SESSION_PREPARE. + * + * @returns VBox status code. + * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message. + * @retval VERR_OUT_OF_RESOURCES if too many pending sessions hanging around. + * @retval VERR_OUT_OF_RANGE if the session ID outside the allowed range. + * @retval VERR_BUFFER_OVERFLOW if key too large. + * @retval VERR_BUFFER_UNDERFLOW if key too small. + * @retval VERR_ACCESS_DENIED if not master or in legacy mode. + * @retval VERR_DUPLICATE if the session ID has been prepared already. + * + * @param pClient The client state. + * @param hCall The call handle for completing it. + * @param cParms Number of parameters. + * @param paParms The parameters. + */ +int GstCtrlService::clientSessionPrepare(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * Validate parameters. + */ + ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT); + ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); + uint32_t const idSession = paParms[0].u.uint32; + ASSERT_GUEST_RETURN(idSession >= 1, VERR_OUT_OF_RANGE); + ASSERT_GUEST_RETURN(idSession <= 0xfff0, VERR_OUT_OF_RANGE); + + ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); + uint32_t const cbKey = paParms[1].u.pointer.size; + void const *pvKey = paParms[1].u.pointer.addr; + ASSERT_GUEST_RETURN(cbKey >= 64, VERR_BUFFER_UNDERFLOW); + ASSERT_GUEST_RETURN(cbKey <= _16K, VERR_BUFFER_OVERFLOW); + + ASSERT_GUEST_RETURN(pClient->m_fIsMaster, VERR_ACCESS_DENIED); + ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED); + Assert(m_idMasterClient == pClient->m_idClient); + Assert(m_pMasterClient == pClient); + + /* Now that we know it's the master, we can check for session ID duplicates. */ + GstCtrlPreparedSession *pCur; + RTListForEach(&m_PreparedSessions, pCur, GstCtrlPreparedSession, ListEntry) + { + ASSERT_GUEST_RETURN(pCur->idSession != idSession, VERR_DUPLICATE); + } + + /* + * Make a copy of the session ID and key. + */ + ASSERT_GUEST_RETURN(m_cPreparedSessions < 128, VERR_OUT_OF_RESOURCES); + + GstCtrlPreparedSession *pPrepped = (GstCtrlPreparedSession *)RTMemAlloc(RT_UOFFSETOF_DYN(GstCtrlPreparedSession, abKey[cbKey])); + AssertReturn(pPrepped, VERR_NO_MEMORY); + pPrepped->idSession = idSession; + pPrepped->cbKey = cbKey; + memcpy(pPrepped->abKey, pvKey, cbKey); + + RTListAppend(&m_PreparedSessions, &pPrepped->ListEntry); + m_cPreparedSessions++; + + /* + * Try complete the message. + */ + int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS); + if (RT_SUCCESS(rc)) + LogFlow(("Prepared %u with a %#x byte key (%u pending).\n", idSession, cbKey, m_cPreparedSessions)); + else + { + LogFunc(("pfnCallComplete -> %Rrc\n", rc)); + RTListNodeRemove(&pPrepped->ListEntry); + RTMemFree(pPrepped); + m_cPreparedSessions--; + } + return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */ +} + + +/** + * Implements GUEST_SESSION_CANCEL_PREPARED. + * + * @returns VBox status code. + * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message. + * @retval VWRN_NOT_FOUND if no session with the specified ID. + * @retval VERR_ACCESS_DENIED if not master or in legacy mode. + * + * @param pClient The client state. + * @param cParms Number of parameters. + * @param paParms The parameters. + */ +int GstCtrlService::clientSessionCancelPrepared(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * Validate parameters. + */ + ASSERT_GUEST_RETURN(cParms == 1, VERR_WRONG_PARAMETER_COUNT); + ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); + uint32_t const idSession = paParms[0].u.uint32; + + ASSERT_GUEST_RETURN(pClient->m_fIsMaster, VERR_ACCESS_DENIED); + ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED); + Assert(m_idMasterClient == pClient->m_idClient); + Assert(m_pMasterClient == pClient); + + /* + * Do the work. + */ + int rc = VWRN_NOT_FOUND; + if (idSession == UINT32_MAX) + { + GstCtrlPreparedSession *pCur, *pNext; + RTListForEachSafe(&m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry) + { + RTListNodeRemove(&pCur->ListEntry); + RTMemFree(pCur); + rc = VINF_SUCCESS; + } + m_cPreparedSessions = 0; + } + else + { + GstCtrlPreparedSession *pCur, *pNext; + RTListForEachSafe(&m_PreparedSessions, pCur, pNext, GstCtrlPreparedSession, ListEntry) + { + if (pCur->idSession == idSession) + { + RTListNodeRemove(&pCur->ListEntry); + RTMemFree(pCur); + m_cPreparedSessions -= 1; + rc = VINF_SUCCESS; + break; + } + } + } + return VINF_SUCCESS; +} + + +/** + * Implements GUEST_SESSION_ACCEPT. + * + * @returns VBox status code. + * @retval VINF_HGCM_ASYNC_EXECUTE on success as we complete the message. + * @retval VERR_NOT_FOUND if the specified session ID wasn't found. + * @retval VERR_MISMATCH if the key didn't match. + * @retval VERR_ACCESS_DENIED if we're in legacy mode or is master. + * @retval VERR_RESOURCE_BUSY if the client is already associated with a + * session. + * + * @param pClient The client state. + * @param hCall The call handle for completing it. + * @param cParms Number of parameters. + * @param paParms The parameters. + */ +int GstCtrlService::clientSessionAccept(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * Validate parameters. + */ + ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT); + ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); + uint32_t const idSession = paParms[0].u.uint32; + ASSERT_GUEST_RETURN(idSession >= 1, VERR_OUT_OF_RANGE); + ASSERT_GUEST_RETURN(idSession <= 0xfff0, VERR_OUT_OF_RANGE); + + ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); + uint32_t const cbKey = paParms[1].u.pointer.size; + void const *pvKey = paParms[1].u.pointer.addr; + ASSERT_GUEST_RETURN(cbKey >= 64, VERR_BUFFER_UNDERFLOW); + ASSERT_GUEST_RETURN(cbKey <= _16K, VERR_BUFFER_OVERFLOW); + + ASSERT_GUEST_RETURN(!pClient->m_fIsMaster, VERR_ACCESS_DENIED); + ASSERT_GUEST_RETURN(!m_fLegacyMode, VERR_ACCESS_DENIED); + Assert(m_idMasterClient != pClient->m_idClient); + Assert(m_pMasterClient != pClient); + ASSERT_GUEST_RETURN(pClient->m_idSession == UINT32_MAX, VERR_RESOURCE_BUSY); + + /* + * Look for the specified session and match the key to it. + */ + GstCtrlPreparedSession *pCur; + RTListForEach(&m_PreparedSessions, pCur, GstCtrlPreparedSession, ListEntry) + { + if (pCur->idSession == idSession) + { + if ( pCur->cbKey == cbKey + && memcmp(pCur->abKey, pvKey, cbKey) == 0) + { + /* + * We've got a match. + * Try insert it into the sessio ID map and complete the request. + */ + try + { + m_SessionIdMap[idSession] = pClient; + } + catch (std::bad_alloc &) + { + LogFunc(("Out of memory!\n")); + return VERR_NO_MEMORY; + } + + int rc = mpHelpers->pfnCallComplete(hCall, VINF_SUCCESS); + if (RT_SUCCESS(rc)) + { + pClient->m_idSession = idSession; + + RTListNodeRemove(&pCur->ListEntry); + RTMemFree(pCur); + m_cPreparedSessions -= 1; + Log(("[Client %RU32] accepted session id %u.\n", pClient->m_idClient, idSession)); + } + else + { + LogFunc(("pfnCallComplete -> %Rrc\n", rc)); + m_SessionIdMap.erase(idSession); + } + return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */ + } + LogFunc(("Key mismatch for %u!\n", pClient->m_idClient)); + return VERR_MISMATCH; + } + } + + LogFunc(("No client prepared for %u!\n", pClient->m_idClient)); + return VERR_NOT_FOUND; +} + + +/** + * Client asks another client (guest) session to close. + * + * @returns VBox status code. + * @param pClient The client state. + * @param cParms Number of parameters. + * @param paParms Array of parameters. + */ +int GstCtrlService::clientSessionCloseOther(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * Validate input. + */ + ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT); + ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); + uint32_t const idContext = paParms[0].u.uint32; + + ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); + uint32_t const fFlags = paParms[1].u.uint32; + + ASSERT_GUEST_RETURN(pClient->m_fIsMaster || (m_fLegacyMode && pClient->m_idSession == UINT32_MAX), VERR_ACCESS_DENIED); + + /* + * Forward the message to the destiation. + * Since we modify the first parameter, we must make a copy of the parameters. + */ + VBOXHGCMSVCPARM aParms[2]; + HGCMSvcSetU64(&aParms[0], idContext | VBOX_GUESTCTRL_DST_SESSION); + HGCMSvcSetU32(&aParms[1], fFlags); + int rc = hostProcessMessage(HOST_MSG_SESSION_CLOSE, RT_ELEMENTS(aParms), aParms); + + LogFlowFunc(("Closing guest context ID=%RU32 (from client ID=%RU32) returned with rc=%Rrc\n", idContext, pClient->m_idClient, rc)); + return rc; +} + + +/** + * For compatiblity with old additions only - filtering / set session ID. + * + * @return VBox status code. + * @param pClient The client state. + * @param cParms Number of parameters. + * @param paParms Array of parameters. + */ +int GstCtrlService::clientMsgOldFilterSet(ClientState *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * Validate input and access. + */ + ASSERT_GUEST_RETURN(cParms == 4, VERR_WRONG_PARAMETER_COUNT); + ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); + uint32_t uValue = paParms[0].u.uint32; + ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); + uint32_t fMaskAdd = paParms[1].u.uint32; + ASSERT_GUEST_RETURN(paParms[2].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); + uint32_t fMaskRemove = paParms[2].u.uint32; + ASSERT_GUEST_RETURN(paParms[3].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* flags, unused */ + + /* + * We have a bunch of expectations here: + * - Never called in non-legacy mode. + * - Only called once per session. + * - Never called by the master session. + * - Clients that doesn't wish for any messages passes all zeros. + * - All other calls has a unique session ID. + */ + ASSERT_GUEST_LOGREL_RETURN(m_fLegacyMode, VERR_WRONG_ORDER); + ASSERT_GUEST_LOGREL_MSG_RETURN(pClient->m_idSession == UINT32_MAX, ("m_idSession=%#x\n", pClient->m_idSession), + VERR_WRONG_ORDER); + ASSERT_GUEST_LOGREL_RETURN(!pClient->m_fIsMaster, VERR_WRONG_ORDER); + + if (uValue == 0) + { + ASSERT_GUEST_LOGREL(fMaskAdd == 0); + ASSERT_GUEST_LOGREL(fMaskRemove == 0); + /* Nothing to do, already muted (UINT32_MAX). */ + } + else + { + ASSERT_GUEST_LOGREL(fMaskAdd == UINT32_C(0xf8000000)); + ASSERT_GUEST_LOGREL(fMaskRemove == 0); + + uint32_t idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uValue); + ASSERT_GUEST_LOGREL_MSG_RETURN(idSession > 0, ("idSession=%u (%#x)\n", idSession, uValue), VERR_OUT_OF_RANGE); + + ClientStateMap::iterator ItConflict = m_SessionIdMap.find(idSession); + ASSERT_GUEST_LOGREL_MSG_RETURN(ItConflict == m_SessionIdMap.end(), + ("idSession=%u uValue=%#x idClient=%u; conflicting with client %u\n", + idSession, uValue, pClient->m_idClient, ItConflict->second->m_idClient), + VERR_DUPLICATE); + + /* Commit it. */ + try + { + m_SessionIdMap[idSession] = pClient; + } + catch (std::bad_alloc &) + { + LogFunc(("Out of memory\n")); + return VERR_NO_MEMORY; + } + pClient->m_idSession = idSession; + } + return VINF_SUCCESS; +} + + +/** + * For compatibility with old additions only - skip the current message w/o + * calling main code. + * + * Please note that we don't care if the caller cancelled the request, because + * old additions code didn't give damn about VERR_INTERRUPT. + * + * @return VBox status code. + * @param pClient The client state. + * @param hCall The call handle for completing it. + * @param cParms Number of parameters. + */ +int GstCtrlService::clientMsgOldSkip(ClientState *pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms) +{ + /* + * Validate input and access. + */ + ASSERT_GUEST_RETURN(cParms == 1, VERR_WRONG_PARAMETER_COUNT); + + /* + * Execute the request. + * + * Note! As it turns out the old and new skip should be mostly the same. The + * pre-6.0 GAs (up to BETA3) has a hack which tries to issue a + * VERR_NOT_SUPPORTED reply to unknown host requests, however the 5.2.x + * and earlier GAs doesn't. We need old skip behavior only for the 6.0 + * beta GAs, nothing else. + * So, we have to track whether they issued a MSG_REPLY or not. Wonderful. + */ + HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry); + if (pFirstMsg) + { + uint32_t const idMsg = pFirstMsg->mType; + bool const f60BetaHackInPlay = pFirstMsg->m_f60BetaHackInPlay; + int rc; + if (!f60BetaHackInPlay) + rc = clientMsgSkip(pClient, hCall, 0, NULL); + else + { + RTListNodeRemove(&pFirstMsg->m_ListEntry); + pFirstMsg->Delete(); + rc = VINF_SUCCESS; + } + + /* Reset legacy message wait/get state: */ + if (RT_SUCCESS(rc)) + { + pClient->mHostMsgRc = VINF_SUCCESS; + pClient->mHostMsgTries = 0; + pClient->mPeekCount = 0; + } + + LogFlowFunc(("[Client %RU32] Legacy message skipping: Skipped %u (%s)%s!\n", + pClient->m_idClient, idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg), f60BetaHackInPlay ? " hack style" : "")); + NOREF(idMsg); + return rc; + } + LogFlowFunc(("[Client %RU32] Legacy message skipping: No messages pending!\n", pClient->m_idClient)); + return VINF_SUCCESS; +} + + +/** + * Forwards client call to the Main API. + * + * This is typically notifications and replys. + * + * @returns VBox status code. + * @param pClient The client state. + * @param idMsg Message ID that occured. + * @param cParms Number of parameters. + * @param paParms Array of parameters. + */ +int GstCtrlService::clientToMain(ClientState *pClient, uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * Do input validation. This class of messages all have a 32-bit context ID as + * the first parameter, so make sure it is there and appropriate for the caller. + */ + ASSERT_GUEST_RETURN(cParms >= 1, VERR_WRONG_PARAMETER_COUNT); + ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_COUNT); + uint32_t const idContext = paParms[0].u.uint32; + uint32_t const idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(idContext); + + ASSERT_GUEST_MSG_RETURN( pClient->m_idSession == idSession + || pClient->m_fIsMaster + || ( m_fLegacyMode /* (see bugref:9313#c16) */ + && pClient->m_idSession == UINT32_MAX + && ( idMsg == GUEST_MSG_EXEC_STATUS + || idMsg == GUEST_MSG_SESSION_NOTIFY)), + ("idSession=%u (CID=%#x) m_idSession=%u idClient=%u idMsg=%u (%s)\n", idSession, idContext, + pClient->m_idSession, pClient->m_idClient, idMsg, GstCtrlGuestMsgToStr((eGuestMsg)idMsg)), + VERR_ACCESS_DENIED); + + /* + * It seems okay, so make the call. + */ + return hostCallback(idMsg, cParms, paParms); +} + + +/** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnCall} + * + * @note All functions which do not involve an unreasonable delay will be + * handled synchronously. If needed, we will add a request handler + * thread in future for those which do. + * @thread HGCM + */ +/*static*/ DECLCALLBACK(void) +GstCtrlService::svcCall(void *pvService, VBOXHGCMCALLHANDLE hCall, uint32_t idClient, void *pvClient, + uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival) +{ + LogFlowFunc(("[Client %RU32] u32Function=%RU32 (%s), cParms=%RU32, paParms=0x%p\n", + idClient, u32Function, GstCtrlGuestMsgToStr((eGuestMsg)u32Function), cParms, paParms)); + RT_NOREF(tsArrival, idClient); + + /* + * Convert opaque pointers to typed ones. + */ + SELF *pThis = reinterpret_cast<SELF *>(pvService); + AssertReturnVoidStmt(pThis, pThis->mpHelpers->pfnCallComplete(hCall, VERR_INTERNAL_ERROR_5)); + ClientState *pClient = reinterpret_cast<ClientState *>(pvClient); + AssertReturnVoidStmt(pClient, pThis->mpHelpers->pfnCallComplete(hCall, VERR_INVALID_CLIENT_ID)); + Assert(pClient->m_idClient == idClient); + + /* + * Do the dispatching. + */ + int rc; + switch (u32Function) + { + case GUEST_MSG_MAKE_ME_MASTER: + LogFlowFunc(("[Client %RU32] GUEST_MAKE_ME_MASTER\n", idClient)); + rc = pThis->clientMakeMeMaster(pClient, hCall, cParms); + break; + case GUEST_MSG_PEEK_NOWAIT: + LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT\n", idClient)); + rc = pThis->clientMsgPeek(pClient, hCall, cParms, paParms, false /*fWait*/); + break; + case GUEST_MSG_PEEK_WAIT: + LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_WAIT\n", idClient)); + rc = pThis->clientMsgPeek(pClient, hCall, cParms, paParms, true /*fWait*/); + break; + case GUEST_MSG_GET: + LogFlowFunc(("[Client %RU32] GUEST_MSG_GET\n", idClient)); + rc = pThis->clientMsgGet(pClient, hCall, cParms, paParms); + break; + case GUEST_MSG_CANCEL: + LogFlowFunc(("[Client %RU32] GUEST_MSG_CANCEL\n", idClient)); + rc = pThis->clientMsgCancel(pClient, cParms); + break; + case GUEST_MSG_SKIP: + LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP\n", idClient)); + rc = pThis->clientMsgSkip(pClient, hCall, cParms, paParms); + break; + case GUEST_MSG_SESSION_PREPARE: + LogFlowFunc(("[Client %RU32] GUEST_SESSION_PREPARE\n", idClient)); + rc = pThis->clientSessionPrepare(pClient, hCall, cParms, paParms); + break; + case GUEST_MSG_SESSION_CANCEL_PREPARED: + LogFlowFunc(("[Client %RU32] GUEST_SESSION_CANCEL_PREPARED\n", idClient)); + rc = pThis->clientSessionCancelPrepared(pClient, cParms, paParms); + break; + case GUEST_MSG_SESSION_ACCEPT: + LogFlowFunc(("[Client %RU32] GUEST_SESSION_ACCEPT\n", idClient)); + rc = pThis->clientSessionAccept(pClient, hCall, cParms, paParms); + break; + case GUEST_MSG_SESSION_CLOSE: + LogFlowFunc(("[Client %RU32] GUEST_SESSION_CLOSE\n", idClient)); + rc = pThis->clientSessionCloseOther(pClient, cParms, paParms); + break; + + /* + * Stuff the goes to various main objects: + */ + case GUEST_MSG_REPLY: + if (cParms >= 3 && paParms[2].u.uint32 == (uint32_t)VERR_NOT_SUPPORTED) + { + HostMsg *pFirstMsg = RTListGetFirstCpp(&pClient->m_HostMsgList, HostMsg, m_ListEntry); + if (pFirstMsg && pFirstMsg->m_idContext == paParms[0].u.uint32) + pFirstMsg->m_f60BetaHackInPlay = true; + } + RT_FALL_THROUGH(); + case GUEST_MSG_PROGRESS_UPDATE: + case GUEST_MSG_SESSION_NOTIFY: + case GUEST_MSG_EXEC_OUTPUT: + case GUEST_MSG_EXEC_STATUS: + case GUEST_MSG_EXEC_INPUT_STATUS: + case GUEST_MSG_EXEC_IO_NOTIFY: + case GUEST_MSG_DIR_NOTIFY: + case GUEST_MSG_FILE_NOTIFY: + LogFlowFunc(("[Client %RU32] %s\n", idClient, GstCtrlGuestMsgToStr((eGuestMsg)u32Function))); + rc = pThis->clientToMain(pClient, u32Function /* Msg */, cParms, paParms); + Assert(rc != VINF_HGCM_ASYNC_EXECUTE); + break; + + /* + * The remaining messages are here for compatibility with older Guest Additions: + */ + case GUEST_MSG_WAIT: + LogFlowFunc(("[Client %RU32] GUEST_MSG_WAIT\n", idClient)); + pThis->clientMsgOldGet(pClient, hCall, cParms, paParms); + rc = VINF_HGCM_ASYNC_EXECUTE; + break; + + case GUEST_MSG_SKIP_OLD: + LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP_OLD\n", idClient)); + rc = pThis->clientMsgOldSkip(pClient, hCall, cParms); + break; + + case GUEST_MSG_FILTER_SET: + LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_SET\n", idClient)); + rc = pThis->clientMsgOldFilterSet(pClient, cParms, paParms); + break; + + case GUEST_MSG_FILTER_UNSET: + LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_UNSET\n", idClient)); + rc = VERR_NOT_IMPLEMENTED; + break; + + /* + * Anything else shall return invalid function. + * Note! We used to return VINF_SUCCESS for these. See bugref:9313 + * and Guest::i_notifyCtrlDispatcher(). + */ + default: + ASSERT_GUEST_MSG_FAILED(("u32Function=%RU32 (%#x)\n", u32Function, u32Function)); + rc = VERR_INVALID_FUNCTION; + break; + } + + if (rc != VINF_HGCM_ASYNC_EXECUTE) + { + /* Tell the client that the call is complete (unblocks waiting). */ + LogFlowFunc(("[Client %RU32] Calling pfnCallComplete w/ rc=%Rrc\n", idClient, rc)); + AssertPtr(pThis->mpHelpers); + pThis->mpHelpers->pfnCallComplete(hCall, rc); + } +} + + +/** + * Notifies the host (using low-level HGCM callbacks) about an event + * which was sent from the client. + * + * @returns VBox status code. + * @param u32Function Message ID that occured. + * @param cParms Number of parameters. + * @param paParms Array of parameters. + */ +int GstCtrlService::hostCallback(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + LogFlowFunc(("u32Function=%RU32 (%s), cParms=%ld, paParms=%p\n", + u32Function, GstCtrlGuestMsgToStr((eGuestMsg)u32Function), cParms, paParms)); + + int rc; + if (mpfnHostCallback) + { + VBOXGUESTCTRLHOSTCALLBACK data(cParms, paParms); + rc = mpfnHostCallback(mpvHostData, u32Function, &data, sizeof(data)); + } + else + rc = VERR_NOT_SUPPORTED; + + LogFlowFunc(("Returning rc=%Rrc\n", rc)); + return rc; +} + + +/** + * Processes a message received from the host side and re-routes it to + * a connect client on the guest. + * + * @returns VBox status code. + * @param idMsg Message ID to process. + * @param cParms Number of parameters. + * @param paParms Array of parameters. + */ +int GstCtrlService::hostProcessMessage(uint32_t idMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + /* + * If no client is connected at all we don't buffer any host messages + * and immediately return an error to the host. This avoids the host + * waiting for a response from the guest side in case VBoxService on + * the guest is not running/system is messed up somehow. + */ + if (m_ClientStateMap.empty()) + { + LogFlow(("GstCtrlService::hostProcessMessage: VERR_NOT_FOUND!\n")); + return VERR_NOT_FOUND; + } + + /* + * Create a host message for each destination. + * Note! There is currently only one scenario in which we send a host + * message to two recipients. + */ + HostMsg *pHostMsg = new (std::nothrow) HostMsg(); + AssertReturn(pHostMsg, VERR_NO_MEMORY); + int rc = pHostMsg->Init(idMsg, cParms, paParms); + if (RT_SUCCESS(rc)) + { + uint64_t const fDestinations = pHostMsg->m_idContextAndDst & VBOX_GUESTCTRL_DST_BOTH; + HostMsg *pHostMsg2 = NULL; + if (fDestinations != VBOX_GUESTCTRL_DST_BOTH) + { /* likely */ } + else + { + pHostMsg2 = new (std::nothrow) HostMsg(); + if (pHostMsg2) + rc = pHostMsg2->Init(idMsg, cParms, paParms); + else + rc = VERR_NO_MEMORY; + } + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("Handling host message m_idContextAndDst=%#RX64, idMsg=%RU32, cParms=%RU32, paParms=%p, cClients=%zu\n", + pHostMsg->m_idContextAndDst, idMsg, cParms, paParms, m_ClientStateMap.size())); + + /* + * Find the message destination and post it to the client. If the + * session ID doesn't match any particular client it goes to the master. + */ + AssertMsg(!m_ClientStateMap.empty(), ("Client state map is empty when it should not be!\n")); + + /* Dispatch to the session. */ + if (fDestinations & VBOX_GUESTCTRL_DST_SESSION) + { + uint32_t const idSession = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHostMsg->m_idContext); + ClientStateMap::iterator It = m_SessionIdMap.find(idSession); + if (It != m_SessionIdMap.end()) + { + ClientState *pClient = It->second; + Assert(pClient->m_idSession == idSession); + RTListAppend(&pClient->m_HostMsgList, &pHostMsg->m_ListEntry); + pHostMsg = pHostMsg2; + pHostMsg2 = NULL; + + int rc2 = pClient->Wakeup(); + LogFlowFunc(("Woke up client ID=%RU32 -> rc=%Rrc\n", pClient->m_idClient, rc2)); + RT_NOREF(rc2); + rc = VINF_SUCCESS; + } + else + { + LogFunc(("No client with session ID %u was found! (idMsg=%d %s)\n", + idSession, idMsg, GstCtrlHostMsgtoStr((eHostMsg)idMsg))); + rc = !(fDestinations & VBOX_GUESTCTRL_DST_ROOT_SVC) ? VERR_NOT_FOUND : VWRN_NOT_FOUND; + } + } + + /* Does the message go to the root service? */ + if ( (fDestinations & VBOX_GUESTCTRL_DST_ROOT_SVC) + && RT_SUCCESS(rc)) + { + Assert(pHostMsg); + if (m_pMasterClient) + { + RTListAppend(&m_pMasterClient->m_HostMsgList, &pHostMsg->m_ListEntry); + pHostMsg = NULL; + + int rc2 = m_pMasterClient->Wakeup(); + LogFlowFunc(("Woke up client ID=%RU32 (master) -> rc=%Rrc\n", m_pMasterClient->m_idClient, rc2)); + NOREF(rc2); + } + else + rc = VERR_NOT_FOUND; + } + } + + /* Drop unset messages. */ + if (pHostMsg2) + pHostMsg2->Delete(); + } + if (pHostMsg) + pHostMsg->Delete(); + + if (RT_FAILURE(rc)) + LogFunc(("Failed %Rrc (idMsg=%u, cParms=%u)\n", rc, idMsg, cParms)); + return rc; +} + + +/** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnHostCall, + * Wraps to the hostProcessMessage() member function.} + */ +/*static*/ DECLCALLBACK(int) +GstCtrlService::svcHostCall(void *pvService, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER); + SELF *pThis = reinterpret_cast<SELF *>(pvService); + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + + LogFlowFunc(("u32Function=%RU32, cParms=%RU32, paParms=0x%p\n", u32Function, cParms, paParms)); + AssertReturn(u32Function != HOST_MSG_CANCEL_PENDING_WAITS, VERR_INVALID_FUNCTION); + return pThis->hostProcessMessage(u32Function, cParms, paParms); +} + + + + +/** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnSaveState} + */ +/*static*/ DECLCALLBACK(int) +GstCtrlService::svcSaveState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM) +{ + RT_NOREF(pvClient); + SELF *pThis = reinterpret_cast<SELF *>(pvService); + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + + /* Note! We don't need to save the idSession here because it's only used + for sessions and the sessions are not persistent across a state + save/restore. The Main objects aren't there. Clients shuts down. + Only the root service survives, so remember who that is and its mode. */ + + SSMR3PutU32(pSSM, 1); + SSMR3PutBool(pSSM, pThis->m_fLegacyMode); + return SSMR3PutBool(pSSM, idClient == pThis->m_idMasterClient); +} + + +/** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnLoadState} + */ +/*static*/ DECLCALLBACK(int) +GstCtrlService::svcLoadState(void *pvService, uint32_t idClient, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion) +{ + SELF *pThis = reinterpret_cast<SELF *>(pvService); + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + ClientState *pClient = reinterpret_cast<ClientState *>(pvClient); + AssertReturn(pClient, VERR_INVALID_CLIENT_ID); + Assert(pClient->m_idClient == idClient); + + if (uVersion >= HGCM_SAVED_STATE_VERSION) + { + uint32_t uSubVersion; + int rc = SSMR3GetU32(pSSM, &uSubVersion); + AssertRCReturn(rc, rc); + if (uSubVersion != 1) + return SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, + "sub version %u, expected 1\n", uSubVersion); + bool fLegacyMode; + rc = SSMR3GetBool(pSSM, &fLegacyMode); + AssertRCReturn(rc, rc); + pThis->m_fLegacyMode = fLegacyMode; + + bool fIsMaster; + rc = SSMR3GetBool(pSSM, &fIsMaster); + AssertRCReturn(rc, rc); + + pClient->m_fIsMaster = fIsMaster; + if (fIsMaster) + { + pThis->m_pMasterClient = pClient; + pThis->m_idMasterClient = idClient; + } + } + else + { + /* + * For old saved states we have to guess at who should be the master. + * Given how HGCMService::CreateAndConnectClient and associates manage + * and saves the client, the first client connecting will be restored + * first. The only time this might go wrong if the there are zombie + * VBoxService session processes in the restored guest, and I don't + * we need to care too much about that scenario. + * + * Given how HGCM first re-connects the clients before this function + * gets called, there isn't anything we need to do here it turns out. :-) + */ + } + pClient->m_fRestored = true; + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnRegisterExtension, + * Installs a host callback for notifications of property changes.} + */ +/*static*/ DECLCALLBACK(int) GstCtrlService::svcRegisterExtension(void *pvService, PFNHGCMSVCEXT pfnExtension, void *pvExtension) +{ + SELF *pThis = reinterpret_cast<SELF *>(pvService); + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + AssertPtrNullReturn(pfnExtension, VERR_INVALID_POINTER); + + pThis->mpfnHostCallback = pfnExtension; + pThis->mpvHostData = pvExtension; + return VINF_SUCCESS; +} + + +/** + * @copydoc VBOXHGCMSVCLOAD + */ +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("pTable=%p\n", pTable)); + + if (!VALID_PTR(pTable)) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + LogFlowFunc(("pTable->cbSize=%d, pTable->u32Version=0x%08X\n", pTable->cbSize, pTable->u32Version)); + + if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE) + || pTable->u32Version != VBOX_HGCM_SVC_VERSION) + { + rc = VERR_VERSION_MISMATCH; + } + else + { + GstCtrlService *pService = NULL; + /* No exceptions may propagate outside. */ + try + { + pService = new GstCtrlService(pTable->pHelpers); + } + catch (int rcThrown) + { + rc = rcThrown; + } + catch(std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + } + + if (RT_SUCCESS(rc)) + { + /* + * We don't need an additional client data area on the host, + * because we're a class which can have members for that :-). + */ + pTable->cbClient = sizeof(ClientState); + + /* Register functions. */ + pTable->pfnUnload = GstCtrlService::svcUnload; + pTable->pfnConnect = GstCtrlService::svcConnect; + pTable->pfnDisconnect = GstCtrlService::svcDisconnect; + pTable->pfnCall = GstCtrlService::svcCall; + pTable->pfnHostCall = GstCtrlService::svcHostCall; + pTable->pfnSaveState = GstCtrlService::svcSaveState; + pTable->pfnLoadState = GstCtrlService::svcLoadState; + pTable->pfnRegisterExtension = GstCtrlService::svcRegisterExtension; + pTable->pfnNotify = NULL; + + /* Service specific initialization. */ + pTable->pvService = pService; + } + else + { + if (pService) + { + delete pService; + pService = NULL; + } + } + } + } + + LogFlowFunc(("Returning %Rrc\n", rc)); + return rc; +} + diff --git a/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.rc b/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.rc new file mode 100644 index 00000000..c72facc4 --- /dev/null +++ b/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxGuestControlSvc.rc $ */ +/** @file + * VBoxGuestControlSvc - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox Guest Control Host Service\0" + VALUE "InternalName", "VBoxGuestControl\0" + VALUE "OriginalFilename", "VBoxGuestControl.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk b/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk new file mode 100644 index 00000000..ac513c7c --- /dev/null +++ b/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk @@ -0,0 +1,46 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Guest Control Host Service testcases. +# + +# +# Copyright (C) 2010-2019 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. +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK) + + # Set this in LocalConfig.kmk if you are working on the guest property + # service to automatically run the testcase at build time. + # OTHERS += $(tstGuestControlSvc_0_OUTDIR)/tstGuestControlSvc.run + # + + PROGRAMS += tstGuestControlSvc + TESTING += $(tstGuestControlSvc_0_OUTDIR)/tstGuestControlSvc.run + tstGuestControlSvc_TEMPLATE = VBOXR3TSTEXE + # The second define here is to ensure that the testcase will run fast, + # without waiting for any thread synchronisation. + tstGuestControlSvc_DEFS = VBOX_WITH_HGCM VBOX_GUEST_CONTROL_TEST_NOTHREAD + tstGuestControlSvc_SOURCES = \ + ../VBoxGuestControlSvc.cpp \ + tstGuestControlSvc.cpp + tstGuestControlSvc_LIBS = $(LIB_RUNTIME) $(LIB_VMM) + +$$(tstGuestControlSvc_0_OUTDIR)/tstGuestControlSvc.run: $$(tstGuestControlSvc_1_STAGE_TARGET) + export VBOX_LOG_DEST=nofile; $(tstGuestControlSvc_1_STAGE_TARGET) quiet + $(QUIET)$(APPEND) -t "$@" "done" + +endif + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp b/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp new file mode 100644 index 00000000..8f4dd460 --- /dev/null +++ b/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp @@ -0,0 +1,272 @@ +/* $Id: tstGuestControlSvc.cpp $ */ +/** @file + * Testcase for the guest control service. + */ + +/* + * Copyright (C) 2011-2019 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 * +*********************************************************************************************************************************/ +#include <VBox/HostServices/GuestControlSvc.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> +#include <iprt/test.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static RTTEST g_hTest = NIL_RTTEST; + +using namespace guestControl; + +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable); + +/** Simple call handle structure for the guest call completion callback */ +struct VBOXHGCMCALLHANDLE_TYPEDEF +{ + /** Where to store the result code. */ + int32_t rc; +}; + +/** Call completion callback for guest calls. */ +static DECLCALLBACK(int) callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc) +{ + callHandle->rc = rc; + return VINF_SUCCESS; +} + +/** + * Initialise the HGCM service table as much as we need to start the + * service. + * + * @return IPRT status code. + * @param pTable the table to initialise + */ +int initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers) +{ + pTable->cbSize = sizeof (VBOXHGCMSVCFNTABLE); + pTable->u32Version = VBOX_HGCM_SVC_VERSION; + pHelpers->pfnCallComplete = callComplete; + pTable->pHelpers = pHelpers; + + return VINF_SUCCESS; +} + +typedef struct CMDHOST +{ + /** The HGCM command to execute. */ + int cmd; + /** Number of parameters. */ + int num_parms; + /** The actual parameters. */ + const PVBOXHGCMSVCPARM parms; + /** Flag indicating whether we need a connected client for this command. */ + bool fNeedsClient; + /** The desired return value from the host. */ + int rc; +} CMDHOST, *PCMDHOST; + +typedef struct CMDCLIENT +{ + /** The client's ID. */ + int client_id; + /** The HGCM command to execute. */ + int cmd; + /** Number of parameters. */ + int num_parms; + /** The actual parameters. */ + const PVBOXHGCMSVCPARM parms; + /** The desired return value from the host. */ + int rc; +} CMDCLIENT, *PCMDCLIENT; + +/** + * Tests the HOST_EXEC_CMD function. + * @returns iprt status value to indicate whether the test went as expected. + * @note prints its own diagnostic information to stdout. + */ +static int testHostCmd(const VBOXHGCMSVCFNTABLE *pTable, const PCMDHOST pCmd, uint32_t uNumTests) +{ + int rc = VINF_SUCCESS; + if (!VALID_PTR(pTable->pfnHostCall)) + { + RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Invalid pfnHostCall() pointer\n"); + rc = VERR_INVALID_POINTER; + } + if (RT_SUCCESS(rc)) + { + for (unsigned i = 0; (i < uNumTests) && RT_SUCCESS(rc); i++) + { + RTTestPrintf(g_hTest, RTTESTLVL_INFO, "Testing #%u (cmd: %d, num_parms: %d, parms: 0x%p\n", + i, pCmd[i].cmd, pCmd[i].num_parms, pCmd[i].parms); + + if (pCmd[i].fNeedsClient) + { + int client_rc = pTable->pfnConnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */, 0, false); + if (RT_FAILURE(client_rc)) + rc = client_rc; + } + + if (RT_SUCCESS(rc)) + { + int host_rc = pTable->pfnHostCall(pTable->pvService, + pCmd[i].cmd, + pCmd[i].num_parms, + pCmd[i].parms); + if (host_rc != pCmd[i].rc) + { + RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Host call test #%u returned with rc=%Rrc instead of rc=%Rrc\n", + i, host_rc, pCmd[i].rc); + rc = host_rc; + if (RT_SUCCESS(rc)) + rc = VERR_INVALID_PARAMETER; + } + + if (pCmd[i].fNeedsClient) + { + int client_rc = pTable->pfnDisconnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */); + if (RT_SUCCESS(rc)) + rc = client_rc; + } + } + } + } + return rc; +} + +static int testHost(const VBOXHGCMSVCFNTABLE *pTable) +{ + RTTestSub(g_hTest, "Testing host commands ..."); + + VBOXHGCMSVCPARM aParms[1]; + HGCMSvcSetU32(&aParms[0], 1000 /* Context ID */); + + CMDHOST aCmdHostAll[] = + { +#if 0 + /** No client connected. */ + { 1024 /* Not existing command */, 0, 0, false, VERR_NOT_FOUND }, + { -1 /* Invalid command */, 0, 0, false, VERR_NOT_FOUND }, + { HOST_CANCEL_PENDING_WAITS, 1024, 0, false, VERR_NOT_FOUND }, + { HOST_CANCEL_PENDING_WAITS, 0, &aParms[0], false, VERR_NOT_FOUND }, + + /** No client connected, valid command. */ + { HOST_CANCEL_PENDING_WAITS, 0, 0, false, VERR_NOT_FOUND }, + + /** Client connected, no parameters given. */ + { HOST_EXEC_SET_INPUT, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER }, + { 1024 /* Not existing command */, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER }, + { -1 /* Invalid command */, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER }, + + /** Client connected, valid parameters given. */ + { HOST_CANCEL_PENDING_WAITS, 0, 0, true, VINF_SUCCESS }, + { HOST_CANCEL_PENDING_WAITS, 1024, &aParms[0], true, VINF_SUCCESS }, + { HOST_CANCEL_PENDING_WAITS, 0, &aParms[0], true, VINF_SUCCESS}, +#endif + + /** Client connected, invalid parameters given. */ + { HOST_MSG_EXEC_CMD, 1024, 0, true, VERR_INVALID_POINTER }, + { HOST_MSG_EXEC_CMD, 1, 0, true, VERR_INVALID_POINTER }, + { HOST_MSG_EXEC_CMD, -1, 0, true, VERR_INVALID_POINTER }, + + /** Client connected, parameters given. */ + { HOST_MSG_CANCEL_PENDING_WAITS, 1, &aParms[0], true, VINF_SUCCESS }, + { HOST_MSG_EXEC_CMD, 1, &aParms[0], true, VINF_SUCCESS }, + { HOST_MSG_EXEC_SET_INPUT, 1, &aParms[0], true, VINF_SUCCESS }, + { HOST_MSG_EXEC_GET_OUTPUT, 1, &aParms[0], true, VINF_SUCCESS }, + + /** Client connected, unknown command + valid parameters given. */ + { -1, 1, &aParms[0], true, VINF_SUCCESS } + }; + + int rc = testHostCmd(pTable, &aCmdHostAll[0], RT_ELEMENTS(aCmdHostAll)); + RTTestSubDone(g_hTest); + return rc; +} + +static int testClient(const VBOXHGCMSVCFNTABLE *pTable) +{ + RTTestSub(g_hTest, "Testing client commands ..."); + + int rc = pTable->pfnConnect(pTable->pvService, 1 /* Client ID */, NULL /* pvClient */, 0, false); + if (RT_SUCCESS(rc)) + { + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + + /* No commands from host yet. */ + VBOXHGCMSVCPARM aParmsGuest[8]; + HGCMSvcSetU32(&aParmsGuest[0], 0 /* Msg type */); + HGCMSvcSetU32(&aParmsGuest[1], 0 /* Parameters */); + pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */, + GUEST_MSG_WAIT, 2, &aParmsGuest[0], 0); + RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc); + + /* Host: Add a dummy command. */ + VBOXHGCMSVCPARM aParmsHost[8]; + HGCMSvcSetU32(&aParmsHost[0], 1000 /* Context ID */); + HGCMSvcSetStr(&aParmsHost[1], "foo.bar"); + HGCMSvcSetStr(&aParmsHost[2], "baz"); + + rc = pTable->pfnHostCall(pTable->pvService, HOST_MSG_EXEC_CMD, 3, &aParmsHost[0]); + RTTEST_CHECK_RC_RET(g_hTest, rc, VINF_SUCCESS, rc); + + /* Client: Disconnect again. */ + int rc2 = pTable->pfnDisconnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + RTTestSubDone(g_hTest); + return rc; +} + +/* + * Set environment variable "IPRT_TEST_MAX_LEVEL=all" to get more debug output! + */ +int main() +{ + RTEXITCODE rcExit = RTTestInitAndCreate("tstGuestControlSvc", &g_hTest); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + RTTestBanner(g_hTest); + + /* Some host info. */ + RTTestIPrintf(RTTESTLVL_ALWAYS, "sizeof(void*)=%d\n", sizeof(void*)); + + /* Do the tests. */ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + RTTEST_CHECK_RC_RET(g_hTest, initTable(&svcTable, &svcHelpers), VINF_SUCCESS, 1); + + do + { + RTTESTI_CHECK_RC_BREAK(VBoxHGCMSvcLoad(&svcTable), VINF_SUCCESS); + + RTTESTI_CHECK_RC_BREAK(testHost(&svcTable), VINF_SUCCESS); + + RTTESTI_CHECK_RC_BREAK(svcTable.pfnUnload(svcTable.pvService), VINF_SUCCESS); + + RTTESTI_CHECK_RC_BREAK(VBoxHGCMSvcLoad(&svcTable), VINF_SUCCESS); + + RTTESTI_CHECK_RC_BREAK(testClient(&svcTable), VINF_SUCCESS); + + RTTESTI_CHECK_RC_BREAK(svcTable.pfnUnload(svcTable.pvService), VINF_SUCCESS); + + } while (0); + + return RTTestSummaryAndDestroy(g_hTest); +} + diff --git a/src/VBox/HostServices/GuestProperties/Makefile.kmk b/src/VBox/HostServices/GuestProperties/Makefile.kmk new file mode 100644 index 00000000..d5c78d8e --- /dev/null +++ b/src/VBox/HostServices/GuestProperties/Makefile.kmk @@ -0,0 +1,50 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Shared Info Services Host Service. +# + +# +# Copyright (C) 2006-2019 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. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# Include sub-makefile(s). +include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk + +# +# The shared folder service DLL. +# +DLLS += VBoxGuestPropSvc +VBoxGuestPropSvc_TEMPLATE = VBOXR3 +VBoxGuestPropSvc_NAME.os2 = VBoxSIS +VBoxGuestPropSvc_DEFS = VBOX_WITH_HGCM +VBoxGuestPropSvc_INCS = $(PATH_ROOT)/src/VBox/Main/include +VBoxGuestPropSvc_INCS.win = \ + $(VBOX_PATH_SDK) + +VBoxGuestPropSvc_SOURCES = \ + VBoxGuestPropSvc.cpp + +VBoxGuestPropSvc_SOURCES.win = \ + VBoxGuestPropSvc.rc + +VBoxGuestPropSvc_LIBS = \ + $(LIB_VMM) \ + $(LIB_RUNTIME) \ + $(LIB_REM) + +VBoxGuestPropSvc_LDFLAGS.darwin = \ + -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxGuestPropSvc.dylib + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.cpp b/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.cpp new file mode 100644 index 00000000..58f95269 --- /dev/null +++ b/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.cpp @@ -0,0 +1,1894 @@ +/* $Id: VBoxGuestPropSvc.cpp $ */ +/** @file + * Guest Property Service: Host service entry points. + */ + +/* + * Copyright (C) 2008-2019 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. + */ + +/** @page pg_svc_guest_properties Guest Property HGCM Service + * + * This HGCM service allows the guest to set and query values in a property + * store on the host. The service proxies the guest requests to the service + * owner on the host using a request callback provided by the owner, and is + * notified of changes to properties made by the host. It forwards these + * notifications to clients in the guest which have expressed interest and + * are waiting for notification. + * + * The service currently consists of two threads. One of these is the main + * HGCM service thread which deals with requests from the guest and from the + * host. The second thread sends the host asynchronous notifications of + * changes made by the guest and deals with notification timeouts. + * + * Guest requests to wait for notification are added to a list of open + * notification requests and completed when a corresponding guest property + * is changed or when the request times out. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_HGCM +#include <VBox/HostServices/GuestPropertySvc.h> + +#include <VBox/log.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/buildconfig.h> +#include <iprt/cpp/autores.h> +#include <iprt/cpp/utils.h> +#include <iprt/cpp/ministring.h> +#include <VBox/err.h> +#include <VBox/hgcmsvc.h> +#include <iprt/mem.h> +#include <iprt/req.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <iprt/time.h> +#include <VBox/vmm/dbgf.h> +#include <VBox/version.h> + +#include <list> + + +namespace guestProp { + +/** + * Structure for holding a property + */ +struct Property +{ + /** The string space core record. */ + RTSTRSPACECORE mStrCore; + /** The name of the property */ + RTCString mName; + /** The property value */ + RTCString mValue; + /** The timestamp of the property */ + uint64_t mTimestamp; + /** The property flags */ + uint32_t mFlags; + + /** Default constructor */ + Property() : mTimestamp(0), mFlags(GUEST_PROP_F_NILFLAG) + { + RT_ZERO(mStrCore); + } + /** Constructor with const char * */ + Property(const char *pcszName, const char *pcszValue, uint64_t nsTimestamp, uint32_t u32Flags) + : mName(pcszName) + , mValue(pcszValue) + , mTimestamp(nsTimestamp) + , mFlags(u32Flags) + { + RT_ZERO(mStrCore); + mStrCore.pszString = mName.c_str(); + } + /** Constructor with std::string */ + Property(RTCString const &rName, RTCString const &rValue, uint64_t nsTimestamp, uint32_t fFlags) + : mName(rName) + , mValue(rValue) + , mTimestamp(nsTimestamp) + , mFlags(fFlags) + {} + + /** Does the property name match one of a set of patterns? */ + bool Matches(const char *pszPatterns) const + { + return ( pszPatterns[0] == '\0' /* match all */ + || RTStrSimplePatternMultiMatch(pszPatterns, RTSTR_MAX, + mName.c_str(), RTSTR_MAX, + NULL) + ); + } + + /** Are two properties equal? */ + bool operator==(const Property &prop) + { + if (mTimestamp != prop.mTimestamp) + return false; + if (mFlags != prop.mFlags) + return false; + if (mName != prop.mName) + return false; + if (mValue != prop.mValue) + return false; + return true; + } + + /* Is the property nil? */ + bool isNull() + { + return mName.isEmpty(); + } +}; +/** The properties list type */ +typedef std::list <Property> PropertyList; + +/** + * Structure for holding an uncompleted guest call + */ +struct GuestCall +{ + uint32_t u32ClientId; + /** The call handle */ + VBOXHGCMCALLHANDLE mHandle; + /** The function that was requested */ + uint32_t mFunction; + /** Number of call parameters. */ + uint32_t mParmsCnt; + /** The call parameters */ + VBOXHGCMSVCPARM *mParms; + /** The default return value, used for passing warnings */ + int mRc; + + /** The standard constructor */ + GuestCall(void) : u32ClientId(0), mFunction(0), mParmsCnt(0) {} + /** The normal constructor */ + GuestCall(uint32_t aClientId, VBOXHGCMCALLHANDLE aHandle, uint32_t aFunction, + uint32_t aParmsCnt, VBOXHGCMSVCPARM aParms[], int aRc) + : u32ClientId(aClientId), mHandle(aHandle), mFunction(aFunction), + mParmsCnt(aParmsCnt), mParms(aParms), mRc(aRc) {} +}; +/** The guest call list type */ +typedef std::list <GuestCall> CallList; + +/** + * Class containing the shared information service functionality. + */ +class Service : public RTCNonCopyable +{ +private: + /** Type definition for use in callback functions */ + typedef Service SELF; + /** HGCM helper functions. */ + PVBOXHGCMSVCHELPERS mpHelpers; + /** Global flags for the service */ + uint32_t mfGlobalFlags; + /** The property string space handle. */ + RTSTRSPACE mhProperties; + /** The number of properties. */ + unsigned mcProperties; + /** The list of property changes for guest notifications; + * only used for timestamp tracking in notifications at the moment */ + PropertyList mGuestNotifications; + /** The list of outstanding guest notification calls */ + CallList mGuestWaiters; + /** @todo we should have classes for thread and request handler thread */ + /** Callback function supplied by the host for notification of updates + * to properties */ + PFNHGCMSVCEXT mpfnHostCallback; + /** User data pointer to be supplied to the host callback function */ + void *mpvHostData; + /** The previous timestamp. + * This is used by getCurrentTimestamp() to decrease the chance of + * generating duplicate timestamps. */ + uint64_t mPrevTimestamp; + /** The number of consecutive timestamp adjustments that we've made. + * Together with mPrevTimestamp, this defines a set of obsolete timestamp + * values: {(mPrevTimestamp - mcTimestampAdjustments), ..., mPrevTimestamp} */ + uint64_t mcTimestampAdjustments; + /** For helping setting host version properties _after_ restoring VMs. */ + bool m_fSetHostVersionProps; + + /** + * Get the next property change notification from the queue of saved + * notification based on the timestamp of the last notification seen. + * Notifications will only be reported if the property name matches the + * pattern given. + * + * @returns iprt status value + * @returns VWRN_NOT_FOUND if the last notification was not found in the queue + * @param pszPatterns the patterns to match the property name against + * @param nsTimestamp the timestamp of the last notification + * @param pProp where to return the property found. If none is + * found this will be set to nil. + * @throws nothing + * @thread HGCM + */ + int getOldNotification(const char *pszPatterns, uint64_t nsTimestamp, Property *pProp) + { + AssertPtrReturn(pszPatterns, VERR_INVALID_POINTER); + /* Zero means wait for a new notification. */ + AssertReturn(nsTimestamp != 0, VERR_INVALID_PARAMETER); + AssertPtrReturn(pProp, VERR_INVALID_POINTER); + int rc = getOldNotificationInternal(pszPatterns, nsTimestamp, pProp); +#ifdef VBOX_STRICT + /* + * ENSURE that pProp is the first event in the notification queue that: + * - Appears later than nsTimestamp + * - Matches the pszPatterns + */ + /** @todo r=bird: This incorrectly ASSUMES that mTimestamp is unique. + * The timestamp resolution can be very coarse on windows for instance. */ + PropertyList::const_iterator it = mGuestNotifications.begin(); + for (; it != mGuestNotifications.end() + && it->mTimestamp != nsTimestamp; ++it) + { /*nothing*/ } + if (it == mGuestNotifications.end()) /* Not found */ + it = mGuestNotifications.begin(); + else + ++it; /* Next event */ + for (; it != mGuestNotifications.end() + && it->mTimestamp != pProp->mTimestamp; ++it) + Assert(!it->Matches(pszPatterns)); + if (pProp->mTimestamp != 0) + { + Assert(*pProp == *it); + Assert(pProp->Matches(pszPatterns)); + } +#endif /* VBOX_STRICT */ + return rc; + } + + /** + * Check whether we have permission to change a property. + * + * @returns Strict VBox status code. + * @retval VINF_SUCCESS if we do. + * @retval VERR_PERMISSION_DENIED if the value is read-only for the requesting + * side. + * @retval VINF_PERMISSION_DENIED if the side is globally marked read-only. + * + * @param fFlags the flags on the property in question + * @param isGuest is the guest or the host trying to make the change? + */ + int checkPermission(uint32_t fFlags, bool isGuest) + { + if (fFlags & (isGuest ? GUEST_PROP_F_RDONLYGUEST : GUEST_PROP_F_RDONLYHOST)) + return VERR_PERMISSION_DENIED; + if (isGuest && (mfGlobalFlags & GUEST_PROP_F_RDONLYGUEST)) + return VINF_PERMISSION_DENIED; + return VINF_SUCCESS; + } + + /** + * Check whether the property name is reserved for host changes only. + * + * @returns Boolean true (host reserved) or false (available to guest). + * + * @param pszName The property name to check. + */ + bool checkHostReserved(const char *pszName) + { + if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/VBoxService/")) + return true; + if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/PAM/")) + return true; + if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/Greeter/")) + return true; + if (RTStrStartsWith(pszName, "/VirtualBox/GuestAdd/SharedFolders/")) + return true; + if (RTStrStartsWith(pszName, "/VirtualBox/HostInfo/")) + return true; + if (RTStrStartsWith(pszName, "/VirtualBox/VMInfo/")) + return true; + return false; + } + + /** + * Gets a property. + * + * @returns Pointer to the property if found, NULL if not. + * + * @param pszName The name of the property to get. + */ + Property *getPropertyInternal(const char *pszName) + { + return (Property *)RTStrSpaceGet(&mhProperties, pszName); + } + +public: + explicit Service(PVBOXHGCMSVCHELPERS pHelpers) + : mpHelpers(pHelpers) + , mfGlobalFlags(GUEST_PROP_F_NILFLAG) + , mhProperties(NULL) + , mcProperties(0) + , mpfnHostCallback(NULL) + , mpvHostData(NULL) + , mPrevTimestamp(0) + , mcTimestampAdjustments(0) + , m_fSetHostVersionProps(false) + , mhThreadNotifyHost(NIL_RTTHREAD) + , mhReqQNotifyHost(NIL_RTREQQUEUE) + { } + + /** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnUnload} + * Simply deletes the service object + */ + static DECLCALLBACK(int) svcUnload(void *pvService) + { + AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER); + SELF *pSelf = reinterpret_cast<SELF *>(pvService); + int rc = pSelf->uninit(); + AssertRC(rc); + if (RT_SUCCESS(rc)) + delete pSelf; + return rc; + } + + /** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnConnect} + * Stub implementation of pfnConnect. + */ + static DECLCALLBACK(int) svcConnect(void * /* pvService */, + uint32_t /* u32ClientID */, + void * /* pvClient */, + uint32_t /*fRequestor*/, + bool /*fRestoring*/) + { + return VINF_SUCCESS; + } + + static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t idClient, void *pvClient); + + /** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnCall} + * Wraps to the call member function + */ + static DECLCALLBACK(void) svcCall(void * pvService, + VBOXHGCMCALLHANDLE callHandle, + uint32_t u32ClientID, + void *pvClient, + uint32_t u32Function, + uint32_t cParms, + VBOXHGCMSVCPARM paParms[], + uint64_t tsArrival) + { + AssertLogRelReturnVoid(VALID_PTR(pvService)); + LogFlowFunc(("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms)); + SELF *pSelf = reinterpret_cast<SELF *>(pvService); + pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms); + LogFlowFunc(("returning\n")); + RT_NOREF_PV(tsArrival); + } + + /** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnHostCall} + * Wraps to the hostCall member function + */ + static DECLCALLBACK(int) svcHostCall(void *pvService, + uint32_t u32Function, + uint32_t cParms, + VBOXHGCMSVCPARM paParms[]) + { + AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER); + LogFlowFunc(("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms)); + SELF *pSelf = reinterpret_cast<SELF *>(pvService); + int rc = pSelf->hostCall(u32Function, cParms, paParms); + LogFlowFunc(("rc=%Rrc\n", rc)); + return rc; + } + + /** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnRegisterExtension} + * Installs a host callback for notifications of property changes. + */ + static DECLCALLBACK(int) svcRegisterExtension(void *pvService, + PFNHGCMSVCEXT pfnExtension, + void *pvExtension) + { + AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER); + SELF *pSelf = reinterpret_cast<SELF *>(pvService); + pSelf->mpfnHostCallback = pfnExtension; + pSelf->mpvHostData = pvExtension; + return VINF_SUCCESS; + } + + int setHostVersionProps(); + void incrementCounterProp(const char *pszName); + static DECLCALLBACK(void) svcNotify(void *pvService, HGCMNOTIFYEVENT enmEvent); + + int initialize(); + +private: + static DECLCALLBACK(int) reqThreadFn(RTTHREAD ThreadSelf, void *pvUser); + uint64_t getCurrentTimestamp(void); + int validateName(const char *pszName, uint32_t cbName); + int validateValue(const char *pszValue, uint32_t cbValue); + int setPropertyBlock(uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int getProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int setProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest); + int setPropertyInternal(const char *pcszName, const char *pcszValue, uint32_t fFlags, uint64_t nsTimestamp, + bool fIsGuest = false); + int delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest); + int enumProps(uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int getOldNotificationInternal(const char *pszPattern, uint64_t nsTimestamp, Property *pProp); + int getNotificationWriteOut(uint32_t cParms, VBOXHGCMSVCPARM paParms[], Property const &prop); + int doNotifications(const char *pszProperty, uint64_t nsTimestamp); + int notifyHost(const char *pszName, const char *pszValue, uint64_t nsTimestamp, const char *pszFlags); + + void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, + void *pvClient, uint32_t eFunction, uint32_t cParms, + VBOXHGCMSVCPARM paParms[]); + int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]); + int uninit(); + static DECLCALLBACK(void) dbgInfo(void *pvUser, PCDBGFINFOHLP pHlp, const char *pszArgs); + + /* Thread for handling host notifications. */ + RTTHREAD mhThreadNotifyHost; + /* Queue for handling requests for notifications. */ + RTREQQUEUE mhReqQNotifyHost; + static DECLCALLBACK(int) threadNotifyHost(RTTHREAD self, void *pvUser); + + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(Service); +}; + + +/** + * Gets the current timestamp. + * + * Since the RTTimeNow resolution can be very coarse, this method takes some + * simple steps to try avoid returning the same timestamp for two consecutive + * calls. Code like getOldNotification() more or less assumes unique + * timestamps. + * + * @returns Nanosecond timestamp. + */ +uint64_t Service::getCurrentTimestamp(void) +{ + RTTIMESPEC time; + uint64_t u64NanoTS = RTTimeSpecGetNano(RTTimeNow(&time)); + if (mPrevTimestamp - u64NanoTS > mcTimestampAdjustments) + mcTimestampAdjustments = 0; + else + { + mcTimestampAdjustments++; + u64NanoTS = mPrevTimestamp + 1; + } + this->mPrevTimestamp = u64NanoTS; + return u64NanoTS; +} + +/** + * Check that a string fits our criteria for a property name. + * + * @returns IPRT status code + * @param pszName the string to check, must be valid Utf8 + * @param cbName the number of bytes @a pszName points to, including the + * terminating '\0' + * @thread HGCM + */ +int Service::validateName(const char *pszName, uint32_t cbName) +{ + LogFlowFunc(("cbName=%d\n", cbName)); + int rc = VINF_SUCCESS; + if (RT_SUCCESS(rc) && (cbName < 2)) + rc = VERR_INVALID_PARAMETER; + for (unsigned i = 0; RT_SUCCESS(rc) && i < cbName; ++i) + if (pszName[i] == '*' || pszName[i] == '?' || pszName[i] == '|') + rc = VERR_INVALID_PARAMETER; + LogFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + + +/** + * Check a string fits our criteria for the value of a guest property. + * + * @returns IPRT status code + * @param pszValue the string to check, must be valid Utf8 + * @param cbValue the length in bytes of @a pszValue, including the + * terminator + * @thread HGCM + */ +int Service::validateValue(const char *pszValue, uint32_t cbValue) +{ + LogFlowFunc(("cbValue=%d\n", cbValue)); RT_NOREF1(pszValue); + + int rc = VINF_SUCCESS; + if (RT_SUCCESS(rc) && cbValue == 0) + rc = VERR_INVALID_PARAMETER; + if (RT_SUCCESS(rc)) + LogFlow((" pszValue=%s\n", cbValue > 0 ? pszValue : NULL)); + LogFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +/** + * Set a block of properties in the property registry, checking the validity + * of the arguments passed. + * + * @returns iprt status value + * @param cParms the number of HGCM parameters supplied + * @param paParms the array of HGCM parameters + * @thread HGCM + */ +int Service::setPropertyBlock(uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + const char **papszNames; + const char **papszValues; + const char **papszFlags; + uint64_t *paNsTimestamps; + uint32_t cbDummy; + int rc = VINF_SUCCESS; + + /* + * Get and validate the parameters + */ + if ( cParms != 4 + || RT_FAILURE(HGCMSvcGetPv(&paParms[0], (void **)&papszNames, &cbDummy)) + || RT_FAILURE(HGCMSvcGetPv(&paParms[1], (void **)&papszValues, &cbDummy)) + || RT_FAILURE(HGCMSvcGetPv(&paParms[2], (void **)&paNsTimestamps, &cbDummy)) + || RT_FAILURE(HGCMSvcGetPv(&paParms[3], (void **)&papszFlags, &cbDummy)) + ) + rc = VERR_INVALID_PARAMETER; + /** @todo validate the array sizes... */ + else + { + for (unsigned i = 0; RT_SUCCESS(rc) && papszNames[i] != NULL; ++i) + { + if ( !RT_VALID_PTR(papszNames[i]) + || !RT_VALID_PTR(papszValues[i]) + || !RT_VALID_PTR(papszFlags[i]) + ) + rc = VERR_INVALID_POINTER; + else + { + uint32_t fFlagsIgn; + rc = GuestPropValidateFlags(papszFlags[i], &fFlagsIgn); + } + } + if (RT_SUCCESS(rc)) + { + /* + * Add the properties. No way to roll back here. + */ + for (unsigned i = 0; papszNames[i] != NULL; ++i) + { + uint32_t fFlags; + rc = GuestPropValidateFlags(papszFlags[i], &fFlags); + AssertRCBreak(rc); + /* + * Handle names which are read-only for the guest. + */ + if (checkHostReserved(papszNames[i])) + fFlags |= GUEST_PROP_F_RDONLYGUEST; + + Property *pProp = getPropertyInternal(papszNames[i]); + if (pProp) + { + /* Update existing property. */ + rc = pProp->mValue.assignNoThrow(papszValues[i]); + AssertRCBreak(rc); + pProp->mTimestamp = paNsTimestamps[i]; + pProp->mFlags = fFlags; + } + else + { + /* Create a new property */ + try + { + pProp = new Property(papszNames[i], papszValues[i], paNsTimestamps[i], fFlags); + } + catch (std::bad_alloc &) + { + return VERR_NO_MEMORY; + } + if (RTStrSpaceInsert(&mhProperties, &pProp->mStrCore)) + mcProperties++; + else + { + delete pProp; + rc = VERR_INTERNAL_ERROR_3; + AssertFailedBreak(); + } + } + } + } + } + + return rc; +} + +/** + * Retrieve a value from the property registry by name, checking the validity + * of the arguments passed. If the guest has not allocated enough buffer + * space for the value then we return VERR_OVERFLOW and set the size of the + * buffer needed in the "size" HGCM parameter. If the name was not found at + * all, we return VERR_NOT_FOUND. + * + * @returns iprt status value + * @param cParms the number of HGCM parameters supplied + * @param paParms the array of HGCM parameters + * @thread HGCM + */ +int Service::getProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + int rc; + const char *pcszName = NULL; /* shut up gcc */ + char *pchBuf = NULL; /* shut up MSC */ + uint32_t cbName; + uint32_t cbBuf = 0; /* shut up MSC */ + + /* + * Get and validate the parameters + */ + LogFlowThisFunc(("\n")); + if ( cParms != 4 /* Hardcoded value as the next lines depend on it. */ + || RT_FAILURE(HGCMSvcGetCStr(&paParms[0], &pcszName, &cbName)) /* name */ + || RT_FAILURE(HGCMSvcGetBuf(&paParms[1], (void **)&pchBuf, &cbBuf)) /* buffer */ + ) + rc = VERR_INVALID_PARAMETER; + else + rc = validateName(pcszName, cbName); + if (RT_FAILURE(rc)) + { + LogFlowThisFunc(("rc = %Rrc\n", rc)); + return rc; + } + + /* + * Read and set the values we will return + */ + + /* Get the property. */ + Property *pProp = getPropertyInternal(pcszName); + if (pProp) + { + char szFlags[GUEST_PROP_MAX_FLAGS_LEN]; + rc = GuestPropWriteFlags(pProp->mFlags, szFlags); + if (RT_SUCCESS(rc)) + { + /* Check that the buffer is big enough */ + size_t const cbFlags = strlen(szFlags) + 1; + size_t const cbValue = pProp->mValue.length() + 1; + size_t const cbNeeded = cbValue + cbFlags; + HGCMSvcSetU32(&paParms[3], (uint32_t)cbNeeded); + if (cbBuf >= cbNeeded) + { + /* Write the value, flags and timestamp */ + memcpy(pchBuf, pProp->mValue.c_str(), cbValue); + memcpy(pchBuf + cbValue, szFlags, cbFlags); + + HGCMSvcSetU64(&paParms[2], pProp->mTimestamp); + + /* + * Done! Do exit logging and return. + */ + Log2(("Queried string %s, value=%s, timestamp=%lld, flags=%s\n", + pcszName, pProp->mValue.c_str(), pProp->mTimestamp, szFlags)); + } + else + rc = VERR_BUFFER_OVERFLOW; + } + } + else + rc = VERR_NOT_FOUND; + + LogFlowThisFunc(("rc = %Rrc (%s)\n", rc, pcszName)); + return rc; +} + +/** + * Set a value in the property registry by name, checking the validity + * of the arguments passed. + * + * @returns iprt status value + * @param cParms the number of HGCM parameters supplied + * @param paParms the array of HGCM parameters + * @param isGuest is this call coming from the guest (or the host)? + * @throws std::bad_alloc if an out of memory condition occurs + * @thread HGCM + */ +int Service::setProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest) +{ + int rc = VINF_SUCCESS; + const char *pcszName = NULL; /* shut up gcc */ + const char *pcszValue = NULL; /* ditto */ + const char *pcszFlags = NULL; + uint32_t cchName = 0; /* ditto */ + uint32_t cchValue = 0; /* ditto */ + uint32_t cchFlags = 0; + uint32_t fFlags = GUEST_PROP_F_NILFLAG; + uint64_t u64TimeNano = getCurrentTimestamp(); + + LogFlowThisFunc(("\n")); + + /* + * General parameter correctness checking. + */ + if ( RT_SUCCESS(rc) + && ( (cParms < 2) || (cParms > 3) /* Hardcoded value as the next lines depend on it. */ + || RT_FAILURE(HGCMSvcGetCStr(&paParms[0], &pcszName, &cchName)) /* name */ + || RT_FAILURE(HGCMSvcGetCStr(&paParms[1], &pcszValue, &cchValue)) /* value */ + || ( (3 == cParms) + && RT_FAILURE(HGCMSvcGetCStr(&paParms[2], &pcszFlags, &cchFlags)) /* flags */ + ) + ) + ) + rc = VERR_INVALID_PARAMETER; + + /* + * Check the values passed in the parameters for correctness. + */ + if (RT_SUCCESS(rc)) + rc = validateName(pcszName, cchName); + if (RT_SUCCESS(rc)) + rc = validateValue(pcszValue, cchValue); + if ((3 == cParms) && RT_SUCCESS(rc)) + rc = RTStrValidateEncodingEx(pcszFlags, cchFlags, + RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED); + if ((3 == cParms) && RT_SUCCESS(rc)) + rc = GuestPropValidateFlags(pcszFlags, &fFlags); + if (RT_FAILURE(rc)) + { + LogFlowThisFunc(("rc = %Rrc\n", rc)); + return rc; + } + + /* + * Hand it over to the internal setter method. + */ + rc = setPropertyInternal(pcszName, pcszValue, fFlags, u64TimeNano, isGuest); + + LogFlowThisFunc(("%s=%s, rc=%Rrc\n", pcszName, pcszValue, rc)); + return rc; +} + +/** + * Internal property setter. + * + * @returns VBox status code. + * @param pcszName The property name. + * @param pcszValue The new value. + * @param fFlags The flags. + * @param nsTimestamp The timestamp. + * @param fIsGuest Is it the guest calling. + * @throws std::bad_alloc if an out of memory condition occurs + * @thread HGCM + */ +int Service::setPropertyInternal(const char *pcszName, const char *pcszValue, uint32_t fFlags, uint64_t nsTimestamp, + bool fIsGuest /*= false*/) +{ + /* + * If the property already exists, check its flags to see if we are allowed + * to change it. + */ + Property *pProp = getPropertyInternal(pcszName); + int rc = checkPermission(pProp ? pProp->mFlags : GUEST_PROP_F_NILFLAG, fIsGuest); + /* + * Handle names which are read-only for the guest. + */ + if (rc == VINF_SUCCESS && checkHostReserved(pcszName)) + { + if (fIsGuest) + rc = VERR_PERMISSION_DENIED; + else + fFlags |= GUEST_PROP_F_RDONLYGUEST; + } + if (rc == VINF_SUCCESS) + { + /* + * Set the actual value + */ + if (pProp) + { + rc = pProp->mValue.assignNoThrow(pcszValue); + if (RT_SUCCESS(rc)) + { + pProp->mTimestamp = nsTimestamp; + pProp->mFlags = fFlags; + } + } + else if (mcProperties < GUEST_PROP_MAX_PROPS) + { + try + { + /* Create a new string space record. */ + pProp = new Property(pcszName, pcszValue, nsTimestamp, fFlags); + AssertPtr(pProp); + + if (RTStrSpaceInsert(&mhProperties, &pProp->mStrCore)) + mcProperties++; + else + { + AssertFailed(); + delete pProp; + + rc = VERR_ALREADY_EXISTS; + } + } + catch (std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + } + } + else + rc = VERR_TOO_MUCH_DATA; + + /* + * Send a notification to the guest and host and return. + */ + // if (fIsGuest) /* Notify the host even for properties that the host + // * changed. Less efficient, but ensures consistency. */ + int rc2 = doNotifications(pcszName, nsTimestamp); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + LogFlowThisFunc(("%s=%s, rc=%Rrc\n", pcszName, pcszValue, rc)); + return rc; +} + + +/** + * Remove a value in the property registry by name, checking the validity + * of the arguments passed. + * + * @returns iprt status value + * @param cParms the number of HGCM parameters supplied + * @param paParms the array of HGCM parameters + * @param isGuest is this call coming from the guest (or the host)? + * @thread HGCM + */ +int Service::delProperty(uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool isGuest) +{ + int rc; + const char *pcszName = NULL; /* shut up gcc */ + uint32_t cbName; + + LogFlowThisFunc(("\n")); + + /* + * Check the user-supplied parameters. + */ + if ( (cParms == 1) /* Hardcoded value as the next lines depend on it. */ + && RT_SUCCESS(HGCMSvcGetCStr(&paParms[0], &pcszName, &cbName)) /* name */ + ) + rc = validateName(pcszName, cbName); + else + rc = VERR_INVALID_PARAMETER; + if (RT_FAILURE(rc)) + { + LogFlowThisFunc(("rc=%Rrc\n", rc)); + return rc; + } + + /* + * If the property exists, check its flags to see if we are allowed + * to change it. + */ + Property *pProp = getPropertyInternal(pcszName); + if (pProp) + rc = checkPermission(pProp->mFlags, isGuest); + + /* + * And delete the property if all is well. + */ + if (rc == VINF_SUCCESS && pProp) + { + uint64_t nsTimestamp = getCurrentTimestamp(); + PRTSTRSPACECORE pStrCore = RTStrSpaceRemove(&mhProperties, pProp->mStrCore.pszString); + AssertPtr(pStrCore); NOREF(pStrCore); + mcProperties--; + delete pProp; + // if (isGuest) /* Notify the host even for properties that the host + // * changed. Less efficient, but ensures consistency. */ + int rc2 = doNotifications(pcszName, nsTimestamp); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + LogFlowThisFunc(("%s: rc=%Rrc\n", pcszName, rc)); + return rc; +} + +/** + * Enumeration data shared between enumPropsCallback and Service::enumProps. + */ +typedef struct ENUMDATA +{ + const char *pszPattern; /**< The pattern to match properties against. */ + char *pchCur; /**< The current buffer postion. */ + size_t cbLeft; /**< The amount of available buffer space. */ + size_t cbNeeded; /**< The amount of needed buffer space. */ +} ENUMDATA; + +/** + * @callback_method_impl{FNRTSTRSPACECALLBACK} + */ +static DECLCALLBACK(int) enumPropsCallback(PRTSTRSPACECORE pStr, void *pvUser) +{ + Property *pProp = (Property *)pStr; + ENUMDATA *pEnum = (ENUMDATA *)pvUser; + + /* Included in the enumeration? */ + if (!pProp->Matches(pEnum->pszPattern)) + return 0; + + /* Convert the non-string members into strings. */ + char szTimestamp[256]; + size_t const cbTimestamp = RTStrFormatNumber(szTimestamp, pProp->mTimestamp, 10, 0, 0, 0) + 1; + + char szFlags[GUEST_PROP_MAX_FLAGS_LEN]; + int rc = GuestPropWriteFlags(pProp->mFlags, szFlags); + if (RT_FAILURE(rc)) + return rc; + size_t const cbFlags = strlen(szFlags) + 1; + + /* Calculate the buffer space requirements. */ + size_t const cbName = pProp->mName.length() + 1; + size_t const cbValue = pProp->mValue.length() + 1; + size_t const cbRequired = cbName + cbValue + cbTimestamp + cbFlags; + pEnum->cbNeeded += cbRequired; + + /* Sufficient buffer space? */ + if (cbRequired > pEnum->cbLeft) + { + pEnum->cbLeft = 0; + return 0; /* don't quit */ + } + pEnum->cbLeft -= cbRequired; + + /* Append the property to the buffer. */ + char *pchCur = pEnum->pchCur; + pEnum->pchCur += cbRequired; + + memcpy(pchCur, pProp->mName.c_str(), cbName); + pchCur += cbName; + + memcpy(pchCur, pProp->mValue.c_str(), cbValue); + pchCur += cbValue; + + memcpy(pchCur, szTimestamp, cbTimestamp); + pchCur += cbTimestamp; + + memcpy(pchCur, szFlags, cbFlags); + pchCur += cbFlags; + + Assert(pchCur == pEnum->pchCur); + return 0; +} + +/** + * Enumerate guest properties by mask, checking the validity + * of the arguments passed. + * + * @returns iprt status value + * @param cParms the number of HGCM parameters supplied + * @param paParms the array of HGCM parameters + * @thread HGCM + */ +int Service::enumProps(uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + int rc = VINF_SUCCESS; + + /* + * Get the HGCM function arguments. + */ + char const *pchPatterns = NULL; + char *pchBuf = NULL; + uint32_t cbPatterns = 0; + uint32_t cbBuf = 0; + LogFlowThisFunc(("\n")); + if ( (cParms != 3) /* Hardcoded value as the next lines depend on it. */ + || RT_FAILURE(HGCMSvcGetCStr(&paParms[0], &pchPatterns, &cbPatterns)) /* patterns */ + || RT_FAILURE(HGCMSvcGetBuf(&paParms[1], (void **)&pchBuf, &cbBuf)) /* return buffer */ + ) + rc = VERR_INVALID_PARAMETER; + if (RT_SUCCESS(rc) && cbPatterns > GUEST_PROP_MAX_PATTERN_LEN) + rc = VERR_TOO_MUCH_DATA; + + /* + * First repack the patterns into the format expected by RTStrSimplePatternMatch() + */ + char szPatterns[GUEST_PROP_MAX_PATTERN_LEN]; + if (RT_SUCCESS(rc)) + { + for (unsigned i = 0; i < cbPatterns - 1; ++i) + if (pchPatterns[i] != '\0') + szPatterns[i] = pchPatterns[i]; + else + szPatterns[i] = '|'; + szPatterns[cbPatterns - 1] = '\0'; + } + + /* + * Next enumerate into the buffer. + */ + if (RT_SUCCESS(rc)) + { + ENUMDATA EnumData; + EnumData.pszPattern = szPatterns; + EnumData.pchCur = pchBuf; + EnumData.cbLeft = cbBuf; + EnumData.cbNeeded = 0; + rc = RTStrSpaceEnumerate(&mhProperties, enumPropsCallback, &EnumData); + AssertRCSuccess(rc); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU32(&paParms[2], (uint32_t)(EnumData.cbNeeded + 4)); + if (EnumData.cbLeft >= 4) + { + /* The final terminators. */ + EnumData.pchCur[0] = '\0'; + EnumData.pchCur[1] = '\0'; + EnumData.pchCur[2] = '\0'; + EnumData.pchCur[3] = '\0'; + } + else + rc = VERR_BUFFER_OVERFLOW; + } + } + + return rc; +} + + +/** Helper query used by getOldNotification + * @throws nothing + */ +int Service::getOldNotificationInternal(const char *pszPatterns, uint64_t nsTimestamp, Property *pProp) +{ + /* We count backwards, as the guest should normally be querying the + * most recent events. */ + int rc = VWRN_NOT_FOUND; + PropertyList::reverse_iterator it = mGuestNotifications.rbegin(); + for (; it != mGuestNotifications.rend(); ++it) + if (it->mTimestamp == nsTimestamp) + { + rc = VINF_SUCCESS; + break; + } + + /* Now look for an event matching the patterns supplied. The base() + * member conveniently points to the following element. */ + PropertyList::iterator base = it.base(); + for (; base != mGuestNotifications.end(); ++base) + if (base->Matches(pszPatterns)) + { + try + { + *pProp = *base; + } + catch (std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + } + return rc; + } + *pProp = Property(); + return rc; +} + + +/** Helper query used by getNotification */ +int Service::getNotificationWriteOut(uint32_t cParms, VBOXHGCMSVCPARM paParms[], Property const &rProp) +{ + AssertReturn(cParms == 4, VERR_INVALID_PARAMETER); /* Basic sanity checking. */ + + /* Format the data to write to the buffer. */ + char *pchBuf; + uint32_t cbBuf; + int rc = HGCMSvcGetBuf(&paParms[2], (void **)&pchBuf, &cbBuf); + if (RT_SUCCESS(rc)) + { + char szFlags[GUEST_PROP_MAX_FLAGS_LEN]; + rc = GuestPropWriteFlags(rProp.mFlags, szFlags); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU64(&paParms[1], rProp.mTimestamp); + + size_t const cbFlags = strlen(szFlags) + 1; + size_t const cbName = rProp.mName.length() + 1; + size_t const cbValue = rProp.mValue.length() + 1; + size_t const cbNeeded = cbName + cbValue + cbFlags; + HGCMSvcSetU32(&paParms[3], (uint32_t)cbNeeded); + if (cbNeeded <= cbBuf) + { + memcpy(pchBuf, rProp.mName.c_str(), cbName); + pchBuf += cbName; + memcpy(pchBuf, rProp.mValue.c_str(), cbValue); + pchBuf += cbValue; + memcpy(pchBuf, szFlags, cbFlags); + } + else + rc = VERR_BUFFER_OVERFLOW; + } + } + return rc; +} + + +/** + * Get the next guest notification. + * + * @returns iprt status value + * @param u32ClientId the client ID + * @param callHandle handle + * @param cParms the number of HGCM parameters supplied + * @param paParms the array of HGCM parameters + * @thread HGCM + * @throws nothing + */ +int Service::getNotification(uint32_t u32ClientId, VBOXHGCMCALLHANDLE callHandle, + uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + int rc = VINF_SUCCESS; + char *pszPatterns = NULL; /* shut up gcc */ + char *pchBuf; + uint32_t cchPatterns = 0; + uint32_t cbBuf = 0; + uint64_t nsTimestamp; + + /* + * Get the HGCM function arguments and perform basic verification. + */ + LogFlowThisFunc(("\n")); + if ( cParms != 4 /* Hardcoded value as the next lines depend on it. */ + || RT_FAILURE(HGCMSvcGetStr(&paParms[0], &pszPatterns, &cchPatterns)) /* patterns */ + || RT_FAILURE(HGCMSvcGetU64(&paParms[1], &nsTimestamp)) /* timestamp */ + || RT_FAILURE(HGCMSvcGetBuf(&paParms[2], (void **)&pchBuf, &cbBuf)) /* return buffer */ + ) + rc = VERR_INVALID_PARAMETER; + else + { + LogFlow(("pszPatterns=%s, nsTimestamp=%llu\n", pszPatterns, nsTimestamp)); + + /* + * If no timestamp was supplied or no notification was found in the queue + * of old notifications, enqueue the request in the waiting queue. + */ + Property prop; + if (RT_SUCCESS(rc) && nsTimestamp != 0) + rc = getOldNotification(pszPatterns, nsTimestamp, &prop); + if (RT_SUCCESS(rc)) + { + if (prop.isNull()) + { + /* + * Check if the client already had the same request. + * Complete the old request with an error in this case. + * Protection against clients, which cancel and resubmits requests. + */ + uint32_t cPendingWaits = 0; + CallList::iterator it = mGuestWaiters.begin(); + while (it != mGuestWaiters.end()) + { + if (u32ClientId == it->u32ClientId) + { + const char *pszPatternsExisting; + uint32_t cchPatternsExisting; + int rc3 = HGCMSvcGetCStr(&it->mParms[0], &pszPatternsExisting, &cchPatternsExisting); + if ( RT_SUCCESS(rc3) + && RTStrCmp(pszPatterns, pszPatternsExisting) == 0) + { + /* Complete the old request. */ + mpHelpers->pfnCallComplete(it->mHandle, VERR_INTERRUPTED); + it = mGuestWaiters.erase(it); + } + else if (mpHelpers->pfnIsCallCancelled(it->mHandle)) + { + /* Cleanup cancelled request. */ + mpHelpers->pfnCallComplete(it->mHandle, VERR_INTERRUPTED); + it = mGuestWaiters.erase(it); + } + else + { + /** @todo check if cancelled. */ + cPendingWaits++; + ++it; + } + } + else + ++it; + } + + if (cPendingWaits < GUEST_PROP_MAX_GUEST_CONCURRENT_WAITS) + { + try + { + mGuestWaiters.push_back(GuestCall(u32ClientId, callHandle, GUEST_PROP_FN_GET_NOTIFICATION, + cParms, paParms, rc)); + rc = VINF_HGCM_ASYNC_EXECUTE; + } + catch (std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + } + } + else + { + LogFunc(("Too many pending waits already!\n")); + rc = VERR_OUT_OF_RESOURCES; + } + } + /* + * Otherwise reply at once with the enqueued notification we found. + */ + else + { + int rc2 = getNotificationWriteOut(cParms, paParms, prop); + if (RT_FAILURE(rc2)) + rc = rc2; + } + } + } + + LogFlowThisFunc(("returning rc=%Rrc\n", rc)); + return rc; +} + + +/** + * Notify the service owner and the guest that a property has been + * added/deleted/changed + * + * @param pszProperty The name of the property which has changed. + * @param nsTimestamp The time at which the change took place. + * @throws nothing. + * @thread HGCM service + */ +int Service::doNotifications(const char *pszProperty, uint64_t nsTimestamp) +{ + AssertPtrReturn(pszProperty, VERR_INVALID_POINTER); + LogFlowThisFunc(("pszProperty=%s, nsTimestamp=%llu\n", pszProperty, nsTimestamp)); + /* Ensure that our timestamp is different to the last one. */ + if ( !mGuestNotifications.empty() + && nsTimestamp == mGuestNotifications.back().mTimestamp) + ++nsTimestamp; + + /* + * Don't keep too many changes around. + */ + if (mGuestNotifications.size() >= GUEST_PROP_MAX_GUEST_NOTIFICATIONS) + mGuestNotifications.pop_front(); + + /* + * Try to find the property. Create a change event if we find it and a + * delete event if we do not. + */ + Property prop; + int rc = prop.mName.assignNoThrow(pszProperty); + AssertRCReturn(rc, rc); + prop.mTimestamp = nsTimestamp; + /* prop is currently a delete event for pszProperty */ + Property const * const pProp = getPropertyInternal(pszProperty); + if (pProp) + { + /* Make prop into a change event. */ + rc = prop.mValue.assignNoThrow(pProp->mValue); + AssertRCReturn(rc, rc); + prop.mFlags = pProp->mFlags; + } + + /* Release guest waiters if applicable and add the event + * to the queue for guest notifications */ + CallList::iterator it = mGuestWaiters.begin(); + if (it != mGuestWaiters.end()) + { + const char *pszPatterns; + uint32_t cchPatterns; + HGCMSvcGetCStr(&it->mParms[0], &pszPatterns, &cchPatterns); + + while (it != mGuestWaiters.end()) + { + if (prop.Matches(pszPatterns)) + { + int rc2 = getNotificationWriteOut(it->mParmsCnt, it->mParms, prop); + if (RT_SUCCESS(rc2)) + rc2 = it->mRc; + mpHelpers->pfnCallComplete(it->mHandle, rc2); + it = mGuestWaiters.erase(it); + } + else + ++it; + } + } + + try + { + mGuestNotifications.push_back(prop); + } + catch (std::bad_alloc &) + { + rc = VERR_NO_MEMORY; + } + + if ( RT_SUCCESS(rc) + && mpfnHostCallback) + { + /* + * Host notifications - first case: if the property exists then send its + * current value + */ + if (pProp) + { + char szFlags[GUEST_PROP_MAX_FLAGS_LEN]; + /* Send out a host notification */ + const char *pszValue = prop.mValue.c_str(); + rc = GuestPropWriteFlags(prop.mFlags, szFlags); + if (RT_SUCCESS(rc)) + rc = notifyHost(pszProperty, pszValue, nsTimestamp, szFlags); + } + /* + * Host notifications - second case: if the property does not exist then + * send the host an empty value + */ + else + { + /* Send out a host notification */ + rc = notifyHost(pszProperty, "", nsTimestamp, ""); + } + } + + LogFlowThisFunc(("returning rc=%Rrc\n", rc)); + return rc; +} + +static DECLCALLBACK(void) +notifyHostAsyncWorker(PFNHGCMSVCEXT pfnHostCallback, void *pvHostData, PGUESTPROPHOSTCALLBACKDATA pHostCallbackData) +{ + pfnHostCallback(pvHostData, 0 /*u32Function*/, (void *)pHostCallbackData, sizeof(GUESTPROPHOSTCALLBACKDATA)); + RTMemFree(pHostCallbackData); +} + +/** + * Notify the service owner that a property has been added/deleted/changed. + * @returns IPRT status value + * @param pszName the property name + * @param pszValue the new value, or NULL if the property was deleted + * @param nsTimestamp the time of the change + * @param pszFlags the new flags string + */ +int Service::notifyHost(const char *pszName, const char *pszValue, uint64_t nsTimestamp, const char *pszFlags) +{ + LogFlowFunc(("pszName=%s, pszValue=%s, nsTimestamp=%llu, pszFlags=%s\n", pszName, pszValue, nsTimestamp, pszFlags)); + int rc; + + /* Allocate buffer for the callback data and strings. */ + size_t cbName = pszName? strlen(pszName): 0; + size_t cbValue = pszValue? strlen(pszValue): 0; + size_t cbFlags = pszFlags? strlen(pszFlags): 0; + size_t cbAlloc = sizeof(GUESTPROPHOSTCALLBACKDATA) + cbName + cbValue + cbFlags + 3; + PGUESTPROPHOSTCALLBACKDATA pHostCallbackData = (PGUESTPROPHOSTCALLBACKDATA)RTMemAlloc(cbAlloc); + if (pHostCallbackData) + { + uint8_t *pu8 = (uint8_t *)pHostCallbackData; + pu8 += sizeof(GUESTPROPHOSTCALLBACKDATA); + + pHostCallbackData->u32Magic = GUESTPROPHOSTCALLBACKDATA_MAGIC; + + pHostCallbackData->pcszName = (const char *)pu8; + memcpy(pu8, pszName, cbName); + pu8 += cbName; + *pu8++ = 0; + + pHostCallbackData->pcszValue = (const char *)pu8; + memcpy(pu8, pszValue, cbValue); + pu8 += cbValue; + *pu8++ = 0; + + pHostCallbackData->u64Timestamp = nsTimestamp; + + pHostCallbackData->pcszFlags = (const char *)pu8; + memcpy(pu8, pszFlags, cbFlags); + pu8 += cbFlags; + *pu8++ = 0; + + rc = RTReqQueueCallEx(mhReqQNotifyHost, NULL, 0, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT, + (PFNRT)notifyHostAsyncWorker, 3, + mpfnHostCallback, mpvHostData, pHostCallbackData); + if (RT_FAILURE(rc)) + { + RTMemFree(pHostCallbackData); + } + } + else + { + rc = VERR_NO_MEMORY; + } + LogFlowFunc(("returning rc=%Rrc\n", rc)); + return rc; +} + + +/** + * Handle an HGCM service call. + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnCall} + * @note All functions which do not involve an unreasonable delay will be + * handled synchronously. If needed, we will add a request handler + * thread in future for those which do. + * + * @thread HGCM + */ +void Service::call (VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, + void * /* pvClient */, uint32_t eFunction, uint32_t cParms, + VBOXHGCMSVCPARM paParms[]) +{ + int rc; + LogFlowFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %p\n", + u32ClientID, eFunction, cParms, paParms)); + + switch (eFunction) + { + /* The guest wishes to read a property */ + case GUEST_PROP_FN_GET_PROP: + LogFlowFunc(("GET_PROP\n")); + rc = getProperty(cParms, paParms); + break; + + /* The guest wishes to set a property */ + case GUEST_PROP_FN_SET_PROP: + LogFlowFunc(("SET_PROP\n")); + rc = setProperty(cParms, paParms, true); + break; + + /* The guest wishes to set a property value */ + case GUEST_PROP_FN_SET_PROP_VALUE: + LogFlowFunc(("SET_PROP_VALUE\n")); + rc = setProperty(cParms, paParms, true); + break; + + /* The guest wishes to remove a configuration value */ + case GUEST_PROP_FN_DEL_PROP: + LogFlowFunc(("DEL_PROP\n")); + rc = delProperty(cParms, paParms, true); + break; + + /* The guest wishes to enumerate all properties */ + case GUEST_PROP_FN_ENUM_PROPS: + LogFlowFunc(("ENUM_PROPS\n")); + rc = enumProps(cParms, paParms); + break; + + /* The guest wishes to get the next property notification */ + case GUEST_PROP_FN_GET_NOTIFICATION: + LogFlowFunc(("GET_NOTIFICATION\n")); + rc = getNotification(u32ClientID, callHandle, cParms, paParms); + break; + + default: + rc = VERR_NOT_IMPLEMENTED; + } + LogFlowFunc(("rc = %Rrc\n", rc)); + if (rc != VINF_HGCM_ASYNC_EXECUTE) + mpHelpers->pfnCallComplete(callHandle, rc); +} + +/** + * Enumeration data shared between dbgInfoCallback and Service::dbgInfoShow. + */ +typedef struct ENUMDBGINFO +{ + PCDBGFINFOHLP pHlp; +} ENUMDBGINFO; + +static DECLCALLBACK(int) dbgInfoCallback(PRTSTRSPACECORE pStr, void *pvUser) +{ + Property *pProp = (Property *)pStr; + PCDBGFINFOHLP pHlp = ((ENUMDBGINFO *)pvUser)->pHlp; + + char szFlags[GUEST_PROP_MAX_FLAGS_LEN]; + int rc = GuestPropWriteFlags(pProp->mFlags, szFlags); + if (RT_FAILURE(rc)) + RTStrPrintf(szFlags, sizeof(szFlags), "???"); + + pHlp->pfnPrintf(pHlp, "%s: '%s', %RU64", pProp->mName.c_str(), pProp->mValue.c_str(), pProp->mTimestamp); + if (strlen(szFlags)) + pHlp->pfnPrintf(pHlp, " (%s)", szFlags); + pHlp->pfnPrintf(pHlp, "\n"); + return 0; +} + + +/** + * Handler for debug info. + * + * @param pvUser user pointer. + * @param pHlp The info helper functions. + * @param pszArgs Arguments, ignored. + */ +DECLCALLBACK(void) Service::dbgInfo(void *pvUser, PCDBGFINFOHLP pHlp, const char *pszArgs) +{ + RT_NOREF1(pszArgs); + SELF *pSelf = reinterpret_cast<SELF *>(pvUser); + + ENUMDBGINFO EnumData = { pHlp }; + RTStrSpaceEnumerate(&pSelf->mhProperties, dbgInfoCallback, &EnumData); +} + + +/** + * Service call handler for the host. + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnHostCall} + * @thread hgcm + */ +int Service::hostCall (uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + int rc; + LogFlowFunc(("fn = %d, cParms = %d, pparms = %p\n", eFunction, cParms, paParms)); + + switch (eFunction) + { + /* The host wishes to set a block of properties */ + case GUEST_PROP_FN_HOST_SET_PROPS: + LogFlowFunc(("SET_PROPS_HOST\n")); + rc = setPropertyBlock(cParms, paParms); + break; + + /* The host wishes to read a configuration value */ + case GUEST_PROP_FN_HOST_GET_PROP: + LogFlowFunc(("GET_PROP_HOST\n")); + rc = getProperty(cParms, paParms); + break; + + /* The host wishes to set a configuration value */ + case GUEST_PROP_FN_HOST_SET_PROP: + LogFlowFunc(("SET_PROP_HOST\n")); + rc = setProperty(cParms, paParms, false); + break; + + /* The host wishes to set a configuration value */ + case GUEST_PROP_FN_HOST_SET_PROP_VALUE: + LogFlowFunc(("SET_PROP_VALUE_HOST\n")); + rc = setProperty(cParms, paParms, false); + break; + + /* The host wishes to remove a configuration value */ + case GUEST_PROP_FN_HOST_DEL_PROP: + LogFlowFunc(("DEL_PROP_HOST\n")); + rc = delProperty(cParms, paParms, false); + break; + + /* The host wishes to enumerate all properties */ + case GUEST_PROP_FN_HOST_ENUM_PROPS: + LogFlowFunc(("ENUM_PROPS\n")); + rc = enumProps(cParms, paParms); + break; + + /* The host wishes to set global flags for the service */ + case GUEST_PROP_FN_HOST_SET_GLOBAL_FLAGS: + LogFlowFunc(("SET_GLOBAL_FLAGS_HOST\n")); + if (cParms == 1) + { + uint32_t fFlags; + rc = HGCMSvcGetU32(&paParms[0], &fFlags); + if (RT_SUCCESS(rc)) + mfGlobalFlags = fFlags; + } + else + rc = VERR_INVALID_PARAMETER; + break; + + default: + rc = VERR_NOT_SUPPORTED; + break; + } + + LogFlowFunc(("rc = %Rrc\n", rc)); + return rc; +} + +/** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnDisconnect} + */ +/*static*/ DECLCALLBACK(int) Service::svcDisconnect(void *pvService, uint32_t idClient, void *pvClient) +{ + RT_NOREF(pvClient); + LogFlowFunc(("idClient=%u\n", idClient)); + SELF *pThis = reinterpret_cast<SELF *>(pvService); + AssertLogRelReturn(pThis, VERR_INVALID_POINTER); + + /* + * Complete all pending requests for this client. + */ + for (CallList::iterator It = pThis->mGuestWaiters.begin(); It != pThis->mGuestWaiters.end();) + { + GuestCall &rCurCall = *It; + if (rCurCall.u32ClientId != idClient) + ++It; + else + { + LogFlowFunc(("Completing call %u (%p)...\n", rCurCall.mFunction, rCurCall.mHandle)); + pThis->mpHelpers->pfnCallComplete(rCurCall.mHandle, VERR_INTERRUPTED); + It = pThis->mGuestWaiters.erase(It); + } + } + + return VINF_SUCCESS; +} + +/** + * Increments a counter property. + * + * It is assumed that this a transient property that is read-only to the guest. + * + * @param pszName The property name. + * @throws std::bad_alloc if an out of memory condition occurs + */ +void Service::incrementCounterProp(const char *pszName) +{ + /* Format the incremented value. */ + char szValue[64]; + Property *pProp = getPropertyInternal(pszName); + if (pProp) + { + uint64_t uValue = RTStrToUInt64(pProp->mValue.c_str()); + RTStrFormatU64(szValue, sizeof(szValue), uValue + 1, 10, 0, 0, 0); + } + else + { + szValue[0] = '1'; + szValue[1] = '\0'; + } + + /* Set it. */ + setPropertyInternal(pszName, szValue, GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, getCurrentTimestamp()); +} + +/** + * Sets the VBoxVer, VBoxVerExt and VBoxRev properties. + */ +int Service::setHostVersionProps() +{ + uint64_t nsTimestamp = getCurrentTimestamp(); + + /* Set the raw VBox version string as a guest property. Used for host/guest + * version comparison. */ + int rc = setPropertyInternal("/VirtualBox/HostInfo/VBoxVer", VBOX_VERSION_STRING_RAW, + GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsTimestamp); + AssertRCReturn(rc, rc); + + /* Set the full VBox version string as a guest property. Can contain vendor-specific + * information/branding and/or pre-release tags. */ + rc = setPropertyInternal("/VirtualBox/HostInfo/VBoxVerExt", VBOX_VERSION_STRING, + GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsTimestamp + 1); + AssertRCReturn(rc, rc); + + /* Set the VBox SVN revision as a guest property */ + rc = setPropertyInternal("/VirtualBox/HostInfo/VBoxRev", RTBldCfgRevisionStr(), + GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsTimestamp + 2); + AssertRCReturn(rc, rc); + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{VBOXHGCMSVCFNTABLE,pfnNotify} + */ +/*static*/ DECLCALLBACK(void) Service::svcNotify(void *pvService, HGCMNOTIFYEVENT enmEvent) +{ + SELF *pThis = reinterpret_cast<SELF *>(pvService); + AssertPtrReturnVoid(pThis); + + /* Make sure the host version properties have been touched and are + up-to-date after a restore: */ + if ( !pThis->m_fSetHostVersionProps + && (enmEvent == HGCMNOTIFYEVENT_RESUME || enmEvent == HGCMNOTIFYEVENT_POWER_ON)) + { + pThis->setHostVersionProps(); + pThis->m_fSetHostVersionProps = true; + } + + if (enmEvent == HGCMNOTIFYEVENT_RESUME) + pThis->incrementCounterProp("/VirtualBox/VMInfo/ResumeCounter"); + + if (enmEvent == HGCMNOTIFYEVENT_RESET) + pThis->incrementCounterProp("/VirtualBox/VMInfo/ResetCounter"); +} + + +/* static */ +DECLCALLBACK(int) Service::threadNotifyHost(RTTHREAD hThreadSelf, void *pvUser) +{ + RT_NOREF1(hThreadSelf); + Service *pThis = (Service *)pvUser; + int rc = VINF_SUCCESS; + + LogFlowFunc(("ENTER: %p\n", pThis)); + + for (;;) + { + rc = RTReqQueueProcess(pThis->mhReqQNotifyHost, RT_INDEFINITE_WAIT); + + AssertMsg(rc == VWRN_STATE_CHANGED, + ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", + rc)); + if (rc == VWRN_STATE_CHANGED) + { + break; + } + } + + LogFlowFunc(("LEAVE: %Rrc\n", rc)); + return rc; +} + +static DECLCALLBACK(int) wakeupNotifyHost(void) +{ + /* Returning a VWRN_* will cause RTReqQueueProcess return. */ + return VWRN_STATE_CHANGED; +} + + +int Service::initialize() +{ + /* + * Insert standard host properties. + */ + /* The host version will but updated again on power on or resume + (after restore), however we need the properties now for restored + guest notification/wait calls. */ + int rc = setHostVersionProps(); + AssertRCReturn(rc, rc); + + /* Sysprep execution by VBoxService (host is allowed to change these). */ + uint64_t nsNow = getCurrentTimestamp(); + rc = setPropertyInternal("/VirtualBox/HostGuest/SysprepExec", "", GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsNow); + AssertRCReturn(rc, rc); + rc = setPropertyInternal("/VirtualBox/HostGuest/SysprepArgs", "", GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsNow); + AssertRCReturn(rc, rc); + + /* Resume and reset counters. */ + rc = setPropertyInternal("/VirtualBox/VMInfo/ResumeCounter", "0", GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsNow); + AssertRCReturn(rc, rc); + rc = setPropertyInternal("/VirtualBox/VMInfo/ResetCounter", "0", GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_RDONLYGUEST, nsNow); + AssertRCReturn(rc, rc); + + /* The host notification thread and queue. */ + rc = RTReqQueueCreate(&mhReqQNotifyHost); + if (RT_SUCCESS(rc)) + { + rc = RTThreadCreate(&mhThreadNotifyHost, + threadNotifyHost, + this, + 0 /* default stack size */, + RTTHREADTYPE_DEFAULT, + RTTHREADFLAGS_WAITABLE, + "GstPropNtfy"); + if (RT_SUCCESS(rc)) + { + /* Finally debug stuff (ignore failures): */ + HGCMSvcHlpInfoRegister(mpHelpers, "guestprops", "Display the guest properties", Service::dbgInfo, this); + return rc; + } + + RTReqQueueDestroy(mhReqQNotifyHost); + mhReqQNotifyHost = NIL_RTREQQUEUE; + } + return rc; +} + +/** + * @callback_method_impl{FNRTSTRSPACECALLBACK, Destroys Property.} + */ +static DECLCALLBACK(int) destroyProperty(PRTSTRSPACECORE pStr, void *pvUser) +{ + RT_NOREF(pvUser); + Property *pProp = RT_FROM_CPP_MEMBER(pStr, struct Property, mStrCore); /* clang objects to offsetof on non-POD.*/ + delete pProp; + return 0; +} + + +int Service::uninit() +{ + if (mpHelpers) + HGCMSvcHlpInfoDeregister(mpHelpers, "guestprops"); + + if (mhReqQNotifyHost != NIL_RTREQQUEUE) + { + /* Stop the thread */ + PRTREQ pReq; + int rc = RTReqQueueCall(mhReqQNotifyHost, &pReq, 10000, (PFNRT)wakeupNotifyHost, 0); + if (RT_SUCCESS(rc)) + RTReqRelease(pReq); + rc = RTThreadWait(mhThreadNotifyHost, 10000, NULL); + AssertRC(rc); + rc = RTReqQueueDestroy(mhReqQNotifyHost); + AssertRC(rc); + mhReqQNotifyHost = NIL_RTREQQUEUE; + mhThreadNotifyHost = NIL_RTTHREAD; + RTStrSpaceDestroy(&mhProperties, destroyProperty, NULL); + mhProperties = NULL; + } + return VINF_SUCCESS; +} + +} /* namespace guestProp */ + +using guestProp::Service; + +/** + * @copydoc VBOXHGCMSVCLOAD + */ +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable) +{ + int rc = VERR_IPE_UNINITIALIZED_STATUS; + + LogFlowFunc(("ptable = %p\n", ptable)); + + if (!RT_VALID_PTR(ptable)) + rc = VERR_INVALID_PARAMETER; + else + { + LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version)); + + if ( ptable->cbSize != sizeof(VBOXHGCMSVCFNTABLE) + || ptable->u32Version != VBOX_HGCM_SVC_VERSION) + rc = VERR_VERSION_MISMATCH; + else + { + Service *pService = NULL; + /* No exceptions may propagate outside. */ + try + { + pService = new Service(ptable->pHelpers); + rc = VINF_SUCCESS; + } + catch (int rcThrown) + { + rc = rcThrown; + } + catch (...) + { + rc = VERR_UNEXPECTED_EXCEPTION; + } + + if (RT_SUCCESS(rc)) + { + /* We do not maintain connections, so no client data is needed. */ + ptable->cbClient = 0; + + ptable->pfnUnload = Service::svcUnload; + ptable->pfnConnect = Service::svcConnect; + ptable->pfnDisconnect = Service::svcDisconnect; + ptable->pfnCall = Service::svcCall; + ptable->pfnHostCall = Service::svcHostCall; + ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */ + ptable->pfnLoadState = NULL; /* construction done before restoring suffices */ + ptable->pfnRegisterExtension = Service::svcRegisterExtension; + ptable->pfnNotify = Service::svcNotify; + ptable->pvService = pService; + + /* Service specific initialization. */ + rc = pService->initialize(); + if (RT_FAILURE(rc)) + { + delete pService; + pService = NULL; + } + } + else + Assert(!pService); + } + } + + LogFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + diff --git a/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.rc b/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.rc new file mode 100644 index 00000000..9ebafa35 --- /dev/null +++ b/src/VBox/HostServices/GuestProperties/VBoxGuestPropSvc.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxGuestPropSvc.rc $ */ +/** @file + * VBoxGuestPropSvc - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox Guest Properties Service\0" + VALUE "InternalName", "VBoxGuestPropSvc\0" + VALUE "OriginalFilename", "VBoxGuestPropSvc.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/GuestProperties/testcase/Makefile.kmk b/src/VBox/HostServices/GuestProperties/testcase/Makefile.kmk new file mode 100644 index 00000000..5a05edeb --- /dev/null +++ b/src/VBox/HostServices/GuestProperties/testcase/Makefile.kmk @@ -0,0 +1,46 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Guest Properties Host Service testcases. +# + +# +# Copyright (C) 2006-2019 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. +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK) + + # Set this in LocalConfig.kmk if you are working on the guest property + # service to automatically run the testcase at build time. + # OTHERS += $(tstGuestPropSvc_0_OUTDIR)/tstGuestPropSvc.run + # + + PROGRAMS += tstGuestPropSvc + TESTING += $(tstGuestPropSvc_0_OUTDIR)/tstGuestPropSvc.run + tstGuestPropSvc_TEMPLATE = VBOXR3TSTEXE + # The second define here is to ensure that the testcase will run fast, + # without waiting for any thread synchronisation. + tstGuestPropSvc_DEFS = VBOX_WITH_HGCM VBOX_GUEST_PROP_TEST_NOTHREAD + tstGuestPropSvc_SOURCES = \ + tstGuestPropSvc.cpp \ + ../VBoxGuestPropSvc.cpp + tstGuestPropSvc_LIBS = $(LIB_RUNTIME) + +$$(tstGuestPropSvc_0_OUTDIR)/tstGuestPropSvc.run: $$(tstGuestPropSvc_1_STAGE_TARGET) + export VBOX_LOG_DEST=nofile; $(tstGuestPropSvc_1_STAGE_TARGET) quiet + $(QUIET)$(APPEND) -t "$@" "done" + +endif + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp b/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp new file mode 100644 index 00000000..fd5ca52e --- /dev/null +++ b/src/VBox/HostServices/GuestProperties/testcase/tstGuestPropSvc.cpp @@ -0,0 +1,1171 @@ +/* $Id: tstGuestPropSvc.cpp $ */ +/** @file + * + * Testcase for the guest property service. + */ + +/* + * Copyright (C) 2008-2019 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 * +*********************************************************************************************************************************/ +#include <VBox/HostServices/GuestPropertySvc.h> +#include <VBox/err.h> +#include <VBox/hgcmsvc.h> +#include <iprt/test.h> +#include <iprt/time.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static RTTEST g_hTest = NIL_RTTEST; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable); + + +/** Simple call handle structure for the guest call completion callback */ +struct VBOXHGCMCALLHANDLE_TYPEDEF +{ + /** Where to store the result code */ + int32_t rc; +}; + +/** Call completion callback for guest calls. */ +static DECLCALLBACK(int) callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc) +{ + callHandle->rc = rc; + return VINF_SUCCESS; +} + +/** + * Initialise the HGCM service table as much as we need to start the + * service + * @param pTable the table to initialise + */ +void initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers) +{ + pTable->cbSize = sizeof (VBOXHGCMSVCFNTABLE); + pTable->u32Version = VBOX_HGCM_SVC_VERSION; + pHelpers->pfnCallComplete = callComplete; + pTable->pHelpers = pHelpers; +} + +/** + * A list of valid flag strings for testConvertFlags. The flag conversion + * functions should accept these and convert them from string to a flag type + * and back without errors. + */ +struct flagStrings +{ + /** Flag string in a format the functions should recognise */ + const char *pcszIn; + /** How the functions should output the string again */ + const char *pcszOut; +} +g_aValidFlagStrings[] = +{ + /* pcszIn, pcszOut */ + { " ", "" }, + { "transient, ", "TRANSIENT" }, + { " rdOnLyHOST, transIENT , READONLY ", "TRANSIENT, READONLY" }, + { " rdonlyguest", "RDONLYGUEST" }, + { "rdonlyhost ", "RDONLYHOST" }, + { "transient, transreset, rdonlyhost", "TRANSIENT, RDONLYHOST, TRANSRESET" }, + { "transient, transreset, rdonlyguest", "TRANSIENT, RDONLYGUEST, TRANSRESET" }, /* max length */ + { "rdonlyguest, rdonlyhost", "READONLY" }, + { "transient, transreset, ", "TRANSIENT, TRANSRESET" }, /* Don't combine them ... */ + { "transreset, ", "TRANSIENT, TRANSRESET" }, /* ... instead expand transreset for old adds. */ +}; + +/** + * A list of invalid flag strings for testConvertFlags. The flag conversion + * functions should reject these. + */ +const char *g_apszInvalidFlagStrings[] = +{ + "RDONLYHOST,,", + " TRANSIENT READONLY" +}; + +/** + * Test the flag conversion functions. + * @returns iprt status value to indicate whether the test went as expected. + * @note prints its own diagnostic information to stdout. + */ +static void testConvertFlags(void) +{ + int rc = VINF_SUCCESS; + char *pszFlagBuffer = (char *)RTTestGuardedAllocTail(g_hTest, GUEST_PROP_MAX_FLAGS_LEN); + + RTTestISub("Conversion of valid flags strings"); + for (unsigned i = 0; i < RT_ELEMENTS(g_aValidFlagStrings) && RT_SUCCESS(rc); ++i) + { + uint32_t fFlags; + rc = GuestPropValidateFlags(g_aValidFlagStrings[i].pcszIn, &fFlags); + if (RT_FAILURE(rc)) + RTTestIFailed("Failed to validate flag string '%s'", g_aValidFlagStrings[i].pcszIn); + if (RT_SUCCESS(rc)) + { + rc = GuestPropWriteFlags(fFlags, pszFlagBuffer); + if (RT_FAILURE(rc)) + RTTestIFailed("Failed to convert flag string '%s' back to a string.", + g_aValidFlagStrings[i].pcszIn); + } + if (RT_SUCCESS(rc) && (strlen(pszFlagBuffer) > GUEST_PROP_MAX_FLAGS_LEN - 1)) + { + RTTestIFailed("String '%s' converts back to a flag string which is too long.\n", + g_aValidFlagStrings[i].pcszIn); + rc = VERR_TOO_MUCH_DATA; + } + if (RT_SUCCESS(rc) && (strcmp(pszFlagBuffer, g_aValidFlagStrings[i].pcszOut) != 0)) + { + RTTestIFailed("String '%s' converts back to '%s' instead of to '%s'\n", + g_aValidFlagStrings[i].pcszIn, pszFlagBuffer, + g_aValidFlagStrings[i].pcszOut); + rc = VERR_PARSE_ERROR; + } + } + if (RT_SUCCESS(rc)) + { + RTTestISub("Rejection of invalid flags strings"); + for (unsigned i = 0; i < RT_ELEMENTS(g_apszInvalidFlagStrings) && RT_SUCCESS(rc); ++i) + { + uint32_t fFlags; + /* This is required to fail. */ + if (RT_SUCCESS(GuestPropValidateFlags(g_apszInvalidFlagStrings[i], &fFlags))) + { + RTTestIFailed("String '%s' was incorrectly accepted as a valid flag string.\n", + g_apszInvalidFlagStrings[i]); + rc = VERR_PARSE_ERROR; + } + } + } + if (RT_SUCCESS(rc)) + { + uint32_t u32BadFlags = GUEST_PROP_F_ALLFLAGS << 1; + RTTestISub("Rejection of an invalid flags field"); + /* This is required to fail. */ + if (RT_SUCCESS(GuestPropWriteFlags(u32BadFlags, pszFlagBuffer))) + { + RTTestIFailed("Flags 0x%x were incorrectly written out as '%.*s'\n", + u32BadFlags, GUEST_PROP_MAX_FLAGS_LEN, pszFlagBuffer); + rc = VERR_PARSE_ERROR; + } + } + + RTTestGuardedFree(g_hTest, pszFlagBuffer); +} + +/** + * List of property names for testSetPropsHost. + */ +const char *g_apcszNameBlock[] = +{ + "test/name/", + "test name", + "TEST NAME", + "/test/name", + NULL +}; + +/** + * List of property values for testSetPropsHost. + */ +const char *g_apcszValueBlock[] = +{ + "test/value/", + "test value", + "TEST VALUE", + "/test/value", + NULL +}; + +/** + * List of property timestamps for testSetPropsHost. + */ +uint64_t g_au64TimestampBlock[] = +{ + 0, 999, 999999, UINT64_C(999999999999), 0 +}; + +/** + * List of property flags for testSetPropsHost. + */ +const char *g_apcszFlagsBlock[] = +{ + "", + "readonly, transient", + "RDONLYHOST", + "RdOnlyGuest", + NULL +}; + +/** + * Test the SET_PROPS_HOST function. + * @returns iprt status value to indicate whether the test went as expected. + * @note prints its own diagnostic information to stdout. + */ +static void testSetPropsHost(VBOXHGCMSVCFNTABLE *ptable) +{ + RTTestISub("SET_PROPS_HOST"); + RTTESTI_CHECK_RETV(RT_VALID_PTR(ptable->pfnHostCall)); + + VBOXHGCMSVCPARM aParms[4]; + HGCMSvcSetPv(&aParms[0], (void *)g_apcszNameBlock, 0); + HGCMSvcSetPv(&aParms[1], (void *)g_apcszValueBlock, 0); + HGCMSvcSetPv(&aParms[2], (void *)g_au64TimestampBlock, 0); + HGCMSvcSetPv(&aParms[3], (void *)g_apcszFlagsBlock, 0); + RTTESTI_CHECK_RC(ptable->pfnHostCall(ptable->pvService, GUEST_PROP_FN_HOST_SET_PROPS, 4, &aParms[0]), VINF_SUCCESS); +} + +/** Result strings for zeroth enumeration test */ +static const char *g_apchEnumResult0[] = +{ + "test/name/\0test/value/\0""0\0", + "test name\0test value\0""999\0TRANSIENT, READONLY", + "TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST", + "/test/name\0/test/value\0""999999999999\0RDONLYGUEST", + NULL +}; + +/** Result string sizes for zeroth enumeration test */ +static const uint32_t g_acbEnumResult0[] = +{ + sizeof("test/name/\0test/value/\0""0\0"), + sizeof("test name\0test value\0""999\0TRANSIENT, READONLY"), + sizeof("TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST"), + sizeof("/test/name\0/test/value\0""999999999999\0RDONLYGUEST"), + 0 +}; + +/** + * The size of the buffer returned by the zeroth enumeration test - + * the - 1 at the end is because of the hidden zero terminator + */ +static const uint32_t g_cbEnumBuffer0 = + sizeof("test/name/\0test/value/\0""0\0\0" + "test name\0test value\0""999\0TRANSIENT, READONLY\0" + "TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST\0" + "/test/name\0/test/value\0""999999999999\0RDONLYGUEST\0\0\0\0\0") - 1; + +/** Result strings for first and second enumeration test */ +static const char *g_apchEnumResult1[] = +{ + "TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST", + "/test/name\0/test/value\0""999999999999\0RDONLYGUEST", + NULL +}; + +/** Result string sizes for first and second enumeration test */ +static const uint32_t g_acbEnumResult1[] = +{ + sizeof("TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST"), + sizeof("/test/name\0/test/value\0""999999999999\0RDONLYGUEST"), + 0 +}; + +/** + * The size of the buffer returned by the first enumeration test - + * the - 1 at the end is because of the hidden zero terminator + */ +static const uint32_t g_cbEnumBuffer1 = + sizeof("TEST NAME\0TEST VALUE\0""999999\0RDONLYHOST\0" + "/test/name\0/test/value\0""999999999999\0RDONLYGUEST\0\0\0\0\0") - 1; + +static const struct enumStringStruct +{ + /** The enumeration pattern to test */ + const char *pszPatterns; + /** The size of the pattern string */ + const uint32_t cchPatterns; + /** The expected enumeration output strings */ + const char **papchResult; + /** The size of the output strings */ + const uint32_t *pacchResult; + /** The size of the buffer needed for the enumeration */ + const uint32_t cbBuffer; +} g_aEnumStrings[] = +{ + { + "", sizeof(""), + g_apchEnumResult0, + g_acbEnumResult0, + g_cbEnumBuffer0 + }, + { + "/*\0?E*", sizeof("/*\0?E*"), + g_apchEnumResult1, + g_acbEnumResult1, + g_cbEnumBuffer1 + }, + { + "/*|?E*", sizeof("/*|?E*"), + g_apchEnumResult1, + g_acbEnumResult1, + g_cbEnumBuffer1 + } +}; + +/** + * Test the ENUM_PROPS_HOST function. + * @returns iprt status value to indicate whether the test went as expected. + * @note prints its own diagnostic information to stdout. + */ +static void testEnumPropsHost(VBOXHGCMSVCFNTABLE *ptable) +{ + RTTestISub("ENUM_PROPS_HOST"); + RTTESTI_CHECK_RETV(RT_VALID_PTR(ptable->pfnHostCall)); + + for (unsigned i = 0; i < RT_ELEMENTS(g_aEnumStrings); ++i) + { + VBOXHGCMSVCPARM aParms[3]; + char abBuffer[2048]; + RTTESTI_CHECK_RETV(g_aEnumStrings[i].cbBuffer < sizeof(abBuffer)); + + /* Check that we get buffer overflow with a too small buffer. */ + HGCMSvcSetPv(&aParms[0], (void *)g_aEnumStrings[i].pszPatterns, g_aEnumStrings[i].cchPatterns); + HGCMSvcSetPv(&aParms[1], (void *)abBuffer, g_aEnumStrings[i].cbBuffer - 1); + memset(abBuffer, 0x55, sizeof(abBuffer)); + int rc2 = ptable->pfnHostCall(ptable->pvService, GUEST_PROP_FN_HOST_ENUM_PROPS, 3, aParms); + if (rc2 == VERR_BUFFER_OVERFLOW) + { + uint32_t cbNeeded; + RTTESTI_CHECK_RC(rc2 = HGCMSvcGetU32(&aParms[2], &cbNeeded), VINF_SUCCESS); + if (RT_SUCCESS(rc2)) + RTTESTI_CHECK_MSG(cbNeeded == g_aEnumStrings[i].cbBuffer, + ("expected %u, got %u, pattern %d\n", g_aEnumStrings[i].cbBuffer, cbNeeded, i)); + } + else + RTTestIFailed("ENUM_PROPS_HOST returned %Rrc instead of VERR_BUFFER_OVERFLOW on too small buffer, pattern number %d.", rc2, i); + + /* Make a successfull call. */ + HGCMSvcSetPv(&aParms[0], (void *)g_aEnumStrings[i].pszPatterns, g_aEnumStrings[i].cchPatterns); + HGCMSvcSetPv(&aParms[1], (void *)abBuffer, g_aEnumStrings[i].cbBuffer); + memset(abBuffer, 0x55, sizeof(abBuffer)); + rc2 = ptable->pfnHostCall(ptable->pvService, GUEST_PROP_FN_HOST_ENUM_PROPS, 3, aParms); + if (rc2 == VINF_SUCCESS) + { + /* Look for each of the result strings in the buffer which was returned */ + for (unsigned j = 0; g_aEnumStrings[i].papchResult[j] != NULL; ++j) + { + bool found = false; + for (unsigned k = 0; !found && k < g_aEnumStrings[i].cbBuffer + - g_aEnumStrings[i].pacchResult[j]; + ++k) + if (memcmp(abBuffer + k, g_aEnumStrings[i].papchResult[j], + g_aEnumStrings[i].pacchResult[j]) == 0) + found = true; + if (!found) + RTTestIFailed("ENUM_PROPS_HOST did not produce the expected output for pattern %d.", i); + } + } + else + RTTestIFailed("ENUM_PROPS_HOST returned %Rrc instead of VINF_SUCCESS, pattern number %d.", rc2, i); + } +} + +/** + * Set a property by calling the service + * @returns the status returned by the call to the service + * + * @param pTable the service instance handle + * @param pcszName the name of the property to set + * @param pcszValue the value to set the property to + * @param pcszFlags the flag string to set if one of the SET_PROP[_HOST] + * commands is used + * @param isHost whether the SET_PROP[_VALUE]_HOST commands should be + * used, rather than the guest ones + * @param useSetProp whether SET_PROP[_HOST] should be used rather than + * SET_PROP_VALUE[_HOST] + */ +int doSetProperty(VBOXHGCMSVCFNTABLE *pTable, const char *pcszName, + const char *pcszValue, const char *pcszFlags, bool isHost, + bool useSetProp) +{ + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + int command = GUEST_PROP_FN_SET_PROP_VALUE; + if (isHost) + { + if (useSetProp) + command = GUEST_PROP_FN_HOST_SET_PROP; + else + command = GUEST_PROP_FN_HOST_SET_PROP_VALUE; + } + else if (useSetProp) + command = GUEST_PROP_FN_SET_PROP; + VBOXHGCMSVCPARM aParms[3]; + /* Work around silly constant issues - we ought to allow passing + * constant strings in the hgcm parameters. */ + char szName[GUEST_PROP_MAX_NAME_LEN]; + char szValue[GUEST_PROP_MAX_VALUE_LEN]; + char szFlags[GUEST_PROP_MAX_FLAGS_LEN]; + RTStrPrintf(szName, sizeof(szName), "%s", pcszName); + RTStrPrintf(szValue, sizeof(szValue), "%s", pcszValue); + RTStrPrintf(szFlags, sizeof(szFlags), "%s", pcszFlags); + HGCMSvcSetStr(&aParms[0], szName); + HGCMSvcSetStr(&aParms[1], szValue); + HGCMSvcSetStr(&aParms[2], szFlags); + if (isHost) + callHandle.rc = pTable->pfnHostCall(pTable->pvService, command, + useSetProp ? 3 : 2, aParms); + else + pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, command, + useSetProp ? 3 : 2, aParms, 0); + return callHandle.rc; +} + +/** + * Test the SET_PROP, SET_PROP_VALUE, SET_PROP_HOST and SET_PROP_VALUE_HOST + * functions. + * @returns iprt status value to indicate whether the test went as expected. + * @note prints its own diagnostic information to stdout. + */ +static void testSetProp(VBOXHGCMSVCFNTABLE *pTable) +{ + RTTestISub("SET_PROP, _VALUE, _HOST, _VALUE_HOST"); + + /** Array of properties for testing SET_PROP_HOST and _GUEST. */ + static const struct + { + /** Property name */ + const char *pcszName; + /** Property value */ + const char *pcszValue; + /** Property flags */ + const char *pcszFlags; + /** Should this be set as the host or the guest? */ + bool isHost; + /** Should we use SET_PROP or SET_PROP_VALUE? */ + bool useSetProp; + /** Should this succeed or be rejected with VERR_PERMISSION_DENIED? */ + bool isAllowed; + } + s_aSetProperties[] = + { + { "Red", "Stop!", "transient", false, true, true }, + { "Amber", "Caution!", "", false, false, true }, + { "Green", "Go!", "readonly", true, true, true }, + { "Blue", "What on earth...?", "", true, false, true }, + { "/test/name", "test", "", false, true, false }, + { "TEST NAME", "test", "", true, true, false }, + { "Green", "gone out...", "", false, false, false }, + { "Green", "gone out...", "", true, false, false }, + { "/VirtualBox/GuestAdd/SharedFolders/MountDir", "test", "", false, true, false }, + { "/VirtualBox/GuestAdd/SomethingElse", "test", "", false, true, true }, + { "/VirtualBox/HostInfo/VRDP/Client/1/Name", "test", "", false, false, false }, + { "/VirtualBox/GuestAdd/SharedFolders/MountDir", "test", "", true, true, true }, + { "/VirtualBox/HostInfo/VRDP/Client/1/Name", "test", "TRANSRESET", true, true, true }, + }; + + for (unsigned i = 0; i < RT_ELEMENTS(s_aSetProperties); ++i) + { + int rc = doSetProperty(pTable, + s_aSetProperties[i].pcszName, + s_aSetProperties[i].pcszValue, + s_aSetProperties[i].pcszFlags, + s_aSetProperties[i].isHost, + s_aSetProperties[i].useSetProp); + if (s_aSetProperties[i].isAllowed && RT_FAILURE(rc)) + RTTestIFailed("Setting property '%s' failed with rc=%Rrc.", + s_aSetProperties[i].pcszName, rc); + else if ( !s_aSetProperties[i].isAllowed + && rc != VERR_PERMISSION_DENIED) + RTTestIFailed("Setting property '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.", + s_aSetProperties[i].pcszName, rc); + } +} + +/** + * Delete a property by calling the service + * @returns the status returned by the call to the service + * + * @param pTable the service instance handle + * @param pcszName the name of the property to delete + * @param isHost whether the DEL_PROP_HOST command should be used, rather + * than the guest one + */ +static int doDelProp(VBOXHGCMSVCFNTABLE *pTable, const char *pcszName, bool isHost) +{ + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + int command = GUEST_PROP_FN_DEL_PROP; + if (isHost) + command = GUEST_PROP_FN_HOST_DEL_PROP; + VBOXHGCMSVCPARM aParms[1]; + HGCMSvcSetStr(&aParms[0], pcszName); + if (isHost) + callHandle.rc = pTable->pfnHostCall(pTable->pvService, command, 1, aParms); + else + pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, command, 1, aParms, 0); + return callHandle.rc; +} + +/** + * Test the DEL_PROP, and DEL_PROP_HOST functions. + * @returns iprt status value to indicate whether the test went as expected. + * @note prints its own diagnostic information to stdout. + */ +static void testDelProp(VBOXHGCMSVCFNTABLE *pTable) +{ + RTTestISub("DEL_PROP, DEL_PROP_HOST"); + + /** Array of properties for testing DEL_PROP_HOST and _GUEST. */ + static const struct + { + /** Property name */ + const char *pcszName; + /** Should this be set as the host or the guest? */ + bool isHost; + /** Should this succeed or be rejected with VERR_PERMISSION_DENIED? */ + bool isAllowed; + } + s_aDelProperties[] = + { + { "Red", false, true }, + { "Amber", true, true }, + { "Red2", false, true }, + { "Amber2", true, true }, + { "Green", false, false }, + { "Green", true, false }, + { "/test/name", false, false }, + { "TEST NAME", true, false }, + }; + + for (unsigned i = 0; i < RT_ELEMENTS(s_aDelProperties); ++i) + { + int rc = doDelProp(pTable, s_aDelProperties[i].pcszName, + s_aDelProperties[i].isHost); + if (s_aDelProperties[i].isAllowed && RT_FAILURE(rc)) + RTTestIFailed("Deleting property '%s' failed with rc=%Rrc.", + s_aDelProperties[i].pcszName, rc); + else if ( !s_aDelProperties[i].isAllowed + && rc != VERR_PERMISSION_DENIED ) + RTTestIFailed("Deleting property '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.", + s_aDelProperties[i].pcszName, rc); + } +} + +/** + * Test the GET_PROP_HOST function. + * @returns iprt status value to indicate whether the test went as expected. + * @note prints its own diagnostic information to stdout. + */ +static void testGetProp(VBOXHGCMSVCFNTABLE *pTable) +{ + RTTestISub("GET_PROP_HOST"); + + /** Array of properties for testing GET_PROP_HOST. */ + static const struct + { + /** Property name */ + const char *pcszName; + /** What value/flags pattern do we expect back? */ + const char *pchValue; + /** What size should the value/flags array be? */ + uint32_t cchValue; + /** Should this property exist? */ + bool exists; + /** Do we expect a particular timestamp? */ + bool hasTimestamp; + /** What timestamp if any do ex expect? */ + uint64_t u64Timestamp; + } + s_aGetProperties[] = + { + { "test/name/", "test/value/\0", sizeof("test/value/\0"), true, true, 0 }, + { "test name", "test value\0TRANSIENT, READONLY", + sizeof("test value\0TRANSIENT, READONLY"), true, true, 999 }, + { "TEST NAME", "TEST VALUE\0RDONLYHOST", sizeof("TEST VALUE\0RDONLYHOST"), + true, true, 999999 }, + { "/test/name", "/test/value\0RDONLYGUEST", + sizeof("/test/value\0RDONLYGUEST"), true, true, UINT64_C(999999999999) }, + { "Green", "Go!\0READONLY", sizeof("Go!\0READONLY"), true, false, 0 }, + { "Blue", "What on earth...?\0", sizeof("What on earth...?\0"), true, + false, 0 }, + { "Red", "", 0, false, false, 0 }, + }; + + for (unsigned i = 0; i < RT_ELEMENTS(s_aGetProperties); ++i) + { + VBOXHGCMSVCPARM aParms[4]; + /* Work around silly constant issues - we ought to allow passing + * constant strings in the hgcm parameters. */ + char szBuffer[GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN]; + RTTESTI_CHECK_RETV(s_aGetProperties[i].cchValue < sizeof(szBuffer)); + + HGCMSvcSetStr(&aParms[0], s_aGetProperties[i].pcszName); + memset(szBuffer, 0x55, sizeof(szBuffer)); + HGCMSvcSetPv(&aParms[1], szBuffer, sizeof(szBuffer)); + int rc2 = pTable->pfnHostCall(pTable->pvService, GUEST_PROP_FN_HOST_GET_PROP, 4, aParms); + + if (s_aGetProperties[i].exists && RT_FAILURE(rc2)) + { + RTTestIFailed("Getting property '%s' failed with rc=%Rrc.", + s_aGetProperties[i].pcszName, rc2); + continue; + } + + if (!s_aGetProperties[i].exists && rc2 != VERR_NOT_FOUND) + { + RTTestIFailed("Getting property '%s' returned %Rrc instead of VERR_NOT_FOUND.", + s_aGetProperties[i].pcszName, rc2); + continue; + } + + if (s_aGetProperties[i].exists) + { + AssertRC(rc2); + + uint32_t u32ValueLen = UINT32_MAX; + RTTESTI_CHECK_RC(rc2 = HGCMSvcGetU32(&aParms[3], &u32ValueLen), VINF_SUCCESS); + if (RT_SUCCESS(rc2)) + { + RTTESTI_CHECK_MSG(u32ValueLen <= sizeof(szBuffer), ("u32ValueLen=%d", u32ValueLen)); + if (memcmp(szBuffer, s_aGetProperties[i].pchValue, s_aGetProperties[i].cchValue) != 0) + RTTestIFailed("Unexpected result '%.*s' for property '%s', expected '%.*s'.", + u32ValueLen, szBuffer, s_aGetProperties[i].pcszName, + s_aGetProperties[i].cchValue, s_aGetProperties[i].pchValue); + } + + if (s_aGetProperties[i].hasTimestamp) + { + uint64_t u64Timestamp = UINT64_MAX; + RTTESTI_CHECK_RC(rc2 = HGCMSvcGetU64(&aParms[2], &u64Timestamp), VINF_SUCCESS); + if (u64Timestamp != s_aGetProperties[i].u64Timestamp) + RTTestIFailed("Bad timestamp %llu for property '%s', expected %llu.", + u64Timestamp, s_aGetProperties[i].pcszName, + s_aGetProperties[i].u64Timestamp); + } + } + } +} + +/** Array of properties for testing GET_PROP_HOST. */ +static const struct +{ + /** Buffer returned */ + const char *pchBuffer; + /** What size should the buffer be? */ + uint32_t cbBuffer; +} +g_aGetNotifications[] = +{ + { "Red\0Stop!\0TRANSIENT", sizeof("Red\0Stop!\0TRANSIENT") }, + { "Amber\0Caution!\0", sizeof("Amber\0Caution!\0") }, + { "Green\0Go!\0READONLY", sizeof("Green\0Go!\0READONLY") }, + { "Blue\0What on earth...?\0", sizeof("Blue\0What on earth...?\0") }, + { "/VirtualBox/GuestAdd/SomethingElse\0test\0", + sizeof("/VirtualBox/GuestAdd/SomethingElse\0test\0") }, + { "/VirtualBox/GuestAdd/SharedFolders/MountDir\0test\0RDONLYGUEST", + sizeof("/VirtualBox/GuestAdd/SharedFolders/MountDir\0test\0RDONLYGUEST") }, + { "/VirtualBox/HostInfo/VRDP/Client/1/Name\0test\0TRANSIENT, RDONLYGUEST, TRANSRESET", + sizeof("/VirtualBox/HostInfo/VRDP/Client/1/Name\0test\0TRANSIENT, RDONLYGUEST, TRANSRESET") }, + { "Red\0\0", sizeof("Red\0\0") }, + { "Amber\0\0", sizeof("Amber\0\0") }, +}; + +/** + * Test the GET_NOTIFICATION function. + * @returns iprt status value to indicate whether the test went as expected. + * @note prints its own diagnostic information to stdout. + */ +static void testGetNotification(VBOXHGCMSVCFNTABLE *pTable) +{ + RTTestISub("GET_NOTIFICATION"); + + /* Test "buffer too small" */ + static char s_szPattern[] = ""; + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + VBOXHGCMSVCPARM aParms[4]; + uint32_t cbRetNeeded; + + for (uint32_t cbBuf = 1; + cbBuf < g_aGetNotifications[0].cbBuffer - 1; + cbBuf++) + { + void *pvBuf = RTTestGuardedAllocTail(g_hTest, cbBuf); + RTTESTI_CHECK_BREAK(pvBuf); + memset(pvBuf, 0x55, cbBuf); + + HGCMSvcSetPv(&aParms[0], (void *)s_szPattern, sizeof(s_szPattern)); + HGCMSvcSetU64(&aParms[1], 1); + HGCMSvcSetPv(&aParms[2], pvBuf, cbBuf); + pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, GUEST_PROP_FN_GET_NOTIFICATION, 4, aParms, 0); + + if ( callHandle.rc != VERR_BUFFER_OVERFLOW + || RT_FAILURE(HGCMSvcGetU32(&aParms[3], &cbRetNeeded)) + || cbRetNeeded != g_aGetNotifications[0].cbBuffer + ) + { + RTTestIFailed("Getting notification for property '%s' with a too small buffer did not fail correctly: %Rrc", + g_aGetNotifications[0].pchBuffer, callHandle.rc); + } + RTTestGuardedFree(g_hTest, pvBuf); + } + + /* Test successful notification queries. Start with an unknown timestamp + * to get the oldest available notification. */ + uint64_t u64Timestamp = 1; + for (unsigned i = 0; i < RT_ELEMENTS(g_aGetNotifications); ++i) + { + uint32_t cbBuf = g_aGetNotifications[i].cbBuffer + _1K; + void *pvBuf = RTTestGuardedAllocTail(g_hTest, cbBuf); + RTTESTI_CHECK_BREAK(pvBuf); + memset(pvBuf, 0x55, cbBuf); + + HGCMSvcSetPv(&aParms[0], (void *)s_szPattern, sizeof(s_szPattern)); + HGCMSvcSetU64(&aParms[1], u64Timestamp); + HGCMSvcSetPv(&aParms[2], pvBuf, cbBuf); + pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, GUEST_PROP_FN_GET_NOTIFICATION, 4, aParms, 0); + if ( RT_FAILURE(callHandle.rc) + || (i == 0 && callHandle.rc != VWRN_NOT_FOUND) + || RT_FAILURE(HGCMSvcGetU64(&aParms[1], &u64Timestamp)) + || RT_FAILURE(HGCMSvcGetU32(&aParms[3], &cbRetNeeded)) + || cbRetNeeded != g_aGetNotifications[i].cbBuffer + || memcmp(pvBuf, g_aGetNotifications[i].pchBuffer, cbRetNeeded) != 0 + ) + { + RTTestIFailed("Failed to get notification for property '%s' (rc=%Rrc).", + g_aGetNotifications[i].pchBuffer, callHandle.rc); + } + RTTestGuardedFree(g_hTest, pvBuf); + } +} + +/** Parameters for the asynchronous guest notification call */ +struct asyncNotification_ +{ + /** Call parameters */ + VBOXHGCMSVCPARM aParms[4]; + /** Result buffer */ + char abBuffer[GUEST_PROP_MAX_NAME_LEN + GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN]; + /** Return value */ + VBOXHGCMCALLHANDLE_TYPEDEF callHandle; +} g_AsyncNotification; + +/** + * Set up the test for the asynchronous GET_NOTIFICATION function. + */ +static void setupAsyncNotification(VBOXHGCMSVCFNTABLE *pTable) +{ + RTTestISub("Async GET_NOTIFICATION without notifications"); + static char s_szPattern[] = ""; + + HGCMSvcSetPv(&g_AsyncNotification.aParms[0], (void *)s_szPattern, sizeof(s_szPattern)); + HGCMSvcSetU64(&g_AsyncNotification.aParms[1], 0); + HGCMSvcSetPv(&g_AsyncNotification.aParms[2], (void *)g_AsyncNotification.abBuffer, + sizeof(g_AsyncNotification.abBuffer)); + g_AsyncNotification.callHandle.rc = VINF_HGCM_ASYNC_EXECUTE; + pTable->pfnCall(pTable->pvService, &g_AsyncNotification.callHandle, 0, NULL, + GUEST_PROP_FN_GET_NOTIFICATION, 4, g_AsyncNotification.aParms, 0); + if (RT_FAILURE(g_AsyncNotification.callHandle.rc)) + RTTestIFailed("GET_NOTIFICATION call failed, rc=%Rrc.", g_AsyncNotification.callHandle.rc); + else if (g_AsyncNotification.callHandle.rc != VINF_HGCM_ASYNC_EXECUTE) + RTTestIFailed("GET_NOTIFICATION call completed when no new notifications should be available."); +} + +/** + * Test the asynchronous GET_NOTIFICATION function. + */ +static void testAsyncNotification(VBOXHGCMSVCFNTABLE *pTable) +{ + RT_NOREF1(pTable); + uint64_t u64Timestamp; + uint32_t u32Size; + if ( g_AsyncNotification.callHandle.rc != VINF_SUCCESS + || RT_FAILURE(HGCMSvcGetU64(&g_AsyncNotification.aParms[1], &u64Timestamp)) + || RT_FAILURE(HGCMSvcGetU32(&g_AsyncNotification.aParms[3], &u32Size)) + || u32Size != g_aGetNotifications[0].cbBuffer + || memcmp(g_AsyncNotification.abBuffer, g_aGetNotifications[0].pchBuffer, u32Size) != 0 + ) + { + RTTestIFailed("Asynchronous GET_NOTIFICATION call did not complete as expected, rc=%Rrc.", + g_AsyncNotification.callHandle.rc); + } +} + + +static void test2(void) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + initTable(&svcTable, &svcHelpers); + + /* The function is inside the service, not HGCM. */ + RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable)); + + testSetPropsHost(&svcTable); + testEnumPropsHost(&svcTable); + + /* Set up the asynchronous notification test */ + setupAsyncNotification(&svcTable); + testSetProp(&svcTable); + RTTestISub("Async notification call data"); + testAsyncNotification(&svcTable); /* Our previous notification call should have completed by now. */ + + testDelProp(&svcTable); + testGetProp(&svcTable); + testGetNotification(&svcTable); + + /* Cleanup */ + RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService)); +} + +/** + * Set the global flags value by calling the service + * @returns the status returned by the call to the service + * + * @param pTable the service instance handle + * @param fFlags the flags to set + */ +static int doSetGlobalFlags(VBOXHGCMSVCFNTABLE *pTable, uint32_t fFlags) +{ + VBOXHGCMSVCPARM paParm; + HGCMSvcSetU32(&paParm, fFlags); + int rc = pTable->pfnHostCall(pTable->pvService, GUEST_PROP_FN_HOST_SET_GLOBAL_FLAGS, 1, &paParm); + if (RT_FAILURE(rc)) + { + char szFlags[GUEST_PROP_MAX_FLAGS_LEN]; + if (RT_FAILURE(GuestPropWriteFlags(fFlags, szFlags))) + RTTestIFailed("Failed to set the global flags."); + else + RTTestIFailed("Failed to set the global flags \"%s\".", szFlags); + } + return rc; +} + +/** + * Test the SET_PROP, SET_PROP_VALUE, SET_PROP_HOST and SET_PROP_VALUE_HOST + * functions. + * @returns iprt status value to indicate whether the test went as expected. + * @note prints its own diagnostic information to stdout. + */ +static void testSetPropROGuest(VBOXHGCMSVCFNTABLE *pTable) +{ + RTTestISub("global READONLYGUEST and SET_PROP*"); + + /** Array of properties for testing SET_PROP_HOST and _GUEST with the + * READONLYGUEST global flag set. */ + static const struct + { + /** Property name */ + const char *pcszName; + /** Property value */ + const char *pcszValue; + /** Property flags */ + const char *pcszFlags; + /** Should this be set as the host or the guest? */ + bool isHost; + /** Should we use SET_PROP or SET_PROP_VALUE? */ + bool useSetProp; + /** Should this succeed or be rejected with VERR_ (NOT VINF_!) + * PERMISSION_DENIED? The global check is done after the property one. */ + bool isAllowed; + } + s_aSetPropertiesROGuest[] = + { + { "Red", "Stop!", "transient", false, true, true }, + { "Amber", "Caution!", "", false, false, true }, + { "Green", "Go!", "readonly", true, true, true }, + { "Blue", "What on earth...?", "", true, false, true }, + { "/test/name", "test", "", false, true, true }, + { "TEST NAME", "test", "", true, true, true }, + { "Green", "gone out...", "", false, false, false }, + { "Green", "gone out....", "", true, false, false }, + }; + + RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(pTable)); + int rc = doSetGlobalFlags(pTable, GUEST_PROP_F_RDONLYGUEST); + if (RT_SUCCESS(rc)) + { + for (unsigned i = 0; i < RT_ELEMENTS(s_aSetPropertiesROGuest); ++i) + { + rc = doSetProperty(pTable, s_aSetPropertiesROGuest[i].pcszName, + s_aSetPropertiesROGuest[i].pcszValue, + s_aSetPropertiesROGuest[i].pcszFlags, + s_aSetPropertiesROGuest[i].isHost, + s_aSetPropertiesROGuest[i].useSetProp); + if (s_aSetPropertiesROGuest[i].isAllowed && RT_FAILURE(rc)) + RTTestIFailed("Setting property '%s' to '%s' failed with rc=%Rrc.", + s_aSetPropertiesROGuest[i].pcszName, + s_aSetPropertiesROGuest[i].pcszValue, rc); + else if ( !s_aSetPropertiesROGuest[i].isAllowed + && rc != VERR_PERMISSION_DENIED) + RTTestIFailed("Setting property '%s' to '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.\n", + s_aSetPropertiesROGuest[i].pcszName, + s_aSetPropertiesROGuest[i].pcszValue, rc); + else if ( !s_aSetPropertiesROGuest[i].isHost + && s_aSetPropertiesROGuest[i].isAllowed + && rc != VINF_PERMISSION_DENIED) + RTTestIFailed("Setting property '%s' to '%s' returned %Rrc instead of VINF_PERMISSION_DENIED.\n", + s_aSetPropertiesROGuest[i].pcszName, + s_aSetPropertiesROGuest[i].pcszValue, rc); + } + } + RTTESTI_CHECK_RC_OK(pTable->pfnUnload(pTable->pvService)); +} + +/** + * Test the DEL_PROP, and DEL_PROP_HOST functions. + * @returns iprt status value to indicate whether the test went as expected. + * @note prints its own diagnostic information to stdout. + */ +static void testDelPropROGuest(VBOXHGCMSVCFNTABLE *pTable) +{ + RTTestISub("global READONLYGUEST and DEL_PROP*"); + + /** Array of properties for testing DEL_PROP_HOST and _GUEST with + * READONLYGUEST set globally. */ + static const struct + { + /** Property name */ + const char *pcszName; + /** Should this be deleted as the host (or the guest)? */ + bool isHost; + /** Should this property be created first? (As host, obviously) */ + bool shouldCreate; + /** And with what flags? */ + const char *pcszFlags; + /** Should this succeed or be rejected with VERR_ (NOT VINF_!) + * PERMISSION_DENIED? The global check is done after the property one. */ + bool isAllowed; + } + s_aDelPropertiesROGuest[] = + { + { "Red", true, true, "", true }, + { "Amber", false, true, "", true }, + { "Red2", true, false, "", true }, + { "Amber2", false, false, "", true }, + { "Red3", true, true, "READONLY", false }, + { "Amber3", false, true, "READONLY", false }, + { "Red4", true, true, "RDONLYHOST", false }, + { "Amber4", false, true, "RDONLYHOST", true }, + }; + + RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(pTable)); + int rc = doSetGlobalFlags(pTable, GUEST_PROP_F_RDONLYGUEST); + if (RT_SUCCESS(rc)) + { + for (unsigned i = 0; i < RT_ELEMENTS(s_aDelPropertiesROGuest); ++i) + { + if (s_aDelPropertiesROGuest[i].shouldCreate) + rc = doSetProperty(pTable, s_aDelPropertiesROGuest[i].pcszName, + "none", s_aDelPropertiesROGuest[i].pcszFlags, + true, true); + rc = doDelProp(pTable, s_aDelPropertiesROGuest[i].pcszName, + s_aDelPropertiesROGuest[i].isHost); + if (s_aDelPropertiesROGuest[i].isAllowed && RT_FAILURE(rc)) + RTTestIFailed("Deleting property '%s' failed with rc=%Rrc.", + s_aDelPropertiesROGuest[i].pcszName, rc); + else if ( !s_aDelPropertiesROGuest[i].isAllowed + && rc != VERR_PERMISSION_DENIED) + RTTestIFailed("Deleting property '%s' returned %Rrc instead of VERR_PERMISSION_DENIED.", + s_aDelPropertiesROGuest[i].pcszName, rc); + else if ( !s_aDelPropertiesROGuest[i].isHost + && s_aDelPropertiesROGuest[i].shouldCreate + && s_aDelPropertiesROGuest[i].isAllowed + && rc != VINF_PERMISSION_DENIED) + RTTestIFailed("Deleting property '%s' as guest returned %Rrc instead of VINF_PERMISSION_DENIED.", + s_aDelPropertiesROGuest[i].pcszName, rc); + } + } + RTTESTI_CHECK_RC_OK(pTable->pfnUnload(pTable->pvService)); +} + +static void test3(void) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + initTable(&svcTable, &svcHelpers); + testSetPropROGuest(&svcTable); + testDelPropROGuest(&svcTable); +} + +static void test4(void) +{ + RTTestISub("GET_PROP_HOST buffer handling"); + + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + initTable(&svcTable, &svcHelpers); + RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable)); + + /* Insert a property that we can mess around with. */ + static char const s_szProp[] = "/MyProperties/Sub/Sub/Sub/Sub/Sub/Sub/Sub/Property"; + static char const s_szValue[] = "Property Value"; + RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, s_szProp, s_szValue, "", true, true)); + + + /* Get the value with buffer sizes up to 1K. */ + for (unsigned iVariation = 0; iVariation < 2; iVariation++) + { + for (uint32_t cbBuf = 0; cbBuf < _1K; cbBuf++) + { + void *pvBuf; + RTTESTI_CHECK_RC_BREAK(RTTestGuardedAlloc(g_hTest, cbBuf, 1, iVariation == 0, &pvBuf), VINF_SUCCESS); + + VBOXHGCMSVCPARM aParms[4]; + HGCMSvcSetStr(&aParms[0], s_szProp); + HGCMSvcSetPv(&aParms[1], pvBuf, cbBuf); + svcTable.pfnHostCall(svcTable.pvService, GUEST_PROP_FN_HOST_GET_PROP, RT_ELEMENTS(aParms), aParms); + + RTTestGuardedFree(g_hTest, pvBuf); + } + } + + /* Done. */ + RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService)); +} + +static void test5(void) +{ + RTTestISub("ENUM_PROPS_HOST buffer handling"); + + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + initTable(&svcTable, &svcHelpers); + RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable)); + + /* Insert a few property that we can mess around with. */ + RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/Sub/Sub/Sub/Sub/Sub/Sub/Sub/Property", "Property Value", "", true, true)); + RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/12357", "83848569", "", true, true)); + RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/56678", "abcdefghijklm", "", true, true)); + RTTESTI_CHECK_RC_OK(doSetProperty(&svcTable, "/MyProperties/932769", "n", "", true, true)); + + /* Get the value with buffer sizes up to 1K. */ + for (unsigned iVariation = 0; iVariation < 2; iVariation++) + { + for (uint32_t cbBuf = 0; cbBuf < _1K; cbBuf++) + { + void *pvBuf; + RTTESTI_CHECK_RC_BREAK(RTTestGuardedAlloc(g_hTest, cbBuf, 1, iVariation == 0, &pvBuf), VINF_SUCCESS); + + VBOXHGCMSVCPARM aParms[3]; + HGCMSvcSetStr(&aParms[0], "*"); + HGCMSvcSetPv(&aParms[1], pvBuf, cbBuf); + svcTable.pfnHostCall(svcTable.pvService, GUEST_PROP_FN_HOST_ENUM_PROPS, RT_ELEMENTS(aParms), aParms); + + RTTestGuardedFree(g_hTest, pvBuf); + } + } + + /* Done. */ + RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService)); +} + +static void test6(void) +{ + RTTestISub("Max properties"); + + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + initTable(&svcTable, &svcHelpers); + RTTESTI_CHECK_RC_OK_RETV(VBoxHGCMSvcLoad(&svcTable)); + + /* Insert the max number of properties. */ + static char const s_szPropFmt[] = "/MyProperties/Sub/Sub/Sub/Sub/Sub/Sub/Sub/PropertyNo#%u"; + char szProp[80]; + unsigned cProps = 0; + for (;;) + { + RTStrPrintf(szProp, sizeof(szProp), s_szPropFmt, cProps); + int rc = doSetProperty(&svcTable, szProp, "myvalue", "", true, true); + if (rc == VERR_TOO_MUCH_DATA) + break; + if (RT_FAILURE(rc)) + { + RTTestIFailed("Unexpected error %Rrc setting property number %u", rc, cProps); + break; + } + cProps++; + } + RTTestIValue("Max Properties", cProps, RTTESTUNIT_OCCURRENCES); + + /* Touch them all again. */ + for (unsigned iProp = 0; iProp < cProps; iProp++) + { + RTStrPrintf(szProp, sizeof(szProp), s_szPropFmt, iProp); + int rc; + RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", true, true)) == VINF_SUCCESS, + ("%Rrc - #%u\n", rc, iProp)); + RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", true, false)) == VINF_SUCCESS, + ("%Rrc - #%u\n", rc, iProp)); + RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", false, true)) == VINF_SUCCESS, + ("%Rrc - #%u\n", rc, iProp)); + RTTESTI_CHECK_MSG((rc = doSetProperty(&svcTable, szProp, "myvalue", "", false, false)) == VINF_SUCCESS, + ("%Rrc - #%u\n", rc, iProp)); + } + + /* Benchmark. */ + uint64_t cNsMax = 0; + uint64_t cNsMin = UINT64_MAX; + uint64_t cNsAvg = 0; + for (unsigned iProp = 0; iProp < cProps; iProp++) + { + size_t cchProp = RTStrPrintf(szProp, sizeof(szProp), s_szPropFmt, iProp); + + uint64_t cNsElapsed = RTTimeNanoTS(); + unsigned iCall; + for (iCall = 0; iCall < 1000; iCall++) + { + VBOXHGCMSVCPARM aParms[4]; + char szBuffer[256]; + HGCMSvcSetPv(&aParms[0], szProp, (uint32_t)cchProp + 1); + HGCMSvcSetPv(&aParms[1], szBuffer, sizeof(szBuffer)); + RTTESTI_CHECK_RC_BREAK(svcTable.pfnHostCall(svcTable.pvService, GUEST_PROP_FN_HOST_GET_PROP, 4, aParms), VINF_SUCCESS); + } + cNsElapsed = RTTimeNanoTS() - cNsElapsed; + if (iCall) + { + uint64_t cNsPerCall = cNsElapsed / iCall; + cNsAvg += cNsPerCall; + if (cNsPerCall < cNsMin) + cNsMin = cNsPerCall; + if (cNsPerCall > cNsMax) + cNsMax = cNsPerCall; + } + } + if (cProps) + cNsAvg /= cProps; + RTTestIValue("GET_PROP_HOST Min", cNsMin, RTTESTUNIT_NS_PER_CALL); + RTTestIValue("GET_PROP_HOST Avg", cNsAvg, RTTESTUNIT_NS_PER_CALL); + RTTestIValue("GET_PROP_HOST Max", cNsMax, RTTESTUNIT_NS_PER_CALL); + + /* Done. */ + RTTESTI_CHECK_RC_OK(svcTable.pfnUnload(svcTable.pvService)); +} + + + + +int main() +{ + RTEXITCODE rcExit = RTTestInitAndCreate("tstGuestPropSvc", &g_hTest); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + RTTestBanner(g_hTest); + + testConvertFlags(); + test2(); + test3(); + test4(); + test5(); + test6(); + + return RTTestSummaryAndDestroy(g_hTest); +} diff --git a/src/VBox/HostServices/HostChannel/HostChannel.cpp b/src/VBox/HostServices/HostChannel/HostChannel.cpp new file mode 100644 index 00000000..972959d2 --- /dev/null +++ b/src/VBox/HostServices/HostChannel/HostChannel.cpp @@ -0,0 +1,1020 @@ +/** @file + * Host channel. + */ + +/* + * Copyright (C) 2012-2019 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. + */ + +#include <iprt/alloc.h> +#include <iprt/string.h> +#include <iprt/asm.h> +#include <iprt/assert.h> + +#include "HostChannel.h" + + +static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvInstance, + uint32_t u32Id, const void *pvEvent, uint32_t cbEvent); +static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel); + + +/* A registered provider of channels. */ +typedef struct VBOXHOSTCHPROVIDER +{ + int32_t volatile cRefs; + + RTLISTNODE nodeContext; /* Member of the list of providers in the service context. */ + + VBOXHOSTCHCTX *pCtx; + + VBOXHOSTCHANNELINTERFACE iface; + + char *pszName; + + RTLISTANCHOR listChannels; +} VBOXHOSTCHPROVIDER; + +/* An established channel. */ +typedef struct VBOXHOSTCHINSTANCE +{ + int32_t volatile cRefs; + + RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */ + RTLISTNODE nodeProvider; /* In the provider, needed for cleanup when the provider is unregistered. */ + + VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel. */ + VBOXHOSTCHPROVIDER *pProvider; /* NULL if the provider was unregistered. */ + void *pvChannel; /* Provider's context of the channel. */ + uint32_t u32Handle; /* handle assigned to the channel by the service. */ +} VBOXHOSTCHINSTANCE; + +struct VBOXHOSTCHCTX +{ + bool fInitialized; + + RTLISTANCHOR listProviders; +}; + +/* The channel callbacks context. The provider passes the pointer as a callback parameter. + * Created for the provider and deleted when the provider says so. + */ +typedef struct VBOXHOSTCHCALLBACKCTX +{ + RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */ + + VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel, NULL when the client does not exist. */ +} VBOXHOSTCHCALLBACKCTX; + +/* Only one service instance is supported. */ +static VBOXHOSTCHCTX g_ctx = { false }; + +static VBOXHOSTCHANNELCALLBACKS g_callbacks = +{ + HostChannelCallbackEvent, + HostChannelCallbackDeleted +}; + + +/* + * Provider management. + */ + +static void vhcProviderDestroy(VBOXHOSTCHPROVIDER *pProvider) +{ + RTStrFree(pProvider->pszName); +} + +static int32_t vhcProviderAddRef(VBOXHOSTCHPROVIDER *pProvider) +{ + return ASMAtomicIncS32(&pProvider->cRefs); +} + +static void vhcProviderRelease(VBOXHOSTCHPROVIDER *pProvider) +{ + int32_t c = ASMAtomicDecS32(&pProvider->cRefs); + Assert(c >= 0); + if (c == 0) + { + vhcProviderDestroy(pProvider); + RTMemFree(pProvider); + } +} + +static VBOXHOSTCHPROVIDER *vhcProviderFind(VBOXHOSTCHCTX *pCtx, const char *pszName) +{ + VBOXHOSTCHPROVIDER *pProvider = NULL; + + int rc = vboxHostChannelLock(); + + if (RT_SUCCESS(rc)) + { + VBOXHOSTCHPROVIDER *pIter; + RTListForEach(&pCtx->listProviders, pIter, VBOXHOSTCHPROVIDER, nodeContext) + { + if (RTStrCmp(pIter->pszName, pszName) == 0) + { + pProvider = pIter; + + vhcProviderAddRef(pProvider); + + break; + } + } + + vboxHostChannelUnlock(); + } + + return pProvider; +} + +static int vhcProviderRegister(VBOXHOSTCHCTX *pCtx, VBOXHOSTCHPROVIDER *pProvider) +{ + int rc = vboxHostChannelLock(); + + if (RT_SUCCESS(rc)) + { + /** @todo check a duplicate. */ + + RTListAppend(&pCtx->listProviders, &pProvider->nodeContext); + + vboxHostChannelUnlock(); + } + + if (RT_FAILURE(rc)) + { + vhcProviderRelease(pProvider); + } + + return rc; +} + +static int vhcProviderUnregister(VBOXHOSTCHPROVIDER *pProvider) +{ + int rc = vboxHostChannelLock(); + + if (RT_SUCCESS(rc)) + { + /** @todo check that the provider is in the list. */ + /** @todo mark the provider as invalid in each instance. also detach channels? */ + + RTListNodeRemove(&pProvider->nodeContext); + + vboxHostChannelUnlock(); + + vhcProviderRelease(pProvider); + } + + return rc; +} + + +/* + * Select an unique handle for the new channel. + * Works under the lock. + */ +static int vhcHandleCreate(VBOXHOSTCHCLIENT *pClient, uint32_t *pu32Handle) +{ + bool fOver = false; + + for(;;) + { + uint32_t u32Handle = ASMAtomicIncU32(&pClient->u32HandleSrc); + + if (u32Handle == 0) + { + if (fOver) + { + return VERR_NOT_SUPPORTED; + } + + fOver = true; + continue; + } + + VBOXHOSTCHINSTANCE *pDuplicate = NULL; + VBOXHOSTCHINSTANCE *pIter; + RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient) + { + if (pIter->u32Handle == u32Handle) + { + pDuplicate = pIter; + break; + } + } + + if (pDuplicate == NULL) + { + *pu32Handle = u32Handle; + break; + } + } + + return VINF_SUCCESS; +} + + +/* + * Channel instance management. + */ + +static void vhcInstanceDestroy(VBOXHOSTCHINSTANCE *pInstance) +{ + RT_NOREF1(pInstance); + HOSTCHLOG(("HostChannel: destroy %p\n", pInstance)); +} + +static int32_t vhcInstanceAddRef(VBOXHOSTCHINSTANCE *pInstance) +{ + HOSTCHLOG(("INST: %p %d addref\n", pInstance, pInstance->cRefs)); + return ASMAtomicIncS32(&pInstance->cRefs); +} + +static void vhcInstanceRelease(VBOXHOSTCHINSTANCE *pInstance) +{ + int32_t c = ASMAtomicDecS32(&pInstance->cRefs); + HOSTCHLOG(("INST: %p %d release\n", pInstance, pInstance->cRefs)); + Assert(c >= 0); + if (c == 0) + { + vhcInstanceDestroy(pInstance); + RTMemFree(pInstance); + } +} + +static int vhcInstanceCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHINSTANCE **ppInstance) +{ + int rc = VINF_SUCCESS; + + VBOXHOSTCHINSTANCE *pInstance = (VBOXHOSTCHINSTANCE *)RTMemAllocZ(sizeof(VBOXHOSTCHINSTANCE)); + + if (pInstance) + { + rc = vboxHostChannelLock(); + + if (RT_SUCCESS(rc)) + { + rc = vhcHandleCreate(pClient, &pInstance->u32Handle); + + if (RT_SUCCESS(rc)) + { + /* Used by the client, that is in the list of channels. */ + vhcInstanceAddRef(pInstance); + /* Add to the list of created channel instances. It is inactive while pClient is 0. */ + RTListAppend(&pClient->listChannels, &pInstance->nodeClient); + + /* Return to the caller. */ + vhcInstanceAddRef(pInstance); + *ppInstance = pInstance; + } + + vboxHostChannelUnlock(); + } + + if (RT_FAILURE(rc)) + { + RTMemFree(pInstance); + } + } + else + { + rc = VERR_NO_MEMORY; + } + + return rc; +} + +static VBOXHOSTCHINSTANCE *vhcInstanceFind(VBOXHOSTCHCLIENT *pClient, uint32_t u32Handle) +{ + VBOXHOSTCHINSTANCE *pInstance = NULL; + + int rc = vboxHostChannelLock(); + + if (RT_SUCCESS(rc)) + { + VBOXHOSTCHINSTANCE *pIter; + RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient) + { + if ( pIter->pClient + && pIter->u32Handle == u32Handle) + { + pInstance = pIter; + + vhcInstanceAddRef(pInstance); + + break; + } + } + + vboxHostChannelUnlock(); + } + + return pInstance; +} + +static VBOXHOSTCHINSTANCE *vhcInstanceFindByChannelPtr(VBOXHOSTCHCLIENT *pClient, void *pvChannel) +{ + VBOXHOSTCHINSTANCE *pInstance = NULL; + + if (pvChannel == NULL) + { + return NULL; + } + + int rc = vboxHostChannelLock(); + + if (RT_SUCCESS(rc)) + { + VBOXHOSTCHINSTANCE *pIter; + RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient) + { + if ( pIter->pClient + && pIter->pvChannel == pvChannel) + { + pInstance = pIter; + + vhcInstanceAddRef(pInstance); + + break; + } + } + + vboxHostChannelUnlock(); + } + + return pInstance; +} + +static void vhcInstanceDetach(VBOXHOSTCHINSTANCE *pInstance) +{ + HOSTCHLOG(("HostChannel: detach %p\n", pInstance)); + + if (pInstance->pProvider) + { + pInstance->pProvider->iface.HostChannelDetach(pInstance->pvChannel); + RTListNodeRemove(&pInstance->nodeProvider); + vhcProviderRelease(pInstance->pProvider); + pInstance->pProvider = NULL; + vhcInstanceRelease(pInstance); /* Not in the provider's list anymore. */ + } + + int rc = vboxHostChannelLock(); + + if (RT_SUCCESS(rc)) + { + RTListNodeRemove(&pInstance->nodeClient); + + vboxHostChannelUnlock(); + + vhcInstanceRelease(pInstance); /* Not used by the client anymore. */ + } +} + +/* + * Channel callback contexts. + */ +static int vhcCallbackCtxCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHCALLBACKCTX **ppCallbackCtx) +{ + int rc = VINF_SUCCESS; + + VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)RTMemAllocZ(sizeof(VBOXHOSTCHCALLBACKCTX)); + + if (pCallbackCtx != NULL) + { + /* The callback context is accessed by the providers threads. */ + rc = vboxHostChannelLock(); + if (RT_SUCCESS(rc)) + { + RTListAppend(&pClient->listContexts, &pCallbackCtx->nodeClient); + pCallbackCtx->pClient = pClient; + + vboxHostChannelUnlock(); + } + else + { + RTMemFree(pCallbackCtx); + } + } + else + { + rc = VERR_NO_MEMORY; + } + + if (RT_SUCCESS(rc)) + { + *ppCallbackCtx = pCallbackCtx; + } + + return rc; +} + +static int vhcCallbackCtxDelete(VBOXHOSTCHCALLBACKCTX *pCallbackCtx) +{ + int rc = vboxHostChannelLock(); + if (RT_SUCCESS(rc)) + { + VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient; + + if (pClient != NULL) + { + /* The callback is associated with a client. + * Check that the callback is in the list and remove it from the list. + */ + bool fFound = false; + + VBOXHOSTCHCALLBACKCTX *pIter; + RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient) + { + if (pIter == pCallbackCtx) + { + fFound = true; + break; + } + } + + if (fFound) + { + RTListNodeRemove(&pCallbackCtx->nodeClient); + } + else + { + AssertFailed(); + rc = VERR_INVALID_PARAMETER; + } + } + else + { + /* It is not in the clients anymore. May be the client has been disconnected. + * Just free the memory. + */ + } + + vboxHostChannelUnlock(); + } + + if (RT_SUCCESS(rc)) + { + RTMemFree(pCallbackCtx); + } + + return rc; +} + +/* + * Host channel service functions. + */ + +int vboxHostChannelInit(void) +{ + VBOXHOSTCHCTX *pCtx = &g_ctx; + + if (pCtx->fInitialized) + { + return VERR_NOT_SUPPORTED; + } + + pCtx->fInitialized = true; + RTListInit(&pCtx->listProviders); + + return VINF_SUCCESS; +} + +void vboxHostChannelDestroy(void) +{ + VBOXHOSTCHCTX *pCtx = &g_ctx; + + VBOXHOSTCHPROVIDER *pIter; + VBOXHOSTCHPROVIDER *pIterNext; + RTListForEachSafe(&pCtx->listProviders, pIter, pIterNext, VBOXHOSTCHPROVIDER, nodeContext) + { + vhcProviderUnregister(pIter); + } + pCtx->fInitialized = false; +} + +int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient) +{ + /* A guest client is connecting to the service. + * Later the client will use Attach calls to connect to channel providers. + * pClient is already zeroed. + */ + pClient->pCtx = &g_ctx; + + RTListInit(&pClient->listChannels); + RTListInit(&pClient->listEvents); + RTListInit(&pClient->listContexts); + + return VINF_SUCCESS; +} + +void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient) +{ + /* Clear the list of contexts and prevent acceess to the client. */ + int rc = vboxHostChannelLock(); + if (RT_SUCCESS(rc)) + { + VBOXHOSTCHCALLBACKCTX *pIter; + VBOXHOSTCHCALLBACKCTX *pNext; + RTListForEachSafe(&pClient->listContexts, pIter, pNext, VBOXHOSTCHCALLBACKCTX, nodeClient) + { + pIter->pClient = NULL; + RTListNodeRemove(&pIter->nodeClient); + } + + vboxHostChannelUnlock(); + } + + /* If there are attached channels, detach them. */ + VBOXHOSTCHINSTANCE *pIter; + VBOXHOSTCHINSTANCE *pIterNext; + RTListForEachSafe(&pClient->listChannels, pIter, pIterNext, VBOXHOSTCHINSTANCE, nodeClient) + { + vhcInstanceDetach(pIter); + } +} + +int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient, + uint32_t *pu32Handle, + const char *pszName, + uint32_t u32Flags) +{ + int rc = VINF_SUCCESS; + + HOSTCHLOG(("HostChannel: Attach: (%d) [%s] 0x%08X\n", pClient->u32ClientID, pszName, u32Flags)); + + /* Look if there is a provider. */ + VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName); + + if (pProvider) + { + VBOXHOSTCHINSTANCE *pInstance = NULL; + + rc = vhcInstanceCreate(pClient, &pInstance); + + if (RT_SUCCESS(rc)) + { + VBOXHOSTCHCALLBACKCTX *pCallbackCtx = NULL; + rc = vhcCallbackCtxCreate(pClient, &pCallbackCtx); + + if (RT_SUCCESS(rc)) + { + void *pvChannel = NULL; + rc = pProvider->iface.HostChannelAttach(pProvider->iface.pvProvider, + &pvChannel, + u32Flags, + &g_callbacks, pCallbackCtx); + + if (RT_SUCCESS(rc)) + { + vhcProviderAddRef(pProvider); + pInstance->pProvider = pProvider; + + pInstance->pClient = pClient; + pInstance->pvChannel = pvChannel; + + /* It is already in the channels list of the client. */ + + vhcInstanceAddRef(pInstance); /* Referenced by the list of provider's channels. */ + RTListAppend(&pProvider->listChannels, &pInstance->nodeProvider); + + *pu32Handle = pInstance->u32Handle; + + HOSTCHLOG(("HostChannel: Attach: (%d) handle %d\n", pClient->u32ClientID, pInstance->u32Handle)); + } + + if (RT_FAILURE(rc)) + { + vhcCallbackCtxDelete(pCallbackCtx); + } + } + + if (RT_FAILURE(rc)) + { + vhcInstanceDetach(pInstance); + } + + vhcInstanceRelease(pInstance); + } + + vhcProviderRelease(pProvider); + } + else + { + rc = VERR_NOT_SUPPORTED; + } + + return rc; +} + +int vboxHostChannelDetach(VBOXHOSTCHCLIENT *pClient, + uint32_t u32Handle) +{ + HOSTCHLOG(("HostChannel: Detach: (%d) handle %d\n", pClient->u32ClientID, u32Handle)); + + int rc = VINF_SUCCESS; + + VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle); + + if (pInstance) + { + vhcInstanceDetach(pInstance); + + vhcInstanceRelease(pInstance); + } + else + { + rc = VERR_NOT_SUPPORTED; + } + + return rc; +} + +int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient, + uint32_t u32Handle, + const void *pvData, + uint32_t cbData) +{ + HOSTCHLOG(("HostChannel: Send: (%d) handle %d, %d bytes\n", pClient->u32ClientID, u32Handle, cbData)); + + int rc = VINF_SUCCESS; + + VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle); + + if (pInstance) + { + if (pInstance->pProvider) + { + pInstance->pProvider->iface.HostChannelSend(pInstance->pvChannel, pvData, cbData); + } + + vhcInstanceRelease(pInstance); + } + else + { + rc = VERR_NOT_SUPPORTED; + } + + return rc; +} + +int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient, + uint32_t u32Handle, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeReceived, + uint32_t *pu32SizeRemaining) +{ + HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData)); + + int rc = VINF_SUCCESS; + + VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle); + + if (pInstance) + { + if (pInstance->pProvider) + { + rc = pInstance->pProvider->iface.HostChannelRecv(pInstance->pvChannel, pvData, cbData, + pu32SizeReceived, pu32SizeRemaining); + + HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, rc %Rrc, cbData %d, recv %d, rem %d\n", + pClient->u32ClientID, u32Handle, rc, cbData, *pu32SizeReceived, *pu32SizeRemaining)); + } + + vhcInstanceRelease(pInstance); + } + else + { + rc = VERR_NOT_SUPPORTED; + } + + return rc; +} + +int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient, + uint32_t u32Handle, + uint32_t u32Code, + void *pvParm, + uint32_t cbParm, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeDataReturned) +{ + HOSTCHLOG(("HostChannel: Control: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData)); + + int rc = VINF_SUCCESS; + + VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle); + + if (pInstance) + { + if (pInstance->pProvider) + { + pInstance->pProvider->iface.HostChannelControl(pInstance->pvChannel, u32Code, + pvParm, cbParm, + pvData, cbData, pu32SizeDataReturned); + } + + vhcInstanceRelease(pInstance); + } + else + { + rc = VERR_NOT_SUPPORTED; + } + + return rc; +} + +typedef struct VBOXHOSTCHANNELEVENT +{ + RTLISTNODE NodeEvent; + + uint32_t u32ChannelHandle; + + uint32_t u32Id; + void *pvEvent; + uint32_t cbEvent; +} VBOXHOSTCHANNELEVENT; + +int vboxHostChannelEventWait(VBOXHOSTCHCLIENT *pClient, + bool *pfEvent, + VBOXHGCMCALLHANDLE callHandle, + VBOXHGCMSVCPARM *paParms) +{ + int rc = vboxHostChannelLock(); + if (RT_FAILURE(rc)) + { + return rc; + } + + if (pClient->fAsync) + { + /* If there is a wait request already, cancel it. */ + vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0); + pClient->fAsync = false; + } + + /* Check if there is something in the client's event queue. */ + VBOXHOSTCHANNELEVENT *pEvent = RTListGetFirst(&pClient->listEvents, VBOXHOSTCHANNELEVENT, NodeEvent); + + HOSTCHLOG(("HostChannel: QueryEvent: (%d), event %p\n", pClient->u32ClientID, pEvent)); + + if (pEvent) + { + /* Report the event. */ + RTListNodeRemove(&pEvent->NodeEvent); + + HOSTCHLOG(("HostChannel: QueryEvent: (%d), cbEvent %d\n", + pClient->u32ClientID, pEvent->cbEvent)); + + vboxHostChannelEventParmsSet(paParms, pEvent->u32ChannelHandle, + pEvent->u32Id, pEvent->pvEvent, pEvent->cbEvent); + + *pfEvent = true; + + RTMemFree(pEvent); + } + else + { + /* No event available at the time. Process asynchronously. */ + pClient->fAsync = true; + pClient->async.callHandle = callHandle; + pClient->async.paParms = paParms; + + /* Tell the caller that there is no event. */ + *pfEvent = false; + } + + vboxHostChannelUnlock(); + return rc; +} + +int vboxHostChannelEventCancel(VBOXHOSTCHCLIENT *pClient) +{ + int rc = vboxHostChannelLock(); + + if (RT_SUCCESS(rc)) + { + if (pClient->fAsync) + { + /* If there is a wait request alredy, cancel it. */ + vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0); + + pClient->fAsync = false; + } + + vboxHostChannelUnlock(); + } + + return rc; +} + +/* @thread provider */ +static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvChannel, + uint32_t u32Id, const void *pvEvent, uint32_t cbEvent) +{ + VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)pvCallbacks; + + int rc = vboxHostChannelLock(); + if (RT_FAILURE(rc)) + { + return; + } + + /* Check that the structure is still associated with a client. + * The client can disconnect and will be invalid. + */ + VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient; + + if (pClient == NULL) + { + vboxHostChannelUnlock(); + + HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client gone.\n", pvEvent)); + + /* The client does not exist anymore, skip the event. */ + return; + } + + bool fFound = false; + + VBOXHOSTCHCALLBACKCTX *pIter; + RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient) + { + if (pIter == pCallbackCtx) + { + fFound = true; + break; + } + } + + if (!fFound) + { + AssertFailed(); + + vboxHostChannelUnlock(); + + HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client does not have the context.\n", pvEvent)); + + /* The context is not in the list of contexts. Skip the event. */ + return; + } + + VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFindByChannelPtr(pClient, pvChannel); + + HOSTCHLOG(("HostChannel: CallbackEvent[%p]: (%d) instance %p\n", + pCallbackCtx, pClient->u32ClientID, pInstance)); + + if (!pInstance) + { + /* Instance was already detached. Skip the event. */ + vboxHostChannelUnlock(); + + return; + } + + uint32_t u32ChannelHandle = pInstance->u32Handle; + + HOSTCHLOG(("HostChannel: CallbackEvent: (%d) handle %d, async %d, cbEvent %d\n", + pClient->u32ClientID, u32ChannelHandle, pClient->fAsync, cbEvent)); + + /* Check whether the event is waited. */ + if (pClient->fAsync) + { + /* Report the event. */ + vboxHostChannelReportAsync(pClient, u32ChannelHandle, u32Id, pvEvent, cbEvent); + + pClient->fAsync = false; + } + else + { + /* Put it to the queue. */ + VBOXHOSTCHANNELEVENT *pEvent = (VBOXHOSTCHANNELEVENT *)RTMemAlloc(sizeof(VBOXHOSTCHANNELEVENT) + cbEvent); + + if (pEvent) + { + pEvent->u32ChannelHandle = u32ChannelHandle; + pEvent->u32Id = u32Id; + + if (cbEvent) + { + pEvent->pvEvent = &pEvent[1]; + memcpy(pEvent->pvEvent, pvEvent, cbEvent); + } + else + { + pEvent->pvEvent = NULL; + } + + pEvent->cbEvent = cbEvent; + + RTListAppend(&pClient->listEvents, &pEvent->NodeEvent); + } + } + + vboxHostChannelUnlock(); + + vhcInstanceRelease(pInstance); +} + +/* @thread provider */ +static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel) +{ + RT_NOREF1(pvChannel); + vhcCallbackCtxDelete((VBOXHOSTCHCALLBACKCTX *)pvCallbacks); +} + +int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient, + const char *pszName, + uint32_t u32Code, + void *pvParm, + uint32_t cbParm, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeDataReturned) +{ + HOSTCHLOG(("HostChannel: Query: (%d) name [%s], cbData %d\n", pClient->u32ClientID, pszName, cbData)); + + int rc = VINF_SUCCESS; + + /* Look if there is a provider. */ + VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName); + + if (pProvider) + { + pProvider->iface.HostChannelControl(NULL, u32Code, + pvParm, cbParm, + pvData, cbData, pu32SizeDataReturned); + + vhcProviderRelease(pProvider); + } + else + { + rc = VERR_NOT_SUPPORTED; + } + + return rc; +} + +int vboxHostChannelRegister(const char *pszName, + const VBOXHOSTCHANNELINTERFACE *pInterface, + uint32_t cbInterface) +{ + RT_NOREF1(cbInterface); + int rc = VINF_SUCCESS; + + VBOXHOSTCHCTX *pCtx = &g_ctx; + + VBOXHOSTCHPROVIDER *pProvider = (VBOXHOSTCHPROVIDER *)RTMemAllocZ(sizeof(VBOXHOSTCHPROVIDER)); + + if (pProvider) + { + pProvider->pCtx = pCtx; + pProvider->iface = *pInterface; + + RTListInit(&pProvider->listChannels); + + pProvider->pszName = RTStrDup(pszName); + if (pProvider->pszName) + { + vhcProviderAddRef(pProvider); + rc = vhcProviderRegister(pCtx, pProvider); + } + else + { + RTMemFree(pProvider); + rc = VERR_NO_MEMORY; + } + } + else + { + rc = VERR_NO_MEMORY; + } + + return rc; +} + +int vboxHostChannelUnregister(const char *pszName) +{ + int rc = VINF_SUCCESS; + + VBOXHOSTCHCTX *pCtx = &g_ctx; + + VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pCtx, pszName); + + if (pProvider) + { + rc = vhcProviderUnregister(pProvider); + vhcProviderRelease(pProvider); + } + + return rc; +} diff --git a/src/VBox/HostServices/HostChannel/HostChannel.h b/src/VBox/HostServices/HostChannel/HostChannel.h new file mode 100644 index 00000000..cb59c391 --- /dev/null +++ b/src/VBox/HostServices/HostChannel/HostChannel.h @@ -0,0 +1,137 @@ +/* @file + * + * Host Channel + */ + +/* + * Copyright (C) 2012-2019 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. + */ + +#ifndef VBOX_INCLUDED_SRC_HostChannel_HostChannel_h +#define VBOX_INCLUDED_SRC_HostChannel_HostChannel_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/list.h> + +#define LOG_GROUP LOG_GROUP_HGCM +#include <VBox/log.h> +#include <VBox/HostServices/VBoxHostChannel.h> + +#define HOSTCHLOG Log + +#ifdef DEBUG_sunlover +# undef HOSTCHLOG +# define HOSTCHLOG LogRel +#endif /* DEBUG_sunlover */ + +struct VBOXHOSTCHCTX; +typedef struct VBOXHOSTCHCTX VBOXHOSTCHCTX; + +typedef struct VBOXHOSTCHCLIENT +{ + RTLISTNODE nodeClient; + + VBOXHOSTCHCTX *pCtx; + + uint32_t u32ClientID; + + RTLISTANCHOR listChannels; + uint32_t volatile u32HandleSrc; + + RTLISTANCHOR listContexts; /* Callback contexts. */ + + RTLISTANCHOR listEvents; + + bool fAsync; /* Guest is waiting for a message. */ + + struct { + VBOXHGCMCALLHANDLE callHandle; + VBOXHGCMSVCPARM *paParms; + } async; + +} VBOXHOSTCHCLIENT; + + +/* + * The service functions. Locking is between the service thread and the host channel provider thread. + */ +int vboxHostChannelLock(void); +void vboxHostChannelUnlock(void); + +int vboxHostChannelInit(void); +void vboxHostChannelDestroy(void); + +int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient); +void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient); + +int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient, + uint32_t *pu32Handle, + const char *pszName, + uint32_t u32Flags); +int vboxHostChannelDetach(VBOXHOSTCHCLIENT *pClient, + uint32_t u32Handle); + +int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient, + uint32_t u32Handle, + const void *pvData, + uint32_t cbData); +int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient, + uint32_t u32Handle, + void *pvData, + uint32_t cbData, + uint32_t *pu32DataReceived, + uint32_t *pu32DataRemaining); +int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient, + uint32_t u32Handle, + uint32_t u32Code, + void *pvParm, + uint32_t cbParm, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeDataReturned); + +int vboxHostChannelEventWait(VBOXHOSTCHCLIENT *pClient, + bool *pfEvent, + VBOXHGCMCALLHANDLE callHandle, + VBOXHGCMSVCPARM *paParms); + +int vboxHostChannelEventCancel(VBOXHOSTCHCLIENT *pClient); + +int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient, + const char *pszName, + uint32_t u32Code, + void *pvParm, + uint32_t cbParm, + void *pvData, + uint32_t cbData, + uint32_t *pu32SizeDataReturned); + +int vboxHostChannelRegister(const char *pszName, + const VBOXHOSTCHANNELINTERFACE *pInterface, + uint32_t cbInterface); +int vboxHostChannelUnregister(const char *pszName); + + +void vboxHostChannelEventParmsSet(VBOXHGCMSVCPARM *paParms, + uint32_t u32ChannelHandle, + uint32_t u32Id, + const void *pvEvent, + uint32_t cbEvent); + +void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient, + uint32_t u32ChannelHandle, + uint32_t u32Id, + const void *pvEvent, + uint32_t cbEvent); + +#endif /* !VBOX_INCLUDED_SRC_HostChannel_HostChannel_h */ diff --git a/src/VBox/HostServices/HostChannel/Makefile.kmk b/src/VBox/HostServices/HostChannel/Makefile.kmk new file mode 100644 index 00000000..80b22ad1 --- /dev/null +++ b/src/VBox/HostServices/HostChannel/Makefile.kmk @@ -0,0 +1,41 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Host Channel Service. +# + +# +# Copyright (C) 2012-2019 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. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# +# The service DLL. +# +DLLS += VBoxHostChannel +VBoxHostChannel_TEMPLATE = VBOXR3 +VBoxHostChannel_DEFS = VBOX_WITH_HGCM +VBoxHostChannel_INCS.win = \ + $(VBOX_PATH_SDK) + +VBoxHostChannel_SOURCES = \ + VBoxHostChannelSvc.cpp \ + HostChannel.cpp + +VBoxHostChannel_SOURCES.win = \ + VBoxHostChannelSvc.rc + +VBoxHostChannel_LIBS = \ + $(LIB_VMM) \ + $(LIB_RUNTIME) + +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp new file mode 100644 index 00000000..bb39ef79 --- /dev/null +++ b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp @@ -0,0 +1,901 @@ +/* $Id: VBoxHostChannelSvc.cpp $ */ +/* @file + * Host Channel: Host service entry points. + */ + +/* + * Copyright (C) 2012-2019 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. + */ + + +/* + * The HostChannel host service provides a generic proxy between a host's + * channel provider and a client running in the guest. + * + * Host providers must register via a HostCall. + * + * A guest client can connect to a host provider and send/receive data. + * + * GuestCalls: + * * Attach - attach to a host channel + * * Detach - completely detach from a channel + * * Send - send data from the guest to the channel + * * Recv - non blocking read of available data from the channel + * * Control - generic channel specific command exchange + * * EventWait - wait for a host event + * * EventCancel - make the blocking EventWait call to return + * HostCalls: + * * Register - register a host channel + * * Unregister - unregister it + * + * The guest HGCM client connects to the service. The client can attach multiple channels. + * + */ + +#include <iprt/alloc.h> +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <VBox/vmm/ssm.h> + +#include "HostChannel.h" + + +static void VBoxHGCMParmUInt32Set(VBOXHGCMSVCPARM *pParm, uint32_t u32) +{ + pParm->type = VBOX_HGCM_SVC_PARM_32BIT; + pParm->u.uint32 = u32; +} + +static int VBoxHGCMParmUInt32Get(VBOXHGCMSVCPARM *pParm, uint32_t *pu32) +{ + if (pParm->type == VBOX_HGCM_SVC_PARM_32BIT) + { + *pu32 = pParm->u.uint32; + return VINF_SUCCESS; + } + + AssertFailed(); + return VERR_INVALID_PARAMETER; +} + +#if 0 /* unused */ +static void VBoxHGCMParmPtrSet(VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb) +{ + pParm->type = VBOX_HGCM_SVC_PARM_PTR; + pParm->u.pointer.size = cb; + pParm->u.pointer.addr = pv; +} +#endif + +static int VBoxHGCMParmPtrGet(VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb) +{ + if (pParm->type == VBOX_HGCM_SVC_PARM_PTR) + { + *ppv = pParm->u.pointer.addr; + *pcb = pParm->u.pointer.size; + return VINF_SUCCESS; + } + + AssertFailed(); + return VERR_INVALID_PARAMETER; +} + + +static PVBOXHGCMSVCHELPERS g_pHelpers = NULL; + +static RTCRITSECT g_critsect; + +/* + * Helpers. + */ + +int vboxHostChannelLock(void) +{ + return RTCritSectEnter(&g_critsect); +} + +void vboxHostChannelUnlock(void) +{ + RTCritSectLeave(&g_critsect); +} + +void vboxHostChannelEventParmsSet(VBOXHGCMSVCPARM *paParms, + uint32_t u32ChannelHandle, + uint32_t u32Id, + const void *pvEvent, + uint32_t cbEvent) +{ + if (cbEvent > 0) + { + void *pvParm = NULL; + uint32_t cbParm = 0; + + VBoxHGCMParmPtrGet(&paParms[2], &pvParm, &cbParm); + + uint32_t cbToCopy = RT_MIN(cbParm, cbEvent); + if (cbToCopy > 0) + { + Assert(pvParm); + memcpy(pvParm, pvEvent, cbToCopy); + } + } + + VBoxHGCMParmUInt32Set(&paParms[0], u32ChannelHandle); + VBoxHGCMParmUInt32Set(&paParms[1], u32Id); + VBoxHGCMParmUInt32Set(&paParms[3], cbEvent); +} + +/* This is called under the lock. */ +void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient, + uint32_t u32ChannelHandle, + uint32_t u32Id, + const void *pvEvent, + uint32_t cbEvent) +{ + Assert(RTCritSectIsOwner(&g_critsect)); + + vboxHostChannelEventParmsSet(pClient->async.paParms, + u32ChannelHandle, + u32Id, + pvEvent, + cbEvent); + + LogRelFlow(("svcCall: CallComplete for pending\n")); + + g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS); +} + + +/* + * Service entry points. + */ + +static DECLCALLBACK(int) svcUnload(void *pvService) +{ + NOREF(pvService); + vboxHostChannelDestroy(); + RTCritSectDelete(&g_critsect); + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t u32ClientID, void *pvClient) +{ + RT_NOREF2(pvService, u32ClientID); + + VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient; + + vboxHostChannelClientDisconnect(pClient); + + memset(pClient, 0, sizeof(VBOXHOSTCHCLIENT)); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) svcConnect(void *pvService, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring) +{ + RT_NOREF(pvService, fRequestor, fRestoring); + VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient; + + /* Register the client. */ + memset(pClient, 0, sizeof(VBOXHOSTCHCLIENT)); + + pClient->u32ClientID = u32ClientID; + + int rc = vboxHostChannelClientConnect(pClient); + + LogRel2(("svcConnect: rc = %Rrc\n", rc)); + + return rc; +} + +static DECLCALLBACK(void) svcCall(void *pvService, + VBOXHGCMCALLHANDLE callHandle, + uint32_t u32ClientID, + void *pvClient, + uint32_t u32Function, + uint32_t cParms, + VBOXHGCMSVCPARM paParms[], + uint64_t tsArrival) +{ + RT_NOREF(pvService, tsArrival); + + int rc = VINF_SUCCESS; + + LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n", + u32ClientID, u32Function, cParms, paParms)); + + VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient; + + bool fAsynchronousProcessing = false; + +#ifdef DEBUG + uint32_t i; + + for (i = 0; i < cParms; i++) + { + /** @todo parameters other than 32 bit */ + LogRel2((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32)); + } +#endif + + switch (u32Function) + { + case VBOX_HOST_CHANNEL_FN_ATTACH: + { + LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_ATTACH\n")); + + if (cParms != 3) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + uint32_t u32Flags; + void *pvName; + uint32_t cbName; + + rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName); + + if (RT_SUCCESS(rc)) + { + rc = VBoxHGCMParmUInt32Get(&paParms[1], &u32Flags); + + if (RT_SUCCESS(rc)) + { + uint32_t u32Handle = 0; + + /** @todo make sure that pvName is a nul terminated */ + rc = vboxHostChannelAttach(pClient, &u32Handle, (const char *)pvName, u32Flags); + + if (RT_SUCCESS(rc)) + { + VBoxHGCMParmUInt32Set(&paParms[2], u32Handle); + } + } + } + } + } break; + + case VBOX_HOST_CHANNEL_FN_DETACH: + { + LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_DETACH\n")); + + if (cParms != 1) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + uint32_t u32Handle; + + rc = VBoxHGCMParmUInt32Get(&paParms[0], &u32Handle); + + if (RT_SUCCESS(rc)) + { + rc = vboxHostChannelDetach(pClient, u32Handle); + } + } + } break; + + case VBOX_HOST_CHANNEL_FN_SEND: + { + LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_SEND\n")); + + if (cParms != 2) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* data */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + uint32_t u32Handle; + void *pvData; + uint32_t cbData; + + rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle); + + if (RT_SUCCESS (rc)) + { + rc = VBoxHGCMParmPtrGet (&paParms[1], &pvData, &cbData); + + if (RT_SUCCESS (rc)) + { + rc = vboxHostChannelSend(pClient, u32Handle, pvData, cbData); + } + } + } + } break; + + case VBOX_HOST_CHANNEL_FN_RECV: + { + LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_RECV\n")); + + if (cParms != 4) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* data */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeReceived */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeRemaining */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + uint32_t u32Handle; + void *pvData; + uint32_t cbData; + + rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle); + + if (RT_SUCCESS (rc)) + { + rc = VBoxHGCMParmPtrGet (&paParms[1], &pvData, &cbData); + + if (RT_SUCCESS (rc)) + { + uint32_t u32SizeReceived = 0; + uint32_t u32SizeRemaining = 0; + + rc = vboxHostChannelRecv(pClient, u32Handle, + pvData, cbData, + &u32SizeReceived, &u32SizeRemaining); + + if (RT_SUCCESS(rc)) + { + VBoxHGCMParmUInt32Set(&paParms[2], u32SizeReceived); + VBoxHGCMParmUInt32Set(&paParms[3], u32SizeRemaining); + } + } + } + } + } break; + + case VBOX_HOST_CHANNEL_FN_CONTROL: + { + LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_CONTROL\n")); + + if (cParms != 5) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* code */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parm */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* data */ + || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeDataReturned */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + uint32_t u32Handle; + uint32_t u32Code; + void *pvParm; + uint32_t cbParm; + void *pvData; + uint32_t cbData; + + rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle); + + if (RT_SUCCESS (rc)) + { + rc = VBoxHGCMParmUInt32Get (&paParms[1], &u32Code); + + if (RT_SUCCESS (rc)) + { + rc = VBoxHGCMParmPtrGet (&paParms[2], &pvParm, &cbParm); + + if (RT_SUCCESS (rc)) + { + rc = VBoxHGCMParmPtrGet (&paParms[3], &pvData, &cbData); + + if (RT_SUCCESS (rc)) + { + uint32_t u32SizeDataReturned = 0; + + rc = vboxHostChannelControl(pClient, u32Handle, u32Code, + pvParm, cbParm, + pvData, cbData, &u32SizeDataReturned); + if (RT_SUCCESS(rc)) + { + VBoxHGCMParmUInt32Set(&paParms[4], u32SizeDataReturned); + } + } + } + } + } + } + } break; + + case VBOX_HOST_CHANNEL_FN_EVENT_WAIT: + { + LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_EVENT_WAIT\n")); + + if (cParms != 4) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* id */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parm */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeReturned */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + bool fEvent = false; + + rc = vboxHostChannelEventWait(pClient, &fEvent, callHandle, paParms); + + if (RT_SUCCESS(rc)) + { + if (!fEvent) + { + /* No event available at the time. Process asynchronously. */ + fAsynchronousProcessing = true; + + LogRel2(("svcCall: async.\n")); + } + } + } + } break; + + case VBOX_HOST_CHANNEL_FN_EVENT_CANCEL: + { + LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_EVENT_CANCEL\n")); + + if (cParms != 0) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + rc = vboxHostChannelEventCancel(pClient); + } + } break; + + case VBOX_HOST_CHANNEL_FN_QUERY: + { + LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_QUERY\n")); + + if (cParms != 5) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* channel name */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* code */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parm */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* data */ + || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeDataReturned */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + void *pvName; + uint32_t cbName; + uint32_t u32Code; + void *pvParm; + uint32_t cbParm; + void *pvData; + uint32_t cbData; + + rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName); + + if (RT_SUCCESS (rc)) + { + rc = VBoxHGCMParmUInt32Get (&paParms[1], &u32Code); + + if (RT_SUCCESS (rc)) + { + rc = VBoxHGCMParmPtrGet (&paParms[2], &pvParm, &cbParm); + + if (RT_SUCCESS (rc)) + { + rc = VBoxHGCMParmPtrGet (&paParms[3], &pvData, &cbData); + + if (RT_SUCCESS (rc)) + { + uint32_t u32SizeDataReturned = 0; + + /** @todo make sure that pvName is a nul terminated */ + rc = vboxHostChannelQuery(pClient, (const char *)pvName, u32Code, + pvParm, cbParm, + pvData, cbData, &u32SizeDataReturned); + if (RT_SUCCESS(rc)) + { + VBoxHGCMParmUInt32Set(&paParms[4], u32SizeDataReturned); + } + } + } + } + } + } + } break; + + default: + { + rc = VERR_NOT_IMPLEMENTED; + } + } + + LogRelFlow(("svcCall: rc = %Rrc, async %d\n", rc, fAsynchronousProcessing)); + + if (!fAsynchronousProcessing) + { + g_pHelpers->pfnCallComplete(callHandle, rc); + } +} + +static DECLCALLBACK(int) svcHostCall(void *pvService, + uint32_t u32Function, + uint32_t cParms, + VBOXHGCMSVCPARM paParms[]) +{ + NOREF(pvService); + + int rc = VINF_SUCCESS; + + LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n", + u32Function, cParms, paParms)); + + switch (u32Function) + { + case VBOX_HOST_CHANNEL_HOST_FN_REGISTER: + { + LogRel2(("svcCall: VBOX_HOST_CHANNEL_HOST_FN_REGISTER\n")); + + if (cParms != 2) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* iface */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + void *pvName; + uint32_t cbName; + void *pvInterface; + uint32_t cbInterface; + + rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName); + + if (RT_SUCCESS(rc)) + { + rc = VBoxHGCMParmPtrGet(&paParms[1], &pvInterface, &cbInterface); + + if (RT_SUCCESS(rc)) + { + rc = vboxHostChannelRegister((const char *)pvName, + (VBOXHOSTCHANNELINTERFACE *)pvInterface, cbInterface); + } + } + } + } break; + + case VBOX_HOST_CHANNEL_HOST_FN_UNREGISTER: + { + LogRel2(("svcCall: VBOX_HOST_CHANNEL_HOST_FN_UNREGISTER\n")); + + if (cParms != 1) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + void *pvName; + uint32_t cbName; + + rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName); + + if (RT_SUCCESS(rc)) + { + rc = vboxHostChannelUnregister((const char *)pvName); + } + } + } break; + + default: + break; + } + + LogRelFlow(("svcHostCall: rc = %Rrc\n", rc)); + return rc; +} + +#if 0 +/** If the client in the guest is waiting for a read operation to complete + * then complete it, otherwise return. See the protocol description in the + * shared clipboard module description. */ +void vboxSvcClipboardCompleteReadData(VBOXHOSTCHCLIENT *pClient, int rc, uint32_t cbActual) +{ + VBOXHGCMCALLHANDLE callHandle = NULL; + VBOXHGCMSVCPARM *paParms = NULL; + bool fReadPending = false; + if (vboxSvcClipboardLock()) /* if not can we do anything useful? */ + { + callHandle = pClient->asyncRead.callHandle; + paParms = pClient->asyncRead.paParms; + fReadPending = pClient->fReadPending; + pClient->fReadPending = false; + vboxSvcClipboardUnlock(); + } + if (fReadPending) + { + VBoxHGCMParmUInt32Set (&paParms[2], cbActual); + g_pHelpers->pfnCallComplete (callHandle, rc); + } +} + +/** + * SSM descriptor table for the VBOXHOSTCHCLIENT structure. + */ +static SSMFIELD const g_aClipboardClientDataFields[] = +{ + SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, u32ClientID), /* for validation purposes */ + SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgQuit), + SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgReadData), + SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgFormats), + SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, u32RequestedFormat), + SSMFIELD_ENTRY_TERM() +}; + +static DECLCALLBACK(int) svcSaveState(void *pvService, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM) +{ + NOREF(pvService); + + /* If there are any pending requests, they must be completed here. Since + * the service is single threaded, there could be only requests + * which the service itself has postponed. + * + * HGCM knows that the state is being saved and that the pfnComplete + * calls are just clean ups. These requests are saved by the VMMDev. + * + * When the state will be restored, these requests will be reissued + * by VMMDev. The service therefore must save state as if there were no + * pending request. + */ + LogRel2 (("svcSaveState: u32ClientID = %d\n", u32ClientID)); + + VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient; + + /* This field used to be the length. We're using it as a version field + with the high bit set. */ + SSMR3PutU32 (pSSM, UINT32_C (0x80000002)); + int rc = SSMR3PutStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL); + AssertRCReturn (rc, rc); + + if (pClient->fAsync) + { + g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS /* error code is not important here. */); + pClient->fAsync = false; + } + + vboxSvcClipboardCompleteReadData (pClient, VINF_SUCCESS, 0); + + return VINF_SUCCESS; +} + +/** + * This structure corresponds to the original layout of the + * VBOXHOSTCHCLIENT structure. As the structure was saved as a whole + * when saving state, we need to remember it forever in order to preserve + * compatibility. + * + * (Starting with 3.1 this is no longer used.) + * + * @remarks Putting this outside svcLoadState to avoid visibility warning caused + * by -Wattributes. + */ +typedef struct CLIPSAVEDSTATEDATA +{ + struct CLIPSAVEDSTATEDATA *pNext; + struct CLIPSAVEDSTATEDATA *pPrev; + + VBOXCLIPBOARDCONTEXT *pCtx; + + uint32_t u32ClientID; + + bool fAsync: 1; /* Guest is waiting for a message. */ + + bool fMsgQuit: 1; + bool fMsgReadData: 1; + bool fMsgFormats: 1; + + struct { + VBOXHGCMCALLHANDLE callHandle; + VBOXHGCMSVCPARM *paParms; + } async; + + struct { + void *pv; + uint32_t cb; + uint32_t u32Format; + } data; + + uint32_t u32AvailableFormats; + uint32_t u32RequestedFormat; + +} CLIPSAVEDSTATEDATA; + +static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM) +{ + LogRel2 (("svcLoadState: u32ClientID = %d\n", u32ClientID)); + + VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient; + + /* Existing client can not be in async state yet. */ + Assert (!pClient->fAsync); + + /* Save the client ID for data validation. */ + /** @todo isn't this the same as u32ClientID? Playing safe for now... */ + uint32_t const u32ClientIDOld = pClient->u32ClientID; + + /* Restore the client data. */ + uint32_t lenOrVer; + int rc = SSMR3GetU32 (pSSM, &lenOrVer); + AssertRCReturn (rc, rc); + if (lenOrVer == UINT32_C (0x80000002)) + { + rc = SSMR3GetStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL); + AssertRCReturn (rc, rc); + } + else if (lenOrVer == (SSMR3HandleHostBits (pSSM) == 64 ? 72 : 48)) + { + /** + * SSM descriptor table for the CLIPSAVEDSTATEDATA structure. + */ + static SSMFIELD const s_aClipSavedStateDataFields30[] = + { + SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pNext), + SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pPrev), + SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pCtx), + SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32ClientID), + SSMFIELD_ENTRY_CUSTOM(fMsgQuit+fMsgReadData+fMsgFormats, RT_OFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4), + SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.callHandle), + SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.paParms), + SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.pv), + SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.cb), + SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.u32Format), + SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, u32AvailableFormats), + SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32RequestedFormat), + SSMFIELD_ENTRY_TERM() + }; + + CLIPSAVEDSTATEDATA savedState; + RT_ZERO (savedState); + rc = SSMR3GetStructEx (pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID, + &s_aClipSavedStateDataFields30[0], NULL); + AssertRCReturn (rc, rc); + + pClient->fMsgQuit = savedState.fMsgQuit; + pClient->fMsgReadData = savedState.fMsgReadData; + pClient->fMsgFormats = savedState.fMsgFormats; + pClient->u32RequestedFormat = savedState.u32RequestedFormat; + } + else + { + LogRel (("Client data size mismatch: got %#x\n", lenOrVer)); + return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; + } + + /* Verify the client ID. */ + if (pClient->u32ClientID != u32ClientIDOld) + { + LogRel (("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID)); + pClient->u32ClientID = u32ClientIDOld; + return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; + } + + /* Actual host data are to be reported to guest (SYNC). */ + vboxClipboardSync (pClient); + + return VINF_SUCCESS; +} +#endif + +static int svcInit(void) +{ + int rc = RTCritSectInit(&g_critsect); + + if (RT_SUCCESS (rc)) + { + rc = vboxHostChannelInit(); + + /* Clean up on failure, because 'svnUnload' will not be called + * if the 'svcInit' returns an error. + */ + if (RT_FAILURE(rc)) + { + RTCritSectDelete(&g_critsect); + } + } + + return rc; +} + +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable) +{ + int rc = VINF_SUCCESS; + + LogRelFlowFunc(("pTable = %p\n", pTable)); + + if (!pTable) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + LogRel2(("VBoxHGCMSvcLoad: pTable->cbSize = %d, pTable->u32Version = 0x%08X\n", + pTable->cbSize, pTable->u32Version)); + + if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE) + || pTable->u32Version != VBOX_HGCM_SVC_VERSION) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + g_pHelpers = pTable->pHelpers; + + pTable->cbClient = sizeof(VBOXHOSTCHCLIENT); + + pTable->pfnUnload = svcUnload; + pTable->pfnConnect = svcConnect; + pTable->pfnDisconnect = svcDisconnect; + pTable->pfnCall = svcCall; + pTable->pfnHostCall = svcHostCall; + pTable->pfnSaveState = NULL; // svcSaveState; + pTable->pfnLoadState = NULL; // svcLoadState; + pTable->pfnRegisterExtension = NULL; + pTable->pvService = NULL; + + /* Service specific initialization. */ + rc = svcInit(); + } + } + + return rc; +} diff --git a/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc new file mode 100644 index 00000000..a6716c12 --- /dev/null +++ b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxHostChannelSvc.rc $ */ +/** @file + * VBoxHostChannel - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox Host Channel Service\0" + VALUE "InternalName", "VBoxHostChannel\0" + VALUE "OriginalFilename", "VBoxHostChannel.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/Makefile.kmk b/src/VBox/HostServices/Makefile.kmk new file mode 100644 index 00000000..3f1ec1b7 --- /dev/null +++ b/src/VBox/HostServices/Makefile.kmk @@ -0,0 +1,51 @@ +# $Id: Makefile.kmk $ +## @file +# Top-level makefile for the VBox Host Services. +# + +# +# Copyright (C) 2006-2019 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. +# + +SUB_DEPTH = ../../.. +include $(KBUILD_PATH)/subheader.kmk + +# Include sub-makefiles. +include $(PATH_SUB_CURRENT)/auth/Makefile.kmk +ifdef VBOX_WITH_SHARED_FOLDERS + include $(PATH_SUB_CURRENT)/SharedFolders/Makefile.kmk +endif +if1of ($(KBUILD_TARGET), win linux solaris darwin freebsd) + ifdef VBOX_WITH_CROGL + include $(PATH_SUB_CURRENT)/SharedOpenGL/Makefile.kmk + endif +endif +if1of ($(KBUILD_TARGET), win linux solaris darwin freebsd) + ifdef VBOX_WITH_SHARED_CLIPBOARD + include $(PATH_SUB_CURRENT)/SharedClipboard/Makefile.kmk + endif +endif +ifdef VBOX_WITH_GUEST_PROPS + include $(PATH_SUB_CURRENT)/GuestProperties/Makefile.kmk +endif +ifdef VBOX_WITH_GUEST_CONTROL + include $(PATH_SUB_CURRENT)/GuestControl/Makefile.kmk +endif +ifdef VBOX_WITH_DRAG_AND_DROP + include $(PATH_SUB_CURRENT)/DragAndDrop/Makefile.kmk +endif +ifdef VBOX_WITH_HOST_CHANNEL + include $(PATH_SUB_CURRENT)/HostChannel/Makefile.kmk +endif +include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostServices/SharedClipboard/Makefile.kmk b/src/VBox/HostServices/SharedClipboard/Makefile.kmk new file mode 100644 index 00000000..5ae7b1a6 --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/Makefile.kmk @@ -0,0 +1,91 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Shared Clipboard Host Service. +# + +# +# Copyright (C) 2006-2019 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. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# Include sub-makefile(s). +include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk + +# +# The shared folder service DLL. +# +DLLS += VBoxSharedClipboard +VBoxSharedClipboard_TEMPLATE = VBOXR3 +VBoxSharedClipboard_DEFS = VBOX_WITH_HGCM +VBoxSharedClipboard_INCS.win = \ + $(VBOX_PATH_SDK) + +VBoxSharedClipboard_SOURCES = \ + VBoxSharedClipboardSvc.cpp +VBoxSharedClipboard_SOURCES.win = \ + VBoxClipboard-win.cpp \ + VBoxSharedClipboardSvc.rc +VBoxSharedClipboard_SOURCES.darwin = \ + darwin.cpp \ + $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp \ + darwin-pasteboard.cpp +if1of ($(KBUILD_TARGET), linux solaris freebsd) ## @todo X11 + ifndef VBOX_HEADLESS + VBoxSharedClipboard_SOURCES += \ + $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-helper.cpp \ + $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/x11-clipboard.cpp \ + x11-clipboard.cpp + else + VBoxSharedClipboard_SOURCES += \ + x11-stub.cpp + endif +endif + +VBoxSharedClipboard_LIBS = \ + $(LIB_VMM) \ + $(LIB_RUNTIME) \ + $(LIB_REM) +if1of ($(KBUILD_TARGET), linux solaris freebsd) + ifndef VBOX_HEADLESS + VBoxSharedClipboard_LIBPATH = \ + $(VBOX_LIBPATH_X11) + VBoxSharedClipboard_LIBS += \ + Xt \ + X11 + endif +endif + +VBoxSharedClipboard_LDFLAGS.darwin = \ + -framework ApplicationServices -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxSharedClipboard.dylib + +if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK) + if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd solaris) + # + # Set this in LocalConfig.kmk if you are working on the X11 clipboard service + # to automatically run the unit test at build time. + # OTHERS += $(tstClipboardX11-2_0_OUTDIR)/tstClipboardX11-2.run + PROGRAMS += tstClipboardX11-2 + TESTING += $(tstClipboardX11-2_0_OUTDIR)/tstClipboardX11-2.run + tstClipboardX11-2_TEMPLATE = VBOXR3TSTEXE + tstClipboardX11-2_DEFS = VBOX_WITH_HGCM TESTCASE + tstClipboardX11-2_SOURCES = x11-clipboard.cpp + tstClipboardX11-2_LIBS = $(LIB_RUNTIME) + tstClipboardX11-2_CLEANS = $(tstClipboardX11-2_0_OUTDIR)/tstClipboardX11-2.run + +$$(tstClipboardX11-2_0_OUTDIR)/tstClipboardX11-2.run: $$(tstClipboardX11-2_1_STAGE_TARGET) + export VBOX_LOG_DEST=nofile; $(tstClipboardX11-2_1_STAGE_TARGET) quiet + $(QUIET)$(APPEND) -t "$@" "done" + endif # 1of ($(KBUILD_TARGET),freebsd linux netbsd openbsd solaris) +endif + +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp b/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp new file mode 100644 index 00000000..0f069a92 --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp @@ -0,0 +1,1248 @@ +/* $Id: VBoxClipboard-win.cpp $ */ +/** @file + * Shared Clipboard Service - Win32 host. + */ + +/* + * Copyright (C) 2006-2019 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_SHARED_CLIPBOARD +#include <iprt/win/windows.h> + +#include <VBox/HostServices/VBoxClipboardSvc.h> + +#include <iprt/alloc.h> +#include <iprt/string.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/thread.h> +#include <iprt/ldr.h> +#include <process.h> + +#include "VBoxClipboard.h" + +#define dprintf Log + +static char gachWindowClassName[] = "VBoxSharedClipboardClass"; + +enum { CBCHAIN_TIMEOUT = 5000 /* ms */ }; + +/* Dynamically load clipboard functions from User32.dll. */ +typedef BOOL WINAPI FNADDCLIPBOARDFORMATLISTENER(HWND); +typedef FNADDCLIPBOARDFORMATLISTENER *PFNADDCLIPBOARDFORMATLISTENER; + +typedef BOOL WINAPI FNREMOVECLIPBOARDFORMATLISTENER(HWND); +typedef FNREMOVECLIPBOARDFORMATLISTENER *PFNREMOVECLIPBOARDFORMATLISTENER; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int ConvertCFHtmlToMime(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pch); +static int ConvertMimeToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput); +static bool IsWindowsHTML(const char *source); + + +#ifndef WM_CLIPBOARDUPDATE +#define WM_CLIPBOARDUPDATE 0x031D +#endif + +struct _VBOXCLIPBOARDCONTEXT +{ + HWND hwnd; + HWND hwndNextInChain; + + UINT timerRefresh; + + bool fCBChainPingInProcess; + + RTTHREAD thread; + + HANDLE hRenderEvent; + + VBOXCLIPBOARDCLIENTDATA *pClient; + + PFNADDCLIPBOARDFORMATLISTENER pfnAddClipboardFormatListener; + PFNREMOVECLIPBOARDFORMATLISTENER pfnRemoveClipboardFormatListener; + +}; + +/* Only one client is supported. There seems to be no need for more clients. */ +static VBOXCLIPBOARDCONTEXT g_ctx; + + +#ifdef LOG_ENABLED +void vboxClipboardDump(const void *pv, size_t cb, uint32_t u32Format) +{ + if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) + { + Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT:\n")); + if (pv && cb) + Log(("%ls\n", pv)); + else + Log(("%p %d\n", pv, cb)); + } + else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP) + dprintf(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n")); + else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML) + { + Log(("DUMP: VBOX_SHARED_CLIPBOARD_FMT_HTML:\n")); + if (pv && cb) + { + Log(("%s\n", pv)); + + //size_t cb = RTStrNLen(pv, ); + char *pszBuf = (char *)RTMemAllocZ(cb + 1); + RTStrCopy(pszBuf, cb + 1, (const char *)pv); + for (size_t off = 0; off < cb; ++off) + { + if (pszBuf[off] == '\n' || pszBuf[off] == '\r') + pszBuf[off] = ' '; + } + + Log(("%s\n", pszBuf)); + RTMemFree(pszBuf); + } + else + Log(("%p %d\n", pv, cb)); + } + else + dprintf(("DUMP: invalid format %02X\n", u32Format)); +} +#else /* !LOG_ENABLED */ +# define vboxClipboardDump(__pv, __cb, __format) do { NOREF(__pv); NOREF(__cb); NOREF(__format); } while (0) +#endif /* !LOG_ENABLED */ + + +static void vboxClipboardInitNewAPI(VBOXCLIPBOARDCONTEXT *pCtx) +{ + RTLDRMOD hUser32 = NIL_RTLDRMOD; + int rc = RTLdrLoadSystem("User32.dll", /* fNoUnload = */ true, &hUser32); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(hUser32, "AddClipboardFormatListener", (void**)&pCtx->pfnAddClipboardFormatListener); + if (RT_SUCCESS(rc)) + { + rc = RTLdrGetSymbol(hUser32, "RemoveClipboardFormatListener", (void**)&pCtx->pfnRemoveClipboardFormatListener); + } + + RTLdrClose(hUser32); + } + + if (RT_SUCCESS(rc)) + { + Log(("New Clipboard API is enabled\n")); + } + else + { + pCtx->pfnAddClipboardFormatListener = NULL; + pCtx->pfnRemoveClipboardFormatListener = NULL; + Log(("New Clipboard API is not available. rc = %Rrc\n", rc)); + } +} + +static bool vboxClipboardIsNewAPI(VBOXCLIPBOARDCONTEXT *pCtx) +{ + return pCtx->pfnAddClipboardFormatListener != NULL; +} + + +static int vboxOpenClipboard(HWND hwnd) +{ + /* "OpenClipboard fails if another window has the clipboard open." + * So try a few times and wait up to 1 second. + */ + BOOL fOpened = FALSE; + + int i = 0; + for (;;) + { + if (OpenClipboard(hwnd)) + { + fOpened = TRUE; + break; + } + + if (i >= 10) /* sleep interval = [1..512] ms */ + break; + + RTThreadSleep(1 << i); + ++i; + } + +#ifdef LOG_ENABLED + if (i > 0) + LogFlowFunc(("%d times tried to open clipboard.\n", i + 1)); +#endif + + int rc; + if (fOpened) + rc = VINF_SUCCESS; + else + { + const DWORD err = GetLastError(); + LogFlowFunc(("error %d\n", err)); + rc = RTErrConvertFromWin32(err); + } + + return rc; +} + + +/** @todo Someone please explain the protocol wrt overflows... */ +static void vboxClipboardGetData (uint32_t u32Format, const void *pvSrc, uint32_t cbSrc, + void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst) +{ + dprintf (("vboxClipboardGetData.\n")); + + LogFlow(("vboxClipboardGetData cbSrc = %d, cbDst = %d\n", cbSrc, cbDst)); + + if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML + && IsWindowsHTML((const char *)pvSrc)) + { + /** @todo r=bird: Why the double conversion? */ + char *pszBuf = NULL; + uint32_t cbBuf = 0; + int rc = ConvertCFHtmlToMime((const char*)pvSrc, cbSrc, &pszBuf, &cbBuf); + if (RT_SUCCESS(rc)) + { + *pcbActualDst = cbBuf; + if (cbBuf > cbDst) + { + /* Do not copy data. The dst buffer is not enough. */ + RTMemFree(pszBuf); + return; + } + memcpy(pvDst, pszBuf, cbBuf); + RTMemFree(pszBuf); + } + else + *pcbActualDst = 0; + } + else + { + *pcbActualDst = cbSrc; + + if (cbSrc > cbDst) + { + /* Do not copy data. The dst buffer is not enough. */ + return; + } + + memcpy(pvDst, pvSrc, cbSrc); + } + + vboxClipboardDump(pvDst, cbSrc, u32Format); + + return; +} + +static int vboxClipboardReadDataFromClient (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format) +{ + Assert(pCtx->pClient); + Assert(pCtx->hRenderEvent); + Assert(pCtx->pClient->data.pv == NULL && pCtx->pClient->data.cb == 0 && pCtx->pClient->data.u32Format == 0); + + LogFlow(("vboxClipboardReadDataFromClient u32Format = %02X\n", u32Format)); + + ResetEvent (pCtx->hRenderEvent); + + vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format); + + DWORD ret = WaitForSingleObject(pCtx->hRenderEvent, INFINITE); + LogFlow(("vboxClipboardReadDataFromClient wait completed, ret 0x%08X, err %d\n", + ret, GetLastError())); NOREF(ret); + + return VINF_SUCCESS; +} + +static void vboxClipboardChanged (VBOXCLIPBOARDCONTEXT *pCtx) +{ + LogFlow(("vboxClipboardChanged\n")); + + if (pCtx->pClient == NULL) + { + return; + } + + /* Query list of available formats and report to host. */ + int rc = vboxOpenClipboard(pCtx->hwnd); + if (RT_SUCCESS(rc)) + { + uint32_t u32Formats = 0; + + UINT format = 0; + + while ((format = EnumClipboardFormats (format)) != 0) + { + LogFlow(("vboxClipboardChanged format %#x\n", format)); + switch (format) + { + case CF_UNICODETEXT: + case CF_TEXT: + u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT; + break; + + case CF_DIB: + case CF_BITMAP: + u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP; + break; + + default: + if (format >= 0xC000) + { + TCHAR szFormatName[256]; + + int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR)); + + if (cActual) + { + if (strcmp (szFormatName, "HTML Format") == 0) + { + u32Formats |= VBOX_SHARED_CLIPBOARD_FMT_HTML; + } + } + } + break; + } + } + + CloseClipboard (); + + LogFlow(("vboxClipboardChanged u32Formats %02X\n", u32Formats)); + + vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, u32Formats); + } + else + { + LogFlow(("vboxClipboardChanged: error in open clipboard. hwnd: %x. err: %Rrc\n", pCtx->hwnd, rc)); + } +} + +/* Add ourselves into the chain of cliboard listeners */ +static void addToCBChain (VBOXCLIPBOARDCONTEXT *pCtx) +{ + if (vboxClipboardIsNewAPI(pCtx)) + pCtx->pfnAddClipboardFormatListener(pCtx->hwnd); + else + pCtx->hwndNextInChain = SetClipboardViewer(pCtx->hwnd); +} + +/* Remove ourselves from the chain of cliboard listeners */ +static void removeFromCBChain (VBOXCLIPBOARDCONTEXT *pCtx) +{ + if (vboxClipboardIsNewAPI(pCtx)) + { + pCtx->pfnRemoveClipboardFormatListener(pCtx->hwnd); + } + else + { + ChangeClipboardChain(pCtx->hwnd, pCtx->hwndNextInChain); + pCtx->hwndNextInChain = NULL; + } +} + +/* Callback which is invoked when we have successfully pinged ourselves down the + * clipboard chain. We simply unset a boolean flag to say that we are responding. + * There is a race if a ping returns after the next one is initiated, but nothing + * very bad is likely to happen. */ +VOID CALLBACK CBChainPingProc(HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult) +{ + (void) hwnd; + (void) uMsg; + (void) lResult; + VBOXCLIPBOARDCONTEXT *pCtx = (VBOXCLIPBOARDCONTEXT *)dwData; + pCtx->fCBChainPingInProcess = FALSE; +} + +static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LRESULT rc = 0; + + VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx; + + switch (msg) + { + case WM_CLIPBOARDUPDATE: + { + Log(("WM_CLIPBOARDUPDATE\n")); + + if (GetClipboardOwner() != hwnd) + { + /* Clipboard was updated by another application. */ + vboxClipboardChanged(pCtx); + } + } break; + + case WM_CHANGECBCHAIN: + { + Log(("WM_CHANGECBCHAIN\n")); + + if (vboxClipboardIsNewAPI(pCtx)) + { + rc = DefWindowProc(hwnd, msg, wParam, lParam); + break; + } + + HWND hwndRemoved = (HWND)wParam; + HWND hwndNext = (HWND)lParam; + + if (hwndRemoved == pCtx->hwndNextInChain) + { + /* The window that was next to our in the chain is being removed. + * Relink to the new next window. + */ + pCtx->hwndNextInChain = hwndNext; + } + else + { + if (pCtx->hwndNextInChain) + { + /* Pass the message further. */ + DWORD_PTR dwResult; + rc = SendMessageTimeout(pCtx->hwndNextInChain, WM_CHANGECBCHAIN, wParam, lParam, 0, CBCHAIN_TIMEOUT, &dwResult); + if (!rc) + rc = (LRESULT)dwResult; + } + } + } break; + + case WM_DRAWCLIPBOARD: + { + Log(("WM_DRAWCLIPBOARD\n")); + + if (GetClipboardOwner () != hwnd) + { + /* Clipboard was updated by another application. */ + vboxClipboardChanged (pCtx); + } + + if (pCtx->hwndNextInChain) + { + Log(("WM_DRAWCLIPBOARD next %p\n", pCtx->hwndNextInChain)); + /* Pass the message to next windows in the clipboard chain. */ + DWORD_PTR dwResult; + rc = SendMessageTimeout(pCtx->hwndNextInChain, msg, wParam, lParam, 0, CBCHAIN_TIMEOUT, &dwResult); + if (!rc) + rc = dwResult; + } + } break; + + case WM_TIMER: + { + if (vboxClipboardIsNewAPI(pCtx)) + break; + + HWND hViewer = GetClipboardViewer(); + + /* Re-register ourselves in the clipboard chain if our last ping + * timed out or there seems to be no valid chain. */ + if (!hViewer || pCtx->fCBChainPingInProcess) + { + removeFromCBChain(pCtx); + addToCBChain(pCtx); + } + /* Start a new ping by passing a dummy WM_CHANGECBCHAIN to be + * processed by ourselves to the chain. */ + pCtx->fCBChainPingInProcess = TRUE; + hViewer = GetClipboardViewer(); + if (hViewer) + SendMessageCallback(hViewer, WM_CHANGECBCHAIN, (WPARAM)pCtx->hwndNextInChain, (LPARAM)pCtx->hwndNextInChain, CBChainPingProc, (ULONG_PTR) pCtx); + } break; + + case WM_RENDERFORMAT: + { + /* Insert the requested clipboard format data into the clipboard. */ + uint32_t u32Format = 0; + + UINT format = (UINT)wParam; + + Log(("WM_RENDERFORMAT %d\n", format)); + + switch (format) + { + case CF_UNICODETEXT: + u32Format |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT; + break; + + case CF_DIB: + u32Format |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP; + break; + + default: + if (format >= 0xC000) + { + TCHAR szFormatName[256]; + + int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR)); + + if (cActual) + { + if (strcmp (szFormatName, "HTML Format") == 0) + { + u32Format |= VBOX_SHARED_CLIPBOARD_FMT_HTML; + } + } + } + break; + } + + if (u32Format == 0 || pCtx->pClient == NULL) + { + /* Unsupported clipboard format is requested. */ + Log(("WM_RENDERFORMAT unsupported format requested or client is not active.\n")); + EmptyClipboard (); + } + else + { + int vboxrc = vboxClipboardReadDataFromClient (pCtx, u32Format); + + dprintf(("vboxClipboardReadDataFromClient vboxrc = %d, pv %p, cb %d, u32Format %d\n", + vboxrc, pCtx->pClient->data.pv, pCtx->pClient->data.cb, pCtx->pClient->data.u32Format)); + + if ( RT_SUCCESS (vboxrc) + && pCtx->pClient->data.pv != NULL + && pCtx->pClient->data.cb > 0 + && pCtx->pClient->data.u32Format == u32Format) + { + HANDLE hMem = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, pCtx->pClient->data.cb); + + dprintf(("hMem %p\n", hMem)); + + if (hMem) + { + void *pMem = GlobalLock (hMem); + + dprintf(("pMem %p, GlobalSize %d\n", pMem, GlobalSize (hMem))); + + if (pMem) + { + Log(("WM_RENDERFORMAT setting data\n")); + + if (pCtx->pClient->data.pv) + { + memcpy (pMem, pCtx->pClient->data.pv, pCtx->pClient->data.cb); + + RTMemFree (pCtx->pClient->data.pv); + pCtx->pClient->data.pv = NULL; + } + + pCtx->pClient->data.cb = 0; + pCtx->pClient->data.u32Format = 0; + + /* The memory must be unlocked before inserting to the Clipboard. */ + GlobalUnlock (hMem); + + /* 'hMem' contains the host clipboard data. + * size is 'cb' and format is 'format'. + */ + HANDLE hClip = SetClipboardData (format, hMem); + + dprintf(("vboxClipboardHostEvent hClip %p\n", hClip)); + + if (hClip) + { + /* The hMem ownership has gone to the system. Nothing to do. */ + break; + } + } + + GlobalFree (hMem); + } + } + + RTMemFree (pCtx->pClient->data.pv); + pCtx->pClient->data.pv = NULL; + pCtx->pClient->data.cb = 0; + pCtx->pClient->data.u32Format = 0; + + /* Something went wrong. */ + EmptyClipboard (); + } + } break; + + case WM_RENDERALLFORMATS: + { + Log(("WM_RENDERALLFORMATS\n")); + + /* Do nothing. The clipboard formats will be unavailable now, because the + * windows is to be destroyed and therefore the guest side becomes inactive. + */ + int vboxrc = vboxOpenClipboard(hwnd); + if (RT_SUCCESS(vboxrc)) + { + EmptyClipboard(); + + CloseClipboard(); + } + else + { + LogFlow(("vboxClipboardWndProc: WM_RENDERALLFORMATS: error in open clipboard. hwnd: %x, rc: %Rrc\n", hwnd, vboxrc)); + } + } break; + + case WM_USER: + { + if (pCtx->pClient == NULL || pCtx->pClient->fMsgFormats) + { + /* Host has pending formats message. Ignore the guest announcement, + * because host clipboard has more priority. + */ + Log(("WM_USER ignored\n")); + break; + } + + /* Announce available formats. Do not insert data, they will be inserted in WM_RENDER*. */ + uint32_t u32Formats = (uint32_t)lParam; + + Log(("WM_USER u32Formats = %02X\n", u32Formats)); + + int vboxrc = vboxOpenClipboard(hwnd); + if (RT_SUCCESS(vboxrc)) + { + EmptyClipboard(); + + Log(("WM_USER emptied clipboard\n")); + + HANDLE hClip = NULL; + + if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) + { + dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n")); + + hClip = SetClipboardData (CF_UNICODETEXT, NULL); + } + + if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP) + { + dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n")); + + hClip = SetClipboardData (CF_DIB, NULL); + } + + if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_HTML) + { + UINT format = RegisterClipboardFormat ("HTML Format"); + dprintf(("window proc WM_USER: VBOX_SHARED_CLIPBOARD_FMT_HTML 0x%04X\n", format)); + if (format != 0) + { + hClip = SetClipboardData (format, NULL); + } + } + + CloseClipboard(); + + dprintf(("window proc WM_USER: hClip %p, err %d\n", hClip, GetLastError ())); + } + else + { + dprintf(("window proc WM_USER: failed to open clipboard. rc: %Rrc\n", vboxrc)); + } + } break; + + case WM_DESTROY: + { + /* MS recommends to remove from Clipboard chain in this callback */ + Assert(pCtx->hwnd); + removeFromCBChain(pCtx); + if (pCtx->timerRefresh) + KillTimer(pCtx->hwnd, 0); + PostQuitMessage(0); + } break; + + default: + { + Log(("WM_ %p\n", msg)); + rc = DefWindowProc(hwnd, msg, wParam, lParam); + } + } + + Log(("WM_ rc %d\n", rc)); + return rc; +} + +DECLCALLBACK(int) VBoxClipboardThread (RTTHREAD hThreadSelf, void *pvUser) +{ + RT_NOREF2(hThreadSelf, pvUser); + /* Create a window and make it a clipboard viewer. */ + int rc = VINF_SUCCESS; + + LogFlow(("VBoxClipboardThread\n")); + + VBOXCLIPBOARDCONTEXT *pCtx = &g_ctx; + + HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); + + /* Register the Window Class. */ + WNDCLASS wc; + RT_ZERO(wc); + + wc.style = CS_NOCLOSE; + wc.lpfnWndProc = vboxClipboardWndProc; + wc.hInstance = hInstance; + wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1); + wc.lpszClassName = gachWindowClassName; + + ATOM atomWindowClass = RegisterClass(&wc); + + if (atomWindowClass == 0) + { + Log(("Failed to register window class\n")); + rc = VERR_NOT_SUPPORTED; + } + else + { + /* Create the window. */ + pCtx->hwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST, + gachWindowClassName, gachWindowClassName, + WS_POPUPWINDOW, + -200, -200, 100, 100, NULL, NULL, hInstance, NULL); + + if (pCtx->hwnd == NULL) + { + Log(("Failed to create window\n")); + rc = VERR_NOT_SUPPORTED; + } + else + { + SetWindowPos(pCtx->hwnd, HWND_TOPMOST, -200, -200, 0, 0, + SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE); + + addToCBChain(pCtx); + if (!vboxClipboardIsNewAPI(pCtx)) + pCtx->timerRefresh = SetTimer(pCtx->hwnd, 0, 10 * 1000, NULL); + + MSG msg; + BOOL msgret = 0; + while ((msgret = GetMessage(&msg, NULL, 0, 0)) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + /* + * Window procedure can return error, + * but this is exceptional situation + * that should be identified in testing + */ + Assert(msgret >= 0); + Log(("VBoxClipboardThread Message loop finished. GetMessage returned %d, message id: %d \n", msgret, msg.message)); + } + } + + pCtx->hwnd = NULL; + + if (atomWindowClass != 0) + { + UnregisterClass (gachWindowClassName, hInstance); + atomWindowClass = 0; + } + + return 0; +} + +/* + * Public platform dependent functions. + */ +int vboxClipboardInit (void) +{ + int rc = VINF_SUCCESS; + + RT_ZERO(g_ctx); + + /* Check that new Clipboard API is available */ + vboxClipboardInitNewAPI(&g_ctx); + + g_ctx.hRenderEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + rc = RTThreadCreate (&g_ctx.thread, VBoxClipboardThread, NULL, 65536, + RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP"); + + if (RT_FAILURE (rc)) + { + CloseHandle (g_ctx.hRenderEvent); + } + + return rc; +} + +void vboxClipboardDestroy (void) +{ + Log(("vboxClipboardDestroy\n")); + + if (g_ctx.hwnd) + { + PostMessage (g_ctx.hwnd, WM_CLOSE, 0, 0); + } + + CloseHandle (g_ctx.hRenderEvent); + + /* Wait for the window thread to terminate. */ + RTThreadWait (g_ctx.thread, RT_INDEFINITE_WAIT, NULL); + + g_ctx.thread = NIL_RTTHREAD; +} + +int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless) +{ + NOREF(fHeadless); + Log(("vboxClipboardConnect\n")); + + if (g_ctx.pClient != NULL) + { + /* One client only. */ + return VERR_NOT_SUPPORTED; + } + + pClient->pCtx = &g_ctx; + + pClient->pCtx->pClient = pClient; + + /* Sync the host clipboard content with the client. */ + vboxClipboardSync (pClient); + + return VINF_SUCCESS; +} + +int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient) +{ + /* Sync the host clipboard content with the client. */ + vboxClipboardChanged (pClient->pCtx); + + return VINF_SUCCESS; +} + +void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient) +{ + RT_NOREF1(pClient); + Log(("vboxClipboardDisconnect\n")); + + g_ctx.pClient = NULL; +} + +void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats) +{ + /* + * The guest announces formats. Forward to the window thread. + */ + PostMessage (pClient->pCtx->hwnd, WM_USER, 0, u32Formats); +} + +int DumpHtml(const char *pszSrc, size_t cb) +{ + size_t cchIgnored = 0; + int rc = RTStrNLenEx(pszSrc, cb, &cchIgnored); + if (RT_SUCCESS(rc)) + { + char *pszBuf = (char *)RTMemAllocZ(cb + 1); + if (pszBuf != NULL) + { + rc = RTStrCopy(pszBuf, cb + 1, (const char *)pszSrc); + if (RT_SUCCESS(rc)) + { + for (size_t i = 0; i < cb; ++i) + if (pszBuf[i] == '\n' || pszBuf[i] == '\r') + pszBuf[i] = ' '; + } + else + Log(("Error in copying string.\n")); + Log(("Removed \\r\\n: %s\n", pszBuf)); + RTMemFree(pszBuf); + } + else + { + rc = VERR_NO_MEMORY; + Log(("Not enough memory to allocate buffer.\n")); + } + } + return rc; +} + +int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual) +{ + LogFlow(("vboxClipboardReadData: u32Format = %02X\n", u32Format)); + + HANDLE hClip = NULL; + + /* + * The guest wants to read data in the given format. + */ + int rc = vboxOpenClipboard(pClient->pCtx->hwnd); + if (RT_SUCCESS(rc)) + { + dprintf(("Clipboard opened.\n")); + + if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP) + { + hClip = GetClipboardData (CF_DIB); + + if (hClip != NULL) + { + LPVOID lp = GlobalLock (hClip); + + if (lp != NULL) + { + dprintf(("CF_DIB\n")); + + vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_BITMAP, lp, GlobalSize (hClip), + pv, cb, pcbActual); + + GlobalUnlock(hClip); + } + else + { + hClip = NULL; + } + } + } + else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) + { + hClip = GetClipboardData(CF_UNICODETEXT); + + if (hClip != NULL) + { + LPWSTR uniString = (LPWSTR)GlobalLock (hClip); + + if (uniString != NULL) + { + dprintf(("CF_UNICODETEXT\n")); + + vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, uniString, (lstrlenW (uniString) + 1) * 2, + pv, cb, pcbActual); + + GlobalUnlock(hClip); + } + else + { + hClip = NULL; + } + } + } + else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML) + { + UINT format = RegisterClipboardFormat ("HTML Format"); + + if (format != 0) + { + hClip = GetClipboardData (format); + + if (hClip != NULL) + { + LPVOID lp = GlobalLock (hClip); + + if (lp != NULL) + { + dprintf(("CF_HTML\n")); + + vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize (hClip), + pv, cb, pcbActual); + LogRelFlowFunc(("Raw HTML clipboard data from host :")); + DumpHtml((char*)pv, cb); + GlobalUnlock(hClip); + } + else + { + hClip = NULL; + } + } + } + } + + CloseClipboard (); + } + else + { + dprintf(("vboxClipboardReadData: failed to open clipboard, rc: %Rrc\n", rc)); + } + + if (hClip == NULL) + { + /* Reply with empty data. */ + vboxClipboardGetData (0, NULL, 0, + pv, cb, pcbActual); + } + + return VINF_SUCCESS; +} + +void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format) +{ + LogFlow(("vboxClipboardWriteData\n")); + + /* + * The guest returns data that was requested in the WM_RENDERFORMAT handler. + */ + Assert(pClient->data.pv == NULL && pClient->data.cb == 0 && pClient->data.u32Format == 0); + + vboxClipboardDump(pv, cb, u32Format); + + if (cb > 0) + { + char *pszResult = NULL; + + if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML + && !IsWindowsHTML((const char*)pv)) + { + /* check that this is not already CF_HTML */ + uint32_t cbResult; + int rc = ConvertMimeToCFHTML((const char *)pv, cb, &pszResult, &cbResult); + if (RT_SUCCESS(rc)) + { + if (pszResult != NULL && cbResult != 0) + { + pClient->data.pv = pszResult; + pClient->data.cb = cbResult; + pClient->data.u32Format = u32Format; + } + } + } + else + { + pClient->data.pv = RTMemDup(pv, cb); + if (pClient->data.pv) + { + pClient->data.cb = cb; + pClient->data.u32Format = u32Format; + } + } + } + + SetEvent(pClient->pCtx->hRenderEvent); +} + + +/** + * Extracts field value from CF_HTML struct + * + * @returns VBox status code + * @param pszSrc source in CF_HTML format + * @param pszOption Name of CF_HTML field + * @param puValue Where to return extracted value of CF_HTML field + */ +static int GetHeaderValue(const char *pszSrc, const char *pszOption, uint32_t *puValue) +{ + int rc = VERR_INVALID_PARAMETER; + + Assert(pszSrc); + Assert(pszOption); + + const char *pszOptionValue = RTStrStr(pszSrc, pszOption); + if (pszOptionValue) + { + size_t cchOption = strlen(pszOption); + Assert(cchOption); + + rc = RTStrToUInt32Ex(pszOptionValue + cchOption, NULL, 10, puValue); + } + return rc; +} + + +/** + * Check that the source string contains CF_HTML struct + * + * @param pszSource source string. + * + * @returns @c true if the @a pszSource string is in CF_HTML format + */ +static bool IsWindowsHTML(const char *pszSource) +{ + return RTStrStr(pszSource, "Version:") != NULL + && RTStrStr(pszSource, "StartHTML:") != NULL; +} + + +/* + * Converts clipboard data from CF_HTML format to mimie clipboard format + * + * Returns allocated buffer that contains html converted to text/html mime type + * + * @returns VBox status code. + * @param pszSource The input. + * @param cch The length of the input. + * @param ppszOutput Where to return the result. Free using RTMemFree. + * @param pcbOutput Where to the return length of the result (bytes/chars). + */ +static int ConvertCFHtmlToMime(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pcbOutput) +{ + Assert(pszSource); + Assert(cch); + Assert(ppszOutput); + Assert(pcbOutput); + + uint32_t offStart; + int rc = GetHeaderValue(pszSource, "StartFragment:", &offStart); + if (RT_SUCCESS(rc)) + { + uint32_t offEnd; + rc = GetHeaderValue(pszSource, "EndFragment:", &offEnd); + if (RT_SUCCESS(rc)) + { + if ( offStart > 0 + && offEnd > 0 + && offEnd > offStart + && offEnd <= cch) + { + uint32_t cchSubStr = offEnd - offStart; + char *pszResult = (char *)RTMemAlloc(cchSubStr + 1); + if (pszResult) + { + rc = RTStrCopyEx(pszResult, cchSubStr + 1, pszSource + offStart, cchSubStr); + if (RT_SUCCESS(rc)) + { + *ppszOutput = pszResult; + *pcbOutput = (uint32_t)(cchSubStr + 1); + rc = VINF_SUCCESS; + } + else + { + LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc)); + RTMemFree(pszResult); + } + } + else + { + LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment.\n")); + rc = VERR_NO_MEMORY; + } + } + else + { + LogRelFlowFunc(("Error: CF_HTML out of bounds - offStart=%#x offEnd=%#x cch=%#x\n", offStart, offEnd, cch)); + rc = VERR_INVALID_PARAMETER; + } + } + else + { + LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc.\n", rc)); + rc = VERR_INVALID_PARAMETER; + } + } + else + { + LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc.\n", rc)); + rc = VERR_INVALID_PARAMETER; + } + + return rc; +} + + + +/** + * Converts source UTF-8 MIME HTML clipboard data to UTF-8 CF_HTML format. + * + * This is just encapsulation work, slapping a header on the data. + * + * It allocates + * + * Calculations: + * Header length = format Length + (2*(10 - 5('%010d'))('digits')) - 2('%s') = format length + 8 + * EndHtml = Header length + fragment length + * StartHtml = 105(constant) + * StartFragment = 141(constant) may vary if the header html content will be extended + * EndFragment = Header length + fragment length - 38(ending length) + * + * @param pszSource Source buffer that contains utf-16 string in mime html format + * @param cb Size of source buffer in bytes + * @param ppszOutput Where to return the allocated output buffer to put converted UTF-8 + * CF_HTML clipboard data. This function allocates memory for this. + * @param pcbOutput Where to return the size of allocated result buffer in bytes/chars, including zero terminator + * + * @note output buffer should be free using RTMemFree() + * @note Everything inside of fragment can be UTF8. Windows allows it. Everything in header should be Latin1. + */ +static int ConvertMimeToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput) +{ + Assert(ppszOutput); + Assert(pcbOutput); + Assert(pszSource); + Assert(cb); + + /* construct CF_HTML formatted string */ + char *pszResult = NULL; + size_t cchFragment; + int rc = RTStrNLenEx(pszSource, cb, &cchFragment); + if (!RT_SUCCESS(rc)) + { + LogRelFlowFunc(("Error: invalid source fragment. rc = %Rrc.\n")); + return VERR_INVALID_PARAMETER; + } + + /* + @StartHtml - pos before <html> + @EndHtml - whole size of text excluding ending zero char + @StartFragment - pos after <!--StartFragment--> + @EndFragment - pos before <!--EndFragment--> + @note: all values includes CR\LF inserted into text + Calculations: + Header length = format Length + (3*6('digits')) - 2('%s') = format length + 16 (control value - 183) + EndHtml = Header length + fragment length + StartHtml = 105(constant) + StartFragment = 143(constant) + EndFragment = Header length + fragment length - 40(ending length) + */ + static const char s_szFormatSample[] = + /* 0: */ "Version:1.0\r\n" + /* 13: */ "StartHTML:000000101\r\n" + /* 34: */ "EndHTML:%0000009u\r\n" // END HTML = Header length + fragment length + /* 53: */ "StartFragment:000000137\r\n" + /* 78: */ "EndFragment:%0000009u\r\n" + /* 101: */ "<html>\r\n" + /* 109: */ "<body>\r\n" + /* 117: */ "<!--StartFragment-->" + /* 137: */ "%s" + /* 137+2: */ "<!--EndFragment-->\r\n" + /* 157+2: */ "</body>\r\n" + /* 166+2: */ "</html>\r\n"; + /* 175+2: */ + AssertCompile(sizeof(s_szFormatSample) == 175+2+1); + + /* calculate parameters of CF_HTML header */ + size_t cchHeader = sizeof(s_szFormatSample) - 1; + size_t offEndHtml = cchHeader + cchFragment; + size_t offEndFragment = cchHeader + cchFragment - 38; /* 175-137 = 38 */ + pszResult = (char *)RTMemAlloc(offEndHtml + 1); + if (pszResult == NULL) + { + LogRelFlowFunc(("Error: Cannot allocate memory for result buffer. rc = %Rrc.\n")); + return VERR_NO_MEMORY; + } + + /* format result CF_HTML string */ + size_t cchFormatted = RTStrPrintf(pszResult, offEndHtml + 1, + s_szFormatSample, offEndHtml, offEndFragment, pszSource); + Assert(offEndHtml == cchFormatted); NOREF(cchFormatted); + +#ifdef VBOX_STRICT + /* Control calculations. check consistency.*/ + static const char s_szStartFragment[] = "<!--StartFragment-->"; + static const char s_szEndFragment[] = "<!--EndFragment-->"; + + /* check 'StartFragment:' value */ + const char *pszRealStartFragment = RTStrStr(pszResult, s_szStartFragment); + Assert(&pszRealStartFragment[sizeof(s_szStartFragment) - 1] - pszResult == 137); + + /* check 'EndFragment:' value */ + const char *pszRealEndFragment = RTStrStr(pszResult, s_szEndFragment); + Assert((size_t)(pszRealEndFragment - pszResult) == offEndFragment); +#endif + + *ppszOutput = pszResult; + *pcbOutput = (uint32_t)cchFormatted + 1; + Assert(*pcbOutput == cchFormatted + 1); + + return VINF_SUCCESS; +} diff --git a/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h b/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h new file mode 100644 index 00000000..2eb9c7a8 --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/VBoxClipboard.h @@ -0,0 +1,103 @@ +/* $Id: VBoxClipboard.h $ */ +/** @file + * Shared Clipboard Service - Internal Header. + */ + +/* + * Copyright (C) 2006-2019 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. + */ + +#ifndef VBOX_INCLUDED_SRC_SharedClipboard_VBoxClipboard_h +#define VBOX_INCLUDED_SRC_SharedClipboard_VBoxClipboard_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/hgcmsvc.h> +#include <VBox/log.h> + +struct _VBOXCLIPBOARDCONTEXT; +typedef struct _VBOXCLIPBOARDCONTEXT VBOXCLIPBOARDCONTEXT; + + +typedef struct _VBOXCLIPBOARDCLIENTDATA +{ + struct _VBOXCLIPBOARDCLIENTDATA *pNext; + struct _VBOXCLIPBOARDCLIENTDATA *pPrev; + + VBOXCLIPBOARDCONTEXT *pCtx; + + uint32_t u32ClientID; + + bool fAsync; /* Guest is waiting for a message. */ + bool fReadPending; /* The guest is waiting for data from the host */ + + bool fMsgQuit; + bool fMsgReadData; + bool fMsgFormats; + + struct { + VBOXHGCMCALLHANDLE callHandle; + VBOXHGCMSVCPARM *paParms; + } async; + + struct { + VBOXHGCMCALLHANDLE callHandle; + VBOXHGCMSVCPARM *paParms; + } asyncRead; + + struct { + void *pv; + uint32_t cb; + uint32_t u32Format; + } data; + + uint32_t u32AvailableFormats; + uint32_t u32RequestedFormat; + +} VBOXCLIPBOARDCLIENTDATA; + +/* + * The service functions. Locking is between the service thread and the platform dependent windows thread. + */ +bool vboxSvcClipboardLock (void); +void vboxSvcClipboardUnlock (void); + +void vboxSvcClipboardReportMsg (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats); + +void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual); + +bool vboxSvcClipboardGetHeadless(void); + +/* + * Platform dependent functions. + */ +int vboxClipboardInit (void); +void vboxClipboardDestroy (void); + +int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless); +void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient); + +void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats); + +int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual); + +void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format); + +int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient); + +/* Host unit testing interface */ +#ifdef UNIT_TEST +uint32_t TestClipSvcGetMode(void); +#endif + +#endif /* !VBOX_INCLUDED_SRC_SharedClipboard_VBoxClipboard_h */ + diff --git a/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp new file mode 100644 index 00000000..1b9b9cbc --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp @@ -0,0 +1,1047 @@ +/* $Id: VBoxSharedClipboardSvc.cpp $ */ +/** @file + * Shared Clipboard Service - Host service entry points. + */ + +/* + * Copyright (C) 2006-2019 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. + */ + + +/** @page pg_hostclip The Shared Clipboard Host Service + * + * The shared clipboard host service provides a proxy between the host's + * clipboard and a similar proxy running on a guest. The service is split + * into a platform-independent core and platform-specific backends. The + * service defines two communication protocols - one to communicate with the + * clipboard service running on the guest, and one to communicate with the + * backend. These will be described in a very skeletal fashion here. + * + * @section sec_hostclip_guest_proto The guest communication protocol + * + * The guest clipboard service communicates with the host service via HGCM + * (the host service runs as an HGCM service). The guest clipboard must + * connect to the host service before all else (Windows hosts currently only + * support one simultaneous connection). Once it has connected, it can send + * HGCM messages to the host services, some of which will receive replies from + * the host. The host can only reply to a guest message, it cannot initiate + * any communication. The guest can in theory send any number of messages in + * parallel (see the descriptions of the messages for the practice), and the + * host will receive these in sequence, and may reply to them at once + * (releasing the caller in the guest) or defer the reply until later. + * + * There are currently four messages defined. The first is + * VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, which waits for a message from the + * host. Host messages currently defined are + * VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT (unused), + * VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA (request that the guest send the + * contents of its clipboard to the host) and + * VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS (to notify the guest that new + * clipboard data is available). If a host message is sent while the guest is + * not waiting, it will be queued until the guest requests it. At most one + * host message of each type will be kept in the queue. The host code only + * supports a single simultaneous VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG call + * from the guest. + * + * The second guest message is VBOX_SHARED_CLIPBOARD_FN_FORMATS, which tells + * the host that the guest has new clipboard data available. The third is + * VBOX_SHARED_CLIPBOARD_FN_READ_DATA, which asks the host to send its + * clipboard data and waits until it arrives. The host supports at most one + * simultaneous VBOX_SHARED_CLIPBOARD_FN_READ_DATA call from the guest - if a + * second call is made before the first has returned, the first will be + * aborted. + * + * The last guest message is VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA, which is + * used to send the contents of the guest clipboard to the host. This call + * should be used after the host has requested data from the guest. + * + * @section sec_hostclip_backend_proto The communication protocol with the + * platform-specific backend + * + * This section may be written in the future :) + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD +#include <VBox/HostServices/VBoxClipboardSvc.h> +#include <VBox/HostServices/VBoxClipboardExt.h> + +#include <iprt/alloc.h> +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <VBox/err.h> +#include <VBox/vmm/ssm.h> + +#include "VBoxClipboard.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static PVBOXHGCMSVCHELPERS g_pHelpers; + +static RTCRITSECT critsect; +static uint32_t g_u32Mode; + +static PFNHGCMSVCEXT g_pfnExtension; +static void *g_pvExtension; + +static VBOXCLIPBOARDCLIENTDATA *g_pClient; + +/* Serialization of data reading and format announcements from the RDP client. */ +static bool g_fReadingData = false; +static bool g_fDelayedAnnouncement = false; +static uint32_t g_u32DelayedFormats = 0; + +/** Is the clipboard running in headless mode? */ +static bool g_fHeadless = false; + + +static void VBoxHGCMParmUInt32Set (VBOXHGCMSVCPARM *pParm, uint32_t u32) +{ + pParm->type = VBOX_HGCM_SVC_PARM_32BIT; + pParm->u.uint32 = u32; +} + +static int VBoxHGCMParmUInt32Get (VBOXHGCMSVCPARM *pParm, uint32_t *pu32) +{ + if (pParm->type == VBOX_HGCM_SVC_PARM_32BIT) + { + *pu32 = pParm->u.uint32; + return VINF_SUCCESS; + } + + return VERR_INVALID_PARAMETER; +} + +#if 0 +static void VBoxHGCMParmPtrSet (VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb) +{ + pParm->type = VBOX_HGCM_SVC_PARM_PTR; + pParm->u.pointer.size = cb; + pParm->u.pointer.addr = pv; +} +#endif + +static int VBoxHGCMParmPtrGet (VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb) +{ + if (pParm->type == VBOX_HGCM_SVC_PARM_PTR) + { + *ppv = pParm->u.pointer.addr; + *pcb = pParm->u.pointer.size; + return VINF_SUCCESS; + } + + return VERR_INVALID_PARAMETER; +} + + +static uint32_t vboxSvcClipboardMode (void) +{ + return g_u32Mode; +} + +#ifdef UNIT_TEST +/** Testing interface, getter for clipboard mode */ +uint32_t TestClipSvcGetMode(void) +{ + return vboxSvcClipboardMode(); +} +#endif + +/** Getter for headless setting */ +bool vboxSvcClipboardGetHeadless(void) +{ + return g_fHeadless; +} + +static void vboxSvcClipboardModeSet (uint32_t u32Mode) +{ + switch (u32Mode) + { + case VBOX_SHARED_CLIPBOARD_MODE_OFF: + case VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST: + case VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST: + case VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL: + g_u32Mode = u32Mode; + break; + + default: + g_u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF; + } +} + +bool vboxSvcClipboardLock (void) +{ + return RT_SUCCESS(RTCritSectEnter (&critsect)); +} + +void vboxSvcClipboardUnlock (void) +{ + RTCritSectLeave (&critsect); +} + +/* Set the HGCM parameters according to pending messages. + * Executed under the clipboard lock. + */ +static bool vboxSvcClipboardReturnMsg (VBOXCLIPBOARDCLIENTDATA *pClient, VBOXHGCMSVCPARM paParms[]) +{ + /* Message priority is taken into account. */ + if (pClient->fMsgQuit) + { + LogRelFlow(("vboxSvcClipboardReturnMsg: Quit\n")); + VBoxHGCMParmUInt32Set (&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT); + VBoxHGCMParmUInt32Set (&paParms[1], 0); + pClient->fMsgQuit = false; + } + else if (pClient->fMsgReadData) + { + uint32_t fFormat = 0; + + LogRelFlow(("vboxSvcClipboardReturnMsg: ReadData %02X\n", pClient->u32RequestedFormat)); + if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) + fFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT; + else if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP) + fFormat = VBOX_SHARED_CLIPBOARD_FMT_BITMAP; + else if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_HTML) + fFormat = VBOX_SHARED_CLIPBOARD_FMT_HTML; + else + AssertStmt(pClient->u32RequestedFormat == 0, pClient->u32RequestedFormat = 0); + pClient->u32RequestedFormat &= ~fFormat; + VBoxHGCMParmUInt32Set (&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA); + VBoxHGCMParmUInt32Set (&paParms[1], fFormat); + if (pClient->u32RequestedFormat == 0) + pClient->fMsgReadData = false; + } + else if (pClient->fMsgFormats) + { + LogRelFlow(("vboxSvcClipboardReturnMsg: Formats %02X\n", pClient->u32AvailableFormats)); + VBoxHGCMParmUInt32Set (&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS); + VBoxHGCMParmUInt32Set (&paParms[1], pClient->u32AvailableFormats); + pClient->fMsgFormats = false; + } + else + { + /* No pending messages. */ + LogRelFlow(("vboxSvcClipboardReturnMsg: no message\n")); + return false; + } + + /* Message information assigned. */ + return true; +} + +void vboxSvcClipboardReportMsg (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats) +{ + if (vboxSvcClipboardLock ()) + { + switch (u32Msg) + { + case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT: + { + LogRelFlow(("vboxSvcClipboardReportMsg: Quit\n")); + pClient->fMsgQuit = true; + } break; + case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA: + { + if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST + && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL) + { + /* Skip the message. */ + break; + } + + LogRelFlow(("vboxSvcClipboardReportMsg: ReadData %02X\n", u32Formats)); + pClient->u32RequestedFormat = u32Formats; + pClient->fMsgReadData = true; + } break; + case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS: + { + if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST + && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL) + { + /* Skip the message. */ + break; + } + + LogRelFlow(("vboxSvcClipboardReportMsg: Formats %02X\n", u32Formats)); + pClient->u32AvailableFormats = u32Formats; + pClient->fMsgFormats = true; + } break; + default: + { + /* Invalid message. */ + LogRelFlow(("vboxSvcClipboardReportMsg: invalid message %d\n", u32Msg)); + } break; + } + + if (pClient->fAsync) + { + /* The client waits for a response. */ + bool fMessageReturned = vboxSvcClipboardReturnMsg (pClient, pClient->async.paParms); + + /* Make a copy of the handle. */ + VBOXHGCMCALLHANDLE callHandle = pClient->async.callHandle; + + if (fMessageReturned) + { + /* There is a response. */ + pClient->fAsync = false; + } + + vboxSvcClipboardUnlock (); + + if (fMessageReturned) + { + LogRelFlow(("vboxSvcClipboardReportMsg: CallComplete\n")); + g_pHelpers->pfnCallComplete (callHandle, VINF_SUCCESS); + } + } + else + { + vboxSvcClipboardUnlock (); + } + } +} + +static int svcInit (void) +{ + int rc = RTCritSectInit (&critsect); + + if (RT_SUCCESS (rc)) + { + vboxSvcClipboardModeSet (VBOX_SHARED_CLIPBOARD_MODE_OFF); + + rc = vboxClipboardInit (); + + /* Clean up on failure, because 'svnUnload' will not be called + * if the 'svcInit' returns an error. + */ + if (RT_FAILURE (rc)) + { + RTCritSectDelete (&critsect); + } + } + + return rc; +} + +static DECLCALLBACK(int) svcUnload (void *) +{ + vboxClipboardDestroy (); + RTCritSectDelete (&critsect); + return VINF_SUCCESS; +} + +/** + * Disconnect the host side of the shared clipboard and send a "host disconnected" message + * to the guest side. + */ +static DECLCALLBACK(int) svcDisconnect (void *, uint32_t u32ClientID, void *pvClient) +{ + VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient; + + LogRel2(("svcDisconnect: u32ClientID = %d\n", u32ClientID)); + + vboxSvcClipboardReportMsg (pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT, 0); + + vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0); + + vboxClipboardDisconnect (pClient); + + memset (pClient, 0, sizeof (*pClient)); + + g_pClient = NULL; + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) svcConnect (void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring) +{ + RT_NOREF(fRequestor, fRestoring); + VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient; + + int rc = VINF_SUCCESS; + + /* If there is already a client connected then we want to release it first. */ + if (g_pClient != NULL) + { + uint32_t u32OldClientID = g_pClient->u32ClientID; + + svcDisconnect(NULL, u32OldClientID, g_pClient); + /* And free the resources in the hgcm subsystem. */ + g_pHelpers->pfnDisconnectClient(g_pHelpers->pvInstance, u32OldClientID); + } + + /* Register the client. */ + memset (pClient, 0, sizeof (*pClient)); + + pClient->u32ClientID = u32ClientID; + + rc = vboxClipboardConnect (pClient, vboxSvcClipboardGetHeadless()); + + if (RT_SUCCESS (rc)) + { + g_pClient = pClient; + } + + LogRel2(("vboxClipboardConnect: rc = %Rrc\n", rc)); + + return rc; +} + +static DECLCALLBACK(void) svcCall (void *, + VBOXHGCMCALLHANDLE callHandle, + uint32_t u32ClientID, + void *pvClient, + uint32_t u32Function, + uint32_t cParms, + VBOXHGCMSVCPARM paParms[], + uint64_t tsArrival) +{ + RT_NOREF_PV(tsArrival); + int rc = VINF_SUCCESS; + + LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n", + u32ClientID, u32Function, cParms, paParms)); + + VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient; + + bool fAsynchronousProcessing = false; + +#ifdef DEBUG + uint32_t i; + + for (i = 0; i < cParms; i++) + { + /** @todo parameters other than 32 bit */ + LogRel2((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32)); + } +#endif + + switch (u32Function) + { + case VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG: + { + /* The quest requests a host message. */ + LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG\n")); + + if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_GET_HOST_MSG) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Atomically verify the client's state. */ + if (vboxSvcClipboardLock ()) + { + bool fMessageReturned = vboxSvcClipboardReturnMsg (pClient, paParms); + + if (fMessageReturned) + { + /* Just return to the caller. */ + pClient->fAsync = false; + } + else + { + /* No event available at the time. Process asynchronously. */ + fAsynchronousProcessing = true; + + pClient->fAsync = true; + pClient->async.callHandle = callHandle; + pClient->async.paParms = paParms; + + LogRel2(("svcCall: async.\n")); + } + + vboxSvcClipboardUnlock (); + } + else + { + rc = VERR_NOT_SUPPORTED; + } + } + } break; + + case VBOX_SHARED_CLIPBOARD_FN_FORMATS: + { + /* The guest reports that some formats are available. */ + LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_FORMATS\n")); + + if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_FORMATS) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + uint32_t u32Formats; + + rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Formats); + + if (RT_SUCCESS (rc)) + { + if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST + && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL) + { + rc = VERR_NOT_SUPPORTED; + break; + } + + if (g_pfnExtension) + { + VBOXCLIPBOARDEXTPARMS parms; + + parms.u32Format = u32Formats; + + g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof (parms)); + } + else + { + vboxClipboardFormatAnnounce (pClient, u32Formats); + } + } + } + } break; + + case VBOX_SHARED_CLIPBOARD_FN_READ_DATA: + { + /* The guest wants to read data in the given format. */ + LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_READ_DATA\n")); + + if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_READ_DATA) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* size */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + uint32_t u32Format; + void *pv; + uint32_t cb; + + rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format); + + if (RT_SUCCESS (rc)) + { + rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb); + + if (RT_SUCCESS (rc)) + { + if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST + && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL) + { + rc = VERR_NOT_SUPPORTED; + break; + } + + uint32_t cbActual = 0; + + if (g_pfnExtension) + { + VBOXCLIPBOARDEXTPARMS parms; + + parms.u32Format = u32Format; + parms.u.pvData = pv; + parms.cbData = cb; + + g_fReadingData = true; + rc = g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ, &parms, sizeof (parms)); + LogRelFlow(("DATA: g_fDelayedAnnouncement = %d, g_u32DelayedFormats = 0x%x\n", g_fDelayedAnnouncement, g_u32DelayedFormats)); + if (g_fDelayedAnnouncement) + { + vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, g_u32DelayedFormats); + g_fDelayedAnnouncement = false; + g_u32DelayedFormats = 0; + } + g_fReadingData = false; + + if (RT_SUCCESS (rc)) + { + cbActual = parms.cbData; + } + } + else + { + /* Release any other pending read, as we only + * support one pending read at one time. */ + vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0); + rc = vboxClipboardReadData (pClient, u32Format, pv, cb, &cbActual); + } + + /* Remember our read request until it is completed. + * See the protocol description above for more + * information. */ + if (rc == VINF_HGCM_ASYNC_EXECUTE) + { + if (vboxSvcClipboardLock()) + { + pClient->asyncRead.callHandle = callHandle; + pClient->asyncRead.paParms = paParms; + pClient->fReadPending = true; + fAsynchronousProcessing = true; + vboxSvcClipboardUnlock(); + } + else + rc = VERR_NOT_SUPPORTED; + } + else if (RT_SUCCESS (rc)) + { + VBoxHGCMParmUInt32Set (&paParms[2], cbActual); + } + } + } + } + } break; + + case VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA: + { + /* The guest writes the requested data. */ + LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA\n")); + + if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_WRITE_DATA) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + void *pv; + uint32_t cb; + uint32_t u32Format; + + rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format); + + if (RT_SUCCESS (rc)) + { + rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb); + + if (RT_SUCCESS (rc)) + { + if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST + && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL) + { + rc = VERR_NOT_SUPPORTED; + break; + } + + if (g_pfnExtension) + { + VBOXCLIPBOARDEXTPARMS parms; + + parms.u32Format = u32Format; + parms.u.pvData = pv; + parms.cbData = cb; + + g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof (parms)); + } + else + { + vboxClipboardWriteData (pClient, pv, cb, u32Format); + } + } + } + } + } break; + + default: + { + rc = VERR_NOT_IMPLEMENTED; + } + } + + LogRelFlow(("svcCall: rc = %Rrc\n", rc)); + + if (!fAsynchronousProcessing) + { + g_pHelpers->pfnCallComplete (callHandle, rc); + } +} + +/** If the client in the guest is waiting for a read operation to complete + * then complete it, otherwise return. See the protocol description in the + * shared clipboard module description. */ +void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual) +{ + VBOXHGCMCALLHANDLE callHandle = NULL; + VBOXHGCMSVCPARM *paParms = NULL; + bool fReadPending = false; + if (vboxSvcClipboardLock()) /* if not can we do anything useful? */ + { + callHandle = pClient->asyncRead.callHandle; + paParms = pClient->asyncRead.paParms; + fReadPending = pClient->fReadPending; + pClient->fReadPending = false; + vboxSvcClipboardUnlock(); + } + if (fReadPending) + { + VBoxHGCMParmUInt32Set (&paParms[2], cbActual); + g_pHelpers->pfnCallComplete (callHandle, rc); + } +} + +/* + * We differentiate between a function handler for the guest and one for the host. + */ +static DECLCALLBACK(int) svcHostCall (void *, + uint32_t u32Function, + uint32_t cParms, + VBOXHGCMSVCPARM paParms[]) +{ + int rc = VINF_SUCCESS; + + LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n", + u32Function, cParms, paParms)); + + switch (u32Function) + { + case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE: + { + LogRel2(("svcCall: VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE\n")); + + if (cParms != 1) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* mode */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + uint32_t u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF; + + rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Mode); + + /* The setter takes care of invalid values. */ + vboxSvcClipboardModeSet (u32Mode); + } + } break; + + case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS: + { + uint32_t u32Headless = g_fHeadless; + + rc = VERR_INVALID_PARAMETER; + if (cParms != 1) + break; + rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Headless); + if (RT_SUCCESS(rc)) + LogRelFlow(("svcCall: VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, u32Headless=%u\n", + (unsigned) u32Headless)); + g_fHeadless = RT_BOOL(u32Headless); + } break; + + default: + break; + } + + LogRelFlow(("svcHostCall: rc = %Rrc\n", rc)); + return rc; +} + +#ifndef UNIT_TEST +/** + * SSM descriptor table for the VBOXCLIPBOARDCLIENTDATA structure. + */ +static SSMFIELD const g_aClipboardClientDataFields[] = +{ + SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32ClientID), /* for validation purposes */ + SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgQuit), + SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgReadData), + SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fMsgFormats), + SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32RequestedFormat), + SSMFIELD_ENTRY_TERM() +}; +#endif + +static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM) +{ +#ifndef UNIT_TEST + /* + * When the state will be restored, pending requests will be reissued + * by VMMDev. The service therefore must save state as if there were no + * pending request. + * Pending requests, if any, will be completed in svcDisconnect. + */ + LogRel2 (("svcSaveState: u32ClientID = %d\n", u32ClientID)); + + VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient; + + /* This field used to be the length. We're using it as a version field + with the high bit set. */ + SSMR3PutU32 (pSSM, UINT32_C (0x80000002)); + int rc = SSMR3PutStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL); + AssertRCReturn (rc, rc); + +#else /* UNIT_TEST */ + RT_NOREF3(u32ClientID, pvClient, pSSM); +#endif /* UNIT_TEST */ + return VINF_SUCCESS; +} + +/** + * This structure corresponds to the original layout of the + * VBOXCLIPBOARDCLIENTDATA structure. As the structure was saved as a whole + * when saving state, we need to remember it forever in order to preserve + * compatibility. + * + * (Starting with 3.1 this is no longer used.) + * + * @remarks Putting this outside svcLoadState to avoid visibility warning caused + * by -Wattributes. + */ +typedef struct CLIPSAVEDSTATEDATA +{ + struct CLIPSAVEDSTATEDATA *pNext; + struct CLIPSAVEDSTATEDATA *pPrev; + + VBOXCLIPBOARDCONTEXT *pCtx; + + uint32_t u32ClientID; + + bool fAsync: 1; /* Guest is waiting for a message. */ + + bool fMsgQuit: 1; + bool fMsgReadData: 1; + bool fMsgFormats: 1; + + struct { + VBOXHGCMCALLHANDLE callHandle; + VBOXHGCMSVCPARM *paParms; + } async; + + struct { + void *pv; + uint32_t cb; + uint32_t u32Format; + } data; + + uint32_t u32AvailableFormats; + uint32_t u32RequestedFormat; + +} CLIPSAVEDSTATEDATA; + +static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion) +{ +#ifndef UNIT_TEST + RT_NOREF(uVersion); + LogRel2 (("svcLoadState: u32ClientID = %d\n", u32ClientID)); + + VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient; + + /* Existing client can not be in async state yet. */ + Assert (!pClient->fAsync); + + /* Save the client ID for data validation. */ + /** @todo isn't this the same as u32ClientID? Playing safe for now... */ + uint32_t const u32ClientIDOld = pClient->u32ClientID; + + /* Restore the client data. */ + uint32_t lenOrVer; + int rc = SSMR3GetU32 (pSSM, &lenOrVer); + AssertRCReturn (rc, rc); + if (lenOrVer == UINT32_C (0x80000002)) + { + rc = SSMR3GetStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL); + AssertRCReturn (rc, rc); + } + else if (lenOrVer == (SSMR3HandleHostBits (pSSM) == 64 ? 72U : 48U)) + { + /** + * SSM descriptor table for the CLIPSAVEDSTATEDATA structure. + */ + static SSMFIELD const s_aClipSavedStateDataFields30[] = + { + SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pNext), + SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pPrev), + SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pCtx), + SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32ClientID), + SSMFIELD_ENTRY_CUSTOM(fMsgQuit + fMsgReadData + fMsgFormats, RT_UOFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4), + SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.callHandle), + SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.paParms), + SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.pv), + SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.cb), + SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.u32Format), + SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, u32AvailableFormats), + SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32RequestedFormat), + SSMFIELD_ENTRY_TERM() + }; + + CLIPSAVEDSTATEDATA savedState; + RT_ZERO (savedState); + rc = SSMR3GetStructEx (pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID, + &s_aClipSavedStateDataFields30[0], NULL); + AssertRCReturn (rc, rc); + + pClient->fMsgQuit = savedState.fMsgQuit; + pClient->fMsgReadData = savedState.fMsgReadData; + pClient->fMsgFormats = savedState.fMsgFormats; + pClient->u32RequestedFormat = savedState.u32RequestedFormat; + } + else + { + LogRel (("Client data size mismatch: got %#x\n", lenOrVer)); + return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; + } + + /* Verify the client ID. */ + if (pClient->u32ClientID != u32ClientIDOld) + { + LogRel (("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID)); + pClient->u32ClientID = u32ClientIDOld; + return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; + } + + /* Actual host data are to be reported to guest (SYNC). */ + vboxClipboardSync (pClient); + +#else /* UNIT_TEST*/ + RT_NOREF(u32ClientID, pvClient, pSSM, uVersion); +#endif /* UNIT_TEST */ + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) extCallback (uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData) +{ + RT_NOREF2(pvData, cbData); + if (g_pClient != NULL) + { + switch (u32Function) + { + case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE: + { + LogRelFlow(("ANNOUNCE: g_fReadingData = %d\n", g_fReadingData)); + if (g_fReadingData) + { + g_fDelayedAnnouncement = true; + g_u32DelayedFormats = u32Format; + } + else + { + vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, u32Format); + } + } break; + + case VBOX_CLIPBOARD_EXT_FN_DATA_READ: + { + vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format); + } break; + + default: + return VERR_NOT_SUPPORTED; + } + } + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension) +{ + LogRelFlowFunc(("pfnExtension = %p\n", pfnExtension)); + + VBOXCLIPBOARDEXTPARMS parms; + + if (pfnExtension) + { + /* Install extension. */ + g_pfnExtension = pfnExtension; + g_pvExtension = pvExtension; + + parms.u.pfnCallback = extCallback; + g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof (parms)); + } + else + { + if (g_pfnExtension) + { + parms.u.pfnCallback = NULL; + g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof (parms)); + } + + /* Uninstall extension. */ + g_pfnExtension = NULL; + g_pvExtension = NULL; + } + + return VINF_SUCCESS; +} + +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable) +{ + int rc = VINF_SUCCESS; + + LogRelFlowFunc(("ptable = %p\n", ptable)); + + if (!ptable) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + LogRel2(("VBoxHGCMSvcLoad: ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version)); + + if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE) + || ptable->u32Version != VBOX_HGCM_SVC_VERSION) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + g_pHelpers = ptable->pHelpers; + + ptable->cbClient = sizeof (VBOXCLIPBOARDCLIENTDATA); + + ptable->pfnUnload = svcUnload; + ptable->pfnConnect = svcConnect; + ptable->pfnDisconnect = svcDisconnect; + ptable->pfnCall = svcCall; + ptable->pfnHostCall = svcHostCall; + ptable->pfnSaveState = svcSaveState; + ptable->pfnLoadState = svcLoadState; + ptable->pfnRegisterExtension = svcRegisterExtension; + ptable->pfnNotify = NULL; + ptable->pvService = NULL; + + /* Service specific initialization. */ + rc = svcInit (); + } + } + + return rc; +} diff --git a/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.rc b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.rc new file mode 100644 index 00000000..4ec3753f --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxSharedClipboardSvc.rc $ */ +/** @file + * Shared Clipboard Service - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox Shared Clipboard Host Service\0" + VALUE "InternalName", "VBoxSharedClipboard\0" + VALUE "OriginalFilename", "VBoxSharedClipboard.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp new file mode 100644 index 00000000..88d4c2f9 --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.cpp @@ -0,0 +1,404 @@ +/* $Id: darwin-pasteboard.cpp $ */ +/** @file + * Shared Clipboard Service - Mac OS X host implementation. + */ + +/* + * Includes contributions from François Revol + * + * Copyright (C) 2008-2019 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_SHARED_CLIPBOARD +#include <Carbon/Carbon.h> + +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/errcore.h> +#include <iprt/utf16.h> + +#include "VBox/log.h" +#include "VBox/HostServices/VBoxClipboardSvc.h" +#include "VBox/GuestHost/clipboard-helper.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/* For debugging */ +//#define SHOW_CLIPBOARD_CONTENT + + +/** + * Initialize the global pasteboard and return a reference to it. + * + * @param pPasteboardRef Reference to the global pasteboard. + * + * @returns IPRT status code. + */ +int initPasteboard(PasteboardRef *pPasteboardRef) +{ + int rc = VINF_SUCCESS; + + if (PasteboardCreate(kPasteboardClipboard, pPasteboardRef)) + rc = VERR_NOT_SUPPORTED; + + return rc; +} + +/** + * Release the reference to the global pasteboard. + * + * @param pPasteboardRef Reference to the global pasteboard. + */ +void destroyPasteboard(PasteboardRef *pPasteboardRef) +{ + CFRelease(*pPasteboardRef); + *pPasteboardRef = NULL; +} + +/** + * Inspect the global pasteboard for new content. Check if there is some type + * that is supported by vbox and return it. + * + * @param pPasteboard Reference to the global pasteboard. + * @param pfFormats Pointer for the bit combination of the + * supported types. + * @param pfChanged True if something has changed after the + * last call. + * + * @returns IPRT status code. (Always VINF_SUCCESS atm.) + */ +int queryNewPasteboardFormats(PasteboardRef pPasteboard, uint32_t *pfFormats, bool *pfChanged) +{ + Log(("queryNewPasteboardFormats\n")); + + OSStatus err = noErr; + *pfChanged = true; + + PasteboardSyncFlags syncFlags; + /* Make sure all is in sync */ + syncFlags = PasteboardSynchronize(pPasteboard); + /* If nothing changed return */ + if (!(syncFlags & kPasteboardModified)) + { + *pfChanged = false; + return VINF_SUCCESS; + } + + /* Are some items in the pasteboard? */ + ItemCount itemCount; + err = PasteboardGetItemCount(pPasteboard, &itemCount); + if (itemCount < 1) + return VINF_SUCCESS; + + /* The id of the first element in the pasteboard */ + int rc = VINF_SUCCESS; + PasteboardItemID itemID; + if (!(err = PasteboardGetItemIdentifier(pPasteboard, 1, &itemID))) + { + /* Retrieve all flavors in the pasteboard, maybe there + * is something we can use. */ + CFArrayRef flavorTypeArray; + if (!(err = PasteboardCopyItemFlavors(pPasteboard, itemID, &flavorTypeArray))) + { + CFIndex flavorCount; + flavorCount = CFArrayGetCount(flavorTypeArray); + for (CFIndex flavorIndex = 0; flavorIndex < flavorCount; flavorIndex++) + { + CFStringRef flavorType; + flavorType = static_cast <CFStringRef>(CFArrayGetValueAtIndex(flavorTypeArray, + flavorIndex)); + /* Currently only unicode supported */ + if (UTTypeConformsTo(flavorType, kUTTypeUTF8PlainText) || + UTTypeConformsTo(flavorType, kUTTypeUTF16PlainText)) + { + Log(("Unicode flavor detected.\n")); + *pfFormats |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT; + } + else if (UTTypeConformsTo(flavorType, kUTTypeBMP)) + { + Log(("BMP flavor detected.\n")); + *pfFormats |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP; + } + } + CFRelease(flavorTypeArray); + } + } + + Log(("queryNewPasteboardFormats: rc = %02X\n", rc)); + return rc; +} + +/** + * Read content from the host clipboard and write it to the internal clipboard + * structure for further processing. + * + * @param pPasteboard Reference to the global pasteboard. + * @param fFormat The format type which should be read. + * @param pv The destination buffer. + * @param cb The size of the destination buffer. + * @param pcbActual The size which is needed to transfer the content. + * + * @returns IPRT status code. + */ +int readFromPasteboard(PasteboardRef pPasteboard, uint32_t fFormat, void *pv, uint32_t cb, uint32_t *pcbActual) +{ + Log(("readFromPasteboard: fFormat = %02X\n", fFormat)); + + OSStatus err = noErr; + + /* Make sure all is in sync */ + PasteboardSynchronize(pPasteboard); + + /* Are some items in the pasteboard? */ + ItemCount itemCount; + err = PasteboardGetItemCount(pPasteboard, &itemCount); + if (itemCount < 1) + return VINF_SUCCESS; + + /* The id of the first element in the pasteboard */ + int rc = VERR_NOT_SUPPORTED; + PasteboardItemID itemID; + if (!(err = PasteboardGetItemIdentifier(pPasteboard, 1, &itemID))) + { + /* The guest request unicode */ + if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) + { + CFDataRef outData; + PRTUTF16 pwszTmp = NULL; + /* Try utf-16 first */ + if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeUTF16PlainText, &outData))) + { + Log(("Clipboard content is utf-16\n")); + + PRTUTF16 pwszString = (PRTUTF16)CFDataGetBytePtr(outData); + if (pwszString) + rc = RTUtf16DupEx(&pwszTmp, pwszString, 0); + else + rc = VERR_INVALID_PARAMETER; + } + /* Second try is utf-8 */ + else + if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeUTF8PlainText, &outData))) + { + Log(("readFromPasteboard: clipboard content is utf-8\n")); + const char *pszString = (const char *)CFDataGetBytePtr(outData); + if (pszString) + rc = RTStrToUtf16(pszString, &pwszTmp); + else + rc = VERR_INVALID_PARAMETER; + } + if (pwszTmp) + { + /* Check how much longer will the converted text will be. */ + size_t cwSrc = RTUtf16Len(pwszTmp); + size_t cwDest; + rc = vboxClipboardUtf16GetWinSize(pwszTmp, cwSrc, &cwDest); + if (RT_FAILURE(rc)) + { + RTUtf16Free(pwszTmp); + Log(("readFromPasteboard: clipboard conversion failed. vboxClipboardUtf16GetWinSize returned %Rrc. Abandoning.\n", rc)); + AssertRCReturn(rc, rc); + } + /* Set the actually needed data size */ + *pcbActual = cwDest * 2; + /* Return success state */ + rc = VINF_SUCCESS; + /* Do not copy data if the dst buffer is not big enough. */ + if (*pcbActual <= cb) + { + rc = vboxClipboardUtf16LinToWin(pwszTmp, RTUtf16Len(pwszTmp), static_cast <PRTUTF16>(pv), cb / 2); + if (RT_FAILURE(rc)) + { + RTUtf16Free(pwszTmp); + Log(("readFromPasteboard: clipboard conversion failed. vboxClipboardUtf16LinToWin() returned %Rrc. Abandoning.\n", rc)); + AssertRCReturn(rc, rc); + } +#ifdef SHOW_CLIPBOARD_CONTENT + Log(("readFromPasteboard: clipboard content: %ls\n", static_cast <PRTUTF16>(pv))); +#endif + } + /* Free the temp string */ + RTUtf16Free(pwszTmp); + } + } + /* The guest request BITMAP */ + else if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP) + { + CFDataRef outData; + const void *pTmp = NULL; + size_t cbTmpSize; + /* Get the data from the pasteboard */ + if (!(err = PasteboardCopyItemFlavorData(pPasteboard, itemID, kUTTypeBMP, &outData))) + { + Log(("Clipboard content is BMP\n")); + pTmp = CFDataGetBytePtr(outData); + cbTmpSize = CFDataGetLength(outData); + } + if (pTmp) + { + const void *pDib; + size_t cbDibSize; + rc = vboxClipboardBmpGetDib(pTmp, cbTmpSize, &pDib, &cbDibSize); + if (RT_FAILURE(rc)) + { + rc = VERR_NOT_SUPPORTED; + Log(("readFromPasteboard: unknown bitmap format. vboxClipboardBmpGetDib returned %Rrc. Abandoning.\n", rc)); + AssertRCReturn(rc, rc); + } + + *pcbActual = cbDibSize; + /* Return success state */ + rc = VINF_SUCCESS; + /* Do not copy data if the dst buffer is not big enough. */ + if (*pcbActual <= cb) + { + memcpy(pv, pDib, cbDibSize); +#ifdef SHOW_CLIPBOARD_CONTENT + Log(("readFromPasteboard: clipboard content bitmap %d bytes\n", cbDibSize)); +#endif + } + } + } + } + + Log(("readFromPasteboard: rc = %02X\n", rc)); + return rc; +} + +/** + * Write clipboard content to the host clipboard from the internal clipboard + * structure. + * + * @param pPasteboard Reference to the global pasteboard. + * @param pv The source buffer. + * @param cb The size of the source buffer. + * @param fFormat The format type which should be written. + * + * @returns IPRT status code. + */ +int writeToPasteboard(PasteboardRef pPasteboard, void *pv, uint32_t cb, uint32_t fFormat) +{ + Log(("writeToPasteboard: fFormat = %02X\n", fFormat)); + + /* Clear the pasteboard */ + if (PasteboardClear(pPasteboard)) + return VERR_NOT_SUPPORTED; + + /* Make sure all is in sync */ + PasteboardSynchronize(pPasteboard); + + int rc = VERR_NOT_SUPPORTED; + /* Handle the unicode text */ + if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) + { + PRTUTF16 pwszSrcText = static_cast <PRTUTF16>(pv); + size_t cwSrc = cb / 2; + size_t cwDest = 0; + /* How long will the converted text be? */ + rc = vboxClipboardUtf16GetLinSize(pwszSrcText, cwSrc, &cwDest); + if (RT_FAILURE(rc)) + { + Log(("writeToPasteboard: clipboard conversion failed. vboxClipboardUtf16GetLinSize returned %Rrc. Abandoning.\n", rc)); + AssertRCReturn(rc, rc); + } + /* Empty clipboard? Not critical */ + if (cwDest == 0) + { + Log(("writeToPasteboard: received empty clipboard data from the guest, returning false.\n")); + return VINF_SUCCESS; + } + /* Allocate the necessary memory */ + PRTUTF16 pwszDestText = static_cast <PRTUTF16>(RTMemAlloc(cwDest * 2)); + if (pwszDestText == NULL) + { + Log(("writeToPasteboard: failed to allocate %d bytes\n", cwDest * 2)); + return VERR_NO_MEMORY; + } + /* Convert the EOL */ + rc = vboxClipboardUtf16WinToLin(pwszSrcText, cwSrc, pwszDestText, cwDest); + if (RT_FAILURE(rc)) + { + Log(("writeToPasteboard: clipboard conversion failed. vboxClipboardUtf16WinToLin() returned %Rrc. Abandoning.\n", rc)); + RTMemFree(pwszDestText); + AssertRCReturn(rc, rc); + } + + CFDataRef textData = NULL; + /* Item id is 1. Nothing special here. */ + PasteboardItemID itemId = (PasteboardItemID)1; + /* Create a CData object which we could pass to the pasteboard */ + if ((textData = CFDataCreate(kCFAllocatorDefault, + reinterpret_cast<UInt8*>(pwszDestText), cwDest * 2))) + { + /* Put the Utf-16 version to the pasteboard */ + PasteboardPutItemFlavor(pPasteboard, itemId, + kUTTypeUTF16PlainText, + textData, 0); + } + /* Create a Utf-8 version */ + char *pszDestText; + rc = RTUtf16ToUtf8(pwszDestText, &pszDestText); + if (RT_SUCCESS(rc)) + { + /* Create a CData object which we could pass to the pasteboard */ + if ((textData = CFDataCreate(kCFAllocatorDefault, + reinterpret_cast<UInt8*>(pszDestText), strlen(pszDestText)))) + { + /* Put the Utf-8 version to the pasteboard */ + PasteboardPutItemFlavor(pPasteboard, itemId, + kUTTypeUTF8PlainText, + textData, 0); + } + RTStrFree(pszDestText); + } + + RTMemFree(pwszDestText); + rc = VINF_SUCCESS; + } + /* Handle the bitmap */ + else if (fFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP) + { + /* Create a full BMP from it */ + void *pBmp; + size_t cbBmpSize; + CFDataRef bmpData = NULL; + /* Item id is 1. Nothing special here. */ + PasteboardItemID itemId = (PasteboardItemID)1; + + rc = vboxClipboardDibToBmp(pv, cb, &pBmp, &cbBmpSize); + if (RT_SUCCESS(rc)) + { + /* Create a CData object which we could pass to the pasteboard */ + if ((bmpData = CFDataCreate(kCFAllocatorDefault, + reinterpret_cast<UInt8*>(pBmp), cbBmpSize))) + { + /* Put the Utf-8 version to the pasteboard */ + PasteboardPutItemFlavor(pPasteboard, itemId, + kUTTypeBMP, + bmpData, 0); + } + RTMemFree(pBmp); + } + rc = VINF_SUCCESS; + } + else + rc = VERR_NOT_IMPLEMENTED; + + Log(("writeToPasteboard: rc = %02X\n", rc)); + return rc; +} + diff --git a/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h new file mode 100644 index 00000000..44606fe2 --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/darwin-pasteboard.h @@ -0,0 +1,34 @@ +/* $Id: darwin-pasteboard.h $ */ +/** @file + * Shared Clipboard Service - Mac OS X host implementation. + */ + +/* + * Copyright (C) 2008-2019 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. + */ + +#ifndef VBOX_INCLUDED_SRC_SharedClipboard_darwin_pasteboard_h +#define VBOX_INCLUDED_SRC_SharedClipboard_darwin_pasteboard_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +typedef struct OpaquePasteboardRef *PasteboardRef; + +int initPasteboard(PasteboardRef *pPasteboardRef); +void destroyPasteboard(PasteboardRef *pPasteboardRef); + +int queryNewPasteboardFormats(PasteboardRef pPasteboard, uint32_t *pfFormats, bool *pfChanged); +int readFromPasteboard(PasteboardRef pPasteboard, uint32_t fFormat, void *pv, uint32_t cb, uint32_t *pcbActual); +int writeToPasteboard(PasteboardRef pPasteboard, void *pv, uint32_t cb, uint32_t fFormat); + +#endif /* !VBOX_INCLUDED_SRC_SharedClipboard_darwin_pasteboard_h */ + diff --git a/src/VBox/HostServices/SharedClipboard/darwin.cpp b/src/VBox/HostServices/SharedClipboard/darwin.cpp new file mode 100644 index 00000000..3dbafbd3 --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/darwin.cpp @@ -0,0 +1,274 @@ +/* $Id: darwin.cpp $ */ +/** @file + * Shared Clipboard Service - Mac OS X host. + */ + +/* + * Copyright (C) 2008-2019 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_SHARED_CLIPBOARD +#include <VBox/HostServices/VBoxClipboardSvc.h> + +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/thread.h> + +#include "VBoxClipboard.h" +#include "darwin-pasteboard.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** Global clipboard context information */ +struct _VBOXCLIPBOARDCONTEXT +{ + /** We have a separate thread to poll for new clipboard content */ + RTTHREAD thread; + bool volatile fTerminate; + + /** The reference to the current pasteboard */ + PasteboardRef pasteboard; + + VBOXCLIPBOARDCLIENTDATA *pClient; +}; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Only one client is supported. There seems to be no need for more clients. */ +static VBOXCLIPBOARDCONTEXT g_ctx; + + +/** + * Checks if something is present on the clipboard and calls vboxSvcClipboardReportMsg. + * + * @returns IPRT status code (ignored). + * @param pCtx The context. + */ +static int vboxClipboardChanged (VBOXCLIPBOARDCONTEXT *pCtx) +{ + if (pCtx->pClient == NULL) + return VINF_SUCCESS; + + uint32_t fFormats = 0; + bool fChanged = false; + /* Retrieve the formats currently in the clipboard and supported by vbox */ + int rc = queryNewPasteboardFormats (pCtx->pasteboard, &fFormats, &fChanged); + if (RT_SUCCESS (rc) && fChanged) + { + vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, fFormats); + Log (("vboxClipboardChanged fFormats %02X\n", fFormats)); + } + + return rc; +} + + +/** + * The poller thread. + * + * This thread will check for the arrival of new data on the clipboard. + * + * @returns VINF_SUCCESS (not used). + * @param ThreadSelf Our thread handle. + * @param pvUser Pointer to the VBOXCLIPBOARDCONTEXT structure. + * + */ +static int vboxClipboardThread (RTTHREAD ThreadSelf, void *pvUser) +{ + Log (("vboxClipboardThread: starting clipboard thread\n")); + + AssertPtrReturn (pvUser, VERR_INVALID_PARAMETER); + VBOXCLIPBOARDCONTEXT *pCtx = (VBOXCLIPBOARDCONTEXT *) pvUser; + + while (!pCtx->fTerminate) + { + /* call this behind the lock because we don't know if the api is + thread safe and in any case we're calling several methods. */ + vboxSvcClipboardLock(); + vboxClipboardChanged (pCtx); + vboxSvcClipboardUnlock(); + + /* Sleep for 200 msecs before next poll */ + RTThreadUserWait (ThreadSelf, 200); + } + + Log (("vboxClipboardThread: clipboard thread terminated successfully with return code %Rrc\n", VINF_SUCCESS)); + return VINF_SUCCESS; +} + +/* + * Public platform dependent functions. + */ + +/** Initialise the host side of the shared clipboard - called by the hgcm layer. */ +int vboxClipboardInit (void) +{ + Log (("vboxClipboardInit\n")); + + g_ctx.fTerminate = false; + + int rc = initPasteboard (&g_ctx.pasteboard); + AssertRCReturn (rc, rc); + + rc = RTThreadCreate (&g_ctx.thread, vboxClipboardThread, &g_ctx, 0, + RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP"); + if (RT_FAILURE (rc)) + { + g_ctx.thread = NIL_RTTHREAD; + destroyPasteboard (&g_ctx.pasteboard); + } + + return rc; +} + +/** Terminate the host side of the shared clipboard - called by the hgcm layer. */ +void vboxClipboardDestroy (void) +{ + Log (("vboxClipboardDestroy\n")); + + /* + * Signal the termination of the polling thread and wait for it to respond. + */ + ASMAtomicWriteBool (&g_ctx.fTerminate, true); + int rc = RTThreadUserSignal (g_ctx.thread); + AssertRC (rc); + rc = RTThreadWait (g_ctx.thread, RT_INDEFINITE_WAIT, NULL); + AssertRC (rc); + + /* + * Destroy the pasteboard and uninitialize the global context record. + */ + destroyPasteboard (&g_ctx.pasteboard); + g_ctx.thread = NIL_RTTHREAD; + g_ctx.pClient = NULL; +} + +/** + * Enable the shared clipboard - called by the hgcm clipboard subsystem. + * + * @param pClient Structure containing context information about the guest system + * @param fHeadless Whether headless. + * @returns RT status code + */ +int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless) +{ + NOREF(fHeadless); + if (g_ctx.pClient != NULL) + { + /* One client only. */ + return VERR_NOT_SUPPORTED; + } + + vboxSvcClipboardLock(); + + pClient->pCtx = &g_ctx; + pClient->pCtx->pClient = pClient; + + /* Initially sync the host clipboard content with the client. */ + int rc = vboxClipboardSync (pClient); + + vboxSvcClipboardUnlock(); + return rc; +} + +/** + * Synchronise the contents of the host clipboard with the guest, called by the HGCM layer + * after a save and restore of the guest. + */ +int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient) +{ + /* Sync the host clipboard content with the client. */ + vboxSvcClipboardLock(); + int rc = vboxClipboardChanged (pClient->pCtx); + vboxSvcClipboardUnlock(); + + return rc; +} + +/** + * Shut down the shared clipboard subsystem and "disconnect" the guest. + */ +void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient) +{ + Log (("vboxClipboardDisconnect\n")); + + vboxSvcClipboardLock(); + pClient->pCtx->pClient = NULL; + vboxSvcClipboardUnlock(); +} + +/** + * The guest is taking possession of the shared clipboard. Called by the HGCM clipboard + * subsystem. + * + * @param pClient Context data for the guest system + * @param u32Formats Clipboard formats the guest is offering + */ +void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats) +{ + Log (("vboxClipboardFormatAnnounce u32Formats %02X\n", u32Formats)); + if (u32Formats == 0) + { + /* This is just an automatism, not a genuine announcement */ + return; + } + + vboxSvcClipboardReportMsg (pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, + u32Formats); +} + +/** + * Called by the HGCM clipboard subsystem when the guest wants to read the host clipboard. + * + * @param pClient Context information about the guest VM + * @param u32Format The format that the guest would like to receive the data in + * @param pv Where to write the data to + * @param cb The size of the buffer to write the data to + * @param pcbActual Where to write the actual size of the written data + */ +int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, + void *pv, uint32_t cb, uint32_t * pcbActual) +{ + vboxSvcClipboardLock(); + + /* Default to no data available. */ + *pcbActual = 0; + int rc = readFromPasteboard (pClient->pCtx->pasteboard, u32Format, pv, cb, pcbActual); + + vboxSvcClipboardUnlock(); + return rc; +} + +/** + * Called by the HGCM clipboard subsystem when we have requested data and that data arrives. + * + * @param pClient Context information about the guest VM + * @param pv Buffer to which the data was written + * @param cb The size of the data written + * @param u32Format The format of the data written + */ +void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, + uint32_t cb, uint32_t u32Format) +{ + vboxSvcClipboardLock(); + + writeToPasteboard (pClient->pCtx->pasteboard, pv, cb, u32Format); + + vboxSvcClipboardUnlock(); +} diff --git a/src/VBox/HostServices/SharedClipboard/testcase/Makefile.kmk b/src/VBox/HostServices/SharedClipboard/testcase/Makefile.kmk new file mode 100644 index 00000000..b1bf2d23 --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/testcase/Makefile.kmk @@ -0,0 +1,33 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Shared Clipboard Host Service testcases. +# + +# +# Copyright (C) 2011-2019 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. +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK) + + PROGRAMS += tstClipboardServiceHost + + tstClipboardServiceHost_TEMPLATE = VBOXR3TSTEXE + tstClipboardServiceHost_DEFS = VBOX_WITH_HGCM UNIT_TEST + tstClipboardServiceHost_SOURCES = \ + ../VBoxSharedClipboardSvc.cpp \ + tstClipboardServiceHost.cpp + +endif + +include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp b/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp new file mode 100644 index 00000000..f431886d --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp @@ -0,0 +1,286 @@ +/* $Id: tstClipboardServiceHost.cpp $ */ +/** @file + * Shared Clipboard host service test case. + */ + +/* + * Copyright (C) 2011-2019 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. + */ + +#include "../VBoxClipboard.h" + +#include <VBox/HostServices/VBoxClipboardSvc.h> + +#include <iprt/assert.h> +#include <iprt/string.h> +#include <iprt/test.h> + +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable); + +static VBOXCLIPBOARDCLIENTDATA g_Client; +static VBOXHGCMSVCHELPERS g_Helpers = { NULL }; + +/** Simple call handle structure for the guest call completion callback */ +struct VBOXHGCMCALLHANDLE_TYPEDEF +{ + /** Where to store the result code */ + int32_t rc; +}; + +/** Call completion callback for guest calls. */ +static DECLCALLBACK(int) callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc) +{ + callHandle->rc = rc; + return VINF_SUCCESS; +} + +static int setupTable(VBOXHGCMSVCFNTABLE *pTable) +{ + pTable->cbSize = sizeof(*pTable); + pTable->u32Version = VBOX_HGCM_SVC_VERSION; + g_Helpers.pfnCallComplete = callComplete; + pTable->pHelpers = &g_Helpers; + return VBoxHGCMSvcLoad(pTable); +} + +static void testSetMode(void) +{ + struct VBOXHGCMSVCPARM parms[2]; + VBOXHGCMSVCFNTABLE table; + uint32_t u32Mode; + int rc; + + RTTestISub("Testing HOST_FN_SET_MODE"); + rc = setupTable(&table); + RTTESTI_CHECK_MSG_RETV(RT_SUCCESS(rc), ("rc=%Rrc\n", rc)); + /* Reset global variable which doesn't reset itself. */ + HGCMSvcSetU32(&parms[0], VBOX_SHARED_CLIPBOARD_MODE_OFF); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, + 1, parms); + RTTESTI_CHECK_RC_OK(rc); + u32Mode = TestClipSvcGetMode(); + RTTESTI_CHECK_MSG(u32Mode == VBOX_SHARED_CLIPBOARD_MODE_OFF, + ("u32Mode=%u\n", (unsigned) u32Mode)); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, + 0, parms); + RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, + 2, parms); + RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER); + HGCMSvcSetU64(&parms[0], 99); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, + 1, parms); + RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER); + HGCMSvcSetU32(&parms[0], VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, + 1, parms); + RTTESTI_CHECK_RC_OK(rc); + u32Mode = TestClipSvcGetMode(); + RTTESTI_CHECK_MSG(u32Mode == VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST, + ("u32Mode=%u\n", (unsigned) u32Mode)); + HGCMSvcSetU32(&parms[0], 99); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, + 1, parms); + RTTESTI_CHECK_RC_OK(rc); + u32Mode = TestClipSvcGetMode(); + RTTESTI_CHECK_MSG(u32Mode == VBOX_SHARED_CLIPBOARD_MODE_OFF, + ("u32Mode=%u\n", (unsigned) u32Mode)); + table.pfnUnload(NULL); +} + +static void testGetHostMsg(void) +{ + struct VBOXHGCMSVCPARM parms[2]; + VBOXHGCMSVCFNTABLE table; + VBOXHGCMCALLHANDLE_TYPEDEF call; + int rc; + + RTTestISub("Setting up VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG test"); + rc = setupTable(&table); + RTTESTI_CHECK_MSG_RETV(RT_SUCCESS(rc), ("rc=%Rrc\n", rc)); + /* Unless we are bidirectional the host message requests will be dropped. */ + HGCMSvcSetU32(&parms[0], VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, + 1, parms); + RTTESTI_CHECK_RC_OK(rc); + + RTTestISub("Testing FN_GET_HOST_MSG, one format, waiting guest call."); + RT_ZERO(g_Client); + HGCMSvcSetU32(&parms[0], 0); + HGCMSvcSetU32(&parms[1], 0); + call.rc = VERR_TRY_AGAIN; + table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, + 2, parms, 0); + RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This should get updated only when the guest call completes. */ + vboxSvcClipboardReportMsg (&g_Client, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, + VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT); + RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA); + RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT); + RTTESTI_CHECK_RC_OK(call.rc); + call.rc = VERR_TRY_AGAIN; + table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, + 2, parms, 0); + RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This call should not complete yet. */ + + RTTestISub("Testing FN_GET_HOST_MSG, one format, no waiting guest calls."); + RT_ZERO(g_Client); + vboxSvcClipboardReportMsg (&g_Client, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, + VBOX_SHARED_CLIPBOARD_FMT_HTML); + HGCMSvcSetU32(&parms[0], 0); + HGCMSvcSetU32(&parms[1], 0); + call.rc = VERR_TRY_AGAIN; + table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, + 2, parms, 0); + RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA); + RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_HTML); + RTTESTI_CHECK_RC_OK(call.rc); + call.rc = VERR_TRY_AGAIN; + table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, + 2, parms, 0); + RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This call should not complete yet. */ + + RTTestISub("Testing FN_GET_HOST_MSG, two formats, waiting guest call."); + RT_ZERO(g_Client); + HGCMSvcSetU32(&parms[0], 0); + HGCMSvcSetU32(&parms[1], 0); + call.rc = VERR_TRY_AGAIN; + table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, + 2, parms, 0); + RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This should get updated only when the guest call completes. */ + vboxSvcClipboardReportMsg (&g_Client, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, + VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT | VBOX_SHARED_CLIPBOARD_FMT_HTML); + RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA); + RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT); + RTTESTI_CHECK_RC_OK(call.rc); + call.rc = VERR_TRY_AGAIN; + table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, + 2, parms, 0); + RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA); + RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_HTML); + RTTESTI_CHECK_RC_OK(call.rc); + call.rc = VERR_TRY_AGAIN; + table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, + 2, parms, 0); + RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This call should not complete yet. */ + + RTTestISub("Testing FN_GET_HOST_MSG, two formats, no waiting guest calls."); + RT_ZERO(g_Client); + vboxSvcClipboardReportMsg (&g_Client, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, + VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT | VBOX_SHARED_CLIPBOARD_FMT_HTML); + HGCMSvcSetU32(&parms[0], 0); + HGCMSvcSetU32(&parms[1], 0); + call.rc = VERR_TRY_AGAIN; + table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, + 2, parms, 0); + RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA); + RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT); + RTTESTI_CHECK_RC_OK(call.rc); + call.rc = VERR_TRY_AGAIN; + table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, + 2, parms, 0); + RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA); + RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHARED_CLIPBOARD_FMT_HTML); + RTTESTI_CHECK_RC_OK(call.rc); + call.rc = VERR_TRY_AGAIN; + table.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, + 2, parms, 0); + RTTESTI_CHECK_RC(call.rc, VERR_TRY_AGAIN); /* This call should not complete yet. */ + table.pfnUnload(NULL); +} + +static void testSetHeadless(void) +{ + struct VBOXHGCMSVCPARM parms[2]; + VBOXHGCMSVCFNTABLE table; + bool fHeadless; + int rc; + + RTTestISub("Testing HOST_FN_SET_HEADLESS"); + rc = setupTable(&table); + RTTESTI_CHECK_MSG_RETV(RT_SUCCESS(rc), ("rc=%Rrc\n", rc)); + /* Reset global variable which doesn't reset itself. */ + HGCMSvcSetU32(&parms[0], false); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, + 1, parms); + RTTESTI_CHECK_RC_OK(rc); + fHeadless = vboxSvcClipboardGetHeadless(); + RTTESTI_CHECK_MSG(fHeadless == false, ("fHeadless=%RTbool\n", fHeadless)); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, + 0, parms); + RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, + 2, parms); + RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER); + HGCMSvcSetU64(&parms[0], 99); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, + 1, parms); + RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER); + HGCMSvcSetU32(&parms[0], true); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, + 1, parms); + RTTESTI_CHECK_RC_OK(rc); + fHeadless = vboxSvcClipboardGetHeadless(); + RTTESTI_CHECK_MSG(fHeadless == true, ("fHeadless=%RTbool\n", fHeadless)); + HGCMSvcSetU32(&parms[0], 99); + rc = table.pfnHostCall(NULL, VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, + 1, parms); + RTTESTI_CHECK_RC_OK(rc); + fHeadless = vboxSvcClipboardGetHeadless(); + RTTESTI_CHECK_MSG(fHeadless == true, ("fHeadless=%RTbool\n", fHeadless)); + table.pfnUnload(NULL); +} + +static void testHostCall(void) +{ + testSetMode(); + testSetHeadless(); +} + + +int main(int argc, char *argv[]) +{ + /* + * Init the runtime, test and say hello. + */ + const char *pcszExecName; + NOREF(argc); + pcszExecName = strrchr(argv[0], '/'); + pcszExecName = pcszExecName ? pcszExecName + 1 : argv[0]; + RTTEST hTest; + RTEXITCODE rcExit = RTTestInitAndCreate(pcszExecName, &hTest); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + RTTestBanner(hTest); + + /* + * Run the tests. + */ + testHostCall(); + testGetHostMsg(); + + /* + * Summary + */ + return RTTestSummaryAndDestroy(hTest); +} + +int vboxClipboardInit() { return VINF_SUCCESS; } +void vboxClipboardDestroy() {} +void vboxClipboardDisconnect(_VBOXCLIPBOARDCLIENTDATA*) { AssertFailed(); } +int vboxClipboardConnect(_VBOXCLIPBOARDCLIENTDATA*, bool) +{ AssertFailed(); return VERR_WRONG_ORDER; } +void vboxClipboardFormatAnnounce(_VBOXCLIPBOARDCLIENTDATA*, unsigned int) +{ AssertFailed(); } +int vboxClipboardReadData(_VBOXCLIPBOARDCLIENTDATA*, unsigned int, void*, unsigned int, unsigned int*) +{ AssertFailed(); return VERR_WRONG_ORDER; } +void vboxClipboardWriteData(_VBOXCLIPBOARDCLIENTDATA*, void*, unsigned int, unsigned int) { AssertFailed(); } +int vboxClipboardSync(_VBOXCLIPBOARDCLIENTDATA*) +{ AssertFailed(); return VERR_WRONG_ORDER; } diff --git a/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp b/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp new file mode 100644 index 00000000..88339e02 --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/x11-clipboard.cpp @@ -0,0 +1,615 @@ +/* $Id: x11-clipboard.cpp $ */ +/** @file + * Shared Clipboard Service - Linux host. + */ + +/* + * Copyright (C) 2006-2019 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_SHARED_CLIPBOARD +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/env.h> +#include <iprt/mem.h> +#include <iprt/semaphore.h> +#include <iprt/string.h> + +#include <VBox/GuestHost/SharedClipboard.h> +#include <VBox/HostServices/VBoxClipboardSvc.h> +#include <VBox/err.h> + +#include "VBoxClipboard.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +struct _VBOXCLIPBOARDREQFROMVBOX; +typedef struct _VBOXCLIPBOARDREQFROMVBOX VBOXCLIPBOARDREQFROMVBOX; + +/** Global context information used by the host glue for the X11 clipboard + * backend */ +struct _VBOXCLIPBOARDCONTEXT +{ + /** This mutex is grabbed during any critical operations on the clipboard + * which might clash with others. */ + RTCRITSECT clipboardMutex; + /** The currently pending request for data from VBox. NULL if there is + * no request pending. The protocol for completing a request is to grab + * the critical section, check that @a pReq is not NULL, fill in the data + * fields and set @a pReq to NULL. The protocol for cancelling a pending + * request is to grab the critical section and set pReq to NULL. + * It is an error if a request arrives while another one is pending, and + * the backend is responsible for ensuring that this does not happen. */ + VBOXCLIPBOARDREQFROMVBOX *pReq; + + /** Pointer to the opaque X11 backend structure */ + CLIPBACKEND *pBackend; + /** Pointer to the VBox host client data structure. */ + VBOXCLIPBOARDCLIENTDATA *pClient; + /** We set this when we start shutting down as a hint not to post any new + * requests. */ + bool fShuttingDown; +}; + + + +/** + * Report formats available in the X11 clipboard to VBox. + * @param pCtx Opaque context pointer for the glue code + * @param u32Formats The formats available + * @note Host glue code + */ +void ClipReportX11Formats(VBOXCLIPBOARDCONTEXT *pCtx, + uint32_t u32Formats) +{ + LogRelFlowFunc(("called. pCtx=%p, u32Formats=%02X\n", pCtx, u32Formats)); + vboxSvcClipboardReportMsg(pCtx->pClient, + VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, + u32Formats); +} + +/** + * Initialise the host side of the shared clipboard. + * @note Host glue code + */ +int vboxClipboardInit (void) +{ + return VINF_SUCCESS; +} + +/** + * Terminate the host side of the shared clipboard. + * @note host glue code + */ +void vboxClipboardDestroy (void) +{ + +} + +/** + * Connect a guest to the shared clipboard. + * @note host glue code + * @note on the host, we assume that some other application already owns + * the clipboard and leave ownership to X11. + */ +int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless) +{ + int rc = VINF_SUCCESS; + CLIPBACKEND *pBackend = NULL; + + LogRel(("Starting host clipboard service\n")); + VBOXCLIPBOARDCONTEXT *pCtx = + (VBOXCLIPBOARDCONTEXT *) RTMemAllocZ(sizeof(VBOXCLIPBOARDCONTEXT)); + if (!pCtx) + rc = VERR_NO_MEMORY; + else + { + RTCritSectInit(&pCtx->clipboardMutex); + pBackend = ClipConstructX11(pCtx, fHeadless); + if (pBackend == NULL) + rc = VERR_NO_MEMORY; + else + { + pCtx->pBackend = pBackend; + pClient->pCtx = pCtx; + pCtx->pClient = pClient; + rc = ClipStartX11(pBackend, true /* grab shared clipboard */); + } + if (RT_FAILURE(rc)) + RTCritSectDelete(&pCtx->clipboardMutex); + } + if (RT_FAILURE(rc)) + { + RTMemFree(pCtx); + LogRel(("Failed to initialise the shared clipboard\n")); + } + LogRelFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +/** + * Synchronise the contents of the host clipboard with the guest, called + * after a save and restore of the guest. + * @note Host glue code + */ +int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient) +{ + /* Tell the guest we have no data in case X11 is not available. If + * there is data in the host clipboard it will automatically be sent to + * the guest when the clipboard starts up. */ + vboxSvcClipboardReportMsg (pClient, + VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, 0); + return VINF_SUCCESS; +} + +/** + * Shut down the shared clipboard service and "disconnect" the guest. + * @note Host glue code + */ +void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient) +{ + LogRelFlow(("vboxClipboardDisconnect\n")); + + LogRel(("Stopping the host clipboard service\n")); + VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx; + /* Drop the reference to the client, in case it is still there. This + * will cause any outstanding clipboard data requests from X11 to fail + * immediately. */ + pCtx->fShuttingDown = true; + /* If there is a currently pending request, release it immediately. */ + vboxClipboardWriteData(pClient, NULL, 0, 0); + int rc = ClipStopX11(pCtx->pBackend); + /** @todo handle this slightly more reasonably, or be really sure + * it won't go wrong. */ + AssertRC(rc); + if (RT_SUCCESS(rc)) /* And if not? */ + { + ClipDestructX11(pCtx->pBackend); + RTCritSectDelete(&pCtx->clipboardMutex); + RTMemFree(pCtx); + } +} + +/** + * VBox is taking possession of the shared clipboard. + * + * @param pClient Context data for the guest system + * @param u32Formats Clipboard formats the guest is offering + * @note Host glue code + */ +void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, + uint32_t u32Formats) +{ + LogRelFlowFunc(("called. pClient=%p, u32Formats=%02X\n", pClient, + u32Formats)); + ClipAnnounceFormatToX11 (pClient->pCtx->pBackend, u32Formats); +} + +/** Structure describing a request for clipoard data from the guest. */ +struct _CLIPREADCBREQ +{ + /** Where to write the returned data to. */ + void *pv; + /** The size of the buffer in pv */ + uint32_t cb; + /** The actual size of the data written */ + uint32_t *pcbActual; +}; + +/** + * Called when VBox wants to read the X11 clipboard. + * + * @returns VINF_SUCCESS on successful completion + * @returns VINF_HGCM_ASYNC_EXECUTE if the operation will complete + * asynchronously + * @returns iprt status code on failure + * @param pClient Context information about the guest VM + * @param u32Format The format that the guest would like to receive the data in + * @param pv Where to write the data to + * @param cb The size of the buffer to write the data to + * @param pcbActual Where to write the actual size of the written data + * @note We always fail or complete asynchronously + * @note On success allocates a CLIPREADCBREQ structure which must be + * freed in ClipCompleteDataRequestFromX11 when it is called back from + * the backend code. + * + */ +int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, + uint32_t u32Format, void *pv, uint32_t cb, + uint32_t *pcbActual) +{ + LogRelFlowFunc(("pClient=%p, u32Format=%02X, pv=%p, cb=%u, pcbActual=%p", + pClient, u32Format, pv, cb, pcbActual)); + + int rc = VINF_SUCCESS; + CLIPREADCBREQ *pReq = (CLIPREADCBREQ *) RTMemAlloc(sizeof(CLIPREADCBREQ)); + if (!pReq) + rc = VERR_NO_MEMORY; + else + { + pReq->pv = pv; + pReq->cb = cb; + pReq->pcbActual = pcbActual; + rc = ClipRequestDataFromX11(pClient->pCtx->pBackend, u32Format, pReq); + if (RT_SUCCESS(rc)) + rc = VINF_HGCM_ASYNC_EXECUTE; + } + LogRelFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +/** + * Complete a request from VBox for the X11 clipboard data. The data should + * be written to the buffer provided in the initial request. + * @param pCtx request context information + * @param rc the completion status of the request + * @param pReq request + * @param pv address + * @param cb size + * + * @todo change this to deal with the buffer issues rather than offloading + * them onto the caller + */ +void ClipCompleteDataRequestFromX11(VBOXCLIPBOARDCONTEXT *pCtx, int rc, + CLIPREADCBREQ *pReq, void *pv, uint32_t cb) +{ + if (cb <= pReq->cb && cb != 0) + memcpy(pReq->pv, pv, cb); + RTMemFree(pReq); + vboxSvcClipboardCompleteReadData(pCtx->pClient, rc, cb); +} + +/** A request for clipboard data from VBox */ +struct _VBOXCLIPBOARDREQFROMVBOX +{ + /** Data received */ + void *pv; + /** The size of the data */ + uint32_t cb; + /** Format of the data */ + uint32_t format; + /** A semaphore for waiting for the data */ + RTSEMEVENT finished; +}; + +/** Wait for clipboard data requested from VBox to arrive. */ +static int clipWaitForDataFromVBox(VBOXCLIPBOARDCONTEXT *pCtx, + VBOXCLIPBOARDREQFROMVBOX *pReq, + uint32_t u32Format) +{ + int rc = VINF_SUCCESS; + LogRelFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq, u32Format)); + /* Request data from VBox */ + vboxSvcClipboardReportMsg(pCtx->pClient, + VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, + u32Format); + /* Which will signal us when it is ready. We use a timeout here + * because we can't be sure that the guest will behave correctly. + */ + rc = RTSemEventWait(pReq->finished, CLIPBOARD_TIMEOUT); + /* If the request hasn't yet completed then we cancel it. We use + * the critical section to prevent these operations colliding. */ + RTCritSectEnter(&pCtx->clipboardMutex); + /* The data may have arrived between the semaphore timing out and + * our grabbing the mutex. */ + if (rc == VERR_TIMEOUT && pReq->pv != NULL) + rc = VINF_SUCCESS; + if (pCtx->pReq == pReq) + pCtx->pReq = NULL; + Assert(pCtx->pReq == NULL); + RTCritSectLeave(&pCtx->clipboardMutex); + if (RT_SUCCESS(rc) && (pReq->pv == NULL)) + rc = VERR_NO_DATA; + LogRelFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +/** Post a request for clipboard data to VBox/the guest and wait for it to be + * completed. */ +static int clipRequestDataFromVBox(VBOXCLIPBOARDCONTEXT *pCtx, + VBOXCLIPBOARDREQFROMVBOX *pReq, + uint32_t u32Format) +{ + int rc = VINF_SUCCESS; + LogRelFlowFunc(("pCtx=%p, pReq=%p, u32Format=%02X\n", pCtx, pReq, + u32Format)); + /* Start by "posting" the request for the next invocation of + * vboxClipboardWriteData. */ + RTCritSectEnter(&pCtx->clipboardMutex); + if (pCtx->pReq != NULL) + { + /* This would be a violation of the protocol, see the comments in the + * context structure definition. */ + Assert(false); + rc = VERR_WRONG_ORDER; + } + else + pCtx->pReq = pReq; + RTCritSectLeave(&pCtx->clipboardMutex); + if (RT_SUCCESS(rc)) + rc = clipWaitForDataFromVBox(pCtx, pReq, u32Format); + LogRelFlowFunc(("returning %Rrc\n", rc)); + return rc; +} + +/** + * Send a request to VBox to transfer the contents of its clipboard to X11. + * + * @param pCtx Pointer to the host clipboard structure + * @param u32Format The format in which the data should be transferred + * @param ppv On success and if pcb > 0, this will point to a buffer + * to be freed with RTMemFree containing the data read. + * @param pcb On success, this contains the number of bytes of data + * returned + * @note Host glue code. + */ +int ClipRequestDataForX11 (VBOXCLIPBOARDCONTEXT *pCtx, + uint32_t u32Format, void **ppv, + uint32_t *pcb) +{ + VBOXCLIPBOARDREQFROMVBOX request = { NULL, 0, 0, NIL_RTSEMEVENT }; + + LogRelFlowFunc(("pCtx=%p, u32Format=%02X, ppv=%p, pcb=%p\n", pCtx, + u32Format, ppv, pcb)); + if (pCtx->fShuttingDown) + { + /* The shared clipboard is disconnecting. */ + LogRelFunc(("host requested guest clipboard data after guest had disconnected.\n")); + return VERR_WRONG_ORDER; + } + int rc = RTSemEventCreate(&request.finished); + if (RT_SUCCESS(rc)) + { + rc = clipRequestDataFromVBox(pCtx, &request, u32Format); + RTSemEventDestroy(request.finished); + } + if (RT_SUCCESS(rc)) + { + *ppv = request.pv; + *pcb = request.cb; + } + LogRelFlowFunc(("returning %Rrc\n", rc)); + if (RT_SUCCESS(rc)) + LogRelFlowFunc(("*ppv=%.*ls, *pcb=%u\n", *pcb / 2, *ppv, *pcb)); + return rc; +} + +/** + * Called when we have requested data from VBox and that data has arrived. + * + * @param pClient Context information about the guest VM + * @param pv Buffer to which the data was written + * @param cb The size of the data written + * @param u32Format The format of the data written + * @note Host glue code + */ +void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, + void *pv, uint32_t cb, uint32_t u32Format) +{ + LogRelFlowFunc (("called. pClient=%p, pv=%p (%.*ls), cb=%u, u32Format=%02X\n", + pClient, pv, cb / 2, pv, cb, u32Format)); + + VBOXCLIPBOARDCONTEXT *pCtx = pClient->pCtx; + /* Grab the mutex and check whether there is a pending request for data. + */ + RTCritSectEnter(&pCtx->clipboardMutex); + VBOXCLIPBOARDREQFROMVBOX *pReq = pCtx->pReq; + if (pReq != NULL) + { + if (cb > 0) + { + pReq->pv = RTMemDup(pv, cb); + if (pReq->pv != NULL) /* NULL may also mean no memory... */ + { + pReq->cb = cb; + pReq->format = u32Format; + } + } + /* Signal that the request has been completed. */ + RTSemEventSignal(pReq->finished); + pCtx->pReq = NULL; + } + RTCritSectLeave(&pCtx->clipboardMutex); +} + +#ifdef TESTCASE +#include <iprt/initterm.h> +#include <iprt/stream.h> + +#define TEST_NAME "tstClipboardX11-2" + +struct _CLIPBACKEND +{ + uint32_t formats; + struct _READDATA + { + uint32_t format; + int rc; + CLIPREADCBREQ *pReq; + } readData; + struct _COMPLETEREAD + { + int rc; + uint32_t cbActual; + } completeRead; + struct _WRITEDATA + { + void *pv; + uint32_t cb; + uint32_t format; + bool timeout; + } writeData; + struct _REPORTDATA + { + uint32_t format; + } reportData; +}; + +void vboxSvcClipboardReportMsg (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats) +{ + RT_NOREF1(u32Formats); + CLIPBACKEND *pBackend = pClient->pCtx->pBackend; + if ( (u32Msg == VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA) + && !pBackend->writeData.timeout) + vboxClipboardWriteData(pClient, pBackend->writeData.pv, + pBackend->writeData.cb, + pBackend->writeData.format); + else + return; +} + +void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual) +{ + CLIPBACKEND *pBackend = pClient->pCtx->pBackend; + pBackend->completeRead.rc = rc; + pBackend->completeRead.cbActual = cbActual; +} + +CLIPBACKEND *ClipConstructX11(VBOXCLIPBOARDCONTEXT *pFrontend, bool) +{ + RT_NOREF1(pFrontend); + return (CLIPBACKEND *)RTMemAllocZ(sizeof(CLIPBACKEND)); +} + +void ClipDestructX11(CLIPBACKEND *pBackend) +{ + RTMemFree(pBackend); +} + +int ClipStartX11(CLIPBACKEND *pBackend, bool) +{ + RT_NOREF1(pBackend); + return VINF_SUCCESS; +} + +int ClipStopX11(CLIPBACKEND *pBackend) +{ + RT_NOREF1(pBackend); + return VINF_SUCCESS; +} + +void ClipAnnounceFormatToX11(CLIPBACKEND *pBackend, + uint32_t u32Formats) +{ + pBackend->formats = u32Formats; +} + +extern int ClipRequestDataFromX11(CLIPBACKEND *pBackend, uint32_t u32Format, + CLIPREADCBREQ *pReq) +{ + pBackend->readData.format = u32Format; + pBackend->readData.pReq = pReq; + return pBackend->readData.rc; +} + +int main() +{ + VBOXCLIPBOARDCLIENTDATA client; + unsigned cErrors = 0; + int rc = RTR3InitExeNoArguments(0); + RTPrintf(TEST_NAME ": TESTING\n"); + AssertRCReturn(rc, 1); + rc = vboxClipboardConnect(&client, false); + CLIPBACKEND *pBackend = client.pCtx->pBackend; + AssertRCReturn(rc, 1); + vboxClipboardFormatAnnounce(&client, + VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT); + if (pBackend->formats != VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT) + { + RTPrintf(TEST_NAME ": vboxClipboardFormatAnnounce failed with VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT\n"); + ++cErrors; + } + pBackend->readData.rc = VINF_SUCCESS; + client.asyncRead.callHandle = (VBOXHGCMCALLHANDLE)pBackend; + client.asyncRead.paParms = (VBOXHGCMSVCPARM *)&client; + uint32_t u32Dummy; + rc = vboxClipboardReadData(&client, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, + &u32Dummy, 42, &u32Dummy); + if (rc != VINF_HGCM_ASYNC_EXECUTE) + { + RTPrintf(TEST_NAME ": vboxClipboardReadData returned %Rrc\n", rc); + ++cErrors; + } + else + { + if ( pBackend->readData.format != + VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT + || pBackend->readData.pReq->pv != &u32Dummy + || pBackend->readData.pReq->cb != 42 + || pBackend->readData.pReq->pcbActual != &u32Dummy) + { + RTPrintf(TEST_NAME ": format=%u, pReq->pv=%p, pReq->cb=%u, pReq->pcbActual=%p\n", + pBackend->readData.format, pBackend->readData.pReq->pv, + pBackend->readData.pReq->cb, + pBackend->readData.pReq->pcbActual); + ++cErrors; + } + else + { + ClipCompleteDataRequestFromX11(client.pCtx, VERR_NO_DATA, + pBackend->readData.pReq, NULL, 43); + if ( pBackend->completeRead.rc != VERR_NO_DATA + || pBackend->completeRead.cbActual != 43) + { + RTPrintf(TEST_NAME ": rc=%Rrc, cbActual=%u\n", + pBackend->completeRead.rc, + pBackend->completeRead.cbActual); + ++cErrors; + } + } + } + void *pv; + uint32_t cb; + pBackend->writeData.pv = (void *)"testing"; + pBackend->writeData.cb = sizeof("testing"); + pBackend->writeData.format = 1234; + pBackend->reportData.format = 4321; /* XX this should be handled! */ + rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb); + if ( rc != VINF_SUCCESS + || strcmp((const char *)pv, "testing") != 0 + || cb != sizeof("testing")) + { + RTPrintf("rc=%Rrc, pv=%p, cb=%u\n", rc, pv, cb); + ++cErrors; + } + else + RTMemFree(pv); + pBackend->writeData.timeout = true; + rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb); + if (rc != VERR_TIMEOUT) + { + RTPrintf("rc=%Rrc, expected VERR_TIMEOUT\n", rc); + ++cErrors; + } + pBackend->writeData.pv = NULL; + pBackend->writeData.cb = 0; + pBackend->writeData.timeout = false; + rc = ClipRequestDataForX11(client.pCtx, 23, &pv, &cb); + if (rc != VERR_NO_DATA) + { + RTPrintf("rc=%Rrc, expected VERR_NO_DATA\n", rc); + ++cErrors; + } + /* Data arriving after a timeout should *not* cause any segfaults or + * memory leaks. Check with Valgrind! */ + vboxClipboardWriteData(&client, (void *)"tested", sizeof("tested"), 999); + vboxClipboardDisconnect(&client); + if (cErrors > 0) + RTPrintf(TEST_NAME ": errors: %u\n", cErrors); + return cErrors > 0 ? 1 : 0; +} +#endif /* TESTCASE */ diff --git a/src/VBox/HostServices/SharedClipboard/x11-stub.cpp b/src/VBox/HostServices/SharedClipboard/x11-stub.cpp new file mode 100644 index 00000000..756afdda --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/x11-stub.cpp @@ -0,0 +1,137 @@ +/* $Id: x11-stub.cpp $*/ +/** @file + * Shared Clipboard Service - Linux host, a stub version with no functionality for use on headless hosts. + */ + +/* + * Copyright (C) 2006-2019 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_SHARED_CLIPBOARD +#include <VBox/HostServices/VBoxClipboardSvc.h> + +#include <iprt/alloc.h> +#include <iprt/asm.h> /* For atomic operations */ +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include <iprt/process.h> +#include <iprt/semaphore.h> +#include <string.h> +#include <stdio.h> +#include <stdint.h> + +#include "VBoxClipboard.h" + + + +/** Initialise the host side of the shared clipboard - called by the hgcm layer. */ +int vboxClipboardInit (void) +{ + LogFlowFunc(("called, returning VINF_SUCCESS.\n")); + return VINF_SUCCESS; +} + +/** Terminate the host side of the shared clipboard - called by the hgcm layer. */ +void vboxClipboardDestroy (void) +{ + LogFlowFunc(("called, returning.\n")); +} + +/** + * Enable the shared clipboard - called by the hgcm clipboard subsystem. + * + * @param pClient Structure containing context information about the guest system + * @param fHeadless Whether headless. + * @returns RT status code + */ +int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, + bool fHeadless) +{ + RT_NOREF(pClient, fHeadless); + LogFlowFunc(("called, returning VINF_SUCCESS.\n")); + return VINF_SUCCESS; +} + +/** + * Synchronise the contents of the host clipboard with the guest, called by the HGCM layer + * after a save and restore of the guest. + */ +int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA * /* pClient */) +{ + LogFlowFunc(("called, returning VINF_SUCCESS.\n")); + return VINF_SUCCESS; +} + +/** + * Shut down the shared clipboard subsystem and "disconnect" the guest. + * + * @param pClient Structure containing context information about the guest system + */ +void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient) +{ + RT_NOREF(pClient); + LogFlowFunc(("called, returning.\n")); +} + +/** + * The guest is taking possession of the shared clipboard. Called by the HGCM clipboard + * subsystem. + * + * @param pClient Context data for the guest system + * @param u32Formats Clipboard formats the guest is offering + */ +void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, + uint32_t u32Formats) +{ + RT_NOREF(pClient, u32Formats); + LogFlowFunc(("called, returning.\n")); +} + +/** + * Called by the HGCM clipboard subsystem when the guest wants to read the host clipboard. + * + * @param pClient Context information about the guest VM + * @param u32Format The format that the guest would like to receive the data in + * @param pv Where to write the data to + * @param cb The size of the buffer to write the data to + * @param pcbActual Where to write the actual size of the written data + */ +int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, + void *pv, uint32_t cb, uint32_t *pcbActual) +{ + RT_NOREF(pClient, u32Format, pv, cb); + LogFlowFunc(("called, returning VINF_SUCCESS.\n")); + /* No data available. */ + *pcbActual = 0; + return VINF_SUCCESS; +} + +/** + * Called by the HGCM clipboard subsystem when we have requested data and that data arrives. + * + * @param pClient Context information about the guest VM + * @param pv Buffer to which the data was written + * @param cb The size of the data written + * @param u32Format The format of the data written + */ +void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, + uint32_t cb, uint32_t u32Format) +{ + RT_NOREF(pClient, pv, cb, u32Format); + LogFlowFunc(("called, returning.\n")); +} + diff --git a/src/VBox/HostServices/SharedFolders/Makefile.kmk b/src/VBox/HostServices/SharedFolders/Makefile.kmk new file mode 100644 index 00000000..1ac6845b --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/Makefile.kmk @@ -0,0 +1,54 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Shared Folders Host Service. +# + +# +# Copyright (C) 2006-2019 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. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# Include sub-makefile(s). +include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk + +# +# The shared folder service DLL. +# +DLLS += VBoxSharedFolders +VBoxSharedFolders_TEMPLATE = VBOXR3 +VBoxSharedFolders_NAME.os2 = VBoxSFld +VBoxSharedFolders_DEFS = VBOX_WITH_HGCM RTSHFL +VBoxSharedFolders_INCS.win = \ + $(VBOX_PATH_SDK) + +VBoxSharedFolders_LDFLAGS.darwin = \ + -framework Carbon \ + -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxSharedFolders.dylib + +VBoxSharedFolders_SOURCES = \ + VBoxSharedFoldersSvc.cpp \ + shflhandle.cpp \ + vbsf.cpp \ + vbsfpath.cpp \ + vbsfpathabs.cpp \ + mappings.cpp +VBoxSharedFolders_SOURCES.win = \ + VBoxSharedFoldersSvc.rc + +VBoxSharedFolders_LIBS = \ + $(LIB_VMM) \ + $(LIB_RUNTIME) \ + $(LIB_REM) + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp new file mode 100644 index 00000000..04fe0b1b --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.cpp @@ -0,0 +1,1838 @@ +/* $Id: VBoxSharedFoldersSvc.cpp $ */ +/** @file + * Shared Folders - Host service entry points. + */ + +/* + * Copyright (C) 2006-2019 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_SHARED_FOLDERS +#include <VBox/shflsvc.h> + +#include "shfl.h" +#include "mappings.h" +#include "shflhandle.h" +#include "vbsf.h" +#include <iprt/alloc.h> +#include <iprt/string.h> +#include <iprt/assert.h> +#include <VBox/AssertGuest.h> +#include <VBox/vmm/ssm.h> +#include <VBox/vmm/pdmifs.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define SHFL_SAVED_STATE_VERSION_FOLDERNAME_UTF16 2 +#define SHFL_SAVED_STATE_VERSION_PRE_AUTO_MOUNT_POINT 3 +#define SHFL_SAVED_STATE_VERSION 4 + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +PVBOXHGCMSVCHELPERS g_pHelpers; +static PPDMLED g_pStatusLed = NULL; + +/** @name Shared folder statistics. + * @{ */ +static STAMPROFILE g_StatQueryMappings; +static STAMPROFILE g_StatQueryMappingsFail; +static STAMPROFILE g_StatQueryMapName; +static STAMPROFILE g_StatCreate; +static STAMPROFILE g_StatCreateFail; +static STAMPROFILE g_StatLookup; +static STAMPROFILE g_StatLookupFail; +static STAMPROFILE g_StatClose; +static STAMPROFILE g_StatCloseFail; +static STAMPROFILE g_StatRead; +static STAMPROFILE g_StatReadFail; +static STAMPROFILE g_StatWrite; +static STAMPROFILE g_StatWriteFail; +static STAMPROFILE g_StatLock; +static STAMPROFILE g_StatLockFail; +static STAMPROFILE g_StatList; +static STAMPROFILE g_StatListFail; +static STAMPROFILE g_StatReadLink; +static STAMPROFILE g_StatReadLinkFail; +static STAMPROFILE g_StatMapFolderOld; +static STAMPROFILE g_StatMapFolder; +static STAMPROFILE g_StatMapFolderFail; +static STAMPROFILE g_StatUnmapFolder; +static STAMPROFILE g_StatUnmapFolderFail; +static STAMPROFILE g_StatInformationFail; +static STAMPROFILE g_StatInformationSetFile; +static STAMPROFILE g_StatInformationSetFileFail; +static STAMPROFILE g_StatInformationSetSize; +static STAMPROFILE g_StatInformationSetSizeFail; +static STAMPROFILE g_StatInformationGetFile; +static STAMPROFILE g_StatInformationGetFileFail; +static STAMPROFILE g_StatInformationGetVolume; +static STAMPROFILE g_StatInformationGetVolumeFail; +static STAMPROFILE g_StatRemove; +static STAMPROFILE g_StatRemoveFail; +static STAMPROFILE g_StatRename; +static STAMPROFILE g_StatRenameFail; +static STAMPROFILE g_StatFlush; +static STAMPROFILE g_StatFlushFail; +static STAMPROFILE g_StatSetUtf8; +static STAMPROFILE g_StatSetFileSize; +static STAMPROFILE g_StatSetFileSizeFail; +static STAMPROFILE g_StatSymlink; +static STAMPROFILE g_StatSymlinkFail; +static STAMPROFILE g_StatSetSymlinks; +static STAMPROFILE g_StatQueryMapInfo; +static STAMPROFILE g_StatWaitForMappingsChanges; +static STAMPROFILE g_StatWaitForMappingsChangesFail; +static STAMPROFILE g_StatCancelMappingsChangesWait; +static STAMPROFILE g_StatUnknown; +static STAMPROFILE g_StatMsgStage1; +/** @} */ + + +/** @page pg_shfl_svc Shared Folders Host Service + * + * Shared Folders map a host file system to guest logical filesystem. + * A mapping represents 'host name'<->'guest name' translation and a root + * identifier to be used to access this mapping. + * Examples: "C:\WINNT"<->"F:", "C:\WINNT\System32"<->"/mnt/host/system32". + * + * Therefore, host name and guest name are strings interpreted + * only by host service and guest client respectively. Host name is + * passed to guest only for informational purpose. Guest may for example + * display the string or construct volume label out of the string. + * + * Root identifiers are unique for whole guest life, + * that is until next guest reset/fresh start. + * 32 bit value incremented for each new mapping is used. + * + * Mapping strings are taken from VM XML configuration on VM startup. + * The service DLL takes mappings during initialization. There is + * also API for changing mappings at runtime. + * + * Current mappings and root identifiers are saved when VM is saved. + * + * Guest may use any of these mappings. Full path information + * about an object on a mapping consists of the root identifier and + * a full path of object. + * + * Guest IFS connects to the service and calls SHFL_FN_QUERY_MAP + * function which returns current mappings. For guest convenience, + * removed mappings also returned with REMOVED flag and new mappings + * are marked with NEW flag. + * + * To access host file system guest just forwards file system calls + * to the service, and specifies full paths or handles for objects. + * + * + */ + + + +static DECLCALLBACK(int) svcUnload (void *) +{ + int rc = VINF_SUCCESS; + + Log(("svcUnload\n")); + vbsfFreeHandleTable(); + + if (g_pHelpers) + HGCMSvcHlpStamDeregister(g_pHelpers, "/HGCM/VBoxSharedFolders/*"); + return rc; +} + +static DECLCALLBACK(int) svcConnect (void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring) +{ + RT_NOREF(u32ClientID, fRequestor, fRestoring); + SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient; + Log(("SharedFolders host service: connected, u32ClientID = %u\n", u32ClientID)); + + pClient->fHasMappingCounts = true; + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) svcDisconnect (void *, uint32_t u32ClientID, void *pvClient) +{ + RT_NOREF1(u32ClientID); + int rc = VINF_SUCCESS; + SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient; + + Log(("SharedFolders host service: disconnected, u32ClientID = %u\n", u32ClientID)); + + vbsfDisconnect(pClient); + return rc; +} + +/** @note We only save as much state as required to access the shared folder again after restore. + * All I/O requests pending at the time of saving will never be completed or result in errors. + * (file handles no longer valid etc) + * This works as designed at the moment. A full state save would be difficult and not always possible + * as the contents of a shared folder might change in between save and restore. + */ +static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM) +{ +#ifndef UNITTEST /* Read this as not yet tested */ + RT_NOREF1(u32ClientID); + SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient; + + Log(("SharedFolders host service: saving state, u32ClientID = %u\n", u32ClientID)); + + int rc = SSMR3PutU32(pSSM, SHFL_SAVED_STATE_VERSION); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, SHFL_MAX_MAPPINGS); + AssertRCReturn(rc, rc); + + /* Save client structure length & contents */ + rc = SSMR3PutU32(pSSM, sizeof(*pClient)); + AssertRCReturn(rc, rc); + + rc = SSMR3PutMem(pSSM, pClient, sizeof(*pClient)); + AssertRCReturn(rc, rc); + + /* Save all the active mappings. */ + for (int i=0;i<SHFL_MAX_MAPPINGS;i++) + { + /* Mapping are saved in the order of increasing root handle values. */ + MAPPING *pFolderMapping = vbsfMappingGetByRoot(i); + + rc = SSMR3PutU32(pSSM, pFolderMapping? pFolderMapping->cMappings: 0); + AssertRCReturn(rc, rc); + + rc = SSMR3PutBool(pSSM, pFolderMapping? pFolderMapping->fValid: false); + AssertRCReturn(rc, rc); + + if (pFolderMapping && pFolderMapping->fValid) + { + uint32_t len = (uint32_t)strlen(pFolderMapping->pszFolderName); + SSMR3PutU32(pSSM, len); + SSMR3PutStrZ(pSSM, pFolderMapping->pszFolderName); + + len = ShflStringSizeOfBuffer(pFolderMapping->pMapName); + SSMR3PutU32(pSSM, len); + SSMR3PutMem(pSSM, pFolderMapping->pMapName, len); + + SSMR3PutBool(pSSM, pFolderMapping->fHostCaseSensitive); + + SSMR3PutBool(pSSM, pFolderMapping->fGuestCaseSensitive); + + len = ShflStringSizeOfBuffer(pFolderMapping->pAutoMountPoint); + SSMR3PutU32(pSSM, len); + rc = SSMR3PutMem(pSSM, pFolderMapping->pAutoMountPoint, len); + AssertRCReturn(rc, rc); + } + } + +#else + RT_NOREF3(u32ClientID, pvClient, pSSM); +#endif + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion) +{ +#ifndef UNITTEST /* Read this as not yet tested */ + RT_NOREF(u32ClientID, uVersion); + uint32_t nrMappings; + SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient; + uint32_t len, version; + + Log(("SharedFolders host service: loading state, u32ClientID = %u\n", u32ClientID)); + + int rc = SSMR3GetU32(pSSM, &version); + AssertRCReturn(rc, rc); + + if ( version > SHFL_SAVED_STATE_VERSION + || version < SHFL_SAVED_STATE_VERSION_FOLDERNAME_UTF16) + return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION; + + rc = SSMR3GetU32(pSSM, &nrMappings); + AssertRCReturn(rc, rc); + if (nrMappings != SHFL_MAX_MAPPINGS) + return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; + + /* Restore the client data (flags + path delimiter + mapping counts (new) at the moment) */ + rc = SSMR3GetU32(pSSM, &len); + AssertRCReturn(rc, rc); + + if (len == RT_UOFFSETOF(SHFLCLIENTDATA, acMappings)) + pClient->fHasMappingCounts = false; + else if (len != sizeof(*pClient)) + return SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, + "Saved SHFLCLIENTDATA size %u differs from current %u!\n", len, sizeof(*pClient)); + + rc = SSMR3GetMem(pSSM, pClient, len); + AssertRCReturn(rc, rc); + + /* We don't actually (fully) restore the state; we simply check if the current state is as we it expect it to be. */ + for (int i=0;i<SHFL_MAX_MAPPINGS;i++) + { + /* Load the saved mapping description and try to find it in the mappings. */ + MAPPING mapping; + RT_ZERO(mapping); + + /* restore the folder mapping counter. */ + rc = SSMR3GetU32(pSSM, &mapping.cMappings); + AssertRCReturn(rc, rc); + + rc = SSMR3GetBool(pSSM, &mapping.fValid); + AssertRCReturn(rc, rc); + + if (mapping.fValid) + { + uint32_t cb; + + /* Load the host path name. */ + rc = SSMR3GetU32(pSSM, &cb); + AssertRCReturn(rc, rc); + + char *pszFolderName; + if (version == SHFL_SAVED_STATE_VERSION_FOLDERNAME_UTF16) + { + AssertReturn(cb > SHFLSTRING_HEADER_SIZE && cb <= UINT16_MAX + SHFLSTRING_HEADER_SIZE && !(cb & 1), + SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, "Bad folder name size: %#x\n", cb)); + PSHFLSTRING pFolderName = (PSHFLSTRING)RTMemAlloc(cb); + AssertReturn(pFolderName != NULL, VERR_NO_MEMORY); + + rc = SSMR3GetMem(pSSM, pFolderName, cb); + AssertRCReturn(rc, rc); + AssertReturn(pFolderName->u16Size < cb && pFolderName->u16Length < pFolderName->u16Size, + SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, + "Bad folder name string: %#x/%#x cb=%#x\n", + pFolderName->u16Size, pFolderName->u16Length, cb)); + + rc = RTUtf16ToUtf8(pFolderName->String.ucs2, &pszFolderName); + RTMemFree(pFolderName); + AssertRCReturn(rc, rc); + } + else + { + pszFolderName = (char *)RTStrAlloc(cb + 1); + AssertReturn(pszFolderName, VERR_NO_MEMORY); + + rc = SSMR3GetStrZ(pSSM, pszFolderName, cb + 1); + AssertRCReturn(rc, rc); + mapping.pszFolderName = pszFolderName; + } + + /* Load the map name. */ + rc = SSMR3GetU32(pSSM, &cb); + AssertRCReturn(rc, rc); + AssertReturn(cb > SHFLSTRING_HEADER_SIZE && cb <= UINT16_MAX + SHFLSTRING_HEADER_SIZE && !(cb & 1), + SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, "Bad map name size: %#x\n", cb)); + + PSHFLSTRING pMapName = (PSHFLSTRING)RTMemAlloc(cb); + AssertReturn(pMapName != NULL, VERR_NO_MEMORY); + + rc = SSMR3GetMem(pSSM, pMapName, cb); + AssertRCReturn(rc, rc); + AssertReturn(pMapName->u16Size < cb && pMapName->u16Length < pMapName->u16Size, + SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, + "Bad map name string: %#x/%#x cb=%#x\n", + pMapName->u16Size, pMapName->u16Length, cb)); + + /* Load case sensitivity config. */ + rc = SSMR3GetBool(pSSM, &mapping.fHostCaseSensitive); + AssertRCReturn(rc, rc); + + rc = SSMR3GetBool(pSSM, &mapping.fGuestCaseSensitive); + AssertRCReturn(rc, rc); + + /* Load the auto mount point. */ + PSHFLSTRING pAutoMountPoint; + if (version > SHFL_SAVED_STATE_VERSION_PRE_AUTO_MOUNT_POINT) + { + rc = SSMR3GetU32(pSSM, &cb); + AssertRCReturn(rc, rc); + AssertReturn(cb > SHFLSTRING_HEADER_SIZE && cb <= UINT16_MAX + SHFLSTRING_HEADER_SIZE && !(cb & 1), + SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, "Bad auto mount point size: %#x\n", cb)); + + pAutoMountPoint = (PSHFLSTRING)RTMemAlloc(cb); + AssertReturn(pAutoMountPoint != NULL, VERR_NO_MEMORY); + + rc = SSMR3GetMem(pSSM, pAutoMountPoint, cb); + AssertRCReturn(rc, rc); + AssertReturn(pAutoMountPoint->u16Size < cb && pAutoMountPoint->u16Length < pAutoMountPoint->u16Size, + SSMR3SetLoadError(pSSM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED, RT_SRC_POS, + "Bad auto mount point string: %#x/%#x cb=%#x\n", + pAutoMountPoint->u16Size, pAutoMountPoint->u16Length, cb)); + + } + else + { + pAutoMountPoint = ShflStringDupUtf8(""); + AssertReturn(pAutoMountPoint, VERR_NO_MEMORY); + } + + mapping.pszFolderName = pszFolderName; + mapping.pMapName = pMapName; + mapping.pAutoMountPoint = pAutoMountPoint; + + /* 'i' is the root handle of the saved mapping. */ + rc = vbsfMappingLoaded (&mapping, i); + if (RT_FAILURE(rc)) + { + LogRel(("SharedFolders host service: %Rrc loading %d [%ls] -> [%s]\n", + rc, i, pMapName->String.ucs2, pszFolderName)); + } + + RTMemFree(pAutoMountPoint); + RTMemFree(pMapName); + RTStrFree(pszFolderName); + + AssertRCReturn(rc, rc); + } + } + Log(("SharedFolders host service: successfully loaded state\n")); +#else + RT_NOREF(u32ClientID, pvClient, pSSM, uVersion); +#endif + return VINF_SUCCESS; +} + +static DECLCALLBACK(void) svcCall (void *, VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, + uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival) +{ + RT_NOREF(u32ClientID, tsArrival); +#ifndef VBOX_WITHOUT_RELEASE_STATISTICS + uint64_t tsStart; + STAM_GET_TS(tsStart); + STAM_REL_PROFILE_ADD_PERIOD(&g_StatMsgStage1, tsStart - tsArrival); +#endif + Log(("SharedFolders host service: svcCall: u32ClientID = %u, fn = %u, cParms = %u, pparms = %p\n", u32ClientID, u32Function, cParms, paParms)); + + SHFLCLIENTDATA *pClient = (SHFLCLIENTDATA *)pvClient; + + bool fAsynchronousProcessing = false; + +#ifdef LOG_ENABLED + for (uint32_t i = 0; i < cParms; i++) + { + /** @todo parameters other than 32 bit */ + Log((" pparms[%d]: type %u, value %u\n", i, paParms[i].type, paParms[i].u.uint32)); + } +#endif + + int rc = VINF_SUCCESS; + PSTAMPROFILE pStat, pStatFail; + switch (u32Function) + { + case SHFL_FN_QUERY_MAPPINGS: + { + pStat = &g_StatQueryMappings; + pStatFail = &g_StatQueryMappingsFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_QUERY_MAPPINGS\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_QUERY_MAPPINGS) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* numberOfMappings */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* mappings */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + uint32_t fu32Flags = paParms[0].u.uint32; + uint32_t cMappings = paParms[1].u.uint32; + SHFLMAPPING *pMappings = (SHFLMAPPING *)paParms[2].u.pointer.addr; + uint32_t cbMappings = paParms[2].u.pointer.size; + + /* Verify parameters values. */ + if ( (fu32Flags & ~SHFL_MF_MASK) != 0 + || cbMappings / sizeof (SHFLMAPPING) != cMappings + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + if (fu32Flags & SHFL_MF_UTF8) + pClient->fu32Flags |= SHFL_CF_UTF8; + /// @todo r=bird: Someone please explain this amusing code (r63916): + //if (fu32Flags & SHFL_MF_AUTOMOUNT) + // pClient->fu32Flags |= SHFL_MF_AUTOMOUNT; + // + //rc = vbsfMappingsQuery(pClient, pMappings, &cMappings); + + rc = vbsfMappingsQuery(pClient, RT_BOOL(fu32Flags & SHFL_MF_AUTOMOUNT), pMappings, &cMappings); + if (RT_SUCCESS(rc)) + { + /* Report that there are more mappings to get if + * handed in buffer is too small. */ + if (paParms[1].u.uint32 < cMappings) + rc = VINF_BUFFER_OVERFLOW; + + /* Update parameters. */ + paParms[1].u.uint32 = cMappings; + } + } + } + + + } break; + + case SHFL_FN_QUERY_MAP_NAME: + { + pStatFail = pStat = &g_StatQueryMapName; + Log(("SharedFolders host service: svcCall: SHFL_FN_QUERY_MAP_NAME\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_QUERY_MAP_NAME) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* Root. */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* Name. */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLSTRING *pString = (SHFLSTRING *)paParms[1].u.pointer.addr; + + /* Verify parameters values. */ + if (!ShflStringIsValidOut(pString, paParms[1].u.pointer.size)) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + rc = vbsfMappingsQueryName(pClient, root, pString); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + ; /* None. */ + } + } + } + + } break; + + case SHFL_FN_CREATE: + { + pStat = &g_StatCreate; + pStatFail = &g_StatCreateFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_CREATE\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_CREATE) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* path */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parms */ + ) + { + Log(("SharedFolders host service: Invalid parameters types\n")); + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLSTRING *pPath = (SHFLSTRING *)paParms[1].u.pointer.addr; + uint32_t cbPath = paParms[1].u.pointer.size; + SHFLCREATEPARMS *pParms = (SHFLCREATEPARMS *)paParms[2].u.pointer.addr; + uint32_t cbParms = paParms[2].u.pointer.size; + + /* Verify parameters values. */ + if ( !ShflStringIsValidIn(pPath, cbPath, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) + || (cbParms != sizeof (SHFLCREATEPARMS)) + ) + { + AssertMsgFailed (("Invalid parameters cbPath or cbParms (%x, %x - expected >=%x, %x)\n", + cbPath, cbParms, sizeof(SHFLSTRING), sizeof (SHFLCREATEPARMS))); + rc = VERR_INVALID_PARAMETER; + } + else + { + if (pParms->CreateFlags & SHFL_CF_LOOKUP) + { + pStat = &g_StatLookup; + pStatFail = &g_StatLookupFail; + } + + /* Execute the function. */ + rc = vbsfCreate (pClient, root, pPath, cbPath, pParms); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + ; /* none */ + } + } + } + break; + } + + case SHFL_FN_CLOSE: + { + pStat = &g_StatClose; + pStatFail = &g_StatCloseFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_CLOSE\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_CLOSE) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLHANDLE Handle = paParms[1].u.uint64; + + /* Verify parameters values. */ + if (Handle == SHFL_HANDLE_ROOT) + { + rc = VERR_INVALID_PARAMETER; + } + else + if (Handle == SHFL_HANDLE_NIL) + { + AssertMsgFailed(("Invalid handle!\n")); + rc = VERR_INVALID_HANDLE; + } + else + { + /* Execute the function. */ + rc = vbsfClose (pClient, root, Handle); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + ; /* none */ + } + } + } + break; + + } + + /** Read object content. */ + case SHFL_FN_READ: + pStat = &g_StatRead; + pStatFail = &g_StatReadFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_READ\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_READ) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_64BIT /* offset */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* count */ + || paParms[4].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLHANDLE Handle = paParms[1].u.uint64; + uint64_t offset = paParms[2].u.uint64; + uint32_t count = paParms[3].u.uint32; + uint8_t *pBuffer = (uint8_t *)paParms[4].u.pointer.addr; + + /* Verify parameters values. */ + if ( Handle == SHFL_HANDLE_ROOT + || count > paParms[4].u.pointer.size + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + if (Handle == SHFL_HANDLE_NIL) + { + AssertMsgFailed(("Invalid handle!\n")); + rc = VERR_INVALID_HANDLE; + } + else + { + /* Execute the function. */ + if (g_pStatusLed) + { + Assert(g_pStatusLed->u32Magic == PDMLED_MAGIC); + g_pStatusLed->Asserted.s.fReading = g_pStatusLed->Actual.s.fReading = 1; + } + + rc = vbsfRead (pClient, root, Handle, offset, &count, pBuffer); + if (g_pStatusLed) + g_pStatusLed->Actual.s.fReading = 0; + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + paParms[3].u.uint32 = count; + } + else + { + paParms[3].u.uint32 = 0; /* nothing read */ + } + } + } + break; + + /** Write new object content. */ + case SHFL_FN_WRITE: + pStat = &g_StatWrite; + pStatFail = &g_StatWriteFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_WRITE\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_WRITE) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_64BIT /* offset */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* count */ + || paParms[4].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLHANDLE Handle = paParms[1].u.uint64; + uint64_t offset = paParms[2].u.uint64; + uint32_t count = paParms[3].u.uint32; + uint8_t *pBuffer = (uint8_t *)paParms[4].u.pointer.addr; + + /* Verify parameters values. */ + if ( Handle == SHFL_HANDLE_ROOT + || count > paParms[4].u.pointer.size + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + if (Handle == SHFL_HANDLE_NIL) + { + AssertMsgFailed(("Invalid handle!\n")); + rc = VERR_INVALID_HANDLE; + } + else + { + /* Execute the function. */ + if (g_pStatusLed) + { + Assert(g_pStatusLed->u32Magic == PDMLED_MAGIC); + g_pStatusLed->Asserted.s.fWriting = g_pStatusLed->Actual.s.fWriting = 1; + } + + rc = vbsfWrite (pClient, root, Handle, offset, &count, pBuffer); + if (g_pStatusLed) + g_pStatusLed->Actual.s.fWriting = 0; + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + paParms[3].u.uint32 = count; + } + else + { + paParms[3].u.uint32 = 0; /* nothing read */ + } + } + } + break; + + /** Lock/unlock a range in the object. */ + case SHFL_FN_LOCK: + pStat = &g_StatLock; + pStatFail = &g_StatLockFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_LOCK\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_LOCK) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_64BIT /* offset */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_64BIT /* length */ + || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLHANDLE Handle = paParms[1].u.uint64; + uint64_t offset = paParms[2].u.uint64; + uint64_t length = paParms[3].u.uint64; + uint32_t flags = paParms[4].u.uint32; + + /* Verify parameters values. */ + if (Handle == SHFL_HANDLE_ROOT) + { + rc = VERR_INVALID_PARAMETER; + } + else + if (Handle == SHFL_HANDLE_NIL) + { + AssertMsgFailed(("Invalid handle!\n")); + rc = VERR_INVALID_HANDLE; + } + else if (flags & SHFL_LOCK_WAIT) + { + /** @todo This should be properly implemented by the shared folders service. + * The service thread must never block. If an operation requires + * blocking, it must be processed by another thread and when it is + * completed, the another thread must call + * + * g_pHelpers->pfnCallComplete (callHandle, rc); + * + * The operation is async. + * fAsynchronousProcessing = true; + */ + + /* Here the operation must be posted to another thread. At the moment it is not implemented. + * Until it is implemented, try to perform the operation without waiting. + */ + flags &= ~SHFL_LOCK_WAIT; + + /* Execute the function. */ + if ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL) + rc = vbsfUnlock(pClient, root, Handle, offset, length, flags); + else + rc = vbsfLock(pClient, root, Handle, offset, length, flags); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + /* none */ + } + } + else + { + /* Execute the function. */ + if ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL) + rc = vbsfUnlock(pClient, root, Handle, offset, length, flags); + else + rc = vbsfLock(pClient, root, Handle, offset, length, flags); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + /* none */ + } + } + } + break; + + /** List object content. */ + case SHFL_FN_LIST: + { + pStat = &g_StatList; + pStatFail = &g_StatListFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_LIST\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_LIST) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* cb */ + || paParms[4].type != VBOX_HGCM_SVC_PARM_PTR /* pPath */ + || paParms[5].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */ + || paParms[6].type != VBOX_HGCM_SVC_PARM_32BIT /* resumePoint */ + || paParms[7].type != VBOX_HGCM_SVC_PARM_32BIT /* cFiles (out) */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLHANDLE Handle = paParms[1].u.uint64; + uint32_t flags = paParms[2].u.uint32; + uint32_t length = paParms[3].u.uint32; + SHFLSTRING *pPath = (paParms[4].u.pointer.size == 0) ? 0 : (SHFLSTRING *)paParms[4].u.pointer.addr; + uint8_t *pBuffer = (uint8_t *)paParms[5].u.pointer.addr; + uint32_t resumePoint = paParms[6].u.uint32; + uint32_t cFiles = 0; + + /* Verify parameters values. */ + if ( (length < sizeof (SHFLDIRINFO)) + || length > paParms[5].u.pointer.size + || !ShflStringIsValidOrNullIn(pPath, paParms[4].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + if (g_pStatusLed) + { + Assert(g_pStatusLed->u32Magic == PDMLED_MAGIC); + g_pStatusLed->Asserted.s.fReading = g_pStatusLed->Actual.s.fReading = 1; + } + + /* Execute the function. */ + rc = vbsfDirList (pClient, root, Handle, pPath, flags, &length, pBuffer, &resumePoint, &cFiles); + + if (g_pStatusLed) + g_pStatusLed->Actual.s.fReading = 0; + + if (rc == VERR_NO_MORE_FILES && cFiles != 0) + rc = VINF_SUCCESS; /* Successfully return these files. */ + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + paParms[3].u.uint32 = length; + paParms[6].u.uint32 = resumePoint; + paParms[7].u.uint32 = cFiles; + } + else + { + paParms[3].u.uint32 = 0; /* nothing read */ + paParms[6].u.uint32 = 0; + paParms[7].u.uint32 = cFiles; + } + } + } + break; + } + + /* Read symlink destination */ + case SHFL_FN_READLINK: + { + pStat = &g_StatReadLink; + pStatFail = &g_StatReadLinkFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_READLINK\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_READLINK) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* path */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLSTRING *pPath = (SHFLSTRING *)paParms[1].u.pointer.addr; + uint32_t cbPath = paParms[1].u.pointer.size; + uint8_t *pBuffer = (uint8_t *)paParms[2].u.pointer.addr; + uint32_t cbBuffer = paParms[2].u.pointer.size; + + /* Verify parameters values. */ + if (!ShflStringIsValidOrNullIn(pPath, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + rc = vbsfReadLink (pClient, root, pPath, cbPath, pBuffer, cbBuffer); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + ; /* none */ + } + } + } + + break; + } + + /* Legacy interface */ + case SHFL_FN_MAP_FOLDER_OLD: + { + pStatFail = pStat = &g_StatMapFolderOld; + Log(("SharedFolders host service: svcCall: SHFL_FN_MAP_FOLDER_OLD\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_MAP_FOLDER_OLD) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* path */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* delimiter */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + PSHFLSTRING pszMapName = (PSHFLSTRING)paParms[0].u.pointer.addr; + SHFLROOT root = (SHFLROOT)paParms[1].u.uint32; + RTUTF16 delimiter = (RTUTF16)paParms[2].u.uint32; + + /* Verify parameters values. */ + if (!ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + rc = vbsfMapFolder (pClient, pszMapName, delimiter, false, &root); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + paParms[1].u.uint32 = root; + } + } + } + break; + } + + case SHFL_FN_MAP_FOLDER: + { + pStat = &g_StatMapFolder; + pStatFail = &g_StatMapFolderFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_MAP_FOLDER\n")); + if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) + Log(("SharedFolders host service: request to map folder '%s'\n", + ((PSHFLSTRING)paParms[0].u.pointer.addr)->String.utf8)); + else + Log(("SharedFolders host service: request to map folder '%ls'\n", + ((PSHFLSTRING)paParms[0].u.pointer.addr)->String.ucs2)); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_MAP_FOLDER) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* path */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* delimiter */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* fCaseSensitive */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + PSHFLSTRING pszMapName = (PSHFLSTRING)paParms[0].u.pointer.addr; + SHFLROOT root = (SHFLROOT)paParms[1].u.uint32; + RTUTF16 delimiter = (RTUTF16)paParms[2].u.uint32; + bool fCaseSensitive = !!paParms[3].u.uint32; + + /* Verify parameters values. */ + if (ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))) + { + rc = VINF_SUCCESS; + } + else + { + rc = VERR_INVALID_PARAMETER; + + /* Fudge for windows GAs getting the length wrong by one char. */ + if ( !(pClient->fu32Flags & SHFL_CF_UTF8) + && paParms[0].u.pointer.size >= sizeof(SHFLSTRING) + && pszMapName->u16Length >= 2 + && pszMapName->String.ucs2[pszMapName->u16Length / 2 - 1] == 0x0000) + { + pszMapName->u16Length -= 2; + if (ShflStringIsValidIn(pszMapName, paParms[0].u.pointer.size, false /*fUtf8Not16*/)) + rc = VINF_SUCCESS; + else + pszMapName->u16Length += 2; + } + } + + /* Execute the function. */ + if (RT_SUCCESS(rc)) + rc = vbsfMapFolder (pClient, pszMapName, delimiter, fCaseSensitive, &root); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + paParms[1].u.uint32 = root; + } + } + Log(("SharedFolders host service: map operation result %Rrc\n", rc)); + if (RT_SUCCESS(rc)) + Log(("SharedFolders host service: mapped to handle %d\n", paParms[1].u.uint32)); + break; + } + + case SHFL_FN_UNMAP_FOLDER: + { + pStat = &g_StatUnmapFolder; + pStatFail = &g_StatUnmapFolderFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_UNMAP_FOLDER\n")); + Log(("SharedFolders host service: request to unmap folder handle %u\n", + paParms[0].u.uint32)); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_UNMAP_FOLDER) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + + /* Execute the function. */ + rc = vbsfUnmapFolder (pClient, root); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + /* nothing */ + } + } + Log(("SharedFolders host service: unmap operation result %Rrc\n", rc)); + break; + } + + /** Query/set object information. */ + case SHFL_FN_INFORMATION: + { + pStatFail = pStat = &g_StatInformationFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_INFORMATION\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_INFORMATION) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* cb */ + || paParms[4].type != VBOX_HGCM_SVC_PARM_PTR /* buffer */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLHANDLE Handle = paParms[1].u.uint64; + uint32_t flags = paParms[2].u.uint32; + uint32_t length = paParms[3].u.uint32; + uint8_t *pBuffer = (uint8_t *)paParms[4].u.pointer.addr; + + /* Verify parameters values. */ + if (length > paParms[4].u.pointer.size) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + if (flags & SHFL_INFO_SET) + { + rc = vbsfSetFSInfo (pClient, root, Handle, flags, &length, pBuffer); + + if (flags & SHFL_INFO_FILE) + { + pStat = &g_StatInformationSetFile; + pStatFail = &g_StatInformationSetFileFail; + } + else if (flags & SHFL_INFO_SIZE) + { + pStat = &g_StatInformationSetSize; + pStatFail = &g_StatInformationSetSizeFail; + } + } + else /* SHFL_INFO_GET */ + { + rc = vbsfQueryFSInfo (pClient, root, Handle, flags, &length, pBuffer); + + if (flags & SHFL_INFO_FILE) + { + pStat = &g_StatInformationGetFile; + pStatFail = &g_StatInformationGetFileFail; + } + else if (flags & SHFL_INFO_VOLUME) + { + pStat = &g_StatInformationGetVolume; + pStatFail = &g_StatInformationGetVolumeFail; + } + } + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + paParms[3].u.uint32 = length; + } + else + { + paParms[3].u.uint32 = 0; /* nothing read */ + } + } + } + break; + } + + /** Remove or rename object */ + case SHFL_FN_REMOVE: + { + pStat = &g_StatRemove; + pStatFail = &g_StatRemoveFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_REMOVE\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_REMOVE) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* path */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLSTRING *pPath = (SHFLSTRING *)paParms[1].u.pointer.addr; + uint32_t cbPath = paParms[1].u.pointer.size; + uint32_t flags = paParms[2].u.uint32; + + /* Verify parameters values. */ + if (!ShflStringIsValidIn(pPath, cbPath, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8))) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + rc = vbsfRemove (pClient, root, pPath, cbPath, flags); + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + ; /* none */ + } + } + } + break; + } + + case SHFL_FN_RENAME: + { + pStat = &g_StatRename; + pStatFail = &g_StatRenameFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_RENAME\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_RENAME) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* src */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* dest */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLSTRING *pSrc = (SHFLSTRING *)paParms[1].u.pointer.addr; + SHFLSTRING *pDest = (SHFLSTRING *)paParms[2].u.pointer.addr; + uint32_t flags = paParms[3].u.uint32; + + /* Verify parameters values. */ + if ( !ShflStringIsValidIn(pSrc, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) + || !ShflStringIsValidIn(pDest, paParms[2].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + rc = vbsfRename (pClient, root, pSrc, pDest, flags); + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + ; /* none */ + } + } + } + break; + } + + case SHFL_FN_FLUSH: + { + pStat = &g_StatFlush; + pStatFail = &g_StatFlushFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_FLUSH\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_FLUSH) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_64BIT /* handle */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLHANDLE Handle = paParms[1].u.uint64; + + /* Verify parameters values. */ + if (Handle == SHFL_HANDLE_ROOT) + { + rc = VERR_INVALID_PARAMETER; + } + else + if (Handle == SHFL_HANDLE_NIL) + { + AssertMsgFailed(("Invalid handle!\n")); + rc = VERR_INVALID_HANDLE; + } + else + { + /* Execute the function. */ + + rc = vbsfFlush (pClient, root, Handle); + + if (RT_SUCCESS(rc)) + { + /* Nothing to do */ + } + } + } + } break; + + case SHFL_FN_SET_UTF8: + { + pStatFail = pStat = &g_StatSetUtf8; + + pClient->fu32Flags |= SHFL_CF_UTF8; + rc = VINF_SUCCESS; + break; + } + + case SHFL_FN_SYMLINK: + { + pStat = &g_StatSymlink; + pStatFail = &g_StatSymlinkFail; + Log(("SharedFolders host service: svnCall: SHFL_FN_SYMLINK\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_SYMLINK) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* root */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* newPath */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* oldPath */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* info */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLROOT root = (SHFLROOT)paParms[0].u.uint32; + SHFLSTRING *pNewPath = (SHFLSTRING *)paParms[1].u.pointer.addr; + SHFLSTRING *pOldPath = (SHFLSTRING *)paParms[2].u.pointer.addr; + SHFLFSOBJINFO *pInfo = (SHFLFSOBJINFO *)paParms[3].u.pointer.addr; + uint32_t cbInfo = paParms[3].u.pointer.size; + + /* Verify parameters values. */ + if ( !ShflStringIsValidIn(pNewPath, paParms[1].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) + || !ShflStringIsValidIn(pOldPath, paParms[2].u.pointer.size, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) + || (cbInfo != sizeof(SHFLFSOBJINFO)) + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + rc = vbsfSymlink (pClient, root, pNewPath, pOldPath, pInfo); + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + ; /* none */ + } + } + } + } + break; + + case SHFL_FN_SET_SYMLINKS: + { + pStatFail = pStat = &g_StatSetSymlinks; + + pClient->fu32Flags |= SHFL_CF_SYMLINKS; + rc = VINF_SUCCESS; + break; + } + + case SHFL_FN_QUERY_MAP_INFO: + { + pStatFail = pStat = &g_StatQueryMapInfo; + Log(("SharedFolders host service: svnCall: SHFL_FN_QUERY_MAP_INFO\n")); + + /* Validate input: */ + rc = VERR_INVALID_PARAMETER; + ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_QUERY_MAP_INFO); + ASSERT_GUEST_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT); /* root */ + ASSERT_GUEST_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_PTR); /* name */ + PSHFLSTRING pNameBuf = (PSHFLSTRING)paParms[1].u.pointer.addr; + ASSERT_GUEST_BREAK(ShflStringIsValidOut(pNameBuf, paParms[1].u.pointer.size)); + ASSERT_GUEST_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_PTR); /* mountPoint */ + PSHFLSTRING pMntPtBuf = (PSHFLSTRING)paParms[2].u.pointer.addr; + ASSERT_GUEST_BREAK(ShflStringIsValidOut(pMntPtBuf, paParms[2].u.pointer.size)); + ASSERT_GUEST_BREAK(paParms[3].type == VBOX_HGCM_SVC_PARM_64BIT); /* flags */ + ASSERT_GUEST_BREAK(!(paParms[3].u.uint64 & ~(SHFL_MIQF_DRIVE_LETTER | SHFL_MIQF_PATH))); /* flags */ + ASSERT_GUEST_BREAK(paParms[4].type == VBOX_HGCM_SVC_PARM_32BIT); /* version */ + + /* Execute the function: */ + rc = vbsfMappingsQueryInfo(pClient, paParms[0].u.uint32, pNameBuf, pMntPtBuf, + &paParms[3].u.uint64, &paParms[4].u.uint32); + break; + } + + case SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES: + { + pStat = &g_StatWaitForMappingsChanges; + pStatFail = &g_StatWaitForMappingsChangesFail; + Log(("SharedFolders host service: svnCall: SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES\n")); + + /* Validate input: */ + rc = VERR_INVALID_PARAMETER; + ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_WAIT_FOR_MAPPINGS_CHANGES); + ASSERT_GUEST_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT); /* uFolderMappingsVersion */ + + /* Execute the function: */ + rc = vbsfMappingsWaitForChanges(pClient, callHandle, paParms, g_pHelpers->pfnIsCallRestored(callHandle)); + fAsynchronousProcessing = rc == VINF_HGCM_ASYNC_EXECUTE; + break; + } + + case SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS: + { + pStatFail = pStat = &g_StatCancelMappingsChangesWait; + Log(("SharedFolders host service: svnCall: SHFL_FN_CANCEL_WAIT_FOR_CHANGES\n")); + + /* Validate input: */ + rc = VERR_INVALID_PARAMETER; + ASSERT_GUEST_BREAK(cParms == SHFL_CPARMS_CANCEL_MAPPINGS_CHANGES_WAITS); + + /* Execute the function: */ + rc = vbsfMappingsCancelChangesWaits(pClient); + break; + } + + case SHFL_FN_SET_FILE_SIZE: + { + pStat = &g_StatSetFileSize; + pStatFail = &g_StatSetFileSizeFail; + Log(("SharedFolders host service: svcCall: SHFL_FN_SET_FILE_SIZE\n")); + + /* Validate input: */ + ASSERT_GUEST_STMT_BREAK(cParms == SHFL_CPARMS_SET_FILE_SIZE, rc = VERR_WRONG_PARAMETER_COUNT); + ASSERT_GUEST_STMT_BREAK(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* id32Root */ + ASSERT_GUEST_STMT_BREAK(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* u64Handle */ + ASSERT_GUEST_STMT_BREAK(paParms[2].type == VBOX_HGCM_SVC_PARM_64BIT, rc = VERR_WRONG_PARAMETER_TYPE); /* cb64NewSize */ + + /* Execute the function: */ + rc = vbsfSetFileSize(pClient, paParms[0].u.uint32, paParms[1].u.uint64, paParms[2].u.uint64); + break; + } + + default: + { + pStatFail = pStat = &g_StatUnknown; + rc = VERR_NOT_IMPLEMENTED; + break; + } + } + + LogFlow(("SharedFolders host service: svcCall: rc=%Rrc\n", rc)); + + if ( !fAsynchronousProcessing + || RT_FAILURE (rc)) + { + /* Complete the operation if it was unsuccessful or + * it was processed synchronously. + */ + g_pHelpers->pfnCallComplete (callHandle, rc); + } + +#ifndef VBOX_WITHOUT_RELEASE_STATISTICS + /* Statistics: */ + uint64_t cTicks; + STAM_GET_TS(cTicks); + cTicks -= tsStart; + if (RT_SUCCESS(rc)) + STAM_REL_PROFILE_ADD_PERIOD(pStat, cTicks); + else + STAM_REL_PROFILE_ADD_PERIOD(pStatFail, cTicks); +#endif + + LogFlow(("\n")); /* Add a new line to differentiate between calls more easily. */ +} + +/* + * We differentiate between a function handler for the guest (svcCall) and one + * for the host. The guest is not allowed to add or remove mappings for obvious + * security reasons. + */ +static DECLCALLBACK(int) svcHostCall (void *, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + int rc = VINF_SUCCESS; + + Log(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n", u32Function, cParms, paParms)); + +#ifdef DEBUG + uint32_t i; + + for (i = 0; i < cParms; i++) + { + /** @todo parameters other than 32 bit */ + Log((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32)); + } +#endif + + switch (u32Function) + { + case SHFL_FN_ADD_MAPPING: + { + Log(("SharedFolders host service: svcCall: SHFL_FN_ADD_MAPPING\n")); + LogRel(("SharedFolders host service: Adding host mapping\n")); + /* Verify parameter count and types. */ + if ( (cParms != SHFL_CPARMS_ADD_MAPPING) + ) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* host folder path */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* map name */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* fFlags */ + || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* auto mount point */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLSTRING *pHostPath = (SHFLSTRING *)paParms[0].u.pointer.addr; + SHFLSTRING *pMapName = (SHFLSTRING *)paParms[1].u.pointer.addr; + uint32_t fFlags = paParms[2].u.uint32; + SHFLSTRING *pAutoMountPoint = (SHFLSTRING *)paParms[3].u.pointer.addr; + + /* Verify parameters values. */ + if ( !ShflStringIsValidIn(pHostPath, paParms[0].u.pointer.size, false /*fUtf8Not16*/) + || !ShflStringIsValidIn(pMapName, paParms[1].u.pointer.size, false /*fUtf8Not16*/) + || !ShflStringIsValidIn(pAutoMountPoint, paParms[3].u.pointer.size, false /*fUtf8Not16*/) + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + LogRel((" Host path '%ls', map name '%ls', %s, automount=%s, automntpnt=%s, create_symlinks=%s, missing=%s\n", + pHostPath->String.utf16, pMapName->String.utf16, + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_WRITABLE) ? "writable" : "read-only", + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_AUTOMOUNT) ? "true" : "false", + pAutoMountPoint->String.utf16, + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_CREATE_SYMLINKS) ? "true" : "false", + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_MISSING) ? "true" : "false")); + + char *pszHostPath; + rc = RTUtf16ToUtf8(pHostPath->String.ucs2, &pszHostPath); + if (RT_SUCCESS(rc)) + { + /* Execute the function. */ + rc = vbsfMappingsAdd(pszHostPath, pMapName, + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_WRITABLE), + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_AUTOMOUNT), + pAutoMountPoint, + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_CREATE_SYMLINKS), + RT_BOOL(fFlags & SHFL_ADD_MAPPING_F_MISSING), + /* fPlaceholder = */ false); + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + ; /* none */ + } + RTStrFree(pszHostPath); + } + } + } + if (RT_FAILURE(rc)) + LogRel(("SharedFolders host service: Adding host mapping failed with rc=%Rrc\n", rc)); + break; + } + + case SHFL_FN_REMOVE_MAPPING: + { + Log(("SharedFolders host service: svcCall: SHFL_FN_REMOVE_MAPPING\n")); + LogRel(("SharedFolders host service: Removing host mapping '%ls'\n", + ((SHFLSTRING *)paParms[0].u.pointer.addr)->String.ucs2)); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_REMOVE_MAPPING) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* folder name */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + SHFLSTRING *pString = (SHFLSTRING *)paParms[0].u.pointer.addr; + + /* Verify parameters values. */ + if (!ShflStringIsValidIn(pString, paParms[0].u.pointer.size, false /*fUtf8Not16*/)) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + rc = vbsfMappingsRemove (pString); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + ; /* none */ + } + } + } + if (RT_FAILURE(rc)) + LogRel(("SharedFolders host service: Removing host mapping failed with rc=%Rrc\n", rc)); + break; + } + + case SHFL_FN_SET_STATUS_LED: + { + Log(("SharedFolders host service: svcCall: SHFL_FN_SET_STATUS_LED\n")); + + /* Verify parameter count and types. */ + if (cParms != SHFL_CPARMS_SET_STATUS_LED) + { + rc = VERR_INVALID_PARAMETER; + } + else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* folder name */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + PPDMLED pLed = (PPDMLED)paParms[0].u.pointer.addr; + uint32_t cbLed = paParms[0].u.pointer.size; + + /* Verify parameters values. */ + if ( (cbLed != sizeof (PDMLED)) + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + g_pStatusLed = pLed; + rc = VINF_SUCCESS; + } + } + break; + } + + default: + rc = VERR_NOT_IMPLEMENTED; + break; + } + + LogFlow(("SharedFolders host service: svcHostCall ended with rc=%Rrc\n", rc)); + return rc; +} + +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable) +{ + int rc = VINF_SUCCESS; + + Log(("SharedFolders host service: VBoxHGCMSvcLoad: ptable = %p\n", ptable)); + + if (!VALID_PTR(ptable)) + { + LogRelFunc(("SharedFolders host service: Bad value of ptable (%p)\n", ptable)); + rc = VERR_INVALID_PARAMETER; + } + else + { + Log(("SharedFolders host service: VBoxHGCMSvcLoad: ptable->cbSize = %u, ptable->u32Version = 0x%08X\n", + ptable->cbSize, ptable->u32Version)); + + if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE) + || ptable->u32Version != VBOX_HGCM_SVC_VERSION) + { + LogRelFunc(("SharedFolders host service: Version mismatch while loading: ptable->cbSize = %u (should be %u), ptable->u32Version = 0x%08X (should be 0x%08X)\n", + ptable->cbSize, sizeof (VBOXHGCMSVCFNTABLE), ptable->u32Version, VBOX_HGCM_SVC_VERSION)); + rc = VERR_VERSION_MISMATCH; + } + else + { + g_pHelpers = ptable->pHelpers; + + ptable->cbClient = sizeof (SHFLCLIENTDATA); + + ptable->pfnUnload = svcUnload; + ptable->pfnConnect = svcConnect; + ptable->pfnDisconnect = svcDisconnect; + ptable->pfnCall = svcCall; + ptable->pfnHostCall = svcHostCall; + ptable->pfnSaveState = svcSaveState; + ptable->pfnLoadState = svcLoadState; + ptable->pfnNotify = NULL; + ptable->pvService = NULL; + } + + /* Init handle table */ + rc = vbsfInitHandleTable(); + AssertRC(rc); + + vbsfMappingInit(); + + /* Finally, register statistics if everything went well: */ + if (RT_SUCCESS(rc)) + { + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMappings, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_QUERY_MAPPINGS successes", "/HGCM/VBoxSharedFolders/FnQueryMappings"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMappingsFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_QUERY_MAPPINGS failures", "/HGCM/VBoxSharedFolders/FnQueryMappingsFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMapName, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_QUERY_MAP_NAME", "/HGCM/VBoxSharedFolders/FnQueryMapName"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCreate, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CREATE/CREATE successes", "/HGCM/VBoxSharedFolders/FnCreate"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCreateFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CREATE/CREATE failures", "/HGCM/VBoxSharedFolders/FnCreateFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLookup, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CREATE/LOOKUP successes", "/HGCM/VBoxSharedFolders/FnLookup"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLookupFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CREATE/LOOKUP failures", "/HGCM/VBoxSharedFolders/FnLookupFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatClose, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CLOSE successes", "/HGCM/VBoxSharedFolders/FnClose"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCloseFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CLOSE failures", "/HGCM/VBoxSharedFolders/FnCloseFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRead, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_READ successes", "/HGCM/VBoxSharedFolders/FnRead"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatReadFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_READ failures", "/HGCM/VBoxSharedFolders/FnReadFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWrite, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_WRITE successes", "/HGCM/VBoxSharedFolders/FnWrite"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWriteFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_WRITE failures", "/HGCM/VBoxSharedFolders/FnWriteFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLock, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_LOCK successes", "/HGCM/VBoxSharedFolders/FnLock"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatLockFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_LOCK failures", "/HGCM/VBoxSharedFolders/FnLockFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatList, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_LIST successes", "/HGCM/VBoxSharedFolders/FnList"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatListFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_LIST failures", "/HGCM/VBoxSharedFolders/FnListFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatReadLink, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_READLINK successes", "/HGCM/VBoxSharedFolders/FnReadLink"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatReadLinkFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_READLINK failures", "/HGCM/VBoxSharedFolders/FnReadLinkFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMapFolderOld, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_MAP_FOLDER_OLD", "/HGCM/VBoxSharedFolders/FnMapFolderOld"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMapFolder, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_MAP_FOLDER successes", "/HGCM/VBoxSharedFolders/FnMapFolder"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMapFolderFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_MAP_FOLDER failures", "/HGCM/VBoxSharedFolders/FnMapFolderFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatUnmapFolder, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_UNMAP_FOLDER successes", "/HGCM/VBoxSharedFolders/FnUnmapFolder"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatUnmapFolderFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_UNMAP_FOLDER failures", "/HGCM/VBoxSharedFolders/FnUnmapFolderFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION early failures", "/HGCM/VBoxSharedFolders/FnInformationFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetFile, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/SET/FILE successes", "/HGCM/VBoxSharedFolders/FnInformationSetFile"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetFileFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/SET/FILE failures", "/HGCM/VBoxSharedFolders/FnInformationSetFileFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetSize, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/SET/SIZE successes", "/HGCM/VBoxSharedFolders/FnInformationSetSize"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationSetSizeFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/SET/SIZE failures", "/HGCM/VBoxSharedFolders/FnInformationSetSizeFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetFile, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/GET/FILE successes", "/HGCM/VBoxSharedFolders/FnInformationGetFile"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetFileFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/GET/FILE failures", "/HGCM/VBoxSharedFolders/FnInformationGetFileFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetVolume, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/GET/VOLUME successes", "/HGCM/VBoxSharedFolders/FnInformationGetVolume"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatInformationGetVolumeFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_INFORMATION/GET/VOLUME failures", "/HGCM/VBoxSharedFolders/FnInformationGetVolumeFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRemove, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_REMOVE successes", "/HGCM/VBoxSharedFolders/FnRemove"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRemoveFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_REMOVE failures", "/HGCM/VBoxSharedFolders/FnRemoveFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRename, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_RENAME successes", "/HGCM/VBoxSharedFolders/FnRename"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatRenameFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_RENAME failures", "/HGCM/VBoxSharedFolders/FnRenameFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatFlush, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_FLUSH successes", "/HGCM/VBoxSharedFolders/FnFlush"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatFlushFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_FLUSH failures", "/HGCM/VBoxSharedFolders/FnFlushFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSetUtf8, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_SET_UTF8", "/HGCM/VBoxSharedFolders/FnSetUtf8"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSymlink, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_SYMLINK successes", "/HGCM/VBoxSharedFolders/FnSymlink"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSymlinkFail, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_SYMLINK failures", "/HGCM/VBoxSharedFolders/FnSymlinkFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatSetSymlinks, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_SET_SYMLINKS", "/HGCM/VBoxSharedFolders/FnSetSymlink"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatQueryMapInfo, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_QUERY_MAP_INFO", "/HGCM/VBoxSharedFolders/FnQueryMapInfo"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWaitForMappingsChanges, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES successes", "/HGCM/VBoxSharedFolders/FnWaitForMappingsChanges"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatWaitForMappingsChangesFail,STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES failures","/HGCM/VBoxSharedFolders/FnWaitForMappingsChangesFail"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatCancelMappingsChangesWait, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS", "/HGCM/VBoxSharedFolders/FnCancelMappingsChangesWaits"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatUnknown, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "SHFL_FN_???", "/HGCM/VBoxSharedFolders/FnUnknown"); + HGCMSvcHlpStamRegister(g_pHelpers, &g_StatMsgStage1, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Time from VMMDev arrival to worker thread.","/HGCM/VBoxSharedFolders/MsgStage1"); + } + } + + return rc; +} + diff --git a/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc new file mode 100644 index 00000000..fb9e5418 --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/VBoxSharedFoldersSvc.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxSharedFoldersSvc.rc $ */ +/** @file + * VBoxSharedFolders - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox Shared Folders Host Service\0" + VALUE "InternalName", "VBoxSharedFolders\0" + VALUE "OriginalFilename", "VBoxSharedFolders.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/SharedFolders/mappings.cpp b/src/VBox/HostServices/SharedFolders/mappings.cpp new file mode 100644 index 00000000..1c76d78c --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/mappings.cpp @@ -0,0 +1,939 @@ +/* $Id: mappings.cpp $ */ +/** @file + * Shared Folders Service - Mappings support. + */ + +/* + * Copyright (C) 2006-2019 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_SHARED_FOLDERS +#ifdef UNITTEST +# include "testcase/tstSharedFolderService.h" +#endif + +#include "mappings.h" +#include "vbsfpath.h" +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/list.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <VBox/AssertGuest.h> + +#ifdef UNITTEST +# include "teststubs.h" +#endif + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +extern PVBOXHGCMSVCHELPERS g_pHelpers; /* service.cpp */ + + +/* Shared folders order in the saved state and in the g_FolderMapping can differ. + * So a translation array of root handle is needed. + */ + +static MAPPING g_FolderMapping[SHFL_MAX_MAPPINGS]; +static SHFLROOT g_aIndexFromRoot[SHFL_MAX_MAPPINGS]; +/**< Array running parallel to g_aIndexFromRoot and which entries are increased + * as an root handle is added or removed. + * + * This helps the guest figuring out that a mapping may have been reconfigured + * or that saved state has been restored. Entry reuse is very likely given that + * vbsfRootHandleAdd() always starts searching at the start for an unused entry. + */ +static uint32_t g_auRootHandleVersions[SHFL_MAX_MAPPINGS]; +/** Version number that is increased for every change made. + * This is used by the automount guest service to wait for changes. + * @note This does not need saving, the guest should be woken up and refresh + * its sate when restored. */ +static uint32_t volatile g_uFolderMappingsVersion = 0; + + +/** For recording async vbsfMappingsWaitForChanges calls. */ +typedef struct SHFLMAPPINGSWAIT +{ + RTLISTNODE ListEntry; /**< List entry. */ + PSHFLCLIENTDATA pClient; /**< The client that's waiting. */ + VBOXHGCMCALLHANDLE hCall; /**< The call handle to signal completion with. */ + PVBOXHGCMSVCPARM pParm; /**< The 32-bit unsigned parameter to stuff g_uFolderMappingsVersion into. */ +} SHFLMAPPINGSWAIT; +/** Pointer to async mappings change wait. */ +typedef SHFLMAPPINGSWAIT *PSHFLMAPPINGSWAIT; +/** List head for clients waiting on mapping changes (SHFLMAPPINGSWAIT). */ +static RTLISTANCHOR g_MappingsChangeWaiters; +/** Number of clients waiting on mapping changes. + * We use this to limit the number of waiting calls the clients can make. */ +static uint32_t g_cMappingChangeWaiters = 0; +static void vbsfMappingsWakeupAllWaiters(void); + + +void vbsfMappingInit(void) +{ + unsigned root; + + for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++) + { + g_aIndexFromRoot[root] = SHFL_ROOT_NIL; + } + + RTListInit(&g_MappingsChangeWaiters); +} + +int vbsfMappingLoaded(const MAPPING *pLoadedMapping, SHFLROOT root) +{ + /* Mapping loaded from the saved state with the index. Which means + * the guest uses the iMapping as root handle for this folder. + * Check whether there is the same mapping in g_FolderMapping and + * update the g_aIndexFromRoot. + * + * Also update the mapping properties, which were lost: cMappings. + */ + if (root >= SHFL_MAX_MAPPINGS) + { + return VERR_INVALID_PARAMETER; + } + + SHFLROOT i; + for (i = 0; i < RT_ELEMENTS(g_FolderMapping); i++) + { + MAPPING *pMapping = &g_FolderMapping[i]; + + /* Equal? */ + if ( pLoadedMapping->fValid == pMapping->fValid + && ShflStringSizeOfBuffer(pLoadedMapping->pMapName) == ShflStringSizeOfBuffer(pMapping->pMapName) + && memcmp(pLoadedMapping->pMapName, pMapping->pMapName, ShflStringSizeOfBuffer(pMapping->pMapName)) == 0) + { + if (!pMapping->fLoadedRootId) + { + pMapping->fLoadedRootId = true; + Log(("vbsfMappingLoaded: root=%u i=%u (was %u) (%ls)\n", + root, i, g_aIndexFromRoot[root], pLoadedMapping->pMapName->String.utf16)); + + /* Actual index is i. */ + /** @todo This will not work with global shared folders, as these can change + * while state is saved and these blind assignments may hid new ones. */ + g_aIndexFromRoot[root] = i; + + /* Update the mapping properties. */ + pMapping->cMappings = pLoadedMapping->cMappings; + + return VINF_SUCCESS; + } + } + } + + /* No corresponding mapping on the host but the guest still uses it. + * Add a 'placeholder' mapping. + */ + LogRel2(("SharedFolders: mapping a placeholder for '%ls' -> '%s'\n", + pLoadedMapping->pMapName->String.ucs2, pLoadedMapping->pszFolderName)); + return vbsfMappingsAdd(pLoadedMapping->pszFolderName, pLoadedMapping->pMapName, + pLoadedMapping->fWritable, pLoadedMapping->fAutoMount, pLoadedMapping->pAutoMountPoint, + pLoadedMapping->fSymlinksCreate, /* fMissing = */ true, /* fPlaceholder = */ true); +} + +MAPPING *vbsfMappingGetByRoot(SHFLROOT root) +{ + if (root < RT_ELEMENTS(g_aIndexFromRoot)) + { + SHFLROOT iMapping = g_aIndexFromRoot[root]; + + if ( iMapping != SHFL_ROOT_NIL + && iMapping < RT_ELEMENTS(g_FolderMapping)) + { + return &g_FolderMapping[iMapping]; + } + } + + return NULL; +} + +static SHFLROOT vbsfMappingGetRootFromIndex(SHFLROOT iMapping) +{ + unsigned root; + + for (root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++) + { + if (iMapping == g_aIndexFromRoot[root]) + { + return root; + } + } + + return SHFL_ROOT_NIL; +} + +static MAPPING *vbsfMappingGetByName(PRTUTF16 pwszName, SHFLROOT *pRoot) +{ + for (unsigned i = 0; i < SHFL_MAX_MAPPINGS; i++) + { + if ( g_FolderMapping[i].fValid + && !g_FolderMapping[i].fPlaceholder) /* Don't allow mapping placeholders. */ + { + if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pwszName)) + { + SHFLROOT root = vbsfMappingGetRootFromIndex(i); + + if (root != SHFL_ROOT_NIL) + { + if (pRoot) + { + *pRoot = root; + } + return &g_FolderMapping[i]; + } + AssertFailed(); + } + } + } + return NULL; +} + +static void vbsfRootHandleAdd(SHFLROOT iMapping) +{ + for (unsigned root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++) + { + if (g_aIndexFromRoot[root] == SHFL_ROOT_NIL) + { + g_aIndexFromRoot[root] = iMapping; + g_auRootHandleVersions[root] += 1; + return; + } + } + + AssertFailed(); +} + +static void vbsfRootHandleRemove(SHFLROOT iMapping) +{ + unsigned cFound = 0; + + for (unsigned root = 0; root < RT_ELEMENTS(g_aIndexFromRoot); root++) + { + if (g_aIndexFromRoot[root] == iMapping) + { + g_aIndexFromRoot[root] = SHFL_ROOT_NIL; + g_auRootHandleVersions[root] += 1; + Log(("vbsfRootHandleRemove: Removed root=%u (iMapping=%u)\n", root, iMapping)); + + /* Note! Do not stop here as g_aIndexFromRoot may (at least it could + prior to the introduction of fLoadedRootId) contain + duplicates after restoring save state. */ + cFound++; + } + } + + Assert(cFound > 0); RT_NOREF(cFound); +} + + + +#ifdef UNITTEST +/** Unit test the SHFL_FN_ADD_MAPPING API. Located here as a form of API + * documentation. */ +void testMappingsAdd(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testMappingsAddBadParameters(hTest); + /* Add tests as required... */ +} +#endif +/* + * We are always executed from one specific HGCM thread. So thread safe. + */ +int vbsfMappingsAdd(const char *pszFolderName, PSHFLSTRING pMapName, bool fWritable, + bool fAutoMount, PSHFLSTRING pAutoMountPoint, bool fSymlinksCreate, bool fMissing, bool fPlaceholder) +{ + unsigned i; + + Assert(pszFolderName && pMapName); + + Log(("vbsfMappingsAdd %ls\n", pMapName->String.ucs2)); + + /* Check for duplicates, ignoring placeholders to give the GUI to change stuff at runtime. */ + /** @todo bird: Not entirely sure about ignoring placeholders, but you cannot + * trigger auto-umounting without ignoring them. */ + if (!fPlaceholder) + { + for (i = 0; i < SHFL_MAX_MAPPINGS; i++) + { + if ( g_FolderMapping[i].fValid + && !g_FolderMapping[i].fPlaceholder) + { + if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2)) + { + AssertMsgFailed(("vbsfMappingsAdd: %ls mapping already exists!!\n", pMapName->String.ucs2)); + return VERR_ALREADY_EXISTS; + } + } + } + } + + for (i = 0; i < SHFL_MAX_MAPPINGS; i++) + { + if (g_FolderMapping[i].fValid == false) + { + /* Make sure the folder name is an absolute path, otherwise we're + likely to get into trouble with buffer sizes in vbsfPathGuestToHost. */ + char szAbsFolderName[RTPATH_MAX]; + int rc = vbsfPathAbs(NULL, pszFolderName, szAbsFolderName, sizeof(szAbsFolderName)); + AssertRCReturn(rc, rc); + + g_FolderMapping[i].pszFolderName = RTStrDup(szAbsFolderName); + g_FolderMapping[i].pMapName = ShflStringDup(pMapName); + g_FolderMapping[i].pAutoMountPoint = ShflStringDup(pAutoMountPoint); + if ( !g_FolderMapping[i].pszFolderName + || !g_FolderMapping[i].pMapName + || !g_FolderMapping[i].pAutoMountPoint) + { + RTStrFree(g_FolderMapping[i].pszFolderName); + RTMemFree(g_FolderMapping[i].pMapName); + RTMemFree(g_FolderMapping[i].pAutoMountPoint); + return VERR_NO_MEMORY; + } + + g_FolderMapping[i].fValid = true; + g_FolderMapping[i].cMappings = 0; + g_FolderMapping[i].fWritable = fWritable; + g_FolderMapping[i].fAutoMount = fAutoMount; + g_FolderMapping[i].fSymlinksCreate = fSymlinksCreate; + g_FolderMapping[i].fMissing = fMissing; + g_FolderMapping[i].fPlaceholder = fPlaceholder; + g_FolderMapping[i].fLoadedRootId = false; + + /* Check if the host file system is case sensitive */ + RTFSPROPERTIES prop; + prop.fCaseSensitive = false; /* Shut up MSC. */ + rc = RTFsQueryProperties(g_FolderMapping[i].pszFolderName, &prop); + AssertRC(rc); + g_FolderMapping[i].fHostCaseSensitive = RT_SUCCESS(rc) ? prop.fCaseSensitive : false; + vbsfRootHandleAdd(i); + vbsfMappingsWakeupAllWaiters(); + break; + } + } + if (i == SHFL_MAX_MAPPINGS) + { + AssertLogRelMsgFailed(("vbsfMappingsAdd: no more room to add mapping %s to %ls!!\n", pszFolderName, pMapName->String.ucs2)); + return VERR_TOO_MUCH_DATA; + } + + Log(("vbsfMappingsAdd: added mapping %s to %ls (slot %u, root %u)\n", + pszFolderName, pMapName->String.ucs2, i, vbsfMappingGetRootFromIndex(i))); + return VINF_SUCCESS; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_REMOVE_MAPPING API. Located here as a form of API + * documentation. */ +void testMappingsRemove(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testMappingsRemoveBadParameters(hTest); + /* Add tests as required... */ +} +#endif +int vbsfMappingsRemove(PSHFLSTRING pMapName) +{ + Assert(pMapName); + Log(("vbsfMappingsRemove %ls\n", pMapName->String.ucs2)); + + /* + * We must iterate thru the whole table as may have 0+ placeholder entries + * and 0-1 regular entries with the same name. Also, it is good to kick + * the guest automounter into action wrt to evicting placeholders. + */ + int rc = VERR_FILE_NOT_FOUND; + for (unsigned i = 0; i < SHFL_MAX_MAPPINGS; i++) + { + if (g_FolderMapping[i].fValid == true) + { + if (!RTUtf16LocaleICmp(g_FolderMapping[i].pMapName->String.ucs2, pMapName->String.ucs2)) + { + if (g_FolderMapping[i].cMappings != 0) + { + LogRel2(("SharedFolders: removing '%ls' -> '%s'%s, which is still used by the guest\n", pMapName->String.ucs2, + g_FolderMapping[i].pszFolderName, g_FolderMapping[i].fPlaceholder ? " (again)" : "")); + g_FolderMapping[i].fMissing = true; + g_FolderMapping[i].fPlaceholder = true; + vbsfMappingsWakeupAllWaiters(); + rc = VINF_PERMISSION_DENIED; + } + else + { + /* pMapName can be the same as g_FolderMapping[i].pMapName when + * called from vbsfUnmapFolder, log it before deallocating the memory. */ + Log(("vbsfMappingsRemove: mapping %ls removed\n", pMapName->String.ucs2)); + bool fSame = g_FolderMapping[i].pMapName == pMapName; + + RTStrFree(g_FolderMapping[i].pszFolderName); + RTMemFree(g_FolderMapping[i].pMapName); + g_FolderMapping[i].pszFolderName = NULL; + g_FolderMapping[i].pMapName = NULL; + g_FolderMapping[i].fValid = false; + vbsfRootHandleRemove(i); + vbsfMappingsWakeupAllWaiters(); + if (rc == VERR_FILE_NOT_FOUND) + rc = VINF_SUCCESS; + if (fSame) + break; + } + } + } + } + + return rc; +} + +const char* vbsfMappingsQueryHostRoot(SHFLROOT root) +{ + MAPPING *pFolderMapping = vbsfMappingGetByRoot(root); + AssertReturn(pFolderMapping, NULL); + if (pFolderMapping->fMissing) + return NULL; + return pFolderMapping->pszFolderName; +} + +int vbsfMappingsQueryHostRootEx(SHFLROOT hRoot, const char **ppszRoot, uint32_t *pcbRootLen) +{ + MAPPING *pFolderMapping = vbsfMappingGetByRoot(hRoot); + AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER); + if (pFolderMapping->fMissing) + return VERR_NOT_FOUND; + if ( pFolderMapping->pszFolderName == NULL + || pFolderMapping->pszFolderName[0] == 0) + return VERR_NOT_FOUND; + *ppszRoot = pFolderMapping->pszFolderName; + *pcbRootLen = (uint32_t)strlen(pFolderMapping->pszFolderName); + return VINF_SUCCESS; +} + +bool vbsfIsGuestMappingCaseSensitive(SHFLROOT root) +{ + MAPPING *pFolderMapping = vbsfMappingGetByRoot(root); + AssertReturn(pFolderMapping, false); + return pFolderMapping->fGuestCaseSensitive; +} + +bool vbsfIsHostMappingCaseSensitive(SHFLROOT root) +{ + MAPPING *pFolderMapping = vbsfMappingGetByRoot(root); + AssertReturn(pFolderMapping, false); + return pFolderMapping->fHostCaseSensitive; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_QUERY_MAPPINGS API. Located here as a form of API + * documentation (or should it better be inline in include/VBox/shflsvc.h?) */ +void testMappingsQuery(RTTEST hTest) +{ + /* The API should return all mappings if we provide enough buffers. */ + testMappingsQuerySimple(hTest); + /* If we provide too few buffers that should be signalled correctly. */ + testMappingsQueryTooFewBuffers(hTest); + /* The SHFL_MF_AUTOMOUNT flag means return only auto-mounted mappings. */ + testMappingsQueryAutoMount(hTest); + /* The mappings return array must have numberOfMappings entries. */ + testMappingsQueryArrayWrongSize(hTest); +} +#endif +/** + * @note If pMappings / *pcMappings is smaller than the actual amount of + * mappings that *could* have been returned *pcMappings contains the + * required buffer size so that the caller can retry the operation if + * wanted. + */ +int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, bool fOnlyAutoMounts, PSHFLMAPPING pMappings, uint32_t *pcMappings) +{ + LogFlow(("vbsfMappingsQuery: pClient = %p, pMappings = %p, pcMappings = %p, *pcMappings = %d\n", + pClient, pMappings, pcMappings, *pcMappings)); + + uint32_t const cMaxMappings = *pcMappings; + uint32_t idx = 0; + for (uint32_t i = 0; i < SHFL_MAX_MAPPINGS; i++) + { + MAPPING *pFolderMapping = vbsfMappingGetByRoot(i); + if ( pFolderMapping != NULL + && pFolderMapping->fValid + && ( !fOnlyAutoMounts + || (pFolderMapping->fAutoMount && !pFolderMapping->fPlaceholder)) ) + { + if (idx < cMaxMappings) + { + pMappings[idx].u32Status = SHFL_MS_NEW; + pMappings[idx].root = i; + } + idx++; + } + } + + /* Return actual number of mappings, regardless whether the handed in + * mapping buffer was big enough. */ + /** @todo r=bird: This is non-standard interface behaviour. We return + * VERR_BUFFER_OVERFLOW or at least a VINF_BUFFER_OVERFLOW here. + * + * Guess this goes well along with ORing SHFL_MF_AUTOMOUNT into + * pClient->fu32Flags rather than passing it as fOnlyAutoMounts... + * Not amused by this. */ + *pcMappings = idx; + + RT_NOREF_PV(pClient); + LogFlow(("vbsfMappingsQuery: returns VINF_SUCCESS (idx=%u, cMaxMappings=%u)\n", idx, cMaxMappings)); + return VINF_SUCCESS; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_QUERY_MAP_NAME API. Located here as a form of API + * documentation. */ +void testMappingsQueryName(RTTEST hTest) +{ + /* If we query an valid mapping it should be returned. */ + testMappingsQueryNameValid(hTest); + /* If we query an invalid mapping that should be signalled. */ + testMappingsQueryNameInvalid(hTest); + /* If we pass in a bad string buffer that should be detected. */ + testMappingsQueryNameBadBuffer(hTest); +} +#endif +int vbsfMappingsQueryName(PSHFLCLIENTDATA pClient, SHFLROOT root, SHFLSTRING *pString) +{ + LogFlow(("vbsfMappingsQuery: pClient = %p, root = %d, *pString = %p\n", pClient, root, pString)); + + int rc; + MAPPING *pFolderMapping = vbsfMappingGetByRoot(root); + if (pFolderMapping) + { + if (pFolderMapping->fValid) + { + if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) + rc = ShflStringCopyUtf16BufAsUtf8(pString, pFolderMapping->pMapName); + else + { + /* Not using ShlfStringCopy here as behaviour shouldn't change... */ + if (pString->u16Size < pFolderMapping->pMapName->u16Size) + { + Log(("vbsfMappingsQuery: passed string too short (%d < %d bytes)!\n", + pString->u16Size, pFolderMapping->pMapName->u16Size)); + rc = VERR_INVALID_PARAMETER; + } + else + { + pString->u16Length = pFolderMapping->pMapName->u16Length; + memcpy(pString->String.ucs2, pFolderMapping->pMapName->String.ucs2, + pFolderMapping->pMapName->u16Size); + rc = VINF_SUCCESS; + } + } + } + else + rc = VERR_FILE_NOT_FOUND; + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlow(("vbsfMappingsQuery:Name return rc = %Rrc\n", rc)); + return rc; +} + +/** Queries fWritable flag for the given root. Returns error if the root is not accessible. + */ +int vbsfMappingsQueryWritable(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fWritable) +{ + RT_NOREF1(pClient); + int rc = VINF_SUCCESS; + + LogFlow(("vbsfMappingsQueryWritable: pClient = %p, root = %d\n", pClient, root)); + + MAPPING *pFolderMapping = vbsfMappingGetByRoot(root); + AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER); + + if ( pFolderMapping->fValid + && !pFolderMapping->fMissing) + *fWritable = pFolderMapping->fWritable; + else + rc = VERR_FILE_NOT_FOUND; + + LogFlow(("vbsfMappingsQuery:Writable return rc = %Rrc\n", rc)); + + return rc; +} + +int vbsfMappingsQueryAutoMount(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fAutoMount) +{ + RT_NOREF1(pClient); + int rc = VINF_SUCCESS; + + LogFlow(("vbsfMappingsQueryAutoMount: pClient = %p, root = %d\n", pClient, root)); + + MAPPING *pFolderMapping = vbsfMappingGetByRoot(root); + AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER); + + if (pFolderMapping->fValid == true) + *fAutoMount = pFolderMapping->fAutoMount; + else + rc = VERR_FILE_NOT_FOUND; + + LogFlow(("vbsfMappingsQueryAutoMount:Writable return rc = %Rrc\n", rc)); + + return rc; +} + +int vbsfMappingsQuerySymlinksCreate(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fSymlinksCreate) +{ + RT_NOREF1(pClient); + int rc = VINF_SUCCESS; + + LogFlow(("vbsfMappingsQueryAutoMount: pClient = %p, root = %d\n", pClient, root)); + + MAPPING *pFolderMapping = vbsfMappingGetByRoot(root); + AssertReturn(pFolderMapping, VERR_INVALID_PARAMETER); + + if (pFolderMapping->fValid == true) + *fSymlinksCreate = pFolderMapping->fSymlinksCreate; + else + rc = VERR_FILE_NOT_FOUND; + + LogFlow(("vbsfMappingsQueryAutoMount:SymlinksCreate return rc = %Rrc\n", rc)); + + return rc; +} + +/** + * Implements SHFL_FN_QUERY_MAP_INFO. + * @since VBox 6.0 + */ +int vbsfMappingsQueryInfo(PSHFLCLIENTDATA pClient, SHFLROOT root, PSHFLSTRING pNameBuf, PSHFLSTRING pMntPtBuf, + uint64_t *pfFlags, uint32_t *puVersion) +{ + LogFlow(("vbsfMappingsQueryInfo: pClient=%p root=%d\n", pClient, root)); + + /* Resolve the root handle. */ + int rc; + PMAPPING pFolderMapping = vbsfMappingGetByRoot(root); + if (pFolderMapping) + { + if (pFolderMapping->fValid) + { + /* + * Produce the output. + */ + *puVersion = g_auRootHandleVersions[root]; + + *pfFlags = 0; + if (pFolderMapping->fWritable) + *pfFlags |= SHFL_MIF_WRITABLE; + if (pFolderMapping->fAutoMount) + *pfFlags |= SHFL_MIF_AUTO_MOUNT; + if (pFolderMapping->fHostCaseSensitive) + *pfFlags |= SHFL_MIF_HOST_ICASE; + if (pFolderMapping->fGuestCaseSensitive) + *pfFlags |= SHFL_MIF_GUEST_ICASE; + if (pFolderMapping->fSymlinksCreate) + *pfFlags |= SHFL_MIF_SYMLINK_CREATION; + + int rc2; + if (pClient->fu32Flags & SHFL_CF_UTF8) + { + rc = ShflStringCopyUtf16BufAsUtf8(pNameBuf, pFolderMapping->pMapName); + rc2 = ShflStringCopyUtf16BufAsUtf8(pMntPtBuf, pFolderMapping->pAutoMountPoint); + } + else + { + rc = ShflStringCopy(pNameBuf, pFolderMapping->pMapName, sizeof(RTUTF16)); + rc2 = ShflStringCopy(pMntPtBuf, pFolderMapping->pAutoMountPoint, sizeof(RTUTF16)); + } + if (RT_SUCCESS(rc)) + rc = rc2; + } + else + rc = VERR_FILE_NOT_FOUND; + } + else + rc = VERR_INVALID_PARAMETER; + LogFlow(("vbsfMappingsQueryInfo: returns %Rrc\n", rc)); + return rc; +} + + + +#ifdef UNITTEST +/** Unit test the SHFL_FN_MAP_FOLDER API. Located here as a form of API + * documentation. */ +void testMapFolder(RTTEST hTest) +{ + /* If we try to map a valid name we should get the root. */ + testMapFolderValid(hTest); + /* If we try to map a valid name we should get VERR_FILE_NOT_FOUND. */ + testMapFolderInvalid(hTest); + /* If we map a folder twice we can unmap it twice. + * Currently unmapping too often is only asserted but not signalled. */ + testMapFolderTwice(hTest); + /* The delimiter should be converted in e.g. file delete operations. */ + testMapFolderDelimiter(hTest); + /* Test case sensitive mapping by opening a file with the wrong case. */ + testMapFolderCaseSensitive(hTest); + /* Test case insensitive mapping by opening a file with the wrong case. */ + testMapFolderCaseInsensitive(hTest); + /* If the number or types of parameters are wrong the API should fail. */ + testMapFolderBadParameters(hTest); +} +#endif +int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName, + RTUTF16 wcDelimiter, bool fCaseSensitive, SHFLROOT *pRoot) +{ + MAPPING *pFolderMapping = NULL; + + if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) + { + Log(("vbsfMapFolder %s\n", pszMapName->String.utf8)); + } + else + { + Log(("vbsfMapFolder %ls\n", pszMapName->String.ucs2)); + } + + AssertMsgReturn(wcDelimiter == '/' || wcDelimiter == '\\', + ("Invalid path delimiter: %#x\n", wcDelimiter), + VERR_INVALID_PARAMETER); + if (pClient->PathDelimiter == 0) + { + pClient->PathDelimiter = wcDelimiter; + } + else + { + AssertMsgReturn(wcDelimiter == pClient->PathDelimiter, + ("wcDelimiter=%#x PathDelimiter=%#x", wcDelimiter, pClient->PathDelimiter), + VERR_INVALID_PARAMETER); + } + + SHFLROOT RootTmp; + if (!pRoot) + pRoot = &RootTmp; + if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) + { + int rc; + PRTUTF16 utf16Name; + + rc = RTStrToUtf16((const char *) pszMapName->String.utf8, &utf16Name); + if (RT_FAILURE (rc)) + return rc; + + pFolderMapping = vbsfMappingGetByName(utf16Name, pRoot); + RTUtf16Free(utf16Name); + } + else + { + pFolderMapping = vbsfMappingGetByName(pszMapName->String.ucs2, pRoot); + } + + if (!pFolderMapping) + { + return VERR_FILE_NOT_FOUND; + } + + /* + * Check for reference count overflows and settings compatibility. + * For paranoid reasons, we don't allow modifying the case sensitivity + * setting while there are other mappings of a folder. + */ + AssertLogRelReturn(*pRoot < RT_ELEMENTS(pClient->acMappings), VERR_INTERNAL_ERROR); + AssertLogRelReturn(!pClient->fHasMappingCounts || pClient->acMappings[*pRoot] < _32K, VERR_TOO_MANY_OPENS); + ASSERT_GUEST_LOGREL_MSG_RETURN( pFolderMapping->cMappings == 0 + || pFolderMapping->fGuestCaseSensitive == fCaseSensitive, + ("Incompatible case sensitivity setting: %s: %u mappings, %ssenitive, requested %ssenitive!\n", + pFolderMapping->pszFolderName, pFolderMapping->cMappings, + pFolderMapping->fGuestCaseSensitive ? "" : "in", fCaseSensitive ? "" : "in"), + VERR_INCOMPATIBLE_CONFIG); + + /* + * Go ahead and map it. + */ + if (pClient->fHasMappingCounts) + pClient->acMappings[*pRoot] += 1; + pFolderMapping->cMappings++; + pFolderMapping->fGuestCaseSensitive = fCaseSensitive; + Log(("vbsfMmapFolder (cMappings=%u, acMappings[%u]=%u)\n", pFolderMapping->cMappings, *pRoot, pClient->acMappings[*pRoot])); + return VINF_SUCCESS; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_UNMAP_FOLDER API. Located here as a form of API + * documentation. */ +void testUnmapFolder(RTTEST hTest) +{ + /* Unmapping a mapped folder should succeed. + * If the folder is not mapped this is only asserted, not signalled. */ + testUnmapFolderValid(hTest); + /* Unmapping a non-existant root should fail. */ + testUnmapFolderInvalid(hTest); + /* If the number or types of parameters are wrong the API should fail. */ + testUnmapFolderBadParameters(hTest); +} +#endif +int vbsfUnmapFolder(PSHFLCLIENTDATA pClient, SHFLROOT root) +{ + RT_NOREF1(pClient); + int rc = VINF_SUCCESS; + + MAPPING *pFolderMapping = vbsfMappingGetByRoot(root); + if (pFolderMapping == NULL) + { + AssertFailed(); + return VERR_FILE_NOT_FOUND; + } + Assert(pFolderMapping->fValid == true && pFolderMapping->cMappings > 0); + + AssertLogRelReturn(root < RT_ELEMENTS(pClient->acMappings), VERR_INTERNAL_ERROR); + AssertLogRelReturn(!pClient->fHasMappingCounts || pClient->acMappings[root] > 0, VERR_INVALID_HANDLE); + + if (pClient->fHasMappingCounts) + pClient->acMappings[root] -= 1; + + if (pFolderMapping->cMappings > 0) + pFolderMapping->cMappings--; + + uint32_t const cMappings = pFolderMapping->cMappings; + if ( cMappings == 0 + && pFolderMapping->fPlaceholder) + { + /* Automatically remove, it is not used by the guest anymore. */ + Assert(pFolderMapping->fMissing); + LogRel2(("SharedFolders: unmapping placeholder '%ls' -> '%s'\n", + pFolderMapping->pMapName->String.ucs2, pFolderMapping->pszFolderName)); + vbsfMappingsRemove(pFolderMapping->pMapName); + } + + Log(("vbsfUnmapFolder (cMappings=%u, acMappings[%u]=%u)\n", cMappings, root, pClient->acMappings[root])); + return rc; +} + +/** + * SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES implementation. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on change. + * @retval VINF_TRY_AGAIN on resume. + * @retval VINF_HGCM_ASYNC_EXECUTE if waiting. + * @retval VERR_CANCELLED if cancelled. + * @retval VERR_OUT_OF_RESOURCES if there are too many pending waits. + * + * @param pClient The calling client. + * @param hCall The call handle. + * @param pParm The parameter (32-bit). + * @param fRestored Set if this is a call restored & resubmitted from saved + * state. + * @since VBox 6.0 + */ +int vbsfMappingsWaitForChanges(PSHFLCLIENTDATA pClient, VBOXHGCMCALLHANDLE hCall, PVBOXHGCMSVCPARM pParm, bool fRestored) +{ + /* + * Return immediately if the fodler mappings have changed since last call + * or if we got restored from saved state (adding of global folders, etc). + */ + uint32_t uCurVersion = g_uFolderMappingsVersion; + if ( pParm->u.uint32 != uCurVersion + || fRestored + || (pClient->fu32Flags & SHFL_CF_CANCEL_NEXT_WAIT) ) + { + int rc = VINF_SUCCESS; + if (pClient->fu32Flags & SHFL_CF_CANCEL_NEXT_WAIT) + { + pClient->fu32Flags &= ~SHFL_CF_CANCEL_NEXT_WAIT; + rc = VERR_CANCELLED; + } + else if (fRestored) + { + rc = VINF_TRY_AGAIN; + if (pParm->u.uint32 == uCurVersion) + uCurVersion = uCurVersion != UINT32_C(0x55555555) ? UINT32_C(0x55555555) : UINT32_C(0x99999999); + } + Log(("vbsfMappingsWaitForChanges: Version %#x -> %#x, returning %Rrc immediately.\n", pParm->u.uint32, uCurVersion, rc)); + pParm->u.uint32 = uCurVersion; + return rc; + } + + /* + * Setup a wait if we can. + */ + if (g_cMappingChangeWaiters < 64) + { + PSHFLMAPPINGSWAIT pWait = (PSHFLMAPPINGSWAIT)RTMemAlloc(sizeof(*pWait)); + if (pWait) + { + pWait->pClient = pClient; + pWait->hCall = hCall; + pWait->pParm = pParm; + + RTListAppend(&g_MappingsChangeWaiters, &pWait->ListEntry); + g_cMappingChangeWaiters += 1; + return VINF_HGCM_ASYNC_EXECUTE; + } + return VERR_NO_MEMORY; + } + LogRelMax(32, ("vbsfMappingsWaitForChanges: Too many threads waiting for changes!\n")); + return VERR_OUT_OF_RESOURCES; +} + +/** + * SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS implementation. + * + * @returns VINF_SUCCESS + * @param pClient The calling client to cancel all waits for. + * @since VBox 6.0 + */ +int vbsfMappingsCancelChangesWaits(PSHFLCLIENTDATA pClient) +{ + uint32_t const uCurVersion = g_uFolderMappingsVersion; + + PSHFLMAPPINGSWAIT pCur, pNext; + RTListForEachSafe(&g_MappingsChangeWaiters, pCur, pNext, SHFLMAPPINGSWAIT, ListEntry) + { + if (pCur->pClient == pClient) + { + RTListNodeRemove(&pCur->ListEntry); + pCur->pParm->u.uint32 = uCurVersion; + g_pHelpers->pfnCallComplete(pCur->hCall, VERR_CANCELLED); + RTMemFree(pCur); + } + } + + /* Set a flag to make sure the next SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES doesn't block. + This should help deal with races between this call and a thread about to do a wait. */ + pClient->fu32Flags |= SHFL_CF_CANCEL_NEXT_WAIT; + + return VINF_SUCCESS; +} + +/** + * Wakes up all clients waiting on + */ +static void vbsfMappingsWakeupAllWaiters(void) +{ + uint32_t const uCurVersion = ++g_uFolderMappingsVersion; + + PSHFLMAPPINGSWAIT pCur, pNext; + RTListForEachSafe(&g_MappingsChangeWaiters, pCur, pNext, SHFLMAPPINGSWAIT, ListEntry) + { + RTListNodeRemove(&pCur->ListEntry); + pCur->pParm->u.uint32 = uCurVersion; + g_pHelpers->pfnCallComplete(pCur->hCall, VERR_CANCELLED); + RTMemFree(pCur); + } +} + diff --git a/src/VBox/HostServices/SharedFolders/mappings.h b/src/VBox/HostServices/SharedFolders/mappings.h new file mode 100644 index 00000000..11840835 --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/mappings.h @@ -0,0 +1,80 @@ +/* $Id: mappings.h $ */ +/** @file + * Shared folders service - Mappings header. + */ + +/* + * Copyright (C) 2006-2019 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. + */ + +#ifndef VBOX_INCLUDED_SRC_SharedFolders_mappings_h +#define VBOX_INCLUDED_SRC_SharedFolders_mappings_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "shfl.h" +#include <VBox/shflsvc.h> + +typedef struct +{ + char *pszFolderName; /**< Directory at the host to share with the guest. */ + PSHFLSTRING pMapName; /**< Share name for the guest. */ + uint32_t cMappings; /**< Number of mappings. */ + bool fValid; /**< Mapping entry is used/valid. */ + bool fHostCaseSensitive; /**< Host file name space is case-sensitive. */ + bool fGuestCaseSensitive; /**< Guest file name space is case-sensitive. */ + bool fWritable; /**< Folder is writable for the guest. */ + PSHFLSTRING pAutoMountPoint; /**< Where the guest should try auto-mount the folder. */ + bool fAutoMount; /**< Folder will be auto-mounted by the guest. */ + bool fSymlinksCreate; /**< Guest is able to create symlinks. */ + bool fMissing; /**< Mapping not invalid but host path does not exist. + Any guest operation on such a folder fails! */ + bool fPlaceholder; /**< Mapping does not exist in the VM settings but the guest + still has. fMissing is always true for this mapping. */ + bool fLoadedRootId; /**< Set if vbsfMappingLoaded has found this mapping already. */ +} MAPPING; +/** Pointer to a MAPPING structure. */ +typedef MAPPING *PMAPPING; + +void vbsfMappingInit(void); + +bool vbsfMappingQuery(uint32_t iMapping, PMAPPING *pMapping); + +int vbsfMappingsAdd(const char *pszFolderName, PSHFLSTRING pMapName, bool fWritable, + bool fAutoMount, PSHFLSTRING pAutoMountPoint, bool fCreateSymlinks, bool fMissing, bool fPlaceholder); +int vbsfMappingsRemove(PSHFLSTRING pMapName); + +int vbsfMappingsQuery(PSHFLCLIENTDATA pClient, bool fOnlyAutoMounts, PSHFLMAPPING pMappings, uint32_t *pcMappings); +int vbsfMappingsQueryName(PSHFLCLIENTDATA pClient, SHFLROOT root, SHFLSTRING *pString); +int vbsfMappingsQueryWritable(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fWritable); +int vbsfMappingsQueryAutoMount(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fAutoMount); +int vbsfMappingsQuerySymlinksCreate(PSHFLCLIENTDATA pClient, SHFLROOT root, bool *fSymlinksCreate); +int vbsfMappingsQueryInfo(PSHFLCLIENTDATA pClient, SHFLROOT root, PSHFLSTRING pNameBuf, PSHFLSTRING pMntPtBuf, + uint64_t *pfFlags, uint32_t *puVersion); + +int vbsfMapFolder(PSHFLCLIENTDATA pClient, PSHFLSTRING pszMapName, RTUTF16 delimiter, + bool fCaseSensitive, SHFLROOT *pRoot); +int vbsfUnmapFolder(PSHFLCLIENTDATA pClient, SHFLROOT root); + +int vbsfMappingsWaitForChanges(PSHFLCLIENTDATA pClient, VBOXHGCMCALLHANDLE hCall, PVBOXHGCMSVCPARM pParm, bool fRestored); +int vbsfMappingsCancelChangesWaits(PSHFLCLIENTDATA pClient); + +const char* vbsfMappingsQueryHostRoot(SHFLROOT root); +int vbsfMappingsQueryHostRootEx(SHFLROOT hRoot, const char **ppszRoot, uint32_t *pcbRootLen); +bool vbsfIsGuestMappingCaseSensitive(SHFLROOT root); +bool vbsfIsHostMappingCaseSensitive(SHFLROOT root); + +int vbsfMappingLoaded(MAPPING const *pLoadedMapping, SHFLROOT root); +PMAPPING vbsfMappingGetByRoot(SHFLROOT root); + +#endif /* !VBOX_INCLUDED_SRC_SharedFolders_mappings_h */ + diff --git a/src/VBox/HostServices/SharedFolders/shfl.h b/src/VBox/HostServices/SharedFolders/shfl.h new file mode 100644 index 00000000..fa120af2 --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/shfl.h @@ -0,0 +1,73 @@ +/** @file + * Shared Folders: Main header - Common data and function prototypes definitions. + */ + +/* + * Copyright (C) 2006-2019 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. + */ + +#ifndef VBOX_INCLUDED_SRC_SharedFolders_shfl_h +#define VBOX_INCLUDED_SRC_SharedFolders_shfl_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/err.h> +#include <VBox/hgcmsvc.h> +#include <VBox/shflsvc.h> + +#include <VBox/log.h> + +/** + * Shared Folders client flags. + * @{ + */ + +/** Client has queried mappings at least once and, therefore, + * the service can process its other requests too. + */ +#define SHFL_CF_MAPPINGS_QUERIED (0x00000001) + +/** Mappings have been changed since last query. */ +#define SHFL_CF_MAPPINGS_CHANGED (0x00000002) + +/** Client uses UTF8 encoding, if not set then unicode 16 bit (UCS2) is used. */ +#define SHFL_CF_UTF8 (0x00000004) + +/** Client both supports and wants to use symlinks. */ +#define SHFL_CF_SYMLINKS (0x00000008) + +/** The call to SHFL_FN_WAIT_FOR_MAPPINGS_CHANGES will return immediately + * because of a SHFL_FN_CANCEL_MAPPINGS_CHANGES_WAITS call. */ +#define SHFL_CF_CANCEL_NEXT_WAIT (0x00000010) + +/** @} */ + +typedef struct _SHFLCLIENTDATA +{ + /** Client flags */ + uint32_t fu32Flags; + /** Path delimiter. */ + RTUTF16 PathDelimiter; + /** Currently unused. */ + uint8_t bPadding; + /** Set if the client has mapping usage counts. + * This is for helping with saved state. */ + uint8_t fHasMappingCounts; + /** Mapping counts for each root ID so we can unmap the folders when the + * session disconnects or the VM resets. */ + uint16_t acMappings[SHFL_MAX_MAPPINGS]; +} SHFLCLIENTDATA; +/** Pointer to a SHFLCLIENTDATA structure. */ +typedef SHFLCLIENTDATA *PSHFLCLIENTDATA; + +#endif /* !VBOX_INCLUDED_SRC_SharedFolders_shfl_h */ + diff --git a/src/VBox/HostServices/SharedFolders/shflhandle.cpp b/src/VBox/HostServices/SharedFolders/shflhandle.cpp new file mode 100644 index 00000000..ece13819 --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/shflhandle.cpp @@ -0,0 +1,226 @@ +/* $Id: shflhandle.cpp $ */ +/** @file + * Shared Folders Service - Handles helper functions. + */ + +/* + * Copyright (C) 2006-2019 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_SHARED_FOLDERS +#include "shflhandle.h" +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Very basic and primitive handle management. Should be sufficient for our needs. + * Handle allocation can be rather slow, but at least lookup is fast. + */ +typedef struct +{ + uint32_t uFlags; + uintptr_t pvUserData; + PSHFLCLIENTDATA pClient; +} SHFLINTHANDLE, *PSHFLINTHANDLE; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static SHFLINTHANDLE *g_pHandles = NULL; +static int32_t gLastHandleIndex = 0; +static RTCRITSECT gLock; + + +int vbsfInitHandleTable() +{ + g_pHandles = (SHFLINTHANDLE *)RTMemAllocZ (sizeof (SHFLINTHANDLE) * SHFLHANDLE_MAX); + if (!g_pHandles) + { + AssertFailed(); + return VERR_NO_MEMORY; + } + + /* Never return handle 0 */ + g_pHandles[0].uFlags = SHFL_HF_TYPE_DONTUSE; + gLastHandleIndex = 1; + + return RTCritSectInit(&gLock); +} + +int vbsfFreeHandleTable() +{ + if (g_pHandles) + RTMemFree(g_pHandles); + + g_pHandles = NULL; + + if (RTCritSectIsInitialized(&gLock)) + RTCritSectDelete(&gLock); + + return VINF_SUCCESS; +} + +SHFLHANDLE vbsfAllocHandle(PSHFLCLIENTDATA pClient, uint32_t uType, + uintptr_t pvUserData) +{ + SHFLHANDLE handle; + + Assert((uType & SHFL_HF_TYPE_MASK) != 0 && pvUserData); + + RTCritSectEnter(&gLock); + + /* Find next free handle */ + if (gLastHandleIndex >= SHFLHANDLE_MAX-1) + gLastHandleIndex = 1; + + /* Nice linear search */ + for(handle=gLastHandleIndex;handle<SHFLHANDLE_MAX;handle++) + { + if (g_pHandles[handle].pvUserData == 0) + { + gLastHandleIndex = handle; + break; + } + } + + if (handle == SHFLHANDLE_MAX) + { + /* Try once more from the start */ + for(handle=1;handle<SHFLHANDLE_MAX;handle++) + { + if (g_pHandles[handle].pvUserData == 0) + { + gLastHandleIndex = handle; + break; + } + } + if (handle == SHFLHANDLE_MAX) + { + /* Out of handles */ + RTCritSectLeave(&gLock); + AssertFailed(); + return SHFL_HANDLE_NIL; + } + } + g_pHandles[handle].uFlags = (uType & SHFL_HF_TYPE_MASK) | SHFL_HF_VALID; + g_pHandles[handle].pvUserData = pvUserData; + g_pHandles[handle].pClient = pClient; + + gLastHandleIndex++; + + RTCritSectLeave(&gLock); + + return handle; +} + +static int vbsfFreeHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle) +{ + if ( handle < SHFLHANDLE_MAX + && (g_pHandles[handle].uFlags & SHFL_HF_VALID) + && g_pHandles[handle].pClient == pClient) + { + g_pHandles[handle].uFlags = 0; + g_pHandles[handle].pvUserData = 0; + g_pHandles[handle].pClient = 0; + return VINF_SUCCESS; + } + return VERR_INVALID_HANDLE; +} + +uintptr_t vbsfQueryHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle, + uint32_t uType) +{ + if ( handle < SHFLHANDLE_MAX + && (g_pHandles[handle].uFlags & SHFL_HF_VALID) + && g_pHandles[handle].pClient == pClient) + { + Assert((uType & SHFL_HF_TYPE_MASK) != 0); + + if (g_pHandles[handle].uFlags & uType) + return g_pHandles[handle].pvUserData; + } + return 0; +} + +SHFLFILEHANDLE *vbsfQueryFileHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle) +{ + return (SHFLFILEHANDLE *)vbsfQueryHandle(pClient, handle, + SHFL_HF_TYPE_FILE); +} + +SHFLFILEHANDLE *vbsfQueryDirHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle) +{ + return (SHFLFILEHANDLE *)vbsfQueryHandle(pClient, handle, + SHFL_HF_TYPE_DIR); +} + +uint32_t vbsfQueryHandleType(PSHFLCLIENTDATA pClient, SHFLHANDLE handle) +{ + if ( handle < SHFLHANDLE_MAX + && (g_pHandles[handle].uFlags & SHFL_HF_VALID) + && g_pHandles[handle].pClient == pClient) + return g_pHandles[handle].uFlags & SHFL_HF_TYPE_MASK; + + return 0; +} + +SHFLHANDLE vbsfAllocDirHandle(PSHFLCLIENTDATA pClient) +{ + SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)RTMemAllocZ (sizeof (SHFLFILEHANDLE)); + + if (pHandle) + { + pHandle->Header.u32Flags = SHFL_HF_TYPE_DIR; + return vbsfAllocHandle(pClient, pHandle->Header.u32Flags, + (uintptr_t)pHandle); + } + + return SHFL_HANDLE_NIL; +} + +SHFLHANDLE vbsfAllocFileHandle(PSHFLCLIENTDATA pClient) +{ + SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)RTMemAllocZ (sizeof (SHFLFILEHANDLE)); + + if (pHandle) + { + pHandle->Header.u32Flags = SHFL_HF_TYPE_FILE; + return vbsfAllocHandle(pClient, pHandle->Header.u32Flags, + (uintptr_t)pHandle); + } + + return SHFL_HANDLE_NIL; +} + +void vbsfFreeFileHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE hHandle) +{ + SHFLFILEHANDLE *pHandle = (SHFLFILEHANDLE *)vbsfQueryHandle(pClient, + hHandle, SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE); + + if (pHandle) + { + vbsfFreeHandle(pClient, hHandle); + RTMemFree (pHandle); + } + else + AssertFailed(); +} + diff --git a/src/VBox/HostServices/SharedFolders/shflhandle.h b/src/VBox/HostServices/SharedFolders/shflhandle.h new file mode 100644 index 00000000..c300996c --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/shflhandle.h @@ -0,0 +1,80 @@ +/* $Id: shflhandle.h $ */ +/** @file + * Shared Folders Host Service - Handles helper functions header. + */ + +/* + * Copyright (C) 2006-2019 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. + */ + +#ifndef VBOX_INCLUDED_SRC_SharedFolders_shflhandle_h +#define VBOX_INCLUDED_SRC_SharedFolders_shflhandle_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "shfl.h" +#include <VBox/shflsvc.h> +#include <iprt/dir.h> + +#define SHFL_HF_TYPE_MASK (0x000000FF) +#define SHFL_HF_TYPE_DIR (0x00000001) +#define SHFL_HF_TYPE_FILE (0x00000002) +#define SHFL_HF_TYPE_VOLUME (0x00000004) +#define SHFL_HF_TYPE_DONTUSE (0x00000080) + +#define SHFL_HF_VALID (0x80000000) + +#define SHFLHANDLE_MAX (4096) + +typedef struct _SHFLHANDLEHDR +{ + uint32_t u32Flags; +} SHFLHANDLEHDR; + +#define ShflHandleType(__Handle) BIT_FLAG(((SHFLHANDLEHDR *)(__Handle))->u32Flags, SHFL_HF_TYPE_MASK) + +typedef struct _SHFLFILEHANDLE +{ + SHFLHANDLEHDR Header; + SHFLROOT root; /* Where the handle has been opened. */ + union + { + struct + { + RTFILE Handle; + } file; + struct + { + RTDIR Handle; + RTDIR SearchHandle; + PRTDIRENTRYEX pLastValidEntry; /* last found file in a directory search */ + } dir; + }; +} SHFLFILEHANDLE; + + +SHFLHANDLE vbsfAllocDirHandle(PSHFLCLIENTDATA pClient); +SHFLHANDLE vbsfAllocFileHandle(PSHFLCLIENTDATA pClient); +void vbsfFreeFileHandle (PSHFLCLIENTDATA pClient, SHFLHANDLE hHandle); + + +int vbsfInitHandleTable(); +int vbsfFreeHandleTable(); +SHFLHANDLE vbsfAllocHandle(PSHFLCLIENTDATA pClient, uint32_t uType, + uintptr_t pvUserData); +SHFLFILEHANDLE *vbsfQueryFileHandle(PSHFLCLIENTDATA pClient, + SHFLHANDLE handle); +SHFLFILEHANDLE *vbsfQueryDirHandle(PSHFLCLIENTDATA pClient, SHFLHANDLE handle); +uint32_t vbsfQueryHandleType(PSHFLCLIENTDATA pClient, + SHFLHANDLE handle); + +#endif /* !VBOX_INCLUDED_SRC_SharedFolders_shflhandle_h */ diff --git a/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk b/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk new file mode 100644 index 00000000..7096d4e8 --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/testcase/Makefile.kmk @@ -0,0 +1,94 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Shared Folders Host Service testcases. +# + +# +# Copyright (C) 2006-2019 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. +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# +# Structure size testcase. +# +PROGRAMS += tstShflSizes +TESTING += $(tstShflSizes_0_OUTDIR)/tstShflSizes.run +ifndef VBOX_ONLY_SDK + ifeq ($(KBUILD_TARGET),$(KBUILD_HOST)) + if1of ($(KBUILD_TARGET_ARCH).$(KBUILD_HOST_ARCH), x86.x86 amd64.amd64 x86.amd64) + OTHERS += $(tstShflSizes_0_OUTDIR)/tstShflSizes.run + endif + endif +endif +tstShflSizes_TEMPLATE = VBOXR3AUTOTST +tstShflSizes_DEFS = VBOX_WITH_HGCM +tstShflSizes_SOURCES = tstShflSizes.cpp +tstShflSizes_CLEAN = $(tstShflSizes_0_OUTDIR)/tstShflSizes.run + +$$(tstShflSizes_0_OUTDIR)/tstShflSizes.run: $$(tstShflSizes_1_STAGE_TARGET) + $(tstShflSizes_1_STAGE_TARGET) quiet + $(QUIET)$(APPEND) -t "$@" "done" + + +ifdef VBOX_WITH_TESTCASES +# +# Case conversion testcase. +# +PROGRAMS += tstShflCase +tstShflCase_TEMPLATE = VBOXR3TSTEXE +tstShflCase_DEFS = VBOX_WITH_HGCM +tstShflCase_SOURCES = tstShflCase.cpp +tstShflCase_LIBS = $(LIB_RUNTIME) + +# +# HGCM service testcase. +# + +PROGRAMS += tstSharedFolderService +tstSharedFolderService_TEMPLATE = VBOXR3TSTEXE +tstSharedFolderService_DEFS = VBOX_WITH_HGCM UNITTEST +tstSharedFolderService_INCS = .. +tstSharedFolderService_SOURCES = \ + tstSharedFolderService.cpp \ + ../mappings.cpp \ + ../VBoxSharedFoldersSvc.cpp \ + ../shflhandle.cpp \ + ../vbsfpathabs.cpp \ + ../vbsfpath.cpp \ + ../vbsf.cpp +tstSharedFolderService_LDFLAGS.darwin = \ + -framework Carbon +tstSharedFolderService_LIBS = $(LIB_RUNTIME) + +if 0 # Cannot define two RT_OS_XXX macros! +# As there are differences between the Windows build of the service and others, +# we do an additional build with RT_OS_WINDOWS defined on non-Windows targets. +PROGRAMS += \ + tstSharedFolderService \ + $(if $(eq $(KBUILD_TARGET),win),,tstSharedFolderService-win) +tstSharedFolderService-win_TEMPLATE = $(tstSharedFolderService_TEMPLATE) +tstSharedFolderService-win_DEFS = \ + $(tstSharedFolderService_DEFS) \ + RT_OS_WINDOWS +tstSharedFolderService-win_INCS = $(tstSharedFolderService_INCS) +tstSharedFolderService-win_SOURCES = $(tstSharedFolderService_SOURCES) +tstSharedFolderService-win_LDFLAGS.darwin = \ + $(tstSharedFolderService_LDFLAGS.darwin) +tstSharedFolderService-win_LIBS = $(tstSharedFolderService_LIBS) +endif + +endif # VBOX_WITH_TESTCASES + + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp new file mode 100644 index 00000000..9706ded0 --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.cpp @@ -0,0 +1,1332 @@ +/* $Id: tstSharedFolderService.cpp $ */ +/** @file + * Testcase for the shared folder service vbsf API. + * + * Note that this is still very threadbare (there is an awful lot which should + * really be tested, but it already took too long to produce this much). The + * idea is that anyone who makes changes to the shared folders service and who + * cares about unit testing them should add tests to the skeleton framework to + * exercise the bits they change before and after changing them. + */ + +/* + * Copyright (C) 2011-2019 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 * +*********************************************************************************************************************************/ + +#include "tstSharedFolderService.h" +#include "vbsf.h" + +#include <iprt/fs.h> +#include <iprt/dir.h> +#include <iprt/file.h> +#include <iprt/path.h> +#include <iprt/symlink.h> +#include <iprt/stream.h> +#include <iprt/test.h> +#include <iprt/string.h> +#include <iprt/utf16.h> + +#include "teststubs.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static RTTEST g_hTest = NIL_RTTEST; + + +/********************************************************************************************************************************* +* Declarations * +*********************************************************************************************************************************/ +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable); + + +/********************************************************************************************************************************* +* Helpers * +*********************************************************************************************************************************/ + +/** Simple call handle structure for the guest call completion callback */ +struct VBOXHGCMCALLHANDLE_TYPEDEF +{ + /** Where to store the result code */ + int32_t rc; +}; + +/** Call completion callback for guest calls. */ +static DECLCALLBACK(int) callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc) +{ + callHandle->rc = rc; + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) stamRegisterV(void *pvInstance, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, + STAMUNIT enmUnit, const char *pszDesc, const char *pszName, va_list va) +{ + RT_NOREF(pvInstance, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, va); + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) stamDeregisterV(void *pvInstance, const char *pszPatFmt, va_list va) +{ + RT_NOREF(pvInstance, pszPatFmt, va); + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) infoRegister(void *pvInstance, const char *pszName, const char *pszDesc, + PFNDBGFHANDLEREXT pfnHandler, void *pvUser) +{ + RT_NOREF(pvInstance, pszName, pszDesc, pfnHandler, pvUser); + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) infoDeregister(void *pvInstance, const char *pszName) +{ + RT_NOREF(pvInstance, pszName); + return VINF_SUCCESS; +} + +/** + * Initialise the HGCM service table as much as we need to start the + * service + * @param pTable the table to initialise + */ +void initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers) +{ + pTable->cbSize = sizeof (VBOXHGCMSVCFNTABLE); + pTable->u32Version = VBOX_HGCM_SVC_VERSION; + pHelpers->pfnCallComplete = callComplete; + pHelpers->pfnStamRegisterV = stamRegisterV; + pHelpers->pfnStamDeregisterV = stamDeregisterV; + pHelpers->pfnInfoRegister = infoRegister; + pHelpers->pfnInfoDeregister = infoDeregister; + pTable->pHelpers = pHelpers; +} + +#define LLUIFY(a) ((unsigned long long)(a)) + +static void bufferFromString(void *pvDest, size_t cb, const char *pcszSrc) +{ + char *pchDest = (char *)pvDest; + + Assert((cb) > 0); + strncpy((pchDest), (pcszSrc), (cb) - 1); + (pchDest)[(cb) - 1] = 0; +} + +static void bufferFromPath(void *pvDest, size_t cb, const char *pcszSrc) +{ + char *psz; + + bufferFromString(pvDest, cb, pcszSrc); + for (psz = (char *)pvDest; psz && psz < (char *)pvDest + cb; ++psz) + if (*psz == '\\') + *psz = '/'; +} + +#define ARRAY_FROM_PATH(a, b) \ + do { \ + void *p=(a); NOREF(p); \ + Assert((a) == p); /* Constant parameter */ \ + Assert(sizeof((a)) > 0); \ + bufferFromPath(a, sizeof(a), b); \ + } while (0) + + +/********************************************************************************************************************************* +* Stub functions and data * +*********************************************************************************************************************************/ +static bool g_fFailIfNotLowercase = false; + +static RTDIR g_testRTDirClose_hDir = NIL_RTDIR; + +extern int testRTDirClose(RTDIR hDir) +{ + /* RTPrintf("%s: hDir=%p\n", __PRETTY_FUNCTION__, hDir); */ + g_testRTDirClose_hDir = hDir; + return VINF_SUCCESS; +} + +static char testRTDirCreatePath[256]; +//static RTFMODE testRTDirCreateMode; - unused + +extern int testRTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate) +{ + RT_NOREF2(fMode, fCreate); + /* RTPrintf("%s: pszPath=%s, fMode=0x%llx\n", __PRETTY_FUNCTION__, pszPath, + LLUIFY(fMode)); */ + if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\"))) + return VERR_FILE_NOT_FOUND; + ARRAY_FROM_PATH(testRTDirCreatePath, pszPath); + return 0; +} + +static char testRTDirOpenName[256]; +static struct TESTDIRHANDLE +{ + int iEntry; + int iDir; +} g_aTestDirHandles[4]; +static int g_iNextDirHandle = 0; +static RTDIR testRTDirOpen_hDir; + +extern int testRTDirOpen(RTDIR *phDir, const char *pszPath) +{ + /* RTPrintf("%s: pszPath=%s\n", __PRETTY_FUNCTION__, pszPath); */ + if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\"))) + return VERR_FILE_NOT_FOUND; + ARRAY_FROM_PATH(testRTDirOpenName, pszPath); + *phDir = testRTDirOpen_hDir; + testRTDirOpen_hDir = NIL_RTDIR; + if (!*phDir && g_fFailIfNotLowercase) + *phDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)]; + if (*phDir) + { + struct TESTDIRHANDLE *pRealDir = (struct TESTDIRHANDLE *)*phDir; + pRealDir->iEntry = 0; + pRealDir->iDir = 0; + const char *pszSlash = pszPath - 1; + while ((pszSlash = strpbrk(pszSlash + 1, "\\/")) != NULL) + pRealDir->iDir += 1; + /*RTPrintf("opendir %s = %d \n", pszPath, pRealDir->iDir);*/ + } + return VINF_SUCCESS; +} + +/** @todo Do something useful with the last two arguments. */ +extern int testRTDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER, uint32_t) +{ + /* RTPrintf("%s: pszPath=%s\n", __PRETTY_FUNCTION__, pszPath); */ + if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\"))) + return VERR_FILE_NOT_FOUND; + ARRAY_FROM_PATH(testRTDirOpenName, pszPath); + *phDir = testRTDirOpen_hDir; + testRTDirOpen_hDir = NIL_RTDIR; + if (!*phDir && g_fFailIfNotLowercase) + *phDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)]; + if (*phDir) + { + struct TESTDIRHANDLE *pRealDir = (struct TESTDIRHANDLE *)*phDir; + pRealDir->iEntry = 0; + pRealDir->iDir = 0; + const char *pszSlash = pszPath - 1; + while ((pszSlash = strpbrk(pszSlash + 1, "\\/")) != NULL) + pRealDir->iDir += 1; + pRealDir->iDir -= 1; + /*RTPrintf("openfiltered %s = %d\n", pszPath, pRealDir->iDir);*/ + } + return VINF_SUCCESS; +} + +static RTDIR g_testRTDirQueryInfo_hDir; +static RTTIMESPEC testRTDirQueryInfoATime; + +extern int testRTDirQueryInfo(RTDIR hDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs) +{ + RT_NOREF1(enmAdditionalAttribs); + /* RTPrintf("%s: hDir=%p, enmAdditionalAttribs=0x%llx\n", __PRETTY_FUNCTION__, + hDir, LLUIFY(enmAdditionalAttribs)); */ + g_testRTDirQueryInfo_hDir = hDir; + RT_ZERO(*pObjInfo); + pObjInfo->AccessTime = testRTDirQueryInfoATime; + RT_ZERO(testRTDirQueryInfoATime); + return VINF_SUCCESS; +} + +extern int testRTDirRemove(const char *pszPath) +{ + if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\"))) + return VERR_FILE_NOT_FOUND; + RTPrintf("%s\n", __PRETTY_FUNCTION__); + return 0; +} + +static RTDIR g_testRTDirReadEx_hDir; + +extern int testRTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, + RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) +{ + RT_NOREF4(pDirEntry, pcbDirEntry, enmAdditionalAttribs, fFlags); + /* RTPrintf("%s: hDir=%p, pcbDirEntry=%d, enmAdditionalAttribs=%llu, fFlags=0x%llx\n", + __PRETTY_FUNCTION__, hDir, pcbDirEntry ? (int) *pcbDirEntry : -1, + LLUIFY(enmAdditionalAttribs), LLUIFY(fFlags)); */ + g_testRTDirReadEx_hDir = hDir; + if (g_fFailIfNotLowercase && hDir != NIL_RTDIR) + { + struct TESTDIRHANDLE *pRealDir = (struct TESTDIRHANDLE *)hDir; + if (pRealDir->iDir == 2) /* /test/mapping/ */ + { + if (pRealDir->iEntry == 0) + { + pRealDir->iEntry++; + RT_ZERO(*pDirEntry); + pDirEntry->Info.Attr.fMode = RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | RTFS_UNIX_IROTH | RTFS_UNIX_IXOTH; + pDirEntry->cbName = 4; + pDirEntry->cwcShortName = 4; + strcpy(pDirEntry->szName, "test"); + RTUtf16CopyAscii(pDirEntry->wszShortName, RT_ELEMENTS(pDirEntry->wszShortName), "test"); + /*RTPrintf("readdir: 'test'\n");*/ + return VINF_SUCCESS; + } + } + else if (pRealDir->iDir == 3) /* /test/mapping/test/ */ + { + if (pRealDir->iEntry == 0) + { + pRealDir->iEntry++; + RT_ZERO(*pDirEntry); + pDirEntry->Info.Attr.fMode = RTFS_TYPE_FILE | RTFS_DOS_NT_NORMAL | RTFS_UNIX_IROTH | RTFS_UNIX_IXOTH; + pDirEntry->cbName = 4; + pDirEntry->cwcShortName = 4; + strcpy(pDirEntry->szName, "file"); + RTUtf16CopyAscii(pDirEntry->wszShortName, RT_ELEMENTS(pDirEntry->wszShortName), "file"); + /*RTPrintf("readdir: 'file'\n");*/ + return VINF_SUCCESS; + } + } + /*else RTPrintf("%s: iDir=%d\n", pRealDir->iDir);*/ + } + return VERR_NO_MORE_FILES; +} + +static RTTIMESPEC testRTDirSetTimesATime; + +extern int testRTDirSetTimes(RTDIR hDir, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, + PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime) +{ + RT_NOREF4(hDir, pModificationTime, pChangeTime, pBirthTime); + /* RTPrintf("%s: hDir=%p, *pAccessTime=%lli, *pModificationTime=%lli, *pChangeTime=%lli, *pBirthTime=%lli\n", + __PRETTY_FUNCTION__, hDir, + pAccessTime ? (long long)RTTimeSpecGetNano(pAccessTime) : -1, + pModificationTime + ? (long long)RTTimeSpecGetNano(pModificationTime) : -1, + pChangeTime ? (long long)RTTimeSpecGetNano(pChangeTime) : -1, + pBirthTime ? (long long)RTTimeSpecGetNano(pBirthTime) : -1); */ + if (pAccessTime) + testRTDirSetTimesATime = *pAccessTime; + else + RT_ZERO(testRTDirSetTimesATime); + return VINF_SUCCESS; +} + +static RTFILE g_testRTFileCloseFile; + +extern int testRTFileClose(RTFILE File) +{ + /* RTPrintf("%s: File=%p\n", __PRETTY_FUNCTION__, File); */ + g_testRTFileCloseFile = File; + return 0; +} + +extern int testRTFileDelete(const char *pszFilename) +{ + if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszFilename, "/\\"))) + return VERR_FILE_NOT_FOUND; + RTPrintf("%s\n", __PRETTY_FUNCTION__); + return 0; +} + +static RTFILE g_testRTFileFlushFile; + +extern int testRTFileFlush(RTFILE File) +{ + /* RTPrintf("%s: File=%p\n", __PRETTY_FUNCTION__, File); */ + g_testRTFileFlushFile = File; + return VINF_SUCCESS; +} + +static RTFILE g_testRTFileLockFile; +static unsigned testRTFileLockfLock; +static int64_t testRTFileLockOffset; +static uint64_t testRTFileLockSize; + +extern int testRTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock) +{ + /* RTPrintf("%s: hFile=%p, fLock=%u, offLock=%lli, cbLock=%llu\n", + __PRETTY_FUNCTION__, hFile, fLock, (long long) offLock, + LLUIFY(cbLock)); */ + g_testRTFileLockFile = hFile; + testRTFileLockfLock = fLock; + testRTFileLockOffset = offLock; + testRTFileLockSize = cbLock; + return VINF_SUCCESS; +} + +static char testRTFileOpenName[256]; +static uint64_t testRTFileOpenFlags; +static RTFILE testRTFileOpenpFile; + +extern int testRTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen) +{ + /* RTPrintf("%s, pszFilename=%s, fOpen=0x%llx\n", __PRETTY_FUNCTION__, + pszFilename, LLUIFY(fOpen)); */ + ARRAY_FROM_PATH(testRTFileOpenName, pszFilename); + testRTFileOpenFlags = fOpen; + if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszFilename, "/\\"))) + return VERR_FILE_NOT_FOUND; + *pFile = testRTFileOpenpFile; + testRTFileOpenpFile = 0; + return VINF_SUCCESS; +} + +static RTFILE g_testRTFileQueryInfoFile; +static RTTIMESPEC testRTFileQueryInfoATime; +static uint32_t testRTFileQueryInfoFMode; + +extern int testRTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs) +{ + RT_NOREF1(enmAdditionalAttribs); + /* RTPrintf("%s, hFile=%p, enmAdditionalAttribs=0x%llx\n", + __PRETTY_FUNCTION__, hFile, LLUIFY(enmAdditionalAttribs)); */ + g_testRTFileQueryInfoFile = hFile; + RT_ZERO(*pObjInfo); + pObjInfo->AccessTime = testRTFileQueryInfoATime; + RT_ZERO(testRTDirQueryInfoATime); + pObjInfo->Attr.fMode = testRTFileQueryInfoFMode; + testRTFileQueryInfoFMode = 0; + return VINF_SUCCESS; +} + +static const char *testRTFileReadData; + +extern int testRTFileRead(RTFILE File, void *pvBuf, size_t cbToRead, size_t *pcbRead) +{ + RT_NOREF1(File); + /* RTPrintf("%s : File=%p, cbToRead=%llu\n", __PRETTY_FUNCTION__, File, + LLUIFY(cbToRead)); */ + bufferFromPath(pvBuf, cbToRead, testRTFileReadData); + if (pcbRead) + *pcbRead = RT_MIN(cbToRead, strlen(testRTFileReadData) + 1); + testRTFileReadData = 0; + return VINF_SUCCESS; +} + +extern int testRTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual) +{ + RT_NOREF3(hFile, offSeek, uMethod); + /* RTPrintf("%s : hFile=%p, offSeek=%llu, uMethod=%u\n", __PRETTY_FUNCTION__, + hFile, LLUIFY(offSeek), uMethod); */ + if (poffActual) + *poffActual = 0; + return VINF_SUCCESS; +} + +static uint64_t testRTFileSetFMode; + +extern int testRTFileSetMode(RTFILE File, RTFMODE fMode) +{ + RT_NOREF1(File); + /* RTPrintf("%s: fMode=%llu\n", __PRETTY_FUNCTION__, LLUIFY(fMode)); */ + testRTFileSetFMode = fMode; + return VINF_SUCCESS; +} + +static RTFILE g_testRTFileSetSizeFile; +static RTFOFF testRTFileSetSizeSize; + +extern int testRTFileSetSize(RTFILE File, uint64_t cbSize) +{ + /* RTPrintf("%s: File=%llu, cbSize=%llu\n", __PRETTY_FUNCTION__, LLUIFY(File), + LLUIFY(cbSize)); */ + g_testRTFileSetSizeFile = File; + testRTFileSetSizeSize = (RTFOFF) cbSize; /* Why was this signed before? */ + return VINF_SUCCESS; +} + +static RTTIMESPEC testRTFileSetTimesATime; + +extern int testRTFileSetTimes(RTFILE File, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, + PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime) +{ + RT_NOREF4(File, pModificationTime, pChangeTime, pBirthTime); + /* RTPrintf("%s: pFile=%p, *pAccessTime=%lli, *pModificationTime=%lli, *pChangeTime=%lli, *pBirthTime=%lli\n", + __PRETTY_FUNCTION__, + pAccessTime ? (long long)RTTimeSpecGetNano(pAccessTime) : -1, + pModificationTime + ? (long long)RTTimeSpecGetNano(pModificationTime) : -1, + pChangeTime ? (long long)RTTimeSpecGetNano(pChangeTime) : -1, + pBirthTime ? (long long)RTTimeSpecGetNano(pBirthTime) : -1); */ + if (pAccessTime) + testRTFileSetTimesATime = *pAccessTime; + else + RT_ZERO(testRTFileSetTimesATime); + return VINF_SUCCESS; +} + +static RTFILE g_testRTFileUnlockFile; +static int64_t testRTFileUnlockOffset; +static uint64_t testRTFileUnlockSize; + +extern int testRTFileUnlock(RTFILE File, int64_t offLock, uint64_t cbLock) +{ + /* RTPrintf("%s: hFile=%p, ofLock=%lli, cbLock=%llu\n", __PRETTY_FUNCTION__, + File, (long long) offLock, LLUIFY(cbLock)); */ + g_testRTFileUnlockFile = File; + testRTFileUnlockOffset = offLock; + testRTFileUnlockSize = cbLock; + return VINF_SUCCESS; +} + +static char testRTFileWriteData[256]; + +extern int testRTFileWrite(RTFILE File, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten) +{ + RT_NOREF2(File, cbToWrite); + /* RTPrintf("%s: File=%p, pvBuf=%.*s, cbToWrite=%llu\n", __PRETTY_FUNCTION__, + File, cbToWrite, (const char *)pvBuf, LLUIFY(cbToWrite)); */ + ARRAY_FROM_PATH(testRTFileWriteData, (const char *)pvBuf); + if (pcbWritten) + *pcbWritten = strlen(testRTFileWriteData) + 1; + return VINF_SUCCESS; +} + +extern int testRTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties) +{ + RT_NOREF1(pszFsPath); + /* RTPrintf("%s, pszFsPath=%s\n", __PRETTY_FUNCTION__, pszFsPath); + RT_ZERO(*pProperties); */ + pProperties->cbMaxComponent = 256; + pProperties->fCaseSensitive = true; + return VINF_SUCCESS; +} + +extern int testRTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial) +{ + RT_NOREF2(pszFsPath, pu32Serial); + RTPrintf("%s\n", __PRETTY_FUNCTION__); + return 0; +} +extern int testRTFsQuerySizes(const char *pszFsPath, PRTFOFF pcbTotal, RTFOFF *pcbFree, uint32_t *pcbBlock, uint32_t *pcbSector) +{ + RT_NOREF5(pszFsPath, pcbTotal, pcbFree, pcbBlock, pcbSector); + RTPrintf("%s\n", __PRETTY_FUNCTION__); + return 0; +} + +extern int testRTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) +{ + RT_NOREF2(enmAdditionalAttribs, fFlags); + /* RTPrintf("%s: pszPath=%s, enmAdditionalAttribs=0x%x, fFlags=0x%x\n", + __PRETTY_FUNCTION__, pszPath, (unsigned) enmAdditionalAttribs, + (unsigned) fFlags); */ + if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszPath, "/\\"))) + return VERR_FILE_NOT_FOUND; + RT_ZERO(*pObjInfo); + return VINF_SUCCESS; +} + +extern int testRTSymlinkDelete(const char *pszSymlink, uint32_t fDelete) +{ + RT_NOREF2(pszSymlink, fDelete); + if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszSymlink, "/\\"))) + return VERR_FILE_NOT_FOUND; + RTPrintf("%s\n", __PRETTY_FUNCTION__); + return 0; +} + +extern int testRTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead) +{ + if (g_fFailIfNotLowercase && !RTStrIsLowerCased(strpbrk(pszSymlink, "/\\"))) + return VERR_FILE_NOT_FOUND; + RT_NOREF4(pszSymlink, pszTarget, cbTarget, fRead); + RTPrintf("%s\n", __PRETTY_FUNCTION__); + return 0; +} + + +/********************************************************************************************************************************* +* Tests * +*********************************************************************************************************************************/ + +/* Sub-tests for testMappingsQuery(). */ +void testMappingsQuerySimple(RTTEST hTest) { RT_NOREF1(hTest); } +void testMappingsQueryTooFewBuffers(RTTEST hTest) { RT_NOREF1(hTest); } +void testMappingsQueryAutoMount(RTTEST hTest) { RT_NOREF1(hTest); } +void testMappingsQueryArrayWrongSize(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testMappingsQueryName(). */ +void testMappingsQueryNameValid(RTTEST hTest) { RT_NOREF1(hTest); } +void testMappingsQueryNameInvalid(RTTEST hTest) { RT_NOREF1(hTest); } +void testMappingsQueryNameBadBuffer(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testMapFolder(). */ +void testMapFolderValid(RTTEST hTest) { RT_NOREF1(hTest); } +void testMapFolderInvalid(RTTEST hTest) { RT_NOREF1(hTest); } +void testMapFolderTwice(RTTEST hTest) { RT_NOREF1(hTest); } +void testMapFolderDelimiter(RTTEST hTest) { RT_NOREF1(hTest); } +void testMapFolderCaseSensitive(RTTEST hTest) { RT_NOREF1(hTest); } +void testMapFolderCaseInsensitive(RTTEST hTest) { RT_NOREF1(hTest); } +void testMapFolderBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testUnmapFolder(). */ +void testUnmapFolderValid(RTTEST hTest) { RT_NOREF1(hTest); } +void testUnmapFolderInvalid(RTTEST hTest) { RT_NOREF1(hTest); } +void testUnmapFolderBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testCreate(). */ +void testCreateBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testClose(). */ +void testCloseBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testRead(). */ +void testReadBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testWrite(). */ +void testWriteBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testLock(). */ +void testLockBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testFlush(). */ +void testFlushBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testDirList(). */ +void testDirListBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testReadLink(). */ +void testReadLinkBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testFSInfo(). */ +void testFSInfoBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testRemove(). */ +void testRemoveBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testRename(). */ +void testRenameBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testSymlink(). */ +void testSymlinkBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testMappingsAdd(). */ +void testMappingsAddBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +/* Sub-tests for testMappingsRemove(). */ +void testMappingsRemoveBadParameters(RTTEST hTest) { RT_NOREF1(hTest); } + +union TESTSHFLSTRING +{ + SHFLSTRING string; + char acData[256]; +}; + +static void fillTestShflString(union TESTSHFLSTRING *pDest, + const char *pcszSource) +{ + const size_t cchSource = strlen(pcszSource); + AssertRelease( cchSource * 2 + 2 + < sizeof(*pDest) - RT_UOFFSETOF(SHFLSTRING, String)); + pDest->string.u16Length = (uint16_t)(cchSource * sizeof(RTUTF16)); + pDest->string.u16Size = pDest->string.u16Length + sizeof(RTUTF16); + /* Copy pcszSource ASCIIZ, including the trailing 0, to the UTF16 pDest->string.String.ucs2. */ + for (unsigned i = 0; i <= cchSource; ++i) + pDest->string.String.ucs2[i] = (uint16_t)pcszSource[i]; +} + +static SHFLROOT initWithWritableMapping(RTTEST hTest, + VBOXHGCMSVCFNTABLE *psvcTable, + VBOXHGCMSVCHELPERS *psvcHelpers, + const char *pcszFolderName, + const char *pcszMapping, + bool fCaseSensitive = true) +{ + VBOXHGCMSVCPARM aParms[RT_MAX(SHFL_CPARMS_ADD_MAPPING, + SHFL_CPARMS_MAP_FOLDER)]; + union TESTSHFLSTRING FolderName; + union TESTSHFLSTRING Mapping; + union TESTSHFLSTRING AutoMountPoint; + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + int rc; + + initTable(psvcTable, psvcHelpers); + AssertReleaseRC(VBoxHGCMSvcLoad(psvcTable)); + AssertRelease( psvcTable->pvService + = RTTestGuardedAllocTail(hTest, psvcTable->cbClient)); + RT_BZERO(psvcTable->pvService, psvcTable->cbClient); + fillTestShflString(&FolderName, pcszFolderName); + fillTestShflString(&Mapping, pcszMapping); + fillTestShflString(&AutoMountPoint, ""); + HGCMSvcSetPv(&aParms[0], &FolderName, RT_UOFFSETOF(SHFLSTRING, String) + + FolderName.string.u16Size); + HGCMSvcSetPv(&aParms[1], &Mapping, RT_UOFFSETOF(SHFLSTRING, String) + + Mapping.string.u16Size); + HGCMSvcSetU32(&aParms[2], 1); + HGCMSvcSetPv(&aParms[3], &AutoMountPoint, SHFLSTRING_HEADER_SIZE + AutoMountPoint.string.u16Size); + rc = psvcTable->pfnHostCall(psvcTable->pvService, SHFL_FN_ADD_MAPPING, + SHFL_CPARMS_ADD_MAPPING, aParms); + AssertReleaseRC(rc); + HGCMSvcSetPv(&aParms[0], &Mapping, RT_UOFFSETOF(SHFLSTRING, String) + + Mapping.string.u16Size); + HGCMSvcSetU32(&aParms[1], 0); /* root */ + HGCMSvcSetU32(&aParms[2], '/'); /* delimiter */ + HGCMSvcSetU32(&aParms[3], fCaseSensitive); + psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0, + psvcTable->pvService, SHFL_FN_MAP_FOLDER, + SHFL_CPARMS_MAP_FOLDER, aParms, 0); + AssertReleaseRC(callHandle.rc); + return aParms[1].u.uint32; +} + +/** @todo Mappings should be automatically removed by unloading the service, + * but unloading is currently a no-op! */ +static void unmapAndRemoveMapping(RTTEST hTest, VBOXHGCMSVCFNTABLE *psvcTable, + SHFLROOT root, const char *pcszFolderName) +{ + RT_NOREF1(hTest); + VBOXHGCMSVCPARM aParms[RT_MAX(SHFL_CPARMS_UNMAP_FOLDER, + SHFL_CPARMS_REMOVE_MAPPING)]; + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + union TESTSHFLSTRING FolderName; + int rc; + + HGCMSvcSetU32(&aParms[0], root); + psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0, + psvcTable->pvService, SHFL_FN_UNMAP_FOLDER, + SHFL_CPARMS_UNMAP_FOLDER, aParms, 0); + AssertReleaseRC(callHandle.rc); + fillTestShflString(&FolderName, pcszFolderName); + HGCMSvcSetPv(&aParms[0], &FolderName, RT_UOFFSETOF(SHFLSTRING, String) + + FolderName.string.u16Size); + rc = psvcTable->pfnHostCall(psvcTable->pvService, SHFL_FN_REMOVE_MAPPING, + SHFL_CPARMS_REMOVE_MAPPING, aParms); + AssertReleaseRC(rc); +} + +static int createFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT Root, + const char *pcszFilename, uint32_t fCreateFlags, + SHFLHANDLE *pHandle, SHFLCREATERESULT *pResult) +{ + VBOXHGCMSVCPARM aParms[SHFL_CPARMS_CREATE]; + union TESTSHFLSTRING Path; + SHFLCREATEPARMS CreateParms; + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + + fillTestShflString(&Path, pcszFilename); + RT_ZERO(CreateParms); + CreateParms.CreateFlags = fCreateFlags; + HGCMSvcSetU32(&aParms[0], Root); + HGCMSvcSetPv(&aParms[1], &Path, RT_UOFFSETOF(SHFLSTRING, String) + + Path.string.u16Size); + HGCMSvcSetPv(&aParms[2], &CreateParms, sizeof(CreateParms)); + psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0, + psvcTable->pvService, SHFL_FN_CREATE, + RT_ELEMENTS(aParms), aParms, 0); + if (RT_FAILURE(callHandle.rc)) + return callHandle.rc; + if (pHandle) + *pHandle = CreateParms.Handle; + if (pResult) + *pResult = CreateParms.Result; + return VINF_SUCCESS; +} + +static int readFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT Root, + SHFLHANDLE hFile, uint64_t offSeek, uint32_t cbRead, + uint32_t *pcbRead, void *pvBuf, uint32_t cbBuf) +{ + VBOXHGCMSVCPARM aParms[SHFL_CPARMS_READ]; + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + + HGCMSvcSetU32(&aParms[0], Root); + HGCMSvcSetU64(&aParms[1], (uint64_t) hFile); + HGCMSvcSetU64(&aParms[2], offSeek); + HGCMSvcSetU32(&aParms[3], cbRead); + HGCMSvcSetPv(&aParms[4], pvBuf, cbBuf); + psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0, + psvcTable->pvService, SHFL_FN_READ, + RT_ELEMENTS(aParms), aParms, 0); + if (pcbRead) + *pcbRead = aParms[3].u.uint32; + return callHandle.rc; +} + +static int writeFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT Root, + SHFLHANDLE hFile, uint64_t offSeek, uint32_t cbWrite, + uint32_t *pcbWritten, const void *pvBuf, uint32_t cbBuf) +{ + VBOXHGCMSVCPARM aParms[SHFL_CPARMS_WRITE]; + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + + HGCMSvcSetU32(&aParms[0], Root); + HGCMSvcSetU64(&aParms[1], (uint64_t) hFile); + HGCMSvcSetU64(&aParms[2], offSeek); + HGCMSvcSetU32(&aParms[3], cbWrite); + HGCMSvcSetPv(&aParms[4], (void *)pvBuf, cbBuf); + psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0, + psvcTable->pvService, SHFL_FN_WRITE, + RT_ELEMENTS(aParms), aParms, 0); + if (pcbWritten) + *pcbWritten = aParms[3].u.uint32; + return callHandle.rc; +} + +static int flushFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root, + SHFLHANDLE handle) +{ + VBOXHGCMSVCPARM aParms[SHFL_CPARMS_FLUSH]; + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + + HGCMSvcSetU32(&aParms[0], root); + HGCMSvcSetU64(&aParms[1], handle); + psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0, + psvcTable->pvService, SHFL_FN_FLUSH, + SHFL_CPARMS_FLUSH, aParms, 0); + return callHandle.rc; +} + +static int listDir(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root, + SHFLHANDLE handle, uint32_t fFlags, + const char *pcszPath, void *pvBuf, uint32_t cbBuf, + uint32_t resumePoint, uint32_t *pcFiles) +{ + VBOXHGCMSVCPARM aParms[SHFL_CPARMS_LIST]; + union TESTSHFLSTRING Path; + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + + HGCMSvcSetU32(&aParms[0], root); + HGCMSvcSetU64(&aParms[1], handle); + HGCMSvcSetU32(&aParms[2], fFlags); + HGCMSvcSetU32(&aParms[3], cbBuf); + if (pcszPath) + { + fillTestShflString(&Path, pcszPath); + HGCMSvcSetPv(&aParms[4], &Path, RT_UOFFSETOF(SHFLSTRING, String) + + Path.string.u16Size); + } + else + HGCMSvcSetPv(&aParms[4], NULL, 0); + HGCMSvcSetPv(&aParms[5], pvBuf, cbBuf); + HGCMSvcSetU32(&aParms[6], resumePoint); + HGCMSvcSetU32(&aParms[7], 0); + psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0, + psvcTable->pvService, SHFL_FN_LIST, + RT_ELEMENTS(aParms), aParms, 0); + if (pcFiles) + *pcFiles = aParms[7].u.uint32; + return callHandle.rc; +} + +static int sfInformation(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root, + SHFLHANDLE handle, uint32_t fFlags, uint32_t cb, + SHFLFSOBJINFO *pInfo) +{ + VBOXHGCMSVCPARM aParms[SHFL_CPARMS_INFORMATION]; + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + + HGCMSvcSetU32(&aParms[0], root); + HGCMSvcSetU64(&aParms[1], handle); + HGCMSvcSetU32(&aParms[2], fFlags); + HGCMSvcSetU32(&aParms[3], cb); + HGCMSvcSetPv(&aParms[4], pInfo, cb); + psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0, + psvcTable->pvService, SHFL_FN_INFORMATION, + RT_ELEMENTS(aParms), aParms, 0); + return callHandle.rc; +} + +static int lockFile(VBOXHGCMSVCFNTABLE *psvcTable, SHFLROOT root, + SHFLHANDLE handle, int64_t offLock, uint64_t cbLock, + uint32_t fFlags) +{ + VBOXHGCMSVCPARM aParms[SHFL_CPARMS_LOCK]; + VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS }; + + HGCMSvcSetU32(&aParms[0], root); + HGCMSvcSetU64(&aParms[1], handle); + HGCMSvcSetU64(&aParms[2], offLock); + HGCMSvcSetU64(&aParms[3], cbLock); + HGCMSvcSetU32(&aParms[4], fFlags); + psvcTable->pfnCall(psvcTable->pvService, &callHandle, 0, + psvcTable->pvService, SHFL_FN_LOCK, + RT_ELEMENTS(aParms), aParms, 0); + return callHandle.rc; +} + +void testCreateFileSimple(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + const RTFILE hcFile = (RTFILE) 0x10000; + SHFLCREATERESULT Result; + int rc; + + RTTestSub(hTest, "Create file simple"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + testRTFileOpenpFile = hcFile; + rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ, NULL, + &Result); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, + !strcmp(&testRTFileOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0], + "/test/mapping/test/file"), + (hTest, "pszFilename=%s\n", &testRTFileOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0])); + RTTEST_CHECK_MSG(hTest, testRTFileOpenFlags == 0x181, + (hTest, "fOpen=%llu\n", LLUIFY(testRTFileOpenFlags))); + RTTEST_CHECK_MSG(hTest, Result == SHFL_FILE_CREATED, + (hTest, "Result=%d\n", (int) Result)); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); + RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, + (hTest, "File=%u\n", (uintptr_t)g_testRTFileCloseFile)); +} + +void testCreateFileSimpleCaseInsensitive(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + const RTFILE hcFile = (RTFILE) 0x10000; + SHFLCREATERESULT Result; + int rc; + + g_fFailIfNotLowercase = true; + + RTTestSub(hTest, "Create file case insensitive"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname", false /*fCaseSensitive*/); + testRTFileOpenpFile = hcFile; + rc = createFile(&svcTable, Root, "/TesT/FilE", SHFL_CF_ACCESS_READ, NULL, + &Result); + RTTEST_CHECK_RC_OK(hTest, rc); + + RTTEST_CHECK_MSG(hTest, + !strcmp(&testRTFileOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0], + "/test/mapping/test/file"), + (hTest, "pszFilename=%s\n", &testRTFileOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0])); + RTTEST_CHECK_MSG(hTest, testRTFileOpenFlags == 0x181, + (hTest, "fOpen=%llu\n", LLUIFY(testRTFileOpenFlags))); + RTTEST_CHECK_MSG(hTest, Result == SHFL_FILE_CREATED, + (hTest, "Result=%d\n", (int) Result)); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); + RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, + (hTest, "File=%u\n", (uintptr_t)g_testRTFileCloseFile)); + + g_fFailIfNotLowercase = false; +} + +void testCreateDirSimple(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + RTDIR hDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)]; + SHFLCREATERESULT Result; + int rc; + + RTTestSub(hTest, "Create directory simple"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + testRTDirOpen_hDir = hDir; + rc = createFile(&svcTable, Root, "test/dir", + SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ, NULL, &Result); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, + !strcmp(&testRTDirCreatePath[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0], + "/test/mapping/test/dir"), + (hTest, "pszPath=%s\n", &testRTDirCreatePath[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0])); + RTTEST_CHECK_MSG(hTest, + !strcmp(&testRTDirOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0], + "/test/mapping/test/dir"), + (hTest, "pszFilename=%s\n", &testRTDirOpenName[RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS ? 2 : 0])); + RTTEST_CHECK_MSG(hTest, Result == SHFL_FILE_CREATED, + (hTest, "Result=%d\n", (int) Result)); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); + RTTEST_CHECK_MSG(hTest, g_testRTDirClose_hDir == hDir, (hTest, "hDir=%p\n", g_testRTDirClose_hDir)); +} + +void testReadFileSimple(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + const RTFILE hcFile = (RTFILE) 0x10000; + SHFLHANDLE Handle; + const char *pcszReadData = "Data to read"; + char acBuf[sizeof(pcszReadData) + 10]; + uint32_t cbRead; + int rc; + + RTTestSub(hTest, "Read file simple"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + testRTFileOpenpFile = hcFile; + rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ, + &Handle, NULL); + RTTEST_CHECK_RC_OK(hTest, rc); + testRTFileReadData = pcszReadData; + rc = readFile(&svcTable, Root, Handle, 0, (uint32_t)strlen(pcszReadData) + 1, + &cbRead, acBuf, (uint32_t)sizeof(acBuf)); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, + !strncmp(acBuf, pcszReadData, sizeof(acBuf)), + (hTest, "pvBuf=%.*s\n", sizeof(acBuf), acBuf)); + RTTEST_CHECK_MSG(hTest, cbRead == strlen(pcszReadData) + 1, + (hTest, "cbRead=%llu\n", LLUIFY(cbRead))); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile)); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); +} + +void testWriteFileSimple(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + const RTFILE hcFile = (RTFILE) 0x10000; + SHFLHANDLE Handle; + const char *pcszWrittenData = "Data to write"; + uint32_t cbToWrite = (uint32_t)strlen(pcszWrittenData) + 1; + uint32_t cbWritten; + int rc; + + RTTestSub(hTest, "Write file simple"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + testRTFileOpenpFile = hcFile; + rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ, + &Handle, NULL); + RTTEST_CHECK_RC_OK(hTest, rc); + rc = writeFile(&svcTable, Root, Handle, 0, cbToWrite, &cbWritten, + pcszWrittenData, cbToWrite); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, + !strcmp(testRTFileWriteData, pcszWrittenData), + (hTest, "pvBuf=%s\n", testRTFileWriteData)); + RTTEST_CHECK_MSG(hTest, cbWritten == cbToWrite, + (hTest, "cbWritten=%llu\n", LLUIFY(cbWritten))); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile)); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); +} + +void testFlushFileSimple(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + const RTFILE hcFile = (RTFILE) 0x10000; + SHFLHANDLE Handle; + int rc; + + RTTestSub(hTest, "Flush file simple"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + testRTFileOpenpFile = hcFile; + rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ, + &Handle, NULL); + RTTEST_CHECK_RC_OK(hTest, rc); + rc = flushFile(&svcTable, Root, Handle); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, g_testRTFileFlushFile == hcFile, (hTest, "File=%u\n", g_testRTFileFlushFile)); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); + RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile)); +} + +void testDirListEmpty(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + RTDIR hDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)]; + SHFLHANDLE Handle; + union + { + SHFLDIRINFO DirInfo; + uint8_t abBuffer[sizeof(SHFLDIRINFO) + 2 * sizeof(RTUTF16)]; + } Buf; + uint32_t cFiles; + int rc; + + RTTestSub(hTest, "List empty directory"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + testRTDirOpen_hDir = hDir; + rc = createFile(&svcTable, Root, "test/dir", + SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ, &Handle, NULL); + RTTEST_CHECK_RC_OK(hTest, rc); + rc = listDir(&svcTable, Root, Handle, 0, NULL, &Buf.DirInfo, sizeof(Buf), 0, &cFiles); + RTTEST_CHECK_RC(hTest, rc, VERR_NO_MORE_FILES); + RTTEST_CHECK_MSG(hTest, g_testRTDirReadEx_hDir == hDir, (hTest, "Dir=%p\n", g_testRTDirReadEx_hDir)); + RTTEST_CHECK_MSG(hTest, cFiles == 0, + (hTest, "cFiles=%llu\n", LLUIFY(cFiles))); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); + RTTEST_CHECK_MSG(hTest, g_testRTDirClose_hDir == hDir, (hTest, "hDir=%p\n", g_testRTDirClose_hDir)); +} + +void testFSInfoQuerySetFMode(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + const RTFILE hcFile = (RTFILE) 0x10000; + const uint32_t fMode = 0660; + SHFLFSOBJINFO Info; + int rc; + + RTTestSub(hTest, "Query and set file size"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + SHFLHANDLE Handle = SHFL_HANDLE_NIL; + testRTFileOpenpFile = hcFile; + rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ, + &Handle, NULL); + RTTEST_CHECK_RC_OK_RETV(hTest, rc); + + RT_ZERO(Info); + testRTFileQueryInfoFMode = fMode; + rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_FILE, sizeof(Info), + &Info); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, g_testRTFileQueryInfoFile == hcFile, (hTest, "File=%u\n", g_testRTFileQueryInfoFile)); + RTTEST_CHECK_MSG(hTest, Info.Attr.fMode == fMode, + (hTest, "cbObject=%llu\n", LLUIFY(Info.cbObject))); + RT_ZERO(Info); + Info.Attr.fMode = fMode; + rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_FILE, + sizeof(Info), &Info); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, testRTFileSetFMode == fMode, + (hTest, "Size=%llu\n", LLUIFY(testRTFileSetFMode))); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); + RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile)); +} + +void testFSInfoQuerySetDirATime(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + const RTDIR hDir = (RTDIR)&g_aTestDirHandles[g_iNextDirHandle++ % RT_ELEMENTS(g_aTestDirHandles)]; + const int64_t ccAtimeNano = 100000; + SHFLFSOBJINFO Info; + SHFLHANDLE Handle; + int rc; + + RTTestSub(hTest, "Query and set directory atime"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + testRTDirOpen_hDir = hDir; + rc = createFile(&svcTable, Root, "test/dir", + SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ, &Handle, NULL); + RTTEST_CHECK_RC_OK(hTest, rc); + RT_ZERO(Info); + RTTimeSpecSetNano(&testRTDirQueryInfoATime, ccAtimeNano); + rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_FILE, sizeof(Info), + &Info); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, g_testRTDirQueryInfo_hDir == hDir, (hTest, "Dir=%p\n", g_testRTDirQueryInfo_hDir)); + RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&Info.AccessTime) == ccAtimeNano, + (hTest, "ATime=%llu\n", + LLUIFY(RTTimeSpecGetNano(&Info.AccessTime)))); + RT_ZERO(Info); + RTTimeSpecSetNano(&Info.AccessTime, ccAtimeNano); + rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_FILE, + sizeof(Info), &Info); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&testRTDirSetTimesATime) + == ccAtimeNano, + (hTest, "ATime=%llu\n", + LLUIFY(RTTimeSpecGetNano(&testRTDirSetTimesATime)))); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); + RTTEST_CHECK_MSG(hTest, g_testRTDirClose_hDir == hDir, (hTest, "hDir=%p\n", g_testRTDirClose_hDir)); +} + +void testFSInfoQuerySetFileATime(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + const RTFILE hcFile = (RTFILE) 0x10000; + const int64_t ccAtimeNano = 100000; + SHFLFSOBJINFO Info; + SHFLHANDLE Handle; + int rc; + + RTTestSub(hTest, "Query and set file atime"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + testRTFileOpenpFile = hcFile; + rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ, + &Handle, NULL); + RTTEST_CHECK_RC_OK(hTest, rc); + RT_ZERO(Info); + RTTimeSpecSetNano(&testRTFileQueryInfoATime, ccAtimeNano); + rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_FILE, sizeof(Info), + &Info); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, g_testRTFileQueryInfoFile == hcFile, (hTest, "File=%u\n", g_testRTFileQueryInfoFile)); + RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&Info.AccessTime) == ccAtimeNano, + (hTest, "ATime=%llu\n", + LLUIFY(RTTimeSpecGetNano(&Info.AccessTime)))); + RT_ZERO(Info); + RTTimeSpecSetNano(&Info.AccessTime, ccAtimeNano); + rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_FILE, + sizeof(Info), &Info); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, RTTimeSpecGetNano(&testRTFileSetTimesATime) + == ccAtimeNano, + (hTest, "ATime=%llu\n", + LLUIFY(RTTimeSpecGetNano(&testRTFileSetTimesATime)))); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); + RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile)); +} + +void testFSInfoQuerySetEndOfFile(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + const RTFILE hcFile = (RTFILE) 0x10000; + const RTFOFF cbNew = 50000; + SHFLFSOBJINFO Info; + SHFLHANDLE Handle; + int rc; + + RTTestSub(hTest, "Set end of file position"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + testRTFileOpenpFile = hcFile; + rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ, + &Handle, NULL); + RTTEST_CHECK_RC_OK(hTest, rc); + RT_ZERO(Info); + Info.cbObject = cbNew; + rc = sfInformation(&svcTable, Root, Handle, SHFL_INFO_SET | SHFL_INFO_SIZE, + sizeof(Info), &Info); + RTTEST_CHECK_RC_OK(hTest, rc); + RTTEST_CHECK_MSG(hTest, g_testRTFileSetSizeFile == hcFile, (hTest, "File=%u\n", g_testRTFileSetSizeFile)); + RTTEST_CHECK_MSG(hTest, testRTFileSetSizeSize == cbNew, + (hTest, "Size=%llu\n", LLUIFY(testRTFileSetSizeSize))); + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); + RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile)); +} + +void testLockFileSimple(RTTEST hTest) +{ + VBOXHGCMSVCFNTABLE svcTable; + VBOXHGCMSVCHELPERS svcHelpers; + SHFLROOT Root; + const RTFILE hcFile = (RTFILE) 0x10000; + const int64_t offLock = 50000; + const uint64_t cbLock = 4000; + SHFLHANDLE Handle; + int rc; + + RTTestSub(hTest, "Simple file lock and unlock"); + Root = initWithWritableMapping(hTest, &svcTable, &svcHelpers, + "/test/mapping", "testname"); + testRTFileOpenpFile = hcFile; + rc = createFile(&svcTable, Root, "/test/file", SHFL_CF_ACCESS_READ, + &Handle, NULL); + RTTEST_CHECK_RC_OK(hTest, rc); + rc = lockFile(&svcTable, Root, Handle, offLock, cbLock, SHFL_LOCK_SHARED); + RTTEST_CHECK_RC_OK(hTest, rc); +#ifdef RT_OS_WINDOWS /* Locking is a no-op elsewhere. */ + RTTEST_CHECK_MSG(hTest, g_testRTFileLockFile == hcFile, (hTest, "File=%u\n", g_testRTFileLockFile)); + RTTEST_CHECK_MSG(hTest, testRTFileLockfLock == 0, + (hTest, "fLock=%u\n", testRTFileLockfLock)); + RTTEST_CHECK_MSG(hTest, testRTFileLockOffset == offLock, + (hTest, "Offs=%llu\n", (long long) testRTFileLockOffset)); + RTTEST_CHECK_MSG(hTest, testRTFileLockSize == cbLock, + (hTest, "Size=%llu\n", LLUIFY(testRTFileLockSize))); +#endif + rc = lockFile(&svcTable, Root, Handle, offLock, cbLock, SHFL_LOCK_CANCEL); + RTTEST_CHECK_RC_OK(hTest, rc); +#ifdef RT_OS_WINDOWS + RTTEST_CHECK_MSG(hTest, g_testRTFileUnlockFile == hcFile, (hTest, "File=%u\n", g_testRTFileUnlockFile)); + RTTEST_CHECK_MSG(hTest, testRTFileUnlockOffset == offLock, + (hTest, "Offs=%llu\n", + (long long) testRTFileUnlockOffset)); + RTTEST_CHECK_MSG(hTest, testRTFileUnlockSize == cbLock, + (hTest, "Size=%llu\n", LLUIFY(testRTFileUnlockSize))); +#endif + unmapAndRemoveMapping(hTest, &svcTable, Root, "testname"); + AssertReleaseRC(svcTable.pfnDisconnect(NULL, 0, svcTable.pvService)); + AssertReleaseRC(svcTable.pfnUnload(NULL)); + RTTestGuardedFree(hTest, svcTable.pvService); + RTTEST_CHECK_MSG(hTest, g_testRTFileCloseFile == hcFile, (hTest, "File=%u\n", g_testRTFileCloseFile)); +} + + +/********************************************************************************************************************************* +* Main code * +*********************************************************************************************************************************/ + +static void testAPI(RTTEST hTest) +{ + testMappingsQuery(hTest); + testMappingsQueryName(hTest); + testMapFolder(hTest); + testUnmapFolder(hTest); + testCreate(hTest); + testClose(hTest); + testRead(hTest); + testWrite(hTest); + testLock(hTest); + testFlush(hTest); + testDirList(hTest); + testReadLink(hTest); + testFSInfo(hTest); + testRemove(hTest); + testRename(hTest); + testSymlink(hTest); + testMappingsAdd(hTest); + testMappingsRemove(hTest); + /* testSetStatusLed(hTest); */ +} + +int main(int argc, char **argv) +{ + RT_NOREF1(argc); + RTEXITCODE rcExit = RTTestInitAndCreate(RTPathFilename(argv[0]), &g_hTest); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + RTTestBanner(g_hTest); + testAPI(g_hTest); + return RTTestSummaryAndDestroy(g_hTest); +} diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h new file mode 100644 index 00000000..93a9995a --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/testcase/tstSharedFolderService.h @@ -0,0 +1,130 @@ +/** @file + * VBox Shared Folders testcase stub redefinitions. + */ + +/* + * Copyright (C) 2011-2019 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. + */ + +#ifndef VBOX_INCLUDED_SRC_SharedFolders_testcase_tstSharedFolderService_h +#define VBOX_INCLUDED_SRC_SharedFolders_testcase_tstSharedFolderService_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +/* Grumble... if the coding style let us use the anonymous "struct RTTESTINT *" + * instead of "PRTTEST" here we wouldn't need to unnecessarily include this. */ +#include <iprt/test.h> + +void testMappingsQuery(RTTEST hTest); +/* Sub-tests for testMappingsQuery(). */ +void testMappingsQuerySimple(RTTEST hTest); +void testMappingsQueryTooFewBuffers(RTTEST hTest); +void testMappingsQueryAutoMount(RTTEST hTest); +void testMappingsQueryArrayWrongSize(RTTEST hTest); + +void testMappingsQueryName(RTTEST hTest); +/* Sub-tests for testMappingsQueryName(). */ +void testMappingsQueryNameValid(RTTEST hTest); +void testMappingsQueryNameInvalid(RTTEST hTest); +void testMappingsQueryNameBadBuffer(RTTEST hTest); + +void testMapFolder(RTTEST hTest); +/* Sub-tests for testMapFolder(). */ +void testMapFolderValid(RTTEST hTest); +void testMapFolderInvalid(RTTEST hTest); +void testMapFolderTwice(RTTEST hTest); +void testMapFolderDelimiter(RTTEST hTest); +void testMapFolderCaseSensitive(RTTEST hTest); +void testMapFolderCaseInsensitive(RTTEST hTest); +void testMapFolderBadParameters(RTTEST hTest); + +void testUnmapFolder(RTTEST hTest); +/* Sub-tests for testUnmapFolder(). */ +void testUnmapFolderValid(RTTEST hTest); +void testUnmapFolderInvalid(RTTEST hTest); +void testUnmapFolderBadParameters(RTTEST hTest); + +void testCreate(RTTEST hTest); +/* Sub-tests for testCreate(). */ +void testCreateFileSimple(RTTEST hTest); +void testCreateFileSimpleCaseInsensitive(RTTEST hTest); +void testCreateDirSimple(RTTEST hTest); +void testCreateBadParameters(RTTEST hTest); + +void testClose(RTTEST hTest); +/* Sub-tests for testClose(). */ +void testCloseBadParameters(RTTEST hTest); + +void testRead(RTTEST hTest); +/* Sub-tests for testRead(). */ +void testReadBadParameters(RTTEST hTest); +void testReadFileSimple(RTTEST hTest); + +void testWrite(RTTEST hTest); +/* Sub-tests for testWrite(). */ +void testWriteBadParameters(RTTEST hTest); +void testWriteFileSimple(RTTEST hTest); + +void testLock(RTTEST hTest); +/* Sub-tests for testLock(). */ +void testLockBadParameters(RTTEST hTest); +void testLockFileSimple(RTTEST hTest); + +void testFlush(RTTEST hTest); +/* Sub-tests for testFlush(). */ +void testFlushBadParameters(RTTEST hTest); +void testFlushFileSimple(RTTEST hTest); + +void testDirList(RTTEST hTest); +/* Sub-tests for testDirList(). */ +void testDirListBadParameters(RTTEST hTest); +void testDirListEmpty(RTTEST hTest); + +void testReadLink(RTTEST hTest); +/* Sub-tests for testReadLink(). */ +void testReadLinkBadParameters(RTTEST hTest); + +void testFSInfo(RTTEST hTest); +/* Sub-tests for testFSInfo(). */ +void testFSInfoBadParameters(RTTEST hTest); +void testFSInfoQuerySetFMode(RTTEST hTest); +void testFSInfoQuerySetDirATime(RTTEST hTest); +void testFSInfoQuerySetFileATime(RTTEST hTest); +void testFSInfoQuerySetEndOfFile(RTTEST hTest); + +void testRemove(RTTEST hTest); +/* Sub-tests for testRemove(). */ +void testRemoveBadParameters(RTTEST hTest); + +void testRename(RTTEST hTest); +/* Sub-tests for testRename(). */ +void testRenameBadParameters(RTTEST hTest); + +void testSymlink(RTTEST hTest); +/* Sub-tests for testSymlink(). */ +void testSymlinkBadParameters(RTTEST hTest); + +void testMappingsAdd(RTTEST hTest); +/* Sub-tests for testMappingsAdd(). */ +void testMappingsAddBadParameters(RTTEST hTest); + +void testMappingsRemove(RTTEST hTest); +/* Sub-tests for testMappingsRemove(). */ +void testMappingsRemoveBadParameters(RTTEST hTest); + +#if 0 /* Where should this go? */ +void testSetStatusLed(RTTEST hTest); +/* Sub-tests for testStatusLed(). */ +void testSetStatusLedBadParameters(RTTEST hTest); +#endif + +#endif /* !VBOX_INCLUDED_SRC_SharedFolders_testcase_tstSharedFolderService_h */ diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp new file mode 100644 index 00000000..6920f8dd --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/testcase/tstShflCase.cpp @@ -0,0 +1,442 @@ +/** @file + * Testcase for shared folder case conversion code. + */ + +/* + * Copyright (C) 2006-2019 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_MISC +#define LOG_ENABLED +#include <VBox/shflsvc.h> +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/fs.h> +#include <iprt/dir.h> +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/uni.h> +#include <stdio.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/* Override slash for non-windows hosts. */ +#undef RTPATH_DELIMITER +#define RTPATH_DELIMITER '\\' + +/* Use our own RTPath and RTDir methods. */ +#define RTPathQueryInfo rtPathQueryInfo +#define RTDirOpenFiltered rtDirOpenFiltered +#define RTDirClose rtDirClose +#define RTDirReadEx rtDirReadEx + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static int iDirList = 0; +static int iDirFile = 0; + +static const char *g_apszDirs[] = +{ + "c:", + "c:\\test dir", + "c:\\test dir\\SUBDIR", +}; + +static const char *g_apszDirsC[] = +{ + ".", + "..", + "test dir" +}; + +static const char *g_apszTestdirEntries[] = +{ + ".", + "..", + "SUBDIR", + "a.bat", + "aTestJe.bat", + "aTestje.bat", + "b.bat", + "c.bat", + "d.bat", + "e.bat", + "f.bat", + "g.bat", + "h.bat", + "x.bat", + "z.bat", +}; + +static const char *g_apszSUBDIREntries[] = +{ + ".", + "..", + "a.bat", + "aTestJe.bat", + "aTestje.bat", + "b.bat", + "c.bat", + "d.bat", + "e.bat", + "f.bat", + "g.bat", + "h.bat", + "x.bat", + "z.bat", +}; + +int rtDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags) +{ + RT_NOREF2(enmFilter, fFlags); + if (!strcmp(pszPath, "c:\\*")) + iDirList = 1; + else if (!strcmp(pszPath, "c:\\test dir\\*")) + iDirList = 2; + else if (!strcmp(pszPath, "c:\\test dir\\SUBDIR\\*")) + iDirList = 3; + else + AssertFailed(); + + *phDir = (RTDIR)1; + return VINF_SUCCESS; +} + +int rtDirClose(RTDIR hDir) +{ + RT_NOREF1(hDir); + iDirFile = 0; + return VINF_SUCCESS; +} + +int rtDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags) +{ + RT_NOREF4(hDir, pcbDirEntry, enmAdditionalAttribs, fFlags); + switch (iDirList) + { + case 1: + if (iDirFile == RT_ELEMENTS(g_apszDirsC)) + return VERR_NO_MORE_FILES; + pDirEntry->cbName = (uint16_t)strlen(g_apszDirsC[iDirFile]); + strcpy(pDirEntry->szName, g_apszDirsC[iDirFile++]); + break; + case 2: + if (iDirFile == RT_ELEMENTS(g_apszTestdirEntries)) + return VERR_NO_MORE_FILES; + pDirEntry->cbName = (uint16_t)strlen(g_apszTestdirEntries[iDirFile]); + strcpy(pDirEntry->szName, g_apszTestdirEntries[iDirFile++]); + break; + case 3: + if (iDirFile == RT_ELEMENTS(g_apszSUBDIREntries)) + return VERR_NO_MORE_FILES; + pDirEntry->cbName = (uint16_t)strlen(g_apszSUBDIREntries[iDirFile]); + strcpy(pDirEntry->szName, g_apszSUBDIREntries[iDirFile++]); + break; + } + return VINF_SUCCESS; +} + +int rtPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs) +{ + RT_NOREF2(pObjInfo, enmAdditionalAttribs); + int cMax; + + /* first try g_apszDirs */ + for (unsigned int i=0;i<RT_ELEMENTS(g_apszDirs);i++) + { + if(!strcmp(pszPath, g_apszDirs[i])) + return VINF_SUCCESS; + } + + const char **papszDirList; + switch (iDirList) + { + case 1: + cMax = RT_ELEMENTS(g_apszDirsC); + papszDirList = g_apszDirsC; + break; + case 2: + cMax = RT_ELEMENTS(g_apszTestdirEntries); + papszDirList = g_apszTestdirEntries; + break; + case 3: + cMax = RT_ELEMENTS(g_apszSUBDIREntries); + papszDirList = g_apszSUBDIREntries; + break; + default: + return VERR_FILE_NOT_FOUND; + } + for (int i = 0; i < cMax; i++) + { + if (!strcmp(pszPath, papszDirList[i])) + return VINF_SUCCESS; + } + return VERR_FILE_NOT_FOUND; +} + +static int vbsfCorrectCasing(char *pszFullPath, char *pszStartComponent) +{ + PRTDIRENTRYEX pDirEntry = NULL; + uint32_t cbDirEntry; + size_t cbComponent; + int rc = VERR_FILE_NOT_FOUND; + RTDIR hSearch = NIL_RTDIR; + char szWildCard[4]; + + Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent)); + + cbComponent = strlen(pszStartComponent); + + cbDirEntry = 4096; + pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry); + if (pDirEntry == 0) + { + AssertFailed(); + return VERR_NO_MEMORY; + } + + /** @todo this is quite inefficient, especially for directories with many files */ + Assert(pszFullPath < pszStartComponent-1); + Assert(*(pszStartComponent-1) == RTPATH_DELIMITER); + *(pszStartComponent-1) = 0; + strcpy(pDirEntry->szName, pszFullPath); + szWildCard[0] = RTPATH_DELIMITER; + szWildCard[1] = '*'; + szWildCard[2] = 0; + strcat(pDirEntry->szName, szWildCard); + + rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0 /*fFlags*/); + *(pszStartComponent-1) = RTPATH_DELIMITER; + if (RT_FAILURE(rc)) + goto end; + + for(;;) + { + size_t cbDirEntrySize = cbDirEntry; + + rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK); + if (rc == VERR_NO_MORE_FILES) + break; + + if (VINF_SUCCESS != rc && rc != VWRN_NO_DIRENT_INFO) + { + AssertFailed(); + if (rc != VERR_NO_TRANSLATION) + break; + else + continue; + } + + Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0])); + if ( pDirEntry->cbName == cbComponent + && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0])) + { + Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent)); + strcpy(pszStartComponent, &pDirEntry->szName[0]); + rc = VINF_SUCCESS; + break; + } + } + if (RT_FAILURE(rc)) + Log(("vbsfCorrectCasing %s failed with %d\n", pszStartComponent, rc)); + +end: + if (pDirEntry) + RTMemFree(pDirEntry); + + if (hSearch) + RTDirClose(hSearch); + return rc; +} + + + +int testCase(char *pszFullPath, bool fWildCard = false) +{ + int rc; + RTFSOBJINFO info; + char *pszWildCardComponent = NULL; + + if (fWildCard) + { + /* strip off the last path component, that contains the wildcard(s) */ + size_t len = strlen(pszFullPath); + char *src = pszFullPath + len - 1; + + while(src > pszFullPath) + { + if (*src == RTPATH_DELIMITER) + break; + src--; + } + if (*src == RTPATH_DELIMITER) + { + bool fHaveWildcards = false; + char *temp = src; + + while(*temp) + { + char uc = *temp; + /** @todo should depend on the guest OS */ + if (uc == '*' || uc == '?' || uc == '>' || uc == '<' || uc == '"') + { + fHaveWildcards = true; + break; + } + temp++; + } + + if (fHaveWildcards) + { + pszWildCardComponent = src; + *pszWildCardComponent = 0; + } + } + } + + rc = RTPathQueryInfo(pszFullPath, &info, RTFSOBJATTRADD_NOTHING); + if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) + { + size_t len = strlen(pszFullPath); + char *src = pszFullPath + len - 1; + + Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath)); + + /* Find partial path that's valid */ + while(src > pszFullPath) + { + if (*src == RTPATH_DELIMITER) + { + *src = 0; + rc = RTPathQueryInfo (pszFullPath, &info, RTFSOBJATTRADD_NOTHING); + *src = RTPATH_DELIMITER; + if (rc == VINF_SUCCESS) + { +#ifdef DEBUG + *src = 0; + Log(("Found valid partial path %s\n", pszFullPath)); + *src = RTPATH_DELIMITER; +#endif + break; + } + } + + src--; + } + Assert(*src == RTPATH_DELIMITER && RT_SUCCESS(rc)); + if ( *src == RTPATH_DELIMITER + && RT_SUCCESS(rc)) + { + src++; + for(;;) + { + char *end = src; + bool fEndOfString = true; + + while(*end) + { + if (*end == RTPATH_DELIMITER) + break; + end++; + } + + if (*end == RTPATH_DELIMITER) + { + fEndOfString = false; + *end = 0; + rc = RTPathQueryInfo(src, &info, RTFSOBJATTRADD_NOTHING); + Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND); + } + else + if (end == src) + rc = VINF_SUCCESS; /* trailing delimiter */ + else + rc = VERR_FILE_NOT_FOUND; + + if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) + { + /* path component is invalid; try to correct the casing */ + rc = vbsfCorrectCasing(pszFullPath, src); + if (RT_FAILURE(rc)) + { + if (!fEndOfString) + *end = RTPATH_DELIMITER; + break; + } + } + + if (fEndOfString) + break; + + *end = RTPATH_DELIMITER; + src = end + 1; + } + if (RT_FAILURE(rc)) + Log(("Unable to find suitable component rc=%d\n", rc)); + } + else + rc = VERR_FILE_NOT_FOUND; + + } + if (pszWildCardComponent) + *pszWildCardComponent = RTPATH_DELIMITER; + + if (RT_SUCCESS(rc)) + Log(("New valid path %s\n", pszFullPath)); + else + Log(("Old invalid path %s\n", pszFullPath)); + return rc; +} + + +int main() +{ + char szTest[128]; + + RTR3InitExeNoArguments(0); + RTLogFlush(NULL); + RTLogDestinations(NULL, "stdout"); + RTLogGroupSettings(NULL, "misc=~0"); + RTLogFlags(NULL, "unbuffered"); + + strcpy(szTest, "c:\\test Dir\\z.bAt"); + testCase(szTest); + strcpy(szTest, "c:\\test dir\\z.bAt"); + testCase(szTest); + strcpy(szTest, "c:\\test dir\\SUBDIR\\z.bAt"); + testCase(szTest); + strcpy(szTest, "c:\\test dir\\SUBDiR\\atestje.bat"); + testCase(szTest); + strcpy(szTest, "c:\\TEST dir\\subDiR\\aTestje.baT"); + testCase(szTest); + strcpy(szTest, "c:\\TEST dir\\subDiR\\*"); + testCase(szTest, true); + strcpy(szTest, "c:\\TEST dir\\subDiR\\"); + testCase(szTest ,true); + strcpy(szTest, "c:\\test dir\\SUBDIR\\"); + testCase(szTest); + strcpy(szTest, "c:\\test dir\\invalid\\SUBDIR\\test.bat"); + testCase(szTest); + return 0; +} + diff --git a/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp b/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp new file mode 100644 index 00000000..ab4e6a91 --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/testcase/tstShflSizes.cpp @@ -0,0 +1,134 @@ +/** @file + * tstShflSize - Testcase for shared folder structure sizes. + * Run this on Linux and Windows, then compare. + */ + +/* + * Copyright (C) 2006-2019 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 * +*********************************************************************************************************************************/ +#include <VBox/shflsvc.h> +#include <iprt/string.h> +#include <stdio.h> + +#define STRUCT(t, size) \ + do { \ + if (fPrintChecks) \ + printf(" STRUCT(" #t ", %d);\n", (int)sizeof(t)); \ + else if ((size) != sizeof(t)) \ + { \ + printf("%30s: %d expected %d!\n", #t, (int)sizeof(t), (size)); \ + cErrors++; \ + } \ + else if (!fQuiet)\ + printf("%30s: %d\n", #t, (int)sizeof(t)); \ + } while (0) + + +int main(int argc, char **argv) +{ + unsigned cErrors = 0; + + /* + * Prints the code below if any argument was giving. + */ + bool fQuiet = argc == 2 && !strcmp(argv[1], "quiet"); + bool fPrintChecks = !fQuiet && argc != 1; + + printf("tstShflSizes: TESTING\n"); + + /* + * The checks. + */ + STRUCT(SHFLROOT, 4); + STRUCT(SHFLHANDLE, 8); + STRUCT(SHFLSTRING, 6); + STRUCT(SHFLCREATERESULT, 4); + STRUCT(SHFLCREATEPARMS, 108); + STRUCT(SHFLMAPPING, 8); + STRUCT(SHFLDIRINFO, 128); + STRUCT(SHFLVOLINFO, 40); + STRUCT(SHFLFSOBJATTR, 44); + STRUCT(SHFLFSOBJINFO, 92); +#ifdef VBOX_WITH_64_BITS_GUESTS +/* The size of the guest structures depends on the current architecture bit count (ARCH_BITS) + * because the HGCMFunctionParameter structure differs in 32 and 64 bit guests. + * The host VMMDev device takes care about this. + * + * Therefore this testcase verifies whether structure sizes are correct for the current ARCH_BITS. + */ +# if ARCH_BITS == 64 + STRUCT(VBoxSFQueryMappings, 88); + STRUCT(VBoxSFQueryMapName, 72); + STRUCT(VBoxSFMapFolder_Old, 88); + STRUCT(VBoxSFMapFolder, 104); + STRUCT(VBoxSFUnmapFolder, 56); + STRUCT(VBoxSFCreate, 88); + STRUCT(VBoxSFClose, 72); + STRUCT(VBoxSFRead, 120); + STRUCT(VBoxSFWrite, 120); + STRUCT(VBoxSFLock, 120); + STRUCT(VBoxSFFlush, 72); + STRUCT(VBoxSFList, 168); + STRUCT(VBoxSFInformation, 120); + STRUCT(VBoxSFRemove, 88); + STRUCT(VBoxSFRename, 104); +# elif ARCH_BITS == 32 + STRUCT(VBoxSFQueryMappings, 24+52); + STRUCT(VBoxSFQueryMapName, 24+40); /* this was changed from 52 in 21976 after VBox-1.4. */ + STRUCT(VBoxSFMapFolder_Old, 24+52); + STRUCT(VBoxSFMapFolder, 24+64); + STRUCT(VBoxSFUnmapFolder, 24+28); + STRUCT(VBoxSFCreate, 24+52); + STRUCT(VBoxSFClose, 24+40); + STRUCT(VBoxSFRead, 24+76); + STRUCT(VBoxSFWrite, 24+76); + STRUCT(VBoxSFLock, 24+76); + STRUCT(VBoxSFFlush, 24+40); + STRUCT(VBoxSFList, 24+112); + STRUCT(VBoxSFInformation, 24+76); + STRUCT(VBoxSFRemove, 24+52); + STRUCT(VBoxSFRename, 24+64); +# else +# error "Unsupported ARCH_BITS" +# endif /* ARCH_BITS */ +#else + STRUCT(VBoxSFQueryMappings, 24+52); + STRUCT(VBoxSFQueryMapName, 24+40); /* this was changed from 52 in 21976 after VBox-1.4. */ + STRUCT(VBoxSFMapFolder_Old, 24+52); + STRUCT(VBoxSFMapFolder, 24+64); + STRUCT(VBoxSFUnmapFolder, 24+28); + STRUCT(VBoxSFCreate, 24+52); + STRUCT(VBoxSFClose, 24+40); + STRUCT(VBoxSFRead, 24+76); + STRUCT(VBoxSFWrite, 24+76); + STRUCT(VBoxSFLock, 24+76); + STRUCT(VBoxSFFlush, 24+40); + STRUCT(VBoxSFList, 24+112); + STRUCT(VBoxSFInformation, 24+76); + STRUCT(VBoxSFRemove, 24+52); + STRUCT(VBoxSFRename, 24+64); +#endif /* VBOX_WITH_64_BITS_GUESTS */ + + /* + * The summary. + */ + if (!cErrors) + printf("tstShflSizes: SUCCESS\n"); + else + printf("tstShflSizes: FAILURE - %d errors\n", cErrors); + return !!cErrors; +} + diff --git a/src/VBox/HostServices/SharedFolders/teststubs.h b/src/VBox/HostServices/SharedFolders/teststubs.h new file mode 100644 index 00000000..13d5ebee --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/teststubs.h @@ -0,0 +1,87 @@ +/** @file + * VBox Shared Folders testcase stub redefinitions. + */ + +/* + * Copyright (C) 2011-2019 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. + */ + +/** + * Macros for renaming iprt file operations to redirect them to testcase + * stub functions (mocks). The religiously correct way to do this would be + * to make the service use a file operations structure with function pointers + * but I'm not sure that would be universally appreciated. */ + +#ifndef VBOX_INCLUDED_SRC_SharedFolders_teststubs_h +#define VBOX_INCLUDED_SRC_SharedFolders_teststubs_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/dir.h> +#include <iprt/time.h> + +#define RTDirClose testRTDirClose +extern int testRTDirClose(RTDIR hDir); +#define RTDirCreate testRTDirCreate +extern int testRTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate); +#define RTDirOpen testRTDirOpen +extern int testRTDirOpen(RTDIR *phDir, const char *pszPath); +#define RTDirOpenFiltered testRTDirOpenFiltered +extern int testRTDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags); +#define RTDirQueryInfo testRTDirQueryInfo +extern int testRTDirQueryInfo(RTDIR hDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs); +#define RTDirRemove testRTDirRemove +extern int testRTDirRemove(const char *pszPath); +#define RTDirReadEx testRTDirReadEx +extern int testRTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags); +#define RTDirSetTimes testRTDirSetTimes +extern int testRTDirSetTimes(RTDIR hDir, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime); +#define RTFileClose testRTFileClose +extern int testRTFileClose(RTFILE hFile); +#define RTFileDelete testRTFileDelete +extern int testRTFileDelete(const char *pszFilename); +#define RTFileFlush testRTFileFlush +extern int testRTFileFlush(RTFILE hFile); +#define RTFileLock testRTFileLock +extern int testRTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock); +#define RTFileOpen testRTFileOpen +extern int testRTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen); +#define RTFileQueryInfo testRTFileQueryInfo +extern int testRTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs); +#define RTFileRead testRTFileRead +extern int testRTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead); +#define RTFileSetMode testRTFileSetMode +extern int testRTFileSetMode(RTFILE hFile, RTFMODE fMode); +#define RTFileSetSize testRTFileSetSize +extern int testRTFileSetSize(RTFILE hFile, uint64_t cbSize); +#define RTFileSetTimes testRTFileSetTimes +extern int testRTFileSetTimes(RTFILE hFile, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime); +#define RTFileSeek testRTFileSeek +extern int testRTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual); +#define RTFileUnlock testRTFileUnlock +extern int testRTFileUnlock(RTFILE hFile, int64_t offLock, uint64_t cbLock); +#define RTFileWrite testRTFileWrite +extern int testRTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten); +#define RTFsQueryProperties testRTFsQueryProperties +extern int testRTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties); +#define RTFsQuerySerial testRTFsQuerySerial +extern int testRTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial); +#define RTFsQuerySizes testRTFsQuerySizes +extern int testRTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree, uint32_t *pcbBlock, uint32_t *pcbSector); +#define RTPathQueryInfoEx testRTPathQueryInfoEx +extern int testRTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags); +#define RTSymlinkDelete testRTSymlinkDelete +extern int testRTSymlinkDelete(const char *pszSymlink, uint32_t fDelete); +#define RTSymlinkRead testRTSymlinkRead +extern int testRTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead); + +#endif /* !VBOX_INCLUDED_SRC_SharedFolders_teststubs_h */ diff --git a/src/VBox/HostServices/SharedFolders/vbsf.cpp b/src/VBox/HostServices/SharedFolders/vbsf.cpp new file mode 100644 index 00000000..e346b3f8 --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/vbsf.cpp @@ -0,0 +1,2107 @@ +/* $Id: vbsf.cpp $ */ +/** @file + * Shared Folders - VBox Shared Folders. + */ + +/* + * Copyright (C) 2006-2019 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_SHARED_FOLDERS +#ifdef UNITTEST +# include "testcase/tstSharedFolderService.h" +#endif + +#include "vbsfpath.h" +#include "mappings.h" +#include "vbsf.h" +#include "shflhandle.h" + +#include <VBox/AssertGuest.h> +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/fs.h> +#include <iprt/dir.h> +#include <iprt/file.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/symlink.h> +#include <iprt/uni.h> +#include <iprt/stream.h> +#ifdef RT_OS_DARWIN +# include <Carbon/Carbon.h> +#endif + +#ifdef UNITTEST +# include "teststubs.h" +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK) + + +/** + * @todo find a better solution for supporting the execute bit for non-windows + * guests on windows host. Search for "0111" to find all the relevant places. + */ + +void vbsfStripLastComponent(char *pszFullPath, uint32_t cbFullPathRoot) +{ + RTUNICP cp; + + /* Do not strip root. */ + char *s = pszFullPath + cbFullPathRoot; + char *delimSecondLast = NULL; + char *delimLast = NULL; + + LogFlowFunc(("%s -> %s\n", pszFullPath, s)); + + for (;;) + { + cp = RTStrGetCp(s); + + if (cp == RTUNICP_INVALID || cp == 0) + { + break; + } + + if (cp == RTPATH_DELIMITER) + { + if (delimLast != NULL) + { + delimSecondLast = delimLast; + } + + delimLast = s; + } + + s = RTStrNextCp(s); + } + + if (cp == 0) + { + if (delimLast + 1 == s) + { + if (delimSecondLast) + { + *delimSecondLast = 0; + } + else if (delimLast) + { + *delimLast = 0; + } + } + else + { + if (delimLast) + { + *delimLast = 0; + } + } + } + + LogFlowFunc(("%s, %s, %s\n", pszFullPath, delimLast, delimSecondLast)); +} + +static int vbsfBuildFullPath(SHFLCLIENTDATA *pClient, SHFLROOT root, PSHFLSTRING pPath, + uint32_t cbPath, char **ppszFullPath, uint32_t *pcbFullPathRoot, + bool fWildCard = false, bool fPreserveLastComponent = false) +{ + char *pszHostPath = NULL; + uint32_t fu32PathFlags = 0; + uint32_t fu32Options = VBSF_O_PATH_CHECK_ROOT_ESCAPE + | (fWildCard? VBSF_O_PATH_WILDCARD: 0) + | (fPreserveLastComponent? VBSF_O_PATH_PRESERVE_LAST_COMPONENT: 0); + + int rc = vbsfPathGuestToHost(pClient, root, pPath, cbPath, + &pszHostPath, pcbFullPathRoot, fu32Options, &fu32PathFlags); + if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) + { + LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*s]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length, &pPath->String.utf8[0], pszHostPath, rc)); + } + else + { + LogRel2(("SharedFolders: GuestToHost 0x%RX32 [%.*ls]->[%s] %Rrc\n", fu32PathFlags, pPath->u16Length / 2, &pPath->String.ucs2[0], pszHostPath, rc)); + } + + if (RT_SUCCESS(rc)) + { + if (ppszFullPath) + *ppszFullPath = pszHostPath; + } + return rc; +} + +static void vbsfFreeFullPath(char *pszFullPath) +{ + vbsfFreeHostPath(pszFullPath); +} + +typedef enum VBSFCHECKACCESS +{ + VBSF_CHECK_ACCESS_READ = 0, + VBSF_CHECK_ACCESS_WRITE = 1 +} VBSFCHECKACCESS; + +/** + * Check if the handle data is valid and the operation is allowed on the shared folder. + * + * @returns IPRT status code + * @param pClient Data structure describing the client accessing the shared folder + * @param root The index of the shared folder in the table of mappings. + * @param pHandle Information about the file or directory object. + * @param enmCheckAccess Whether the operation needs read only or write access. + */ +static int vbsfCheckHandleAccess(SHFLCLIENTDATA *pClient, SHFLROOT root, + SHFLFILEHANDLE *pHandle, VBSFCHECKACCESS enmCheckAccess) +{ + /* Handle from the same 'root' index? */ + if (RT_LIKELY(RT_VALID_PTR(pHandle) && root == pHandle->root)) + { /* likely */ } + else + return VERR_INVALID_HANDLE; + + /* Check if the guest is still allowed to access this share. + * vbsfMappingsQueryWritable returns error if the shared folder has been removed from the VM settings. + */ + bool fWritable; + int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable); + if (RT_SUCCESS(rc)) + { /* likely */ } + else + return VERR_ACCESS_DENIED; + + if (enmCheckAccess == VBSF_CHECK_ACCESS_WRITE) + { + /* Operation requires write access. Check if the shared folder is writable too. */ + if (RT_LIKELY(fWritable)) + { /* likely */ } + else + return VERR_WRITE_PROTECT; + } + + return VINF_SUCCESS; +} + +/** + * Convert shared folder create flags (see include/iprt/shflsvc.h) into iprt create flags. + * + * @returns iprt status code + * @param fWritable whether the shared folder is writable + * @param fShflFlags shared folder create flags + * @param fMode file attributes + * @param handleInitial initial handle + * @retval pfOpen iprt create flags + */ +static int vbsfConvertFileOpenFlags(bool fWritable, unsigned fShflFlags, RTFMODE fMode, SHFLHANDLE handleInitial, uint32_t *pfOpen) +{ + uint32_t fOpen = 0; + int rc = VINF_SUCCESS; + + if ( (fMode & RTFS_DOS_MASK) != 0 + && (fMode & RTFS_UNIX_MASK) == 0) + { + /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*. + * @todo this is based on rtFsModeNormalize/rtFsModeFromDos. + * May be better to use RTFsModeNormalize here. + */ + fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH; + /* x for directories. */ + if (fMode & RTFS_DOS_DIRECTORY) + fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH; + /* writable? */ + if (!(fMode & RTFS_DOS_READONLY)) + fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH; + + /* Set the requested mode using only allowed bits. */ + fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK; + } + else + { + /* Old linux and solaris additions did not initialize the Info.Attr.fMode field + * and it contained random bits from stack. Detect this using the handle field value + * passed from the guest: old additions set it (incorrectly) to 0, new additions + * set it to SHFL_HANDLE_NIL(~0). + */ + if (handleInitial == 0) + { + /* Old additions. Do nothing, use default mode. */ + } + else + { + /* New additions or Windows additions. Set the requested mode using only allowed bits. + * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode + * will be set in fOpen. + */ + fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK; + } + } + + switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_RW)) + { + default: + case SHFL_CF_ACCESS_NONE: + { +#ifdef RT_OS_WINDOWS + if (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR) != SHFL_CF_ACCESS_ATTR_NONE) + fOpen |= RTFILE_O_ATTR_ONLY; + else +#endif + fOpen |= RTFILE_O_READ; + Log(("FLAG: SHFL_CF_ACCESS_NONE\n")); + break; + } + + case SHFL_CF_ACCESS_READ: + { + fOpen |= RTFILE_O_READ; + Log(("FLAG: SHFL_CF_ACCESS_READ\n")); + break; + } + + case SHFL_CF_ACCESS_WRITE: + { + fOpen |= RTFILE_O_WRITE; + Log(("FLAG: SHFL_CF_ACCESS_WRITE\n")); + break; + } + + case SHFL_CF_ACCESS_READWRITE: + { + fOpen |= RTFILE_O_READWRITE; + Log(("FLAG: SHFL_CF_ACCESS_READWRITE\n")); + break; + } + } + + if (fShflFlags & SHFL_CF_ACCESS_APPEND) + { + fOpen |= RTFILE_O_APPEND; + } + + switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_ATTR)) + { + default: + case SHFL_CF_ACCESS_ATTR_NONE: + { + fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT; + Log(("FLAG: SHFL_CF_ACCESS_ATTR_NONE\n")); + break; + } + + case SHFL_CF_ACCESS_ATTR_READ: + { + fOpen |= RTFILE_O_ACCESS_ATTR_READ; + Log(("FLAG: SHFL_CF_ACCESS_ATTR_READ\n")); + break; + } + + case SHFL_CF_ACCESS_ATTR_WRITE: + { + fOpen |= RTFILE_O_ACCESS_ATTR_WRITE; + Log(("FLAG: SHFL_CF_ACCESS_ATTR_WRITE\n")); + break; + } + + case SHFL_CF_ACCESS_ATTR_READWRITE: + { + fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE; + Log(("FLAG: SHFL_CF_ACCESS_ATTR_READWRITE\n")); + break; + } + } + + /* Sharing mask */ + switch (BIT_FLAG(fShflFlags, SHFL_CF_ACCESS_MASK_DENY)) + { + default: + case SHFL_CF_ACCESS_DENYNONE: + fOpen |= RTFILE_O_DENY_NONE; + Log(("FLAG: SHFL_CF_ACCESS_DENYNONE\n")); + break; + + case SHFL_CF_ACCESS_DENYREAD: + fOpen |= RTFILE_O_DENY_READ; + Log(("FLAG: SHFL_CF_ACCESS_DENYREAD\n")); + break; + + case SHFL_CF_ACCESS_DENYWRITE: + fOpen |= RTFILE_O_DENY_WRITE; + Log(("FLAG: SHFL_CF_ACCESS_DENYWRITE\n")); + break; + + case SHFL_CF_ACCESS_DENYALL: + fOpen |= RTFILE_O_DENY_ALL; + Log(("FLAG: SHFL_CF_ACCESS_DENYALL\n")); + break; + } + + /* Open/Create action mask */ + switch (BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_EXISTS)) + { + case SHFL_CF_ACT_OPEN_IF_EXISTS: + if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) + { + fOpen |= RTFILE_O_OPEN_CREATE; + Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n")); + } + else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) + { + fOpen |= RTFILE_O_OPEN; + Log(("FLAGS: SHFL_CF_ACT_OPEN_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n")); + } + else + { + Log(("FLAGS: invalid open/create action combination\n")); + rc = VERR_INVALID_PARAMETER; + } + break; + case SHFL_CF_ACT_FAIL_IF_EXISTS: + if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) + { + fOpen |= RTFILE_O_CREATE; + Log(("FLAGS: SHFL_CF_ACT_FAIL_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n")); + } + else + { + Log(("FLAGS: invalid open/create action combination\n")); + rc = VERR_INVALID_PARAMETER; + } + break; + case SHFL_CF_ACT_REPLACE_IF_EXISTS: + if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) + { + fOpen |= RTFILE_O_CREATE_REPLACE; + Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n")); + } + else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) + { + fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE; + Log(("FLAGS: SHFL_CF_ACT_REPLACE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n")); + } + else + { + Log(("FLAGS: invalid open/create action combination\n")); + rc = VERR_INVALID_PARAMETER; + } + break; + case SHFL_CF_ACT_OVERWRITE_IF_EXISTS: + if (SHFL_CF_ACT_CREATE_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) + { + fOpen |= RTFILE_O_CREATE_REPLACE; + Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_CREATE_IF_NEW\n")); + } + else if (SHFL_CF_ACT_FAIL_IF_NEW == BIT_FLAG(fShflFlags, SHFL_CF_ACT_MASK_IF_NEW)) + { + fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE; + Log(("FLAGS: SHFL_CF_ACT_OVERWRITE_IF_EXISTS and SHFL_CF_ACT_FAIL_IF_NEW\n")); + } + else + { + Log(("FLAGS: invalid open/create action combination\n")); + rc = VERR_INVALID_PARAMETER; + } + break; + default: + rc = VERR_INVALID_PARAMETER; + Log(("FLAG: SHFL_CF_ACT_MASK_IF_EXISTS - invalid parameter\n")); + } + + if (RT_SUCCESS(rc)) + { + if (!fWritable) + fOpen &= ~RTFILE_O_WRITE; + + *pfOpen = fOpen; + } + return rc; +} + +/** + * Open a file or create and open a new one. + * + * @returns IPRT status code + * @param pClient Data structure describing the client accessing the shared folder + * @param root The index of the shared folder in the table of mappings. + * @param pszPath Path to the file or folder on the host. + * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h + * @param pParms @a Info When a new file is created this specifies the initial parameters. + * When a file is created or overwritten, it also specifies the + * initial size. + * @retval pParms @a Resulte Shared folder status code, see include/VBox/shflsvc.h + * @retval pParms @a Handle On success the (shared folder) handle of the file opened or + * created + * @retval pParms @a Info On success the parameters of the file opened or created + */ +static int vbsfOpenFile(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath, SHFLCREATEPARMS *pParms) +{ + LogFlow(("vbsfOpenFile: pszPath = %s, pParms = %p\n", pszPath, pParms)); + Log(("SHFL create flags %08x\n", pParms->CreateFlags)); + + SHFLHANDLE handle = SHFL_HANDLE_NIL; + SHFLFILEHANDLE *pHandle = 0; + /* Open or create a file. */ + uint32_t fOpen = 0; + bool fNoError = false; + static int cErrors; + + /* is the guest allowed to write to this share? */ + bool fWritable; + int rc = vbsfMappingsQueryWritable(pClient, root, &fWritable); + if (RT_FAILURE(rc)) + fWritable = false; + + rc = vbsfConvertFileOpenFlags(fWritable, pParms->CreateFlags, pParms->Info.Attr.fMode, pParms->Handle, &fOpen); + if (RT_SUCCESS(rc)) + { + rc = VERR_NO_MEMORY; /* Default error. */ + handle = vbsfAllocFileHandle(pClient); + if (handle != SHFL_HANDLE_NIL) + { + pHandle = vbsfQueryFileHandle(pClient, handle); + if (pHandle) + { + pHandle->root = root; + rc = RTFileOpen(&pHandle->file.Handle, pszPath, fOpen); + } + } + } + if (RT_FAILURE(rc)) + { + switch (rc) + { + case VERR_FILE_NOT_FOUND: + pParms->Result = SHFL_FILE_NOT_FOUND; + + /* This actually isn't an error, so correct the rc before return later, + because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */ + fNoError = true; + break; + case VERR_PATH_NOT_FOUND: + pParms->Result = SHFL_PATH_NOT_FOUND; + + /* This actually isn't an error, so correct the rc before return later, + because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */ + fNoError = true; + break; + case VERR_ALREADY_EXISTS: + RTFSOBJINFO info; + + /** @todo Possible race left here. */ + if (RT_SUCCESS(RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)))) + { +#ifdef RT_OS_WINDOWS + info.Attr.fMode |= 0111; +#endif + vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info); + } + pParms->Result = SHFL_FILE_EXISTS; + + /* This actually isn't an error, so correct the rc before return later, + because the driver (VBoxSF.sys) expects rc = VINF_SUCCESS and checks the result code. */ + fNoError = true; + break; + case VERR_TOO_MANY_OPEN_FILES: + if (cErrors < 32) + { + LogRel(("SharedFolders host service: Cannot open '%s' -- too many open files.\n", pszPath)); +#if defined RT_OS_LINUX || defined(RT_OS_SOLARIS) + if (cErrors < 1) + LogRel(("SharedFolders host service: Try to increase the limit for open files (ulimit -n)\n")); +#endif + cErrors++; + } + pParms->Result = SHFL_NO_RESULT; + break; + default: + pParms->Result = SHFL_NO_RESULT; + } + } + else + { + /** @note The shared folder status code is very approximate, as the runtime + * does not really provide this information. */ + pParms->Result = SHFL_FILE_EXISTS; /* We lost the information as to whether it was + created when we eliminated the race. */ + if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS + == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)) + || ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS + == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))) + { + /* For now, we do not treat a failure here as fatal. */ + /** @todo Also set the size for SHFL_CF_ACT_CREATE_IF_NEW if + SHFL_CF_ACT_FAIL_IF_EXISTS is set. */ + RTFileSetSize(pHandle->file.Handle, pParms->Info.cbObject); + pParms->Result = SHFL_FILE_REPLACED; + } + if ( ( SHFL_CF_ACT_FAIL_IF_EXISTS + == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)) + || ( SHFL_CF_ACT_CREATE_IF_NEW + == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))) + { + pParms->Result = SHFL_FILE_CREATED; + } +#if 0 + /** @todo */ + /* Set new attributes. */ + if ( ( SHFL_CF_ACT_REPLACE_IF_EXISTS + == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS)) + || ( SHFL_CF_ACT_CREATE_IF_NEW + == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW))) + { + RTFileSetTimes(pHandle->file.Handle, + &pParms->Info.AccessTime, + &pParms->Info.ModificationTime, + &pParms->Info.ChangeTime, + &pParms->Info.BirthTime + ); + + RTFileSetMode (pHandle->file.Handle, pParms->Info.Attr.fMode); + } +#endif + RTFSOBJINFO info; + + /* Get file information */ + rc = RTFileQueryInfo(pHandle->file.Handle, &info, RTFSOBJATTRADD_NOTHING); + if (RT_SUCCESS(rc)) + { +#ifdef RT_OS_WINDOWS + info.Attr.fMode |= 0111; +#endif + vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info); + } + } + /* Free resources if any part of the function has failed. */ + if (RT_FAILURE(rc)) + { + if ( (0 != pHandle) + && (NIL_RTFILE != pHandle->file.Handle) + && (0 != pHandle->file.Handle)) + { + RTFileClose(pHandle->file.Handle); + pHandle->file.Handle = NIL_RTFILE; + } + if (SHFL_HANDLE_NIL != handle) + { + vbsfFreeFileHandle(pClient, handle); + } + pParms->Handle = SHFL_HANDLE_NIL; + } + else + { + pParms->Handle = handle; + } + + /* Report the driver that all is okay, we're done here */ + if (fNoError) + rc = VINF_SUCCESS; + + LogFlow(("vbsfOpenFile: rc = %Rrc\n", rc)); + return rc; +} + +/** + * Open a folder or create and open a new one. + * + * @returns IPRT status code + * @param pClient Data structure describing the client accessing the shared folder + * @param root The index of the shared folder in the table of mappings. + * @param pszPath Path to the file or folder on the host. + * @param pParms @a CreateFlags Creation or open parameters, see include/VBox/shflsvc.h + * @retval pParms @a Result Shared folder status code, see include/VBox/shflsvc.h + * @retval pParms @a Handle On success the (shared folder) handle of the folder opened or + * created + * @retval pParms @a Info On success the parameters of the folder opened or created + * + * @note folders are created with fMode = 0777 + */ +static int vbsfOpenDir(SHFLCLIENTDATA *pClient, SHFLROOT root, const char *pszPath, + SHFLCREATEPARMS *pParms) +{ + LogFlow(("vbsfOpenDir: pszPath = %s, pParms = %p\n", pszPath, pParms)); + Log(("SHFL create flags %08x\n", pParms->CreateFlags)); + + int rc = VERR_NO_MEMORY; + SHFLHANDLE handle = vbsfAllocDirHandle(pClient); + SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, handle); + if (0 != pHandle) + { + pHandle->root = root; + rc = VINF_SUCCESS; + pParms->Result = SHFL_FILE_EXISTS; /* May be overwritten with SHFL_FILE_CREATED. */ + /** @todo Can anyone think of a sensible, race-less way to do this? Although + I suspect that the race is inherent, due to the API available... */ + /* Try to create the folder first if "create if new" is specified. If this + fails, and "open if exists" is specified, then we ignore the failure and try + to open the folder anyway. */ + if ( SHFL_CF_ACT_CREATE_IF_NEW + == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_NEW)) + { + /** @todo render supplied attributes. + * bird: The guest should specify this. For windows guests RTFS_DOS_DIRECTORY should suffice. */ + RTFMODE fMode = 0777; + + pParms->Result = SHFL_FILE_CREATED; + rc = RTDirCreate(pszPath, fMode, 0); + if (RT_FAILURE(rc)) + { + switch (rc) + { + case VERR_ALREADY_EXISTS: + pParms->Result = SHFL_FILE_EXISTS; + break; + case VERR_PATH_NOT_FOUND: + pParms->Result = SHFL_PATH_NOT_FOUND; + break; + default: + pParms->Result = SHFL_NO_RESULT; + } + } + } + if ( RT_SUCCESS(rc) + || (SHFL_CF_ACT_OPEN_IF_EXISTS == BIT_FLAG(pParms->CreateFlags, SHFL_CF_ACT_MASK_IF_EXISTS))) + { + /* Open the directory now */ + rc = RTDirOpenFiltered(&pHandle->dir.Handle, pszPath, RTDIRFILTER_NONE, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + RTFSOBJINFO info; + + rc = RTDirQueryInfo(pHandle->dir.Handle, &info, RTFSOBJATTRADD_NOTHING); + if (RT_SUCCESS(rc)) + { + vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info); + } + } + else + { + switch (rc) + { + case VERR_FILE_NOT_FOUND: /* Does this make sense? */ + pParms->Result = SHFL_FILE_NOT_FOUND; + break; + case VERR_PATH_NOT_FOUND: + pParms->Result = SHFL_PATH_NOT_FOUND; + break; + case VERR_ACCESS_DENIED: + pParms->Result = SHFL_FILE_EXISTS; + break; + default: + pParms->Result = SHFL_NO_RESULT; + } + } + } + } + if (RT_FAILURE(rc)) + { + if ( (0 != pHandle) + && (0 != pHandle->dir.Handle)) + { + RTDirClose(pHandle->dir.Handle); + pHandle->dir.Handle = 0; + } + if (SHFL_HANDLE_NIL != handle) + { + vbsfFreeFileHandle(pClient, handle); + } + pParms->Handle = SHFL_HANDLE_NIL; + } + else + { + pParms->Handle = handle; + } + LogFlow(("vbsfOpenDir: rc = %Rrc\n", rc)); + return rc; +} + +static int vbsfCloseDir(SHFLFILEHANDLE *pHandle) +{ + int rc = VINF_SUCCESS; + + LogFlow(("vbsfCloseDir: Handle = %08X Search Handle = %08X\n", + pHandle->dir.Handle, pHandle->dir.SearchHandle)); + + RTDirClose(pHandle->dir.Handle); + + if (pHandle->dir.SearchHandle) + RTDirClose(pHandle->dir.SearchHandle); + + if (pHandle->dir.pLastValidEntry) + { + RTMemFree(pHandle->dir.pLastValidEntry); + pHandle->dir.pLastValidEntry = NULL; + } + + LogFlow(("vbsfCloseDir: rc = %d\n", rc)); + + return rc; +} + + +static int vbsfCloseFile(SHFLFILEHANDLE *pHandle) +{ + int rc = VINF_SUCCESS; + + LogFlow(("vbsfCloseFile: Handle = %08X\n", + pHandle->file.Handle)); + + rc = RTFileClose(pHandle->file.Handle); + + LogFlow(("vbsfCloseFile: rc = %d\n", rc)); + + return rc; +} + +/** + * Look up file or folder information by host path. + * + * @returns iprt status code (currently VINF_SUCCESS) + * @param pClient client data + * @param pszPath The path of the file to be looked up + * @retval pParms->Result Status of the operation (success or error) + * @retval pParms->Info On success, information returned about the file + */ +static int vbsfLookupFile(SHFLCLIENTDATA *pClient, char *pszPath, SHFLCREATEPARMS *pParms) +{ + RTFSOBJINFO info; + int rc; + + rc = RTPathQueryInfoEx(pszPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); + LogFlow(("SHFL_CF_LOOKUP\n")); + /* Client just wants to know if the object exists. */ + switch (rc) + { + case VINF_SUCCESS: + { +#ifdef RT_OS_WINDOWS + info.Attr.fMode |= 0111; +#endif + vbfsCopyFsObjInfoFromIprt(&pParms->Info, &info); + pParms->Result = SHFL_FILE_EXISTS; + break; + } + + case VERR_FILE_NOT_FOUND: + { + pParms->Result = SHFL_FILE_NOT_FOUND; + rc = VINF_SUCCESS; + break; + } + + case VERR_PATH_NOT_FOUND: + { + pParms->Result = SHFL_PATH_NOT_FOUND; + rc = VINF_SUCCESS; + break; + } + } + pParms->Handle = SHFL_HANDLE_NIL; + return rc; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_CREATE API. Located here as a form of API + * documentation. */ +void testCreate(RTTEST hTest) +{ + /* Simple opening of an existing file. */ + testCreateFileSimple(hTest); + testCreateFileSimpleCaseInsensitive(hTest); + /* Simple opening of an existing directory. */ + /** @todo How do wildcards in the path name work? */ + testCreateDirSimple(hTest); + /* If the number or types of parameters are wrong the API should fail. */ + testCreateBadParameters(hTest); + /* Add tests as required... */ +} +#endif + +/** + * Create or open a file or folder. Perform character set and case + * conversion on the file name if necessary. + * + * @returns IPRT status code, but see note below + * @param pClient Data structure describing the client accessing the shared + * folder + * @param root The index of the shared folder in the table of mappings. + * The host path of the shared folder is found using this. + * @param pPath The path of the file or folder relative to the host path + * indexed by root. + * @param cbPath Presumably the length of the path in pPath. Actually + * ignored, as pPath contains a length parameter. + * @param pParms @a Info If a new file is created or an old one overwritten, set + * these attributes + * @retval pParms @a Result Shared folder result code, see include/VBox/shflsvc.h + * @retval pParms @a Handle Shared folder handle to the newly opened file + * @retval pParms @a Info Attributes of the file or folder opened + * + * @note This function returns success if a "non-exceptional" error occurred, + * such as "no such file". In this case, the caller should check the + * pParms->Result return value and whether pParms->Handle is valid. + */ +int vbsfCreate(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms) +{ + int rc = VINF_SUCCESS; + + LogFlow(("vbsfCreate: pClient = %p, pPath = %p, cbPath = %d, pParms = %p CreateFlags=%x\n", + pClient, pPath, cbPath, pParms, pParms->CreateFlags)); + + /* Check the client access rights to the root. */ + /** @todo */ + + /* Build a host full path for the given path, handle file name case issues (if the guest + * expects case-insensitive paths but the host is case-sensitive) and convert ucs2 to utf8 if + * necessary. + */ + char *pszFullPath = NULL; + uint32_t cbFullPathRoot = 0; + + rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot); + if (RT_SUCCESS(rc)) + { + /* Reset return value in case client forgot to do so. + * pParms->Handle must not be reset here, as it is used + * in vbsfOpenFile to detect old additions. + */ + pParms->Result = SHFL_NO_RESULT; + + if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_LOOKUP)) + { + rc = vbsfLookupFile(pClient, pszFullPath, pParms); + } + else + { + /* Query path information. */ + RTFSOBJINFO info; + + rc = RTPathQueryInfoEx(pszFullPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); + LogFlow(("RTPathQueryInfoEx returned %Rrc\n", rc)); + + if (RT_SUCCESS(rc)) + { + /* Mark it as a directory in case the caller didn't. */ + /** + * @todo I left this in in order not to change the behaviour of the + * function too much. Is it really needed, and should it really be + * here? + */ + if (BIT_FLAG(info.Attr.fMode, RTFS_DOS_DIRECTORY)) + { + pParms->CreateFlags |= SHFL_CF_DIRECTORY; + } + + /** + * @todo This should be in the Windows Guest Additions, as no-one else + * needs it. + */ + if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_OPEN_TARGET_DIRECTORY)) + { + vbsfStripLastComponent(pszFullPath, cbFullPathRoot); + pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_EXISTS; + pParms->CreateFlags &= ~SHFL_CF_ACT_MASK_IF_NEW; + pParms->CreateFlags |= SHFL_CF_DIRECTORY; + pParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS; + pParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW; + } + } + + rc = VINF_SUCCESS; + + /* Note: do not check the SHFL_CF_ACCESS_WRITE here, only check if the open operation + * will cause changes. + * + * Actual operations (write, set attr, etc), which can write to a shared folder, have + * the check and will return VERR_WRITE_PROTECT if the folder is not writable. + */ + if ( (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_REPLACE_IF_EXISTS + || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_EXISTS) == SHFL_CF_ACT_OVERWRITE_IF_EXISTS + || (pParms->CreateFlags & SHFL_CF_ACT_MASK_IF_NEW) == SHFL_CF_ACT_CREATE_IF_NEW + ) + { + /* is the guest allowed to write to this share? */ + bool fWritable; + rc = vbsfMappingsQueryWritable(pClient, root, &fWritable); + if (RT_FAILURE(rc) || !fWritable) + rc = VERR_WRITE_PROTECT; + } + + if (RT_SUCCESS(rc)) + { + if (BIT_FLAG(pParms->CreateFlags, SHFL_CF_DIRECTORY)) + { + rc = vbsfOpenDir(pClient, root, pszFullPath, pParms); + } + else + { + rc = vbsfOpenFile(pClient, root, pszFullPath, pParms); + } + } + else + { + pParms->Handle = SHFL_HANDLE_NIL; + } + } + + /* free the path string */ + vbsfFreeFullPath(pszFullPath); + } + + Log(("vbsfCreate: handle = %RX64 rc = %Rrc result=%x\n", (uint64_t)pParms->Handle, rc, pParms->Result)); + + return rc; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_CLOSE API. Located here as a form of API + * documentation. */ +void testClose(RTTEST hTest) +{ + /* If the API parameters are invalid the API should fail. */ + testCloseBadParameters(hTest); + /* Add tests as required... */ +} +#endif + +int vbsfClose(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle) +{ + LogFunc(("pClient = %p, root 0x%RX32, Handle = 0x%RX64\n", + pClient, root, Handle)); + + int rc = VERR_INVALID_HANDLE; + uint32_t type = vbsfQueryHandleType(pClient, Handle); + Assert((type & ~(SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) == 0); + switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) + { + case SHFL_HF_TYPE_DIR: + { + SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle); + if (RT_LIKELY(pHandle && root == pHandle->root)) + { + rc = vbsfCloseDir(pHandle); + vbsfFreeFileHandle(pClient, Handle); + } + break; + } + case SHFL_HF_TYPE_FILE: + { + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); + if (RT_LIKELY(pHandle && root == pHandle->root)) + { + rc = vbsfCloseFile(pHandle); + vbsfFreeFileHandle(pClient, Handle); + } + break; + } + default: + break; + } + + LogFunc(("rc = %Rrc\n", rc)); + return rc; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_READ API. Located here as a form of API + * documentation. */ +void testRead(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testReadBadParameters(hTest); + /* Basic reading from a file. */ + testReadFileSimple(hTest); + /* Add tests as required... */ +} +#endif +int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer) +{ + LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n", + pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0)); + + AssertPtrReturn(pClient, VERR_INVALID_PARAMETER); + + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); + int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ); + if (RT_SUCCESS(rc)) + { /* likely */ } + else + return rc; + + if (RT_LIKELY(*pcbBuffer != 0)) + { + rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL); + if (RT_SUCCESS(rc)) + { + size_t count = 0; + rc = RTFileRead(pHandle->file.Handle, pBuffer, *pcbBuffer, &count); + *pcbBuffer = (uint32_t)count; + } + else + AssertRC(rc); + } + else + { + /* Reading zero bytes always succeeds. */ + rc = VINF_SUCCESS; + } + + LogFunc(("%Rrc bytes read 0x%RX32\n", rc, *pcbBuffer)); + return rc; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_WRITE API. Located here as a form of API + * documentation. */ +void testWrite(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testWriteBadParameters(hTest); + /* Simple test of writing to a file. */ + testWriteFileSimple(hTest); + /* Add tests as required... */ +} +#endif +int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer) +{ + LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64, offset 0x%RX64, bytes 0x%RX32\n", + pClient, root, Handle, offset, pcbBuffer? *pcbBuffer: 0)); + + AssertPtrReturn(pClient, VERR_INVALID_PARAMETER); + + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); + int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE); + if (RT_SUCCESS(rc)) + { /* likely */ } + else + return rc; + + if (RT_LIKELY(*pcbBuffer != 0)) + { + rc = RTFileSeek(pHandle->file.Handle, offset, RTFILE_SEEK_BEGIN, NULL); + if (RT_SUCCESS(rc)) + { + size_t count = 0; + rc = RTFileWrite(pHandle->file.Handle, pBuffer, *pcbBuffer, &count); + *pcbBuffer = (uint32_t)count; + } + else + AssertRC(rc); + } + else + { + /** @todo What writing zero bytes should do? */ + rc = VINF_SUCCESS; + } + + LogFunc(("%Rrc bytes written 0x%RX32\n", rc, *pcbBuffer)); + return rc; +} + + +#ifdef UNITTEST +/** Unit test the SHFL_FN_FLUSH API. Located here as a form of API + * documentation. */ +void testFlush(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testFlushBadParameters(hTest); + /* Simple opening and flushing of a file. */ + testFlushFileSimple(hTest); + /* Add tests as required... */ +} +#endif + +int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle) +{ + LogFunc(("pClient %p, root 0x%RX32, Handle 0x%RX64\n", + pClient, root, Handle)); + + AssertPtrReturn(pClient, VERR_INVALID_PARAMETER); + + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); + int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE); + if (RT_SUCCESS(rc)) + { /* likely */ } + else + return rc; + + rc = RTFileFlush(pHandle->file.Handle); + + LogFunc(("%Rrc\n", rc)); + return rc; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_LIST API. Located here as a form of API + * documentation. */ +void testDirList(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testDirListBadParameters(hTest); + /* Test listing an empty directory (simple edge case). */ + testDirListEmpty(hTest); + /* Add tests as required... */ +} +#endif +int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags, + uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles) +{ + PRTDIRENTRYEX pDirEntry = 0, pDirEntryOrg; + uint32_t cbDirEntry, cbBufferOrg; + PSHFLDIRINFO pSFDEntry; + PRTUTF16 pwszString; + RTDIR hDir; + const bool fUtf8 = BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8) != 0; + + AssertPtrReturn(pClient, VERR_INVALID_PARAMETER); + + SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle); + int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ); + if (RT_SUCCESS(rc)) + { /* likely */ } + else + return rc; + + Assert(*pIndex == 0); + + cbDirEntry = 4096; + pDirEntryOrg = pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry); + if (pDirEntry == 0) + { + AssertFailed(); + return VERR_NO_MEMORY; + } + + cbBufferOrg = *pcbBuffer; + *pcbBuffer = 0; + pSFDEntry = (PSHFLDIRINFO)pBuffer; + + *pIndex = 1; /* not yet complete */ + *pcFiles = 0; + + if (!pPath) + hDir = pHandle->dir.Handle; + else + { + if (pHandle->dir.SearchHandle == 0) + { + /* Build a host full path for the given path + * and convert ucs2 to utf8 if necessary. + */ + char *pszFullPath = NULL; + + Assert(pHandle->dir.pLastValidEntry == 0); + + rc = vbsfBuildFullPath(pClient, root, pPath, pPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPath, NULL, true); + + if (RT_SUCCESS(rc)) + { + rc = RTDirOpenFiltered(&pHandle->dir.SearchHandle, pszFullPath, RTDIRFILTER_WINNT, 0 /*fFlags*/); + + /* free the path string */ + vbsfFreeFullPath(pszFullPath); + + if (RT_FAILURE(rc)) + goto end; + } + else + goto end; + flags &= ~SHFL_LIST_RESTART; + } + Assert(pHandle->dir.SearchHandle); + hDir = pHandle->dir.SearchHandle; + } + + if (flags & SHFL_LIST_RESTART) + { + rc = RTDirRewind(hDir); + if (RT_FAILURE(rc)) + goto end; + } + + while (cbBufferOrg) + { + size_t cbDirEntrySize = cbDirEntry; + uint32_t cbNeeded; + + /* Do we still have a valid last entry for the active search? If so, then return it here */ + if (pHandle->dir.pLastValidEntry) + { + pDirEntry = pHandle->dir.pLastValidEntry; + } + else + { + pDirEntry = pDirEntryOrg; + + rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); + if (rc == VERR_NO_MORE_FILES) + { + *pIndex = 0; /* listing completed */ + break; + } + + if ( rc != VINF_SUCCESS + && rc != VWRN_NO_DIRENT_INFO) + { + //AssertFailed(); + if ( rc == VERR_NO_TRANSLATION + || rc == VERR_INVALID_UTF8_ENCODING) + continue; + break; + } + } + + cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String); + if (fUtf8) + cbNeeded += pDirEntry->cbName + 1; + else + /* Overestimating, but that's ok */ + cbNeeded += (pDirEntry->cbName + 1) * 2; + + if (cbBufferOrg < cbNeeded) + { + /* No room, so save this directory entry, or else it's lost forever */ + pHandle->dir.pLastValidEntry = pDirEntry; + + if (*pcFiles == 0) + { + AssertFailed(); + return VINF_BUFFER_OVERFLOW; /* Return directly and don't free pDirEntry */ + } + return VINF_SUCCESS; /* Return directly and don't free pDirEntry */ + } + +#ifdef RT_OS_WINDOWS + pDirEntry->Info.Attr.fMode |= 0111; +#endif + vbfsCopyFsObjInfoFromIprt(&pSFDEntry->Info, &pDirEntry->Info); + pSFDEntry->cucShortName = 0; + + if (fUtf8) + { + void *src, *dst; + + src = &pDirEntry->szName[0]; + dst = &pSFDEntry->name.String.utf8[0]; + + memcpy(dst, src, pDirEntry->cbName + 1); + + pSFDEntry->name.u16Size = pDirEntry->cbName + 1; + pSFDEntry->name.u16Length = pDirEntry->cbName; + } + else + { + pSFDEntry->name.String.ucs2[0] = 0; + pwszString = pSFDEntry->name.String.ucs2; + int rc2 = RTStrToUtf16Ex(pDirEntry->szName, RTSTR_MAX, &pwszString, pDirEntry->cbName+1, NULL); + AssertRC(rc2); + +#ifdef RT_OS_DARWIN +/** @todo This belongs in rtPathToNative or in the windows shared folder file system driver... + * The question is simply whether the NFD normalization is actually applied on a (virtual) file + * system level in darwin, or just by the user mode application libs. */ + { + // Convert to + // Normalization Form C (composed Unicode). We need this because + // Mac OS X file system uses NFD (Normalization Form D :decomposed Unicode) + // while most other OS', server-side programs usually expect NFC. + uint16_t ucs2Length; + CFRange rangeCharacters; + CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0); + + ::CFStringAppendCharacters(inStr, (UniChar *)pwszString, RTUtf16Len(pwszString)); + ::CFStringNormalize(inStr, kCFStringNormalizationFormC); + ucs2Length = ::CFStringGetLength(inStr); + + rangeCharacters.location = 0; + rangeCharacters.length = ucs2Length; + ::CFStringGetCharacters(inStr, rangeCharacters, pwszString); + pwszString[ucs2Length] = 0x0000; // NULL terminated + + CFRelease(inStr); + } +#endif + pSFDEntry->name.u16Length = (uint32_t)RTUtf16Len(pSFDEntry->name.String.ucs2) * 2; + pSFDEntry->name.u16Size = pSFDEntry->name.u16Length + 2; + + Log(("SHFL: File name size %d\n", pSFDEntry->name.u16Size)); + Log(("SHFL: File name %ls\n", &pSFDEntry->name.String.ucs2)); + + // adjust cbNeeded (it was overestimated before) + cbNeeded = RT_OFFSETOF(SHFLDIRINFO, name.String) + pSFDEntry->name.u16Size; + } + + pSFDEntry = (PSHFLDIRINFO)((uintptr_t)pSFDEntry + cbNeeded); + *pcbBuffer += cbNeeded; + cbBufferOrg-= cbNeeded; + + *pcFiles += 1; + + /* Free the saved last entry, that we've just returned */ + if (pHandle->dir.pLastValidEntry) + { + RTMemFree(pHandle->dir.pLastValidEntry); + pHandle->dir.pLastValidEntry = NULL; + + /* And use the newly allocated buffer from now. */ + pDirEntry = pDirEntryOrg; + } + + if (flags & SHFL_LIST_RETURN_ONE) + break; /* we're done */ + } + Assert(rc != VINF_SUCCESS || *pcbBuffer > 0); + +end: + if (pDirEntry) + RTMemFree(pDirEntry); + + return rc; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_READLINK API. Located here as a form of API + * documentation. */ +void testReadLink(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testReadLinkBadParameters(hTest); + /* Add tests as required... */ +} +#endif +int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer) +{ + int rc = VINF_SUCCESS; + + if (pPath == 0 || pBuffer == 0) + { + AssertFailed(); + return VERR_INVALID_PARAMETER; + } + + /* Build a host full path for the given path, handle file name case issues + * (if the guest expects case-insensitive paths but the host is + * case-sensitive) and convert ucs2 to utf8 if necessary. + */ + char *pszFullPath = NULL; + uint32_t cbFullPathRoot = 0; + + rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, &cbFullPathRoot); + + if (RT_SUCCESS(rc)) + { + rc = RTSymlinkRead(pszFullPath, (char *) pBuffer, cbBuffer, 0); + if (RT_SUCCESS(rc)) + { + /* Convert the slashes in the link target to the guest path separator characters. */ + char *psz = (char *)pBuffer; + while (*psz != '\0') + { + if (*psz == RTPATH_DELIMITER) + *psz = pClient->PathDelimiter; + psz++; + } + } + + /* free the path string */ + vbsfFreeFullPath(pszFullPath); + } + + return rc; +} + +int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, + uint32_t *pcbBuffer, uint8_t *pBuffer) +{ + RT_NOREF1(flags); + uint32_t type = vbsfQueryHandleType(pClient, Handle); + int rc = VINF_SUCCESS; + SHFLFSOBJINFO *pObjInfo = (SHFLFSOBJINFO *)pBuffer; + RTFSOBJINFO fileinfo; + + + AssertReturn(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE, VERR_INVALID_PARAMETER); + AssertReturn(pcbBuffer != NULL, VERR_INVALID_PARAMETER); + AssertReturn(pObjInfo != NULL, VERR_INVALID_PARAMETER); + AssertReturn(*pcbBuffer >= sizeof(SHFLFSOBJINFO), VERR_INVALID_PARAMETER); + + /** @todo other options */ + Assert(flags == (SHFL_INFO_GET|SHFL_INFO_FILE)); + + *pcbBuffer = 0; + + if (type == SHFL_HF_TYPE_DIR) + { + SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle); + rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ); + if (RT_SUCCESS(rc)) + rc = RTDirQueryInfo(pHandle->dir.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING); + } + else + { + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); + rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ); + if (RT_SUCCESS(rc)) + rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING); +#ifdef RT_OS_WINDOWS + if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode)) + pObjInfo->Attr.fMode |= 0111; +#endif + } + if (rc == VINF_SUCCESS) + { + vbfsCopyFsObjInfoFromIprt(pObjInfo, &fileinfo); + *pcbBuffer = sizeof(SHFLFSOBJINFO); + } + else + AssertFailed(); + + return rc; +} + +static int vbsfSetFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, + uint32_t *pcbBuffer, uint8_t *pBuffer) +{ + RT_NOREF1(flags); + uint32_t type = vbsfQueryHandleType(pClient, Handle); + int rc = VINF_SUCCESS; + SHFLFSOBJINFO *pSFDEntry; + + if ( !(type == SHFL_HF_TYPE_DIR || type == SHFL_HF_TYPE_FILE) + || pcbBuffer == 0 + || pBuffer == 0 + || *pcbBuffer < sizeof(SHFLFSOBJINFO)) + { + AssertFailed(); + return VERR_INVALID_PARAMETER; + } + + *pcbBuffer = 0; + pSFDEntry = (SHFLFSOBJINFO *)pBuffer; + + Assert(flags == (SHFL_INFO_SET | SHFL_INFO_FILE)); + + /* Change only the time values that are not zero */ + if (type == SHFL_HF_TYPE_DIR) + { + SHFLFILEHANDLE *pHandle = vbsfQueryDirHandle(pClient, Handle); + rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE); + if (RT_SUCCESS(rc)) + rc = RTDirSetTimes(pHandle->dir.Handle, + (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL, + (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL, + (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL, + (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL + ); + } + else + { + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); + rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE); + if (RT_SUCCESS(rc)) + rc = RTFileSetTimes(pHandle->file.Handle, + (RTTimeSpecGetNano(&pSFDEntry->AccessTime)) ? &pSFDEntry->AccessTime : NULL, + (RTTimeSpecGetNano(&pSFDEntry->ModificationTime)) ? &pSFDEntry->ModificationTime: NULL, + (RTTimeSpecGetNano(&pSFDEntry->ChangeTime)) ? &pSFDEntry->ChangeTime: NULL, + (RTTimeSpecGetNano(&pSFDEntry->BirthTime)) ? &pSFDEntry->BirthTime: NULL + ); + } + if (rc != VINF_SUCCESS) + { + Log(("RTFileSetTimes failed with %Rrc\n", rc)); + Log(("AccessTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->AccessTime))); + Log(("ModificationTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ModificationTime))); + Log(("ChangeTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->ChangeTime))); + Log(("BirthTime %RX64\n", RTTimeSpecGetNano(&pSFDEntry->BirthTime))); + /* temporary hack */ + rc = VINF_SUCCESS; + } + + if (type == SHFL_HF_TYPE_FILE) + { + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); + rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE); + if (RT_SUCCESS(rc)) + { + /* Change file attributes if necessary */ + if (pSFDEntry->Attr.fMode) + { + RTFMODE fMode = pSFDEntry->Attr.fMode; + +#ifndef RT_OS_WINDOWS + /* Don't allow the guest to clear the own bit, otherwise the guest wouldn't be + * able to access this file anymore. Only for guests, which set the UNIX mode. + * Also, clear bits which we don't pass through for security reasons. */ + if (fMode & RTFS_UNIX_MASK) + { + fMode |= RTFS_UNIX_IRUSR; + fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT); + } +#endif + + rc = RTFileSetMode(pHandle->file.Handle, fMode); + if (rc != VINF_SUCCESS) + { + Log(("RTFileSetMode %x failed with %Rrc\n", fMode, rc)); + /* silent failure, because this tends to fail with e.g. windows guest & linux host */ + rc = VINF_SUCCESS; + } + } + } + } + /** @todo mode for directories */ + + if (rc == VINF_SUCCESS) + { + uint32_t bufsize = sizeof(*pSFDEntry); + + rc = vbsfQueryFileInfo(pClient, root, Handle, SHFL_INFO_GET|SHFL_INFO_FILE, &bufsize, (uint8_t *)pSFDEntry); + if (rc == VINF_SUCCESS) + { + *pcbBuffer = sizeof(SHFLFSOBJINFO); + } + else + AssertFailed(); + } + + return rc; +} + + +/** + * Handles SHFL_FN_SET_FILE_SIZE. + */ +int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize) +{ + /* + * Resolve handle and validate write access. + */ + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, hHandle); + ASSERT_GUEST_RETURN(pHandle, VERR_INVALID_HANDLE); + + int rc = vbsfCheckHandleAccess(pClient, idRoot, pHandle, VBSF_CHECK_ACCESS_WRITE); + if (RT_SUCCESS(rc)) + { + /* + * Execute the request. + */ + rc = RTFileSetSize(pHandle->file.Handle, cbNewSize); + } + return rc; +} + + +static int vbsfSetEndOfFile(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, + uint32_t *pcbBuffer, uint8_t *pBuffer) +{ + RT_NOREF1(flags); + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); + SHFLFSOBJINFO *pSFDEntry; + + if (pHandle == 0 || pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLFSOBJINFO)) + { + AssertFailed(); + return VERR_INVALID_PARAMETER; + } + + int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_WRITE); + if (RT_SUCCESS(rc)) + { /* likely */ } + else + return rc; + + *pcbBuffer = 0; + pSFDEntry = (SHFLFSOBJINFO *)pBuffer; + + if (flags & SHFL_INFO_SIZE) + { + rc = RTFileSetSize(pHandle->file.Handle, pSFDEntry->cbObject); + if (rc != VINF_SUCCESS) + AssertFailed(); + } + else + AssertFailed(); + + if (rc == VINF_SUCCESS) + { + RTFSOBJINFO fileinfo; + + /* Query the new object info and return it */ + rc = RTFileQueryInfo(pHandle->file.Handle, &fileinfo, RTFSOBJATTRADD_NOTHING); + if (rc == VINF_SUCCESS) + { +#ifdef RT_OS_WINDOWS + fileinfo.Attr.fMode |= 0111; +#endif + vbfsCopyFsObjInfoFromIprt(pSFDEntry, &fileinfo); + *pcbBuffer = sizeof(SHFLFSOBJINFO); + } + else + AssertFailed(); + } + + return rc; +} + +int vbsfQueryVolumeInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer) +{ + RT_NOREF2(root, flags); + int rc = VINF_SUCCESS; + SHFLVOLINFO *pSFDEntry; + char *pszFullPath = NULL; + union + { + SHFLSTRING Dummy; + uint8_t abDummy[SHFLSTRING_HEADER_SIZE + sizeof(RTUTF16)]; + } Buf; + + if (pcbBuffer == 0 || pBuffer == 0 || *pcbBuffer < sizeof(SHFLVOLINFO)) + { + AssertFailed(); + return VERR_INVALID_PARAMETER; + } + + /** @todo other options */ + Assert(flags == (SHFL_INFO_GET|SHFL_INFO_VOLUME)); + + *pcbBuffer = 0; + pSFDEntry = (PSHFLVOLINFO)pBuffer; + + ShflStringInitBuffer(&Buf.Dummy, sizeof(Buf)); + Buf.Dummy.String.ucs2[0] = '\0'; + rc = vbsfBuildFullPath(pClient, root, &Buf.Dummy, sizeof(Buf), &pszFullPath, NULL); + + if (RT_SUCCESS(rc)) + { + rc = RTFsQuerySizes(pszFullPath, &pSFDEntry->ullTotalAllocationBytes, &pSFDEntry->ullAvailableAllocationBytes, &pSFDEntry->ulBytesPerAllocationUnit, &pSFDEntry->ulBytesPerSector); + if (rc != VINF_SUCCESS) + goto exit; + + rc = RTFsQuerySerial(pszFullPath, &pSFDEntry->ulSerial); + if (rc != VINF_SUCCESS) + goto exit; + + RTFSPROPERTIES FsProperties; + rc = RTFsQueryProperties(pszFullPath, &FsProperties); + if (rc != VINF_SUCCESS) + goto exit; + vbfsCopyFsPropertiesFromIprt(&pSFDEntry->fsProperties, &FsProperties); + + *pcbBuffer = sizeof(SHFLVOLINFO); + } + else AssertFailed(); + +exit: + AssertMsg(rc == VINF_SUCCESS, ("failure: rc = %Rrc\n", rc)); + /* free the path string */ + vbsfFreeFullPath(pszFullPath); + return rc; +} + +int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer) +{ + if (pcbBuffer == 0 || pBuffer == 0) + { + AssertFailed(); + return VERR_INVALID_PARAMETER; + } + + if (flags & SHFL_INFO_FILE) + return vbsfQueryFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer); + + if (flags & SHFL_INFO_VOLUME) + return vbsfQueryVolumeInfo(pClient, root, flags, pcbBuffer, pBuffer); + + AssertFailed(); + return VERR_INVALID_PARAMETER; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_INFORMATION API. Located here as a form of API + * documentation. */ +void testFSInfo(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testFSInfoBadParameters(hTest); + /* Basic get and set file size test. */ + testFSInfoQuerySetFMode(hTest); + /* Basic get and set dir atime test. */ + testFSInfoQuerySetDirATime(hTest); + /* Basic get and set file atime test. */ + testFSInfoQuerySetFileATime(hTest); + /* Basic set end of file. */ + testFSInfoQuerySetEndOfFile(hTest); + /* Add tests as required... */ +} +#endif +int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer) +{ + uint32_t type = vbsfQueryHandleType(pClient, Handle) + & (SHFL_HF_TYPE_DIR|SHFL_HF_TYPE_FILE|SHFL_HF_TYPE_VOLUME); + + if (type == 0 || pcbBuffer == 0 || pBuffer == 0) + { + AssertFailed(); + return VERR_INVALID_PARAMETER; + } + + if (flags & SHFL_INFO_FILE) + return vbsfSetFileInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer); + + if (flags & SHFL_INFO_SIZE) + return vbsfSetEndOfFile(pClient, root, Handle, flags, pcbBuffer, pBuffer); + +// if (flags & SHFL_INFO_VOLUME) +// return vbsfVolumeInfo(pClient, root, Handle, flags, pcbBuffer, pBuffer); + AssertFailed(); + return VERR_INVALID_PARAMETER; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_LOCK API. Located here as a form of API + * documentation. */ +void testLock(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testLockBadParameters(hTest); + /* Simple file locking and unlocking test. */ + testLockFileSimple(hTest); + /* Add tests as required... */ +} +#endif + +int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags) +{ + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); + uint32_t fRTLock = 0; + + Assert((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL); + + int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ); + if (RT_SUCCESS(rc)) + { /* likely */ } + else + return rc; + + if ( ((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL) + || (flags & SHFL_LOCK_ENTIRE) + ) + { + AssertFailed(); + return VERR_INVALID_PARAMETER; + } + + /* Lock type */ + switch(flags & SHFL_LOCK_MODE_MASK) + { + case SHFL_LOCK_SHARED: + fRTLock = RTFILE_LOCK_READ; + break; + + case SHFL_LOCK_EXCLUSIVE: + fRTLock = RTFILE_LOCK_READ | RTFILE_LOCK_WRITE; + break; + + default: + AssertFailed(); + return VERR_INVALID_PARAMETER; + } + + /* Lock wait type */ + if (flags & SHFL_LOCK_WAIT) + fRTLock |= RTFILE_LOCK_WAIT; + else + fRTLock |= RTFILE_LOCK_IMMEDIATELY; + +#ifdef RT_OS_WINDOWS + rc = RTFileLock(pHandle->file.Handle, fRTLock, offset, length); + if (rc != VINF_SUCCESS) + Log(("RTFileLock %RTfile %RX64 %RX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc)); +#else + Log(("vbsfLock: Pretend success handle=%x\n", Handle)); + rc = VINF_SUCCESS; + RT_NOREF2(offset, length); +#endif + return rc; +} + +int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags) +{ + SHFLFILEHANDLE *pHandle = vbsfQueryFileHandle(pClient, Handle); + + Assert((flags & SHFL_LOCK_MODE_MASK) == SHFL_LOCK_CANCEL); + + int rc = vbsfCheckHandleAccess(pClient, root, pHandle, VBSF_CHECK_ACCESS_READ); + if (RT_SUCCESS(rc)) + { /* likely */ } + else + return rc; + + if ( ((flags & SHFL_LOCK_MODE_MASK) != SHFL_LOCK_CANCEL) + || (flags & SHFL_LOCK_ENTIRE) + ) + { + return VERR_INVALID_PARAMETER; + } + +#ifdef RT_OS_WINDOWS + rc = RTFileUnlock(pHandle->file.Handle, offset, length); + if (rc != VINF_SUCCESS) + Log(("RTFileUnlock %RTfile %RX64 %RTX64 failed with %Rrc\n", pHandle->file.Handle, offset, length, rc)); +#else + Log(("vbsfUnlock: Pretend success handle=%x\n", Handle)); + rc = VINF_SUCCESS; + RT_NOREF2(offset, length); +#endif + + return rc; +} + + +#ifdef UNITTEST +/** Unit test the SHFL_FN_REMOVE API. Located here as a form of API + * documentation. */ +void testRemove(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testRemoveBadParameters(hTest); + /* Add tests as required... */ +} +#endif +int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags) +{ + int rc = VINF_SUCCESS; + + /* Validate input */ + if ( flags & ~(SHFL_REMOVE_FILE|SHFL_REMOVE_DIR|SHFL_REMOVE_SYMLINK) + || cbPath == 0 + || pPath == 0) + { + AssertFailed(); + return VERR_INVALID_PARAMETER; + } + + /* Build a host full path for the given path + * and convert ucs2 to utf8 if necessary. + */ + char *pszFullPath = NULL; + + rc = vbsfBuildFullPath(pClient, root, pPath, cbPath, &pszFullPath, NULL); + if (RT_SUCCESS(rc)) + { + /* is the guest allowed to write to this share? */ + bool fWritable; + rc = vbsfMappingsQueryWritable(pClient, root, &fWritable); + if (RT_FAILURE(rc) || !fWritable) + rc = VERR_WRITE_PROTECT; + + if (RT_SUCCESS(rc)) + { + if (flags & SHFL_REMOVE_SYMLINK) + rc = RTSymlinkDelete(pszFullPath, 0); + else if (flags & SHFL_REMOVE_FILE) + rc = RTFileDelete(pszFullPath); + else + rc = RTDirRemove(pszFullPath); + } + +#ifndef DEBUG_dmik + // VERR_ACCESS_DENIED for example? + // Assert(rc == VINF_SUCCESS || rc == VERR_DIR_NOT_EMPTY); +#endif + /* free the path string */ + vbsfFreeFullPath(pszFullPath); + } + return rc; +} + + +#ifdef UNITTEST +/** Unit test the SHFL_FN_RENAME API. Located here as a form of API + * documentation. */ +void testRename(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testRenameBadParameters(hTest); + /* Add tests as required... */ +} +#endif +int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags) +{ + int rc = VINF_SUCCESS; + + /* Validate input */ + if ( flags & ~(SHFL_RENAME_FILE|SHFL_RENAME_DIR|SHFL_RENAME_REPLACE_IF_EXISTS) + || pSrc == 0 + || pDest == 0) + { + AssertFailed(); + return VERR_INVALID_PARAMETER; + } + + /* Build a host full path for the given path + * and convert ucs2 to utf8 if necessary. + */ + char *pszFullPathSrc = NULL; + char *pszFullPathDest = NULL; + + rc = vbsfBuildFullPath(pClient, root, pSrc, pSrc->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathSrc, NULL); + if (rc != VINF_SUCCESS) + return rc; + + rc = vbsfBuildFullPath(pClient, root, pDest, pDest->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullPathDest, NULL, false, true); + if (RT_SUCCESS (rc)) + { + Log(("Rename %s to %s\n", pszFullPathSrc, pszFullPathDest)); + + /* is the guest allowed to write to this share? */ + bool fWritable; + rc = vbsfMappingsQueryWritable(pClient, root, &fWritable); + if (RT_FAILURE(rc) || !fWritable) + rc = VERR_WRITE_PROTECT; + + if (RT_SUCCESS(rc)) + { + if ((flags & (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) == (SHFL_RENAME_FILE | SHFL_RENAME_DIR)) + { + rc = RTPathRename(pszFullPathSrc, pszFullPathDest, + flags & SHFL_RENAME_REPLACE_IF_EXISTS ? RTPATHRENAME_FLAGS_REPLACE : 0); + } + else if (flags & SHFL_RENAME_FILE) + { + rc = RTFileMove(pszFullPathSrc, pszFullPathDest, + ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTFILEMOVE_FLAGS_REPLACE : 0)); + } + else + { + /* NT ignores the REPLACE flag and simply return and already exists error. */ + rc = RTDirRename(pszFullPathSrc, pszFullPathDest, + ((flags & SHFL_RENAME_REPLACE_IF_EXISTS) ? RTPATHRENAME_FLAGS_REPLACE : 0)); + } + } + + /* free the path string */ + vbsfFreeFullPath(pszFullPathDest); + } + /* free the path string */ + vbsfFreeFullPath(pszFullPathSrc); + return rc; +} + +#ifdef UNITTEST +/** Unit test the SHFL_FN_SYMLINK API. Located here as a form of API + * documentation. */ +void testSymlink(RTTEST hTest) +{ + /* If the number or types of parameters are wrong the API should fail. */ + testSymlinkBadParameters(hTest); + /* Add tests as required... */ +} +#endif +int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo) +{ + int rc = VINF_SUCCESS; + + char *pszFullNewPath = NULL; + char *pszFullOldPath = NULL; + + /* XXX: no support for UCS2 at the moment. */ + if (!BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) + return VERR_NOT_IMPLEMENTED; + + bool fSymlinksCreate; + rc = vbsfMappingsQuerySymlinksCreate(pClient, root, &fSymlinksCreate); + AssertRCReturn(rc, rc); + if (!fSymlinksCreate) + return VERR_WRITE_PROTECT; /* XXX or VERR_TOO_MANY_SYMLINKS? */ + + rc = vbsfBuildFullPath(pClient, root, pNewPath, pNewPath->u16Size + SHFLSTRING_HEADER_SIZE, &pszFullNewPath, NULL); + AssertRCReturn(rc, rc); + + /* Verify that the link target can be a valid host path, i.e. does not contain invalid characters. */ + uint32_t fu32PathFlags = 0; + uint32_t fu32Options = 0; + rc = vbsfPathGuestToHost(pClient, root, pOldPath, pOldPath->u16Size + SHFLSTRING_HEADER_SIZE, + &pszFullOldPath, NULL, fu32Options, &fu32PathFlags); + if (RT_FAILURE(rc)) + { + vbsfFreeFullPath(pszFullNewPath); + return rc; + } + + rc = RTSymlinkCreate(pszFullNewPath, (const char *)pOldPath->String.utf8, + RTSYMLINKTYPE_UNKNOWN, 0); + if (RT_SUCCESS(rc)) + { + RTFSOBJINFO info; + rc = RTPathQueryInfoEx(pszFullNewPath, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); + if (RT_SUCCESS(rc)) + vbfsCopyFsObjInfoFromIprt(pInfo, &info); + } + + vbsfFreeFullPath(pszFullOldPath); + vbsfFreeFullPath(pszFullNewPath); + + return rc; +} + +/* + * Clean up our mess by freeing all handles that are still valid. + * + */ +int vbsfDisconnect(SHFLCLIENTDATA *pClient) +{ + for (int i = 0; i < SHFLHANDLE_MAX; ++i) + { + SHFLFILEHANDLE *pHandle = NULL; + SHFLHANDLE Handle = (SHFLHANDLE)i; + + uint32_t type = vbsfQueryHandleType(pClient, Handle); + switch (type & (SHFL_HF_TYPE_DIR | SHFL_HF_TYPE_FILE)) + { + case SHFL_HF_TYPE_DIR: + { + pHandle = vbsfQueryDirHandle(pClient, Handle); + break; + } + case SHFL_HF_TYPE_FILE: + { + pHandle = vbsfQueryFileHandle(pClient, Handle); + break; + } + default: + break; + } + + if (pHandle) + { + LogFunc(("Opened handle 0x%08x\n", i)); + vbsfClose(pClient, pHandle->root, Handle); + } + } + + for (uint32_t i = 0; i < RT_ELEMENTS(pClient->acMappings); i++) + if (pClient->acMappings[i]) + { + uint16_t cMappings = pClient->acMappings[i]; + while (cMappings-- > 0) + vbsfUnmapFolder(pClient, i); + } + + return VINF_SUCCESS; +} diff --git a/src/VBox/HostServices/SharedFolders/vbsf.h b/src/VBox/HostServices/SharedFolders/vbsf.h new file mode 100644 index 00000000..9a91d512 --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/vbsf.h @@ -0,0 +1,49 @@ +/* $Id: vbsf.h $ */ +/** @file + * VBox Shared Folders header. + */ + +/* + * Copyright (C) 2006-2019 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. + */ + +#ifndef VBOX_INCLUDED_SRC_SharedFolders_vbsf_h +#define VBOX_INCLUDED_SRC_SharedFolders_vbsf_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "shfl.h" +#include <VBox/shflsvc.h> + +int vbsfCreate (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, SHFLCREATEPARMS *pParms); + +int vbsfClose (SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle); + +int vbsfRead(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer); +int vbsfWrite(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer); +int vbsfLock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags); +int vbsfUnlock(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint64_t offset, uint64_t length, uint32_t flags); +int vbsfRemove(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint32_t flags); +int vbsfRename(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pSrc, SHFLSTRING *pDest, uint32_t flags); +int vbsfDirList(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, SHFLSTRING *pPath, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer, uint32_t *pIndex, uint32_t *pcFiles); +int vbsfFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer); +int vbsfSetFileSize(SHFLCLIENTDATA *pClient, SHFLROOT idRoot, SHFLHANDLE hHandle, uint64_t cbNewSize); +int vbsfQueryFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer); +int vbsfSetFSInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer); +int vbsfFlush(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle); +int vbsfDisconnect(SHFLCLIENTDATA *pClient); +int vbsfQueryFileInfo(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLHANDLE Handle, uint32_t flags, uint32_t *pcbBuffer, uint8_t *pBuffer); +int vbsfReadLink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pPath, uint32_t cbPath, uint8_t *pBuffer, uint32_t cbBuffer); +int vbsfSymlink(SHFLCLIENTDATA *pClient, SHFLROOT root, SHFLSTRING *pNewPath, SHFLSTRING *pOldPath, SHFLFSOBJINFO *pInfo); + +#endif /* !VBOX_INCLUDED_SRC_SharedFolders_vbsf_h */ + diff --git a/src/VBox/HostServices/SharedFolders/vbsfpath.cpp b/src/VBox/HostServices/SharedFolders/vbsfpath.cpp new file mode 100644 index 00000000..509ff18f --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/vbsfpath.cpp @@ -0,0 +1,701 @@ +/* $Id: vbsfpath.cpp $ */ +/** @file + * Shared Folders Service - guest/host path convertion and verification. + */ + +/* + * Copyright (C) 2006-2019 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_SHARED_FOLDERS +#ifdef UNITTEST +# include "testcase/tstSharedFolderService.h" +#endif + +#include "vbsfpath.h" +#include "mappings.h" +#include "vbsf.h" +#include "shflhandle.h" + +#include <iprt/alloc.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/fs.h> +#include <iprt/dir.h> +#include <iprt/file.h> +#include <iprt/path.h> +#include <iprt/string.h> +#include <iprt/symlink.h> +#include <iprt/uni.h> +#include <iprt/stream.h> +#ifdef RT_OS_DARWIN +# include <Carbon/Carbon.h> +#endif + +#ifdef UNITTEST +# include "teststubs.h" +#endif + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define SHFL_RT_LINK(pClient) ((pClient)->fu32Flags & SHFL_CF_SYMLINKS ? RTPATH_F_ON_LINK : RTPATH_F_FOLLOW_LINK) + + +/** + * @todo find a better solution for supporting the execute bit for non-windows + * guests on windows host. Search for "0111" to find all the relevant places. + */ + +/** + * Corrects the casing of the final component + * + * @returns + * @param pClient . + * @param pszFullPath . + * @param pszStartComponent . + */ +static int vbsfCorrectCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, char *pszStartComponent) +{ + Log2(("vbsfCorrectCasing: %s %s\n", pszFullPath, pszStartComponent)); + + AssertReturn((uintptr_t)pszFullPath < (uintptr_t)pszStartComponent - 1U, VERR_INTERNAL_ERROR_2); + AssertReturn(pszStartComponent[-1] == RTPATH_DELIMITER, VERR_INTERNAL_ERROR_5); + + /* + * Allocate a buffer that can hold really long file name entries as well as + * the initial search pattern. + */ + size_t cchComponent = strlen(pszStartComponent); + size_t cchParentDir = pszStartComponent - pszFullPath; + size_t cchFullPath = cchParentDir + cchComponent; + Assert(strlen(pszFullPath) == cchFullPath); + + size_t cbDirEntry = 4096; + if (cchFullPath + 4 > cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName)) + cbDirEntry = RT_OFFSETOF(RTDIRENTRYEX, szName) + cchFullPath + 4; + + PRTDIRENTRYEX pDirEntry = (PRTDIRENTRYEX)RTMemAlloc(cbDirEntry); + if (pDirEntry == NULL) + return VERR_NO_MEMORY; + + /* + * Construct the search criteria in the szName member of pDirEntry. + */ + /** @todo This is quite inefficient, especially for directories with many + * files. If any of the typically case sensitive host systems start + * supporting opendir wildcard filters, it would make sense to build + * one here with '?' for case foldable charaters. */ + /** @todo Use RTDirOpen here and drop the whole uncessary path copying? */ + int rc = RTPathJoinEx(pDirEntry->szName, cbDirEntry - RT_OFFSETOF(RTDIRENTRYEX, szName), + pszFullPath, cchParentDir, + RT_STR_TUPLE("*")); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + RTDIR hSearch = NULL; + rc = RTDirOpenFiltered(&hSearch, pDirEntry->szName, RTDIRFILTER_WINNT, 0 /*fFlags*/); + if (RT_SUCCESS(rc)) + { + for (;;) + { + size_t cbDirEntrySize = cbDirEntry; + + rc = RTDirReadEx(hSearch, pDirEntry, &cbDirEntrySize, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); + if (rc == VERR_NO_MORE_FILES) + break; + + if ( rc != VINF_SUCCESS + && rc != VWRN_NO_DIRENT_INFO) + { + if ( rc == VERR_NO_TRANSLATION + || rc == VERR_INVALID_UTF8_ENCODING) + continue; + AssertMsgFailed(("%Rrc\n", rc)); + break; + } + + Log2(("vbsfCorrectCasing: found %s\n", &pDirEntry->szName[0])); + if ( pDirEntry->cbName == cchComponent + && !RTStrICmp(pszStartComponent, &pDirEntry->szName[0])) + { + Log(("Found original name %s (%s)\n", &pDirEntry->szName[0], pszStartComponent)); + strcpy(pszStartComponent, &pDirEntry->szName[0]); + rc = VINF_SUCCESS; + break; + } + } + + RTDirClose(hSearch); + } + } + + if (RT_FAILURE(rc)) + Log(("vbsfCorrectCasing %s failed with %Rrc\n", pszStartComponent, rc)); + + RTMemFree(pDirEntry); + + return rc; +} + +/* Temporary stand-in for RTPathExistEx. */ +static int vbsfQueryExistsEx(const char *pszPath, uint32_t fFlags) +{ +#if 0 /** @todo Fix the symlink issue on windows! */ + return RTPathExistsEx(pszPath, fFlags); +#else + RTFSOBJINFO IgnInfo; + return RTPathQueryInfoEx(pszPath, &IgnInfo, RTFSOBJATTRADD_NOTHING, fFlags); +#endif +} + +/** + * Helper for vbsfBuildFullPath that performs case corrections on the path + * that's being build. + * + * @returns VINF_SUCCESS at the moment. + * @param pClient The client data. + * @param pszFullPath Pointer to the full path. This is the path + * which may need case corrections. The + * corrections will be applied in place. + * @param cchFullPath The length of the full path. + * @param fWildCard Whether the last component may contain + * wildcards and thus might require exclusion + * from the case correction. + * @param fPreserveLastComponent Always exclude the last component from case + * correction if set. + */ +static int vbsfCorrectPathCasing(SHFLCLIENTDATA *pClient, char *pszFullPath, size_t cchFullPath, + bool fWildCard, bool fPreserveLastComponent) +{ + /* + * Hide the last path component if it needs preserving. This is required + * in the following cases: + * - Contains the wildcard(s). + * - Is a 'rename' target. + */ + char *pszLastComponent = NULL; + if (fWildCard || fPreserveLastComponent) + { + char *pszSrc = pszFullPath + cchFullPath - 1; + Assert(strchr(pszFullPath, '\0') == pszSrc + 1); + while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath) + { + if (*pszSrc == RTPATH_DELIMITER) + break; + pszSrc--; + } + if (*pszSrc == RTPATH_DELIMITER) + { + if ( fPreserveLastComponent + /* Or does it really have wildcards? */ + || strchr(pszSrc + 1, '*') != NULL + || strchr(pszSrc + 1, '?') != NULL + || strchr(pszSrc + 1, '>') != NULL + || strchr(pszSrc + 1, '<') != NULL + || strchr(pszSrc + 1, '"') != NULL ) + { + pszLastComponent = pszSrc; + *pszLastComponent = '\0'; + } + } + } + + /* + * If the path/file doesn't exist, we need to attempt case correcting it. + */ + /** @todo Don't check when creating files or directories; waste of time. */ + int rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient)); + if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) + { + Log(("Handle case insensitive guest fs on top of host case sensitive fs for %s\n", pszFullPath)); + + /* + * Work from the end of the path to find a partial path that's valid. + */ + char *pszSrc = pszLastComponent ? pszLastComponent - 1 : pszFullPath + cchFullPath - 1; + Assert(strchr(pszFullPath, '\0') == pszSrc + 1); + + while ((uintptr_t)pszSrc > (uintptr_t)pszFullPath) + { + if (*pszSrc == RTPATH_DELIMITER) + { + *pszSrc = '\0'; + rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient)); + *pszSrc = RTPATH_DELIMITER; + if (RT_SUCCESS(rc)) + { +#ifdef DEBUG + *pszSrc = '\0'; + Log(("Found valid partial path %s\n", pszFullPath)); + *pszSrc = RTPATH_DELIMITER; +#endif + break; + } + } + + pszSrc--; + } + Assert(*pszSrc == RTPATH_DELIMITER && RT_SUCCESS(rc)); + if ( *pszSrc == RTPATH_DELIMITER + && RT_SUCCESS(rc)) + { + /* + * Turn around and work the other way case correcting the components. + */ + pszSrc++; + for (;;) + { + bool fEndOfString = true; + + /* Find the end of the component. */ + char *pszEnd = pszSrc; + while (*pszEnd) + { + if (*pszEnd == RTPATH_DELIMITER) + break; + pszEnd++; + } + + if (*pszEnd == RTPATH_DELIMITER) + { + fEndOfString = false; + *pszEnd = '\0'; +#if 0 /** @todo Please, double check this. The original code is in the #if 0, what I hold as correct is in the #else. */ + rc = RTPathQueryInfoEx(pszSrc, &info, RTFSOBJATTRADD_NOTHING, SHFL_RT_LINK(pClient)); +#else + rc = vbsfQueryExistsEx(pszFullPath, SHFL_RT_LINK(pClient)); +#endif + Assert(rc == VINF_SUCCESS || rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND); + } + else if (pszEnd == pszSrc) + rc = VINF_SUCCESS; /* trailing delimiter */ + else + rc = VERR_FILE_NOT_FOUND; + + if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND) + { + /* Path component is invalid; try to correct the casing. */ + rc = vbsfCorrectCasing(pClient, pszFullPath, pszSrc); + if (RT_FAILURE(rc)) + { + /* Failed, so don't bother trying any further components. */ + if (!fEndOfString) + *pszEnd = RTPATH_DELIMITER; /* Restore the original full path. */ + break; + } + } + + /* Next (if any). */ + if (fEndOfString) + break; + + *pszEnd = RTPATH_DELIMITER; + pszSrc = pszEnd + 1; + } + if (RT_FAILURE(rc)) + Log(("Unable to find suitable component rc=%d\n", rc)); + } + else + rc = VERR_FILE_NOT_FOUND; + + } + + /* Restore the final component if it was dropped. */ + if (pszLastComponent) + *pszLastComponent = RTPATH_DELIMITER; + + /* might be a new file so don't fail here! */ + return VINF_SUCCESS; +} + + +#ifdef RT_OS_DARWIN +/* Misplaced hack! See todo! */ + +/** Normalize the string using kCFStringNormalizationFormD. + * + * @param pwszSrc The input UTF-16 string. + * @param cwcSrc Length of the input string in characters. + * @param ppwszDst Where to store the pointer to the resulting normalized string. + * @param pcwcDst Where to store length of the normalized string in characters (without the trailing nul). + */ +static int vbsfNormalizeStringDarwin(const PRTUTF16 pwszSrc, uint32_t cwcSrc, PRTUTF16 *ppwszDst, uint32_t *pcwcDst) +{ + /** @todo This belongs in rtPathToNative or in the windows shared folder file system driver... + * The question is simply whether the NFD normalization is actually applied on a (virtual) file + * system level in darwin, or just by the user mode application libs. */ + + PRTUTF16 pwszNFD; + uint32_t cwcNFD; + + CFMutableStringRef inStr = ::CFStringCreateMutable(NULL, 0); + + /* Is 8 times length enough for decomposed in worst case...? */ + size_t cbNFDAlloc = cwcSrc * 8 + 2; + pwszNFD = (PRTUTF16)RTMemAllocZ(cbNFDAlloc); + if (!pwszNFD) + { + return VERR_NO_MEMORY; + } + + ::CFStringAppendCharacters(inStr, (UniChar*)pwszSrc, cwcSrc); + ::CFStringNormalize(inStr, kCFStringNormalizationFormD); + cwcNFD = ::CFStringGetLength(inStr); + + CFRange rangeCharacters; + rangeCharacters.location = 0; + rangeCharacters.length = cwcNFD; + ::CFStringGetCharacters(inStr, rangeCharacters, pwszNFD); + + pwszNFD[cwcNFD] = 0x0000; /* NULL terminated */ + + CFRelease(inStr); + + *ppwszDst = pwszNFD; + *pcwcDst = cwcNFD; + return VINF_SUCCESS; +} +#endif + + +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) +/* See MSDN "Naming Files, Paths, and Namespaces". + * '<', '>' and '"' are allowed as possible wildcards (see ANSI_DOS_STAR, etc in ntifs.h) + */ +static const char sachCharBlackList[] = ":/\\|"; +#else +/* Something else. */ +static const char sachCharBlackList[] = "/"; +#endif + +/** Verify if the character can be used in a host file name. + * Wildcard characters ('?', '*') are allowed. + * + * @param c Character to verify. + */ +static bool vbsfPathIsValidNameChar(unsigned char c) +{ + /* Character 0 is not allowed too. */ + if (c == 0 || strchr(sachCharBlackList, c)) + { + return false; + } + +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + /* Characters less than 32 are not allowed. */ + if (c < 32) + { + return false; + } +#endif + + return true; +} + +/** Verify if the character is a wildcard. + * + * @param c Character to verify. + */ +static bool vbsfPathIsWildcardChar(char c) +{ + if ( c == '*' + || c == '?' +#ifdef RT_OS_WINDOWS /* See ntifs.h */ + || c == '<' /* ANSI_DOS_STAR */ + || c == '>' /* ANSI_DOS_QM */ + || c == '"' /* ANSI_DOS_DOT */ +#endif + ) + { + return true; + } + + return false; +} + +int vbsfPathGuestToHost(SHFLCLIENTDATA *pClient, SHFLROOT hRoot, + PSHFLSTRING pGuestString, uint32_t cbGuestString, + char **ppszHostPath, uint32_t *pcbHostPathRoot, + uint32_t fu32Options, + uint32_t *pfu32PathFlags) +{ +#ifdef VBOX_STRICT + /* + * Check that the pGuestPath has correct size and encoding. + */ + if (ShflStringIsValidIn(pGuestString, cbGuestString, RT_BOOL(pClient->fu32Flags & SHFL_CF_UTF8)) == false) + { + LogFunc(("Invalid input string\n")); + return VERR_INTERNAL_ERROR; + } +#else + NOREF(cbGuestString); +#endif + + /* + * Resolve the root handle into a string. + */ + uint32_t cbRootLen = 0; + const char *pszRoot = NULL; + int rc = vbsfMappingsQueryHostRootEx(hRoot, &pszRoot, &cbRootLen); + if (RT_FAILURE(rc)) + { + LogFunc(("invalid root\n")); + return rc; + } + + AssertReturn(cbRootLen > 0, VERR_INTERNAL_ERROR_2); /* vbsfMappingsQueryHostRootEx ensures this. */ + + /* + * Get the UTF8 string with the relative path provided by the guest. + * If guest uses UTF-16 then convert it to UTF-8. + */ + uint32_t cbGuestPath = 0; /* Shut up MSC */ + const char *pchGuestPath = NULL; /* Ditto. */ + char *pchGuestPathAllocated = NULL; /* Converted from UTF-16. */ + if (BIT_FLAG(pClient->fu32Flags, SHFL_CF_UTF8)) + { + /* UTF-8 */ + cbGuestPath = pGuestString->u16Length; + pchGuestPath = (char *)&pGuestString->String.utf8[0]; + } + else + { + /* UTF-16 */ + uint32_t cwcSrc; + PRTUTF16 pwszSrc; + +#ifdef RT_OS_DARWIN /* Misplaced hack! See todo! */ + cwcSrc = 0; + pwszSrc = NULL; + rc = vbsfNormalizeStringDarwin(&pGuestString->String.ucs2[0], + pGuestString->u16Length / sizeof(RTUTF16), + &pwszSrc, &cwcSrc); +#else + cwcSrc = pGuestString->u16Length / sizeof(RTUTF16); + pwszSrc = &pGuestString->String.ucs2[0]; +#endif + + if (RT_SUCCESS(rc)) + { + size_t cbPathAsUtf8 = RTUtf16CalcUtf8Len(pwszSrc); + if (cbPathAsUtf8 >= cwcSrc) + { + /* Allocate buffer that will be able to contain the converted UTF-8 string. */ + pchGuestPathAllocated = (char *)RTMemAlloc(cbPathAsUtf8 + 1); + if (RT_LIKELY(pchGuestPathAllocated != NULL)) + { + if (RT_LIKELY(cbPathAsUtf8)) + { + size_t cchActual; + char *pszDst = pchGuestPathAllocated; + rc = RTUtf16ToUtf8Ex(pwszSrc, cwcSrc, &pszDst, cbPathAsUtf8 + 1, &cchActual); + AssertRC(rc); + AssertStmt(RT_FAILURE(rc) || cchActual == cbPathAsUtf8, rc = VERR_INTERNAL_ERROR_4); + Assert(strlen(pszDst) == cbPathAsUtf8); + } + + if (RT_SUCCESS(rc)) + { + /* Terminate the string. */ + pchGuestPathAllocated[cbPathAsUtf8] = '\0'; + + cbGuestPath = (uint32_t)cbPathAsUtf8; Assert(cbGuestPath == cbPathAsUtf8); + pchGuestPath = pchGuestPathAllocated; + } + } + else + { + rc = VERR_NO_MEMORY; + } + } + else + { + AssertFailed(); + rc = VERR_INTERNAL_ERROR_3; + } + +#ifdef RT_OS_DARWIN + RTMemFree(pwszSrc); +#endif + } + } + + char *pszFullPath = NULL; + + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("Root %s path %.*s\n", pszRoot, cbGuestPath, pchGuestPath)); + + /* + * Allocate enough memory to build the host full path from the root and the relative path. + */ + const uint32_t cbFullPathAlloc = cbRootLen + 1 + cbGuestPath + 1; /* root + possible_slash + relative + 0 */ + pszFullPath = (char *)RTMemAlloc(cbFullPathAlloc); + if (RT_LIKELY(pszFullPath != NULL)) + { + /* Buffer for the verified guest path. */ + char *pchVerifiedPath = (char *)RTMemAlloc(cbGuestPath + 1); + if (RT_LIKELY(pchVerifiedPath != NULL)) + { + /* Init the pointer for the guest relative path. */ + uint32_t cbSrc = cbGuestPath; + const char *pchSrc = pchGuestPath; + + /* Strip leading delimiters from the path the guest specified. */ + while ( cbSrc > 0 + && *pchSrc == pClient->PathDelimiter) + { + ++pchSrc; + --cbSrc; + } + + /* + * Iterate the guest path components, verify each of them replacing delimiters with the host slash. + */ + char *pchDst = pchVerifiedPath; + bool fLastComponentHasWildcard = false; + for (; cbSrc > 0; --cbSrc, ++pchSrc) + { + if (RT_LIKELY(*pchSrc != pClient->PathDelimiter)) + { + if (RT_LIKELY(vbsfPathIsValidNameChar(*pchSrc))) + { + if (pfu32PathFlags && vbsfPathIsWildcardChar(*pchSrc)) + { + fLastComponentHasWildcard = true; + } + + *pchDst++ = *pchSrc; + } + else + { + rc = VERR_INVALID_NAME; + break; + } + } + else + { + /* Replace with the host slash. */ + *pchDst++ = RTPATH_SLASH; + + if (pfu32PathFlags && fLastComponentHasWildcard && cbSrc > 1) + { + /* Processed component has a wildcard and there are more characters in the path. */ + *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_PREFIX; + } + fLastComponentHasWildcard = false; + } + } + + if (RT_SUCCESS(rc)) + { + *pchDst++ = 0; + + /* Construct the full host path removing '.' and '..'. */ + rc = vbsfPathAbs(pszRoot, pchVerifiedPath, pszFullPath, cbFullPathAlloc); + if (RT_SUCCESS(rc)) + { + if (pfu32PathFlags && fLastComponentHasWildcard) + { + *pfu32PathFlags |= VBSF_F_PATH_HAS_WILDCARD_IN_LAST; + } + + /* Check if the full path is still within the shared folder. */ + if (fu32Options & VBSF_O_PATH_CHECK_ROOT_ESCAPE) + { + if (!RTPathStartsWith(pszFullPath, pszRoot)) + { + rc = VERR_INVALID_NAME; + } + } + + if (RT_SUCCESS(rc)) + { + /* + * If the host file system is case sensitive and the guest expects + * a case insensitive fs, then correct the path components casing. + */ + if ( vbsfIsHostMappingCaseSensitive(hRoot) + && !vbsfIsGuestMappingCaseSensitive(hRoot)) + { + const bool fWildCard = RT_BOOL(fu32Options & VBSF_O_PATH_WILDCARD); + const bool fPreserveLastComponent = RT_BOOL(fu32Options & VBSF_O_PATH_PRESERVE_LAST_COMPONENT); + rc = vbsfCorrectPathCasing(pClient, pszFullPath, strlen(pszFullPath), + fWildCard, fPreserveLastComponent); + } + + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("%s\n", pszFullPath)); + + /* Return the full host path. */ + *ppszHostPath = pszFullPath; + + if (pcbHostPathRoot) + { + /* Return the length of the root path without the trailing slash. */ + *pcbHostPathRoot = RTPATH_IS_SLASH(pszFullPath[cbRootLen - 1]) ? + cbRootLen - 1 : /* pszRoot already had the trailing slash. */ + cbRootLen; /* pszRoot did not have the trailing slash. */ + } + } + } + } + else + { + LogFunc(("vbsfPathAbs %Rrc\n", rc)); + } + } + + RTMemFree(pchVerifiedPath); + } + else + { + rc = VERR_NO_MEMORY; + } + } + else + { + rc = VERR_NO_MEMORY; + } + } + + /* + * Cleanup. + */ + RTMemFree(pchGuestPathAllocated); + + if (RT_SUCCESS(rc)) + { + return rc; + } + + /* + * Cleanup on failure. + */ + RTMemFree(pszFullPath); + + LogFunc(("%Rrc\n", rc)); + return rc; +} + +void vbsfFreeHostPath(char *pszHostPath) +{ + RTMemFree(pszHostPath); +} + diff --git a/src/VBox/HostServices/SharedFolders/vbsfpath.h b/src/VBox/HostServices/SharedFolders/vbsfpath.h new file mode 100644 index 00000000..cec601c4 --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/vbsfpath.h @@ -0,0 +1,70 @@ +/* $Id: vbsfpath.h $ */ +/** @file + * Shared Folders Service - Guest/host path convertion and verification. + */ + +/* + * Copyright (C) 2006-2019 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. + */ + +#ifndef VBOX_INCLUDED_SRC_SharedFolders_vbsfpath_h +#define VBOX_INCLUDED_SRC_SharedFolders_vbsfpath_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "shfl.h" +#include <VBox/shflsvc.h> + +#define VBSF_O_PATH_WILDCARD UINT32_C(0x00000001) +#define VBSF_O_PATH_PRESERVE_LAST_COMPONENT UINT32_C(0x00000002) +#define VBSF_O_PATH_CHECK_ROOT_ESCAPE UINT32_C(0x00000004) + +#define VBSF_F_PATH_HAS_WILDCARD_IN_PREFIX UINT32_C(0x00000001) /* A component before the last one contains a wildcard. */ +#define VBSF_F_PATH_HAS_WILDCARD_IN_LAST UINT32_C(0x00000002) /* The last component contains a wildcard. */ + +/** + * + * @param pClient Shared folder client. + * @param hRoot Root handle. + * @param pGuestString Guest want to access the path. + * @param cbGuestString Size of pGuestString memory buffer. + * @param ppszHostPath Returned full host path: root prefix + guest path. + * @param pcbHostPathRoot Length of the root prefix in bytes. Optional, can be NULL. + * @param fu32Options Options. + * @param pfu32PathFlags VBSF_F_PATH_* flags. Optional, can be NULL. + */ +int vbsfPathGuestToHost(SHFLCLIENTDATA *pClient, SHFLROOT hRoot, + PSHFLSTRING pGuestString, uint32_t cbGuestString, + char **ppszHostPath, uint32_t *pcbHostPathRoot, + uint32_t fu32Options, uint32_t *pfu32PathFlags); + +/** Free the host path returned by vbsfPathGuestToHost. + * + * @param pszHostPath Host path string. + */ +void vbsfFreeHostPath(char *pszHostPath); + +/** + * Build the absolute path by combining an absolute pszRoot and a relative pszPath. + * The resulting path does not contain '.' and '..' components. + * Similar to RTPathAbsEx but with support for Windows extended-length paths ("\\?\" prefix). + * Uses RTPathAbsEx for regular paths and on non-Windows hosts. + * + * @param pszRoot The absolute prefix. It is copied to the pszAbsPath without any processing. + * If NULL then the pszPath must be converted to the absolute path. + * @param pszPath The relative path to be appended to pszRoot. Already has correct delimiters (RTPATH_SLASH). + * @param pszAbsPath Where to store the resulting absolute path. + * @param cbAbsPath Size of pszAbsBuffer in bytes. + */ +int vbsfPathAbs(const char *pszRoot, const char *pszPath, char *pszAbsPath, size_t cbAbsPath); + +#endif /* !VBOX_INCLUDED_SRC_SharedFolders_vbsfpath_h */ diff --git a/src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp b/src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp new file mode 100644 index 00000000..096e193d --- /dev/null +++ b/src/VBox/HostServices/SharedFolders/vbsfpathabs.cpp @@ -0,0 +1,185 @@ +/* $Id: vbsfpathabs.cpp $ */ +/** @file + * Shared Folders Service - guest/host path convertion and verification. + */ + +/* + * Copyright (C) 2017-2019 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_SHARED_FOLDERS +#include <iprt/err.h> +#include <iprt/path.h> +#include <iprt/string.h> + + +#if defined(RT_OS_WINDOWS) +static void vbsfPathResolveRelative(char *pszPathBegin) +{ + char *pszCur = pszPathBegin; + char * const pszTop = pszCur; + + /* + * Get rid of double dot path components by evaluating them. + */ + for (;;) + { + char const chFirst = pszCur[0]; + if ( chFirst == '.' + && pszCur[1] == '.' + && (!pszCur[2] || pszCur[2] == RTPATH_SLASH)) + { + /* rewind to the previous component if any */ + char *pszPrev = pszCur; + if ((uintptr_t)pszPrev > (uintptr_t)pszTop) + { + pszPrev--; + while ( (uintptr_t)pszPrev > (uintptr_t)pszTop + && pszPrev[-1] != RTPATH_SLASH) + pszPrev--; + } + if (!pszCur[2]) + { + if (pszPrev != pszTop) + pszPrev[-1] = '\0'; + else + *pszPrev = '\0'; + break; + } + Assert(pszPrev[-1] == RTPATH_SLASH); + memmove(pszPrev, pszCur + 3, strlen(pszCur + 3) + 1); + pszCur = pszPrev - 1; + } + else if ( chFirst == '.' + && (!pszCur[1] || pszCur[1] == RTPATH_SLASH)) + { + /* remove unnecessary '.' */ + if (!pszCur[1]) + { + if (pszCur != pszTop) + pszCur[-1] = '\0'; + else + *pszCur = '\0'; + break; + } + memmove(pszCur, pszCur + 2, strlen(pszCur + 2) + 1); + continue; + } + else + { + /* advance to end of component. */ + while (*pszCur && *pszCur != RTPATH_SLASH) + pszCur++; + } + + if (!*pszCur) + break; + + /* skip the slash */ + ++pszCur; + } +} +#endif /* RT_OS_WINDOWS */ + +int vbsfPathAbs(const char *pszRoot, const char *pszPath, char *pszAbsPath, size_t cbAbsPath) +{ +#if defined(RT_OS_WINDOWS) + const char *pszPathStart = pszRoot? pszRoot: pszPath; + + /* Windows extended-length paths. */ + if ( RTPATH_IS_SLASH(pszPathStart[0]) + && RTPATH_IS_SLASH(pszPathStart[1]) + && pszPathStart[2] == '?' + && RTPATH_IS_SLASH(pszPathStart[3]) + ) + { + /* Maximum total path length of 32,767 characters. */ + if (cbAbsPath > _32K) + cbAbsPath = _32K; + + /* Copy the root to pszAbsPath buffer. */ + size_t cchRoot = pszRoot? strlen(pszRoot): 0; + if (cchRoot >= cbAbsPath) + return VERR_FILENAME_TOO_LONG; + + if (pszRoot) + { + /* Caller must ensure that the path is relative, without the leading path separator. */ + if (RTPATH_IS_SLASH(pszPath[0])) + return VERR_INVALID_PARAMETER; + + if (cchRoot) + memcpy(pszAbsPath, pszRoot, cchRoot); + + if (cchRoot == 0 || !RTPATH_IS_SLASH(pszAbsPath[cchRoot - 1])) + { + /* Append path separator after the root. */ + ++cchRoot; + if (cchRoot >= cbAbsPath) + return VERR_FILENAME_TOO_LONG; + + pszAbsPath[cchRoot - 1] = RTPATH_SLASH; + } + } + + /* Append the path to the pszAbsPath buffer. */ + const size_t cchPath = strlen(pszPath); + if (cchRoot + cchPath >= cbAbsPath) + return VERR_FILENAME_TOO_LONG; + + memcpy(&pszAbsPath[cchRoot], pszPath, cchPath + 1); /* Including trailing 0. */ + + /* Find out where the actual path begins, i.e. skip the root spec. */ + char *pszPathBegin = &pszAbsPath[4]; /* Skip the extended-length path prefix "\\?\" */ + if ( pszPathBegin[0] + && RTPATH_IS_VOLSEP(pszPathBegin[1]) + && pszPathBegin[2] == RTPATH_SLASH) + { + /* "\\?\C:\" */ + pszPathBegin += 3; + } + else if ( pszPathBegin[0] == 'U' + && pszPathBegin[1] == 'N' + && pszPathBegin[2] == 'C' + && pszPathBegin[3] == RTPATH_SLASH) + { + /* "\\?\UNC\server\share" */ + pszPathBegin += 4; + + /* Skip "server\share" too. */ + while (*pszPathBegin != RTPATH_SLASH && *pszPathBegin) + ++pszPathBegin; + if (*pszPathBegin == RTPATH_SLASH) + { + ++pszPathBegin; + while (*pszPathBegin != RTPATH_SLASH && *pszPathBegin) + ++pszPathBegin; + if (*pszPathBegin == RTPATH_SLASH) + ++pszPathBegin; + } + } + else + return VERR_INVALID_NAME; + + /* Process pszAbsPath in place. */ + vbsfPathResolveRelative(pszPathBegin); + + return VINF_SUCCESS; + } +#endif /* RT_OS_WINDOWS */ + + /* Fallback for the common paths. */ + return RTPathAbsEx(pszRoot, pszPath, pszAbsPath, cbAbsPath); +} diff --git a/src/VBox/HostServices/SharedOpenGL/.scm-settings b/src/VBox/HostServices/SharedOpenGL/.scm-settings new file mode 100644 index 00000000..b16af1ce --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/.scm-settings @@ -0,0 +1,42 @@ +# $Id: .scm-settings $ +## @file +# Source code massager settings for the SharedOpenGL host service. +# + +# +# Copyright (C) 2017-2019 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. +# + + +# The basic config here is external copyright, however there are lots of exceptions +/*.h: --external-copyright --no-convert-tabs --no-strip-trailing-blanks --no-fix-header-guards +/crserverlib/presenter/server_presenter.h: --no-external-copyright --convert-tabs --strip-trailing-blanks +/render/renderspu_cocoa_helper.h: --no-external-copyright --convert-tabs --strip-trailing-blanks + +/*.c: --external-copyright --no-convert-tabs --no-strip-trailing-blanks +/crserverlib/server_framebuffer.c: --no-external-copyright --convert-tabs --strip-trailing-blanks +/crserverlib/server_getshaders.c: --no-external-copyright --convert-tabs --strip-trailing-blanks +/crserverlib/server_glsl.c: --no-external-copyright --convert-tabs --strip-trailing-blanks +/crserverlib/server_texture.c: --no-external-copyright --convert-tabs --strip-trailing-blanks +/dlm/dlm_lists.c: --no-external-copyright --convert-tabs --strip-trailing-blanks +/dlm/dlm_state.c: --no-external-copyright --convert-tabs --strip-trailing-blanks +/expando/expandospu.c: --no-external-copyright --convert-tabs --strip-trailing-blanks +/render/renderspu_cocoa.c: --no-external-copyright --convert-tabs --strip-trailing-blanks +/unpacker/unpack_framebuffer.c: --no-external-copyright --convert-tabs --strip-trailing-blanks +/unpacker/unpack_shaders.c: --no-external-copyright --convert-tabs --strip-trailing-blanks +/unpacker/unpack_visibleregion.c: --no-external-copyright --convert-tabs --strip-trailing-blanks + +/*.def: --external-copyright +/*.py: --external-copyright --no-convert-tabs --no-strip-trailing-blanks + +--filter-out-files *_special +--filter-out-files /LICENSE + diff --git a/src/VBox/HostServices/SharedOpenGL/LICENSE b/src/VBox/HostServices/SharedOpenGL/LICENSE new file mode 100644 index 00000000..d609a358 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/LICENSE @@ -0,0 +1,32 @@ +Copyright (c) 2002, Stanford University +All rights reserved. + +Some portions of Chromium are copyrighted by individual organizations. +Please see the files COPYRIGHT.LLNL and COPYRIGHT.REDHAT for more +information. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Stanford University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/src/VBox/HostServices/SharedOpenGL/Makefile.kmk b/src/VBox/HostServices/SharedOpenGL/Makefile.kmk new file mode 100644 index 00000000..e0faf5d6 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/Makefile.kmk @@ -0,0 +1,459 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Shared OpenGL Host Service. +# + +# +# Copyright (C) 2008-2019 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. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + + +# +# Target lists. +# +ifdef VBOX_WITH_MAIN +DLLS += VBoxSharedCrOpenGL VBoxOGLrenderspu +LIBRARIES += VBoxOGLcrserverlib VBoxOGLcrunpacker +BLDDIRS += \ + $(VBOX_PATH_CROGL_GENFILES)/ +endif + +ifdef VBOX_WITH_CR_DISPLAY_LISTS + LIBRARIES += VBoxOGLcrdlm + DLLS += VBoxOGLexpandospu +endif # VBOX_WITH_CR_DISPLAY_LISTS + +ifeq ($(KBUILD_TARGET),darwin) + # + # We have to symlink the system headers of OpenGl cause they have some + # different directory hierarchy on Mac OS X (no /GL sub directory). + # + # See Config.kmk for the global variables. + # + INSTALLS += DarwinOpenGLHdrs + DarwinOpenGLHdrs_INST = $(VBOX_DARWIN_OPENGL_INST) + DarwinOpenGLHdrs_SYMLINKS = \ + $(foreach hdr, $(VBOX_DARWIN_OPENGL_HEADERS),\ + $(hdr)=>$(VBOX_PATH_MACOSX_SDK)/System/Library/Frameworks/OpenGL.framework/Versions/Current/Headers/$(hdr)) +endif # darwin + +# +# VBoxSharedCrOpenGL +# +VBoxSharedCrOpenGL_TEMPLATE = VBOXCROGLR3HOSTDLL +ifdef VBOX_WITH_XPCOM + VBoxSharedCrOpenGL_DEFS = VBOX_WITH_XPCOM + VBoxSharedCrOpenGL_CXXFLAGS = -Wno-non-virtual-dtor -fshort-wchar $(VBOX_GCC_std) +endif +VBoxSharedCrOpenGL_INTERMEDIATES = \ + $(TEMPLATE_VBOXMAINEXE_INTERMEDIATES) +VBoxSharedCrOpenGL_INCS = $(VBOX_GRAPHICS_INCS) +VBoxSharedCrOpenGL_INCS.win = \ + $(VBOX_PATH_SDK)/bindings/mscom/include +ifdef VBOX_WITH_XPCOM +VBoxSharedCrOpenGL_INCS += \ + $(VBOX_XPCOM_INCS) +endif +VBoxSharedCrOpenGL_SOURCES = \ + crserver/crservice.cpp +VBoxSharedCrOpenGL_SOURCES.win = \ + crserver/VBoxSharedCrOpenGL.rc +VBoxSharedCrOpenGL_LDFLAGS.darwin = -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxSharedCrOpenGL.dylib +VBoxSharedCrOpenGL_LIBS = \ + $(PATH_STAGE_LIB)/VBoxOGLcrserverlib$(VBOX_SUFF_LIB) \ + $(PATH_STAGE_LIB)/VBoxOGLhostcrstate$(VBOX_SUFF_LIB) \ + $(PATH_STAGE_LIB)/VBoxOGLcrunpacker$(VBOX_SUFF_LIB) \ + $(PATH_STAGE_LIB)/VBoxOGLhostcrpacker$(VBOX_SUFF_LIB) \ + $(PATH_STAGE_LIB)/VBoxOGLhostspuload$(VBOX_SUFF_LIB) \ + $(VBOX_LIB_OGL_HOSTCRUTIL) \ + $(PATH_STAGE_LIB)/VBoxCOM$(VBOX_SUFF_LIB) \ + $(LIB_RUNTIME) \ + $(LIB_VMM) +VBoxSharedCrOpenGL_LIBS.darwin = \ + $(LIB_REM) +ifeq ($(KBUILD_TARGET),win) + VBoxSharedCrOpenGL_LIBS += \ + $(PATH_OBJ)/VBoxOGLrenderspu/VBoxOGLrenderspu$(VBOX_SUFF_LIB) +else + VBoxSharedCrOpenGL_LIBS += \ + $(PATH_STAGE_BIN)/VBoxOGLrenderspu$(VBOX_SUFF_DLL) +endif +ifdef VBOX_WITH_XPCOM + VBoxSharedCrOpenGL_LIBS += \ + $(LIB_XPCOM) +endif +ifdef VBOX_WITH_CRHGSMI +VBoxSharedCrOpenGL_DEFS += VBOX_WITH_CRHGSMI +endif +ifdef VBOX_WITH_CR_DISPLAY_LISTS +VBoxSharedCrOpenGL_LIBS += $(PATH_STAGE_LIB)/VBoxOGLcrdlm$(VBOX_SUFF_LIB) +endif + +# +# VBoxOGLcrserverlib +# +VBoxOGLcrserverlib_TEMPLATE = VBOXCROGLR3HOSTLIB +VBoxOGLcrserverlib_INCS = \ + . \ + crserverlib \ + $(VBOX_GRAPHICS_INCS) +VBoxOGLcrserverlib_INTERMEDIATES = \ + $(VBOX_PATH_CROGL_GENFILES)/spu_dispatch_table.h \ + $(VBOX_PATH_CROGL_GENFILES)/server_dispatch.h \ + $(VBOX_PATH_CROGL_GENFILES)/cr_opcodes.h \ + $(VBOX_PATH_CROGL_GENFILES)/state/cr_currentpointers.h \ + $(VBOX_PATH_CROGL_GENFILES)/state/cr_statefuncs.h + +ifdef VBOX_WITH_CR_DISPLAY_LISTS +VBoxOGLcrserverlib_INTERMEDIATES += $(VBOX_PATH_CROGL_GENFILES)/cr_dlm.h +endif + +VBoxOGLcrserverlib_SOURCES := \ + crserverlib/server_main.c \ + crserverlib/server_boundsinfo.c \ + crserverlib/server_bufferobject.c \ + crserverlib/server_clear.c \ + crserverlib/server_clip.c \ + crserverlib/server_config.c \ + crserverlib/server_context.c \ + crserverlib/server_gentextures.c \ + crserverlib/server_getmap.c \ + crserverlib/server_getstring.c \ + crserverlib/server_getpointer.c \ + crserverlib/server_getpixelmap.c \ + crserverlib/server_getteximage.c \ + crserverlib/server_lists.c \ + crserverlib/server_misc.c \ + crserverlib/server_occlude.c \ + crserverlib/server_papi.c \ + crserverlib/server_projmatrix.c \ + crserverlib/server_readpixels.c \ + crserverlib/server_stream.c \ + crserverlib/server_viewport.c \ + crserverlib/server_window.c \ + crserverlib/server_winpos.c \ + crserverlib/server_writeback.c \ + crserverlib/server_getshaders.c \ + crserverlib/server_framebuffer.c \ + crserverlib/server_glsl.c \ + crserverlib/server_muralfbo.cpp \ + crserverlib/server_texture.c \ + crserverlib/presenter/server_presenter.cpp \ + crserverlib/presenter/display_base.cpp \ + crserverlib/presenter/display_composite.cpp \ + crserverlib/presenter/window.cpp \ + crserverlib/presenter/display_window.cpp \ + crserverlib/presenter/display_window_rootvr.cpp \ + crserverlib/presenter/display_vrdp.cpp \ + crserverlib/server_rpw.cpp \ + $(VBOX_PATH_CROGL_GENFILES)/server_dispatch.c \ + $(VBOX_PATH_CROGL_GENFILES)/server_retval.c \ + $(VBOX_PATH_CROGL_GENFILES)/server_get.c \ + $(VBOX_PATH_CROGL_GENFILES)/server_simpleget.c +VBoxOGLcrserverlib_CLEAN = \ + $(VBOX_PATH_CROGL_GENFILES)/server_dispatch.c \ + $(VBOX_PATH_CROGL_GENFILES)/server_retval.c \ + $(VBOX_PATH_CROGL_GENFILES)/server_get.c \ + $(VBOX_PATH_CROGL_GENFILES)/server_simpleget.c \ + $(VBOX_PATH_CROGL_GENFILES)/server_dispatch.h +ifdef VBOX_WITH_CR_DISPLAY_LISTS +VBoxOGLcrserverlib_DEFS += VBOX_WITH_CR_DISPLAY_LISTS +endif +ifdef VBOXCR_LOGFPS +VBoxOGLcrserverlib_DEFS += VBOXCR_LOGFPS +endif +ifdef VBOX_WITH_CRHGSMI +VBoxOGLcrserverlib_DEFS += ifdef VBOX_WITH_CRHGSMI +endif +ifdef VBOX_WITH_CRDUMPER +VBoxOGLcrserverlib_DEFS += VBOX_WITH_CRDUMPER +endif +ifdef VBOX_WITH_CRSERVER_DUMPER +VBoxOGLcrserverlib_DEFS += VBOX_WITH_CRSERVER_DUMPER +endif + + +# +# Generate files for VBoxOGLcrserverlib +# +$(VBOX_PATH_CROGL_GENFILES)/server_dispatch.h: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_dispatch_header.py server_special) $(VBOX_CROGL_API_FILES) $(PATH_ROOT)/src/VBox/GuestHost/OpenGL/state_tracker/state_special | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) + +$(VBOX_PATH_CROGL_GENFILES)/server_dispatch.c: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_dispatch.py server_special) $(VBOX_CROGL_API_FILES) $(PATH_ROOT)/src/VBox/GuestHost/OpenGL/state_tracker/state_special | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) + +$(VBOX_PATH_CROGL_GENFILES)/server_retval.c: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_retval.py server_special) $(VBOX_CROGL_API_FILES) | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) + +$(VBOX_PATH_CROGL_GENFILES)/server_get.c: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_get.py server_special) $(VBOX_CROGL_API_FILES) | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) + +$(VBOX_PATH_CROGL_GENFILES)/server_simpleget.c: $(addprefix $(PATH_SUB_CURRENT)/crserverlib/, server_simpleget.py get_sizes.py) $(VBOX_CROGL_API_FILES) | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) + + +# +# VBoxOGLcrunpacker +# +VBoxOGLcrunpacker_TEMPLATE = VBOXCROGLR3HOSTLIB +VBoxOGLcrunpacker_INCS = \ + unpacker \ + $(VBOX_GRAPHICS_INCS) +VBoxOGLcrunpacker_INTERMEDIATES = \ + $(VBOX_PATH_CROGL_GENFILES)/spu_dispatch_table.h \ + $(VBOX_PATH_CROGL_GENFILES)/cr_opcodes.h \ + $(VBOX_PATH_CROGL_GENFILES)/unpack_extend.h \ + $(VBOX_PATH_CROGL_GENFILES)/state/cr_currentpointers.h \ + $(VBOX_PATH_CROGL_GENFILES)/state/cr_statefuncs.h +VBoxOGLcrunpacker_SOURCES = \ + unpacker/unpack_arrays.c \ + unpacker/unpack_bounds.c \ + unpacker/unpack_bufferobject.c \ + unpacker/unpack_calllists.c \ + unpacker/unpack_clipplane.c \ + unpacker/unpack_context.c \ + unpacker/unpack_drawpixels.c \ + unpacker/unpack_fence.c \ + unpacker/unpack_fog.c \ + unpacker/unpack_lights.c \ + unpacker/unpack_map.c \ + unpacker/unpack_materials.c \ + unpacker/unpack_matrices.c \ + unpacker/unpack_misc.c \ + unpacker/unpack_pixelmap.c \ + unpacker/unpack_point.c \ + unpacker/unpack_program.c \ + unpacker/unpack_readpixels.c \ + unpacker/unpack_regcombiner.c \ + unpacker/unpack_stipple.c \ + unpacker/unpack_texture.c \ + unpacker/unpack_writeback.c \ + unpacker/unpack_visibleregion.c \ + unpacker/unpack_shaders.c \ + unpacker/unpack_framebuffer.c \ + $(VBOX_PATH_CROGL_GENFILES)/unpack.c +VBoxOGLcrunpacker_CLEAN = \ + $(VBOX_PATH_CROGL_GENFILES)/unpack.c \ + $(VBOX_PATH_CROGL_GENFILES)/unpack_extend.h + +# +# Generate files for VBoxOGLcrunpacker. +# +$(VBOX_PATH_CROGL_GENFILES)/unpack.c: \ + $(addprefix $(PATH_SUB_CURRENT)/unpacker/, unpack.py unpacker_special) \ + $(VBOX_PATH_CROGL_GENFILES)/unpack_extend.h \ + $(VBOX_CROGL_API_FILES) \ + | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) + +$(VBOX_PATH_CROGL_GENFILES)/unpack_extend.h: \ + $(addprefix $(PATH_SUB_CURRENT)/unpacker/, unpack_extend.py unpacker_special) \ + $(VBOX_CROGL_API_FILES) \ + | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) + + +ifdef VBOX_WITH_CR_DISPLAY_LISTS +# +# VBoxOGLcrdlm +# + +VBoxOGLcrdlm_TEMPLATE = VBOXCROGLR3HOSTLIB +VBoxOGLcrdlm_INCS = \ + dlm +VBoxOGLcrdlm_INTERMEDIATES = \ + $(VBOX_PATH_CROGL_GENFILES)/cr_dlm.h \ + $(VBOX_PATH_CROGL_GENFILES)/dlm_generated.h + +VBoxOGLcrdlm_SOURCES = \ + dlm/dlm.c \ + dlm/dlm_arrays.c \ + dlm/dlm_state.c \ + dlm/dlm_checklist.c \ + dlm/dlm_error.c \ + dlm/dlm_lists.c \ + dlm/dlm_pointers.c \ + $(VBOX_PATH_CROGL_GENFILES)/dlm_generated.c + +VBoxOGLcrdlm_CLEAN = \ + $(VBOX_PATH_CROGL_GENFILES)/dlm_generated.c \ + $(VBOX_PATH_CROGL_GENFILES)/cr_dlm.h \ + $(VBOX_PATH_CROGL_GENFILES)/dlm_generated.h +# +# Generate files for VBoxOGLcrdlm. +# +$(VBOX_PATH_CROGL_GENFILES)/cr_dlm.h: \ + $(addprefix $(PATH_SUB_CURRENT)/dlm/, dlm_header.py) \ + $(VBOX_CROGL_API_FILES) \ + | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< header $(<D) $(VBOX_PATH_CROGL_GLAPI) > $@ + +$(VBOX_PATH_CROGL_GENFILES)/dlm_generated.h: \ + $(addprefix $(PATH_SUB_CURRENT)/dlm/, dlm_generated.py dlm_special) \ + $(VBOX_PATH_CROGL_GENFILES)/cr_dlm.h \ + $(VBOX_CROGL_API_FILES) \ + | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< headers $(<D) $(VBOX_PATH_CROGL_GLAPI) > $@ + +$(VBOX_PATH_CROGL_GENFILES)/dlm_generated.c: \ + $(addprefix $(PATH_SUB_CURRENT)/dlm/, dlm_generated.py dlm_special) \ + $(VBOX_PATH_CROGL_GENFILES)/dlm_generated.h \ + $(VBOX_CROGL_API_FILES) \ + | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< source $(<D) $(VBOX_PATH_CROGL_GLAPI) > $@ + + +# +# VBoxOGLexpandospu +# +VBoxOGLexpandospu_TEMPLATE = VBOXCROGLR3HOSTDLL +VBoxOGLexpandospu_INCS = \ + expando +VBoxOGLexpandospu_SOURCES = \ + expando/expandospu.c \ + expando/expandospu_config.c \ + expando/expandospu_init.c \ + $(VBOX_PATH_CROGL_GENFILES)/expando.c +VBoxOGLexpandospu_CLEAN = \ + $(VBOX_PATH_CROGL_GENFILES)/expando.c +VBoxOGLexpandospu_CLEAN = \ + $(VBOX_PATH_CROGL_GENFILES)/expando.c +VBoxOGLexpandospu_LDFLAGS.darwin += -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxOGLexpandospu.dylib +VBoxOGLexpandospu_LIBS = \ + $(PATH_STAGE_LIB)/VBoxOGLcrdlm$(VBOX_SUFF_LIB) \ + $(PATH_STAGE_LIB)/VBoxOGLhostcrstate$(VBOX_SUFF_LIB) \ + $(PATH_STAGE_LIB)/VBoxOGLhostspuload$(VBOX_SUFF_LIB) \ + $(VBOX_LIB_OGL_HOSTCRUTIL) \ + $(LIB_RUNTIME) \ + $(LIB_VMM) +# +# Generate files for VBoxOGLexpandospu. +# +$(VBOX_PATH_CROGL_GENFILES)/expando.c: \ + $(addprefix $(PATH_SUB_CURRENT)/expando/, expando.py expando_special) \ + $(VBOX_CROGL_API_FILES) \ + | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) $(<D) > $@ +endif + + +# +# VBoxOGLrenderspu +# +VBoxOGLrenderspu_TEMPLATE = VBOXCROGLR3HOSTDLL +VBoxOGLrenderspu_INTERMEDIATES = \ + $(VBOX_PATH_CROGL_GENFILES)/state/cr_currentpointers.h \ + $(VBOX_PATH_CROGL_GENFILES)/state/cr_statefuncs.h +VBoxOGLrenderspu_INCS = $(VBOX_GRAPHICS_INCS) +VBoxOGLrenderspu_SOURCES = \ + render/renderspu.c \ + render/renderspu_config.c \ + render/renderspu_init.c +VBoxOGLrenderspu_SOURCES.win = \ + render/renderspu_wgl.c \ + render/render.def \ + render/VBoxOGLrenderspu.rc +VBoxOGLrenderspu_SOURCES.linux = render/renderspu_glx.c +VBoxOGLrenderspu_SOURCES.solaris = render/renderspu_glx.c +VBoxOGLrenderspu_SOURCES.freebsd = render/renderspu_glx.c +VBoxOGLrenderspu_OBJCFLAGS.darwin = -Wno-shadow +VBoxOGLrenderspu_SOURCES.darwin = \ + OpenGLTest/OpenGLTestDarwin.cpp \ + render/renderspu_cocoa.c \ + render/renderspu_cocoa_helper.m +ifdef VBOX_WITH_CRHGSMI +VBoxOGLrenderspu_DEFS += VBOX_WITH_CRHGSMI +endif +ifdef VBOX_WITH_VDMA +VBoxOGLrenderspu_DEFS += VBOX_WITH_VDMA +endif +VBoxOGLrenderspu_LDFLAGS.darwin += -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxOGLrenderspu.dylib -framework IOKit +VBoxOGLrenderspu_LIBS = \ + $(PATH_STAGE_LIB)/VBoxOGLhostspuload$(VBOX_SUFF_LIB) \ + $(VBOX_LIB_OGL_HOSTCRUTIL) \ + $(LIB_RUNTIME) +if1of ($(KBUILD_TARGET), freebsd linux netbsd openbsd solaris) # the X11 gang + VBoxOGLrenderspu_LIBS += \ + Xmu \ + X11 \ + Xext + VBoxOGLrenderspu_LIBPATH = \ + $(VBOX_LIBPATH_X11) +endif + +LIBRARIES += VBoxOGLTest +VBoxOGLTest_TEMPLATE = VBOXR3 +ifneq ($(KBUILD_TARGET),darwin) + VBoxOGLTest_SOURCES = OpenGLTest/OpenGLTest.cpp +endif +VBoxOGLTest_SOURCES.darwin = OpenGLTest/OpenGLTestDarwin.cpp + +# +# VBoxTestOGL - OpenGL support test app. +# Note! Doesn't link with VBOX_WITH_DEBUG_VCC_CRT defined because it uses Qt. +# +if ( defined(VBOX_WITH_QTGUI) \ + && (defined(VBOX_WITH_CROGL) || defined(VBOX_WITH_VIDEOHWACCEL)) \ + && !defined(VBOX_WITH_DEBUG_VCC_CRT)) + ifneq ($(KBUILD_TARGET),darwin) + ifdef VBOX_WITH_VIDEOHWACCEL + USES += qt5 + endif + PROGRAMS += VBoxTestOGL + VBoxTestOGL_TEMPLATE = $(if $(VBOX_WITH_VIDEOHWACCEL),$(if $(VBOX_WITH_HARDENING),VBOXQTGUI,VBOXQTGUIEXE),VBOXMAINEXE) + VBoxTestOGL_SOURCES = OpenGLTest/OpenGLTestApp.cpp + VBoxTestOGL_SOURCES.win = OpenGLTest/VBoxTestOGL.rc + VBoxTestOGL_LIBS = \ + $(if $(VBOX_WITH_CROGL), \ + $(PATH_STAGE_LIB)/VBoxOGLhostspuload$(VBOX_SUFF_LIB) \ + $(VBOX_LIB_OGL_HOSTCRUTIL),) \ + $(if $(VBOX_WITH_VIDEOHWACCEL), $(PATH_STAGE_LIB)/VBoxOGL2D$(VBOX_SUFF_LIB),) \ + $(LIB_RUNTIME) + VBoxTestOGL_DEFS += \ + VBOX_BUILD_TARGET=\"$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)\" \ + $(if $(VBOX_WITH_CROGL), VBOX_WITH_CROGL,) \ + $(if $(VBOX_WITH_VIDEOHWACCEL), VBOX_WITH_VIDEOHWACCEL,) + ifdef VBOX_WITH_VIDEOHWACCEL + VBoxTestOGL_QT_MODULES += Core Gui OpenGL Widgets + VBoxTestOGL_LIBS.linux += xcb + VBoxTestOGL_LIBS.solaris += xcb + VBoxTestOGL_LIBS.freebsd += xcb + VBoxTestOGL_LDFLAGS.darwin += -framework OpenGL + VBoxTestOGL_LIBS.win += $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/Opengl32.lib + if1of ($(KBUILD_TARGET), solaris linux freebsd) + # must come after VBoxOGL2D, therefore don't set the arch-specific LIBS variable here! + VBoxTestOGL_LIBS += GL + endif + endif + # Don't let ld strip out explicitly linked libraries even when they are not needed. + # This was causing some dynamic library loading problems in case of indirect dependencies + # in systems where RUNPATH instead of RPATH is utilized. + VBoxTestOGL_LDFLAGS.linux = -Wl,--no-as-needed + VBoxTestOGL_LDFLAGS.win = /SUBSYSTEM:windows + endif +endif + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp new file mode 100644 index 00000000..379fbcdd --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTest.cpp @@ -0,0 +1,103 @@ +/* $Id: OpenGLTest.cpp $ */ +/** @file + * VBox host opengl support test - generic implementation. + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +#include <VBox/err.h> +#include <iprt/assert.h> +#include <iprt/env.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/process.h> +#include <iprt/string.h> +#include <iprt/time.h> +#include <iprt/thread.h> +#include <iprt/env.h> +#include <iprt/log.h> + +#include <VBox/VBoxOGL.h> + +bool RTCALL VBoxOglIs3DAccelerationSupported(void) +{ + if (RTEnvExist("VBOX_CROGL_FORCE_SUPPORTED")) + { + LogRel(("VBOX_CROGL_FORCE_SUPPORTED is specified, skipping 3D test, and treating as supported\n")); + return true; + } + + static char pszVBoxPath[RTPATH_MAX]; + const char *papszArgs[4] = { NULL, "-test", "3D", NULL}; + int rc; + RTPROCESS Process; + RTPROCSTATUS ProcStatus; + uint64_t StartTS; + +#ifdef __SANITIZE_ADDRESS__ + /* The OpenGL test tool contains a number of memory leaks which cause it to + * return failure when run with ASAN unless we disable the leak detector. */ + RTENV env; + if (RT_FAILURE(RTEnvClone(&env, RTENV_DEFAULT))) + return false; + RTEnvPutEx(env, "ASAN_OPTIONS=detect_leaks=0"); /* If this fails we will notice later */ +#endif + rc = RTPathExecDir(pszVBoxPath, RTPATH_MAX); AssertRCReturn(rc, false); +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + rc = RTPathAppend(pszVBoxPath, RTPATH_MAX, "VBoxTestOGL.exe"); +#else + rc = RTPathAppend(pszVBoxPath, RTPATH_MAX, "VBoxTestOGL"); +#endif + papszArgs[0] = pszVBoxPath; /* argv[0] */ + AssertRCReturn(rc, false); + +#ifndef __SANITIZE_ADDRESS__ + rc = RTProcCreate(pszVBoxPath, papszArgs, RTENV_DEFAULT, 0, &Process); +#else + rc = RTProcCreate(pszVBoxPath, papszArgs, env, 0, &Process); + RTEnvDestroy(env); +#endif + if (RT_FAILURE(rc)) + return false; + + StartTS = RTTimeMilliTS(); + + while (1) + { + rc = RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus); + if (rc != VERR_PROCESS_RUNNING) + break; + +#ifndef DEBUG_misha + if (RTTimeMilliTS() - StartTS > 30*1000 /* 30 sec */) + { + RTProcTerminate(Process); + RTThreadSleep(100); + RTProcWait(Process, RTPROCWAIT_FLAGS_NOBLOCK, &ProcStatus); + return false; + } +#endif + RTThreadSleep(100); + } + + if (RT_SUCCESS(rc)) + { + if ((ProcStatus.enmReason==RTPROCEXITREASON_NORMAL) && (ProcStatus.iStatus==0)) + { + return true; + } + } + + return false; +} + diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp new file mode 100644 index 00000000..dca5daf6 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestApp.cpp @@ -0,0 +1,363 @@ +/* $Id: OpenGLTestApp.cpp $ */ +/** @file + * VBox host opengl support test application. + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +#include <iprt/assert.h> +#include <iprt/buildconfig.h> +#include <iprt/errcore.h> +#include <iprt/getopt.h> +#include <iprt/initterm.h> +#include <iprt/stream.h> +#ifdef RT_OS_WINDOWS +# include <iprt/win/windows.h> +#endif +#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) +# include <sys/resource.h> +# include <fcntl.h> +# include <unistd.h> +#endif + +#include <string.h> + +#define VBOXGLTEST_WITH_LOGGING + +#ifdef VBOXGLTEST_WITH_LOGGING +#include "package-generated.h" + +#include <iprt/log.h> +#include <iprt/param.h> +#include <iprt/time.h> +#include <iprt/system.h> +#include <iprt/process.h> +#include <iprt/env.h> + +#include <VBox/log.h> +#include <VBox/version.h> +#endif + +#ifdef VBOX_WITH_CROGL + +extern "C" +{ + extern void * crSPULoad(void *, int, char *, char *, void *); + extern void crSPUUnloadChain(void *); +} + + +static int vboxCheck3DAccelerationSupported() +{ + LogRel(("Testing 3D Support:\n")); + void *spu = crSPULoad(NULL, 0, (char*)"render", NULL, NULL); + if (spu) + { + crSPUUnloadChain(spu); + LogRel(("Testing 3D Succeeded!\n")); + return 0; + } + LogRel(("Testing 3D Failed\n")); + return 1; +} +#endif + +#ifdef VBOX_WITH_VIDEOHWACCEL +#include <QGLWidget> +#include <QApplication> +#include <VBox/VBoxGL2D.h> + +static int vboxCheck2DVideoAccelerationSupported() +{ + LogRel(("Testing 2D Support:\n")); + static int dummyArgc = 1; + static char * dummyArgv = (char*)"GlTest"; + QApplication app (dummyArgc, &dummyArgv); + + VBoxGLTmpContext ctx; + const QGLContext *pContext = ctx.makeCurrent(); + if(pContext) + { + VBoxVHWAInfo supportInfo; + supportInfo.init(pContext); + if(supportInfo.isVHWASupported()) + { + LogRel(("Testing 2D Succeeded!\n")); + return 0; + } + } + else + { + LogRel(("Failed to create gl context\n")); + } + LogRel(("Testing 2D Failed\n")); + return 1; +} +#endif + +#ifdef VBOXGLTEST_WITH_LOGGING +static int vboxInitLogging(const char *pszFilename, bool bGenNameSuffix) +{ + PRTLOGGER loggerRelease; + static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG; +#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) + fFlags |= RTLOGFLAGS_USECRLF; +#endif + const char * pszFilenameFmt; + RTLOGDEST enmLogDest; + if(pszFilename) + { + if(bGenNameSuffix) + pszFilenameFmt = "%s.%ld.log"; + else + pszFilenameFmt = "%s"; + enmLogDest = RTLOGDEST_FILE; + } + else + { + pszFilenameFmt = NULL; + enmLogDest = RTLOGDEST_STDOUT; + } + + int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all", + "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, enmLogDest, + NULL /* pfnBeginEnd */, 0 /* cHistory */, 0 /* cbHistoryFileMax */, 0 /* uHistoryTimeMax */, + NULL /* pErrInfo */, pszFilenameFmt, pszFilename, RTTimeMilliTS()); + if (RT_SUCCESS(vrc)) + { + /* some introductory information */ + RTTIMESPEC timeSpec; + char szTmp[256]; + RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp)); + RTLogRelLogger(loggerRelease, 0, ~0U, + "VBoxTestGL %s r%u %s (%s %s) release log\n" +#ifdef VBOX_BLEEDING_EDGE + "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n" +#endif + "Log opened %s\n", + VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET, + __DATE__, __TIME__, szTmp); + + vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp)); + if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) + RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp); + vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp)); + if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) + RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp); + vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp)); + if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) + RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp); + vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp)); + if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) + RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp); +// RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n", +// uHostRamMb, uHostRamAvailMb); + /* the package type is interesting for Linux distributions */ + char szExecName[RTPATH_MAX]; + char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName)); + RTLogRelLogger(loggerRelease, 0, ~0U, + "Executable: %s\n" + "Process ID: %u\n" + "Package type: %s" +#ifdef VBOX_OSE + " (OSE)" +#endif + "\n", + pszExecName ? pszExecName : "unknown", + RTProcSelf(), + VBOX_PACKAGE_STRING); + + /* register this logger as the release logger */ + RTLogRelSetDefaultInstance(loggerRelease); + + return VINF_SUCCESS; + } + + return vrc; +} +#endif + +static int vboxInitQuietMode() +{ +#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2) + /* This small test application might crash on some hosts. Do never + * generate a core dump as most likely some OpenGL library is + * responsible. */ + struct rlimit lim = { 0, 0 }; + setrlimit(RLIMIT_CORE, &lim); + + /* Redirect stderr to /dev/null */ + int fd = open("/dev/null", O_WRONLY); + if (fd != -1) + dup2(fd, STDERR_FILENO); +#endif + return 0; +} + +int main(int argc, char **argv) +{ + int rc = 0; + + RTR3InitExe(argc, &argv, 0); + + if(argc < 2) + { +#ifdef VBOX_WITH_CROGL + /* backwards compatibility: check 3D */ + rc = vboxCheck3DAccelerationSupported(); +#endif + } + else + { + static const RTGETOPTDEF s_aOptionDefs[] = + { + { "--test", 't', RTGETOPT_REQ_STRING }, + { "-test", 't', RTGETOPT_REQ_STRING }, +#ifdef VBOXGLTEST_WITH_LOGGING + { "--log", 'l', RTGETOPT_REQ_STRING }, +#endif + }; + + RTGETOPTSTATE State; + rc = RTGetOptInit(&State, argc-1, argv+1, &s_aOptionDefs[0], RT_ELEMENTS(s_aOptionDefs), 0, 0); + AssertRCReturn(rc, 49); + +#ifdef VBOX_WITH_VIDEOHWACCEL + bool bTest2D = false; +#endif +#ifdef VBOX_WITH_CROGL + bool bTest3D = false; +#endif +#ifdef VBOXGLTEST_WITH_LOGGING + bool bLog = false; + bool bLogSuffix = false; + const char * pLog = NULL; +#endif + + for (;;) + { + RTGETOPTUNION Val; + rc = RTGetOpt(&State, &Val); + if (!rc) + break; + switch (rc) + { + case 't': +#ifdef VBOX_WITH_CROGL + if (!strcmp(Val.psz, "3D") || !strcmp(Val.psz, "3d")) + { + bTest3D = true; + rc = 0; + break; + } +#endif +#ifdef VBOX_WITH_VIDEOHWACCEL + if (!strcmp(Val.psz, "2D") || !strcmp(Val.psz, "2d")) + { + bTest2D = true; + rc = 0; + break; + } +#endif + rc = 1; + break; +#ifdef VBOXGLTEST_WITH_LOGGING + case 'l': + bLog = true; + pLog = Val.psz; + rc = 0; + break; +#endif + case 'h': + RTPrintf(VBOX_PRODUCT " Helper for testing 2D/3D OpenGL capabilities %u.%u.%u\n" + "(C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n" + "All rights reserved.\n" + "\n" + "Parameters:\n" +#ifdef VBOX_WITH_VIDEOHWACCEL + " --test 2D test for 2D (video) OpenGL capabilities\n" +#endif +#ifdef VBOX_WITH_CROGL + " --test 3D test for 3D OpenGL capabilities\n" +#endif +#ifdef VBOXGLTEST_WITH_LOGGING + " --log <log_file_name> log the GL test result to the given file\n" + "\n" + "Logging can alternatively be enabled by specifying the VBOXGLTEST_LOG=<log_file_name> env variable\n" + +#endif + "\n", + RTBldCfgVersionMajor(), RTBldCfgVersionMinor(), RTBldCfgVersionBuild()); + break; + + case 'V': + RTPrintf("$Revision: 127855 $\n"); + return 0; + + case VERR_GETOPT_UNKNOWN_OPTION: + case VINF_GETOPT_NOT_OPTION: + rc = 1; + + default: + /* complain? RTGetOptPrintError(rc, &Val); */ + break; + } + + if (rc) + break; + } + + if(!rc) + { +#ifdef VBOXGLTEST_WITH_LOGGING + if(!bLog) + { + /* check the VBOXGLTEST_LOG env var */ + pLog = RTEnvGet("VBOXGLTEST_LOG"); + if(pLog) + bLog = true; + bLogSuffix = true; + } + if(bLog) + rc = vboxInitLogging(pLog, bLogSuffix); + else +#endif + rc = vboxInitQuietMode(); + +#ifdef VBOX_WITH_CROGL + if(!rc && bTest3D) + rc = vboxCheck3DAccelerationSupported(); +#endif + +#ifdef VBOX_WITH_VIDEOHWACCEL + if(!rc && bTest2D) + rc = vboxCheck2DVideoAccelerationSupported(); +#endif + + } + } + + /*RTR3Term();*/ + return rc; + +} + +#ifdef RT_OS_WINDOWS +extern "C" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nShowCmd); + return main(__argc, __argv); +} +#endif + diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp new file mode 100644 index 00000000..35b573c2 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/OpenGLTestDarwin.cpp @@ -0,0 +1,176 @@ +/* $Id: OpenGLTestDarwin.cpp $ */ +/** @file + * VBox host opengl support test + */ + +/* + * Copyright (C) 2009-2019 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 * +*********************************************************************************************************************************/ +#include <VBox/VBoxOGL.h> + +#include <IOKit/IOKitLib.h> +#include <OpenGL/OpenGL.h> +#include <ApplicationServices/ApplicationServices.h> +#include <OpenGL/gl.h> +#ifdef VBOX_WITH_COCOA_QT +# include <OpenGL/glu.h> +#endif + +#include <iprt/env.h> +#include <iprt/log.h> +#include <iprt/once.h> + + + +/** + * @callback_method_impl{FNRTONCE, + * For determining the cached VBoxOglIsOfflineRenderingAppropriate result.} + */ +static DECLCALLBACK(int32_t) vboxOglIsOfflineRenderingAppropriateOnce(void *pvUser) +{ + bool *pfAppropriate = (bool *)pvUser; + + /* It is assumed that it is makes sense to enable offline rendering + only in case if host has more than one GPU installed. This routine + counts all the PCI devices in IORegistry which have IOName property + set to "display". If the number of such devices is greater than one, + it sets pfAppropriate to TRUE, otherwise to FALSE. */ + + CFStringRef apKeyStrings[] = { CFSTR(kIOProviderClassKey), CFSTR(kIONameMatchKey) }; + CFStringRef apValueStrings[] = { CFSTR("IOPCIDevice"), CFSTR("display") }; + Assert(RT_ELEMENTS(apKeyStrings) == RT_ELEMENTS(apValueStrings)); + + CFDictionaryRef pMatchingDictionary = CFDictionaryCreate(kCFAllocatorDefault, + (const void **)apKeyStrings, + (const void **)apValueStrings, + RT_ELEMENTS(apKeyStrings), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (pMatchingDictionary) + { + /* The reference to pMatchingDictionary is consumed by the function below => no IORelease(pMatchingDictionary)! */ + io_iterator_t matchingServices; + kern_return_t krc = IOServiceGetMatchingServices(kIOMasterPortDefault, pMatchingDictionary, &matchingServices); + if (krc == kIOReturnSuccess) + { + io_object_t matchingService; + int cMatchingServices = 0; + + while ((matchingService = IOIteratorNext(matchingServices)) != 0) + { + cMatchingServices++; + IOObjectRelease(matchingService); + } + + *pfAppropriate = cMatchingServices > 1; + + IOObjectRelease(matchingServices); + } + } + + LogRel(("OpenGL: Offline rendering support is %s (pid=%d)\n", *pfAppropriate ? "ON" : "OFF", (int)getpid())); + return VINF_SUCCESS; +} + + +bool RTCALL VBoxOglIsOfflineRenderingAppropriate(void) +{ + /* In order to do not slowdown 3D engine which can ask about offline rendering several times, + let's cache the result and assume that renderers amount value is constant. Use the IPRT + execute once construct to make sure there aren't any threading issues. */ + static RTONCE s_Once = RTONCE_INITIALIZER; + static bool s_fCached = false; + int rc = RTOnce(&s_Once, vboxOglIsOfflineRenderingAppropriateOnce, &s_fCached); + AssertRC(rc); + return s_fCached; +} + + +bool RTCALL VBoxOglIs3DAccelerationSupported(void) +{ + if (RTEnvExist("VBOX_CROGL_FORCE_SUPPORTED")) + { + LogRel(("VBOX_CROGL_FORCE_SUPPORTED is specified, skipping 3D test, and treating as supported\n")); + return true; + } + + CGOpenGLDisplayMask cglDisplayMask = CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()); + CGLPixelFormatAttribute aAttribs[] = + { + kCGLPFADisplayMask, + (CGLPixelFormatAttribute)cglDisplayMask, + kCGLPFAAccelerated, + kCGLPFADoubleBuffer, + VBoxOglIsOfflineRenderingAppropriate() ? kCGLPFAAllowOfflineRenderers : (CGLPixelFormatAttribute)NULL, + (CGLPixelFormatAttribute)NULL + }; + CGLPixelFormatObj pPixelFormat = NULL; + GLint cPixelFormatsIgnored = 0; + CGLError rcCgl = CGLChoosePixelFormat(aAttribs, &pPixelFormat, &cPixelFormatsIgnored); + if (rcCgl != kCGLNoError) + { + LogRel(("OpenGL Info: 3D test unable to choose pixel format (rcCgl=0x%X)\n", rcCgl)); + return false; + } + + if (pPixelFormat) + { + CGLContextObj pCglContext = 0; + rcCgl = CGLCreateContext(pPixelFormat, NULL, &pCglContext); + CGLDestroyPixelFormat(pPixelFormat); + + if (rcCgl != kCGLNoError) + { + LogRel(("OpenGL Info: 3D test unable to create context (rcCgl=0x%X)\n", rcCgl)); + return false; + } + + if (pCglContext) + { + GLboolean isSupported = GL_TRUE; + +#ifdef VBOX_WITH_COCOA_QT + /* + * In the Cocoa port we depend on the GL_EXT_framebuffer_object & + * the GL_EXT_texture_rectangle extension. If they are not + * available, disable 3D support. + */ + CGLSetCurrentContext(pCglContext); + const GLubyte *pszExts = glGetString(GL_EXTENSIONS); + isSupported = gluCheckExtension((const GLubyte *)"GL_EXT_framebuffer_object", pszExts); + if (isSupported) + { + isSupported = gluCheckExtension((const GLubyte *)"GL_EXT_texture_rectangle", pszExts); + if (!isSupported) + LogRel(("OpenGL Info: 3D test found that GL_EXT_texture_rectangle extension not supported.\n")); + } + else + LogRel(("OpenGL Info: 3D test found that GL_EXT_framebuffer_object extension not supported.\n")); +#endif /* VBOX_WITH_COCOA_QT */ + + CGLDestroyContext(pCglContext); + LogRel(("OpenGL Info: 3D test %spassed\n", isSupported == GL_TRUE ? "" : "not ")); + return isSupported == GL_TRUE; + } + + LogRel(("OpenGL Info: 3D test unable to create context (internal error).\n")); + } + else + LogRel(("OpenGL Info: 3D test unable to choose pixel format (internal error).\n")); + + return false; +} + diff --git a/src/VBox/HostServices/SharedOpenGL/OpenGLTest/VBoxTestOGL.rc b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/VBoxTestOGL.rc new file mode 100644 index 00000000..a6504424 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/OpenGLTest/VBoxTestOGL.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxTestOGL.rc $ */ +/** @file + * VBoxTestOGL - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox OpenGL Test Tool\0" + VALUE "InternalName", "VBoxTestOGL\0" + VALUE "OriginalFilename", "VBoxTestOGL.exe\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/SharedOpenGL/crserver/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/crserver/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserver/Makefile.kup diff --git a/src/VBox/HostServices/SharedOpenGL/crserver/VBoxSharedCrOpenGL.rc b/src/VBox/HostServices/SharedOpenGL/crserver/VBoxSharedCrOpenGL.rc new file mode 100644 index 00000000..bf24ea32 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserver/VBoxSharedCrOpenGL.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxSharedCrOpenGL.rc $ */ +/** @file + * VBoxSharedCrOpenGL - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox crOpenGL Host Service\0" + VALUE "InternalName", "VBoxSharedCrOpenGL\0" + VALUE "OriginalFilename", "VBoxSharedCrOpenGL.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp b/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp new file mode 100644 index 00000000..770b5847 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserver/crservice.cpp @@ -0,0 +1,1603 @@ +/* $Id: crservice.cpp $ */ +/** @file + * VBox crOpenGL - Host service entry points. + */ + +/* + * Copyright (C) 2006-2019 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_SHARED_CROPENGL + +#define __STDC_CONSTANT_MACROS /* needed for a definition in iprt/string.h */ + +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/critsect.h> +#include <iprt/mem.h> +#include <iprt/semaphore.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/thread.h> + +#include <VBox/err.h> +#include <VBox/hgcmsvc.h> +#include <VBox/log.h> +#include <VBox/com/array.h> +#include <VBox/com/ErrorInfo.h> +#include <VBox/com/VirtualBox.h> +#include <VBox/com/errorprint.h> +#include <VBox/HostServices/VBoxCrOpenGLSvc.h> +#include <VBox/vmm/ssm.h> +#include <VBox/VBoxOGL.h> + +#include "cr_mem.h" +#include "cr_server.h" + +#ifndef RT_OS_WINDOWS +# define DWORD int +# define WINAPI +#endif + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +PVBOXHGCMSVCHELPERS g_pHelpers; +static IConsole* g_pConsole = NULL; +static uint32_t g_u32ScreenCount = 0; +static PVM g_pVM = NULL; +static uint32_t g_u32fCrHgcmDisabled = 0; + +static const char *gszVBoxOGLSSMMagic = "***OpenGL state data***"; + +/* Used to process guest calls exceeding maximum allowed HGCM call size in a sequence of smaller calls */ +typedef struct _CRVBOXSVCBUFFER_t { + uint32_t uiId; + uint32_t uiSize; + void* pData; + _CRVBOXSVCBUFFER_t *pNext, *pPrev; +} CRVBOXSVCBUFFER_t; + +static CRVBOXSVCBUFFER_t *g_pCRVBoxSVCBuffers = NULL; +static uint32_t g_CRVBoxSVCBufferID = 0; + +/* svcPresentFBO related data */ +typedef struct _CRVBOXSVCPRESENTFBOCMD_t { + void *pData; + int32_t screenId, x, y, w, h; + _CRVBOXSVCPRESENTFBOCMD_t *pNext; +} CRVBOXSVCPRESENTFBOCMD_t, *PCRVBOXSVCPRESENTFBOCMD_t; + + +static DECLCALLBACK(void) svcNotifyEventCB(int32_t screenId, uint32_t uEvent, void* pvData, uint32_t cbData) +{ + ComPtr<IDisplay> pDisplay; + ComPtr<IFramebuffer> pFramebuffer; + + if (!g_pConsole) + { + crWarning("Console not defined!"); + return; + } + + CHECK_ERROR2I_STMT(g_pConsole, COMGETTER(Display)(pDisplay.asOutParam()), return); + + CHECK_ERROR2I_STMT(pDisplay, QueryFramebuffer(screenId, pFramebuffer.asOutParam()), return); + + if (!pFramebuffer) + return; + + com::SafeArray<BYTE> data(cbData); + if (cbData) + memcpy(data.raw(), pvData, cbData); + + pFramebuffer->Notify3DEvent(uEvent, ComSafeArrayAsInParam(data)); +} + + +static DECLCALLBACK(int) svcUnload (void *) +{ + int rc = VINF_SUCCESS; + + Log(("SHARED_CROPENGL svcUnload\n")); + + crVBoxServerTearDown(); + + return rc; +} + +static DECLCALLBACK(int) svcConnect (void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring) +{ + RT_NOREF(pvClient, fRequestor, fRestoring); + + if (g_u32fCrHgcmDisabled) + { + WARN(("connect not expected")); + return VERR_INVALID_STATE; + } + + Log(("SHARED_CROPENGL svcConnect: u32ClientID = %d\n", u32ClientID)); + + int rc = crVBoxServerAddClient(u32ClientID); + + return rc; +} + +static DECLCALLBACK(int) svcDisconnect (void *, uint32_t u32ClientID, void *pvClient) +{ + int rc = VINF_SUCCESS; + + NOREF(pvClient); + + if (g_u32fCrHgcmDisabled) + { + WARN(("disconnect not expected")); + return VINF_SUCCESS; + } + + Log(("SHARED_CROPENGL svcDisconnect: u32ClientID = %d\n", u32ClientID)); + + crVBoxServerRemoveClient(u32ClientID); + + return rc; +} + +static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM) +{ + int rc = VINF_SUCCESS; + + NOREF(pvClient); + + Log(("SHARED_CROPENGL svcSaveState: u32ClientID = %d\n", u32ClientID)); + + /* Start*/ + rc = SSMR3PutStrZ(pSSM, gszVBoxOGLSSMMagic); + AssertRCReturn(rc, rc); + + /* Version */ + rc = SSMR3PutU32(pSSM, (uint32_t) SHCROGL_SSM_VERSION); + AssertRCReturn(rc, rc); + + /* The state itself */ + rc = crVBoxServerSaveState(pSSM); + AssertRCReturn(rc, rc); + + /* Save svc buffers info */ + { + CRVBOXSVCBUFFER_t *pBuffer = g_pCRVBoxSVCBuffers; + + rc = SSMR3PutU32(pSSM, g_CRVBoxSVCBufferID); + AssertRCReturn(rc, rc); + + while (pBuffer) + { + rc = SSMR3PutU32(pSSM, pBuffer->uiId); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, pBuffer->uiSize); + AssertRCReturn(rc, rc); + + rc = SSMR3PutMem(pSSM, pBuffer->pData, pBuffer->uiSize); + AssertRCReturn(rc, rc); + + pBuffer = pBuffer->pNext; + } + + rc = SSMR3PutU32(pSSM, 0); + AssertRCReturn(rc, rc); + } + + /* End */ + rc = SSMR3PutStrZ(pSSM, gszVBoxOGLSSMMagic); + AssertRCReturn(rc, rc); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion) +{ + RT_NOREF(pvClient, uVersion); + int rc = VINF_SUCCESS; + + Log(("SHARED_CROPENGL svcLoadState: u32ClientID = %d\n", u32ClientID)); + + char psz[2000]; + uint32_t ui32; + + /* Start of data */ + rc = SSMR3GetStrZEx(pSSM, psz, 2000, NULL); + AssertRCReturn(rc, rc); + if (strcmp(gszVBoxOGLSSMMagic, psz)) + return VERR_SSM_UNEXPECTED_DATA; + + /* Version */ + rc = SSMR3GetU32(pSSM, &ui32); + AssertRCReturn(rc, rc); + + /* The state itself */ + rc = crVBoxServerLoadState(pSSM, ui32); + + if (rc==VERR_SSM_DATA_UNIT_FORMAT_CHANGED && ui32!=SHCROGL_SSM_VERSION) + { + LogRel(("OpenGL: svcLoadState: Unsupported save state version %d\n", ui32)); + + /** @todo ugly hack, as we don't know size of stored opengl data try to read untill end of opengl data marker*/ + /*VBoxSharedCrOpenGL isn't last hgcm service now, so can't use SSMR3SkipToEndOfUnit*/ + { + const char *pMatch = &gszVBoxOGLSSMMagic[0]; + char current; + + while (*pMatch) + { + rc = SSMR3GetS8(pSSM, (int8_t*)¤t); + AssertRCReturn(rc, rc); + + if (current==*pMatch) + { + pMatch++; + } + else + { + pMatch = &gszVBoxOGLSSMMagic[0]; + } + } + } + + return VINF_SUCCESS; + } + AssertRCReturn(rc, rc); + + /* Load svc buffers info */ + if (ui32>=24) + { + uint32_t uiId; + + rc = SSMR3GetU32(pSSM, &g_CRVBoxSVCBufferID); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &uiId); + AssertRCReturn(rc, rc); + + while (uiId) + { + CRVBOXSVCBUFFER_t *pBuffer = (CRVBOXSVCBUFFER_t *) RTMemAlloc(sizeof(CRVBOXSVCBUFFER_t)); + if (!pBuffer) + { + return VERR_NO_MEMORY; + } + pBuffer->uiId = uiId; + + rc = SSMR3GetU32(pSSM, &pBuffer->uiSize); + AssertRCReturn(rc, rc); + + pBuffer->pData = RTMemAlloc(pBuffer->uiSize); + if (!pBuffer->pData) + { + RTMemFree(pBuffer); + return VERR_NO_MEMORY; + } + + rc = SSMR3GetMem(pSSM, pBuffer->pData, pBuffer->uiSize); + AssertRCReturn(rc, rc); + + pBuffer->pNext = g_pCRVBoxSVCBuffers; + pBuffer->pPrev = NULL; + if (g_pCRVBoxSVCBuffers) + { + g_pCRVBoxSVCBuffers->pPrev = pBuffer; + } + g_pCRVBoxSVCBuffers = pBuffer; + + rc = SSMR3GetU32(pSSM, &uiId); + AssertRCReturn(rc, rc); + } + } + + /* End of data */ + rc = SSMR3GetStrZEx(pSSM, psz, 2000, NULL); + AssertRCReturn(rc, rc); + if (strcmp(gszVBoxOGLSSMMagic, psz)) + return VERR_SSM_UNEXPECTED_DATA; + + return VINF_SUCCESS; +} + +static void svcClientVersionUnsupported(uint32_t minor, uint32_t major) +{ + LogRel(("OpenGL: Unsupported client version %d.%d\n", minor, major)); + + /*MS's opengl32 tries to load our ICD around 30 times on failure...this is to prevent unnecessary spam*/ + static int shown = 0; + + if (g_pVM && !shown) + { + VMSetRuntimeError(g_pVM, VMSETRTERR_FLAGS_NO_WAIT, "3DSupportIncompatibleAdditions", + "An attempt by the virtual machine to use hardware 3D acceleration failed. " + "The version of the Guest Additions installed in the virtual machine does not match the " + "version of VirtualBox on the host. Please install appropriate Guest Additions to fix this issue"); + shown = 1; + } +} + +static CRVBOXSVCBUFFER_t* svcGetBuffer(uint32_t iBuffer, uint32_t cbBufferSize) +{ + CRVBOXSVCBUFFER_t* pBuffer; + + if (iBuffer) + { + pBuffer = g_pCRVBoxSVCBuffers; + while (pBuffer) + { + if (pBuffer->uiId == iBuffer) + { + if (cbBufferSize && pBuffer->uiSize!=cbBufferSize) + { + static int shown=0; + + if (shown<20) + { + shown++; + LogRel(("OpenGL: svcGetBuffer: Invalid buffer(%i) size %i instead of %i\n", + iBuffer, pBuffer->uiSize, cbBufferSize)); + } + return NULL; + } + return pBuffer; + } + pBuffer = pBuffer->pNext; + } + return NULL; + } + else /*allocate new buffer*/ + { + pBuffer = (CRVBOXSVCBUFFER_t*) RTMemAlloc(sizeof(CRVBOXSVCBUFFER_t)); + if (pBuffer) + { + /* Filling host buffer with zeroes to prevent possible host->guest memory disclosure */ + pBuffer->pData = RTMemAllocZ(cbBufferSize); + if (!pBuffer->pData) + { + LogRel(("OpenGL: svcGetBuffer: Not enough memory (%d)\n", cbBufferSize)); + RTMemFree(pBuffer); + return NULL; + } + pBuffer->uiId = ++g_CRVBoxSVCBufferID; + if (!pBuffer->uiId) + { + pBuffer->uiId = ++g_CRVBoxSVCBufferID; + } + Assert(pBuffer->uiId); + pBuffer->uiSize = cbBufferSize; + pBuffer->pPrev = NULL; + pBuffer->pNext = g_pCRVBoxSVCBuffers; + if (g_pCRVBoxSVCBuffers) + { + g_pCRVBoxSVCBuffers->pPrev = pBuffer; + } + g_pCRVBoxSVCBuffers = pBuffer; + } + else + { + LogRel(("OpenGL: svcGetBuffer: Not enough memory (%d)\n", sizeof(CRVBOXSVCBUFFER_t))); + } + return pBuffer; + } +} + +static void svcFreeBuffer(CRVBOXSVCBUFFER_t* pBuffer) +{ + Assert(pBuffer); + + if (pBuffer->pPrev) + { + pBuffer->pPrev->pNext = pBuffer->pNext; + } + else + { + Assert(pBuffer==g_pCRVBoxSVCBuffers); + g_pCRVBoxSVCBuffers = pBuffer->pNext; + } + + if (pBuffer->pNext) + { + pBuffer->pNext->pPrev = pBuffer->pPrev; + } + + RTMemFree(pBuffer->pData); + RTMemFree(pBuffer); +} + +static DECLCALLBACK(void) svcCall (void *, VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, + uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[], uint64_t tsArrival) +{ + RT_NOREF(pvClient, tsArrival); + int rc = VINF_SUCCESS; + + if (g_u32fCrHgcmDisabled) + { + WARN(("cr hgcm disabled!")); + return; + } + + Log(("SHARED_CROPENGL svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n", u32ClientID, u32Function, cParms, paParms)); + +#ifdef DEBUG + uint32_t i; + + for (i = 0; i < cParms; i++) + { + /** @todo parameters other than 32 bit */ + Log((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32)); + } +#endif + + switch (u32Function) + { + case SHCRGL_GUEST_FN_WRITE: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_WRITE) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pBuffer */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + uint8_t *pBuffer = (uint8_t *)paParms[0].u.pointer.addr; + uint32_t cbBuffer = paParms[0].u.pointer.size; + + /* Execute the function. */ + rc = crVBoxServerClientWrite(u32ClientID, pBuffer, cbBuffer); + if (!RT_SUCCESS(rc)) + { + Assert(VERR_NOT_SUPPORTED==rc); + svcClientVersionUnsupported(0, 0); + } + + } + break; + } + + case SHCRGL_GUEST_FN_INJECT: + { + Log(("svcCall: SHCRGL_GUEST_FN_INJECT\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_INJECT) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* u32ClientID */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* pBuffer */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + uint32_t u32InjectClientID = paParms[0].u.uint32; + uint8_t *pBuffer = (uint8_t *)paParms[1].u.pointer.addr; + uint32_t cbBuffer = paParms[1].u.pointer.size; + + /* Execute the function. */ + rc = crVBoxServerClientWrite(u32InjectClientID, pBuffer, cbBuffer); + if (!RT_SUCCESS(rc)) + { + if (VERR_NOT_SUPPORTED==rc) + { + svcClientVersionUnsupported(0, 0); + } + else + { + crWarning("SHCRGL_GUEST_FN_INJECT failed to inject for %i from %i", u32InjectClientID, u32ClientID); + } + } + } + break; + } + + case SHCRGL_GUEST_FN_READ: + { + Log(("svcCall: SHCRGL_GUEST_FN_READ\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_READ) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pBuffer */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* cbBuffer */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + + /* Fetch parameters. */ + uint8_t *pBuffer = (uint8_t *)paParms[0].u.pointer.addr; + uint32_t cbBuffer = paParms[0].u.pointer.size; + + /* Execute the function. */ + rc = crVBoxServerClientRead(u32ClientID, pBuffer, &cbBuffer); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + paParms[0].u.pointer.size = cbBuffer; /// @todo guest doesn't see this change somehow? + } else if (VERR_NOT_SUPPORTED==rc) + { + svcClientVersionUnsupported(0, 0); + } + + /* Return the required buffer size always */ + paParms[1].u.uint32 = cbBuffer; + + break; + } + + case SHCRGL_GUEST_FN_WRITE_READ: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_WRITE_READ) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pBuffer */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* pWriteback */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* cbWriteback */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + uint8_t *pBuffer = (uint8_t *)paParms[0].u.pointer.addr; + uint32_t cbBuffer = paParms[0].u.pointer.size; + + uint8_t *pWriteback = (uint8_t *)paParms[1].u.pointer.addr; + uint32_t cbWriteback = paParms[1].u.pointer.size; + + /* Execute the function. */ + rc = crVBoxServerClientWrite(u32ClientID, pBuffer, cbBuffer); + if (!RT_SUCCESS(rc)) + { + Assert(VERR_NOT_SUPPORTED==rc); + svcClientVersionUnsupported(0, 0); + } + + rc = crVBoxServerClientRead(u32ClientID, pWriteback, &cbWriteback); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + paParms[1].u.pointer.size = cbWriteback; + } + /* Return the required buffer size always */ + paParms[2].u.uint32 = cbWriteback; + } + + break; + } + + case SHCRGL_GUEST_FN_SET_VERSION: + { + Log(("svcCall: SHCRGL_GUEST_FN_SET_VERSION\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_SET_VERSION) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* vMajor */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* vMinor */ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + uint32_t vMajor = paParms[0].u.uint32; + uint32_t vMinor = paParms[1].u.uint32; + + /* Execute the function. */ + rc = crVBoxServerClientSetVersion(u32ClientID, vMajor, vMinor); + + if (!RT_SUCCESS(rc)) + { + svcClientVersionUnsupported(vMajor, vMinor); + } + } + + break; + } + + case SHCRGL_GUEST_FN_SET_PID: + { + Log(("svcCall: SHCRGL_GUEST_FN_SET_PID\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_SET_PID) + { + rc = VERR_INVALID_PARAMETER; + } + else + if (paParms[0].type != VBOX_HGCM_SVC_PARM_64BIT) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + uint64_t pid = paParms[0].u.uint64; + + /* Execute the function. */ + rc = crVBoxServerClientSetPID(u32ClientID, pid); + } + + break; + } + + case SHCRGL_GUEST_FN_WRITE_BUFFER: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE_BUFFER\n")); + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_WRITE_BUFFER) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /*iBufferID*/ + || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /*cbBufferSize*/ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /*ui32Offset*/ + || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /*pBuffer*/ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + uint32_t iBuffer = paParms[0].u.uint32; + uint32_t cbBufferSize = paParms[1].u.uint32; + uint32_t ui32Offset = paParms[2].u.uint32; + uint8_t *pBuffer = (uint8_t *)paParms[3].u.pointer.addr; + uint32_t cbBuffer = paParms[3].u.pointer.size; + + /* Execute the function. */ + CRVBOXSVCBUFFER_t *pSvcBuffer = svcGetBuffer(iBuffer, cbBufferSize); + if (!pSvcBuffer || ((uint64_t)ui32Offset+cbBuffer)>cbBufferSize) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + memcpy((void*)((uintptr_t)pSvcBuffer->pData+ui32Offset), pBuffer, cbBuffer); + + /* Return the buffer id */ + paParms[0].u.uint32 = pSvcBuffer->uiId; + } + } + + break; + } + + case SHCRGL_GUEST_FN_WRITE_READ_BUFFERED: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ_BUFFERED\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_WRITE_READ_BUFFERED) + { + rc = VERR_INVALID_PARAMETER; + } + else + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* iBufferID */ + || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* pWriteback */ + || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* cbWriteback */ + || !paParms[0].u.uint32 /*iBufferID can't be 0 here*/ + ) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + uint32_t iBuffer = paParms[0].u.uint32; + uint8_t *pWriteback = (uint8_t *)paParms[1].u.pointer.addr; + uint32_t cbWriteback = paParms[1].u.pointer.size; + + CRVBOXSVCBUFFER_t *pSvcBuffer = svcGetBuffer(iBuffer, 0); + if (!pSvcBuffer) + { + LogRel(("OpenGL: svcCall(WRITE_READ_BUFFERED): Invalid buffer (%d)\n", iBuffer)); + rc = VERR_INVALID_PARAMETER; + break; + } + + uint8_t *pBuffer = (uint8_t *)pSvcBuffer->pData; + uint32_t cbBuffer = pSvcBuffer->uiSize; + + /* Execute the function. */ + rc = crVBoxServerClientWrite(u32ClientID, pBuffer, cbBuffer); + if (!RT_SUCCESS(rc)) + { + Assert(VERR_NOT_SUPPORTED==rc); + svcClientVersionUnsupported(0, 0); + } + + rc = crVBoxServerClientRead(u32ClientID, pWriteback, &cbWriteback); + + if (RT_SUCCESS(rc)) + { + /* Update parameters.*/ + paParms[1].u.pointer.size = cbWriteback; + } + /* Return the required buffer size always */ + paParms[2].u.uint32 = cbWriteback; + + svcFreeBuffer(pSvcBuffer); + } + + break; + } + + case SHCRGL_GUEST_FN_GET_CAPS_NEW: + { + Log(("svcCall: SHCRGL_GUEST_FN_GET_CAPS_NEW\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_GET_CAPS_NEW) + { + WARN(("invalid parameter count")); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR) + { + WARN(("invalid parameter")); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms[0].u.pointer.size < sizeof (CR_CAPS_INFO)) + { + WARN(("invalid buffer size")); + rc = VERR_INVALID_PARAMETER; + break; + } + + CR_CAPS_INFO *pInfo = (CR_CAPS_INFO*)paParms[0].u.pointer.addr; + rc = crVBoxServerClientGetCapsNew(u32ClientID, pInfo); + AssertRC(rc); + + break; + } + + case SHCRGL_GUEST_FN_GET_CAPS_LEGACY: + { + Log(("svcCall: SHCRGL_GUEST_FN_GET_CAPS_LEGACY\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_GET_CAPS_LEGACY) + { + rc = VERR_INVALID_PARAMETER; + } + else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + rc = crVBoxServerClientGetCapsLegacy(u32ClientID, &paParms[0].u.uint32); + AssertRC(rc); + } + + break; + } + + default: + { + WARN(("svcCall: unexpected u32Function %d", u32Function)); + rc = VERR_NOT_IMPLEMENTED; + } + } + + + LogFlow(("svcCall: rc = %Rrc\n", rc)); + + g_pHelpers->pfnCallComplete (callHandle, rc); +} + +static void crScreenshotHandle(CRVBOXHGCMTAKESCREENSHOT *pScreenshot, uint32_t idScreen, uint64_t u64Now) +{ + if (!pScreenshot->pfnScreenshotBegin || pScreenshot->pfnScreenshotBegin(pScreenshot->pvContext, idScreen, u64Now)) + { + CR_SCREENSHOT Screenshot; + + int rc = crServerVBoxScreenshotGet(idScreen, pScreenshot->u32Width, pScreenshot->u32Height, pScreenshot->u32Pitch, pScreenshot->pvBuffer, &Screenshot); + if (RT_SUCCESS(rc)) + { + if (pScreenshot->pfnScreenshotPerform) + pScreenshot->pfnScreenshotPerform(pScreenshot->pvContext, idScreen, + 0, 0, 32, + Screenshot.Img.pitch, Screenshot.Img.width, Screenshot.Img.height, + (uint8_t*)Screenshot.Img.pvData, u64Now); + crServerVBoxScreenshotRelease(&Screenshot); + } + else + { + Assert(rc == VERR_INVALID_STATE); + } + + if (pScreenshot->pfnScreenshotEnd) + pScreenshot->pfnScreenshotEnd(pScreenshot->pvContext, idScreen, u64Now); + } +} + +/* + * We differentiate between a function handler for the guest and one for the host. + */ +static int svcHostCallPerform(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + int rc = VINF_SUCCESS; + + Log(("SHARED_CROPENGL svcHostCall: fn = %d, cParms = %d, pparms = %d\n", u32Function, cParms, paParms)); + +#ifdef DEBUG + uint32_t i; + + for (i = 0; i < cParms; i++) + { + /** @todo parameters other than 32 bit */ + Log((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32)); + } +#endif + + switch (u32Function) + { +#ifdef VBOX_WITH_CRHGSMI + case SHCRGL_HOST_FN_CRHGSMI_CMD: + { + Assert(cParms == 1 && paParms[0].type == VBOX_HGCM_SVC_PARM_PTR); + if (cParms == 1 && paParms[0].type == VBOX_HGCM_SVC_PARM_PTR) + { + rc = crVBoxServerCrHgsmiCmd((PVBOXVDMACMD_CHROMIUM_CMD)paParms[0].u.pointer.addr, paParms[0].u.pointer.size); + if (VERR_NOT_SUPPORTED == rc) + { + svcClientVersionUnsupported(0, 0); + } + } + else + rc = VERR_INVALID_PARAMETER; + } break; + case SHCRGL_HOST_FN_CRHGSMI_CTL: + { + Assert(cParms == 1 && paParms[0].type == VBOX_HGCM_SVC_PARM_PTR); + if (cParms == 1 && paParms[0].type == VBOX_HGCM_SVC_PARM_PTR) + rc = crVBoxServerCrHgsmiCtl((PVBOXVDMACMD_CHROMIUM_CTL)paParms[0].u.pointer.addr, paParms[0].u.pointer.size); + else + rc = VERR_INVALID_PARAMETER; + } break; +#endif + case SHCRGL_HOST_FN_SET_CONSOLE: + { + Log(("svcCall: SHCRGL_HOST_FN_SET_DISPLAY\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_SET_CONSOLE) + { + rc = VERR_INVALID_PARAMETER; + } + else if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + IConsole* pConsole = (IConsole*)paParms[0].u.pointer.addr; + uint32_t cbData = paParms[0].u.pointer.size; + + /* Verify parameters values. */ + if (cbData != sizeof (IConsole*)) + { + rc = VERR_INVALID_PARAMETER; + } + else if (!pConsole) + { + rc = VERR_INVALID_PARAMETER; + } + else /* Execute the function. */ + { + ComPtr<IMachine> pMachine; + ComPtr<IDisplay> pDisplay; + ComPtr<IFramebuffer> pFramebuffer; + LONG xo, yo; + LONG64 winId = 0; + ULONG monitorCount, i, w, h; + + CHECK_ERROR_BREAK(pConsole, COMGETTER(Machine)(pMachine.asOutParam())); + CHECK_ERROR_BREAK(pMachine, COMGETTER(MonitorCount)(&monitorCount)); + CHECK_ERROR_BREAK(pConsole, COMGETTER(Display)(pDisplay.asOutParam())); + + g_pConsole = pConsole; + g_u32ScreenCount = monitorCount; + + rc = crVBoxServerSetScreenCount(monitorCount); + AssertRCReturn(rc, rc); + +#if 1 + crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE); + + for (i=0; i<monitorCount; ++i) + { + CHECK_ERROR_RET(pDisplay, QueryFramebuffer(i, pFramebuffer.asOutParam()), rc); + + if (!pFramebuffer) + { + rc = crVBoxServerUnmapScreen(i); + AssertRCReturn(rc, rc); + } + else + { + CHECK_ERROR_RET(pFramebuffer, COMGETTER(WinId)(&winId), rc); + CHECK_ERROR_RET(pFramebuffer, COMGETTER(Width)(&w), rc); + CHECK_ERROR_RET(pFramebuffer, COMGETTER(Height)(&h), rc); + ULONG dummy; + GuestMonitorStatus_T monitorStatus; + CHECK_ERROR_RET(pDisplay, GetScreenResolution(i, &dummy, &dummy, &dummy, &xo, &yo, &monitorStatus), rc); + + rc = crVBoxServerMapScreen(i, xo, yo, w, h, winId); + AssertRCReturn(rc, rc); + } + } + + crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE); +#endif + + rc = VINF_SUCCESS; + } + } + break; + } + case SHCRGL_HOST_FN_SET_VM: + { + Log(("svcCall: SHCRGL_HOST_FN_SET_VM\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_SET_VM) + { + rc = VERR_INVALID_PARAMETER; + } + else if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + PVM pVM = (PVM)paParms[0].u.pointer.addr; + uint32_t cbData = paParms[0].u.pointer.size; + + /* Verify parameters values. */ + if (cbData != sizeof (PVM)) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Execute the function. */ + g_pVM = pVM; + rc = VINF_SUCCESS; + } + } + break; + } + case SHCRGL_HOST_FN_SET_VISIBLE_REGION: + { + Log(("svcCall: SHCRGL_HOST_FN_SET_VISIBLE_REGION\n")); + + if (cParms != SHCRGL_CPARMS_SET_VISIBLE_REGION) + { + rc = VERR_INVALID_PARAMETER; + break; + } + + if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* pRects */ + ) + { + rc = VERR_INVALID_PARAMETER; + break; + } + + Assert(sizeof (RTRECT) == 4 * sizeof (GLint)); + + rc = crVBoxServerSetRootVisibleRegion(paParms[0].u.pointer.size / sizeof (RTRECT), (const RTRECT*)paParms[0].u.pointer.addr); + break; + } + case SHCRGL_HOST_FN_SCREEN_CHANGED: + { + Log(("svcCall: SHCRGL_HOST_FN_SCREEN_CHANGED\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_SCREEN_CHANGED) + { + rc = VERR_INVALID_PARAMETER; + } + else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + uint32_t screenId = paParms[0].u.uint32; + + /* Execute the function. */ + ComPtr<IDisplay> pDisplay; + ComPtr<IFramebuffer> pFramebuffer; + LONG xo, yo; + LONG64 winId = 0; + ULONG w, h; + + Assert(g_pConsole); + CHECK_ERROR_RET(g_pConsole, COMGETTER(Display)(pDisplay.asOutParam()), rc); + CHECK_ERROR_RET(pDisplay, QueryFramebuffer(screenId, pFramebuffer.asOutParam()), rc); + + crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE); + + if (!pFramebuffer) + { + rc = crVBoxServerUnmapScreen(screenId); + AssertRCReturn(rc, rc); + } + else + { + do { + /* determine if the framebuffer is functional */ + com::SafeArray<BYTE> data; + rc = pFramebuffer->Notify3DEvent(VBOX3D_NOTIFY_EVENT_TYPE_TEST_FUNCTIONAL, ComSafeArrayAsInParam(data)); + + if (rc == S_OK) + CHECK_ERROR_BREAK(pFramebuffer, COMGETTER(WinId)(&winId)); + + if (!winId) + { + /* View associated with framebuffer is destroyed, happens with 2d accel enabled */ + rc = crVBoxServerUnmapScreen(screenId); + AssertRCReturn(rc, rc); + } + else + { + CHECK_ERROR_BREAK(pFramebuffer, COMGETTER(Width)(&w)); + CHECK_ERROR_BREAK(pFramebuffer, COMGETTER(Height)(&h)); + ULONG dummy; + GuestMonitorStatus_T monitorStatus; + CHECK_ERROR_BREAK(pDisplay, GetScreenResolution(screenId, &dummy, &dummy, &dummy, &xo, &yo, &monitorStatus)); + + rc = crVBoxServerMapScreen(screenId, xo, yo, w, h, winId); + AssertRCReturn(rc, rc); + } + } while (0); + } + + crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE); + + rc = VINF_SUCCESS; + } + break; + } + case SHCRGL_HOST_FN_TAKE_SCREENSHOT: + { + if (cParms != 1) + { + LogRel(("OpenGL: SHCRGL_HOST_FN_TAKE_SCREENSHOT: cParms invalid - %d", cParms)); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms->type != VBOX_HGCM_SVC_PARM_PTR) + { + AssertMsgFailed(("invalid param\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (!paParms->u.pointer.addr) + { + AssertMsgFailed(("invalid param\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms->u.pointer.size != sizeof (CRVBOXHGCMTAKESCREENSHOT)) + { + AssertMsgFailed(("invalid param\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRVBOXHGCMTAKESCREENSHOT *pScreenshot = (CRVBOXHGCMTAKESCREENSHOT*)paParms->u.pointer.addr; + uint64_t u64Now = RTTimeProgramMilliTS(); + + if (pScreenshot->u32Screen == CRSCREEN_ALL) + { + for (uint32_t i = 0; i < g_u32ScreenCount; ++i) + { + crScreenshotHandle(pScreenshot, i, u64Now); + } + } + else if (pScreenshot->u32Screen < g_u32ScreenCount) + { + crScreenshotHandle(pScreenshot, pScreenshot->u32Screen, u64Now); + } + else + { + AssertMsgFailed(("invalid screen id\n")); + rc = VERR_INVALID_PARAMETER; + break; + } + break; + } + case SHCRGL_HOST_FN_DEV_RESIZE: + { + Log(("svcCall: SHCRGL_HOST_FN_DEV_RESIZE\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_DEV_RESIZE) + { + LogRel(("OpenGL: SHCRGL_HOST_FN_DEV_RESIZE: cParms invalid - %d", cParms)); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms->type != VBOX_HGCM_SVC_PARM_PTR) + { + AssertMsgFailed(("invalid param\n")); + return VERR_INVALID_PARAMETER; + } + + if (!paParms->u.pointer.addr) + { + AssertMsgFailed(("invalid param\n")); + return VERR_INVALID_PARAMETER; + } + + if (paParms->u.pointer.size != sizeof (CRVBOXHGCMDEVRESIZE)) + { + AssertMsgFailed(("invalid param\n")); + return VERR_INVALID_PARAMETER; + } + + CRVBOXHGCMDEVRESIZE *pResize = (CRVBOXHGCMDEVRESIZE*)paParms->u.pointer.addr; + + rc = crVBoxServerNotifyResize(&pResize->Screen, pResize->pvVRAM); + break; + } + case SHCRGL_HOST_FN_VIEWPORT_CHANGED: + { + Log(("svcCall: SHCRGL_HOST_FN_VIEWPORT_CHANGED\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_VIEWPORT_CHANGED) + { + LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: cParms invalid - %d", cParms)); + rc = VERR_INVALID_PARAMETER; + break; + } + + for (int i = 0; i < SHCRGL_CPARMS_VIEWPORT_CHANGED; ++i) + { + if (paParms[i].type != VBOX_HGCM_SVC_PARM_32BIT) + { + LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: param[%d] type invalid - %d", i, paParms[i].type)); + rc = VERR_INVALID_PARAMETER; + break; + } + } + + if (!RT_SUCCESS(rc)) + { + LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: param validation failed, returning..")); + break; + } + + crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE); + + rc = crVBoxServerSetScreenViewport((int)paParms[0].u.uint32, + paParms[1].u.uint32, /* x */ + paParms[2].u.uint32, /* y */ + paParms[3].u.uint32, /* w */ + paParms[4].u.uint32 /* h */); + if (!RT_SUCCESS(rc)) + { + LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: crVBoxServerSetScreenViewport failed, rc %d", rc)); + } + + crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE); + + break; + } + case SHCRGL_HOST_FN_VIEWPORT_CHANGED2: + { + Log(("svcCall: SHCRGL_HOST_FN_VIEWPORT_CHANGED\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_VIEWPORT_CHANGED) + { + LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: cParms invalid - %d", cParms)); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR + || !paParms[0].u.pointer.addr + || paParms[0].u.pointer.size != sizeof (CRVBOXHGCMVIEWPORT)) + { + LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: param invalid - %d, %#x, %d", + paParms[0].type, + paParms[0].u.pointer.addr, + paParms[0].u.pointer.size)); + rc = VERR_INVALID_PARAMETER; + break; + } + + crServerVBoxCompositionSetEnableStateGlobal(GL_FALSE); + + CRVBOXHGCMVIEWPORT *pViewportInfo = (CRVBOXHGCMVIEWPORT*)paParms[0].u.pointer.addr; + + rc = crVBoxServerSetScreenViewport(pViewportInfo->u32Screen, + pViewportInfo->x, /* x */ + pViewportInfo->y, /* y */ + pViewportInfo->width, /* w */ + pViewportInfo->height /* h */); + if (!RT_SUCCESS(rc)) + { + LogRel(("OpenGL: SHCRGL_HOST_FN_VIEWPORT_CHANGED: crVBoxServerSetScreenViewport failed, rc %d", rc)); + } + + crServerVBoxCompositionSetEnableStateGlobal(GL_TRUE); + + break; + } + case SHCRGL_HOST_FN_SET_OUTPUT_REDIRECT: + { + /* + * OutputRedirect. + * Note: the service calls OutputRedirect callbacks directly + * and they must not block. If asynchronous processing is needed, + * the callback provider must organize this. + */ + Log(("svcCall: SHCRGL_HOST_FN_SET_OUTPUT_REDIRECT\n")); + + /* Verify parameter count and types. */ + if (cParms != SHCRGL_CPARMS_SET_OUTPUT_REDIRECT) + { + rc = VERR_INVALID_PARAMETER; + } + else if (paParms[0].type != VBOX_HGCM_SVC_PARM_PTR) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + /* Fetch parameters. */ + H3DOUTPUTREDIRECT *pOutputRedirect = (H3DOUTPUTREDIRECT *)paParms[0].u.pointer.addr; + uint32_t cbData = paParms[0].u.pointer.size; + + /* Verify parameters values. */ + if (cbData != sizeof (H3DOUTPUTREDIRECT)) + { + rc = VERR_INVALID_PARAMETER; + } + else /* Execute the function. */ + { + if (pOutputRedirect->H3DORBegin != NULL) + { + CROutputRedirect outputRedirect; + outputRedirect.pvContext = pOutputRedirect->pvContext; + outputRedirect.CRORBegin = pOutputRedirect->H3DORBegin; + outputRedirect.CRORGeometry = pOutputRedirect->H3DORGeometry; + outputRedirect.CRORVisibleRegion = pOutputRedirect->H3DORVisibleRegion; + outputRedirect.CRORFrame = pOutputRedirect->H3DORFrame; + outputRedirect.CROREnd = pOutputRedirect->H3DOREnd; + outputRedirect.CRORContextProperty = pOutputRedirect->H3DORContextProperty; + rc = crVBoxServerOutputRedirectSet(&outputRedirect); + if (RT_SUCCESS(rc)) + { + rc = crVBoxServerSetOffscreenRendering(GL_TRUE); + } + } + else + { + /* Redirection is disabled. */ + crVBoxServerSetOffscreenRendering(GL_FALSE); + crVBoxServerOutputRedirectSet(NULL); + } + } + } + break; + } + case SHCRGL_HOST_FN_WINDOWS_SHOW: + { + /* Verify parameter count and types. */ + if (cParms != 1) + { + WARN(("invalid parameter")); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) + { + WARN(("invalid parameter")); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crServerVBoxWindowsShow(!!paParms[0].u.uint32); + if (!RT_SUCCESS(rc)) + WARN(("crServerVBoxWindowsShow failed rc %d", rc)); + + break; + } + case SHCRGL_HOST_FN_SET_SCALE_FACTOR: + { + /* Verify parameter count and types. */ + if (cParms != 1 + || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR + || paParms[0].u.pointer.size != sizeof(CRVBOXHGCMSETSCALEFACTOR) + || !paParms[0].u.pointer.addr) + { + WARN(("invalid parameter")); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRVBOXHGCMSETSCALEFACTOR *pData = (CRVBOXHGCMSETSCALEFACTOR *)paParms[0].u.pointer.addr; + double dScaleFactorW = (double)(pData->u32ScaleFactorWMultiplied) / VBOX_OGL_SCALE_FACTOR_MULTIPLIER; + double dScaleFactorH = (double)(pData->u32ScaleFactorHMultiplied) / VBOX_OGL_SCALE_FACTOR_MULTIPLIER; + + rc = VBoxOglSetScaleFactor(pData->u32Screen, dScaleFactorW, dScaleFactorH); + + /* Log scaling factor rounded to nearest 'int' value (not so precise). */ + LogRel(("OpenGL: Set 3D content scale factor to (%u, %u), multiplier %d (rc=%Rrc)\n", + pData->u32ScaleFactorWMultiplied, + pData->u32ScaleFactorHMultiplied, + (int)VBOX_OGL_SCALE_FACTOR_MULTIPLIER, + rc)); + + break; + } + + case SHCRGL_HOST_FN_SET_UNSCALED_HIDPI: + { + /* Verify parameter count and types. */ + if (cParms != 1 + || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR + || paParms[0].u.pointer.size != sizeof(CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT) + || !paParms[0].u.pointer.addr) + { + WARN(("invalid parameter")); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT *pData = (CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT *)paParms[0].u.pointer.addr; + crServerSetUnscaledHiDPI(pData->fUnscaledHiDPI); + LogRel(("OpenGL: Set OpenGL scale policy on HiDPI displays (fUnscaledHiDPI=%d)\n", pData->fUnscaledHiDPI)); + break; + } + + default: + WARN(("svcHostCallPerform: unexpected u32Function %d", u32Function)); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + LogFlow(("svcHostCall: rc = %Rrc\n", rc)); + return rc; +} + +int crVBoxServerHostCtl(VBOXCRCMDCTL *pCtl, uint32_t cbCtl) +{ + if ((cbCtl - sizeof (VBOXCRCMDCTL)) % sizeof(VBOXHGCMSVCPARM)) + { + WARN(("invalid param size")); + return VERR_INVALID_PARAMETER; + } + uint32_t cParams = (cbCtl - sizeof (VBOXCRCMDCTL)) / sizeof (VBOXHGCMSVCPARM); + bool fHasCallout = VBOXCRCMDCTL_IS_CALLOUT_AVAILABLE(pCtl); + if (fHasCallout) + crVBoxServerCalloutEnable(pCtl); + + int rc = svcHostCallPerform(pCtl->u32Function, cParams, (VBOXHGCMSVCPARM*)(pCtl + 1)); + + if (fHasCallout) + crVBoxServerCalloutDisable(); + + return rc; +} + +static DECLCALLBACK(int) svcHostCall(void *, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + switch (u32Function) + { + case SHCRGL_HOST_FN_CTL: + { + if (cParms != 1) + { + WARN(("cParams != 1")); + return VERR_INVALID_PARAMETER; + } + + if (paParms->type != VBOX_HGCM_SVC_PARM_PTR) + { + WARN(("invalid param type")); + return VERR_INVALID_PARAMETER; + } + + if (paParms->u.pointer.size < sizeof (VBOXCRCMDCTL)) + { + WARN(("invalid param size")); + return VERR_INVALID_PARAMETER; + } + + VBOXCRCMDCTL *pCtl = (VBOXCRCMDCTL*)paParms->u.pointer.addr; + switch (pCtl->enmType) + { + case VBOXCRCMDCTL_TYPE_HGCM: + { + return crVBoxServerHostCtl(pCtl, paParms->u.pointer.size); + } + case VBOXCRCMDCTL_TYPE_DISABLE: + { + if (paParms->u.pointer.size != sizeof (VBOXCRCMDCTL_DISABLE)) + WARN(("invalid param size")); + VBOXCRCMDCTL_DISABLE *pDisable = (VBOXCRCMDCTL_DISABLE*)pCtl; + int rc = crVBoxServerHgcmDisable(&pDisable->Data); + if (RT_SUCCESS(rc)) + g_u32fCrHgcmDisabled = 1; + else + WARN(("crVBoxServerHgcmDisable failed %d", rc)); + return rc; + } + case VBOXCRCMDCTL_TYPE_ENABLE: + { + if (paParms->u.pointer.size != sizeof (VBOXCRCMDCTL_ENABLE)) + WARN(("invalid param size")); + VBOXCRCMDCTL_ENABLE *pEnable = (VBOXCRCMDCTL_ENABLE*)pCtl; + int rc = crVBoxServerHgcmEnable(&pEnable->Data); + if (RT_SUCCESS(rc)) + g_u32fCrHgcmDisabled = 0; + else + WARN(("crVBoxServerHgcmEnable failed %d", rc)); + return rc; + } + default: + WARN(("svcHostCall: invalid function %d", pCtl->enmType)); + return VERR_INVALID_PARAMETER; + } + /* not reached. */ + } + + default: + if (g_u32fCrHgcmDisabled) + { + WARN(("cr hgcm disabled!")); + return VERR_INVALID_STATE; + } + return svcHostCallPerform(u32Function, cParms, paParms); + } +} + +extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable) +{ + int rc = VINF_SUCCESS; + + Log(("SHARED_CROPENGL VBoxHGCMSvcLoad: ptable = %p\n", ptable)); + + if (!ptable) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + Log(("VBoxHGCMSvcLoad: ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version)); + + if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE) + || ptable->u32Version != VBOX_HGCM_SVC_VERSION) + { + rc = VERR_INVALID_PARAMETER; + } + else + { + g_pHelpers = ptable->pHelpers; + + g_u32fCrHgcmDisabled = 0; + + ptable->cbClient = sizeof (void*); + + ptable->pfnUnload = svcUnload; + ptable->pfnConnect = svcConnect; + ptable->pfnDisconnect = svcDisconnect; + ptable->pfnCall = svcCall; + ptable->pfnHostCall = svcHostCall; + ptable->pfnSaveState = svcSaveState; + ptable->pfnLoadState = svcLoadState; + ptable->pfnNotify = NULL; + ptable->pvService = NULL; + + if (!crVBoxServerInit()) + return VERR_NOT_SUPPORTED; + + crServerVBoxSetNotifyEventCB(svcNotifyEventCB); + } + } + + return rc; +} + +#ifdef RT_OS_WINDOWS +#define WIN32_LEAN_AND_MEAN +#include <iprt/win/windows.h> +BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved) +{ + (void) lpvReserved; + + switch (fdwReason) + { + case DLL_THREAD_ATTACH: + { + crStateVBoxAttachThread(); + break; + } + + case DLL_PROCESS_DETACH: + /* do exactly the same thing as for DLL_THREAD_DETACH since + * DLL_THREAD_DETACH is not called for the thread doing DLL_PROCESS_DETACH according to msdn docs */ + case DLL_THREAD_DETACH: + { + crStateVBoxDetachThread(); + break; + } + + case DLL_PROCESS_ATTACH: + default: + break; + } + + return TRUE; +} +#endif diff --git a/src/VBox/HostServices/SharedOpenGL/crserver/main.c b/src/VBox/HostServices/SharedOpenGL/crserver/main.c new file mode 100644 index 00000000..9b525e56 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserver/main.c @@ -0,0 +1,23 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_server.h" + +/** + * \mainpage Crserver + * + * \section CrserverIntroduction Introduction + * + * Chromium consists of all the top-level files in the cr + * directory. The crserver module basically takes care of API dispatch, + * and OpenGL state management. + * + */ + +int main( int argc, char *argv[] ) +{ + return CRServerMain( argc, argv ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/crserverlib/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/Makefile.kup diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/crserverlib.def b/src/VBox/HostServices/SharedOpenGL/crserverlib/crserverlib.def new file mode 100644 index 00000000..a72ac3f9 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/crserverlib.def @@ -0,0 +1,14 @@ +; Copyright (c) 2001, Stanford University +; All rights reserved. +; +; See the file LICENSE.txt for information on redistributing this software. +EXPORTS +CRServerMain +crVBoxServerInit +crVBoxServerTearDown +crVBoxServerAddClient +crVBoxServerRemoveClient +crVBoxServerClientWrite +crVBoxServerClientRead +crVBoxServerCrHgsmiCmd +crVBoxServerCrHgsmiCtl diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py new file mode 100755 index 00000000..cbe1061b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/get_sizes.py @@ -0,0 +1,468 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. + +from __future__ import print_function + +num_get_values = { + 'GL_ACCUM_ALPHA_BITS' : 1, + 'GL_ACCUM_BLUE_BITS' : 1, + 'GL_ACCUM_CLEAR_VALUE': 4, + 'GL_ACCUM_GREEN_BITS' : 1, + 'GL_ACCUM_RED_BITS' : 1, + 'GL_ALPHA_BIAS' : 1, + 'GL_ALPHA_BITS' : 1, + 'GL_ALPHA_SCALE' : 1, + 'GL_ALPHA_TEST' : 1, + 'GL_ALPHA_TEST_FUNC' : 1, + 'GL_ALPHA_TEST_REF' : 1, + 'GL_ATTRIB_STACK_DEPTH' : 1, + 'GL_AUTO_NORMAL' : 1, + 'GL_AUX_BUFFERS' : 1, + 'GL_BLEND' : 1, + 'GL_BLEND_DST' : 1, + 'GL_BLEND_SRC' : 1, + 'GL_BLUE_BIAS' : 1, + 'GL_BLUE_BITS' : 1, + 'GL_BLUE_SCALE' : 1, + 'GL_CLIENT_ATTRIB_STACK_DEPTH' : 1, + 'GL_CLIP_PLANE0' : 1, + 'GL_CLIP_PLANE1' : 1, + 'GL_CLIP_PLANE2' : 1, + 'GL_CLIP_PLANE3' : 1, + 'GL_CLIP_PLANE4' : 1, + 'GL_CLIP_PLANE5' : 1, + 'GL_COLOR_ARRAY' : 1, + 'GL_COLOR_ARRAY_SIZE' : 1, + 'GL_COLOR_ARRAY_STRIDE' : 1, + 'GL_COLOR_ARRAY_TYPE' : 1, + 'GL_COLOR_CLEAR_VALUE': 4, + 'GL_COLOR_LOGIC_OP' : 1, + 'GL_COLOR_MATERIAL' : 1, + 'GL_COLOR_MATERIAL_FACE' : 1, + 'GL_COLOR_MATERIAL_PARAMETER' : 1, + 'GL_COLOR_MATRIX_STACK_DEPTH' : 1, + 'GL_COLOR_WRITEMASK': 4, + 'GL_CULL_FACE' : 1, + 'GL_CULL_FACE_MODE' : 1, + 'GL_CURRENT_COLOR': 4, + 'GL_CURRENT_INDEX' : 1, + 'GL_CURRENT_NORMAL': 3, + 'GL_CURRENT_RASTER_COLOR': 4, + 'GL_CURRENT_RASTER_DISTANCE' : 1, + 'GL_CURRENT_RASTER_INDEX' : 1, + 'GL_CURRENT_RASTER_POSITION': 4, + 'GL_CURRENT_RASTER_POSITION_VALID' : 1, + 'GL_CURRENT_RASTER_TEXTURE_COORDS': 4, + 'GL_CURRENT_TEXTURE_COORDS': 4, + 'GL_DEPTH_BIAS' : 1, + 'GL_DEPTH_BITS' : 1, + 'GL_DEPTH_CLEAR_VALUE' : 1, + 'GL_DEPTH_FUNC' : 1, + 'GL_DEPTH_RANGE': 2, + 'GL_DEPTH_SCALE' : 1, + 'GL_DEPTH_TEST' : 1, + 'GL_DEPTH_WRITEMASK' : 1, + 'GL_DITHER' : 1, + 'GL_DOUBLEBUFFER' : 1, + 'GL_DRAW_BUFFER' : 1, + 'GL_EDGE_FLAG' : 1, + 'GL_EDGE_FLAG_ARRAY' : 1, + 'GL_EDGE_FLAG_ARRAY_STRIDE' : 1, + 'GL_FEEDBACK_BUFFER_SIZE' : 1, + 'GL_FEEDBACK_BUFFER_TYPE' : 1, + 'GL_FOG' : 1, + 'GL_FOG_COLOR': 4, + 'GL_FOG_DENSITY' : 1, + 'GL_FOG_END' : 1, + 'GL_FOG_HINT' : 1, + 'GL_FOG_INDEX' : 1, + 'GL_FOG_MODE' : 1, + 'GL_FOG_START' : 1, + 'GL_FRONT_FACE' : 1, + 'GL_GREEN_BIAS' : 1, + 'GL_GREEN_BITS' : 1, + 'GL_GREEN_SCALE' : 1, + 'GL_INDEX_ARRAY' : 1, + 'GL_INDEX_ARRAY_STRIDE' : 1, + 'GL_INDEX_ARRAY_TYPE' : 1, + 'GL_INDEX_BITS' : 1, + 'GL_INDEX_CLEAR_VALUE' : 1, + 'GL_INDEX_LOGIC_OP' : 1, + 'GL_INDEX_MODE' : 1, + 'GL_INDEX_OFFSET' : 1, + 'GL_INDEX_SHIFT' : 1, + 'GL_INDEX_WRITEMASK' : 1, + 'GL_LIGHT0' : 1, + 'GL_LIGHT1' : 1, + 'GL_LIGHT2' : 1, + 'GL_LIGHT3' : 1, + 'GL_LIGHT4' : 1, + 'GL_LIGHT5' : 1, + 'GL_LIGHT6' : 1, + 'GL_LIGHT7' : 1, + 'GL_LIGHTING' : 1, + 'GL_LIGHT_MODEL_AMBIENT': 4, + 'GL_LIGHT_MODEL_LOCAL_VIEWER' : 1, + 'GL_LIGHT_MODEL_TWO_SIDE' : 1, + 'GL_LINE_SMOOTH' : 1, + 'GL_LINE_SMOOTH_HINT' : 1, + 'GL_LINE_STIPPLE' : 1, + 'GL_LINE_STIPPLE_PATTERN' : 1, + 'GL_LINE_STIPPLE_REPEAT' : 1, + 'GL_LINE_WIDTH' : 1, + 'GL_LINE_WIDTH_GRANULARITY' : 1, + 'GL_LINE_WIDTH_RANGE': 2, + 'GL_LIST_BASE' : 1, + 'GL_LIST_INDEX' : 1, + 'GL_LIST_MODE' : 1, + 'GL_LOGIC_OP_MODE' : 1, + 'GL_MAP1_COLOR_4' : 1, + 'GL_MAP1_GRID_DOMAIN': 2, + 'GL_MAP1_GRID_SEGMENTS' : 1, + 'GL_MAP1_INDEX' : 1, + 'GL_MAP1_NORMAL' : 1, + 'GL_MAP1_TEXTURE_COORD_1' : 1, + 'GL_MAP1_TEXTURE_COORD_2' : 1, + 'GL_MAP1_TEXTURE_COORD_3' : 1, + 'GL_MAP1_TEXTURE_COORD_4' : 1, + 'GL_MAP1_VERTEX_3' : 1, + 'GL_MAP1_VERTEX_4' : 1, + 'GL_MAP2_COLOR_4' : 1, + 'GL_MAP2_GRID_DOMAIN': 4, + 'GL_MAP2_GRID_SEGMENTS': 2, + 'GL_MAP2_INDEX' : 1, + 'GL_MAP2_NORMAL' : 1, + 'GL_MAP2_TEXTURE_COORD_1' : 1, + 'GL_MAP2_TEXTURE_COORD_2' : 1, + 'GL_MAP2_TEXTURE_COORD_3' : 1, + 'GL_MAP2_TEXTURE_COORD_4' : 1, + 'GL_MAP2_VERTEX_3' : 1, + 'GL_MAP2_VERTEX_4' : 1, + 'GL_MAP_COLOR' : 1, + 'GL_MAP_STENCIL' : 1, + 'GL_MATRIX_MODE' : 1, + 'GL_MAX_CLIENT_ATTRIB_STACK_DEPTH' : 1, + 'GL_MAX_ATTRIB_STACK_DEPTH' : 1, + 'GL_MAX_CLIP_PLANES' : 1, + 'GL_MAX_COLOR_MATRIX_STACK_DEPTH' : 1, + 'GL_MAX_EVAL_ORDER' : 1, + 'GL_MAX_LIGHTS' : 1, + 'GL_MAX_LIST_NESTING' : 1, + 'GL_MAX_MODELVIEW_STACK_DEPTH' : 1, + 'GL_MAX_NAME_STACK_DEPTH' : 1, + 'GL_MAX_PIXEL_MAP_TABLE' : 1, + 'GL_MAX_PROJECTION_STACK_DEPTH' : 1, + 'GL_MAX_TEXTURE_SIZE' : 1, + 'GL_MAX_3D_TEXTURE_SIZE' : 1, + 'GL_MAX_TEXTURE_STACK_DEPTH' : 1, + 'GL_MAX_VIEWPORT_DIMS': 2, + 'GL_MODELVIEW_MATRIX': 16, + 'GL_MODELVIEW_STACK_DEPTH' : 1, + 'GL_NAME_STACK_DEPTH' : 1, + 'GL_NORMAL_ARRAY' : 1, + 'GL_NORMAL_ARRAY_STRIDE' : 1, + 'GL_NORMAL_ARRAY_TYPE' : 1, + 'GL_NORMALIZE' : 1, + 'GL_PACK_ALIGNMENT' : 1, + 'GL_PACK_LSB_FIRST' : 1, + 'GL_PACK_ROW_LENGTH' : 1, + 'GL_PACK_SKIP_PIXELS' : 1, + 'GL_PACK_SKIP_ROWS' : 1, + 'GL_PACK_SWAP_BYTES' : 1, + 'GL_PERSPECTIVE_CORRECTION_HINT' : 1, + 'GL_PIXEL_MAP_A_TO_A_SIZE' : 1, + 'GL_PIXEL_MAP_B_TO_B_SIZE' : 1, + 'GL_PIXEL_MAP_G_TO_G_SIZE' : 1, + 'GL_PIXEL_MAP_I_TO_A_SIZE' : 1, + 'GL_PIXEL_MAP_I_TO_B_SIZE' : 1, + 'GL_PIXEL_MAP_I_TO_G_SIZE' : 1, + 'GL_PIXEL_MAP_I_TO_I_SIZE' : 1, + 'GL_PIXEL_MAP_I_TO_R_SIZE' : 1, + 'GL_PIXEL_MAP_R_TO_R_SIZE' : 1, + 'GL_PIXEL_MAP_S_TO_S_SIZE' : 1, + 'GL_POINT_SIZE' : 1, + 'GL_POINT_SIZE_GRANULARITY' : 1, + 'GL_POINT_SIZE_RANGE': 2, + 'GL_POINT_SMOOTH' : 1, + 'GL_POINT_SMOOTH_HINT' : 1, + 'GL_POLYGON_MODE': 2, + 'GL_POLYGON_OFFSET_FACTOR' : 1, + 'GL_POLYGON_OFFSET_UNITS' : 1, + 'GL_POLYGON_OFFSET_FILL' : 1, + 'GL_POLYGON_OFFSET_LINE' : 1, + 'GL_POLYGON_OFFSET_POINT' : 1, + 'GL_POLYGON_SMOOTH' : 1, + 'GL_POLYGON_SMOOTH_HINT' : 1, + 'GL_POLYGON_STIPPLE' : 1, + 'GL_PROJECTION_MATRIX': 16, + 'GL_PROJECTION_STACK_DEPTH' : 1, + 'GL_READ_BUFFER' : 1, + 'GL_RED_BIAS' : 1, + 'GL_RED_BITS' : 1, + 'GL_RED_SCALE' : 1, + 'GL_RENDER_MODE' : 1, + 'GL_RGBA_MODE' : 1, + 'GL_SCISSOR_BOX': 4, + 'GL_SCISSOR_TEST' : 1, + 'GL_SELECTION_BUFFER_SIZE' : 1, + 'GL_SHADE_MODEL' : 1, + 'GL_STENCIL_BITS' : 1, + 'GL_STENCIL_CLEAR_VALUE' : 1, + 'GL_STENCIL_FAIL' : 1, + 'GL_STENCIL_FUNC' : 1, + 'GL_STENCIL_PASS_DEPTH_FAIL' : 1, + 'GL_STENCIL_PASS_DEPTH_PASS' : 1, + 'GL_STENCIL_REF' : 1, + 'GL_STENCIL_TEST' : 1, + 'GL_STENCIL_VALUE_MASK' : 1, + 'GL_STENCIL_WRITEMASK' : 1, + 'GL_STEREO' : 1, + 'GL_SUBPIXEL_BITS' : 1, + 'GL_TEXTURE_1D' : 1, + 'GL_TEXTURE_2D' : 1, + 'GL_TEXTURE_BINDING_1D' : 1, + 'GL_TEXTURE_BINDING_2D' : 1, + 'GL_TEXTURE_BINDING_3D' : 1, + 'GL_TEXTURE_COORD_ARRAY' : 1, + 'GL_TEXTURE_COORD_ARRAY_SIZE' : 1, + 'GL_TEXTURE_COORD_ARRAY_STRIDE' : 1, + 'GL_TEXTURE_COORD_ARRAY_TYPE' : 1, + 'GL_TEXTURE_ENV_COLOR': 4, + 'GL_TEXTURE_ENV_MODE' : 1, + 'GL_TEXTURE_GEN_Q' : 1, + 'GL_TEXTURE_GEN_R' : 1, + 'GL_TEXTURE_GEN_S' : 1, + 'GL_TEXTURE_GEN_T' : 1, + 'GL_TEXTURE_MATRIX': 16, + 'GL_TEXTURE_STACK_DEPTH' : 1, + 'GL_UNPACK_ALIGNMENT' : 1, + 'GL_UNPACK_LSB_FIRST' : 1, + 'GL_UNPACK_ROW_LENGTH' : 1, + 'GL_UNPACK_SKIP_PIXELS' : 1, + 'GL_UNPACK_SKIP_ROWS' : 1, + 'GL_UNPACK_SWAP_BYTES' : 1, + 'GL_VERTEX_ARRAY' : 1, + 'GL_VERTEX_ARRAY_SIZE' : 1, + 'GL_VERTEX_ARRAY_STRIDE' : 1, + 'GL_VERTEX_ARRAY_TYPE' : 1, + 'GL_VIEWPORT': 4, + 'GL_ZOOM_X' : 1, + 'GL_ZOOM_Y' : 1, + #GL_ARB_IMAGING which is part of 1.2.1 + 'GL_COLOR_MATRIX' : 16, + 'GL_COLOR_MATRIX_STACK_DEPTH' : 1, + 'GL_COLOR_TABLE' : 1, + 'GL_POST_CONVOLUTION_COLOR_TABLE' : 1, + 'GL_POST_COLOR_MATRIX_COLOR_TABLE' : 1, + 'GL_PROXY_COLOR_TABLE' : 1, + 'GL_CONVOLUTION_1D' : 1, + 'GL_CONVOLUTION_2D' : 1, + 'GL_SEPARABLE_2D' : 1, + 'GL_POST_CONVOLUTION_RED_SCALE' : 1, + 'GL_POST_CONVOLUTION_GREEN_SCALE' : 1, + 'GL_POST_CONVOLUTION_BLUE_SCALE' : 1, + 'GL_POST_CONVOLUTION_ALPHA_SCALE' : 1, + 'GL_POST_CONVOLUTION_RED_BIAS' : 1, + 'GL_POST_CONVOLUTION_GREEN_BIAS' : 1, + 'GL_POST_CONVOLUTION_BLUE_BIAS' : 1, + 'GL_POST_CONVOLUTION_ALPHA_BIAS' : 1, + 'GL_HISTOGRAM' : 1, + 'GL_MINMAX' : 1, + 'GL_MAX_COLOR_MATRIX_STACK_DEPTH' : 1, + 'GL_MAX_CONVOLUTION_WIDTH' : 1, + 'GL_MAX_CONVOLUTION_HEIGHT' : 1, +} + +extensions_num_get_values = { + 'GL_BLEND_COLOR_EXT': (4, 'CR_EXT_blend_color'), + 'GL_BLEND_EQUATION_EXT': (1, 'CR_EXT_blend_minmax'), + 'GL_BLEND_SRC_RGB_EXT': (1, 'CR_EXT_blend_func_separate'), + 'GL_BLEND_DST_RGB_EXT': (1, 'CR_EXT_blend_func_separate'), + 'GL_BLEND_SRC_ALPHA_EXT': (1, 'CR_EXT_blend_func_separate'), + 'GL_BLEND_DST_ALPHA_EXT': (1, 'CR_EXT_blend_func_separate'), + 'GL_FOG_DISTANCE_MODE_NV': (1, 'CR_NV_fog_distance'), + 'GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB': (1, 'CR_ARB_texture_cube_map'), + 'GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT': (1, 'CR_EXT_texture_filter_anisotropic'), + 'GL_TEXTURE_BINDING_CUBE_MAP_ARB': (1, 'CR_ARB_texture_cube_map'), + 'GL_TEXTURE_CUBE_MAP_ARB': (1, 'CR_ARB_texture_cube_map'), + 'GL_ACTIVE_TEXTURE_ARB': (1, 'CR_ARB_multitexture'), + 'GL_CLIENT_ACTIVE_TEXTURE_ARB': (1, 'CR_ARB_multitexture'), + 'GL_MAX_TEXTURE_UNITS_ARB': (1, 'CR_ARB_multitexture'), + 'GL_NUM_GENERAL_COMBINERS_NV': (1, 'CR_NV_register_combiners'), + 'GL_MAX_GENERAL_COMBINERS_NV': (1, 'CR_NV_register_combiners'), + 'GL_COLOR_SUM_CLAMP_NV': (1, 'CR_NV_register_combiners'), + 'GL_CONSTANT_COLOR0_NV': (4, 'CR_NV_register_combiners'), + 'GL_CONSTANT_COLOR1_NV': (4, 'CR_NV_register_combiners'), + 'GL_PER_STAGE_CONSTANTS_NV': (1, 'CR_NV_register_combiners2'), + 'GL_LIGHT_MODEL_COLOR_CONTROL_EXT': (1, 'CR_EXT_separate_specular_color'), + 'GL_COLOR_SUM_EXT': (1, 'CR_EXT_secondary_color'), + 'GL_CURRENT_SECONDARY_COLOR_EXT': (4, 'CR_EXT_secondary_color'), + 'GL_SECONDARY_COLOR_ARRAY_SIZE_EXT': (1, 'CR_EXT_secondary_color'), + 'GL_SECONDARY_COLOR_ARRAY_TYPE_EXT': (1, 'CR_EXT_secondary_color'), + 'GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT': (1, 'CR_EXT_secondary_color'), + 'GL_RESCALE_NORMAL': (1, 'CR_OPENGL_VERSION_1_2'), + 'GL_NUM_COMPRESSED_TEXTURE_FORMATS': (1, 'CR_ARB_texture_compression'), + 'GL_TEXTURE_3D': (1, 'CR_OPENGL_VERSION_1_2'), + 'GL_LIGHT_MODEL_COLOR_CONTROL': (1, 'CR_OPENGL_VERSION_1_2'), + 'GL_UNPACK_IMAGE_HEIGHT': (1, 'CR_OPENGL_VERSION_1_2'), + 'GL_UNPACK_SKIP_IMAGES': (1, 'CR_OPENGL_VERSION_1_2'), + 'GL_PACK_IMAGE_HEIGHT': (1, 'CR_OPENGL_VERSION_1_2'), + 'GL_PACK_SKIP_IMAGES': (1, 'CR_OPENGL_VERSION_1_2'), + 'GL_ALIASED_POINT_SIZE_RANGE': (2, 'CR_OPENGL_VERSION_1_2'), + 'GL_ALIASED_LINE_WIDTH_RANGE': (2, 'CR_OPENGL_VERSION_1_2'), + 'GL_MAX_ELEMENTS_INDICES': (1, 'CR_OPENGL_VERSION_1_2'), + 'GL_MAX_ELEMENTS_VERTICES': (1, 'CR_OPENGL_VERSION_1_2'), + 'GL_MULTISAMPLE_ARB': (1, 'CR_ARB_multisample'), + 'GL_SAMPLE_ALPHA_TO_COVERAGE_ARB': (1, 'CR_ARB_multisample'), + 'GL_SAMPLE_ALPHA_TO_ONE_ARB': (1, 'CR_ARB_multisample'), + 'GL_SAMPLE_COVERAGE_ARB': (1, 'CR_ARB_multisample'), + 'GL_SAMPLE_BUFFERS_ARB': (1, 'CR_ARB_multisample'), + 'GL_SAMPLES_ARB': (1, 'CR_ARB_multisample'), + 'GL_SAMPLE_COVERAGE_VALUE_ARB': (1, 'CR_ARB_multisample'), + 'GL_SAMPLE_COVERAGE_INVERT_ARB': (1, 'CR_ARB_multisample'), + 'GL_POINT_SPRITE_ARB': (1, 'CR_ARB_point_sprite'), + 'GL_MAX_TEXTURE_LOD_BIAS_EXT': (1, 'CR_EXT_texture_lod_bias'), + 'GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB': (1, 'CR_ARB_texture_compression'), + 'GL_PROGRAM_ERROR_POSITION_NV': (1, 'CR_NV_vertex_program'), + 'GL_VERTEX_PROGRAM_BINDING_NV': (1, 'CR_NV_vertex_program'), + 'GL_MAX_VERTEX_ATTRIBS_ARB': (1, 'CR_ARB_vertex_program'), + 'GL_MAX_TEXTURE_COORDS_ARB': (1, 'CR_ARB_vertex_program'), + 'GL_PROGRAM_ERROR_POSITION_NV': (1, 'CR_NV_fragment_program'), + 'GL_FRAGMENT_PROGRAM_BINDING_NV': (1, 'CR_NV_fragment_program'), + 'GL_MAX_RECTANGLE_TEXTURE_SIZE_NV': (1, 'CR_NV_texture_rectangle'), + 'GL_TEXTURE_RECTANGLE_NV': (1, 'CR_NV_texture_rectangle'), + 'GL_TEXTURE_BINDING_RECTANGLE_NV': (1, 'CR_NV_texture_rectangle'), + 'GL_CLIP_VOLUME_CLIPPING_HINT_EXT' : (3, 'CR_EXT_clip_volume_hint'), + 'GL_RASTER_POSITION_UNCLIPPED_IBM' : (1, 'CR_IBM_rasterpos_clip'), + 'GL_GENERATE_MIPMAP_HINT_SGIS' : (1, 'CR_SGIS_generate_mipmap'), + 'GL_CURRENT_FOG_COORDINATE_EXT' : (1, 'CR_EXT_fog_coord'), + 'GL_FOG_COORDINATE_ARRAY_TYPE_EXT' : (1, 'CR_EXT_fog_coord'), + 'GL_FOG_COORDINATE_ARRAY_STRIDE_EXT' : (1, 'CR_EXT_fog_coord'), + 'GL_TRANSPOSE_COLOR_MATRIX_ARB': (16, 'CR_ARB_transpose_matrix'), + 'GL_TRANSPOSE_MODELVIEW_MATRIX_ARB': (16, 'CR_ARB_transpose_matrix'), + 'GL_TRANSPOSE_PROJECTION_MATRIX_ARB': (16, 'CR_ARB_transpose_matrix'), + 'GL_TRANSPOSE_TEXTURE_MATRIX_ARB': (16, 'CR_ARB_transpose_matrix'), + 'GL_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_VERTEX_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_NORMAL_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_COLOR_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_INDEX_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB': (1, 'CR_ARB_vertex_buffer_object'), + 'GL_MAX_TEXTURE_IMAGE_UNITS_ARB': (1, 'CR_ARB_fragment_program'), + 'GL_MAX_PROGRAM_MATRICES_ARB': (1, 'CR_ARB_vertex_program'), + 'GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB': (1, 'CR_ARB_vertex_program'), + # Vertex shaders (2.0) # + 'GL_MAX_VERTEX_UNIFORM_COMPONENTS': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_MAX_VARYING_FLOATS': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_MAX_VERTEX_ATTRIBS': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_MAX_TEXTURE_IMAGE_UNITS': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_MAX_TEXTURE_COORDS': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_VERTEX_PROGRAM_POINT_SIZE': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_VERTEX_PROGRAM_TWO_SIDE': (1, 'CR_OPENGL_VERSION_2_0'), + # Fragment shaders (2.0) # + 'GL_MAX_FRAGMENT_UNIFORM_COMPONENTS': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_FRAGMENT_SHADER_DERIVATIVE_HINT': (1, 'CR_OPENGL_VERSION_2_0'), + # Draw buffers (2.0) / GL_ARB_draw_buffers # + 'GL_MAX_DRAW_BUFFERS': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER0': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER1': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER2': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER3': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER4': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER5': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER6': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER7': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER8': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER9': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER10': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER11': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER12': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER13': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER14': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_DRAW_BUFFER15': (1, 'CR_OPENGL_VERSION_2_0'), + # Point sprite (2.0) # + 'GL_POINT_SPRITE': (1, 'CR_OPENGL_VERSION_2_0'), + # Separate stencil (2.0) # + 'GL_STENCIL_BACK_FUNC': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_STENCIL_BACK_REF': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_STENCIL_BACK_VALUE_MASK': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_STENCIL_BACK_FAIL': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_STENCIL_BACK_PASS_DEPTH_FAIL': (1, 'CR_OPENGL_VERSION_2_0'), + 'GL_STENCIL_BACK_PASS_DEPTH_PASS': (1, 'CR_OPENGL_VERSION_2_0'), + # Frame buffer object EXT # + 'GL_FRAMEBUFFER_BINDING_EXT': (1, 'CR_EXT_framebuffer_object'), + 'GL_RENDERBUFFER_BINDING_EXT': (1, 'CR_EXT_framebuffer_object'), + 'GL_MAX_COLOR_ATTACHMENTS_EXT': (1, 'CR_EXT_framebuffer_object'), + 'GL_MAX_RENDERBUFFER_SIZE_EXT': (1, 'CR_EXT_framebuffer_object'), + # ARB_shader_objects + 'GL_CURRENT_PROGRAM': (1, 'CR_ARB_shader_objects'), + # EXT_framebuffer_blit + 'GL_READ_FRAMEBUFFER_BINDING_EXT': (1, 'CR_EXT_framebuffer_blit'), + 'GL_DRAW_FRAMEBUFFER_BINDING_EXT': (1, 'CR_EXT_framebuffer_blit'), + # EXT_stencil_two_side + 'GL_ACTIVE_STENCIL_FACE_EXT': (1, 'CR_EXT_stencil_two_side'), +} + +get_keys = list(num_get_values.keys()) + list(extensions_num_get_values.keys()) +get_keys.sort() +max_keyvalues = 0 + +print(""" +static struct nv_struct { GLenum pname; unsigned int num_values; +#ifdef VBOX_WITH_CRDUMPER +const char* pszName; +#endif +} num_values_array[] = { +""") +for key in get_keys: + try: + keyvalues = num_get_values[key] + if max_keyvalues < keyvalues: + max_keyvalues = keyvalues + print(""" + \t{ %s, %d +#ifdef VBOX_WITH_CRDUMPER + , "%s" +#endif + }, + """ % (key, keyvalues, key)) + except KeyError: + (nv, ifdef) = extensions_num_get_values[key] + if max_keyvalues < nv: + max_keyvalues = nv + print('#ifdef %s' % ifdef) + print(""" + \t{ %s, %d + #ifdef VBOX_WITH_CRDUMPER + , "%s" + #endif + }, + """ % (key, nv, key)) + print('#endif /* %s */' % ifdef) +print("\t{ 0, 0 }") +print("};") +print("#define CR_MAX_GET_VALUES %d" % max_keyvalues) + +print(""" +static unsigned int __numValues( GLenum pname ) +{ + struct nv_struct *temp; + + for (temp = num_values_array; temp->num_values != 0 ; temp++) + { + if (temp->pname == pname) + return temp->num_values; + } + crDebug( "Invalid pname to __numValues: 0x%x\\n", (int) pname ); + return 0; +} +""") diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/Makefile.kup diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_base.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_base.cpp new file mode 100644 index 00000000..797104f0 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_base.cpp @@ -0,0 +1,390 @@ +/* $Id: display_base.cpp $ */ + +/** @file + * Presenter API: display base class implementation. + */ + +/* + * Copyright (C) 2014-2019 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. + */ + +#include "server_presenter.h" + +CrFbDisplayBase::CrFbDisplayBase() : + mpContainer(NULL), + mpFb(NULL), + mcUpdates(0), + mhSlot(CRHTABLE_HANDLE_INVALID) +{ + mFlags.u32Value = 0; +} + + +CrFbDisplayBase::~CrFbDisplayBase() +{ + Assert(!mcUpdates); + + if (mpContainer) + mpContainer->remove(this); +} + + +bool CrFbDisplayBase::isComposite() +{ + return false; +} + + +class CrFbDisplayComposite* CrFbDisplayBase::getContainer() +{ + return mpContainer; +} + + +bool CrFbDisplayBase::isInList() +{ + return !!mpContainer; +} + + +bool CrFbDisplayBase::isUpdating() +{ + return !!mcUpdates; +} + + +int CrFbDisplayBase::setRegionsChanged() +{ + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + mFlags.fRegionsShanged = 1; + return VINF_SUCCESS; +} + + +int CrFbDisplayBase::setFramebuffer(struct CR_FRAMEBUFFER *pFb) +{ + if (mcUpdates) + { + WARN(("trying to set framebuffer while update is in progress")); + return VERR_INVALID_STATE; + } + + if (mpFb == pFb) + return VINF_SUCCESS; + + int rc = setFramebufferBegin(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + if (mpFb) + { + rc = fbCleanup(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + setFramebufferEnd(pFb); + return rc; + } + } + + mpFb = pFb; + + if (mpFb) + { + rc = fbSync(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + setFramebufferEnd(pFb); + return rc; + } + } + + setFramebufferEnd(pFb); + return VINF_SUCCESS; +} + + +struct CR_FRAMEBUFFER* CrFbDisplayBase::getFramebuffer() +{ + return mpFb; +} + + +int CrFbDisplayBase::UpdateBegin(struct CR_FRAMEBUFFER *pFb) +{ + ++mcUpdates; + Assert(!mFlags.fRegionsShanged || mcUpdates > 1); + return VINF_SUCCESS; +} + + +void CrFbDisplayBase::UpdateEnd(struct CR_FRAMEBUFFER *pFb) +{ + --mcUpdates; + Assert(mcUpdates < UINT32_MAX/2); + if (!mcUpdates) + onUpdateEnd(); +} + + +int CrFbDisplayBase::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + return VINF_SUCCESS; +} + + +int CrFbDisplayBase::EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + mFlags.fRegionsShanged = 1; + return VINF_SUCCESS; +} + + +int CrFbDisplayBase::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, + HCR_FRAMEBUFFER_ENTRY hReplacedEntry) +{ + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + return VINF_SUCCESS; +} + + +int CrFbDisplayBase::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + return VINF_SUCCESS; +} + + +int CrFbDisplayBase::EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + mFlags.fRegionsShanged = 1; + return VINF_SUCCESS; +} + + +int CrFbDisplayBase::EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + return VINF_SUCCESS; +} + + +int CrFbDisplayBase::EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + mFlags.fRegionsShanged = 1; + return VINF_SUCCESS; +} + + +int CrFbDisplayBase::RegionsChanged(struct CR_FRAMEBUFFER *pFb) +{ + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + mFlags.fRegionsShanged = 1; + return VINF_SUCCESS; +} + + +int CrFbDisplayBase::FramebufferChanged(struct CR_FRAMEBUFFER *pFb) +{ + if (!mcUpdates) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + return VINF_SUCCESS; +} + + +void CrFbDisplayBase::onUpdateEnd() +{ + if (mFlags.fRegionsShanged) + { + mFlags.fRegionsShanged = 0; + if (getFramebuffer()) /*<-dont't do anything on cleanup*/ + ueRegions(); + } +} + + +void CrFbDisplayBase::ueRegions() +{ +} + + +DECLCALLBACK(bool) CrFbDisplayBase::entriesCreateCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext) +{ + int rc = ((ICrFbDisplay*)(pvContext))->EntryCreated(hFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + } + return true; +} + + +DECLCALLBACK(bool) CrFbDisplayBase::entriesDestroyCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext) +{ + int rc = ((ICrFbDisplay*)(pvContext))->EntryDestroyed(hFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + } + return true; +} + + +int CrFbDisplayBase::fbSynchAddAllEntries() +{ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + + CrVrScrCompositorConstIterInit(CrFbGetCompositor(mpFb), &Iter); + int rc = VINF_SUCCESS; + + CrFbVisitCreatedEntries(mpFb, entriesCreateCb, this); + + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + + rc = EntryAdded(mpFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + EntryDestroyed(mpFb, hEntry); + break; + } + } + + return rc; +} + + +int CrFbDisplayBase::fbCleanupRemoveAllEntries() +{ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + + CrVrScrCompositorConstIterInit(CrFbGetCompositor(mpFb), &Iter); + + int rc = VINF_SUCCESS; + + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + rc = EntryRemoved(mpFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + break; + } + } + + CrFbVisitCreatedEntries(mpFb, entriesDestroyCb, this); + + return rc; +} + + +int CrFbDisplayBase::setFramebufferBegin(struct CR_FRAMEBUFFER *pFb) +{ + return UpdateBegin(pFb); +} + + +void CrFbDisplayBase::setFramebufferEnd(struct CR_FRAMEBUFFER *pFb) +{ + UpdateEnd(pFb); +} + + +DECLCALLBACK(void) CrFbDisplayBase::slotEntryReleaseCB(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext) +{ +} + + +void CrFbDisplayBase::slotRelease() +{ + Assert(mhSlot); + CrFbDDataReleaseSlot(mpFb, mhSlot, slotEntryReleaseCB, this); +} + + +int CrFbDisplayBase::fbCleanup() +{ + if (mhSlot) + { + slotRelease(); + mhSlot = 0; + } + + mpFb = NULL; + return VINF_SUCCESS; +} + + +int CrFbDisplayBase::fbSync() +{ + return VINF_SUCCESS; +} + + +CRHTABLE_HANDLE CrFbDisplayBase::slotGet() +{ + if (!mhSlot) + { + if (mpFb) + mhSlot = CrFbDDataAllocSlot(mpFb); + } + + return mhSlot; +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_composite.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_composite.cpp new file mode 100644 index 00000000..697c2c96 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_composite.cpp @@ -0,0 +1,341 @@ +/* $Id: display_composite.cpp $ */ + +/** @file + * Presenter API: display composite class implementation. + */ + +/* + * Copyright (C) 2014-2019 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. + */ + +#include "server_presenter.h" + +CrFbDisplayComposite::CrFbDisplayComposite() : + mcDisplays(0) +{ + RTListInit(&mDisplays); +} + + +bool CrFbDisplayComposite::isComposite() +{ + return true; +} + + +uint32_t CrFbDisplayComposite::getDisplayCount() +{ + return mcDisplays; +} + + +bool CrFbDisplayComposite::add(CrFbDisplayBase *pDisplay) +{ + if (pDisplay->isInList()) + { + WARN(("entry in list already")); + return false; + } + + RTListAppend(&mDisplays, &pDisplay->mNode); + pDisplay->mpContainer = this; + pDisplay->setFramebuffer(getFramebuffer()); + ++mcDisplays; + return true; +} + + +bool CrFbDisplayComposite::remove(CrFbDisplayBase *pDisplay, bool fCleanupDisplay) +{ + if (pDisplay->getContainer() != this) + { + WARN(("invalid entry container")); + return false; + } + + RTListNodeRemove(&pDisplay->mNode); + pDisplay->mpContainer = NULL; + if (fCleanupDisplay) + pDisplay->setFramebuffer(NULL); + --mcDisplays; + return true; +} + + +CrFbDisplayBase* CrFbDisplayComposite::first() +{ + return RTListGetFirstCpp(&mDisplays, CrFbDisplayBase, mNode); +} + + +CrFbDisplayBase* CrFbDisplayComposite::next(CrFbDisplayBase* pDisplay) +{ + if (pDisplay->getContainer() != this) + { + WARN(("invalid entry container")); + return NULL; + } + + return RTListGetNextCpp(&mDisplays, pDisplay, CrFbDisplayBase, mNode); +} + + +int CrFbDisplayComposite::setFramebuffer(struct CR_FRAMEBUFFER *pFb) +{ + CrFbDisplayBase::setFramebuffer(pFb); + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + pIter->setFramebuffer(pFb); + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayComposite::UpdateBegin(struct CR_FRAMEBUFFER *pFb) +{ + int rc = CrFbDisplayBase::UpdateBegin(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + rc = pIter->UpdateBegin(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; +} + + +void CrFbDisplayComposite::UpdateEnd(struct CR_FRAMEBUFFER *pFb) +{ + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + pIter->UpdateEnd(pFb); + } + + CrFbDisplayBase::UpdateEnd(pFb); +} + + +int CrFbDisplayComposite::EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryAdded(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryAdded(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; +} + + +int CrFbDisplayComposite::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryAdded(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryCreated(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; +} + + +int CrFbDisplayComposite::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) +{ + int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryReplaced(pFb, hNewEntry, hReplacedEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayComposite::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryTexChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; +} + + +int CrFbDisplayComposite::EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryRemoved(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryRemoved(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; +} + + +int CrFbDisplayComposite::EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryDestroyed(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->EntryDestroyed(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayComposite::RegionsChanged(struct CR_FRAMEBUFFER *pFb) +{ + int rc = CrFbDisplayBase::RegionsChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->RegionsChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + return VINF_SUCCESS; +} + + +int CrFbDisplayComposite::FramebufferChanged(struct CR_FRAMEBUFFER *pFb) +{ + int rc = CrFbDisplayBase::FramebufferChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + CrFbDisplayBase *pIter; + RTListForEachCpp(&mDisplays, pIter, CrFbDisplayBase, mNode) + { + int rc = pIter->FramebufferChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + return VINF_SUCCESS; +} + + +CrFbDisplayComposite::~CrFbDisplayComposite() +{ + cleanup(); +} + + +void CrFbDisplayComposite::cleanup(bool fCleanupDisplays) +{ + CrFbDisplayBase *pIter, *pIterNext; + RTListForEachSafeCpp(&mDisplays, pIter, pIterNext, CrFbDisplayBase, mNode) + { + remove(pIter, fCleanupDisplays); + } +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_vrdp.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_vrdp.cpp new file mode 100644 index 00000000..908117d4 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_vrdp.cpp @@ -0,0 +1,381 @@ +/* $Id: display_vrdp.cpp $ */ + +/** @file + * Presenter API: CrFbDisplayVrdp class implementation -- display content over VRDP. + */ + +/* + * Copyright (C) 2014-2019 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. + */ + +#include "server_presenter.h" + + +CrFbDisplayVrdp::CrFbDisplayVrdp() +{ + memset(&mPos, 0, sizeof (mPos)); +} + + +int CrFbDisplayVrdp::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryCreated(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("EntryAdded failed rc %d", rc)); + return rc; + } + + Assert(!CrFbDDataEntryGet(hEntry, slotGet())); + rc = vrdpCreate(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("vrdpCreate failed rc %d", rc)); + return rc; + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayVrdp::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) +{ + int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pReplacedEntry = CrFbEntryGetCompositorEntry(hReplacedEntry); + CR_TEXDATA *pReplacedTex = CrVrScrCompositorEntryTexGet(pReplacedEntry); + const VBOXVR_SCR_COMPOSITOR_ENTRY* pNewEntry = CrFbEntryGetCompositorEntry(hNewEntry); + CR_TEXDATA *pNewTex = CrVrScrCompositorEntryTexGet(pNewEntry); + + CrTdBltDataInvalidateNe(pReplacedTex); + + rc = CrTdBltEnter(pNewTex); + if (RT_SUCCESS(rc)) + { + rc = vrdpFrame(hNewEntry); + CrTdBltLeave(pNewTex); + } + else + WARN(("CrTdBltEnter failed %d", rc)); + + return rc; +} + + +int CrFbDisplayVrdp::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry); + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry); + + rc = CrTdBltEnter(pTex); + if (RT_SUCCESS(rc)) + { + rc = vrdpFrame(hEntry); + CrTdBltLeave(pTex); + } + else + WARN(("CrTdBltEnter failed %d", rc)); + + return rc; +} + + +int CrFbDisplayVrdp::EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryRemoved(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry); + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry); + CrTdBltDataInvalidateNe(pTex); + + return vrdpRegions(pFb, hEntry); +} + + +int CrFbDisplayVrdp::EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryDestroyed(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + vrdpDestroy(hEntry); + return VINF_SUCCESS; +} + + +int CrFbDisplayVrdp::EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryPosChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + vrdpGeometry(hEntry); + + return VINF_SUCCESS; +} + + +int CrFbDisplayVrdp::RegionsChanged(struct CR_FRAMEBUFFER *pFb) +{ + int rc = CrFbDisplayBase::RegionsChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return vrdpRegionsAll(pFb); +} + + +int CrFbDisplayVrdp::FramebufferChanged(struct CR_FRAMEBUFFER *pFb) +{ + int rc = CrFbDisplayBase::FramebufferChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + syncPos(); + + rc = vrdpSyncEntryAll(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return vrdpRegionsAll(pFb); +} + + +void CrFbDisplayVrdp::syncPos() +{ + const struct VBVAINFOSCREEN* pScreenInfo = CrFbGetScreenInfo(getFramebuffer()); + mPos.x = pScreenInfo->i32OriginX; + mPos.y = pScreenInfo->i32OriginY; +} + +int CrFbDisplayVrdp::fbCleanup() +{ + int rc = fbCleanupRemoveAllEntries(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return CrFbDisplayBase::fbCleanup(); +} + + +int CrFbDisplayVrdp::fbSync() +{ + syncPos(); + + int rc = fbSynchAddAllEntries(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return CrFbDisplayBase::fbSync(); +} + + +void CrFbDisplayVrdp::vrdpDestroy(HCR_FRAMEBUFFER_ENTRY hEntry) +{ + void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet()); + cr_server.outputRedirect.CROREnd(pVrdp); +} + + +void CrFbDisplayVrdp::vrdpGeometry(HCR_FRAMEBUFFER_ENTRY hEntry) +{ + void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet()); + const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry); + + cr_server.outputRedirect.CRORGeometry( + pVrdp, + mPos.x + CrVrScrCompositorEntryRectGet(pEntry)->xLeft, + mPos.y + CrVrScrCompositorEntryRectGet(pEntry)->yTop, + CrVrScrCompositorEntryTexGet(pEntry)->Tex.width, + CrVrScrCompositorEntryTexGet(pEntry)->Tex.height); +} + + +int CrFbDisplayVrdp::vrdpRegions(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet()); + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb); + const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry); + uint32_t cRects; + const RTRECT *pRects; + + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRects, &pRects, NULL, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorEntryRegionsGet failed, rc %d", rc)); + return rc; + } + + cr_server.outputRedirect.CRORVisibleRegion(pVrdp, cRects, pRects); + return VINF_SUCCESS; +} + + +int CrFbDisplayVrdp::vrdpFrame(HCR_FRAMEBUFFER_ENTRY hEntry) +{ + void *pVrdp = CrFbDDataEntryGet(hEntry, slotGet()); + const VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry = CrFbEntryGetCompositorEntry(hEntry); + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry); + const CR_BLITTER_IMG *pImg; + CrTdBltDataInvalidateNe(pTex); + + int rc = CrTdBltDataAcquire(pTex, GL_BGRA, !!(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS), &pImg); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltDataAcquire failed rc %d", rc)); + return rc; + } + + cr_server.outputRedirect.CRORFrame(pVrdp, pImg->pvData, pImg->cbData); + CrTdBltDataRelease(pTex); + return VINF_SUCCESS; +} + + +int CrFbDisplayVrdp::vrdpRegionsAll(struct CR_FRAMEBUFFER *pFb) +{ + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb); + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + CrVrScrCompositorConstIterInit(pCompositor, &Iter); + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + vrdpRegions(pFb, hEntry); + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayVrdp::vrdpSynchEntry(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + vrdpGeometry(hEntry); + + return vrdpRegions(pFb, hEntry);; +} + + +int CrFbDisplayVrdp::vrdpSyncEntryAll(struct CR_FRAMEBUFFER *pFb) +{ + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(pFb); + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + CrVrScrCompositorConstIterInit(pCompositor, &Iter); + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + int rc = vrdpSynchEntry(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("vrdpSynchEntry failed rc %d", rc)); + return rc; + } + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayVrdp::vrdpCreate(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + void *pVrdp; + + /* Query supported formats. */ + uint32_t cbFormats = 4096; + char *pachFormats = (char *)crAlloc(cbFormats); + + if (!pachFormats) + { + WARN(("crAlloc failed")); + return VERR_NO_MEMORY; + } + + int rc = cr_server.outputRedirect.CRORContextProperty(cr_server.outputRedirect.pvContext, + 0 /* H3DOR_PROP_FORMATS */, /// @todo from a header + pachFormats, cbFormats, &cbFormats); + if (RT_SUCCESS(rc)) + { + if (RTStrStr(pachFormats, "H3DOR_FMT_RGBA_TOPDOWN")) + { + cr_server.outputRedirect.CRORBegin( + cr_server.outputRedirect.pvContext, + &pVrdp, + "H3DOR_FMT_RGBA_TOPDOWN"); /// @todo from a header + + if (pVrdp) + { + rc = CrFbDDataEntryPut(hEntry, slotGet(), pVrdp); + if (RT_SUCCESS(rc)) + { + vrdpGeometry(hEntry); + vrdpRegions(hFb, hEntry); + //vrdpFrame(hEntry); + return VINF_SUCCESS; + } + else + WARN(("CrFbDDataEntryPut failed rc %d", rc)); + + cr_server.outputRedirect.CROREnd(pVrdp); + } + else + { + WARN(("CRORBegin failed")); + rc = VERR_GENERAL_FAILURE; + } + } + } + else + WARN(("CRORContextProperty failed rc %d", rc)); + + crFree(pachFormats); + + return rc; +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window.cpp new file mode 100644 index 00000000..c888987c --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window.cpp @@ -0,0 +1,574 @@ +/* $Id: display_window.cpp $ */ + +/** @file + * Presenter API: CrFbDisplayWindow class implementation -- display content into host GUI window. + */ + +/* + * Copyright (C) 2014-2019 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. + */ + +#include "server_presenter.h" + +CrFbDisplayWindow::CrFbDisplayWindow(const RTRECT *pViewportRect, uint64_t parentId) : + mpWindow(NULL), + mViewportRect(*pViewportRect), + mu32Screen(~0), + mParentId(parentId) +{ + mFlags.u32Value = 0; +} + + +CrFbDisplayWindow::~CrFbDisplayWindow() +{ + if (mpWindow) + delete mpWindow; +} + + +int CrFbDisplayWindow::UpdateBegin(struct CR_FRAMEBUFFER *pFb) +{ + int rc = mpWindow ? mpWindow->UpdateBegin() : VINF_SUCCESS; + if (RT_SUCCESS(rc)) + { + rc = CrFbDisplayBase::UpdateBegin(pFb); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + else + { + WARN(("err")); + if (mpWindow) + mpWindow->UpdateEnd(); + } + } + else + WARN(("err")); + + return rc; +} + + +void CrFbDisplayWindow::UpdateEnd(struct CR_FRAMEBUFFER *pFb) +{ + CrFbDisplayBase::UpdateEnd(pFb); + + if (mpWindow) + mpWindow->UpdateEnd(); +} + + +int CrFbDisplayWindow::RegionsChanged(struct CR_FRAMEBUFFER *pFb) +{ + int rc = CrFbDisplayBase::RegionsChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + if (mpWindow && mpWindow->GetParentId()) + { + rc = mpWindow->Create(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindow::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryCreated(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + if (mpWindow && mpWindow->GetParentId()) + { + rc = mpWindow->Create(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindow::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) +{ + int rc = CrFbDisplayBase::EntryReplaced(pFb, hNewEntry, hReplacedEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + if (mpWindow && mpWindow->GetParentId()) + { + rc = mpWindow->Create(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindow::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayBase::EntryTexChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + if (mpWindow && mpWindow->GetParentId()) + { + rc = mpWindow->Create(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindow::FramebufferChanged(struct CR_FRAMEBUFFER *pFb) +{ + int rc = CrFbDisplayBase::FramebufferChanged(pFb); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return screenChanged(); +} + + +const RTRECT* CrFbDisplayWindow::getViewportRect() +{ + return &mViewportRect; +} + + +int CrFbDisplayWindow::setViewportRect(const RTRECT *pViewportRect) +{ + if (!isUpdating()) + { + WARN(("not updating!")); + return VERR_INVALID_STATE; + } + + // always call SetPosition to ensure window is adjustep properly + // if (pViewportRect->xLeft != mViewportRect.xLeft || pViewportRect->yTop != mViewportRect.yTop) + if (mpWindow) + { + const RTRECT* pRect = getRect(); + int rc = mpWindow->SetPosition(pRect->xLeft - pViewportRect->xLeft, pRect->yTop - pViewportRect->yTop); + if (!RT_SUCCESS(rc)) + { + WARN(("SetPosition failed")); + return rc; + } + } + + mViewportRect = *pViewportRect; + + return VINF_SUCCESS; +} + + +CrFbWindow * CrFbDisplayWindow::windowDetach(bool fCleanup) +{ + if (isUpdating()) + { + WARN(("updating!")); + return NULL; + } + + CrFbWindow * pWindow = mpWindow; + if (mpWindow) + { + if (fCleanup) + windowCleanup(); + mpWindow = NULL; + } + return pWindow; +} + + +CrFbWindow * CrFbDisplayWindow::windowAttach(CrFbWindow * pNewWindow) +{ + if (isUpdating()) + { + WARN(("updating!")); + return NULL; + } + + CrFbWindow * pOld = mpWindow; + if (mpWindow) + windowDetach(); + + mpWindow = pNewWindow; + if (pNewWindow) + windowSync(); + + return mpWindow; +} + + +int CrFbDisplayWindow::reparent(uint64_t parentId) +{ + if (!isUpdating()) + { + WARN(("not updating!")); + return VERR_INVALID_STATE; + } + + crDebug("CrFbDisplayWindow: change parent from %p to %p.", mParentId, parentId); + + mParentId = parentId; + int rc = VINF_SUCCESS; + + /* Force notify Render SPU about parent window ID change in order to prevent + * crashes when it tries to access already deallocated parent window. + * Previously, we also used isActive() here, however it might become FALSE for the case + * when VM Window goes fullscreen mode and back. */ + if ( /* isActive() && */ mpWindow) + { + rc = mpWindow->Reparent(parentId); + if (!RT_SUCCESS(rc)) + WARN(("window reparent failed")); + + mFlags.fNeForce = 1; + } + + return rc; +} + + +bool CrFbDisplayWindow::isVisible() +{ + HCR_FRAMEBUFFER hFb = getFramebuffer(); + if (!hFb) + return false; + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(hFb); + return !CrVrScrCompositorIsEmpty(pCompositor); +} + + +int CrFbDisplayWindow::winVisibilityChanged() +{ + HCR_FRAMEBUFFER hFb = getFramebuffer(); + if (!hFb || !CrFbIsEnabled(hFb)) + { + Assert(!mpWindow || !mpWindow->IsVisivle()); + return VINF_SUCCESS; + } + + int rc = VINF_SUCCESS; + + if (mpWindow) + { + rc = mpWindow->UpdateBegin(); + if (RT_SUCCESS(rc)) + { + rc = mpWindow->SetVisible(!g_CrPresenter.fWindowsForceHidden); + if (!RT_SUCCESS(rc)) + WARN(("SetVisible failed, rc %d", rc)); + + mpWindow->UpdateEnd(); + } + else + WARN(("UpdateBegin failed, rc %d", rc)); + } + + return rc; +} + + +CrFbWindow* CrFbDisplayWindow::getWindow() +{ + return mpWindow; +} + + +void CrFbDisplayWindow::onUpdateEnd() +{ + CrFbDisplayBase::onUpdateEnd(); + bool fVisible = isVisible(); + if (mFlags.fNeVisible != fVisible || mFlags.fNeForce) + { + crVBoxServerNotifyEvent(mu32Screen, + fVisible? VBOX3D_NOTIFY_EVENT_TYPE_3DDATA_VISIBLE: + VBOX3D_NOTIFY_EVENT_TYPE_3DDATA_HIDDEN, + NULL, 0); + mFlags.fNeVisible = fVisible; + mFlags.fNeForce = 0; + } +} + + +void CrFbDisplayWindow::ueRegions() +{ + if (mpWindow) + mpWindow->SetVisibleRegionsChanged(); +} + + +int CrFbDisplayWindow::screenChanged() +{ + if (!isUpdating()) + { + WARN(("not updating!")); + return VERR_INVALID_STATE; + } + + int rc = windowDimensionsSync(); + if (!RT_SUCCESS(rc)) + { + WARN(("windowDimensionsSync failed rc %d", rc)); + return rc; + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindow::windowSetCompositor(bool fSet) +{ + if (!mpWindow) + return VINF_SUCCESS; + + if (fSet) + { + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(getFramebuffer()); + return mpWindow->SetCompositor(pCompositor); + } + return mpWindow->SetCompositor(NULL); +} + + +int CrFbDisplayWindow::windowCleanup() +{ + if (!mpWindow) + return VINF_SUCCESS; + + int rc = mpWindow->UpdateBegin(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = windowDimensionsSync(true); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + mpWindow->UpdateEnd(); + return rc; + } + + rc = windowSetCompositor(false); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + mpWindow->UpdateEnd(); + return rc; + } + + mpWindow->UpdateEnd(); + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindow::fbCleanup() +{ + int rc = windowCleanup(); + if (!RT_SUCCESS(rc)) + { + WARN(("windowCleanup failed")); + return rc; + } + return CrFbDisplayBase::fbCleanup(); +} + + +bool CrFbDisplayWindow::isActive() +{ + HCR_FRAMEBUFFER hFb = getFramebuffer(); + return hFb && CrFbIsEnabled(hFb); +} + + +int CrFbDisplayWindow::windowDimensionsSync(bool fForceCleanup) +{ + int rc = VINF_SUCCESS; + + if (!mpWindow) + return VINF_SUCCESS; + + //HCR_FRAMEBUFFER hFb = getFramebuffer(); + if (!fForceCleanup && isActive()) + { + const RTRECT* pRect = getRect(); + + if (mpWindow->GetParentId() != mParentId) + { + rc = mpWindow->Reparent(mParentId); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + + rc = mpWindow->SetPosition(pRect->xLeft - mViewportRect.xLeft, pRect->yTop - mViewportRect.yTop); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + setRegionsChanged(); + + rc = mpWindow->SetSize((uint32_t)(pRect->xRight - pRect->xLeft), (uint32_t)(pRect->yBottom - pRect->yTop)); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = mpWindow->SetVisible(!g_CrPresenter.fWindowsForceHidden); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + else + { + rc = mpWindow->SetVisible(false); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } +#if 0 + rc = mpWindow->Reparent(mDefaultParentId); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } +#endif + } + + return rc; +} + + +int CrFbDisplayWindow::windowSync() +{ + if (!mpWindow) + return VINF_SUCCESS; + + int rc = mpWindow->UpdateBegin(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = windowSetCompositor(true); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + mpWindow->UpdateEnd(); + return rc; + } + + rc = windowDimensionsSync(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + mpWindow->UpdateEnd(); + return rc; + } + + mpWindow->UpdateEnd(); + + return rc; +} + + +int CrFbDisplayWindow::fbSync() +{ + int rc = CrFbDisplayBase::fbSync(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + HCR_FRAMEBUFFER hFb = getFramebuffer(); + + mu32Screen = CrFbGetScreenInfo(hFb)->u32ViewIndex; + + rc = windowSync(); + if (!RT_SUCCESS(rc)) + { + WARN(("windowSync failed %d", rc)); + return rc; + } + + if (CrFbHas3DData(hFb)) + { + if (mpWindow && mpWindow->GetParentId()) + { + rc = mpWindow->Create(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + } + } + + return VINF_SUCCESS; +} + + +const struct RTRECT* CrFbDisplayWindow::getRect() +{ + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(getFramebuffer()); + return CrVrScrCompositorRectGet(pCompositor); +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window_rootvr.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window_rootvr.cpp new file mode 100644 index 00000000..ddc76848 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/display_window_rootvr.cpp @@ -0,0 +1,347 @@ +/* $Id: display_window_rootvr.cpp $ */ + +/** @file + * Presenter API: CrFbDisplayWindowRootVr class implementation -- display seamless content (visible regions). + */ + +/* + * Copyright (C) 2014-2019 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. + */ + +#include "server_presenter.h" + + +CrFbDisplayWindowRootVr::CrFbDisplayWindowRootVr(const RTRECT *pViewportRect, uint64_t parentId) : + CrFbDisplayWindow(pViewportRect, parentId) +{ + CrVrScrCompositorInit(&mCompositor, NULL); +} + + +int CrFbDisplayWindowRootVr::EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayWindow::EntryCreated(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + Assert(!CrFbDDataEntryGet(hEntry, slotGet())); + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = entryAlloc(); + CrVrScrCompositorEntryInit(pMyEntry, CrVrScrCompositorEntryRectGet(pSrcEntry), CrVrScrCompositorEntryTexGet(pSrcEntry), NULL); + CrVrScrCompositorEntryFlagsSet(pMyEntry, CrVrScrCompositorEntryFlagsGet(pSrcEntry)); + rc = CrFbDDataEntryPut(hEntry, slotGet(), pMyEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbDDataEntryPut failed rc %d", rc)); + entryFree(pMyEntry); + return rc; + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindowRootVr::EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayWindow::EntryAdded(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet()); + Assert(pMyEntry); + CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcEntry)); + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindowRootVr::EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) +{ + int rc = CrFbDisplayWindow::EntryReplaced(pFb, hNewEntry, hReplacedEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcNewEntry = CrFbEntryGetCompositorEntry(hNewEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hNewEntry, slotGet()); + CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcNewEntry)); + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindowRootVr::EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayWindow::EntryTexChanged(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet()); + CrVrScrCompositorEntryTexSet(pMyEntry, CrVrScrCompositorEntryTexGet(pSrcEntry)); + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindowRootVr::EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayWindow::EntryRemoved(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet()); + rc = CrVrScrCompositorEntryRegionsSet(&mCompositor, pMyEntry, NULL, 0, NULL, false, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindowRootVr::EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + int rc = CrFbDisplayWindow::EntryDestroyed(pFb, hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + const VBOXVR_SCR_COMPOSITOR_ENTRY* pSrcEntry = CrFbEntryGetCompositorEntry(hEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, slotGet()); + CrVrScrCompositorEntryCleanup(pMyEntry); + entryFree(pMyEntry); + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindowRootVr::setViewportRect(const RTRECT *pViewportRect) +{ + int rc = CrFbDisplayWindow::setViewportRect(pViewportRect); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = setRegionsChanged(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindowRootVr::windowSetCompositor(bool fSet) +{ + if (fSet) + return getWindow()->SetCompositor(&mCompositor); + return getWindow()->SetCompositor(NULL); +} + + +void CrFbDisplayWindowRootVr::ueRegions() +{ + synchCompositorRegions(); +} + + +int CrFbDisplayWindowRootVr::compositorMarkUpdated() +{ + CrVrScrCompositorClear(&mCompositor); + + int rc = CrVrScrCompositorRectSet(&mCompositor, CrVrScrCompositorRectGet(CrFbGetCompositor(getFramebuffer())), NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = setRegionsChanged(); + if (!RT_SUCCESS(rc)) + { + WARN(("screenChanged failed %d", rc)); + return rc; + } + + return VINF_SUCCESS; +} + + +int CrFbDisplayWindowRootVr::screenChanged() +{ + int rc = compositorMarkUpdated(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + rc = CrFbDisplayWindow::screenChanged(); + if (!RT_SUCCESS(rc)) + { + WARN(("screenChanged failed %d", rc)); + return rc; + } + + return VINF_SUCCESS; +} + + +const struct RTRECT* CrFbDisplayWindowRootVr::getRect() +{ + return CrVrScrCompositorRectGet(&mCompositor); +} + +int CrFbDisplayWindowRootVr::fbCleanup() +{ + int rc = clearCompositor(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return CrFbDisplayWindow::fbCleanup(); +} + + +int CrFbDisplayWindowRootVr::fbSync() +{ + int rc = synchCompositor(); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return CrFbDisplayWindow::fbSync(); +} + + +VBOXVR_SCR_COMPOSITOR_ENTRY* CrFbDisplayWindowRootVr::entryAlloc() +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + return (VBOXVR_SCR_COMPOSITOR_ENTRY*)RTMemCacheAlloc(g_CrPresenter.CEntryLookasideList); +#else + return (VBOXVR_SCR_COMPOSITOR_ENTRY*)RTMemAlloc(sizeof (VBOXVR_SCR_COMPOSITOR_ENTRY)); +#endif +} + + +void CrFbDisplayWindowRootVr::entryFree(VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry) +{ + Assert(!CrVrScrCompositorEntryIsUsed(pEntry)); +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheFree(g_CrPresenter.CEntryLookasideList, pEntry); +#else + RTMemFree(pEntry); +#endif +} + + +int CrFbDisplayWindowRootVr::synchCompositorRegions() +{ + int rc; + + rootVrTranslateForPos(); + + /* ensure the rootvr compositor does not hold any data, + * i.e. cleanup all rootvr entries data */ + CrVrScrCompositorClear(&mCompositor); + + rc = CrVrScrCompositorIntersectedList(CrFbGetCompositor(getFramebuffer()), &cr_server.RootVr, &mCompositor, rootVrGetCEntry, this, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorIntersectedList failed, rc %d", rc)); + return rc; + } + + return getWindow()->SetVisibleRegionsChanged(); +} + + +int CrFbDisplayWindowRootVr::synchCompositor() +{ + int rc = compositorMarkUpdated(); + if (!RT_SUCCESS(rc)) + { + WARN(("compositorMarkUpdated failed, rc %d", rc)); + return rc; + } + + rc = fbSynchAddAllEntries(); + if (!RT_SUCCESS(rc)) + { + WARN(("fbSynchAddAllEntries failed, rc %d", rc)); + return rc; + } + + return rc; +} + + +int CrFbDisplayWindowRootVr::clearCompositor() +{ + return fbCleanupRemoveAllEntries(); +} + + +void CrFbDisplayWindowRootVr::rootVrTranslateForPos() +{ + const RTRECT *pRect = getViewportRect(); + const struct VBVAINFOSCREEN* pScreen = CrFbGetScreenInfo(getFramebuffer()); + int32_t x = pScreen->i32OriginX; + int32_t y = pScreen->i32OriginY; + int32_t dx = cr_server.RootVrCurPoint.x - x; + int32_t dy = cr_server.RootVrCurPoint.y - y; + + cr_server.RootVrCurPoint.x = x; + cr_server.RootVrCurPoint.y = y; + + VBoxVrListTranslate(&cr_server.RootVr, dx, dy); +} + + +DECLCALLBACK(VBOXVR_SCR_COMPOSITOR_ENTRY*) CrFbDisplayWindowRootVr::rootVrGetCEntry(const VBOXVR_SCR_COMPOSITOR_ENTRY*pEntry, void *pvContext) +{ + CrFbDisplayWindowRootVr *pThis = (CrFbDisplayWindowRootVr*)pvContext; + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + VBOXVR_SCR_COMPOSITOR_ENTRY *pMyEntry = (VBOXVR_SCR_COMPOSITOR_ENTRY*)CrFbDDataEntryGet(hEntry, pThis->slotGet()); + Assert(!CrVrScrCompositorEntryIsUsed(pMyEntry)); + CrVrScrCompositorEntryRectSet(&pThis->mCompositor, pMyEntry, CrVrScrCompositorEntryRectGet(pEntry)); + return pMyEntry; +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.cpp new file mode 100644 index 00000000..7ced10de --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.cpp @@ -0,0 +1,4063 @@ +/* $Id: server_presenter.cpp $ */ + +/** @file + * Presenter API + */ + +/* + * Copyright (C) 2012-2019 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. + */ + +#ifdef DEBUG_misha +# define VBOXVDBG_MEMCACHE_DISABLE +#endif + +#ifndef VBOXVDBG_MEMCACHE_DISABLE +# include <iprt/memcache.h> +#endif + +#include "server_presenter.h" + +//#define CR_SERVER_WITH_CLIENT_CALLOUTS + +#define PCR_FBTEX_FROM_TEX(_pTex) ((CR_FBTEX*)((uint8_t*)(_pTex) - RT_UOFFSETOF(CR_FBTEX, Tex))) +#define PCR_FRAMEBUFFER_FROM_COMPOSITOR(_pCompositor) ((CR_FRAMEBUFFER*)((uint8_t*)(_pCompositor) - RT_UOFFSETOF(CR_FRAMEBUFFER, Compositor))) +#define PCR_FBENTRY_FROM_ENTRY(_pEntry) ((CR_FRAMEBUFFER_ENTRY*)((uint8_t*)(_pEntry) - RT_UOFFSETOF(CR_FRAMEBUFFER_ENTRY, Entry))) + + +static int crPMgrFbConnectTargetDisplays(HCR_FRAMEBUFFER hFb, CR_FBDISPLAY_INFO *pDpInfo, uint32_t u32ModeAdd); + +CR_PRESENTER_GLOBALS g_CrPresenter; + +/* FRAMEBUFFER */ + +void CrFbInit(CR_FRAMEBUFFER *pFb, uint32_t idFb) +{ + RTRECT Rect; + Rect.xLeft = 0; + Rect.yTop = 0; + Rect.xRight = 1; + Rect.yBottom = 1; + memset(pFb, 0, sizeof (*pFb)); + pFb->ScreenInfo.u16Flags = VBVA_SCREEN_F_DISABLED; + pFb->ScreenInfo.u32ViewIndex = idFb; + CrVrScrCompositorInit(&pFb->Compositor, &Rect); + RTListInit(&pFb->EntriesList); + CrHTableCreate(&pFb->SlotTable, 0); +} + + +bool CrFbIsEnabled(CR_FRAMEBUFFER *pFb) +{ + return !(pFb->ScreenInfo.u16Flags & VBVA_SCREEN_F_DISABLED); +} + + +const struct VBOXVR_SCR_COMPOSITOR* CrFbGetCompositor(CR_FRAMEBUFFER *pFb) +{ + return &pFb->Compositor; +} + +DECLINLINE(CR_FRAMEBUFFER*) CrFbFromCompositor(const struct VBOXVR_SCR_COMPOSITOR* pCompositor) +{ + return RT_FROM_MEMBER(pCompositor, CR_FRAMEBUFFER, Compositor); +} + +const struct VBVAINFOSCREEN* CrFbGetScreenInfo(HCR_FRAMEBUFFER hFb) +{ + return &hFb->ScreenInfo; +} + +void* CrFbGetVRAM(HCR_FRAMEBUFFER hFb) +{ + return hFb->pvVram; +} + +int CrFbUpdateBegin(CR_FRAMEBUFFER *pFb) +{ + ++pFb->cUpdating; + + if (pFb->cUpdating == 1) + { + if (pFb->pDisplay) + pFb->pDisplay->UpdateBegin(pFb); + } + + return VINF_SUCCESS; +} + +void CrFbUpdateEnd(CR_FRAMEBUFFER *pFb) +{ + if (!pFb->cUpdating) + { + WARN(("invalid UpdateEnd call!")); + return; + } + + --pFb->cUpdating; + + if (!pFb->cUpdating) + { + if (pFb->pDisplay) + pFb->pDisplay->UpdateEnd(pFb); + } +} + +bool CrFbIsUpdating(const CR_FRAMEBUFFER *pFb) +{ + return !!pFb->cUpdating; +} + +bool CrFbHas3DData(HCR_FRAMEBUFFER hFb) +{ + return !CrVrScrCompositorIsEmpty(&hFb->Compositor); +} + +static void crFbImgFromScreenVram(const VBVAINFOSCREEN *pScreen, void *pvVram, CR_BLITTER_IMG *pImg) +{ + pImg->pvData = pvVram; + pImg->cbData = pScreen->u32LineSize * pScreen->u32Height; + pImg->enmFormat = GL_BGRA; + pImg->width = pScreen->u32Width; + pImg->height = pScreen->u32Height; + pImg->bpp = pScreen->u16BitsPerPixel; + pImg->pitch = pScreen->u32LineSize; +} + +static void crFbImgFromDimPtrBGRA(void *pvVram, uint32_t width, uint32_t height, CR_BLITTER_IMG *pImg) +{ + pImg->pvData = pvVram; + pImg->cbData = width * height * 4; + pImg->enmFormat = GL_BGRA; + pImg->width = width; + pImg->height = height; + pImg->bpp = 32; + pImg->pitch = width * 4; +} + +static int8_t crFbImgFromDimOffVramBGRA(VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, CR_BLITTER_IMG *pImg) +{ + uint32_t cbBuff = width * height * 4; + if (offVRAM >= g_cbVRam + || offVRAM + cbBuff >= g_cbVRam) + { + WARN(("invalid param")); + return -1; + } + + uint8_t *pu8Buf = g_pvVRamBase + offVRAM; + crFbImgFromDimPtrBGRA(pu8Buf, width, height, pImg); + + return 0; +} + +static int8_t crFbImgFromDescBGRA(const VBOXCMDVBVA_ALLOCDESC *pDesc, CR_BLITTER_IMG *pImg) +{ + return crFbImgFromDimOffVramBGRA(pDesc->Info.u.offVRAM, pDesc->u16Width, pDesc->u16Height, pImg); +} + +static void crFbImgFromFb(HCR_FRAMEBUFFER hFb, CR_BLITTER_IMG *pImg) +{ + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + void *pvVram = CrFbGetVRAM(hFb); + crFbImgFromScreenVram(pScreen, pvVram, pImg); +} + +static int crFbTexDataGetContents(CR_TEXDATA *pTex, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst) +{ + const CR_BLITTER_IMG *pSrcImg; + int rc = CrTdBltDataAcquire(pTex, GL_BGRA, false, &pSrcImg); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltDataAcquire failed rc %d", rc)); + return rc; + } + + CrMBltImg(pSrcImg, pPos, cRects, pRects, pDst); + + CrTdBltDataRelease(pTex); + + return VINF_SUCCESS; +} + +static int crFbBltGetContentsScaledDirect(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst) +{ + VBOXVR_LIST List; + uint32_t c2DRects = 0; + CR_TEXDATA *pEnteredTex = NULL; + PCR_BLITTER pEnteredBlitter = NULL; + + /* Scaled texture size and rect calculated for every new "entered" texture. */ + uint32_t width = 0, height = 0; + RTRECT ScaledSrcRect = {0}; + + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + int32_t srcWidth = pSrcRectSize->cx; + int32_t srcHeight = pSrcRectSize->cy; + int32_t dstWidth = pDstRect->xRight - pDstRect->xLeft; + int32_t dstHeight = pDstRect->yBottom - pDstRect->yTop; + + RTPOINT DstPoint = {pDstRect->xLeft, pDstRect->yTop}; + float strX = ((float)dstWidth) / srcWidth; + float strY = ((float)dstHeight) / srcHeight; + bool fScale = (dstWidth != srcWidth || dstHeight != srcHeight); + Assert(fScale); + + /* 'List' contains the destination rectangles to be updated (in pDst coords). */ + VBoxVrListInit(&List); + int rc = VBoxVrListRectsAdd(&List, cRects, pRects, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsAdd failed rc %d", rc)); + goto end; + } + + CrVrScrCompositorConstIterInit(&hFb->Compositor, &Iter); + + for(const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrVrScrCompositorConstIterNext(&Iter); + pEntry; + pEntry = CrVrScrCompositorConstIterNext(&Iter)) + { + /* Where the entry would be located in pDst coords, i.e. convert pEntry hFb coord to pDst coord. */ + RTPOINT ScaledEntryPoint; + ScaledEntryPoint.x = CR_FLOAT_RCAST(int32_t, strX * CrVrScrCompositorEntryRectGet(pEntry)->xLeft) + pDstRect->xLeft; + ScaledEntryPoint.y = CR_FLOAT_RCAST(int32_t, strY * CrVrScrCompositorEntryRectGet(pEntry)->yTop) + pDstRect->yTop; + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry); + + /* Optimization to avoid entering/leaving the same texture and its blitter. */ + if (pEnteredTex != pTex) + { + if (!pEnteredBlitter) + { + pEnteredBlitter = CrTdBlitterGet(pTex); + rc = CrBltEnter(pEnteredBlitter); + if (!RT_SUCCESS(rc)) + { + WARN(("CrBltEnter failed %d", rc)); + pEnteredBlitter = NULL; + goto end; + } + } + + if (pEnteredTex) + { + CrTdBltLeave(pEnteredTex); + + pEnteredTex = NULL; + + if (pEnteredBlitter != CrTdBlitterGet(pTex)) + { + WARN(("blitters not equal!")); + CrBltLeave(pEnteredBlitter); + + pEnteredBlitter = CrTdBlitterGet(pTex); + rc = CrBltEnter(pEnteredBlitter); + if (!RT_SUCCESS(rc)) + { + WARN(("CrBltEnter failed %d", rc)); + pEnteredBlitter = NULL; + goto end; + } + } + } + + rc = CrTdBltEnter(pTex); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltEnter failed %d", rc)); + goto end; + } + + pEnteredTex = pTex; + + const VBOXVR_TEXTURE *pVrTex = CrTdTexGet(pTex); + + width = CR_FLOAT_RCAST(uint32_t, strX * pVrTex->width); + height = CR_FLOAT_RCAST(uint32_t, strY * pVrTex->height); + ScaledSrcRect.xLeft = ScaledEntryPoint.x; + ScaledSrcRect.yTop = ScaledEntryPoint.y; + ScaledSrcRect.xRight = width + ScaledEntryPoint.x; + ScaledSrcRect.yBottom = height + ScaledEntryPoint.y; + } + + bool fInvert = !(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS); + + /* pRegions is where the pEntry was drawn in hFb coords. */ + uint32_t cRegions; + const RTRECT *pRegions; + rc = CrVrScrCompositorEntryRegionsGet(&hFb->Compositor, pEntry, &cRegions, NULL, NULL, &pRegions); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", rc)); + goto end; + } + + /* CrTdBltDataAcquireScaled/CrTdBltDataReleaseScaled can use cached data, + * so it is not necessary to optimize and Aquire only when Tex changes. + */ + const CR_BLITTER_IMG *pSrcImg; + rc = CrTdBltDataAcquireScaled(pTex, GL_BGRA, false, width, height, &pSrcImg); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltDataAcquire failed rc %d", rc)); + goto end; + } + + for (uint32_t j = 0; j < cRegions; ++j) + { + /* rects are in dst coordinates, + * while the pReg is in source coords + * convert */ + const RTRECT * pReg = &pRegions[j]; + RTRECT ScaledReg; + /* scale */ + VBoxRectScaled(pReg, strX, strY, &ScaledReg); + /* translate */ + VBoxRectTranslate(&ScaledReg, pDstRect->xLeft, pDstRect->yTop); + + /* Exclude the pEntry rectangle, because it will be updated now in pDst. + * List uses dst coords and pRegions use hFb coords, therefore use + * ScaledReg which is already translated to dst. + */ + rc = VBoxVrListRectsSubst(&List, 1, &ScaledReg, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsSubst failed rc %d", rc)); + goto end; + } + + for (uint32_t i = 0; i < cRects; ++i) + { + const RTRECT * pRect = &pRects[i]; + + RTRECT Intersection; + VBoxRectIntersected(pRect, &ScaledReg, &Intersection); + if (VBoxRectIsZero(&Intersection)) + continue; + + VBoxRectIntersect(&Intersection, &ScaledSrcRect); + if (VBoxRectIsZero(&Intersection)) + continue; + + CrMBltImgRect(pSrcImg, &ScaledEntryPoint, fInvert, &Intersection, pDst); + } + } + + CrTdBltDataReleaseScaled(pTex, pSrcImg); + } + + /* Blit still not updated dst rects, i.e. not covered by 3D entries. */ + c2DRects = VBoxVrListRectsCount(&List); + if (c2DRects) + { + if (g_CrPresenter.cbTmpBuf2 < c2DRects * sizeof (RTRECT)) + { + if (g_CrPresenter.pvTmpBuf2) + RTMemFree(g_CrPresenter.pvTmpBuf2); + + g_CrPresenter.cbTmpBuf2 = (c2DRects + 10) * sizeof (RTRECT); + g_CrPresenter.pvTmpBuf2 = RTMemAlloc(g_CrPresenter.cbTmpBuf2); + if (!g_CrPresenter.pvTmpBuf2) + { + WARN(("RTMemAlloc failed!")); + g_CrPresenter.cbTmpBuf2 = 0; + rc = VERR_NO_MEMORY; + goto end; + } + } + + RTRECT *p2DRects = (RTRECT *)g_CrPresenter.pvTmpBuf2; + + rc = VBoxVrListRectsGet(&List, c2DRects, p2DRects); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsGet failed, rc %d", rc)); + goto end; + } + + /* p2DRects are in pDst coords and already scaled. */ + + CR_BLITTER_IMG FbImg; + + crFbImgFromFb(hFb, &FbImg); + + CrMBltImgScaled(&FbImg, pSrcRectSize, pDstRect, c2DRects, p2DRects, pDst); + } + +end: + + if (pEnteredTex) + CrTdBltLeave(pEnteredTex); + + if (pEnteredBlitter) + CrBltLeave(pEnteredBlitter); + + VBoxVrListClear(&List); + + return rc; +} + +static int crFbBltGetContentsScaledCPU(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg) +{ + WARN(("not implemented!")); + return VERR_NOT_IMPLEMENTED; +#if 0 + int32_t srcWidth = pSrcRectSize->cx; + int32_t srcHeight = pSrcRectSize->cy; + int32_t dstWidth = pDstRect->xRight - pDstRect->xLeft; + int32_t dstHeight = pDstRect->yBottom - pDstRect->yTop; + + RTPOINT DstPoint = {pDstRect->xLeft, pDstRect->yTop}; + float strX = ((float)dstWidth) / srcWidth; + float strY = ((float)dstHeight) / srcHeight; + + RTPOINT UnscaledPos; + UnscaledPos.x = CR_FLOAT_RCAST(int32_t, pDstRect->xLeft / strX); + UnscaledPos.y = CR_FLOAT_RCAST(int32_t, pDstRect->yTop / strY); + + /* destination is bigger than the source, do 3D data stretching with CPU */ + CR_BLITTER_IMG Img; + Img.cbData = srcWidth * srcHeight * 4; + Img.pvData = RTMemAlloc(Img.cbData); + if (!Img.pvData) + { + WARN(("RTMemAlloc Failed")); + return VERR_NO_MEMORY; + } + Img.enmFormat = pImg->enmFormat; + Img.width = srcWidth; + Img.height = srcHeight; + Img.bpp = pImg->bpp; + Img.pitch = Img.width * 4; + + int rc = CrFbBltGetContents(hFb, &UnscaledPos, cRects, pRects, &Img); + if (RT_SUCCESS(rc)) + { + CrBmpScale32((uint8_t *)pImg->pvData, + pImg->pitch, + pImg->width, pImg->height, + (const uint8_t *)Img.pvData, + Img.pitch, + Img.width, Img.height); + } + else + WARN(("CrFbBltGetContents failed %d", rc)); + + RTMemFree(Img.pvData); + + return rc; +#endif +} + +static int CrFbBltGetContents(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pDst) +{ + VBOXVR_LIST List; + uint32_t c2DRects = 0; + CR_TEXDATA *pEnteredTex = NULL; + PCR_BLITTER pEnteredBlitter = NULL; + + /* 'List' contains the destination rectangles to be updated (in pDst coords). */ + VBoxVrListInit(&List); + int rc = VBoxVrListRectsAdd(&List, cRects, pRects, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsAdd failed rc %d", rc)); + goto end; + } + + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + CrVrScrCompositorConstIterInit(&hFb->Compositor, &Iter); + + for(const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrVrScrCompositorConstIterNext(&Iter); + pEntry; + pEntry = CrVrScrCompositorConstIterNext(&Iter)) + { + /* Where the entry would be located in pDst coords (pPos = pDst_coord - hFb_coord). */ + RTPOINT EntryPoint; + EntryPoint.x = CrVrScrCompositorEntryRectGet(pEntry)->xLeft + pPos->x; + EntryPoint.y = CrVrScrCompositorEntryRectGet(pEntry)->yTop + pPos->y; + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(pEntry); + + /* Optimization to avoid entering/leaving the same texture and its blitter. */ + if (pEnteredTex != pTex) + { + if (!pEnteredBlitter) + { + pEnteredBlitter = CrTdBlitterGet(pTex); + rc = CrBltEnter(pEnteredBlitter); + if (!RT_SUCCESS(rc)) + { + WARN(("CrBltEnter failed %d", rc)); + pEnteredBlitter = NULL; + goto end; + } + } + + if (pEnteredTex) + { + CrTdBltLeave(pEnteredTex); + + pEnteredTex = NULL; + + if (pEnteredBlitter != CrTdBlitterGet(pTex)) + { + WARN(("blitters not equal!")); + CrBltLeave(pEnteredBlitter); + + pEnteredBlitter = CrTdBlitterGet(pTex); + rc = CrBltEnter(pEnteredBlitter); + if (!RT_SUCCESS(rc)) + { + WARN(("CrBltEnter failed %d", rc)); + pEnteredBlitter = NULL; + goto end; + } + } + } + + rc = CrTdBltEnter(pTex); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltEnter failed %d", rc)); + goto end; + } + + pEnteredTex = pTex; + } + + bool fInvert = !(CrVrScrCompositorEntryFlagsGet(pEntry) & CRBLT_F_INVERT_SRC_YCOORDS); + + /* pRegions is where the pEntry was drawn in hFb coords. */ + uint32_t cRegions; + const RTRECT *pRegions; + rc = CrVrScrCompositorEntryRegionsGet(&hFb->Compositor, pEntry, &cRegions, NULL, NULL, &pRegions); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", rc)); + goto end; + } + + /* CrTdBltDataAcquire/CrTdBltDataRelease can use cached data, + * so it is not necessary to optimize and Aquire only when Tex changes. + */ + const CR_BLITTER_IMG *pSrcImg; + rc = CrTdBltDataAcquire(pTex, GL_BGRA, false, &pSrcImg); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltDataAcquire failed rc %d", rc)); + goto end; + } + + for (uint32_t j = 0; j < cRegions; ++j) + { + /* rects are in dst coordinates, + * while the pReg is in source coords + * convert */ + const RTRECT * pReg = &pRegions[j]; + RTRECT SrcReg; + /* translate */ + VBoxRectTranslated(pReg, pPos->x, pPos->y, &SrcReg); + + /* Exclude the pEntry rectangle, because it will be updated now in pDst. + * List uses dst coords and pRegions use hFb coords, therefore use + * SrcReg which is already translated to dst. + */ + rc = VBoxVrListRectsSubst(&List, 1, &SrcReg, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsSubst failed rc %d", rc)); + goto end; + } + + for (uint32_t i = 0; i < cRects; ++i) + { + const RTRECT * pRect = &pRects[i]; + + RTRECT Intersection; + VBoxRectIntersected(pRect, &SrcReg, &Intersection); + if (VBoxRectIsZero(&Intersection)) + continue; + + CrMBltImgRect(pSrcImg, &EntryPoint, fInvert, &Intersection, pDst); + } + } + + CrTdBltDataRelease(pTex); + } + + /* Blit still not updated dst rects, i.e. not covered by 3D entries. */ + c2DRects = VBoxVrListRectsCount(&List); + if (c2DRects) + { + if (g_CrPresenter.cbTmpBuf2 < c2DRects * sizeof (RTRECT)) + { + if (g_CrPresenter.pvTmpBuf2) + RTMemFree(g_CrPresenter.pvTmpBuf2); + + g_CrPresenter.cbTmpBuf2 = (c2DRects + 10) * sizeof (RTRECT); + g_CrPresenter.pvTmpBuf2 = RTMemAlloc(g_CrPresenter.cbTmpBuf2); + if (!g_CrPresenter.pvTmpBuf2) + { + WARN(("RTMemAlloc failed!")); + g_CrPresenter.cbTmpBuf2 = 0; + rc = VERR_NO_MEMORY; + goto end; + } + } + + RTRECT *p2DRects = (RTRECT *)g_CrPresenter.pvTmpBuf2; + + rc = VBoxVrListRectsGet(&List, c2DRects, p2DRects); + if (!RT_SUCCESS(rc)) + { + WARN(("VBoxVrListRectsGet failed, rc %d", rc)); + goto end; + } + + CR_BLITTER_IMG FbImg; + + crFbImgFromFb(hFb, &FbImg); + + CrMBltImg(&FbImg, pPos, c2DRects, p2DRects, pDst); + } + +end: + + if (pEnteredTex) + CrTdBltLeave(pEnteredTex); + + if (pEnteredBlitter) + CrBltLeave(pEnteredBlitter); + + VBoxVrListClear(&List); + + return rc; +} + +int CrFbBltGetContentsEx(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg) +{ + uint32_t srcWidth = pSrcRectSize->cx; + uint32_t srcHeight = pSrcRectSize->cy; + uint32_t dstWidth = pDstRect->xRight - pDstRect->xLeft; + uint32_t dstHeight = pDstRect->yBottom - pDstRect->yTop; + if (srcWidth == dstWidth + && srcHeight == dstHeight) + { + RTPOINT Pos = {pDstRect->xLeft, pDstRect->yTop}; + return CrFbBltGetContents(hFb, &Pos, cRects, pRects, pImg); + } + if (!CrFbHas3DData(hFb) + || (srcWidth * srcHeight > dstWidth * dstHeight)) + return crFbBltGetContentsScaledDirect(hFb, pSrcRectSize, pDstRect, cRects, pRects, pImg); + + return crFbBltGetContentsScaledCPU(hFb, pSrcRectSize, pDstRect, cRects, pRects, pImg); +} + +static void crFbBltPutContentsFbVram(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pSrc) +{ + const RTRECT *pCompRect = CrVrScrCompositorRectGet(&hFb->Compositor); + + CR_BLITTER_IMG FbImg; + + crFbImgFromFb(hFb, &FbImg); + + CrMBltImg(pSrc, pPos, cRects, pRects, &FbImg); +} + +static void crFbClrFillFbVram(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, uint32_t u32Color) +{ + CR_BLITTER_IMG FbImg; + + crFbImgFromFb(hFb, &FbImg); + + CrMClrFillImg(&FbImg, cRects, pRects, u32Color); +} + +int CrFbClrFill(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, uint32_t u32Color) +{ + if (!hFb->cUpdating) + { + WARN(("framebuffer not updating")); + return VERR_INVALID_STATE; + } + + crFbClrFillFbVram(hFb, cRects, pRects, u32Color); + + RTPOINT DstPoint = {0, 0}; + + int rc = CrFbEntryRegionsAdd(hFb, NULL, &DstPoint, cRects, pRects, false); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbEntryRegionsAdd failed %d", rc)); + return rc; + } + + return VINF_SUCCESS; +} + +static int crFbBltPutContents(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg) +{ + crFbBltPutContentsFbVram(hFb, pPos, cRects, pRects, pImg); + + int rc = CrFbEntryRegionsAdd(hFb, NULL, pPos, cRects, pRects, false); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbEntryRegionsAdd failed %d", rc)); + return rc; + } + + return VINF_SUCCESS; +} + +int CrFbBltPutContents(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg) +{ + if (!hFb->cUpdating) + { + WARN(("framebuffer not updating")); + return VERR_INVALID_STATE; + } + + return crFbBltPutContents(hFb, pPos, cRects, pRects, pImg); +} + +static int crFbRegionsIsIntersectRects(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, bool *pfRegChanged) +{ + uint32_t cCompRects; + const RTRECT *pCompRects; + int rc = CrVrScrCompositorRegionsGet(&hFb->Compositor, &cCompRects, NULL, NULL, &pCompRects); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorRegionsGet failed rc %d", rc)); + return rc; + } + + bool fRegChanged = false; + for (uint32_t i = 0; i < cCompRects; ++i) + { + const RTRECT *pCompRect = &pCompRects[i]; + for (uint32_t j = 0; j < cRects; ++j) + { + const RTRECT *pRect = &pRects[j]; + if (VBoxRectIsIntersect(pCompRect, pRect)) + { + *pfRegChanged = true; + return VINF_SUCCESS; + } + } + } + + *pfRegChanged = false; + return VINF_SUCCESS; +} + +int CrFbBltPutContentsNe(HCR_FRAMEBUFFER hFb, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg) +{ + bool fRegChanged = false; + int rc = crFbRegionsIsIntersectRects(hFb, cRects, pRects, &fRegChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("crFbRegionsIsIntersectRects failed rc %d", rc)); + return rc; + } + + if (fRegChanged) + { + rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + rc = CrFbBltPutContents(hFb, pPos, cRects, pRects, pImg); + if (!RT_SUCCESS(rc)) + WARN(("CrFbBltPutContents failed rc %d", rc)); + CrFbUpdateEnd(hFb); + } + else + WARN(("CrFbUpdateBegin failed rc %d", rc)); + + return rc; + } + + crFbBltPutContentsFbVram(hFb, pPos, cRects, pRects, pImg); + return VINF_SUCCESS; +} + +int CrFbClrFillNe(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects, uint32_t u32Color) +{ + bool fRegChanged = false; + int rc = crFbRegionsIsIntersectRects(hFb, cRects, pRects, &fRegChanged); + if (!RT_SUCCESS(rc)) + { + WARN(("crFbRegionsIsIntersectRects failed rc %d", rc)); + return rc; + } + + if (fRegChanged) + { + rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + rc = CrFbClrFill(hFb, cRects, pRects, u32Color); + if (!RT_SUCCESS(rc)) + WARN(("CrFbClrFill failed rc %d", rc)); + CrFbUpdateEnd(hFb); + } + else + WARN(("CrFbUpdateBegin failed rc %d", rc)); + + return rc; + } + + crFbClrFillFbVram(hFb, cRects, pRects, u32Color); + return VINF_SUCCESS; +} + +int CrFbResize(CR_FRAMEBUFFER *pFb, const struct VBVAINFOSCREEN * pScreen, void *pvVRAM) +{ + if (!pFb->cUpdating) + { + WARN(("no update in progress")); + return VERR_INVALID_STATE; + } + + int rc = VINF_SUCCESS; + if (CrFbIsEnabled(pFb)) + { + rc = CrFbRegionsClear(pFb); + if (RT_FAILURE(rc)) + { + WARN(("CrFbRegionsClear failed %d", rc)); + return rc; + } + } + + RTRECT Rect; + Rect.xLeft = 0; + Rect.yTop = 0; + Rect.xRight = pScreen->u32Width; + Rect.yBottom = pScreen->u32Height; + rc = CrVrScrCompositorRectSet(&pFb->Compositor, &Rect, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorRectSet failed rc %d", rc)); + return rc; + } + + pFb->ScreenInfo = *pScreen; + pFb->pvVram = pvVRAM ? pvVRAM : g_pvVRamBase + pScreen->u32StartOffset; + + if (pFb->pDisplay) + pFb->pDisplay->FramebufferChanged(pFb); + + return VINF_SUCCESS; +} + +void CrFbTerm(CR_FRAMEBUFFER *pFb) +{ + if (pFb->cUpdating) + { + WARN(("update in progress")); + return; + } + uint32_t idFb = pFb->ScreenInfo.u32ViewIndex; + + CrVrScrCompositorClear(&pFb->Compositor); + CrHTableDestroy(&pFb->SlotTable); + + Assert(RTListIsEmpty(&pFb->EntriesList)); + Assert(!pFb->cEntries); + + memset(pFb, 0, sizeof (*pFb)); + + pFb->ScreenInfo.u16Flags = VBVA_SCREEN_F_DISABLED; + pFb->ScreenInfo.u32ViewIndex = idFb; +} + +ICrFbDisplay* CrFbDisplayGet(CR_FRAMEBUFFER *pFb) +{ + return pFb->pDisplay; +} + +int CrFbDisplaySet(CR_FRAMEBUFFER *pFb, ICrFbDisplay *pDisplay) +{ + if (pFb->cUpdating) + { + WARN(("update in progress")); + return VERR_INVALID_STATE; + } + + if (pFb->pDisplay == pDisplay) + return VINF_SUCCESS; + + pFb->pDisplay = pDisplay; + + return VINF_SUCCESS; +} + +#define CR_PMGR_MODE_WINDOW 0x1 +/* mutually exclusive with CR_PMGR_MODE_WINDOW */ +#define CR_PMGR_MODE_ROOTVR 0x2 +#define CR_PMGR_MODE_VRDP 0x4 +#define CR_PMGR_MODE_ALL 0x7 + +static int crPMgrModeModifyGlobal(uint32_t u32ModeAdd, uint32_t u32ModeRemove); +static void crPMgrCleanUnusedDisplays(); + +static CR_FBTEX* crFbTexAlloc() +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + return (CR_FBTEX*)RTMemCacheAlloc(g_CrPresenter.FbTexLookasideList); +#else + return (CR_FBTEX*)RTMemAlloc(sizeof (CR_FBTEX)); +#endif +} + +static void crFbTexFree(CR_FBTEX *pTex) +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheFree(g_CrPresenter.FbTexLookasideList, pTex); +#else + RTMemFree(pTex); +#endif +} + +static CR_FRAMEBUFFER_ENTRY* crFbEntryAlloc() +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + return (CR_FRAMEBUFFER_ENTRY*)RTMemCacheAlloc(g_CrPresenter.FbEntryLookasideList); +#else + return (CR_FRAMEBUFFER_ENTRY*)RTMemAlloc(sizeof (CR_FRAMEBUFFER_ENTRY)); +#endif +} + +static void crFbEntryFree(CR_FRAMEBUFFER_ENTRY *pEntry) +{ + Assert(!CrVrScrCompositorEntryIsUsed(&pEntry->Entry)); +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheFree(g_CrPresenter.FbEntryLookasideList, pEntry); +#else + RTMemFree(pEntry); +#endif +} + +DECLCALLBACK(void) crFbTexRelease(CR_TEXDATA *pTex) +{ + CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTex); + CRTextureObj *pTobj = pFbTex->pTobj; + + CrTdBltDataCleanupNe(pTex); + + if (pTobj) + { + crHashtableDelete(g_CrPresenter.pFbTexMap, pTobj->id, NULL); + + crStateReleaseTexture(cr_server.MainContextInfo.pContext, pTobj); + + + crStateGlobalSharedRelease(); + } + + crFbTexFree(pFbTex); +} + +void CrFbTexDataInit(CR_TEXDATA* pFbTex, const VBOXVR_TEXTURE *pTex, PFNCRTEXDATA_RELEASED pfnTextureReleased) +{ + PCR_BLITTER pBlitter = crServerVBoxBlitterGet(); + + CrTdInit(pFbTex, pTex, pBlitter, pfnTextureReleased); +} + +static CR_FBTEX* crFbTexCreate(const VBOXVR_TEXTURE *pTex) +{ + CR_FBTEX *pFbTex = crFbTexAlloc(); + if (!pFbTex) + { + WARN(("crFbTexAlloc failed!")); + return NULL; + } + + CrFbTexDataInit(&pFbTex->Tex, pTex, crFbTexRelease); + pFbTex->pTobj = NULL; + + return pFbTex; +} + +CR_TEXDATA* CrFbTexDataCreate(const VBOXVR_TEXTURE *pTex) +{ + CR_FBTEX *pFbTex = crFbTexCreate(pTex); + if (!pFbTex) + { + WARN(("crFbTexCreate failed!")); + return NULL; + } + + return &pFbTex->Tex; +} + +static CR_FBTEX* crFbTexAcquire(GLuint idTexture) +{ + CR_FBTEX *pFbTex = (CR_FBTEX *)crHashtableSearch(g_CrPresenter.pFbTexMap, idTexture); + if (pFbTex) + { + CrTdAddRef(&pFbTex->Tex); + return pFbTex; + } + + CRSharedState *pShared = crStateGlobalSharedAcquire(); + if (!pShared) + { + WARN(("pShared is null!")); + return NULL; + } + + CRTextureObj *pTobj = (CRTextureObj*)crHashtableSearch(pShared->textureTable, idTexture); + if (!pTobj) + { + LOG(("pTobj is null!")); + crStateGlobalSharedRelease(); + return NULL; + } + + Assert(pTobj->id == idTexture); + + GLuint hwid = crStateGetTextureObjHWID(pTobj); + if (!hwid) + { + WARN(("hwId is null!")); + crStateGlobalSharedRelease(); + return NULL; + } + + VBOXVR_TEXTURE Tex; + Tex.width = pTobj->level[0]->width; + Tex.height = pTobj->level[0]->height; + Tex.hwid = hwid; + Tex.target = pTobj->target; + + pFbTex = crFbTexCreate(&Tex); + if (!pFbTex) + { + WARN(("crFbTexCreate failed!")); + crStateGlobalSharedRelease(); + return NULL; + } + + CR_STATE_SHAREDOBJ_USAGE_SET(pTobj, cr_server.MainContextInfo.pContext); + + pFbTex->pTobj = pTobj; + + crHashtableAdd(g_CrPresenter.pFbTexMap, idTexture, pFbTex); + + return pFbTex; +} + +static CR_TEXDATA* CrFbTexDataAcquire(GLuint idTexture) +{ + CR_FBTEX* pTex = crFbTexAcquire(idTexture); + if (!pTex) + { + WARN(("crFbTexAcquire failed for %d", idTexture)); + return NULL; + } + + return &pTex->Tex; +} + +static void crFbEntryMarkDestroyed(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry) +{ + if (pEntry->Flags.fCreateNotified) + { + pEntry->Flags.fCreateNotified = 0; + if (pFb->pDisplay) + pFb->pDisplay->EntryDestroyed(pFb, pEntry); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pEntry->Entry); + if (pTex) + CrTdBltDataInvalidateNe(pTex); + } +} + +static void crFbEntryDestroy(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry) +{ + crFbEntryMarkDestroyed(pFb, pEntry); + CrVrScrCompositorEntryCleanup(&pEntry->Entry); + CrHTableDestroy(&pEntry->HTable); + Assert(pFb->cEntries); + RTListNodeRemove(&pEntry->Node); + --pFb->cEntries; + crFbEntryFree(pEntry); +} + +DECLINLINE(uint32_t) crFbEntryAddRef(CR_FRAMEBUFFER_ENTRY* pEntry) +{ + return ++pEntry->cRefs; +} + +DECLINLINE(uint32_t) crFbEntryRelease(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY* pEntry) +{ + uint32_t cRefs = --pEntry->cRefs; + if (!cRefs) + crFbEntryDestroy(pFb, pEntry); + return cRefs; +} + +static DECLCALLBACK(void) crFbEntryReleased(const struct VBOXVR_SCR_COMPOSITOR *pCompositor, struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry, struct VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacingEntry) +{ + CR_FRAMEBUFFER *pFb = PCR_FRAMEBUFFER_FROM_COMPOSITOR(pCompositor); + CR_FRAMEBUFFER_ENTRY *pFbEntry = PCR_FBENTRY_FROM_ENTRY(pEntry); + CR_FRAMEBUFFER_ENTRY *pFbReplacingEntry = pReplacingEntry ? PCR_FBENTRY_FROM_ENTRY(pReplacingEntry) : NULL; + if (pFbReplacingEntry) + { + /*replace operation implies the replaced entry gets auto-destroyed, + * while all its data gets moved to the *clean* replacing entry + * 1. ensure the replacing entry is cleaned up */ + crFbEntryMarkDestroyed(pFb, pFbReplacingEntry); + + CrHTableMoveTo(&pFbEntry->HTable, &pFbReplacingEntry->HTable); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pFbEntry->Entry); + CR_TEXDATA *pReplacingTex = CrVrScrCompositorEntryTexGet(&pFbReplacingEntry->Entry); + + CrTdBltScaleCacheMoveTo(pTex, pReplacingTex); + + if (pFb->pDisplay) + pFb->pDisplay->EntryReplaced(pFb, pFbReplacingEntry, pFbEntry); + + CrTdBltDataInvalidateNe(pTex); + + /* 2. mark the replaced entry is destroyed */ + Assert(pFbEntry->Flags.fCreateNotified); + Assert(pFbEntry->Flags.fInList); + pFbEntry->Flags.fCreateNotified = 0; + pFbEntry->Flags.fInList = 0; + pFbReplacingEntry->Flags.fCreateNotified = 1; + pFbReplacingEntry->Flags.fInList = 1; + } + else + { + if (pFbEntry->Flags.fInList) + { + pFbEntry->Flags.fInList = 0; + if (pFb->pDisplay) + pFb->pDisplay->EntryRemoved(pFb, pFbEntry); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pFbEntry->Entry); + if (pTex) + CrTdBltDataInvalidateNe(pTex); + } + } + + crFbEntryRelease(pFb, pFbEntry); +} + +static CR_FRAMEBUFFER_ENTRY* crFbEntryCreate(CR_FRAMEBUFFER *pFb, CR_TEXDATA* pTex, const RTRECT *pRect, uint32_t fFlags) +{ + CR_FRAMEBUFFER_ENTRY *pEntry = crFbEntryAlloc(); + if (!pEntry) + { + WARN(("crFbEntryAlloc failed!")); + return NULL; + } + + CrVrScrCompositorEntryInit(&pEntry->Entry, pRect, pTex, crFbEntryReleased); + CrVrScrCompositorEntryFlagsSet(&pEntry->Entry, fFlags); + pEntry->cRefs = 1; + pEntry->Flags.Value = 0; + CrHTableCreate(&pEntry->HTable, 0); + + RTListAppend(&pFb->EntriesList, &pEntry->Node); + ++pFb->cEntries; + + return pEntry; +} + +int CrFbEntryCreateForTexData(CR_FRAMEBUFFER *pFb, struct CR_TEXDATA *pTex, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry) +{ + if (pTex == NULL) + { + WARN(("pTex is NULL")); + return VERR_INVALID_PARAMETER; + } + + RTRECT Rect; + Rect.xLeft = 0; + Rect.yTop = 0; + Rect.xRight = pTex->Tex.width; + Rect.yBottom = pTex->Tex.height; + CR_FRAMEBUFFER_ENTRY* pEntry = crFbEntryCreate(pFb, pTex, &Rect, fFlags); + if (!pEntry) + { + WARN(("crFbEntryCreate failed")); + return VERR_NO_MEMORY; + } + + *phEntry = pEntry; + return VINF_SUCCESS; +} + +int CrFbEntryTexDataUpdate(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY pEntry, struct CR_TEXDATA *pTex) +{ + if (!pFb->cUpdating) + { + WARN(("framebuffer not updating")); + return VERR_INVALID_STATE; + } + + if (pTex) + CrVrScrCompositorEntryTexSet(&pEntry->Entry, pTex); + + if (CrVrScrCompositorEntryIsUsed(&pEntry->Entry)) + { + if (pFb->pDisplay) + pFb->pDisplay->EntryTexChanged(pFb, pEntry); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&pEntry->Entry); + if (pTex) + CrTdBltDataInvalidateNe(pTex); + } + + return VINF_SUCCESS; +} + + +int CrFbEntryCreateForTexId(CR_FRAMEBUFFER *pFb, GLuint idTexture, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry) +{ + CR_FBTEX* pFbTex = crFbTexAcquire(idTexture); + if (!pFbTex) + { + LOG(("crFbTexAcquire failed")); + return VERR_INVALID_PARAMETER; + } + + CR_TEXDATA* pTex = &pFbTex->Tex; + int rc = CrFbEntryCreateForTexData(pFb, pTex, fFlags, phEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbEntryCreateForTexData failed rc %d", rc)); + } + + /*always release the tex, the CrFbEntryCreateForTexData will do incref as necessary */ + CrTdRelease(pTex); + return rc; +} + +void CrFbEntryAddRef(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + ++hEntry->cRefs; +} + +void CrFbEntryRelease(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) +{ + crFbEntryRelease(pFb, hEntry); +} + +static int8_t crVBoxServerCrCmdBltPrimaryVramGenericProcess(uint32_t u32PrimaryID, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, bool fToPrimary); + +int CrFbRegionsClear(HCR_FRAMEBUFFER hFb) +{ + if (!hFb->cUpdating) + { + WARN(("framebuffer not updating")); + return VERR_INVALID_STATE; + } + + uint32_t cRegions; + const RTRECT *pRegions; + int rc = CrVrScrCompositorRegionsGet(&hFb->Compositor, &cRegions, NULL, NULL, &pRegions); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", rc)); + return rc; + } + + const struct VBVAINFOSCREEN* pScreen = CrFbGetScreenInfo(hFb); + VBOXCMDVBVAOFFSET offVRAM = (VBOXCMDVBVAOFFSET)(((uintptr_t)CrFbGetVRAM(hFb)) - ((uintptr_t)g_pvVRamBase)); + RTPOINT Pos = {0,0}; + int8_t i8Result = crVBoxServerCrCmdBltPrimaryVramGenericProcess(pScreen->u32ViewIndex, offVRAM, pScreen->u32Width, pScreen->u32Height, &Pos, cRegions, pRegions, true); + if (i8Result) + { + WARN(("crVBoxServerCrCmdBltPrimaryVramGenericProcess failed")); + return VERR_INTERNAL_ERROR; + } + +#ifdef DEBUG + { + uint32_t cTmpRegions; + const RTRECT *pTmpRegions; + int tmpRc = CrVrScrCompositorRegionsGet(&hFb->Compositor, &cTmpRegions, NULL, NULL, &pTmpRegions); + if (!RT_SUCCESS(tmpRc)) + { + WARN(("CrVrScrCompositorEntryRegionsGet failed rc %d", tmpRc)); + } + Assert(!cTmpRegions); + } +#endif + + /* just in case */ + bool fChanged = false; + CrVrScrCompositorRegionsClear(&hFb->Compositor, &fChanged); + Assert(!fChanged); + + if (cRegions) + { + if (hFb->pDisplay) + hFb->pDisplay->RegionsChanged(hFb); + } + + return VINF_SUCCESS; +} + +int CrFbEntryRegionsAdd(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated) +{ + if (!pFb->cUpdating) + { + WARN(("framebuffer not updating")); + return VERR_INVALID_STATE; + } + + uint32_t fChangeFlags = 0; + VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = NULL; + VBOXVR_SCR_COMPOSITOR_ENTRY *pNewEntry; + bool fEntryWasInList; + + if (hEntry) + { + crFbEntryAddRef(hEntry); + pNewEntry = &hEntry->Entry; + fEntryWasInList = CrVrScrCompositorEntryIsUsed(pNewEntry); + + Assert(!hEntry->Flags.fInList == !fEntryWasInList); + } + else + { + pNewEntry = NULL; + fEntryWasInList = false; + } + + int rc = CrVrScrCompositorEntryRegionsAdd(&pFb->Compositor, hEntry ? &hEntry->Entry : NULL, pPos, cRegions, paRegions, fPosRelated, &pReplacedScrEntry, &fChangeFlags); + if (RT_SUCCESS(rc)) + { + if (fChangeFlags & VBOXVR_COMPOSITOR_CF_REGIONS_CHANGED) + { + if (!fEntryWasInList && pNewEntry) + { + Assert(CrVrScrCompositorEntryIsUsed(pNewEntry)); + if (!hEntry->Flags.fCreateNotified) + { + hEntry->Flags.fCreateNotified = 1; + if (pFb->pDisplay) + pFb->pDisplay->EntryCreated(pFb, hEntry); + } + +#ifdef DEBUG_misha + /* in theory hEntry->Flags.fInList can be set if entry is replaced, + * but then modified to fit the compositor rects, + * and so we get the regions changed notification as a result + * this should not generally happen though, so put an assertion to debug that situation */ + Assert(!hEntry->Flags.fInList); +#endif + if (!hEntry->Flags.fInList) + { + hEntry->Flags.fInList = 1; + + if (pFb->pDisplay) + pFb->pDisplay->EntryAdded(pFb, hEntry); + } + } + if (pFb->pDisplay) + pFb->pDisplay->RegionsChanged(pFb); + + Assert(!pReplacedScrEntry); + } + else if (fChangeFlags & VBOXVR_COMPOSITOR_CF_ENTRY_REPLACED) + { + Assert(pReplacedScrEntry); + /* we have already processed that in a "release" callback */ + Assert(hEntry); + } + else + { + Assert(!fChangeFlags); + Assert(!pReplacedScrEntry); + } + + if (hEntry) + { + if (CrVrScrCompositorEntryIsUsed(&hEntry->Entry)) + { + if (pFb->pDisplay) + pFb->pDisplay->EntryTexChanged(pFb, hEntry); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&hEntry->Entry); + if (pTex) + CrTdBltDataInvalidateNe(pTex); + } + } + } + else + WARN(("CrVrScrCompositorEntryRegionsAdd failed, rc %d", rc)); + + return rc; +} + +int CrFbEntryRegionsSet(CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated) +{ + if (!pFb->cUpdating) + { + WARN(("framebuffer not updating")); + return VERR_INVALID_STATE; + } + + bool fChanged = 0; + VBOXVR_SCR_COMPOSITOR_ENTRY *pReplacedScrEntry = NULL; + VBOXVR_SCR_COMPOSITOR_ENTRY *pNewEntry; + bool fEntryWasInList; + + if (hEntry) + { + crFbEntryAddRef(hEntry); + pNewEntry = &hEntry->Entry; + fEntryWasInList = CrVrScrCompositorEntryIsUsed(pNewEntry); + Assert(!hEntry->Flags.fInList == !fEntryWasInList); + } + else + { + pNewEntry = NULL; + fEntryWasInList = false; + } + + int rc = CrVrScrCompositorEntryRegionsSet(&pFb->Compositor, pNewEntry, pPos, cRegions, paRegions, fPosRelated, &fChanged); + if (RT_SUCCESS(rc)) + { + if (fChanged) + { + if (!fEntryWasInList && pNewEntry) + { + if (CrVrScrCompositorEntryIsUsed(pNewEntry)) + { + if (!hEntry->Flags.fCreateNotified) + { + hEntry->Flags.fCreateNotified = 1; + + if (pFb->pDisplay) + pFb->pDisplay->EntryCreated(pFb, hEntry); + } + + Assert(!hEntry->Flags.fInList); + hEntry->Flags.fInList = 1; + + if (pFb->pDisplay) + pFb->pDisplay->EntryAdded(pFb, hEntry); + } + } + + if (pFb->pDisplay) + pFb->pDisplay->RegionsChanged(pFb); + } + + if (hEntry) + { + if (CrVrScrCompositorEntryIsUsed(&hEntry->Entry)) + { + if (pFb->pDisplay) + pFb->pDisplay->EntryTexChanged(pFb, hEntry); + + CR_TEXDATA *pTex = CrVrScrCompositorEntryTexGet(&hEntry->Entry); + if (pTex) + CrTdBltDataInvalidateNe(pTex); + } + } + } + else + WARN(("CrVrScrCompositorEntryRegionsSet failed, rc %d", rc)); + + return rc; +} + +const struct VBOXVR_SCR_COMPOSITOR_ENTRY* CrFbEntryGetCompositorEntry(HCR_FRAMEBUFFER_ENTRY hEntry) +{ + return &hEntry->Entry; +} + +HCR_FRAMEBUFFER_ENTRY CrFbEntryFromCompositorEntry(const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry) +{ + return RT_FROM_MEMBER(pCEntry, CR_FRAMEBUFFER_ENTRY, Entry); +} + +void CrFbVisitCreatedEntries(HCR_FRAMEBUFFER hFb, PFNCR_FRAMEBUFFER_ENTRIES_VISITOR_CB pfnVisitorCb, void *pvContext) +{ + HCR_FRAMEBUFFER_ENTRY hEntry, hNext; + RTListForEachSafe(&hFb->EntriesList, hEntry, hNext, CR_FRAMEBUFFER_ENTRY, Node) + { + if (hEntry->Flags.fCreateNotified) + { + if (!pfnVisitorCb(hFb, hEntry, pvContext)) + return; + } + } +} + + +CRHTABLE_HANDLE CrFbDDataAllocSlot(CR_FRAMEBUFFER *pFb) +{ + return CrHTablePut(&pFb->SlotTable, (void*)1); +} + +void CrFbDDataReleaseSlot(CR_FRAMEBUFFER *pFb, CRHTABLE_HANDLE hSlot, PFNCR_FRAMEBUFFER_SLOT_RELEASE_CB pfnReleaseCb, void *pvContext) +{ + HCR_FRAMEBUFFER_ENTRY hEntry, hNext; + RTListForEachSafe(&pFb->EntriesList, hEntry, hNext, CR_FRAMEBUFFER_ENTRY, Node) + { + if (CrFbDDataEntryGet(hEntry, hSlot)) + { + if (pfnReleaseCb) + pfnReleaseCb(pFb, hEntry, pvContext); + + CrFbDDataEntryClear(hEntry, hSlot); + } + } + + CrHTableRemove(&pFb->SlotTable, hSlot); +} + +int CrFbDDataEntryPut(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot, void *pvData) +{ + return CrHTablePutToSlot(&hEntry->HTable, hSlot, pvData); +} + +void* CrFbDDataEntryClear(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot) +{ + return CrHTableRemove(&hEntry->HTable, hSlot); +} + +void* CrFbDDataEntryGet(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot) +{ + return CrHTableGet(&hEntry->HTable, hSlot); +} + +int CrPMgrDisable() +{ + if (!g_CrPresenter.fEnabled) + return VINF_SUCCESS; + + g_CrPresenter.u32DisabledDisplayMode = g_CrPresenter.u32DisplayMode; + + int rc = crPMgrModeModifyGlobal(0, CR_PMGR_MODE_WINDOW); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrModeModifyGlobal failed %d", rc)); + return rc; + } + + crPMgrCleanUnusedDisplays(); + + g_CrPresenter.fEnabled = false; + + return VINF_SUCCESS; +} + +int CrPMgrEnable() +{ + if (g_CrPresenter.fEnabled) + return VINF_SUCCESS; + + g_CrPresenter.fEnabled = true; + + int rc = crPMgrModeModifyGlobal(g_CrPresenter.u32DisabledDisplayMode, 0); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrModeModifyGlobal failed %d", rc)); + g_CrPresenter.fEnabled = false; + return rc; + } + + g_CrPresenter.u32DisabledDisplayMode = 0; + + return VINF_SUCCESS; +} + +int CrPMgrInit() +{ + int rc = VINF_SUCCESS; + memset(&g_CrPresenter, 0, sizeof (g_CrPresenter)); + g_CrPresenter.fEnabled = true; + for (int i = 0; i < RT_ELEMENTS(g_CrPresenter.aDisplayInfos); ++i) + { + g_CrPresenter.aDisplayInfos[i].u32Id = i; + g_CrPresenter.aDisplayInfos[i].iFb = -1; + + g_CrPresenter.aFbInfos[i].u32Id = i; + } + + g_CrPresenter.pFbTexMap = crAllocHashtable(); + if (g_CrPresenter.pFbTexMap) + { +#ifndef VBOXVDBG_MEMCACHE_DISABLE + rc = RTMemCacheCreate(&g_CrPresenter.FbEntryLookasideList, sizeof (CR_FRAMEBUFFER_ENTRY), + 0, /* size_t cbAlignment */ + UINT32_MAX, /* uint32_t cMaxObjects */ + NULL, /* PFNMEMCACHECTOR pfnCtor*/ + NULL, /* PFNMEMCACHEDTOR pfnDtor*/ + NULL, /* void *pvUser*/ + 0 /* uint32_t fFlags*/ + ); + if (RT_SUCCESS(rc)) + { + rc = RTMemCacheCreate(&g_CrPresenter.FbTexLookasideList, sizeof (CR_FBTEX), + 0, /* size_t cbAlignment */ + UINT32_MAX, /* uint32_t cMaxObjects */ + NULL, /* PFNMEMCACHECTOR pfnCtor*/ + NULL, /* PFNMEMCACHEDTOR pfnDtor*/ + NULL, /* void *pvUser*/ + 0 /* uint32_t fFlags*/ + ); + if (RT_SUCCESS(rc)) + { + rc = RTMemCacheCreate(&g_CrPresenter.CEntryLookasideList, sizeof (VBOXVR_SCR_COMPOSITOR_ENTRY), + 0, /* size_t cbAlignment */ + UINT32_MAX, /* uint32_t cMaxObjects */ + NULL, /* PFNMEMCACHECTOR pfnCtor*/ + NULL, /* PFNMEMCACHEDTOR pfnDtor*/ + NULL, /* void *pvUser*/ + 0 /* uint32_t fFlags*/ + ); + if (RT_SUCCESS(rc)) + { +#endif + rc = crPMgrModeModifyGlobal(CR_PMGR_MODE_WINDOW, 0); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + else + WARN(("crPMgrModeModifyGlobal failed rc %d", rc)); +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheDestroy(g_CrPresenter.CEntryLookasideList); + } + else + WARN(("RTMemCacheCreate failed rc %d", rc)); + + RTMemCacheDestroy(g_CrPresenter.FbTexLookasideList); + } + else + WARN(("RTMemCacheCreate failed rc %d", rc)); + + RTMemCacheDestroy(g_CrPresenter.FbEntryLookasideList); + } + else + WARN(("RTMemCacheCreate failed rc %d", rc)); +#endif + } + else + { + WARN(("crAllocHashtable failed")); + rc = VERR_NO_MEMORY; + } + return rc; +} + +void CrPMgrTerm() +{ + crPMgrModeModifyGlobal(0, CR_PMGR_MODE_ALL); + + HCR_FRAMEBUFFER hFb; + + for (hFb = CrPMgrFbGetFirstInitialized(); + hFb; + hFb = CrPMgrFbGetNextInitialized(hFb)) + { + uint32_t iFb = CrFbGetScreenInfo(hFb)->u32ViewIndex; + CrFbDisplaySet(hFb, NULL); + CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[iFb]; + if (pFbInfo->pDpComposite) + { + delete pFbInfo->pDpComposite; + pFbInfo->pDpComposite = NULL; + } + + CrFbTerm(hFb); + } + + crPMgrCleanUnusedDisplays(); + +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMemCacheDestroy(g_CrPresenter.FbEntryLookasideList); + RTMemCacheDestroy(g_CrPresenter.FbTexLookasideList); + RTMemCacheDestroy(g_CrPresenter.CEntryLookasideList); +#endif + crFreeHashtable(g_CrPresenter.pFbTexMap, NULL); + + if (g_CrPresenter.pvTmpBuf) + RTMemFree(g_CrPresenter.pvTmpBuf); + + if (g_CrPresenter.pvTmpBuf2) + RTMemFree(g_CrPresenter.pvTmpBuf2); + + memset(&g_CrPresenter, 0, sizeof (g_CrPresenter)); +} + +HCR_FRAMEBUFFER CrPMgrFbGet(uint32_t idFb) +{ + if (idFb >= CR_MAX_GUEST_MONITORS) + { + WARN(("invalid idFb %d", idFb)); + return NULL; + } + + if (!CrFBmIsSet(&g_CrPresenter.FramebufferInitMap, idFb)) + { + CrFbInit(&g_CrPresenter.aFramebuffers[idFb], idFb); + CrFBmSetAtomic(&g_CrPresenter.FramebufferInitMap, idFb); + } + else + Assert(g_CrPresenter.aFramebuffers[idFb].ScreenInfo.u32ViewIndex == idFb); + + return &g_CrPresenter.aFramebuffers[idFb]; +} + +HCR_FRAMEBUFFER CrPMgrFbGetInitialized(uint32_t idFb) +{ + if (idFb >= CR_MAX_GUEST_MONITORS) + { + WARN(("invalid idFb %d", idFb)); + return NULL; + } + + if (!CrFBmIsSet(&g_CrPresenter.FramebufferInitMap, idFb)) + { + return NULL; + } + else + Assert(g_CrPresenter.aFramebuffers[idFb].ScreenInfo.u32ViewIndex == idFb); + + return &g_CrPresenter.aFramebuffers[idFb]; +} + +HCR_FRAMEBUFFER CrPMgrFbGetEnabled(uint32_t idFb) +{ + HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(idFb); + + if(hFb && CrFbIsEnabled(hFb)) + return hFb; + + return NULL; +} + +HCR_FRAMEBUFFER CrPMgrFbGetEnabledForScreen(uint32_t idScreen) +{ + if (idScreen >= (uint32_t)cr_server.screenCount) + { + WARN(("invalid target id")); + return NULL; + } + + const CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + if (pDpInfo->iFb < 0) + return NULL; + + return CrPMgrFbGetEnabled(pDpInfo->iFb); +} + +static HCR_FRAMEBUFFER crPMgrFbGetNextEnabled(uint32_t i) +{ + for (;i < (uint32_t)cr_server.screenCount; ++i) + { + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(i); + if (hFb) + return hFb; + } + + return NULL; +} + +static HCR_FRAMEBUFFER crPMgrFbGetNextInitialized(uint32_t i) +{ + for (;i < (uint32_t)cr_server.screenCount; ++i) + { + HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(i); + if (hFb) + return hFb; + } + + return NULL; +} + +HCR_FRAMEBUFFER CrPMgrFbGetFirstEnabled() +{ + HCR_FRAMEBUFFER hFb = crPMgrFbGetNextEnabled(0); +// if (!hFb) +// WARN(("no enabled framebuffer found")); + return hFb; +} + +HCR_FRAMEBUFFER CrPMgrFbGetNextEnabled(HCR_FRAMEBUFFER hFb) +{ + return crPMgrFbGetNextEnabled(hFb->ScreenInfo.u32ViewIndex+1); +} + +HCR_FRAMEBUFFER CrPMgrFbGetFirstInitialized() +{ + HCR_FRAMEBUFFER hFb = crPMgrFbGetNextInitialized(0); +// if (!hFb) +// WARN(("no initialized framebuffer found")); + return hFb; +} + +HCR_FRAMEBUFFER CrPMgrFbGetNextInitialized(HCR_FRAMEBUFFER hFb) +{ + return crPMgrFbGetNextInitialized(hFb->ScreenInfo.u32ViewIndex+1); +} + +HCR_FRAMEBUFFER CrPMgrFbGetEnabledByVramStart(VBOXCMDVBVAOFFSET offVRAM) +{ + for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + if (pScreen->u32StartOffset == offVRAM) + return hFb; + } + + return NULL; +} + + +static uint32_t crPMgrModeAdjustVal(uint32_t u32Mode) +{ + u32Mode = CR_PMGR_MODE_ALL & u32Mode; + if (CR_PMGR_MODE_ROOTVR & u32Mode) + u32Mode &= ~CR_PMGR_MODE_WINDOW; + return u32Mode; +} + +static int crPMgrCheckInitWindowDisplays(uint32_t idScreen) +{ +#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + if (pDpInfo->iFb >= 0) + { + uint32_t u32ModeAdd = g_CrPresenter.u32DisplayMode & (CR_PMGR_MODE_WINDOW | CR_PMGR_MODE_ROOTVR); + int rc = crPMgrFbConnectTargetDisplays(&g_CrPresenter.aFramebuffers[pDpInfo->iFb], pDpInfo, u32ModeAdd); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbConnectTargetDisplays failed %d", rc)); + return rc; + } + } +#endif + return VINF_SUCCESS; +} + +extern "C" DECLEXPORT(int) VBoxOglSetScaleFactor(uint32_t idScreen, double dScaleFactorW, double dScaleFactorH) +{ + if (idScreen >= CR_MAX_GUEST_MONITORS) + { + crDebug("Can't set scale factor because specified screen ID (%u) is out of range (max=%d).", idScreen, CR_MAX_GUEST_MONITORS); + return VERR_INVALID_PARAMETER; + } + + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + if (pDpInfo->pDpWin) + { + CrFbWindow *pWin = pDpInfo->pDpWin->getWindow(); + if (pWin) + { + bool rc; + crDebug("Set scale factor for initialized display."); + rc = pWin->SetScaleFactor((GLdouble)dScaleFactorW, (GLdouble)dScaleFactorH); + return rc ? 0 : VERR_LOCK_FAILED; + } + else + crDebug("Can't apply scale factor at the moment bacause overlay window obgect not yet created. Will be chached."); + } + else + crDebug("Can't apply scale factor at the moment bacause display not yet initialized. Will be chached."); + + /* Display output not yet initialized. Let's cache values. */ + pDpInfo->dInitialScaleFactorW = dScaleFactorW; + pDpInfo->dInitialScaleFactorH = dScaleFactorH; + + return 0; +} + +int CrPMgrScreenChanged(uint32_t idScreen) +{ + if (idScreen >= CR_MAX_GUEST_MONITORS) + { + WARN(("invalid idScreen %d", idScreen)); + return VERR_INVALID_PARAMETER; + } + + int rc = VINF_SUCCESS; + + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + HCR_FRAMEBUFFER hFb = pDpInfo->iFb >= 0 ? CrPMgrFbGet(pDpInfo->iFb) : NULL; + + if (hFb && CrFbIsUpdating(hFb)) + { + WARN(("trying to update viewport while framebuffer is being updated")); + return VERR_INVALID_STATE; + } + + if (pDpInfo->pDpWin) + { + CRASSERT(pDpInfo->pDpWin->getWindow()); + + rc = pDpInfo->pDpWin->UpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + pDpInfo->pDpWin->reparent(cr_server.screen[idScreen].winID); + pDpInfo->pDpWin->UpdateEnd(hFb); + } + } + else + { + if (pDpInfo->pWindow) + { + rc = pDpInfo->pWindow->UpdateBegin(); + if (RT_SUCCESS(rc)) + { + rc = pDpInfo->pWindow->SetVisible(false); + if (RT_SUCCESS(rc)) + rc = pDpInfo->pWindow->Reparent(cr_server.screen[idScreen].winID); + + pDpInfo->pWindow->UpdateEnd(); + } + } + + if (RT_SUCCESS(rc)) + rc = crPMgrCheckInitWindowDisplays(idScreen); + } + + CRASSERT(!rc); + + return rc; +} + +int CrPMgrViewportUpdate(uint32_t idScreen) +{ + if (idScreen >= CR_MAX_GUEST_MONITORS) + { + WARN(("invalid idScreen %d", idScreen)); + return VERR_INVALID_PARAMETER; + } + + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[idScreen]; + if (pDpInfo->iFb >= 0) + { + HCR_FRAMEBUFFER hFb = CrPMgrFbGet(pDpInfo->iFb); + if (CrFbIsUpdating(hFb)) + { + WARN(("trying to update viewport while framebuffer is being updated")); + return VERR_INVALID_STATE; + } + + if (pDpInfo->pDpWin) + { + CRASSERT(pDpInfo->pDpWin->getWindow()); + int rc = pDpInfo->pDpWin->UpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + pDpInfo->pDpWin->setViewportRect(&cr_server.screenVieport[idScreen].Rect); + pDpInfo->pDpWin->UpdateEnd(hFb); + } + else + WARN(("UpdateBegin failed %d", rc)); + } + } + + return VINF_SUCCESS; +} + +static int crPMgrFbDisconnectDisplay(HCR_FRAMEBUFFER hFb, CrFbDisplayBase *pDp) +{ + if (pDp->getFramebuffer() != hFb) + return VINF_SUCCESS; + + CrFbDisplayBase * pCurDp = (CrFbDisplayBase*)CrFbDisplayGet(hFb); + if (!pCurDp) + { + WARN(("no display set, unexpected")); + return VERR_INTERNAL_ERROR; + } + + if (pCurDp == pDp) + { + pDp->setFramebuffer(NULL); + CrFbDisplaySet(hFb, NULL); + return VINF_SUCCESS; + } + + uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex; + CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb]; + if (pFbInfo->pDpComposite != pCurDp) + { + WARN(("misconfig, expectig the curret framebuffer to be present, and thus composite is expected")); + return VERR_INTERNAL_ERROR; + } + + if (pDp->getContainer() == pFbInfo->pDpComposite) + { + pFbInfo->pDpComposite->remove(pDp); + uint32_t cDisplays = pFbInfo->pDpComposite->getDisplayCount(); + if (cDisplays <= 1) + { + Assert(cDisplays == 1); + CrFbDisplayBase *pDpFirst = pFbInfo->pDpComposite->first(); + if (pDpFirst) + pFbInfo->pDpComposite->remove(pDpFirst, false); + CrFbDisplaySet(hFb, pDpFirst); + } + return VINF_SUCCESS; + } + + WARN(("misconfig")); + return VERR_INTERNAL_ERROR; +} + +static int crPMgrFbConnectDisplay(HCR_FRAMEBUFFER hFb, CrFbDisplayBase *pDp) +{ + if (pDp->getFramebuffer() == hFb) + return VINF_SUCCESS; + + CrFbDisplayBase * pCurDp = (CrFbDisplayBase*)CrFbDisplayGet(hFb); + if (!pCurDp) + { + pDp->setFramebuffer(hFb); + CrFbDisplaySet(hFb, pDp); + return VINF_SUCCESS; + } + + if (pCurDp == pDp) + { + WARN(("misconfig, current framebuffer is not expected to be set")); + return VERR_INTERNAL_ERROR; + } + + uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex; + CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb]; + if (pFbInfo->pDpComposite != pCurDp) + { + if (!pFbInfo->pDpComposite) + { + pFbInfo->pDpComposite = new CrFbDisplayComposite(); + pFbInfo->pDpComposite->setFramebuffer(hFb); + } + + pFbInfo->pDpComposite->add(pCurDp); + CrFbDisplaySet(hFb, pFbInfo->pDpComposite); + } + + pFbInfo->pDpComposite->add(pDp); + return VINF_SUCCESS; +} + +static int crPMgrFbDisconnectTarget(HCR_FRAMEBUFFER hFb, uint32_t i) +{ + uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex; + CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb]; + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i]; + if (pDpInfo->iFb != idFb) + { + WARN(("target not connected")); + Assert(!ASMBitTest(pFbInfo->aTargetMap, i)); + return VINF_SUCCESS; + } + + Assert(ASMBitTest(pFbInfo->aTargetMap, i)); + + int rc = VINF_SUCCESS; + if (pDpInfo->pDpVrdp) + { + rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpVrdp); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnectDisplay failed %d", rc)); + return rc; + } + } + + if (pDpInfo->pDpWinRootVr) + { +#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS + CrFbWindow *pWindow = pDpInfo->pDpWinRootVr->windowDetach(false); + Assert(pWindow == pDpInfo->pWindow); +#endif + rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWinRootVr); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnectDisplay failed %d", rc)); + return rc; + } + } + else if (pDpInfo->pDpWin) + { +#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS + CrFbWindow *pWindow = pDpInfo->pDpWin->windowDetach(false); + Assert(pWindow == pDpInfo->pWindow); +#endif + rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWin); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnectDisplay failed %d", rc)); + return rc; + } + } + + ASMBitClear(pFbInfo->aTargetMap, i); + pDpInfo->iFb = -1; + + return VINF_SUCCESS; +} + +static void crPMgrDpWinRootVrCreate(CR_FBDISPLAY_INFO *pDpInfo) +{ + if (!pDpInfo->pDpWinRootVr) + { + if (pDpInfo->pDpWin) + { + CrFbWindow *pWin = pDpInfo->pDpWin->windowDetach(); + CRASSERT(pWin); + Assert(pWin == pDpInfo->pWindow); + delete pDpInfo->pDpWin; + pDpInfo->pDpWin = NULL; + } + else if (!pDpInfo->pWindow) + { + pDpInfo->pWindow = new CrFbWindow(0); + } + + pDpInfo->pDpWinRootVr = new CrFbDisplayWindowRootVr(&cr_server.screenVieport[pDpInfo->u32Id].Rect, cr_server.screen[pDpInfo->u32Id].winID); + pDpInfo->pDpWin = pDpInfo->pDpWinRootVr; + pDpInfo->pDpWinRootVr->windowAttach(pDpInfo->pWindow); + + /* Set scale factor once it was previously cached when display output was not yet initialized. */ + if (pDpInfo->dInitialScaleFactorW || pDpInfo->dInitialScaleFactorH) + { + crDebug("Set cached scale factor for seamless mode."); + pDpInfo->pWindow->SetScaleFactor((GLdouble)pDpInfo->dInitialScaleFactorW, (GLdouble)pDpInfo->dInitialScaleFactorH); + /* Invalidate cache. */ + pDpInfo->dInitialScaleFactorW = pDpInfo->dInitialScaleFactorH = 0; + } + } +} + +static void crPMgrDpWinCreate(CR_FBDISPLAY_INFO *pDpInfo) +{ + if (pDpInfo->pDpWinRootVr) + { + CRASSERT(pDpInfo->pDpWinRootVr == pDpInfo->pDpWin); + CrFbWindow *pWin = pDpInfo->pDpWin->windowDetach(); + CRASSERT(pWin); + Assert(pWin == pDpInfo->pWindow); + delete pDpInfo->pDpWinRootVr; + pDpInfo->pDpWinRootVr = NULL; + pDpInfo->pDpWin = NULL; + } + + if (!pDpInfo->pDpWin) + { + if (!pDpInfo->pWindow) + pDpInfo->pWindow = new CrFbWindow(0); + + pDpInfo->pDpWin = new CrFbDisplayWindow(&cr_server.screenVieport[pDpInfo->u32Id].Rect, cr_server.screen[pDpInfo->u32Id].winID); + pDpInfo->pDpWin->windowAttach(pDpInfo->pWindow); + + /* Set scale factor once it was previously cached when display output was not yet initialized. */ + if (pDpInfo->dInitialScaleFactorW || pDpInfo->dInitialScaleFactorH) + { + crDebug("Set cached scale factor for host window."); + pDpInfo->pWindow->SetScaleFactor((GLdouble)pDpInfo->dInitialScaleFactorW, (GLdouble)pDpInfo->dInitialScaleFactorH); + /* Invalidate cache. */ + pDpInfo->dInitialScaleFactorW = pDpInfo->dInitialScaleFactorH = 0; + } + } +} + +static int crPMgrFbDisconnectTargetDisplays(HCR_FRAMEBUFFER hFb, CR_FBDISPLAY_INFO *pDpInfo, uint32_t u32ModeRemove) +{ + int rc = VINF_SUCCESS; + if (u32ModeRemove & CR_PMGR_MODE_ROOTVR) + { + if (pDpInfo->pDpWinRootVr) + { +#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS + CrFbWindow *pWindow = pDpInfo->pDpWinRootVr->windowDetach(false); + Assert(pWindow == pDpInfo->pWindow); +#endif + CRASSERT(pDpInfo->pDpWin == pDpInfo->pDpWinRootVr); + rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWinRootVr); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnectDisplay pDpWinRootVr failed %d", rc)); + return rc; + } + } + } + else if (u32ModeRemove & CR_PMGR_MODE_WINDOW) + { + CRASSERT(!pDpInfo->pDpWinRootVr); + if (pDpInfo->pDpWin) + { +#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS + CrFbWindow *pWindow = pDpInfo->pDpWin->windowDetach(false); + Assert(pWindow == pDpInfo->pWindow); +#endif + rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpWin); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnectDisplay pDpWin failed %d", rc)); + return rc; + } + } + } + + if (u32ModeRemove & CR_PMGR_MODE_VRDP) + { + if (pDpInfo->pDpVrdp) + { + rc = crPMgrFbDisconnectDisplay(hFb, pDpInfo->pDpVrdp); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnectDisplay pDpVrdp failed %d", rc)); + return rc; + } + } + } + + pDpInfo->u32DisplayMode &= ~u32ModeRemove; + + return VINF_SUCCESS; +} + +static int crPMgrFbConnectTargetDisplays(HCR_FRAMEBUFFER hFb, CR_FBDISPLAY_INFO *pDpInfo, uint32_t u32ModeAdd) +{ + int rc = VINF_SUCCESS; + + if (u32ModeAdd & CR_PMGR_MODE_ROOTVR) + { + crPMgrDpWinRootVrCreate(pDpInfo); + + rc = crPMgrFbConnectDisplay(hFb, pDpInfo->pDpWinRootVr); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbConnectDisplay pDpWinRootVr failed %d", rc)); + return rc; + } + } + else if (u32ModeAdd & CR_PMGR_MODE_WINDOW) + { + crPMgrDpWinCreate(pDpInfo); + + rc = crPMgrFbConnectDisplay(hFb, pDpInfo->pDpWin); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbConnectDisplay pDpWin failed %d", rc)); + return rc; + } + } + + if (u32ModeAdd & CR_PMGR_MODE_VRDP) + { + if (!pDpInfo->pDpVrdp) + pDpInfo->pDpVrdp = new CrFbDisplayVrdp(); + + rc = crPMgrFbConnectDisplay(hFb, pDpInfo->pDpVrdp); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbConnectDisplay pDpVrdp failed %d", rc)); + return rc; + } + } + + pDpInfo->u32DisplayMode |= u32ModeAdd; + + return VINF_SUCCESS; +} + +static int crPMgrFbConnectTarget(HCR_FRAMEBUFFER hFb, uint32_t i) +{ + uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex; + CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb]; + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i]; + if (pDpInfo->iFb == idFb) + { + WARN(("target not connected")); + Assert(ASMBitTest(pFbInfo->aTargetMap, i)); + return VINF_SUCCESS; + } + + Assert(!ASMBitTest(pFbInfo->aTargetMap, i)); + + int rc = VINF_SUCCESS; + + if (pDpInfo->iFb != -1) + { + Assert(pDpInfo->iFb < cr_server.screenCount); + HCR_FRAMEBUFFER hAssignedFb = CrPMgrFbGet(pDpInfo->iFb); + Assert(hAssignedFb); + rc = crPMgrFbDisconnectTarget(hAssignedFb, i); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnectTarget failed %d", rc)); + return rc; + } + } + + rc = crPMgrFbConnectTargetDisplays(hFb, pDpInfo, g_CrPresenter.u32DisplayMode +#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS + & ~(CR_PMGR_MODE_WINDOW | CR_PMGR_MODE_ROOTVR) +#endif + ); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbConnectTargetDisplays failed %d", rc)); + return rc; + } + + ASMBitSet(pFbInfo->aTargetMap, i); + pDpInfo->iFb = idFb; + + return VINF_SUCCESS; +} + +static int crPMgrFbDisconnect(HCR_FRAMEBUFFER hFb, const uint32_t *pTargetMap) +{ + int rc = VINF_SUCCESS; + for (int i = ASMBitFirstSet(pTargetMap, cr_server.screenCount); + i >= 0; + i = ASMBitNextSet(pTargetMap, cr_server.screenCount, i)) + { + rc = crPMgrFbDisconnectTarget(hFb, (uint32_t)i); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnectTarget failed %d", rc)); + return rc; + } + } + + return VINF_SUCCESS; +} + +static int crPMgrFbConnect(HCR_FRAMEBUFFER hFb, const uint32_t *pTargetMap) +{ + int rc = VINF_SUCCESS; + for (int i = ASMBitFirstSet(pTargetMap, cr_server.screenCount); + i >= 0; + i = ASMBitNextSet(pTargetMap, cr_server.screenCount, i)) + { + rc = crPMgrFbConnectTarget(hFb, (uint32_t)i); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbConnectTarget failed %d", rc)); + return rc; + } + } + + return VINF_SUCCESS; +} + +static int crPMgrModeModifyTarget(HCR_FRAMEBUFFER hFb, uint32_t iDisplay, uint32_t u32ModeAdd, uint32_t u32ModeRemove) +{ + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[iDisplay]; + int rc = crPMgrFbDisconnectTargetDisplays(hFb, pDpInfo, u32ModeRemove); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnectTargetDisplays failed %d", rc)); + return rc; + } + + rc = crPMgrFbConnectTargetDisplays(hFb, pDpInfo, u32ModeAdd); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbConnectTargetDisplays failed %d", rc)); + return rc; + } + + return VINF_SUCCESS; +} + +static int crPMgrModeModify(HCR_FRAMEBUFFER hFb, uint32_t u32ModeAdd, uint32_t u32ModeRemove) +{ + int rc = VINF_SUCCESS; + uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex; + CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb]; + for (int i = ASMBitFirstSet(pFbInfo->aTargetMap, cr_server.screenCount); + i >= 0; + i = ASMBitNextSet(pFbInfo->aTargetMap, cr_server.screenCount, i)) + { + rc = crPMgrModeModifyTarget(hFb, (uint32_t)i, u32ModeAdd, u32ModeRemove); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrModeModifyTarget failed %d", rc)); + return rc; + } + } + + return VINF_SUCCESS; +} + +static void crPMgrCleanUnusedDisplays() +{ + for (int i = 0; i < cr_server.screenCount; ++i) + { + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i]; + + if (pDpInfo->pDpWinRootVr) + { + if (!pDpInfo->pDpWinRootVr->getFramebuffer()) + { + pDpInfo->pDpWinRootVr->windowDetach(false); + delete pDpInfo->pDpWinRootVr; + pDpInfo->pDpWinRootVr = NULL; + pDpInfo->pDpWin = NULL; + if (pDpInfo->pWindow) + { + delete pDpInfo->pWindow; + pDpInfo->pWindow = NULL; + } + } + else + WARN(("pDpWinRootVr is used")); + } + else if (pDpInfo->pDpWin) + { + if (!pDpInfo->pDpWin->getFramebuffer()) + { + pDpInfo->pDpWin->windowDetach(false); + delete pDpInfo->pDpWin; + pDpInfo->pDpWin = NULL; + if (pDpInfo->pWindow) + { + delete pDpInfo->pWindow; + pDpInfo->pWindow = NULL; + } + } + else + WARN(("pDpWin is used")); + } + + if (pDpInfo->pDpVrdp) + { + if (!pDpInfo->pDpVrdp->getFramebuffer()) + { + delete pDpInfo->pDpVrdp; + pDpInfo->pDpVrdp = NULL; + } + else + WARN(("pDpVrdp is used")); + } + } +} + +static int crPMgrModeModifyGlobal(uint32_t u32ModeAdd, uint32_t u32ModeRemove) +{ + uint32_t u32InternalMode = g_CrPresenter.fEnabled ? g_CrPresenter.u32DisplayMode : g_CrPresenter.u32DisabledDisplayMode; + + u32ModeRemove = ((u32ModeRemove | crPMgrModeAdjustVal(u32ModeRemove)) & CR_PMGR_MODE_ALL); + u32ModeAdd = crPMgrModeAdjustVal(u32ModeAdd); + u32ModeRemove &= u32InternalMode; + u32ModeAdd &= ~(u32ModeRemove | u32InternalMode); + uint32_t u32ModeResulting = ((u32InternalMode | u32ModeAdd) & ~u32ModeRemove); + uint32_t u32Tmp = crPMgrModeAdjustVal(u32ModeResulting); + if (u32Tmp != u32ModeResulting) + { + u32ModeAdd |= (u32Tmp & ~u32ModeResulting); + u32ModeRemove |= (~u32Tmp & u32ModeResulting); + u32ModeResulting = u32Tmp; + Assert(u32ModeResulting == ((u32InternalMode | u32ModeAdd) & ~u32ModeRemove)); + } + if (!u32ModeRemove && !u32ModeAdd) + return VINF_SUCCESS; + + uint32_t u32DisplayMode = (g_CrPresenter.u32DisplayMode | u32ModeAdd) & ~u32ModeRemove; + if (!g_CrPresenter.fEnabled) + { + Assert(g_CrPresenter.u32DisplayMode == 0); + g_CrPresenter.u32DisabledDisplayMode = u32DisplayMode; + return VINF_SUCCESS; + } + + g_CrPresenter.u32DisplayMode = u32DisplayMode; + + /* disabled framebuffers may still have displays attached */ + for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstInitialized(); + hFb; + hFb = CrPMgrFbGetNextInitialized(hFb)) + { + crPMgrModeModify(hFb, u32ModeAdd, u32ModeRemove); + } + + return VINF_SUCCESS; +} + +int CrPMgrClearRegionsGlobal() +{ + for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + int rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + rc = CrFbRegionsClear(hFb); + if (RT_FAILURE(rc)) + { + WARN(("CrFbRegionsClear failed %d", rc)); + } + + CrFbUpdateEnd(hFb); + } + } + + return VINF_SUCCESS; +} + +int CrPMgrModeVrdp(bool fEnable) +{ + uint32_t u32ModeAdd, u32ModeRemove; + if (fEnable) + { + u32ModeAdd = CR_PMGR_MODE_VRDP; + u32ModeRemove = 0; + } + else + { + u32ModeAdd = 0; + u32ModeRemove = CR_PMGR_MODE_VRDP; + } + return crPMgrModeModifyGlobal(u32ModeAdd, u32ModeRemove); +} + +int CrPMgrModeRootVr(bool fEnable) +{ + uint32_t u32ModeAdd, u32ModeRemove; + if (fEnable) + { + u32ModeAdd = CR_PMGR_MODE_ROOTVR; + u32ModeRemove = CR_PMGR_MODE_WINDOW; + } + else + { + u32ModeAdd = CR_PMGR_MODE_WINDOW; + u32ModeRemove = CR_PMGR_MODE_ROOTVR; + } + + return crPMgrModeModifyGlobal(u32ModeAdd, u32ModeRemove); +} + +int CrPMgrModeWinVisible(bool fEnable) +{ + if (!g_CrPresenter.fWindowsForceHidden == !!fEnable) + return VINF_SUCCESS; + + g_CrPresenter.fWindowsForceHidden = !fEnable; + + for (int i = 0; i < cr_server.screenCount; ++i) + { + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i]; + + if (pDpInfo->pDpWin) + pDpInfo->pDpWin->winVisibilityChanged(); + } + + return VINF_SUCCESS; +} + +int CrPMgrRootVrUpdate() +{ + for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + if (!CrFbHas3DData(hFb)) + continue; + + uint32_t idFb = CrFbGetScreenInfo(hFb)->u32ViewIndex; + CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb]; + int rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + for (int i = ASMBitFirstSet(pFbInfo->aTargetMap, cr_server.screenCount); + i >= 0; + i = ASMBitNextSet(pFbInfo->aTargetMap, cr_server.screenCount, i)) + { + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i]; + Assert(pDpInfo->iFb == (int32_t)idFb); + + pDpInfo->pDpWinRootVr->RegionsChanged(hFb); + } + + CrFbUpdateEnd(hFb); + } + else + WARN(("CrFbUpdateBegin failed %d", rc)); + } + + return VINF_SUCCESS; +} + +/*helper function that calls CrFbUpdateBegin for all enabled framebuffers */ +int CrPMgrHlpGlblUpdateBegin(CR_FBMAP *pMap) +{ + CrFBmInit(pMap); + for (HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + int rc = CrFbUpdateBegin(hFb); + if (!RT_SUCCESS(rc)) + { + WARN(("UpdateBegin failed, rc %d", rc)); + for (HCR_FRAMEBUFFER hTmpFb = CrPMgrFbGetFirstEnabled(); + hFb != hTmpFb; + hTmpFb = CrPMgrFbGetNextEnabled(hTmpFb)) + { + CrFbUpdateEnd(hTmpFb); + CrFBmClear(pMap, CrFbGetScreenInfo(hFb)->u32ViewIndex); + } + return rc; + } + + CrFBmSet(pMap, CrFbGetScreenInfo(hFb)->u32ViewIndex); + } + + return VINF_SUCCESS; +} + +/*helper function that calls CrFbUpdateEnd for all framebuffers being updated */ +void CrPMgrHlpGlblUpdateEnd(CR_FBMAP *pMap) +{ + for (uint32_t i = 0; i < (uint32_t)cr_server.screenCount; ++i) + { + if (!CrFBmIsSet(pMap, i)) + continue; + + HCR_FRAMEBUFFER hFb = CrPMgrFbGetInitialized(i); + CRASSERT(hFb); + CrFbUpdateEnd(hFb); + } +} + +int CrPMgrResize(const struct VBVAINFOSCREEN *pScreen, void *pvVRAM, const uint32_t *pTargetMap) +{ + int rc = VINF_SUCCESS; + + if (pScreen->u32ViewIndex == 0xffffffff) + { + /* this is just a request to disable targets, search and disable */ + for (int i = ASMBitFirstSet(pTargetMap, cr_server.screenCount); + i >= 0; + i = ASMBitNextSet(pTargetMap, cr_server.screenCount, i)) + { + CR_FBDISPLAY_INFO *pDpInfo = &g_CrPresenter.aDisplayInfos[i]; + if (pDpInfo->iFb < 0) + continue; + + Assert(pDpInfo->iFb < cr_server.screenCount); + HCR_FRAMEBUFFER hAssignedFb = CrPMgrFbGet(pDpInfo->iFb); + + rc = crPMgrFbDisconnectTarget(hAssignedFb, (uint32_t)i); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnectTarget failed %d", rc)); + return rc; + } + } + + return VINF_SUCCESS; + } + + HCR_FRAMEBUFFER hFb = CrPMgrFbGet(pScreen->u32ViewIndex); + if (!hFb) + { + WARN(("CrPMgrFbGet failed")); + return VERR_INVALID_PARAMETER; + } + + const VBVAINFOSCREEN *pFbScreen = CrFbGetScreenInfo(hFb); + bool fFbInfoChanged = true; + + if (!memcmp(pFbScreen, pScreen, sizeof (*pScreen))) + { + if (!pvVRAM || pvVRAM == CrFbGetVRAM(hFb)) + fFbInfoChanged = false; + } + + CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[pScreen->u32ViewIndex]; + + VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aRemovedTargetMap); + VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aAddedTargetMap); + + bool fDisplaysAdded = false, fDisplaysRemoved = false; + + memcpy(aRemovedTargetMap, pFbInfo->aTargetMap, sizeof (aRemovedTargetMap)); + + if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED) + { + /* so far there is no need in keeping displays attached to disabled Framebffer, + * just disconnect everything */ + for (int i = 0; i < RT_ELEMENTS(aRemovedTargetMap); ++i) + { + if (aRemovedTargetMap[i]) + { + fDisplaysRemoved = true; + break; + } + } + + memset(aAddedTargetMap, 0, sizeof (aAddedTargetMap)); + } + else + { + for (int i = 0; i < RT_ELEMENTS(aRemovedTargetMap); ++i) + { + aRemovedTargetMap[i] = (aRemovedTargetMap[i] & ~pTargetMap[i]); + if (aRemovedTargetMap[i]) + fDisplaysRemoved = true; + } + + memcpy(aAddedTargetMap, pFbInfo->aTargetMap, sizeof (aAddedTargetMap)); + for (int i = 0; i < RT_ELEMENTS(aAddedTargetMap); ++i) + { + aAddedTargetMap[i] = (pTargetMap[i] & ~aAddedTargetMap[i]); + if (aAddedTargetMap[i]) + fDisplaysAdded = true; + } + } + + if (!fFbInfoChanged && !fDisplaysRemoved && !fDisplaysAdded) + { + crDebug("resize: no changes"); + return VINF_SUCCESS; + } + + if (fDisplaysRemoved) + { + rc = crPMgrFbDisconnect(hFb, aRemovedTargetMap); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbDisconnect failed %d", rc)); + return rc; + } + } + + if (fFbInfoChanged) + { +#ifdef CR_SERVER_WITH_CLIENT_CALLOUTS + rc = crPMgrModeModify(hFb, 0, CR_PMGR_MODE_WINDOW | CR_PMGR_MODE_ROOTVR); + if (!RT_SUCCESS(rc)) + { + WARN(("crPMgrModeModifyTarget failed %d", rc)); + return rc; + } +#endif + rc = CrFbUpdateBegin(hFb); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbUpdateBegin failed %d", rc)); + return rc; + } + + crVBoxServerMuralFbResizeBegin(hFb); + + rc = CrFbResize(hFb, pScreen, pvVRAM); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbResize failed %d", rc)); + } + + crVBoxServerMuralFbResizeEnd(hFb); + + CrFbUpdateEnd(hFb); + } + + if (fDisplaysAdded) + { + rc = crPMgrFbConnect(hFb, aAddedTargetMap); + if (RT_FAILURE(rc)) + { + WARN(("crPMgrFbConnect failed %d", rc)); + return rc; + } + } + + return VINF_SUCCESS; +} + +int CrFbEntrySaveState(CR_FRAMEBUFFER *pFb, CR_FRAMEBUFFER_ENTRY *hEntry, PSSMHANDLE pSSM) +{ + const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrFbEntryGetCompositorEntry(hEntry); + CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData); + int rc = SSMR3PutU32(pSSM, pFbTex->pTobj->id); + AssertRCReturn(rc, rc); + uint32_t u32 = 0; + + u32 = CrVrScrCompositorEntryFlagsGet(pEntry); + rc = SSMR3PutU32(pSSM, u32); + AssertRCReturn(rc, rc); + + const RTRECT *pRect = CrVrScrCompositorEntryRectGet(pEntry); + + rc = SSMR3PutS32(pSSM, pRect->xLeft); + AssertRCReturn(rc, rc); + rc = SSMR3PutS32(pSSM, pRect->yTop); + AssertRCReturn(rc, rc); +#if 0 + rc = SSMR3PutS32(pSSM, pRect->xRight); + AssertRCReturn(rc, rc); + rc = SSMR3PutS32(pSSM, pRect->yBottom); + AssertRCReturn(rc, rc); +#endif + + rc = CrVrScrCompositorEntryRegionsGet(&pFb->Compositor, pEntry, &u32, NULL, NULL, &pRect); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, u32); + AssertRCReturn(rc, rc); + + if (u32) + { + rc = SSMR3PutMem(pSSM, pRect, u32 * sizeof (*pRect)); + AssertRCReturn(rc, rc); + } + return rc; +} + +int CrFbSaveState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM) +{ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR Iter; + CrVrScrCompositorConstIterInit(&pFb->Compositor, &Iter); + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + uint32_t u32 = 0; + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CRASSERT(pTexData); + CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData); + if (pFbTex->pTobj) + ++u32; + } + + int rc = SSMR3PutU32(pSSM, u32); + AssertRCReturn(rc, rc); + + CrVrScrCompositorConstIterInit(&pFb->Compositor, &Iter); + + while ((pEntry = CrVrScrCompositorConstIterNext(&Iter)) != NULL) + { + CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData); + if (pFbTex->pTobj) + { + HCR_FRAMEBUFFER_ENTRY hEntry = CrFbEntryFromCompositorEntry(pEntry); + rc = CrFbEntrySaveState(pFb, hEntry, pSSM); + AssertRCReturn(rc, rc); + } + } + + return VINF_SUCCESS; +} + +int CrPMgrSaveState(PSSMHANDLE pSSM) +{ + int rc; + int cDisplays = 0, i; + + for (i = 0; i < cr_server.screenCount; ++i) + { + if (CrPMgrFbGetEnabled(i)) + ++cDisplays; + } + + rc = SSMR3PutS32(pSSM, cDisplays); + AssertRCReturn(rc, rc); + + if (!cDisplays) + return VINF_SUCCESS; + + rc = SSMR3PutS32(pSSM, cr_server.screenCount); + AssertRCReturn(rc, rc); + + for (i = 0; i < cr_server.screenCount; ++i) + { + CR_FRAMEBUFFER *hFb = CrPMgrFbGetEnabled(i); + if (hFb) + { + Assert(hFb->ScreenInfo.u32ViewIndex == i); + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32ViewIndex); + AssertRCReturn(rc, rc); + + rc = SSMR3PutS32(pSSM, hFb->ScreenInfo.i32OriginX); + AssertRCReturn(rc, rc); + + rc = SSMR3PutS32(pSSM, hFb->ScreenInfo.i32OriginY); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32StartOffset); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32LineSize); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32Width); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32Height); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU16(pSSM, hFb->ScreenInfo.u16BitsPerPixel); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU16(pSSM, hFb->ScreenInfo.u16Flags); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, hFb->ScreenInfo.u32StartOffset); + AssertRCReturn(rc, rc); + + CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[hFb->ScreenInfo.u32ViewIndex]; + rc = SSMR3PutMem(pSSM, pFbInfo->aTargetMap, sizeof (pFbInfo->aTargetMap)); + AssertRCReturn(rc, rc); + + rc = CrFbSaveState(hFb, pSSM); + AssertRCReturn(rc, rc); + } + } + + return VINF_SUCCESS; +} + +int CrFbEntryLoadState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM, uint32_t version) +{ + uint32_t texture; + int rc = SSMR3GetU32(pSSM, &texture); + AssertRCReturn(rc, rc); + + uint32_t fFlags; + rc = SSMR3GetU32(pSSM, &fFlags); + AssertRCReturn(rc, rc); + + + HCR_FRAMEBUFFER_ENTRY hEntry; + + rc = CrFbEntryCreateForTexId(pFb, texture, fFlags, &hEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbEntryCreateForTexId Failed")); + return rc; + } + + Assert(hEntry); + + const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry = CrFbEntryGetCompositorEntry(hEntry); + CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CR_FBTEX *pFbTex = PCR_FBTEX_FROM_TEX(pTexData); + + RTPOINT Point; + rc = SSMR3GetS32(pSSM, &Point.x); + AssertRCReturn(rc, rc); + + rc = SSMR3GetS32(pSSM, &Point.y); + AssertRCReturn(rc, rc); + + uint32_t cRects; + rc = SSMR3GetU32(pSSM, &cRects); + AssertRCReturn(rc, rc); + + RTRECT * pRects = NULL; + if (cRects) + { + pRects = (RTRECT *)crAlloc(cRects * sizeof (*pRects)); + AssertReturn(pRects, VERR_NO_MEMORY); + + rc = SSMR3GetMem(pSSM, pRects, cRects * sizeof (*pRects)); + AssertRCReturn(rc, rc); + } + + rc = CrFbEntryRegionsSet(pFb, hEntry, &Point, cRects, pRects, false); + AssertRCReturn(rc, rc); + + if (pRects) + crFree(pRects); + + CrFbEntryRelease(pFb, hEntry); + + return VINF_SUCCESS; +} + +int CrFbLoadState(CR_FRAMEBUFFER *pFb, PSSMHANDLE pSSM, uint32_t version) +{ + uint32_t u32 = 0; + int rc = SSMR3GetU32(pSSM, &u32); + AssertRCReturn(rc, rc); + + if (!u32) + return VINF_SUCCESS; + + rc = CrFbUpdateBegin(pFb); + AssertRCReturn(rc, rc); + + for (uint32_t i = 0; i < u32; ++i) + { + rc = CrFbEntryLoadState(pFb, pSSM, version); + AssertRCReturn(rc, rc); + } + + CrFbUpdateEnd(pFb); + + return VINF_SUCCESS; +} + +int CrPMgrLoadState(PSSMHANDLE pSSM, uint32_t version) +{ + int rc; + int cDisplays, screenCount, i; + + rc = SSMR3GetS32(pSSM, &cDisplays); + AssertRCReturn(rc, rc); + + if (!cDisplays) + return VINF_SUCCESS; + + rc = SSMR3GetS32(pSSM, &screenCount); + AssertRCReturn(rc, rc); + + CRASSERT(screenCount == cr_server.screenCount); + + CRScreenInfo screen[CR_MAX_GUEST_MONITORS]; + + if (version < SHCROGL_SSM_VERSION_WITH_FB_INFO) + { + for (i = 0; i < cr_server.screenCount; ++i) + { + rc = SSMR3GetS32(pSSM, &screen[i].x); + AssertRCReturn(rc, rc); + + rc = SSMR3GetS32(pSSM, &screen[i].y); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &screen[i].w); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &screen[i].h); + AssertRCReturn(rc, rc); + } + } + + for (i = 0; i < cDisplays; ++i) + { + int iScreen; + + rc = SSMR3GetS32(pSSM, &iScreen); + AssertRCReturn(rc, rc); + + CR_FRAMEBUFFER *pFb = CrPMgrFbGet(iScreen); + Assert(pFb); + + VBVAINFOSCREEN Screen; + + Screen.u32ViewIndex = iScreen; + + VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap); + + memset(aTargetMap, 0, sizeof (aTargetMap)); + ASMBitSet(aTargetMap, iScreen); + + if (version < SHCROGL_SSM_VERSION_WITH_FB_INFO) + { + memset(&Screen, 0, sizeof (Screen)); + Screen.u32LineSize = 4 * screen[iScreen].w; + Screen.u32Width = screen[iScreen].w; + Screen.u32Height = screen[iScreen].h; + Screen.u16BitsPerPixel = 4; + Screen.u16Flags = VBVA_SCREEN_F_ACTIVE; + } + else + { + rc = SSMR3GetS32(pSSM, &Screen.i32OriginX); + AssertRCReturn(rc, rc); + + rc = SSMR3GetS32(pSSM, &Screen.i32OriginY); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &Screen.u32StartOffset); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &Screen.u32LineSize); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &Screen.u32Width); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &Screen.u32Height); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU16(pSSM, &Screen.u16BitsPerPixel); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU16(pSSM, &Screen.u16Flags); + AssertRCReturn(rc, rc); + + rc = SSMR3GetU32(pSSM, &Screen.u32StartOffset); + AssertRCReturn(rc, rc); + if (Screen.u32StartOffset == 0xffffffff) + { + WARN(("not expected offVram")); + Screen.u32StartOffset = 0; + } + + if (version >= SHCROGL_SSM_VERSION_WITH_SCREEN_MAP_REORDERED) + { + rc = SSMR3GetMem(pSSM, aTargetMap, sizeof (aTargetMap)); + AssertRCReturn(rc, rc); + } + + if (version == SHCROGL_SSM_VERSION_WITH_SCREEN_MAP) + { + VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aEmptyTargetMap); + + memset(aEmptyTargetMap, 0, sizeof (aEmptyTargetMap)); + + rc = CrPMgrResize(&Screen, cr_server.fCrCmdEnabled ? NULL : CrFbGetVRAM(pFb), aEmptyTargetMap); + AssertRCReturn(rc, rc); + + rc = CrFbLoadState(pFb, pSSM, version); + AssertRCReturn(rc, rc); + + rc = SSMR3GetMem(pSSM, aTargetMap, sizeof (aTargetMap)); + AssertRCReturn(rc, rc); + } + } + + rc = CrPMgrResize(&Screen, cr_server.fCrCmdEnabled ? NULL : CrFbGetVRAM(pFb), aTargetMap); + AssertRCReturn(rc, rc); + + if (version >= SHCROGL_SSM_VERSION_WITH_FB_INFO && version != SHCROGL_SSM_VERSION_WITH_SCREEN_MAP) + { + rc = CrFbLoadState(pFb, pSSM, version); + AssertRCReturn(rc, rc); + } + } + + return VINF_SUCCESS; +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchVBoxTexPresent(GLuint texture, GLuint cfg, GLint xPos, GLint yPos, GLint cRects, const GLint *pRects) +{ + uint32_t idFb = CR_PRESENT_GET_SCREEN(cfg); + if (idFb >= CR_MAX_GUEST_MONITORS) + { + WARN(("Invalid guest screen")); + return; + } + + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(idFb); + if (!hFb) + { + WARN(("request to present on disabled framebuffer, ignore")); + return; + } + + HCR_FRAMEBUFFER_ENTRY hEntry; + int rc; + if (texture) + { + rc = CrFbEntryCreateForTexId(hFb, texture, (cfg & CR_PRESENT_FLAG_TEX_NONINVERT_YCOORD) ? 0 : CRBLT_F_INVERT_SRC_YCOORDS, &hEntry); + if (!RT_SUCCESS(rc)) + { + LOG(("CrFbEntryCreateForTexId Failed")); + return; + } + + Assert(hEntry); + +#if 0 + if (!(cfg & CR_PRESENT_FLAG_CLEAR_RECTS)) + { + CR_SERVER_DUMP_TEXPRESENT(&pEntry->CEntry.Tex); + } +#endif + } + else + hEntry = NULL; + + rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + if (!(cfg & CR_PRESENT_FLAG_CLEAR_RECTS)) + { + RTPOINT Point = {xPos, yPos}; + rc = CrFbEntryRegionsAdd(hFb, hEntry, &Point, (uint32_t)cRects, (const RTRECT*)pRects, false); + } + else + { + CrFbRegionsClear(hFb); + } + + CrFbUpdateEnd(hFb); + } + else + { + WARN(("CrFbUpdateBegin Failed")); + } + + if (hEntry) + CrFbEntryRelease(hFb, hEntry); +} + +DECLINLINE(void) crVBoxPRectUnpack(const VBOXCMDVBVA_RECT *pVbvaRect, RTRECT *pRect) +{ + pRect->xLeft = pVbvaRect->xLeft; + pRect->yTop = pVbvaRect->yTop; + pRect->xRight = pVbvaRect->xRight; + pRect->yBottom = pVbvaRect->yBottom; +} + +DECLINLINE(void) crVBoxPRectUnpacks(const VBOXCMDVBVA_RECT *paVbvaRects, RTRECT *paRects, uint32_t cRects) +{ + uint32_t i = 0; + for (; i < cRects; ++i) + { + crVBoxPRectUnpack(&paVbvaRects[i], &paRects[i]); + } +} + +static RTRECT * crVBoxServerCrCmdBltRecsUnpack(const VBOXCMDVBVA_RECT *pPRects, uint32_t cRects) +{ + if (g_CrPresenter.cbTmpBuf < cRects * sizeof (RTRECT)) + { + if (g_CrPresenter.pvTmpBuf) + RTMemFree(g_CrPresenter.pvTmpBuf); + + g_CrPresenter.cbTmpBuf = (cRects + 10) * sizeof (RTRECT); + g_CrPresenter.pvTmpBuf = RTMemAlloc(g_CrPresenter.cbTmpBuf); + if (!g_CrPresenter.pvTmpBuf) + { + WARN(("RTMemAlloc failed!")); + g_CrPresenter.cbTmpBuf = 0; + return NULL; + } + } + + RTRECT *pRects = (RTRECT *)g_CrPresenter.pvTmpBuf; + crVBoxPRectUnpacks(pPRects, pRects, cRects); + + return pRects; +} + +static void crPMgrPrimaryUpdateScreen(HCR_FRAMEBUFFER hFb, uint32_t idScreen, uint32_t cRects, const RTRECT *pRects) +{ + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + + bool fDirtyEmpty = true; + RTRECT dirtyRect = {0}; + cr_server.CrCmdClientInfo.pfnCltScrUpdateBegin(cr_server.CrCmdClientInfo.hCltScr, idScreen); + + VBVACMDHDR hdr; + for (uint32_t i = 0; i < cRects; ++i) + { + hdr.x = pRects[i].xLeft; + hdr.y = pRects[i].yTop; + hdr.w = hdr.x + pRects[i].xRight; + hdr.h = hdr.y + pRects[i].yBottom; + + cr_server.CrCmdClientInfo.pfnCltScrUpdateProcess(cr_server.CrCmdClientInfo.hCltScr, idScreen, &hdr, sizeof (hdr)); + + if (fDirtyEmpty) + { + /* This is the first rectangle to be added. */ + dirtyRect.xLeft = pRects[i].xLeft; + dirtyRect.yTop = pRects[i].yTop; + dirtyRect.xRight = pRects[i].xRight; + dirtyRect.yBottom = pRects[i].yBottom; + fDirtyEmpty = false; + } + else + { + /* Adjust region coordinates. */ + if (dirtyRect.xLeft > pRects[i].xLeft) + { + dirtyRect.xLeft = pRects[i].xLeft; + } + + if (dirtyRect.yTop > pRects[i].yTop) + { + dirtyRect.yTop = pRects[i].yTop; + } + + if (dirtyRect.xRight < pRects[i].xRight) + { + dirtyRect.xRight = pRects[i].xRight; + } + + if (dirtyRect.yBottom < pRects[i].yBottom) + { + dirtyRect.yBottom = pRects[i].yBottom; + } + } + } + + if (dirtyRect.xRight - dirtyRect.xLeft) + { + cr_server.CrCmdClientInfo.pfnCltScrUpdateEnd(cr_server.CrCmdClientInfo.hCltScr, idScreen, pScreen->i32OriginX + dirtyRect.xLeft, pScreen->i32OriginY + dirtyRect.yTop, + dirtyRect.xRight - dirtyRect.xLeft, dirtyRect.yBottom - dirtyRect.yTop); + } + else + { + cr_server.CrCmdClientInfo.pfnCltScrUpdateEnd(cr_server.CrCmdClientInfo.hCltScr, idScreen, 0, 0, 0, 0); + } + +} + +static void crPMgrPrimaryUpdate(HCR_FRAMEBUFFER hFb, uint32_t cRects, const RTRECT *pRects) +{ + if (!cRects) + return; + + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + + uint32_t idFb = pScreen->u32ViewIndex; + CR_FB_INFO *pFbInfo = &g_CrPresenter.aFbInfos[idFb]; + + for (int i = ASMBitFirstSet(pFbInfo->aTargetMap, cr_server.screenCount); + i >= 0; + i = ASMBitNextSet(pFbInfo->aTargetMap, cr_server.screenCount, i)) + { + crPMgrPrimaryUpdateScreen(hFb, i, cRects, pRects); + } +} + +static int8_t crVBoxServerCrCmdBltPrimaryVramGenericProcess(uint32_t u32PrimaryID, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects, bool fToPrimary) +{ + CR_BLITTER_IMG Img; + int8_t i8Result = crFbImgFromDimOffVramBGRA(offVRAM, width, height, &Img); + if (i8Result) + { + WARN(("invalid param")); + return -1; + } + + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u32PrimaryID); + if (!hFb) + { + WARN(("request to present on disabled framebuffer")); + return -1; + } + + if (!fToPrimary) + { + int rc = CrFbBltGetContents(hFb, pPos, cRects, pRects, &Img); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbBltGetContents failed %d", rc)); + return -1; + } + + return 0; + } + + int rc = CrFbBltPutContentsNe(hFb, pPos, cRects, pRects, &Img); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbBltPutContentsNe failed %d", rc)); + return -1; + } + + return 0; +} + +static int8_t crVBoxServerCrCmdBltPrimaryProcess(const VBOXCMDVBVA_BLT_PRIMARY *pCmd, uint32_t cbCmd) +{ + uint32_t u32PrimaryID = (uint32_t)pCmd->Hdr.Hdr.u.u8PrimaryID; + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u32PrimaryID); + if (!hFb) + { + WARN(("request to present on disabled framebuffer, ignore")); + return 0; + } + + uint32_t cRects; + const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects; + if ((cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_PRIMARY, aRects)) % sizeof (VBOXCMDVBVA_RECT)) + { + WARN(("invalid argument size")); + return -1; + } + + cRects = (cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_PRIMARY, aRects)) / sizeof (VBOXCMDVBVA_RECT); + + RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects); + if (!pRects) + { + WARN(("crVBoxServerCrCmdBltRecsUnpack failed")); + return -1; + } + + uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags; + + if (u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID) + { + uint32_t texId = pCmd->alloc.u.id; + if (!texId) + { + WARN(("texId is NULL!\n")); + return -1; + } + + if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2) + { + WARN(("blit from primary to texture not implemented")); + return -1; + } + + crServerDispatchVBoxTexPresent(texId, u32PrimaryID, pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y, cRects, (const GLint*)pRects); + + return 0; + } + else + { + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + uint32_t width = pScreen->u32Width, height = pScreen->u32Height; + VBOXCMDVBVAOFFSET offVRAM = pCmd->alloc.u.offVRAM; + + bool fToPrymary = !(u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2); + RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y}; + int8_t i8Result = crVBoxServerCrCmdBltPrimaryVramGenericProcess(u32PrimaryID, offVRAM, width, height, &Pos, cRects, pRects, fToPrymary); + if (i8Result < 0) + { + WARN(("crVBoxServerCrCmdBltPrimaryVramGenericProcess failed")); + return i8Result; + } + + if (!fToPrymary) + return 0; + } + + crPMgrPrimaryUpdate(hFb, cRects, pRects); + + return 0; +} + +static int8_t crVBoxServerCrCmdBltIdToVramMem(uint32_t hostId, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects) +{ + CR_TEXDATA* pTex = CrFbTexDataAcquire(hostId); + if (!pTex) + { + WARN(("pTex failed for %d", hostId)); + return -1; + } + + const VBOXVR_TEXTURE *pVrTex = CrTdTexGet(pTex); + if (!width) + { + width = pVrTex->width; + height = pVrTex->height; + } + + CR_BLITTER_IMG Img; + int8_t i8Result = crFbImgFromDimOffVramBGRA(offVRAM, width, height, &Img); + if (i8Result) + { + WARN(("invalid param")); + return -1; + } + + int rc = CrTdBltEnter(pTex); + if (!RT_SUCCESS(rc)) + { + WARN(("CrTdBltEnter failed %d", rc)); + return -1; + } + + rc = crFbTexDataGetContents(pTex, pPos, cRects, pRects, &Img); + + CrTdBltLeave(pTex); + + CrTdRelease(pTex); + + if (!RT_SUCCESS(rc)) + { + WARN(("crFbTexDataGetContents failed %d", rc)); + return -1; + } + + return 0; +} + +static int8_t crVBoxServerCrCmdBltIdToVram(uint32_t hostId, VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects) +{ + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabledByVramStart(offVRAM); + if (hFb) + { + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + Assert(!width || pScreen->u32Width == width); + Assert(!height || pScreen->u32Height == height); + + crServerDispatchVBoxTexPresent(hostId, pScreen->u32ViewIndex, pPos->x, pPos->y, cRects, (const GLint*)pRects); + return 0; + } + + return crVBoxServerCrCmdBltIdToVramMem(hostId, offVRAM, width, height, pPos, cRects, pRects); +} + +static int8_t crVBoxServerCrCmdBltVramToVramMem(VBOXCMDVBVAOFFSET offSrcVRAM, uint32_t srcWidth, uint32_t srcHeight, VBOXCMDVBVAOFFSET offDstVRAM, uint32_t dstWidth, uint32_t dstHeight, const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects) +{ + CR_BLITTER_IMG srcImg, dstImg; + int8_t i8Result = crFbImgFromDimOffVramBGRA(offSrcVRAM, srcWidth, srcHeight, &srcImg); + if (i8Result) + { + WARN(("invalid param")); + return -1; + } + + i8Result = crFbImgFromDimOffVramBGRA(offDstVRAM, dstWidth, dstHeight, &dstImg); + if (i8Result) + { + WARN(("invalid param")); + return -1; + } + + CrMBltImg(&srcImg, pPos, cRects, pRects, &dstImg); + + return 0; +} + +static int8_t crVBoxServerCrCmdBltVramToVram(VBOXCMDVBVAOFFSET offSrcVRAM, uint32_t srcWidth, uint32_t srcHeight, + VBOXCMDVBVAOFFSET offDstVRAM, uint32_t dstWidth, uint32_t dstHeight, + const RTPOINT *pPos, uint32_t cRects, const RTRECT *pRects) +{ + HCR_FRAMEBUFFER hSrcFb = CrPMgrFbGetEnabledByVramStart(offSrcVRAM); + HCR_FRAMEBUFFER hDstFb = CrPMgrFbGetEnabledByVramStart(offDstVRAM); + + if (hDstFb) + { + if (hSrcFb) + { + LOG(("blit from one framebuffer, wow")); + + int rc = CrFbUpdateBegin(hSrcFb); + if (RT_SUCCESS(rc)) + { + CrFbRegionsClear(hSrcFb); + + CrFbUpdateEnd(hSrcFb); + } + else + WARN(("CrFbUpdateBegin failed %d", rc)); + } + + CR_BLITTER_IMG Img; + int8_t i8Result = crFbImgFromDimOffVramBGRA(offSrcVRAM, srcWidth, srcHeight, &Img); + if (i8Result) + { + WARN(("invalid param")); + return -1; + } + + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hDstFb); + if (pScreen->u32Width == dstWidth && pScreen->u32Height == dstHeight) + { + int rc = CrFbBltPutContentsNe(hDstFb, pPos, cRects, pRects, &Img); + if (RT_FAILURE(rc)) + { + WARN(("CrFbBltPutContentsNe failed %d", rc)); + return -1; + } + } + else + { + int rc = CrFbUpdateBegin(hDstFb); + if (RT_SUCCESS(rc)) + { + CrFbRegionsClear(hDstFb); + + CrFbUpdateEnd(hDstFb); + } + else + WARN(("CrFbUpdateBegin failed %d", rc)); + + rc = crVBoxServerCrCmdBltVramToVramMem(offSrcVRAM, srcWidth, srcHeight, offDstVRAM, dstWidth, dstHeight, pPos, cRects, pRects); + if (RT_FAILURE(rc)) + { + WARN(("crVBoxServerCrCmdBltVramToVramMem failed, %d", rc)); + return -1; + } + } + + crPMgrPrimaryUpdate(hDstFb, cRects, pRects); + + return 0; + } + else if (hSrcFb) + { + CR_BLITTER_IMG Img; + int8_t i8Result = crFbImgFromDimOffVramBGRA(offDstVRAM, dstWidth, dstHeight, &Img); + if (i8Result) + { + WARN(("invalid param")); + return -1; + } + + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hSrcFb); + if (pScreen->u32Width == srcWidth && pScreen->u32Height == srcHeight) + { + int rc = CrFbBltGetContents(hSrcFb, pPos, cRects, pRects, &Img); + if (RT_FAILURE(rc)) + { + WARN(("CrFbBltGetContents failed %d", rc)); + return -1; + } + } + else + { + int rc = CrFbUpdateBegin(hSrcFb); + if (RT_SUCCESS(rc)) + { + CrFbRegionsClear(hSrcFb); + + CrFbUpdateEnd(hSrcFb); + } + else + WARN(("CrFbUpdateBegin failed %d", rc)); + + rc = crVBoxServerCrCmdBltVramToVramMem(offSrcVRAM, srcWidth, srcHeight, offDstVRAM, dstWidth, dstHeight, pPos, cRects, pRects); + if (RT_FAILURE(rc)) + { + WARN(("crVBoxServerCrCmdBltVramToVramMem failed, %d", rc)); + return -1; + } + } + + return 0; + } + + return crVBoxServerCrCmdBltVramToVramMem(offSrcVRAM, srcWidth, srcHeight, offDstVRAM, dstWidth, dstHeight, pPos, cRects, pRects); +} + + +static int8_t crVBoxServerCrCmdBltOffIdProcess(const VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID *pCmd, uint32_t cbCmd) +{ + uint32_t cRects; + const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects; + if ((cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID, aRects)) % sizeof (VBOXCMDVBVA_RECT)) + { + WARN(("invalid argument size")); + return -1; + } + + cRects = (cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID, aRects)) / sizeof (VBOXCMDVBVA_RECT); + + RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects); + if (!pRects) + { + WARN(("crVBoxServerCrCmdBltRecsUnpack failed")); + return -1; + } + + uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags; + uint32_t hostId = pCmd->id; + + Assert(u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID); + + if (!hostId) + { + WARN(("zero host id")); + return -1; + } + + if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID) + { + WARN(("blit from texture to texture not implemented")); + return -1; + } + + if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2) + { + WARN(("blit to texture not implemented")); + return -1; + } + + VBOXCMDVBVAOFFSET offVRAM = pCmd->alloc.u.offVRAM; + + RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y}; + return crVBoxServerCrCmdBltIdToVram(hostId, offVRAM, 0, 0, &Pos, cRects, pRects); +} + +static int8_t crVBoxServerCrCmdBltSameDimOrId(const VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8 *pCmd, uint32_t cbCmd) +{ + uint32_t cRects; + const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects; + if ((cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8, aRects)) % sizeof (VBOXCMDVBVA_RECT)) + { + WARN(("invalid argument size")); + return -1; + } + + cRects = (cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8, aRects)) / sizeof (VBOXCMDVBVA_RECT); + + RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects); + if (!pRects) + { + WARN(("crVBoxServerCrCmdBltRecsUnpack failed")); + return -1; + } + + uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags; + VBOXCMDVBVAOFFSET offVRAM = pCmd->alloc1.Info.u.offVRAM; + uint32_t width = pCmd->alloc1.u16Width; + uint32_t height = pCmd->alloc1.u16Height; + RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y}; + + if (u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID) + { + uint32_t hostId = pCmd->info2.u.id; + + if (!hostId) + { + WARN(("zero host id")); + return -1; + } + + if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID) + { + WARN(("blit from texture to texture not implemented")); + return -1; + } + + if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2) + { + WARN(("blit to texture not implemented")); + return -1; + } + + return crVBoxServerCrCmdBltIdToVram(hostId, offVRAM, width, height, &Pos, cRects, pRects); + } + + if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID) + { + if (!(u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)) + { + WARN(("blit to texture not implemented")); + return -1; + } + + return crVBoxServerCrCmdBltIdToVram(pCmd->alloc1.Info.u.id, pCmd->info2.u.offVRAM, width, height, &Pos, cRects, pRects); + } + + if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2) + crVBoxServerCrCmdBltVramToVram(offVRAM, width, height, pCmd->info2.u.offVRAM, width, height, &Pos, cRects, pRects); + else + crVBoxServerCrCmdBltVramToVram(pCmd->info2.u.offVRAM, width, height, offVRAM, width, height, &Pos, cRects, pRects); + + return 0; +} + +static int8_t crVBoxServerCrCmdBltGenericBGRAProcess(const VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8 *pCmd, uint32_t cbCmd) +{ + uint32_t cRects; + const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects; + if ((cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8, aRects)) % sizeof (VBOXCMDVBVA_RECT)) + { + WARN(("invalid argument size")); + return -1; + } + + cRects = (cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8, aRects)) / sizeof (VBOXCMDVBVA_RECT); + + RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects); + if (!pRects) + { + WARN(("crVBoxServerCrCmdBltRecsUnpack failed")); + return -1; + } + + uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags; + RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y}; + + if (u8Flags & VBOXCMDVBVA_OPF_OPERAND2_ISID) + { + if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID) + { + WARN(("blit from texture to texture not implemented")); + return -1; + } + + if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2) + { + WARN(("blit to texture not implemented")); + return -1; + } + + return crVBoxServerCrCmdBltIdToVram(pCmd->alloc2.Info.u.id, pCmd->alloc1.Info.u.offVRAM, pCmd->alloc1.u16Width, pCmd->alloc1.u16Height, &Pos, cRects, pRects); + } + else + { + if (u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID) + { + if (!(u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2)) + { + WARN(("blit to texture not implemented")); + return -1; + } + + RTPOINT Pos = {pCmd->Hdr.Pos.x, pCmd->Hdr.Pos.y}; + return crVBoxServerCrCmdBltIdToVram(pCmd->alloc1.Info.u.id, pCmd->alloc2.Info.u.offVRAM, pCmd->alloc2.u16Width, pCmd->alloc2.u16Height, &Pos, cRects, pRects); + } + + if (u8Flags & VBOXCMDVBVA_OPF_BLT_DIR_IN_2) + crVBoxServerCrCmdBltVramToVram(pCmd->alloc1.Info.u.offVRAM, pCmd->alloc1.u16Width, pCmd->alloc1.u16Height, pCmd->alloc2.Info.u.offVRAM, pCmd->alloc2.u16Width, pCmd->alloc2.u16Height, &Pos, cRects, pRects); + else + crVBoxServerCrCmdBltVramToVram(pCmd->alloc2.Info.u.offVRAM, pCmd->alloc2.u16Width, pCmd->alloc2.u16Height, pCmd->alloc1.Info.u.offVRAM, pCmd->alloc1.u16Width, pCmd->alloc1.u16Height, &Pos, cRects, pRects); + + return 0; + } +} + +static int8_t crVBoxServerCrCmdClrFillPrimaryGenericProcess(uint32_t u32PrimaryID, const RTRECT *pRects, uint32_t cRects, uint32_t u32Color) +{ + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(u32PrimaryID); + if (!hFb) + { + WARN(("request to present on disabled framebuffer, ignore")); + return 0; + } + + int rc = CrFbClrFillNe(hFb, cRects, pRects, u32Color); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbClrFillNe failed %d", rc)); + return -1; + } + + return 0; +} + +static int8_t crVBoxServerCrCmdClrFillVramGenericProcess(VBOXCMDVBVAOFFSET offVRAM, uint32_t width, uint32_t height, const RTRECT *pRects, uint32_t cRects, uint32_t u32Color) +{ + CR_BLITTER_IMG Img; + int8_t i8Result = crFbImgFromDimOffVramBGRA(offVRAM, width, height, &Img); + if (i8Result) + { + WARN(("invalid param")); + return -1; + } + + CrMClrFillImg(&Img, cRects, pRects, u32Color); + + return 0; +} + +static int8_t crVBoxServerCrCmdClrFillGenericBGRAProcess(const VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8 *pCmd, uint32_t cbCmd) +{ + uint32_t cRects; + const VBOXCMDVBVA_RECT *pPRects = pCmd->aRects; + if ((cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8, aRects)) % sizeof (VBOXCMDVBVA_RECT)) + { + WARN(("invalid argument size")); + return -1; + } + + cRects = (cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8, aRects)) / sizeof (VBOXCMDVBVA_RECT); + + RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects); + if (!pRects) + { + WARN(("crVBoxServerCrCmdBltRecsUnpack failed")); + return -1; + } + +// uint8_t u8Flags = pCmd->Hdr.Hdr.u8Flags; + int8_t i8Result = crVBoxServerCrCmdClrFillVramGenericProcess(pCmd->dst.Info.u.offVRAM, pCmd->dst.u16Width, pCmd->dst.u16Height, pRects, cRects, pCmd->Hdr.u32Color); + if (i8Result < 0) + { + WARN(("crVBoxServerCrCmdClrFillVramGenericProcess failed")); + return i8Result; + } + + return 0; +} + +/** @todo RT_UNTRUSTED_VOLATILE_GUEST */ +int8_t crVBoxServerCrCmdClrFillProcess(VBOXCMDVBVA_CLRFILL_HDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmdTodo, uint32_t cbCmd) +{ + VBOXCMDVBVA_CLRFILL_HDR const *pCmd = (VBOXCMDVBVA_CLRFILL_HDR const *)pCmdTodo; + uint8_t u8Flags = pCmd->Hdr.u8Flags; + uint8_t u8Cmd = (VBOXCMDVBVA_OPF_CLRFILL_TYPE_MASK & u8Flags); + + switch (u8Cmd) + { + case VBOXCMDVBVA_OPF_CLRFILL_TYPE_GENERIC_A8R8G8B8: + { + if (cbCmd < sizeof (VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8)) + { + WARN(("VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8: invalid command size")); + return -1; + } + + return crVBoxServerCrCmdClrFillGenericBGRAProcess((const VBOXCMDVBVA_CLRFILL_GENERIC_A8R8G8B8*)pCmd, cbCmd); + } + default: + WARN(("unsupported command")); + return -1; + } + +} + +/** @todo RT_UNTRUSTED_VOLATILE_GUEST */ +int8_t crVBoxServerCrCmdBltProcess(VBOXCMDVBVA_BLT_HDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmdTodo, uint32_t cbCmd) +{ + VBOXCMDVBVA_BLT_HDR const *pCmd = (VBOXCMDVBVA_BLT_HDR const *)pCmdTodo; + uint8_t u8Flags = pCmd->Hdr.u8Flags; + uint8_t u8Cmd = (VBOXCMDVBVA_OPF_BLT_TYPE_MASK & u8Flags); + + switch (u8Cmd) + { + case VBOXCMDVBVA_OPF_BLT_TYPE_SAMEDIM_A8R8G8B8: + { + if (cbCmd < sizeof (VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8)) + { + WARN(("VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8: invalid command size")); + return -1; + } + + return crVBoxServerCrCmdBltSameDimOrId((const VBOXCMDVBVA_BLT_SAMEDIM_A8R8G8B8 *)pCmd, cbCmd); + } + case VBOXCMDVBVA_OPF_BLT_TYPE_OFFPRIMSZFMT_OR_ID: + { + if (cbCmd < sizeof (VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID)) + { + WARN(("VBOXCMDVBVA_OPF_BLT_TYPE_OFFPRIMSZFMT_OR_ID: invalid command size")); + return -1; + } + + return crVBoxServerCrCmdBltOffIdProcess((const VBOXCMDVBVA_BLT_OFFPRIMSZFMT_OR_ID *)pCmd, cbCmd); + } + case VBOXCMDVBVA_OPF_BLT_TYPE_GENERIC_A8R8G8B8: + { + if (cbCmd < sizeof (VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8)) + { + WARN(("VBOXCMDVBVA_OPF_BLT_TYPE_GENERIC_A8R8G8B8: invalid command size")); + return -1; + } + + return crVBoxServerCrCmdBltGenericBGRAProcess((const VBOXCMDVBVA_BLT_GENERIC_A8R8G8B8 *)pCmd, cbCmd); + } + default: + WARN(("unsupported command")); + return -1; + } +} + +/** @todo RT_UNTRUSTED_VOLATILE_GUEST */ +int8_t crVBoxServerCrCmdFlipProcess(VBOXCMDVBVA_FLIP const RT_UNTRUSTED_VOLATILE_GUEST *pFlipTodo, uint32_t cbCmd) +{ + VBOXCMDVBVA_FLIP const *pFlip = (VBOXCMDVBVA_FLIP const *)pFlipTodo; + uint32_t hostId; + const VBOXCMDVBVA_RECT *pPRects = pFlip->aRects; + uint32_t cRects; + + if (pFlip->Hdr.u8Flags & VBOXCMDVBVA_OPF_OPERAND1_ISID) + { + hostId = pFlip->src.u.id; + if (!hostId) + { + WARN(("hostId is NULL")); + return -1; + } + } + else + { + WARN(("VBOXCMDVBVA_OPF_ALLOC_SRCID not specified")); + hostId = 0; + } + + uint32_t idFb = pFlip->Hdr.u.u8PrimaryID; + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabled(idFb); + if (!hFb) + { + WARN(("request to present on disabled framebuffer, ignore")); + return 0; + } + + cRects = (cbCmd - VBOXCMDVBVA_SIZEOF_FLIPSTRUCT_MIN) / sizeof (VBOXCMDVBVA_RECT); + if (cRects > 0) + { + RTRECT *pRects = crVBoxServerCrCmdBltRecsUnpack(pPRects, cRects); + if (pRects) + { + crServerDispatchVBoxTexPresent(hostId, idFb, 0, 0, cRects, (const GLint*)pRects); + return 0; + } + } + else + { + /* Prior to r100476 guest WDDM driver was not supplying us with sub-rectangles + * data obtained in DxgkDdiPresentNew() callback. Therefore, in order to support backward compatibility, + * lets play in old way if no rectangles were supplied. */ + const RTRECT *pRect = CrVrScrCompositorRectGet(&hFb->Compositor); + crServerDispatchVBoxTexPresent(hostId, idFb, 0, 0, 1, (const GLint*)pRect); + } + + return -1; +} + +typedef struct CRSERVER_CLIENT_CALLOUT +{ + VBOXCRCMDCTL_CALLOUT_LISTENTRY Entry; + PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb; + void*pvCb; +} CRSERVER_CLIENT_CALLOUT; + +static DECLCALLBACK(void) crServerClientCalloutCb(struct VBOXCRCMDCTL_CALLOUT_LISTENTRY *pEntry) +{ + CRSERVER_CLIENT_CALLOUT *pCallout = RT_FROM_MEMBER(pEntry, CRSERVER_CLIENT_CALLOUT, Entry); + pCallout->pfnCb(pCallout->pvCb); + int rc = RTSemEventSignal(cr_server.hCalloutCompletionEvent); + if (RT_FAILURE(rc)) + WARN(("RTSemEventSignal failed rc %d", rc)); +} + +static DECLCALLBACK(void) crServerClientCallout(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void*pvCb) +{ + Assert(cr_server.pCurrentCalloutCtl); + CRSERVER_CLIENT_CALLOUT Callout; + Callout.pfnCb = pfnCb; + Callout.pvCb = pvCb; + cr_server.ClientInfo.pfnCallout(cr_server.ClientInfo.hClient, cr_server.pCurrentCalloutCtl, &Callout.Entry, crServerClientCalloutCb); + + int rc = RTSemEventWait(cr_server.hCalloutCompletionEvent, RT_INDEFINITE_WAIT); + if (RT_FAILURE(rc)) + WARN(("RTSemEventWait failed %d", rc)); +} + + +DECLEXPORT(void) crVBoxServerCalloutEnable(VBOXCRCMDCTL *pCtl) +{ +#if 1 //def CR_SERVER_WITH_CLIENT_CALLOUTS + Assert(!cr_server.pCurrentCalloutCtl); + cr_server.pCurrentCalloutCtl = pCtl; + + cr_server.head_spu->dispatch_table.ChromiumParametervCR(GL_HH_SET_CLIENT_CALLOUT, 0, 0, (void*)crServerClientCallout); +#endif +} + +extern DECLEXPORT(void) crVBoxServerCalloutDisable() +{ +#if 1 //def CR_SERVER_WITH_CLIENT_CALLOUTS + Assert(cr_server.pCurrentCalloutCtl); + + cr_server.head_spu->dispatch_table.ChromiumParametervCR(GL_HH_SET_CLIENT_CALLOUT, 0, 0, NULL); + + cr_server.pCurrentCalloutCtl = NULL; +#endif +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.h b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.h new file mode 100644 index 00000000..759f8dbd --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/server_presenter.h @@ -0,0 +1,440 @@ +/* $Id: server_presenter.h $ */ + +/** @file + * Presenter API definitions. + */ + +/* + * Copyright (C) 2014-2019 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. + */ + +#ifndef __SERVER_PRESENTER_H__ +#define __SERVER_PRESENTER_H__ + +#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "cr_net.h" +#include "cr_rand.h" +#include "server_dispatch.h" +#include "server.h" +#include "cr_mem.h" +#include "cr_string.h" +#include <cr_vreg.h> +#include <cr_htable.h> +#include <cr_bmpscale.h> + +#include "render/renderspu.h" + +#include <iprt/cdefs.h> +#include <iprt/types.h> +#include <iprt/asm.h> +#include <iprt/mem.h> +#include <iprt/list.h> + + +class ICrFbDisplay +{ + public: + virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb) = 0; + virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb) = 0; + + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry) = 0; + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry) = 0; + + virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb) = 0; + + virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb) = 0; + + virtual ~ICrFbDisplay() {} +}; + + +typedef struct CR_FRAMEBUFFER +{ + VBOXVR_SCR_COMPOSITOR Compositor; + struct VBVAINFOSCREEN ScreenInfo; + void *pvVram; + ICrFbDisplay *pDisplay; + RTLISTNODE EntriesList; + uint32_t cEntries; /* <- just for debugging */ + uint32_t cUpdating; + CRHTABLE SlotTable; +} CR_FRAMEBUFFER; + + +typedef union CR_FBENTRY_FLAGS +{ + struct { + uint32_t fCreateNotified : 1; + uint32_t fInList : 1; + uint32_t Reserved : 30; + }; + uint32_t Value; +} CR_FBENTRY_FLAGS; + + +typedef struct CR_FRAMEBUFFER_ENTRY +{ + VBOXVR_SCR_COMPOSITOR_ENTRY Entry; + RTLISTNODE Node; + uint32_t cRefs; + CR_FBENTRY_FLAGS Flags; + CRHTABLE HTable; +} CR_FRAMEBUFFER_ENTRY; + + +typedef struct CR_FBTEX +{ + CR_TEXDATA Tex; + CRTextureObj *pTobj; +} CR_FBTEX; + + +class CrFbDisplayBase : public ICrFbDisplay +{ + public: + + CrFbDisplayBase(); + + virtual bool isComposite(); + class CrFbDisplayComposite* getContainer(); + bool isInList(); + bool isUpdating(); + int setRegionsChanged(); + int setFramebuffer(struct CR_FRAMEBUFFER *pFb); + struct CR_FRAMEBUFFER* getFramebuffer(); + virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb); + virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb); + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry); + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb); + virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb); + virtual ~CrFbDisplayBase(); + + /*@todo: move to protected and switch from RTLISTNODE*/ + RTLISTNODE mNode; + class CrFbDisplayComposite* mpContainer; + + protected: + + virtual void onUpdateEnd(); + virtual void ueRegions(); + static DECLCALLBACK(bool) entriesCreateCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext); + static DECLCALLBACK(bool) entriesDestroyCb(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext); + int fbSynchAddAllEntries(); + int fbCleanupRemoveAllEntries(); + virtual int setFramebufferBegin(struct CR_FRAMEBUFFER *pFb); + virtual void setFramebufferEnd(struct CR_FRAMEBUFFER *pFb); + static DECLCALLBACK(void) slotEntryReleaseCB(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext); + virtual void slotRelease(); + virtual int fbCleanup(); + virtual int fbSync(); + CRHTABLE_HANDLE slotGet(); + + private: + + typedef union CR_FBDISPBASE_FLAGS + { + struct { + uint32_t fRegionsShanged : 1; + uint32_t Reserved : 31; + }; + uint32_t u32Value; + } CR_FBDISPBASE_FLAGS; + + struct CR_FRAMEBUFFER *mpFb; + uint32_t mcUpdates; + CRHTABLE_HANDLE mhSlot; + CR_FBDISPBASE_FLAGS mFlags; +}; + + +class CrFbDisplayComposite : public CrFbDisplayBase +{ + public: + + CrFbDisplayComposite(); + virtual bool isComposite(); + uint32_t getDisplayCount(); + bool add(CrFbDisplayBase *pDisplay); + bool remove(CrFbDisplayBase *pDisplay, bool fCleanupDisplay = true); + CrFbDisplayBase* first(); + CrFbDisplayBase* next(CrFbDisplayBase* pDisplay); + virtual int setFramebuffer(struct CR_FRAMEBUFFER *pFb); + virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb); + virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb); + virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry); + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb); + virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb); + virtual ~CrFbDisplayComposite(); + void cleanup(bool fCleanupDisplays = true); + + private: + + RTLISTNODE mDisplays; + uint32_t mcDisplays; +}; + + +class CrFbWindow +{ + public: + + CrFbWindow(uint64_t parentId); + bool IsCreated() const; + bool IsVisivle() const; + void Destroy(); + int Reparent(uint64_t parentId); + int SetVisible(bool fVisible); + int SetSize(uint32_t width, uint32_t height, bool fForced=false); + int SetPosition(int32_t x, int32_t y, bool fForced=false); + int SetVisibleRegionsChanged(); + int SetCompositor(const struct VBOXVR_SCR_COMPOSITOR * pCompositor); + bool SetScaleFactor(GLdouble scaleFactorW, GLdouble scaleFactorH); + bool GetScaleFactor(GLdouble *scaleFactorW, GLdouble *scaleFactorH); + int UpdateBegin(); + void UpdateEnd(); + uint64_t GetParentId(); + int Create(); + ~CrFbWindow(); + + protected: + + void checkRegions(); + bool isPresentNeeded(); + bool checkInitedUpdating(); + + private: + + typedef union CR_FBWIN_FLAGS + { + struct { + uint32_t fVisible : 1; + uint32_t fDataPresented : 1; + uint32_t fForcePresentOnReenable : 1; + uint32_t fCompositoEntriesModified : 1; + uint32_t Reserved : 28; + }; + uint32_t Value; + } CR_FBWIN_FLAGS; + + GLint mSpuWindow; + const struct VBOXVR_SCR_COMPOSITOR * mpCompositor; + uint32_t mcUpdates; + int32_t mxPos; + int32_t myPos; + uint32_t mWidth; + uint32_t mHeight; + CR_FBWIN_FLAGS mFlags; + uint64_t mParentId; + + RTSEMRW scaleFactorLock; + GLdouble mScaleFactorWStorage; + GLdouble mScaleFactorHStorage; +}; + + +class CrFbDisplayWindow : public CrFbDisplayBase +{ + public: + + CrFbDisplayWindow(const RTRECT *pViewportRect, uint64_t parentId); + virtual ~CrFbDisplayWindow(); + virtual int UpdateBegin(struct CR_FRAMEBUFFER *pFb); + virtual void UpdateEnd(struct CR_FRAMEBUFFER *pFb); + virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb); + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry); + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb); + const RTRECT* getViewportRect(); + virtual int setViewportRect(const RTRECT *pViewportRect); + virtual CrFbWindow * windowDetach(bool fCleanup = true); + virtual CrFbWindow * windowAttach(CrFbWindow * pNewWindow); + virtual int reparent(uint64_t parentId); + virtual bool isVisible(); + int winVisibilityChanged(); + CrFbWindow* getWindow(); + + protected: + + virtual void onUpdateEnd(); + virtual void ueRegions(); + virtual int screenChanged(); + virtual int windowSetCompositor(bool fSet); + virtual int windowCleanup(); + virtual int fbCleanup(); + bool isActive(); + int windowDimensionsSync(bool fForceCleanup = false); + virtual int windowSync(); + virtual int fbSync(); + virtual const struct RTRECT* getRect(); + + private: + + typedef union CR_FBDISPWINDOW_FLAGS + { + struct { + uint32_t fNeVisible : 1; + uint32_t fNeForce : 1; + uint32_t Reserved : 30; + }; + uint32_t u32Value; + } CR_FBDISPWINDOW_FLAGS; + + CrFbWindow *mpWindow; + RTRECT mViewportRect; + CR_FBDISPWINDOW_FLAGS mFlags; + uint32_t mu32Screen; + uint64_t mParentId; +}; + + +class CrFbDisplayWindowRootVr : public CrFbDisplayWindow +{ + public: + + CrFbDisplayWindowRootVr(const RTRECT *pViewportRect, uint64_t parentId); + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryAdded(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry); + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int setViewportRect(const RTRECT *pViewportRect); + + protected: + + virtual int windowSetCompositor(bool fSet); + virtual void ueRegions(); + int compositorMarkUpdated(); + virtual int screenChanged(); + virtual const struct RTRECT* getRect(); + virtual int fbCleanup(); + virtual int fbSync(); + VBOXVR_SCR_COMPOSITOR_ENTRY* entryAlloc(); + void entryFree(VBOXVR_SCR_COMPOSITOR_ENTRY* pEntry); + int synchCompositorRegions(); + virtual int synchCompositor(); + virtual int clearCompositor(); + void rootVrTranslateForPos(); + static DECLCALLBACK(VBOXVR_SCR_COMPOSITOR_ENTRY*) rootVrGetCEntry(const VBOXVR_SCR_COMPOSITOR_ENTRY*pEntry, void *pvContext); + + private: + + VBOXVR_SCR_COMPOSITOR mCompositor; +}; + + +class CrFbDisplayVrdp : public CrFbDisplayBase +{ + public: + + CrFbDisplayVrdp(); + virtual int EntryCreated(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryReplaced(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hNewEntry, HCR_FRAMEBUFFER_ENTRY hReplacedEntry); + virtual int EntryTexChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryRemoved(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryDestroyed(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int EntryPosChanged(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + virtual int RegionsChanged(struct CR_FRAMEBUFFER *pFb); + virtual int FramebufferChanged(struct CR_FRAMEBUFFER *pFb); + + protected: + + void syncPos(); + virtual int fbCleanup(); + virtual int fbSync(); + void vrdpDestroy(HCR_FRAMEBUFFER_ENTRY hEntry); + void vrdpGeometry(HCR_FRAMEBUFFER_ENTRY hEntry); + int vrdpRegions(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + int vrdpFrame(HCR_FRAMEBUFFER_ENTRY hEntry); + int vrdpRegionsAll(struct CR_FRAMEBUFFER *pFb); + int vrdpSynchEntry(struct CR_FRAMEBUFFER *pFb, HCR_FRAMEBUFFER_ENTRY hEntry); + int vrdpSyncEntryAll(struct CR_FRAMEBUFFER *pFb); + int vrdpCreate(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry); + + private: + + RTPOINT mPos; +}; + + +typedef struct CR_FB_INFO +{ + CrFbDisplayComposite *pDpComposite; + uint32_t u32Id; + VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap); +} CR_FB_INFO; + +typedef struct CR_FBDISPLAY_INFO +{ + CrFbDisplayWindow *pDpWin; + CrFbDisplayWindowRootVr *pDpWinRootVr; + CrFbDisplayVrdp *pDpVrdp; + CrFbWindow *pWindow; + uint32_t u32DisplayMode; + uint32_t u32Id; + int32_t iFb; + + /* Cache scaling factor here before display output + * initialized (i.e., guest not yet initiated first 3D call). + * No synchronization stuff needed here because all the reads + * and writes are done in context of 3D HGCM thread. */ + double dInitialScaleFactorW; + double dInitialScaleFactorH; +} CR_FBDISPLAY_INFO; + +typedef struct CR_PRESENTER_GLOBALS +{ +#ifndef VBOXVDBG_MEMCACHE_DISABLE + RTMEMCACHE FbEntryLookasideList; + RTMEMCACHE FbTexLookasideList; + RTMEMCACHE CEntryLookasideList; +#endif + uint32_t u32DisplayMode; + uint32_t u32DisabledDisplayMode; + bool fEnabled; + CRHashTable *pFbTexMap; + CR_FBDISPLAY_INFO aDisplayInfos[CR_MAX_GUEST_MONITORS]; + CR_FBMAP FramebufferInitMap; + CR_FRAMEBUFFER aFramebuffers[CR_MAX_GUEST_MONITORS]; + CR_FB_INFO aFbInfos[CR_MAX_GUEST_MONITORS]; + bool fWindowsForceHidden; + uint32_t cbTmpBuf; + void *pvTmpBuf; + uint32_t cbTmpBuf2; + void *pvTmpBuf2; +} CR_PRESENTER_GLOBALS; + +extern CR_PRESENTER_GLOBALS g_CrPresenter; + + +HCR_FRAMEBUFFER_ENTRY CrFbEntryFromCompositorEntry(const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry); + +#endif /* __SERVER_PRESENTER_H__ */ + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/window.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/window.cpp new file mode 100644 index 00000000..dd160996 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/presenter/window.cpp @@ -0,0 +1,480 @@ +/* $Id: window.cpp $ */ + +/** @file + * Presenter API: window class implementation. + */ + +/* + * Copyright (C) 2014-2019 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. + */ + +#include "server_presenter.h" +#include <VBox/VBoxOGL.h> + +CrFbWindow::CrFbWindow(uint64_t parentId) : + mSpuWindow(0), + mpCompositor(NULL), + mcUpdates(0), + mxPos(0), + myPos(0), + mWidth(0), + mHeight(0), + mParentId(parentId), + mScaleFactorWStorage(1.0), + mScaleFactorHStorage(1.0) +{ + int rc; + + mFlags.Value = 0; + + rc = RTSemRWCreate(&scaleFactorLock); + if (!RT_SUCCESS(rc)) + WARN(("Unable to initialize scaling factor data lock.")); +} + + +bool CrFbWindow::IsCreated() const +{ + return !!mSpuWindow; +} + +bool CrFbWindow::IsVisivle() const +{ + return mFlags.fVisible; +} + + +void CrFbWindow::Destroy() +{ + CRASSERT(!mcUpdates); + + if (!mSpuWindow) + return; + + cr_server.head_spu->dispatch_table.WindowDestroy(mSpuWindow); + + mSpuWindow = 0; + mFlags.fDataPresented = 0; +} + + +int CrFbWindow::Reparent(uint64_t parentId) +{ + if (!checkInitedUpdating()) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + crDebug("CrFbWindow: reparent to %p (current mxPos=%d, myPos=%d, mWidth=%u, mHeight=%u)", + parentId, mxPos, myPos, mWidth, mHeight); + + uint64_t oldParentId = mParentId; + + mParentId = parentId; + + if (mSpuWindow) + { + if (oldParentId && !parentId && mFlags.fVisible) + cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, false); + + renderspuSetWindowId(mParentId); + renderspuReparentWindow(mSpuWindow); + renderspuSetWindowId(cr_server.screen[0].winID); + + if (parentId) + { + if (mFlags.fVisible) + cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos); + cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, mFlags.fVisible); + } + } + + return VINF_SUCCESS; +} + + +int CrFbWindow::SetVisible(bool fVisible) +{ + if (!checkInitedUpdating()) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + LOG(("CrWIN: Visible [%d]", fVisible)); + + if (!fVisible != !mFlags.fVisible) + { + mFlags.fVisible = fVisible; + if (mSpuWindow && mParentId) + { + if (fVisible) + cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos); + cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, fVisible); + } + } + + return VINF_SUCCESS; +} + + +int CrFbWindow::SetSize(uint32_t width, uint32_t height, bool fForced) +{ + if (!fForced && !checkInitedUpdating()) + { + crDebug("CrFbWindow: SetSize request dropped because window is currently updating" + "(width=%d, height=%d, mWidth=%d, mHeight=%d).", width, height, mWidth, mHeight); + return VERR_INVALID_STATE; + } + + if (mWidth != width || mHeight != height || fForced) + { + GLdouble scaleFactorW, scaleFactorH; + uint32_t scaledWidth, scaledHeight; + + /* Reset to default values if operation was unsuccessfull. */ + if (!GetScaleFactor(&scaleFactorW, &scaleFactorH)) + scaleFactorW = scaleFactorH = 1.0; + + mFlags.fCompositoEntriesModified = 1; + + /* Keep mWidth and mHeight unchanged (not multiplied by scale factor scalar). */ + mWidth = width; + mHeight = height; + + scaledWidth = (uint32_t)((GLdouble)width * scaleFactorW); + scaledHeight = (uint32_t)((GLdouble)height * scaleFactorH); + + if (mSpuWindow) + { + cr_server.head_spu->dispatch_table.WindowSize(mSpuWindow, scaledWidth, scaledHeight); + crDebug("CrFbWindow: SetSize request performed successfully " + "(width=%d, height=%d, scaledWidth=%d, scaledHeight=%d).", width, height, scaledWidth, scaledHeight); + } + else + crDebug("CrFbWindow: SetSize request skipped because mSpuWindow not yet constructed " + "(width=%d, height=%d, scaledWidth=%d, scaledHeight=%d).", width, height, scaledWidth, scaledHeight); + } + else + crDebug("CrFbWindow: SetSize request skipped because window arleady has requested size " + "(width=%d, height=%d, mWidth=%d, mHeight=%d).", width, height, mWidth, mHeight); + + return VINF_SUCCESS; +} + + +int CrFbWindow::SetPosition(int32_t x, int32_t y, bool fForced) +{ + if (!fForced && !checkInitedUpdating()) + { + crDebug("CrFbWindow: SetPosition request dropped because window is currently updating (x=%d, y=%d).", x, y); + return VERR_INVALID_STATE; + } + + LOG(("CrWIN: Pos [%d ; %d]", x, y)); +// always do WindowPosition to ensure window is adjusted properly +// if (x != mxPos || y != myPos) + { + mxPos = x; + myPos = y; + if (mSpuWindow) + cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, x, y); + crDebug("CrFbWindow: SetPosition performed successfully (x=%d, y=%d).", x, y); + } + + return VINF_SUCCESS; +} + + +int CrFbWindow::SetVisibleRegionsChanged() +{ + if (!checkInitedUpdating()) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + mFlags.fCompositoEntriesModified = 1; + return VINF_SUCCESS; +} + + +int CrFbWindow::SetCompositor(const struct VBOXVR_SCR_COMPOSITOR * pCompositor) +{ + if (!checkInitedUpdating()) + { + WARN(("err")); + return VERR_INVALID_STATE; + } + + mpCompositor = pCompositor; + mFlags.fCompositoEntriesModified = 1; + + return VINF_SUCCESS; +} + + +bool CrFbWindow::SetScaleFactor(GLdouble scaleFactorW, GLdouble scaleFactorH) +{ + int rc; + + /* Simple check for input values. */ + if ( !( (scaleFactorW >= VBOX_OGL_SCALE_FACTOR_MIN && scaleFactorW <= VBOX_OGL_SCALE_FACTOR_MAX) + && (scaleFactorH >= VBOX_OGL_SCALE_FACTOR_MIN && scaleFactorH <= VBOX_OGL_SCALE_FACTOR_MAX))) + { + crDebug("CrFbWindow: attempt to set scale factor out of valid values range: scaleFactorW=%d, scaleFactorH=%d, multiplier=%d.", + (int)(scaleFactorW * VBOX_OGL_SCALE_FACTOR_MULTIPLIER), (int)(scaleFactorH * VBOX_OGL_SCALE_FACTOR_MULTIPLIER), + (int)VBOX_OGL_SCALE_FACTOR_MULTIPLIER); + + return false; + } + + rc = RTSemRWRequestWrite(scaleFactorLock, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + mScaleFactorWStorage = scaleFactorW; + mScaleFactorHStorage = scaleFactorH; + RTSemRWReleaseWrite(scaleFactorLock); + + crDebug("CrFbWindow: set scale factor: scaleFactorW=%d, scaleFactorH=%d, multiplier=%d.", + (int)(scaleFactorW * VBOX_OGL_SCALE_FACTOR_MULTIPLIER), (int)(scaleFactorH * VBOX_OGL_SCALE_FACTOR_MULTIPLIER), + (int)VBOX_OGL_SCALE_FACTOR_MULTIPLIER); + + /* Update window geometry. Do not wait for GAs to send SetSize() and SetPosition() + * events since they might not be running or installed at all. */ + SetSize(mWidth, mHeight, true); + SetPosition(mxPos, myPos, true); + + return true; + } + + crDebug("CrFbWindow: unable to set scale factor because RW lock cannot be aquired: scaleFactorW=%d, scaleFactorH=%d, multiplier=%d.", + (int)(scaleFactorW * VBOX_OGL_SCALE_FACTOR_MULTIPLIER), (int)(scaleFactorH * VBOX_OGL_SCALE_FACTOR_MULTIPLIER), + (int)VBOX_OGL_SCALE_FACTOR_MULTIPLIER); + + return false; +} + + +bool CrFbWindow::GetScaleFactor(GLdouble *scaleFactorW, GLdouble *scaleFactorH) +{ + int rc; + + rc = RTSemRWRequestRead(scaleFactorLock, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + *scaleFactorW = mScaleFactorWStorage; + *scaleFactorH = mScaleFactorHStorage; + RTSemRWReleaseRead(scaleFactorLock); + return true; + } + + return false; +} + + +int CrFbWindow::UpdateBegin() +{ + ++mcUpdates; + if (mcUpdates > 1) + return VINF_SUCCESS; + + Assert(!mFlags.fForcePresentOnReenable); + + crDebug("CrFbWindow::UpdateBegin ENTER, mSpuWindow(0x%X) fDataPresented(%d)", mSpuWindow, mFlags.fDataPresented); + + if (mFlags.fDataPresented) + { + Assert(mSpuWindow); + cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, NULL, NULL); + mFlags.fForcePresentOnReenable = isPresentNeeded(); + } + + crDebug("CrFbWindow::UpdateBegin LEAVE, fForcePresentOnReenable(%d)", mFlags.fForcePresentOnReenable); + + return VINF_SUCCESS; +} + + +void CrFbWindow::UpdateEnd() +{ + --mcUpdates; + Assert(mcUpdates < UINT32_MAX/2); + if (mcUpdates) + return; + + crDebug("CrFbWindow::UpdateEnd ENTER, mSpuWindow(0x%X) mpCompositor(0x%X) fForcePresentOnReenable(%d)", mSpuWindow, mpCompositor, mFlags.fForcePresentOnReenable); + + if (mSpuWindow) + { + bool fPresentNeeded = isPresentNeeded(); + GLdouble scaleFactorW, scaleFactorH; + /* Reset to default values if operation was unseccessfull. */ + if (!GetScaleFactor(&scaleFactorW, &scaleFactorH)) + scaleFactorW = scaleFactorH = 1.0; + + if (mpCompositor) + { + CrVrScrCompositorSetStretching((VBOXVR_SCR_COMPOSITOR *)mpCompositor, scaleFactorW, scaleFactorH); + checkRegions(); + } + + if (fPresentNeeded || mFlags.fForcePresentOnReenable) + { + mFlags.fForcePresentOnReenable = false; + if (mpCompositor) + { + cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, mpCompositor, NULL); + } + else + { + VBOXVR_SCR_COMPOSITOR TmpCompositor; + RTRECT Rect; + Rect.xLeft = 0; + Rect.yTop = 0; + Rect.xRight = (uint32_t)((GLdouble)mWidth * scaleFactorW); + Rect.yBottom = (uint32_t)((GLdouble)mHeight * scaleFactorH); + CrVrScrCompositorInit(&TmpCompositor, &Rect); + CrVrScrCompositorSetStretching((VBOXVR_SCR_COMPOSITOR *)&TmpCompositor, scaleFactorW, scaleFactorH); + /* this is a cleanup operation + * empty compositor is guarantid to be released on VBoxPresentComposition return */ + cr_server.head_spu->dispatch_table.VBoxPresentComposition(mSpuWindow, &TmpCompositor, NULL); + } + g_pLed->Asserted.s.fWriting = 1; + } + + /* even if the above branch is entered due to mFlags.fForcePresentOnReenable, + * the backend should clean up the compositor as soon as presentation is performed */ + mFlags.fDataPresented = fPresentNeeded; + } + else + { + Assert(!mFlags.fDataPresented); + Assert(!mFlags.fForcePresentOnReenable); + } +} + + +uint64_t CrFbWindow::GetParentId() +{ + return mParentId; +} + + +int CrFbWindow::Create() +{ + if (mSpuWindow) + { + //WARN(("window already created")); + return VINF_ALREADY_INITIALIZED; + } + + crDebug("CrFbWindow::Create ENTER, mParentId(0x%X)\n", mParentId); + + CRASSERT(cr_server.fVisualBitsDefault); + renderspuSetWindowId(mParentId); + mSpuWindow = cr_server.head_spu->dispatch_table.WindowCreate("", cr_server.fVisualBitsDefault); + renderspuSetWindowId(cr_server.screen[0].winID); + if (mSpuWindow < 0) { + WARN(("WindowCreate failed")); + return VERR_GENERAL_FAILURE; + } + + GLdouble scaleFactorW, scaleFactorH; + /* Reset to default values if operation was unseccessfull. */ + if (!GetScaleFactor(&scaleFactorW, &scaleFactorH)) + scaleFactorW = scaleFactorH = 1.0; + + uint32_t scaledWidth, scaledHeight; + + scaledWidth = (uint32_t)((GLdouble)mWidth * scaleFactorW); + scaledHeight = (uint32_t)((GLdouble)mHeight * scaleFactorH); + + cr_server.head_spu->dispatch_table.WindowSize(mSpuWindow, scaledWidth, scaledHeight); + cr_server.head_spu->dispatch_table.WindowPosition(mSpuWindow, mxPos, myPos); + + checkRegions(); + + if (mParentId && mFlags.fVisible) + cr_server.head_spu->dispatch_table.WindowShow(mSpuWindow, true); + + crDebug("CrFbWindow::Create LEAVE, mParentId(0x%X) mSpuWindow(0x%X)\n", mParentId, mSpuWindow); + return VINF_SUCCESS; +} + + +CrFbWindow::~CrFbWindow() +{ + int rc; + + Destroy(); + + rc = RTSemRWDestroy(scaleFactorLock); + if (!RT_SUCCESS(rc)) + WARN(("Unable to release scaling factor data lock.")); +} + + +void CrFbWindow::checkRegions() +{ + crDebug("CrFbWindow::checkRegions ENTER, mSpuWindow(0x%X) mpCompositor(0x%X) fCompositoEntriesModified(%d)", + mSpuWindow, mpCompositor, mFlags.fCompositoEntriesModified); + + if (!mSpuWindow) + return; + + if (!mFlags.fCompositoEntriesModified) + return; + + uint32_t cRects; + const RTRECT *pRects; + if (mpCompositor) + { + int rc = CrVrScrCompositorRegionsGet(mpCompositor, &cRects, NULL, &pRects, NULL); + if (!RT_SUCCESS(rc)) + { + WARN(("CrVrScrCompositorRegionsGet failed rc %d", rc)); + cRects = 0; + pRects = NULL; + } + } + else + { + cRects = 0; + pRects = NULL; + } + + cr_server.head_spu->dispatch_table.WindowVisibleRegion(mSpuWindow, cRects, (const GLint*)pRects); + + mFlags.fCompositoEntriesModified = 0; + + crDebug("CrFbWindow::checkRegions LEAVE, cRects(%d)", cRects); +} + + +bool CrFbWindow::isPresentNeeded() +{ + return mFlags.fVisible && mWidth && mHeight && mpCompositor && !CrVrScrCompositorIsEmpty(mpCompositor); +} + + +bool CrFbWindow::checkInitedUpdating() +{ + if (!mcUpdates) + { + WARN(("not updating")); + return false; + } + + return true; +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h b/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h new file mode 100644 index 00000000..2b3c2318 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server.h @@ -0,0 +1,718 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved. + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#ifndef CR_SERVER_H +#define CR_SERVER_H + +#include "cr_protocol.h" +#include "cr_glstate.h" +#include "spu_dispatch_table.h" + +#include "state/cr_currentpointers.h" + +#include "cr_server.h" +#include <cr_htable.h> +#include <cr_compositor.h> + +#ifdef VBOX_WITH_CRHGSMI +# include <VBoxVideo.h> + +#include <iprt/cdefs.h> + +RT_C_DECLS_BEGIN + +extern uint8_t* g_pvVRamBase; +extern uint32_t g_cbVRam; +extern PPDMLED g_pLed; +extern HCRHGSMICMDCOMPLETION g_hCrHgsmiCompletion; +extern PFNCRHGSMICMDCOMPLETION g_pfnCrHgsmiCompletion; + +#define VBOXCRHGSMI_PTR(_off, _t) ((_t*)(g_pvVRamBase + (_off))) +#define VBOXCRHGSMI_PTR_SAFE(_off, _cb, _t) ((_t*)crServerCrHgsmiPtrGet(_off, _cb)) + +DECLINLINE(void*) crServerCrHgsmiPtrGet(VBOXVIDEOOFFSET offBuffer, uint32_t cbBuffer) +{ + return ((offBuffer) + (cbBuffer) <= g_cbVRam ? VBOXCRHGSMI_PTR(offBuffer, void) : NULL); +} + +DECLINLINE(void) crServerCrHgsmiCmdComplete(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, int cmdProcessingRc) +{ + g_pfnCrHgsmiCompletion(g_hCrHgsmiCompletion, pCmd, cmdProcessingRc); +} + +#define VBOXCRHGSMI_CMD_COMPLETE(_pData, _rc) do { \ + CRVBOXHGSMI_CMDDATA_ASSERT_ISSET(_pData); \ + CRVBOXHGSMI_CMDDATA_RC(_pData, _rc); \ + if (CRVBOXHGSMI_CMDDATA_IS_HGSMICMD(_pData)) { \ + Assert(CRVBOXHGSMI_CMDDATA_IS_HGSMICMD(_pData)); \ + crServerCrHgsmiCmdComplete((_pData)->pHgsmiCmd, VINF_SUCCESS); \ + } \ + } while (0) + +#define VBOXCRHGSMI_CMD_CHECK_COMPLETE(_pData, _rc) do { \ + if (CRVBOXHGSMI_CMDDATA_IS_SET(_pData)) {\ + VBOXCRHGSMI_CMD_COMPLETE(_pData, _rc); \ + } \ + } while (0) + +#endif + +/* + * This is the base number for window and context IDs + */ +#define MAGIC_OFFSET 5000 + +extern CRServer cr_server; + +/* Semaphore wait queue node */ +typedef struct _wqnode { + RunQueue *q; + struct _wqnode *next; +} wqnode; + +typedef struct { + GLuint count; + GLuint num_waiting; + RunQueue **waiting; +} CRServerBarrier; + +typedef struct { + GLuint count; + wqnode *waiting, *tail; +} CRServerSemaphore; + +typedef struct { + GLuint id; + GLint projParamStart; + GLfloat projMat[16]; /* projection matrix, accumulated via calls to */ + /* glProgramLocalParameterARB, glProgramParameterNV */ +} CRServerProgram; + +void crServerSetVBoxConfiguration(); +void crServerSetVBoxConfigurationHGCM(); +void crServerInitDispatch(void); +void crServerReturnValue( const void *payload, unsigned int payload_len ); +void crServerWriteback(void); +int crServerRecv( CRConnection *conn, CRMessage *msg, unsigned int len ); +void crServerSerializeRemoteStreams(void); +void crServerAddToRunQueue( CRClient *client ); +void crServerDeleteClient( CRClient *client ); + + +void crServerApplyBaseProjection( const CRmatrix *baseProj ); +void crServerApplyViewMatrix( const CRmatrix *view ); +void crServerSetOutputBounds( const CRMuralInfo *mural, int extNum ); +void crServerComputeViewportBounds( const CRViewportState *v, CRMuralInfo *mural ); + +GLboolean crServerInitializeBucketing(CRMuralInfo *mural); + +void crComputeOverlapGeom(double *quads, int nquad, CRPoly ***res); +void crComputeKnockoutGeom(double *quads, int nquad, int my_quad_idx, CRPoly **res); + +int crServerGetCurrentEye(void); + +GLboolean crServerClientInBeginEnd(const CRClient *client); + +GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLint shareCtx, GLint preloadCtxID, int32_t internalID); +GLint crServerDispatchWindowCreateEx(const char *dpyName, GLint visBits, GLint preloadWinID); +GLint crServerMuralInit(CRMuralInfo *mural, GLboolean fGuestWindow, GLint visBits, GLint preloadWinID); +void crServerMuralTerm(CRMuralInfo *mural); +GLboolean crServerMuralSize(CRMuralInfo *mural, GLint width, GLint height); +void crServerMuralPosition(CRMuralInfo *mural, GLint x, GLint y); +void crServerMuralVisibleRegion( CRMuralInfo *mural, GLint cRects, const GLint *pRects ); +void crServerMuralShow( CRMuralInfo *mural, GLint state ); + +GLint crServerGenerateID(GLint *pCounter); + +GLint crServerSPUWindowID(GLint serverWindow); + +GLuint crServerTranslateProgramID(GLuint id); + +CRMuralInfo * crServerGetDummyMural(GLint visualBits); + +void crServerCheckMuralGeometry(CRMuralInfo *mural); +void crServerCheckAllMuralGeometry(CRMuralInfo *pMI); +GLboolean crServerSupportRedirMuralFBO(void); + +void crVBoxServerMuralFbResizeBegin(HCR_FRAMEBUFFER hFb); +void crVBoxServerMuralFbResizeEnd(HCR_FRAMEBUFFER hFb); + +void crVBoxServerNotifyEvent(int32_t idScreen, uint32_t uEvent, void* pvData, uint32_t cbData); + +void crServerRedirMuralFbClear(CRMuralInfo *mural); + +void crServerWindowReparent(CRMuralInfo *pMural); + +void crServerRedirMuralFBO(CRMuralInfo *mural, bool fEnabled); +void crServerDeleteMuralFBO(CRMuralInfo *mural); +void crServerPresentFBO(CRMuralInfo *mural); +GLboolean crServerIsRedirectedToFBO(); +GLint crServerMuralFBOIdxFromBufferName(CRMuralInfo *mural, GLenum buffer); +void crServerMuralFBOSwapBuffers(CRMuralInfo *mural); + +HCR_FRAMEBUFFER CrPMgrFbGetFirstEnabled(); +HCR_FRAMEBUFFER CrPMgrFbGetNextEnabled(HCR_FRAMEBUFFER hFb); +HCR_FRAMEBUFFER CrPMgrFbGetFirstInitialized(); +HCR_FRAMEBUFFER CrPMgrFbGetNextInitialized(HCR_FRAMEBUFFER hFb); + +int CrFbRegionsClear(HCR_FRAMEBUFFER hFb); + + +#define CR_SERVER_FBO_BB_IDX(_mural) ((_mural)->iBbBuffer) +#define CR_SERVER_FBO_FB_IDX(_mural) (((_mural)->iBbBuffer + 1) % ((_mural)->cBuffers)) +/* returns a valid index to be used for negative _idx, i.e. for GL_NONE cases */ +//#define CR_SERVER_FBO_ADJUST_IDX(_mural, _idx) ((_idx) >= 0 ? (_idx) : CR_SERVER_FBO_BB_IDX(_mural)) +/* just a helper that uses CR_SERVER_FBO_ADJUST_IDX for getting mural's FBO id for buffer index*/ +//#define CR_SERVER_FBO_FOR_IDX(_mural, _idx) ((_mural)->aidFBOs[CR_SERVER_FBO_ADJUST_IDX((_mural), (_idx))]) +//#define CR_SERVER_FBO_TEX_FOR_IDX(_mural, _idx) ((_mural)->aidColorTexs[CR_SERVER_FBO_ADJUST_IDX((_mural), (_idx))]) +#define CR_SERVER_FBO_FOR_IDX(_mural, _idx) ((_idx) >= 0 ? (_mural)->aidFBOs[(_idx)] : 0) +#define CR_SERVER_FBO_TEX_FOR_IDX(_mural, _idx) ((_idx) >= 0 ? (_mural)->aidColorTexs[(_idx)] : 0) + +int32_t crVBoxServerInternalClientRead(CRClient *pClient, uint8_t *pBuffer, uint32_t *pcbBuffer); + +void crServerPerformMakeCurrent( CRMuralInfo *mural, CRContextInfo *ctxInfo ); + +PCR_BLITTER crServerVBoxBlitterGet(); +PCR_BLITTER crServerVBoxBlitterGetInitialized(); + +DECLINLINE(void) crServerVBoxBlitterWinInit(CR_BLITTER_WINDOW *win, CRMuralInfo *mural) +{ + win->Base.id = mural->spuWindow; + win->Base.visualBits = mural->CreateInfo.realVisualBits; + win->width = mural->width; + win->height = mural->height; +} + +DECLINLINE(void) crServerVBoxBlitterCtxInit(CR_BLITTER_CONTEXT *ctx, CRContextInfo *ctxInfo) +{ + ctx->Base.id = ctxInfo->SpuContext; + if (ctx->Base.id < 0) + ctx->Base.id = cr_server.MainContextInfo.SpuContext; + ctx->Base.visualBits = cr_server.curClient->currentCtxInfo->CreateInfo.realVisualBits; +} + +/* display worker thread. + * see comments for CR_SERVER_RPW struct definition in cr_server.h */ +DECLINLINE(void) crServerXchgI8(int8_t *pu8Val1, int8_t *pu8Val2) +{ + int8_t tmp; + tmp = *pu8Val1; + *pu8Val1 = *pu8Val2; + *pu8Val2 = tmp; +} + +#ifdef DEBUG +# define CR_GLERR_CHECK(_op) do { \ + GLenum status; \ + while ((status = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) {/*Assert(0);*/} \ + _op \ + while ((status = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) {Assert(0);} \ + } while (0) +#else +# define CR_GLERR_CHECK(_op) do { \ + _op \ + } while (0) +#endif + +#ifdef DEBUG_misha +# define CR_SERVER_RPW_DEBUG +#endif +/* * + * _name : Draw, Submitted, Worker, Gpu + */ + +#ifdef CR_SERVER_RPW_DEBUG +# define crServerRpwEntryDbgVerify(_pE) crServerRpwEntryDbgDoVerify(_pE) +#else +# define crServerRpwEntryDbgVerify(_pE) do {} while (0) +#endif + + +#define CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _name) ((_pEntry)->iTex##_name > 0) + +#define CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(_pEntry, _name) do { \ + crServerRpwEntryDbgVerify(_pEntry); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _name)); \ + (_pEntry)->iTex##_name = -(_pEntry)->iTex##_name; \ + crServerRpwEntryDbgVerify(_pEntry); \ + } while (0) + +#define CR_SERVER_RPW_ENTRY_TEX_PROMOTE(_pEntry, _fromName, _toName) do { \ + crServerRpwEntryDbgVerify(_pEntry); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \ + Assert(!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \ + crServerXchgI8(&(_pEntry)->iTex##_fromName, &(_pEntry)->iTex##_toName); \ + crServerRpwEntryDbgVerify(_pEntry); \ + } while (0) + +#define CR_SERVER_RPW_ENTRY_TEX_XCHG_VALID(_pEntry, _fromName, _toName) do { \ + crServerRpwEntryDbgVerify(_pEntry); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \ + crServerXchgI8(&(_pEntry)->iTex##_fromName, &(_pEntry)->iTex##_toName); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \ + crServerRpwEntryDbgVerify(_pEntry); \ + } while (0) + + +#define CR_SERVER_RPW_ENTRY_TEX_PROMOTE_KEEPVALID(_pEntry, _fromName, _toName) do { \ + crServerRpwEntryDbgVerify(_pEntry); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \ + Assert(!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \ + crServerXchgI8(&(_pEntry)->iTex##_fromName, &(_pEntry)->iTex##_toName); \ + (_pEntry)->iTex##_fromName = -(_pEntry)->iTex##_fromName; \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _fromName)); \ + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(_pEntry, _toName)); \ + crServerRpwEntryDbgVerify(_pEntry); \ + } while (0) + +#define CR_SERVER_RPW_ENTRY_TEX(_pEntry, _name) ((_pEntry)->aidWorkerTexs[(_pEntry)->iTex##_name - 1]) + +#define CR_SERVER_RPW_ENTRY_PBO_NEXT_ID(_i) (((_i) + 1) % 2) +#define CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(_pEntry) ((_pEntry)->iCurPBO >= 0) +#define CR_SERVER_RPW_ENTRY_PBO_CUR(_pEntry) ((_pEntry)->aidPBOs[(_pEntry)->iCurPBO]) +#define CR_SERVER_RPW_ENTRY_PBO_COMPLETED(_pEntry) ((_pEntry)->aidPBOs[CR_SERVER_RPW_ENTRY_PBO_NEXT_ID((_pEntry)->iCurPBO)]) +#define CR_SERVER_RPW_ENTRY_PBO_FLIP(_pEntry) do { \ + (_pEntry)->iCurPBO = CR_SERVER_RPW_ENTRY_PBO_NEXT_ID((_pEntry)->iCurPBO); \ + } while (0) + +#ifdef CR_SERVER_RPW_DEBUG +DECLINLINE(void) crServerRpwEntryDbgDoVerify(CR_SERVER_RPW_ENTRY *pEntry) +{ + int tstMask = 0; + int8_t iVal; + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Draw)); + +#define CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(_v) do { \ + iVal = RT_ABS(_v); \ + Assert(iVal > 0); \ + Assert(iVal < 5); \ + Assert(!(tstMask & (1 << iVal))); \ + tstMask |= (1 << iVal); \ + } while (0) + + CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexDraw); + CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexSubmitted); + CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexWorker); + CR_VERVER_RPW_ENTRY_DBG_CHECKVAL(pEntry->iTexGpu); + Assert(tstMask == 0x1E); +} +#endif + +DECLINLINE(bool) crServerRpwIsInitialized(const CR_SERVER_RPW *pWorker) +{ + return !!pWorker->ctxId; +} +int crServerRpwInit(CR_SERVER_RPW *pWorker); +int crServerRpwTerm(CR_SERVER_RPW *pWorker); +DECLINLINE(bool) crServerRpwEntryIsInitialized(const CR_SERVER_RPW_ENTRY *pEntry) +{ + return !!pEntry->pfnData; +} +int crServerRpwEntryInit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height, PFNCR_SERVER_RPW_DATA pfnData); +int crServerRpwEntryCleanup(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry); +int crServerRpwEntryResize(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height); +int crServerRpwEntrySubmit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry); +int crServerRpwEntryWaitComplete(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry); +int crServerRpwEntryCancel(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry); +DECLINLINE(void) crServerRpwEntryDrawSettingsToTex(const CR_SERVER_RPW_ENTRY *pEntry, VBOXVR_TEXTURE *pTex) +{ + pTex->width = pEntry->Size.cx; + pTex->height = pEntry->Size.cy; + pTex->target = GL_TEXTURE_2D; + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Draw)); + pTex->hwid = CR_SERVER_RPW_ENTRY_TEX(pEntry, Draw); +} +/**/ + +typedef struct CR_SERVER_CTX_SWITCH +{ + GLuint idDrawFBO, idReadFBO; + CRContext *pNewCtx; + CRContext *pOldCtx; +} CR_SERVER_CTX_SWITCH; + +DECLINLINE(void) crServerCtxSwitchPrepare(CR_SERVER_CTX_SWITCH *pData, CRContext *pNewCtx) +{ + CRMuralInfo *pCurrentMural = cr_server.currentMural; + CRContextInfo *pCurCtxInfo = cr_server.currentCtxInfo; + GLuint idDrawFBO, idReadFBO; + CRContext *pCurCtx = pCurCtxInfo ? pCurCtxInfo->pContext : NULL; + + CRASSERT(pCurCtx == crStateGetCurrent()); + + if (pCurrentMural) + { + idDrawFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurDrawBuffer); + idReadFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurReadBuffer); + } + else + { + idDrawFBO = 0; + idReadFBO = 0; + } + + crStateSwitchPrepare(pNewCtx, pCurCtx, idDrawFBO, idReadFBO); + + pData->idDrawFBO = idDrawFBO; + pData->idReadFBO = idReadFBO; + pData->pNewCtx = pNewCtx; + pData->pOldCtx = pCurCtx; +} + +DECLINLINE(void) crServerCtxSwitchPostprocess(CR_SERVER_CTX_SWITCH *pData) +{ + crStateSwitchPostprocess(pData->pOldCtx, pData->pNewCtx, pData->idDrawFBO, pData->idReadFBO); +} + +void crServerInitTmpCtxDispatch(); + +typedef struct CR_FBMAP +{ + uint8_t Map[(CR_MAX_GUEST_MONITORS+7)/8]; +} CR_FBMAP; + +DECLINLINE(void) CrFBmInit(CR_FBMAP *pMap) +{ + memset(pMap, 0, sizeof (*pMap)); +} + +DECLINLINE(bool) CrFBmIsSet(CR_FBMAP *pMap, uint32_t i) +{ + return ASMBitTest(&pMap->Map, i); +} + +DECLINLINE(void) CrFBmSet(CR_FBMAP *pMap, uint32_t i) +{ + ASMBitSet(&pMap->Map, i); +} + +DECLINLINE(void) CrFBmSetAtomic(CR_FBMAP *pMap, uint32_t i) +{ + ASMAtomicBitSet(&pMap->Map, i); +} + +DECLINLINE(void) CrFBmClear(CR_FBMAP *pMap, uint32_t i) +{ + ASMBitClear(&pMap->Map, i); +} + +/*helper function that calls CrFbUpdateBegin for all enabled framebuffers */ +int CrPMgrHlpGlblUpdateBegin(CR_FBMAP *pMap); +/*helper function that calls CrFbUpdateEnd for all framebuffers being updated */ +void CrPMgrHlpGlblUpdateEnd(CR_FBMAP *pMap); +HCR_FRAMEBUFFER CrPMgrFbGetFirstEnabled(); +HCR_FRAMEBUFFER CrPMgrFbGetNextEnabled(HCR_FRAMEBUFFER hFb); +HCR_FRAMEBUFFER CrPMgrFbGetEnabled(uint32_t idFb); +HCR_FRAMEBUFFER CrPMgrFbGetEnabledForScreen(uint32_t idScreen); +int CrPMgrModeVrdp(bool fEnable); +int CrPMgrModeRootVr(bool fEnable); +int CrPMgrModeWinVisible(bool fEnable); +int CrPMgrRootVrUpdate(); +int CrPMgrViewportUpdate(uint32_t idScreen); +int CrPMgrScreenChanged(uint32_t idScreen); +int CrPMgrResize(const struct VBVAINFOSCREEN *pScreen, void *pvVRAM, const uint32_t *pTargetMap); +int CrPMgrSaveState(PSSMHANDLE pSSM); +int CrPMgrLoadState(PSSMHANDLE pSSM, uint32_t version); +HCR_FRAMEBUFFER CrPMgrFbGet(uint32_t idScreen); +int CrPMgrClearRegionsGlobal(); +/*cleanup stuff*/ + + +int CrPMgrInit(); +void CrPMgrTerm(); +int CrPMgrDisable(); +int CrPMgrEnable(); + +typedef DECLCALLBACKPTR(bool, PFNCR_FRAMEBUFFER_ENTRIES_VISITOR_CB)(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext); + +bool CrFbHas3DData(HCR_FRAMEBUFFER hFb); +void CrFbVisitCreatedEntries(HCR_FRAMEBUFFER hFb, PFNCR_FRAMEBUFFER_ENTRIES_VISITOR_CB pfnVisitorCb, void *pvContext); +int CrFbResize(HCR_FRAMEBUFFER hFb, const struct VBVAINFOSCREEN * pScreen, void *pvVRAM); +int CrFbBltGetContentsEx(HCR_FRAMEBUFFER hFb, const RTRECTSIZE *pSrcRectSize, const RTRECT *pDstRect, uint32_t cRects, const RTRECT *pRects, CR_BLITTER_IMG *pImg); +bool CrFbIsEnabled(HCR_FRAMEBUFFER hFb); +int CrFbEntryCreateForTexId(HCR_FRAMEBUFFER hFb, GLuint idTex, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry); +int CrFbEntryCreateForTexData(HCR_FRAMEBUFFER hFb, struct CR_TEXDATA *pTex, uint32_t fFlags, HCR_FRAMEBUFFER_ENTRY *phEntry); +void CrFbEntryAddRef(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry); +void CrFbEntryRelease(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry); +const struct VBVAINFOSCREEN* CrFbGetScreenInfo(HCR_FRAMEBUFFER hFb); +void* CrFbGetVRAM(HCR_FRAMEBUFFER hFb); +const struct VBOXVR_SCR_COMPOSITOR* CrFbGetCompositor(HCR_FRAMEBUFFER hFb); +const struct VBOXVR_SCR_COMPOSITOR_ENTRY* CrFbEntryGetCompositorEntry(HCR_FRAMEBUFFER_ENTRY hEntry); + +/* start doing modifications to the framebuffer */ +int CrFbUpdateBegin(HCR_FRAMEBUFFER hFb); +/*below commands can only be used in Framebuffer update mode, i.e. after the CrFbUpdateBegin succeeded */ +int CrFbEntryRegions(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry); + +/* complete doing modifications to the framebuffer */ +void CrFbUpdateEnd(HCR_FRAMEBUFFER hFb); + +int CrFbEntryRegionsAdd(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY pEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated); +int CrFbEntryRegionsSet(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY pEntry, const RTPOINT *pPos, uint32_t cRegions, const RTRECT *paRegions, bool fPosRelated); + +int CrFbEntryTexDataUpdate(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY pEntry, struct CR_TEXDATA *pTex); + +CRHTABLE_HANDLE CrFbDDataAllocSlot(HCR_FRAMEBUFFER hFb); + +typedef DECLCALLBACKPTR(void, PFNCR_FRAMEBUFFER_SLOT_RELEASE_CB)(HCR_FRAMEBUFFER hFb, HCR_FRAMEBUFFER_ENTRY hEntry, void *pvContext); + +void CrFbDDataReleaseSlot(HCR_FRAMEBUFFER hFb, CRHTABLE_HANDLE hSlot, PFNCR_FRAMEBUFFER_SLOT_RELEASE_CB pfnReleaseCb, void *pvContext); +int CrFbDDataEntryPut(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot, void *pvData); +void* CrFbDDataEntryClear(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot); +void* CrFbDDataEntryGet(HCR_FRAMEBUFFER_ENTRY hEntry, CRHTABLE_HANDLE hSlot); + +CR_TEXDATA* CrFbTexDataCreate(const VBOXVR_TEXTURE *pTex); +void CrFbTexDataInit(CR_TEXDATA* pFbTex, const VBOXVR_TEXTURE *pTex, PFNCRTEXDATA_RELEASED pfnTextureReleased); + +int8_t crVBoxServerCrCmdBltProcess(VBOXCMDVBVA_BLT_HDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmd, uint32_t cbCmd); +int8_t crVBoxServerCrCmdClrFillProcess(VBOXCMDVBVA_CLRFILL_HDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmd, uint32_t cbCmd); +int8_t crVBoxServerCrCmdFlipProcess(VBOXCMDVBVA_FLIP const RT_UNTRUSTED_VOLATILE_GUEST *pFlip, uint32_t cbCmd); + + +int32_t crVBoxServerClientGet(uint32_t u32ClientID, CRClient **ppClient); + +int crServerPendSaveState(PSSMHANDLE pSSM); +int crServerPendLoadState(PSSMHANDLE pSSM, uint32_t u32Version); + +//#define VBOX_WITH_CRSERVER_DUMPER +#ifdef VBOX_WITH_CRSERVER_DUMPER +void crServerDumpCheckTerm(); +int crServerDumpCheckInit(); +void crServerDumpBuffer(int idx); +void crServerDumpTextures(); +void crServerDumpTexture(const VBOXVR_TEXTURE *pTex); +void crServerDumpShader(GLint id); +void crServerDumpProgram(GLint id); +void crServerDumpCurrentProgram(); +void crServerDumpRecompileDumpCurrentProgram(); +void crServerRecompileCurrentProgram(); +void crServerDumpCurrentProgramUniforms(); +void crServerDumpCurrentProgramAttribs(); +void crServerDumpFramesCheck(); +void crServerDumpState(); +void crServerDumpDrawel(const char*pszFormat, ...); +void crServerDumpDrawelv(GLuint idx, const char*pszElFormat, uint32_t cbEl, const void *pvVal, uint32_t cVal); + +extern int64_t g_CrDbgDumpPid; +extern unsigned long g_CrDbgDumpEnabled; +extern unsigned long g_CrDbgDumpDraw; +extern unsigned long g_CrDbgDumpDrawFramesSettings; +extern unsigned long g_CrDbgDumpDrawFramesAppliedSettings; +extern unsigned long g_CrDbgDumpDrawFramesCount; + +extern uint32_t g_CrDbgDumpVertattrFixupOn; + +bool crServerDumpFilterDmp(unsigned long event, CR_DUMPER *pDumper); +bool crServerDumpFilterOpEnter(unsigned long event, CR_DUMPER *pDumper); +void crServerDumpFilterOpLeave(unsigned long event, CR_DUMPER *pDumper); + +//#define CR_SERVER_DUMP_MASK_OP 0x0000fffc +//#define CR_SERVER_DUMP_OFF_OP 2 +// +//#define CR_SERVER_DUMP_MASK_DIR 0x00000003 +//#define CR_SERVER_DUMP_OFF_DIR 0 +// +//#define CR_SERVER_DUMP_MASK_DMP 0xffff0000 +//#define CR_SERVER_DUMP_OFF_DMP 16 +// +//#define CR_SERVER_DUMP_MAKE_OP(_v) (1 << ((_v) + CR_SERVER_DUMP_OFF_OP)) +//#define CR_SERVER_DUMP_MAKE_DIR(_v) (1 << ((_v) + CR_SERVER_DUMP_OFF_DIR)) +//#define CR_SERVER_DUMP_MAKE_DMP(_v) (1 << ((_v) + CR_SERVER_DUMP_OFF_DMP)) +// +//#define CR_SERVER_DUMP_GET_OP(_v) ((_v) & CR_SERVER_DUMP_MASK_OP) +//#define CR_SERVER_DUMP_GET_DMP(_v) ((_v) & CR_SERVER_DUMP_MASK_DMP) +//#define CR_SERVER_DUMP_GET_DIR(_v) ((_v) & CR_SERVER_DUMP_MASK_DIR) +// +//#define CR_SERVER_DUMP_ISANY_OP(_v1, _v2) (!!(CR_SERVER_DUMP_GET_OP(_v1) & CR_SERVER_DUMP_GET_OP(_v2))) +//#define CR_SERVER_DUMP_ISANY_DIR(_v1, _v2) (!!(CR_SERVER_DUMP_GET_DIR(_v1) & CR_SERVER_DUMP_GET_DIR(_v2))) +//#define CR_SERVER_DUMP_ISANY_DMP(_v1, _v2) (!!(CR_SERVER_DUMP_GET_DMP(_v1) & CR_SERVER_DUMP_GET_DMP(_v2))) +// +//#define CR_SERVER_DUMP_ISANY_OP(_v1, _v2) ((CR_SERVER_DUMP_GET_OP(_v1) & CR_SERVER_DUMP_GET_OP(_v2)) == CR_SERVER_DUMP_GET_OP(_v2)) +//#define CR_SERVER_DUMP_ISANY_DIR(_v1, _v2) ((CR_SERVER_DUMP_GET_DIR(_v1) & CR_SERVER_DUMP_GET_DIR(_v2)) == CR_SERVER_DUMP_GET_DIR(_v2)) +//#define CR_SERVER_DUMP_ISANY_DMP(_v1, _v2) ((CR_SERVER_DUMP_GET_DMP(_v1) & CR_SERVER_DUMP_GET_DMP(_v2)) == CR_SERVER_DUMP_GET_DMP(_v2)) +// +//#define CR_SERVER_DUMP_F_DIR_ENTER CR_SERVER_DUMP_MAKE_DIR(0) +//#define CR_SERVER_DUMP_F_DIR_LEAVE CR_SERVER_DUMP_MAKE_DIR(1) +// +//#define CR_SERVER_DUMP_F_OP_DRAW CR_SERVER_DUMP_MAKE_OP(0) +//#define CR_SERVER_DUMP_F_OP_SWAPBUFFERS CR_SERVER_DUMP_MAKE_OP(1) +//#define CR_SERVER_DUMP_F_OP_LINK_PROGRAM CR_SERVER_DUMP_MAKE_OP(2) +//#define CR_SERVER_DUMP_F_OP_COMPILE_PROGRAM CR_SERVER_DUMP_MAKE_OP(3) +// +//#define CR_SERVER_DUMP_F_DMP_BUFF CR_SERVER_DUMP_MAKE_DMP(0) +//#define CR_SERVER_DUMP_F_DMP_TEX CR_SERVER_DUMP_MAKE_DMP(0) +//#define CR_SERVER_DUMP_F_DMP_PROGRAM CR_SERVER_DUMP_MAKE_DMP(0) +//#define CR_SERVER_DUMP_F_DMP_PROGRAM_UNIFORMS CR_SERVER_DUMP_MAKE_DMP(0) +//#define CR_SERVER_DUMP_F_DMP_STATE CR_SERVER_DUMP_MAKE_DMP(0) +// +//#define CR_SERVER_DUMP_GET_OP(_v) ((_v) & CR_SERVER_DUMP_MASK_OP) +//#define CR_SERVER_DUMP_GET_DMP(_v) ((_v) & CR_SERVER_DUMP_MASK_DMP) +//#define CR_SERVER_DUMP_GET_DIR(_v) ((_v) & CR_SERVER_DUMP_MASK_DIR) + +#define CR_SERVER_DUMP_F_DRAW_BUFF_ENTER 0x00000001 +#define CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE 0x00000002 +#define CR_SERVER_DUMP_F_DRAW_STATE_ENTER 0x00000004 +#define CR_SERVER_DUMP_F_DRAW_STATE_LEAVE 0x00000008 +#define CR_SERVER_DUMP_F_DRAW_TEX_ENTER 0x00000010 +#define CR_SERVER_DUMP_F_DRAW_TEX_LEAVE 0x00000020 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER 0x00000040 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_LEAVE 0x00000080 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER 0x00000100 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_LEAVE 0x00000200 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER 0x00000400 +#define CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_LEAVE 0x00000800 + +#define CR_SERVER_DUMP_F_DRAW_ENTER_ALL (CR_SERVER_DUMP_F_DRAW_BUFF_ENTER \ + | CR_SERVER_DUMP_F_DRAW_TEX_ENTER \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER \ + | CR_SERVER_DUMP_F_DRAW_STATE_ENTER \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER) + +#define CR_SERVER_DUMP_F_DRAW_LEAVE_ALL (CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE \ + | CR_SERVER_DUMP_F_DRAW_TEX_LEAVE \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_LEAVE \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_LEAVE \ + | CR_SERVER_DUMP_F_DRAW_STATE_LEAVE \ + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_LEAVE) + +#define CR_SERVER_DUMP_F_DRAW_ALL (CR_SERVER_DUMP_F_DRAW_ENTER_ALL | CR_SERVER_DUMP_F_DRAW_LEAVE_ALL) + +#define CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER 0x00010000 +#define CR_SERVER_DUMP_F_SWAPBUFFERS_LEAVE 0x00020000 +#define CR_SERVER_DUMP_F_TEXPRESENT 0x00040000 +#define CR_SERVER_DUMP_F_DRAWEL 0x00100000 +#define CR_SERVER_DUMP_F_COMPILE_SHADER 0x01000000 +#define CR_SERVER_DUMP_F_SHADER_SOURCE 0x02000000 +#define CR_SERVER_DUMP_F_LINK_PROGRAM 0x04000000 + + +#define CR_SERVER_DUMP_DEFAULT_FILTER_OP(_ev) ((((_ev) & g_CrDbgDumpDraw) != 0) \ + || ((_ev) == CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER && g_CrDbgDumpDrawFramesCount)) + +#define CR_SERVER_DUMP_DEFAULT_FILTER_DMP(_ev) (((_ev) & g_CrDbgDumpDraw) != 0) + +#define CR_SERVER_DUMP_FILTER_OP(_ev, _pDumper) (g_CrDbgDumpEnabled \ + && (!g_CrDbgDumpPid \ + || (g_CrDbgDumpPid > 0 && ((uint64_t)g_CrDbgDumpPid) == cr_server.curClient->pid) \ + || (g_CrDbgDumpPid < 0 && ((uint64_t)(-g_CrDbgDumpPid)) != cr_server.curClient->pid)) \ + && crServerDumpFilterOpEnter((_ev), (_pDumper))) +#define CR_SERVER_DUMP_FILTER_DMP(_ev, _pDumper) (crServerDumpFilterDmp((_ev), (_pDumper))) + +#define CR_SERVER_DUMP_DRAW_ENTER() do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAW_ENTER_ALL, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_STATE_ENTER, cr_server.Recorder.pDumper)) { crServerDumpState(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgram(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramUniforms(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramAttribs(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_TEX_ENTER, cr_server.Recorder.pDumper)) { crServerDumpTextures(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_BUFF_ENTER, cr_server.Recorder.pDumper)) { crServerDumpBuffer(-1); } \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAW_ENTER_ALL, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_DRAW_LEAVE() do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAW_LEAVE_ALL, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_TEX_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpTextures(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpBuffer(-1); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramUniforms(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgramAttribs(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_PROGRAM_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpCurrentProgram(); } \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_DRAW_STATE_LEAVE, cr_server.Recorder.pDumper)) { crServerDumpState(); } \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAW_LEAVE_ALL, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_COMPILE_SHADER(_id) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_COMPILE_SHADER, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpShader((_id)); \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_COMPILE_SHADER, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_SHADER_SOURCE(_id) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_SHADER_SOURCE, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpShader((_id)); \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_SHADER_SOURCE, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_LINK_PROGRAM(_id) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_LINK_PROGRAM, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpProgram((_id)); \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_LINK_PROGRAM, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_SWAPBUFFERS_ENTER() do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + if (CR_SERVER_DUMP_FILTER_DMP(CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER, cr_server.Recorder.pDumper)) { crServerDumpBuffer(CR_SERVER_FBO_BB_IDX(cr_server.currentMural)); } \ + if (g_CrDbgDumpDrawFramesCount) { crServerDumpFramesCheck(); } \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done ENTER[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_TEXPRESENT(_pTex) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_TEXPRESENT, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpTexture((_pTex)); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_TEXPRESENT, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_SWAPBUFFERS_LEAVE() do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_SWAPBUFFERS_LEAVE, cr_server.Recorder.pDumper)) break; \ + crDmpStrF(cr_server.Recorder.pDumper, "==LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==Done LEAVE[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_SWAPBUFFERS_LEAVE, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_DRAWEL_F(_msg) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpDrawel _msg; \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper); \ + } while (0) + +#define CR_SERVER_DUMP_DRAWEL_V(_index, _pszElFormat, _cbEl, _pvVal, _cVal) do { \ + if (!CR_SERVER_DUMP_FILTER_OP(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper)) break; \ + crServerDumpCheckInit(); \ + crDmpStrF(cr_server.Recorder.pDumper, "==[%d] %s==", (uint32_t)cr_server.curClient->pid, __FUNCTION__); \ + crServerDumpDrawelv((_index), (_pszElFormat), (_cbEl), (_pvVal), (_cVal)); \ + crServerDumpFilterOpLeave(CR_SERVER_DUMP_F_DRAWEL, cr_server.Recorder.pDumper); \ + } while (0) +#else /* if !defined VBOX_WITH_CRSERVER_DUMPER */ +#define CR_SERVER_DUMP_DRAW_ENTER() do {} while (0) +#define CR_SERVER_DUMP_DRAW_LEAVE() do {} while (0) +#define CR_SERVER_DUMP_COMPILE_SHADER(_id) do {} while (0) +#define CR_SERVER_DUMP_LINK_PROGRAM(_id) do {} while (0) +#define CR_SERVER_DUMP_TEXPRESENT(_pTex) do {} while (0) +#define CR_SERVER_DUMP_SWAPBUFFERS_ENTER() do {} while (0) +#define CR_SERVER_DUMP_SWAPBUFFERS_LEAVE() do {} while (0) +#define CR_SERVER_DUMP_SHADER_SOURCE(_id) do {} while (0) +#define CR_SERVER_DUMP_DRAWEL_F(_msg) do {} while (0) +#define CR_SERVER_DUMP_DRAWEL_V(_index, _pszElFormat, _cbEl, _pvVal, _cVal) do {} while (0) +#endif /* !VBOX_WITH_CRSERVER_DUMPER */ + +RT_C_DECLS_END + +#endif /* CR_SERVER_H */ diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_boundsinfo.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_boundsinfo.c new file mode 100644 index 00000000..8cfa7999 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_boundsinfo.c @@ -0,0 +1,329 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "server_dispatch.h" +#include "server.h" +#include "cr_error.h" +#include "cr_unpack.h" +#include "cr_mem.h" +#include "state/cr_statetypes.h" + +/* This code copied from the tilesorter (fooey) */ + +typedef struct BucketRegion *BucketRegion_ptr; +typedef struct BucketRegion { + CRbitvalue id; + CRrecti extents; + BucketRegion_ptr right; + BucketRegion_ptr up; +} BucketRegion; + +#define HASHRANGE 256 + +#define BKT_DOWNHASH(a, range) ((a)*HASHRANGE/(range)) +#define BKT_UPHASH(a, range) ((a)*HASHRANGE/(range) + ((a)*HASHRANGE%(range)?1:0)) + +struct BucketingInfo { + BucketRegion *rhash[HASHRANGE][HASHRANGE]; + BucketRegion *rlist; +}; + + +/* + * At this point we know that the tiles are uniformly sized so we can use + * a hash-based bucketing method. Setup the hash table now. + */ +static GLboolean +fillBucketingHash(CRMuralInfo *mural) +{ +#if 0 + int i, j, k, m; + int r_len = 0; + int xinc, yinc; + int rlist_alloc = 64 * 128; + BucketRegion *rptr; + struct BucketingInfo *bucketInfo; + + if (mural->bucketInfo) { + crFree(mural->bucketInfo->rlist); + crFree(mural->bucketInfo); + mural->bucketInfo = NULL; + } + + bucketInfo = (struct BucketingInfo *) crCalloc(sizeof(struct BucketingInfo)); + if (!bucketInfo) + return GL_FALSE; + + /* Allocate rlist (don't free it!!!) */ + bucketInfo->rlist = (BucketRegion *) crAlloc(rlist_alloc * sizeof(BucketRegion)); + + for ( i = 0; i < HASHRANGE; i++ ) + { + for ( j = 0; j < HASHRANGE; j++ ) + { + bucketInfo->rhash[i][j] = NULL; + } + } + + /* Fill the rlist */ + xinc = mural->extents[0].imagewindow.x2 - mural->extents[0].imagewindow.x1; + yinc = mural->extents[0].imagewindow.y2 - mural->extents[0].imagewindow.y1; + CRASSERT(xinc > 0 || mural->width == 0); + CRASSERT(yinc > 0 || mural->height == 0); + + rptr = bucketInfo->rlist; + for (i=0; i < (int) mural->width; i+=xinc) + { + for (j=0; j < (int) mural->height; j+=yinc) + { + for (k=0; k < mural->numExtents; k++) + { + if (mural->extents[k].imagewindow.x1 == i && + mural->extents[k].imagewindow.y1 == j) + { + rptr->extents = mural->extents[k].imagewindow; /* x1,y1,x2,y2 */ + rptr->id = k; + break; + } + } + if (k == mural->numExtents) + { + rptr->extents.x1 = i; + rptr->extents.y1 = j; + rptr->extents.x2 = i + xinc; + rptr->extents.y2 = j + yinc; + rptr->id = -1; + } + rptr++; + } + } + r_len = rptr - bucketInfo->rlist; + + /* Fill hash table */ + for (i = 0; i < r_len; i++) + { + BucketRegion *r = &bucketInfo->rlist[i]; + + for (k=BKT_DOWNHASH(r->extents.x1, (int)mural->width); + k<=BKT_UPHASH(r->extents.x2, (int)mural->width) && + k < HASHRANGE; + k++) + { + for (m=BKT_DOWNHASH(r->extents.y1, (int)mural->height); + m<=BKT_UPHASH(r->extents.y2, (int)mural->height) && + m < HASHRANGE; + m++) + { + if ( bucketInfo->rhash[m][k] == NULL || + (bucketInfo->rhash[m][k]->extents.x1 > r->extents.x1 && + bucketInfo->rhash[m][k]->extents.y1 > r->extents.y1)) + { + bucketInfo->rhash[m][k] = r; + } + } + } + } + + /* Initialize links */ + for (i=0; i<r_len; i++) + { + BucketRegion *r = &bucketInfo->rlist[i]; + r->right = NULL; + r->up = NULL; + } + + /* Build links */ + for (i=0; i<r_len; i++) + { + BucketRegion *r = &bucketInfo->rlist[i]; + for (j=0; j<r_len; j++) + { + BucketRegion *q = &bucketInfo->rlist[j]; + if (r==q) + continue; + + /* Right Edge */ + if (r->extents.x2 == q->extents.x1 && + r->extents.y1 == q->extents.y1 && + r->extents.y2 == q->extents.y2) + { + r->right = q; + } + + /* Upper Edge */ + if (r->extents.y2 == q->extents.y1 && + r->extents.x1 == q->extents.x1 && + r->extents.x2 == q->extents.x2) + { + r->up = q; + } + } + } + + mural->bucketInfo = bucketInfo; +#endif + return GL_TRUE; +} + + +/* + * Check if the tiles are the same size. If so, initialize hash-based + * bucketing. + */ +GLboolean +crServerInitializeBucketing(CRMuralInfo *mural) +{ +#if 0 + int optTileWidth = 0, optTileHeight = 0; + int i; + + for (i = 0; i < mural->numExtents; i++) + { + const int w = mural->extents[i].imagewindow.x2 - + mural->extents[i].imagewindow.x1; + const int h = mural->extents[i].imagewindow.y2 - + mural->extents[i].imagewindow.y1; + + if (optTileWidth == 0 && optTileHeight == 0) { + /* First tile */ + optTileWidth = w; + optTileHeight = h; + } + else + { + /* Subsequent tile - make sure it's the same size as first and + * falls on the expected x/y location. + */ + if (w != optTileWidth || h != optTileHeight) { + crWarning("Tile %d, %d .. %d, %d is not the right size!", + mural->extents[i].imagewindow.x1, mural->extents[i].imagewindow.y1, + mural->extents[i].imagewindow.x2, mural->extents[i].imagewindow.y2); + crWarning("All tiles must be same size with optimize_bucket."); + crWarning("Turning off optimize_bucket for this mural."); + return GL_FALSE; + } + else if ((mural->extents[i].imagewindow.x1 % optTileWidth) != 0 || + (mural->extents[i].imagewindow.x2 % optTileWidth) != 0 || + (mural->extents[i].imagewindow.y1 % optTileHeight) != 0 || + (mural->extents[i].imagewindow.y2 % optTileHeight) != 0) + { + crWarning("Tile %d, %d .. %d, %d is not positioned correctly " + "to use optimize_bucket.", + mural->extents[i].imagewindow.x1, mural->extents[i].imagewindow.y1, + mural->extents[i].imagewindow.x2, mural->extents[i].imagewindow.y2); + crWarning("Turning off optimize_bucket for this mural."); + return GL_FALSE; + } + } + } +#endif + return fillBucketingHash(mural); +} + + +/** + * Process a crBoundsInfoCR message/function. This is a bounding box + * followed by a payload of arbitrary Chromium rendering commands. + * The tilesort SPU will send this. + * Note: the bounding box is in mural pixel coordinates (y=0=bottom) + */ +void SERVER_DISPATCH_APIENTRY +crServerDispatchBoundsInfoCR( const CRrecti *bounds, const GLbyte *payload, + GLint len, GLint num_opcodes ) +{ +#if 0 + CRMuralInfo *mural = cr_server.curClient->currentMural; + char *data_ptr = (char*)(payload + ((num_opcodes + 3 ) & ~0x03)); + unsigned int bx, by; +#endif + + /* Save current unpacker state */ + crUnpackPush(); +#if 0 + /* pass bounds info to first SPU */ + { + /* bias bounds to extent/window coords */ + CRrecti bounds2; + const int dx = mural->extents[0].imagewindow.x1; + const int dy = mural->extents[0].imagewindow.y1; + if (bounds->x1 == -CR_MAXINT) { + /* "infinite" bounds: convert to full image bounds */ + bounds2.x1 = 0; + bounds2.y1 = 0; + bounds2.x2 = mural->extents[0].imagewindow.x2 - dx; /* width */ + bounds2.y2 = mural->extents[0].imagewindow.y2 - dy; /* height */ + } + else { + bounds2.x1 = bounds->x1 - dx; + bounds2.y1 = bounds->y1 - dy; + bounds2.x2 = bounds->x2 - dx; + bounds2.y2 = bounds->y2 - dy; + } + cr_server.head_spu->dispatch_table.BoundsInfoCR(&bounds2, NULL, 0, 0); + } + + if (!mural->viewportValidated) { + crServerComputeViewportBounds(&(cr_server.curClient->currentCtxInfo->pContext->viewport), + mural); + } + + bx = BKT_DOWNHASH(bounds->x1, mural->width); + by = BKT_DOWNHASH(bounds->y1, mural->height); + + /* Check for out of bounds, and optimizeBucket to enable */ + if (mural->optimizeBucket && (bx <= HASHRANGE) && (by <= HASHRANGE)) + { + const struct BucketingInfo *bucketInfo = mural->bucketInfo; + const BucketRegion *r; + const BucketRegion *p; + + CRASSERT(bucketInfo); + + for (r = bucketInfo->rhash[by][bx]; r && bounds->y2 >= r->extents.y1; + r = r->up) + { + for (p=r; p && bounds->x2 >= p->extents.x1; p = p->right) + { + if ( p->id != (unsigned int) -1 && + bounds->x1 < p->extents.x2 && + bounds->y1 < p->extents.y2 && + bounds->y2 >= p->extents.y1 ) + { + mural->curExtent = p->id; + if (cr_server.run_queue->client->currentCtxInfo && cr_server.run_queue->client->currentCtxInfo->pContext) { + crServerSetOutputBounds( mural, mural->curExtent ); + } + crUnpack( data_ptr, NULL, data_ptr-1, num_opcodes, &(cr_server.dispatch) ); + } + } + } + } + else + { + /* non-optimized bucketing - unpack/render for each tile/extent */ + int i; + for ( i = 0; i < mural->numExtents; i++ ) + { + CRExtent *extent = &mural->extents[i]; + + if (cr_server.localTileSpec || + (extent->imagewindow.x2 > bounds->x1 && + extent->imagewindow.x1 < bounds->x2 && + extent->imagewindow.y2 > bounds->y1 && + extent->imagewindow.y1 < bounds->y2)) + { + mural->curExtent = i; + if (cr_server.run_queue->client->currentCtxInfo && cr_server.run_queue->client->currentCtxInfo->pContext) { + crServerSetOutputBounds( mural, i ); + } + crUnpack( data_ptr, NULL, data_ptr-1, num_opcodes, &(cr_server.dispatch) ); + } + } + } +#endif + /* Restore previous unpacker state */ + crUnpackPop(); +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c new file mode 100644 index 00000000..0f898f5f --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_bufferobject.c @@ -0,0 +1,111 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "chromium.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "server_dispatch.h" +#include "server.h" +#include "cr_unpack.h" + +void * SERVER_DISPATCH_APIENTRY +crServerDispatchMapBufferARB( GLenum target, GLenum access ) +{ + return NULL; +} + +GLboolean SERVER_DISPATCH_APIENTRY +crServerDispatchUnmapBufferARB( GLenum target ) +{ + return GL_FALSE; +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGenBuffersARB(GLsizei n, GLuint *buffers) +{ + GLuint *local_buffers; + (void) buffers; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchGenBuffersARB: parameter 'n' is out of range"); + return; + } + + local_buffers = (GLuint *)crCalloc(n * sizeof(*local_buffers)); + + if (!local_buffers) + { + crError("crServerDispatchGenBuffersARB: out of memory"); + return; + } + + crStateGenBuffersARB(n, local_buffers); + + crServerReturnValue( local_buffers, n * sizeof(*local_buffers) ); + crFree( local_buffers ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteBuffersARB( GLsizei n, const GLuint * buffer ) +{ + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint) || !DATA_POINTER_CHECK(n * sizeof(GLuint))) + { + crError("glDeleteBuffersARB: parameter 'n' is out of range"); + return; + } + + crStateDeleteBuffersARB( n, buffer ); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGetBufferPointervARB(GLenum target, GLenum pname, GLvoid **params) +{ + crError( "glGetBufferPointervARB isn't *ever* allowed to be on the wire!" ); + (void) target; + (void) pname; + (void) params; +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGetBufferSubDataARB(GLenum target, GLintptrARB offset, GLsizeiptrARB size, void * data) +{ + void *b; + + if (size <= 0 || size >= INT32_MAX / 2) + { + crError("crServerDispatchGetBufferSubDataARB: size is out of range"); + return; + } + + b = crCalloc(size); + + if (b) { + cr_server.head_spu->dispatch_table.GetBufferSubDataARB( target, offset, size, b ); + + crServerReturnValue( b, size ); + crFree( b ); + } + else { + crError("Out of memory in crServerDispatchGetBufferSubDataARB"); + } +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchBindBufferARB(GLenum target, GLuint buffer) +{ + crStateBindBufferARB(target, buffer); + cr_server.head_spu->dispatch_table.BindBufferARB(target, crStateGetBufferHWID(buffer)); +} + +GLboolean SERVER_DISPATCH_APIENTRY +crServerDispatchIsBufferARB(GLuint buffer) +{ + /* since GenBuffersARB issued to host ogl only on bind + some other ops, the host drivers may not know about them + * so use state data*/ + GLboolean retval = crStateIsBufferARB(buffer); + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c new file mode 100644 index 00000000..64ef9462 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clear.c @@ -0,0 +1,492 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_spu.h" +#include "chromium.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" + +#ifdef VBOXCR_LOGFPS +#include <iprt/timer.h> +#include <iprt/ctype.h> +typedef struct VBOXCRFPS +{ + uint64_t mPeriodSum; + uint64_t *mpaPeriods; + uint64_t mPrevTime; + uint64_t mcFrames; + uint32_t mcPeriods; + uint32_t miPeriod; + + uint64_t mBytesSum; + uint32_t *mpaBytes; + + uint64_t mBytesSentSum; + uint32_t *mpaBytesSent; + + uint64_t mCallsSum; + uint32_t *mpaCalls; + + uint64_t mOpsSum; + uint32_t *mpaOps; + + uint64_t mTimeUsedSum; + uint64_t *mpaTimes; +} VBOXCRFPS, *PVBOXCRFPS; + +void vboxCrFpsInit(PVBOXCRFPS pFps, uint32_t cPeriods) +{ + crMemset(pFps, 0, sizeof (*pFps)); + pFps->mcPeriods = cPeriods; + pFps->mpaPeriods = crCalloc(sizeof (pFps->mpaPeriods[0]) * cPeriods); + pFps->mpaBytes = crCalloc(sizeof (pFps->mpaBytes[0]) * cPeriods); + pFps->mpaBytesSent = crCalloc(sizeof (pFps->mpaBytesSent[0]) * cPeriods); + pFps->mpaCalls = crCalloc(sizeof (pFps->mpaCalls[0]) * cPeriods); + pFps->mpaOps = crCalloc(sizeof (pFps->mpaOps[0]) * cPeriods); + pFps->mpaTimes = crCalloc(sizeof (pFps->mpaTimes[0]) * cPeriods); +} + +void vboxCrFpsTerm(PVBOXCRFPS pFps) +{ + crFree(pFps->mpaPeriods); + crFree(pFps->mpaBytes); + crFree(pFps->mpaCalls); +} + +void vboxCrFpsReportFrame(PVBOXCRFPS pFps) +{ + uint64_t cur = RTTimeNanoTS(); + uint64_t curBytes, curBytesSent, curCalls, curOps, curTimeUsed; + int i; + + curBytes = 0; + curBytesSent = 0; + curCalls = 0; + curOps = 0; + curTimeUsed = 0; + + for (i = 0; i < cr_server.numClients; i++) + { + if (cr_server.clients[i] && cr_server.clients[i]->conn) + { + curBytes += cr_server.clients[i]->conn->total_bytes_recv; + curBytesSent += cr_server.clients[i]->conn->total_bytes_sent; + curCalls += cr_server.clients[i]->conn->recv_count; + curOps += cr_server.clients[i]->conn->opcodes_count; + curTimeUsed += cr_server.clients[i]->timeUsed; + cr_server.clients[i]->conn->total_bytes_recv = 0; + cr_server.clients[i]->conn->total_bytes_sent = 0; + cr_server.clients[i]->conn->recv_count = 0; + cr_server.clients[i]->conn->opcodes_count = 0; + cr_server.clients[i]->timeUsed = 0; + } + } + + if(pFps->mPrevTime) + { + uint64_t curPeriod = cur - pFps->mPrevTime; + + pFps->mPeriodSum += curPeriod - pFps->mpaPeriods[pFps->miPeriod]; + pFps->mpaPeriods[pFps->miPeriod] = curPeriod; + + pFps->mBytesSum += curBytes - pFps->mpaBytes[pFps->miPeriod]; + pFps->mpaBytes[pFps->miPeriod] = curBytes; + + pFps->mBytesSentSum += curBytesSent - pFps->mpaBytesSent[pFps->miPeriod]; + pFps->mpaBytesSent[pFps->miPeriod] = curBytesSent; + + pFps->mCallsSum += curCalls - pFps->mpaCalls[pFps->miPeriod]; + pFps->mpaCalls[pFps->miPeriod] = curCalls; + + pFps->mOpsSum += curOps - pFps->mpaOps[pFps->miPeriod]; + pFps->mpaOps[pFps->miPeriod] = curOps; + + pFps->mTimeUsedSum += curTimeUsed - pFps->mpaTimes[pFps->miPeriod]; + pFps->mpaTimes[pFps->miPeriod] = curTimeUsed; + + ++pFps->miPeriod; + pFps->miPeriod %= pFps->mcPeriods; + } + pFps->mPrevTime = cur; + ++pFps->mcFrames; +} + +uint64_t vboxCrFpsGetEveragePeriod(PVBOXCRFPS pFps) +{ + return pFps->mPeriodSum / pFps->mcPeriods; +} + +double vboxCrFpsGetFps(PVBOXCRFPS pFps) +{ + return ((double)1000000000.0) / vboxCrFpsGetEveragePeriod(pFps); +} + +double vboxCrFpsGetBps(PVBOXCRFPS pFps) +{ + return vboxCrFpsGetFps(pFps) * pFps->mBytesSum / pFps->mcPeriods; +} + +double vboxCrFpsGetBpsSent(PVBOXCRFPS pFps) +{ + return vboxCrFpsGetFps(pFps) * pFps->mBytesSentSum / pFps->mcPeriods; +} + +double vboxCrFpsGetCps(PVBOXCRFPS pFps) +{ + return vboxCrFpsGetFps(pFps) * pFps->mCallsSum / pFps->mcPeriods; +} + +double vboxCrFpsGetOps(PVBOXCRFPS pFps) +{ + return vboxCrFpsGetFps(pFps) * pFps->mOpsSum / pFps->mcPeriods; +} + +double vboxCrFpsGetTimeProcPercent(PVBOXCRFPS pFps) +{ + return 100.0*pFps->mTimeUsedSum/pFps->mPeriodSum; +} + +uint64_t vboxCrFpsGetNumFrames(PVBOXCRFPS pFps) +{ + return pFps->mcFrames; +} + +#endif + + +void SERVER_DISPATCH_APIENTRY crServerDispatchClear( GLenum mask ) +{ + CRMuralInfo *mural = cr_server.curClient->currentMural; + const RunQueue *q = cr_server.run_queue; + + if (cr_server.only_swap_once) + { + /* NOTE: we only do the clear for the _last_ client in the list. + * This is because in multi-threaded apps the zeroeth client may + * be idle and never call glClear at all. See threadtest.c + * It's pretty likely that the last client will be active. + */ + if ((mask & GL_COLOR_BUFFER_BIT) && + (cr_server.curClient != cr_server.clients[cr_server.numClients - 1])) + return; + } + + cr_server.head_spu->dispatch_table.Clear( mask ); +} + +static void __draw_poly(CRPoly *p) +{ + int b; + + cr_server.head_spu->dispatch_table.Begin(GL_POLYGON); + for (b=0; b<p->npoints; b++) + cr_server.head_spu->dispatch_table.Vertex2dv(p->points+2*b); + cr_server.head_spu->dispatch_table.End(); +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchSwapBuffers( GLint window, GLint flags ) +{ + CRMuralInfo *mural; + CRContext *ctx; + +#ifdef VBOXCR_LOGFPS + static VBOXCRFPS Fps; + static bool bFpsInited = false; + + if (!bFpsInited) + { + vboxCrFpsInit(&Fps, 64 /* cPeriods */); + bFpsInited = true; + } + vboxCrFpsReportFrame(&Fps); + if(!(vboxCrFpsGetNumFrames(&Fps) % 31)) + { + double fps = vboxCrFpsGetFps(&Fps); + double bps = vboxCrFpsGetBps(&Fps); + double bpsSent = vboxCrFpsGetBpsSent(&Fps); + double cps = vboxCrFpsGetCps(&Fps); + double ops = vboxCrFpsGetOps(&Fps); + double tup = vboxCrFpsGetTimeProcPercent(&Fps); + crDebug("fps: %f, rec Mbps: %.1f, send Mbps: %.1f, cps: %.1f, ops: %.0f, host %.1f%%", + fps, bps/(1024.0*1024.0), bpsSent/(1024.0*1024.0), cps, ops, tup); + } +#endif + mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); + if (!mural) { + return; + } + + + if (cr_server.only_swap_once) + { + /* NOTE: we only do the clear for the _last_ client in the list. + * This is because in multi-threaded apps the zeroeth client may + * be idle and never call glClear at all. See threadtest.c + * It's pretty likely that the last client will be active. + */ + if (cr_server.curClient != cr_server.clients[cr_server.numClients - 1]) + { + return; + } + } + +#if 0 + if (cr_server.overlapBlending) + { + int a; + CRPoly *p; + GLboolean lighting, fog, blend, cull, tex[3]; + GLenum mm, blendSrc, blendDst; + GLcolorf col; + CRContext *ctx = crStateGetCurrent(); + const CRmatrix *baseProj; + + /* + * I've probably missed some state here, or it + * might be easier just to push/pop it.... + */ + lighting = ctx->lighting.lighting; + fog = ctx->fog.enable; + tex[0] = 0; + for (a=0; a<CR_MAX_TEXTURE_UNITS; a++) + { + if (!ctx->texture.unit[a].enabled1D) continue; + + tex[0] = 1; + break; + } + tex[1] = 0; + for (a=0; a<CR_MAX_TEXTURE_UNITS; a++) + { + if (!ctx->texture.unit[a].enabled2D) continue; + + tex[1] = 1; + break; + } + tex[2] = 0; + for (a=0; a<CR_MAX_TEXTURE_UNITS; a++) + { + if (!ctx->texture.unit[a].enabled3D) continue; + + tex[2] = 1; + break; + } + + cull = ctx->polygon.cullFace; + blend = ctx->buffer.blend; + blendSrc = ctx->buffer.blendSrcRGB; + blendDst = ctx->buffer.blendDstRGB; + mm = ctx->transform.matrixMode; + col.r = ctx->current.vertexAttrib[VERT_ATTRIB_COLOR0][0]; + col.g = ctx->current.vertexAttrib[VERT_ATTRIB_COLOR0][1]; + col.b = ctx->current.vertexAttrib[VERT_ATTRIB_COLOR0][2]; + col.a = ctx->current.vertexAttrib[VERT_ATTRIB_COLOR0][3]; + + baseProj = &(cr_server.curClient->currentMural->extents[0].baseProjection); + + switch(mm) + { + case GL_PROJECTION: + cr_server.head_spu->dispatch_table.PushMatrix(); + cr_server.head_spu->dispatch_table.LoadMatrixf((GLfloat *) baseProj); + cr_server.head_spu->dispatch_table.MultMatrixf(cr_server.unnormalized_alignment_matrix); + cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW); + cr_server.head_spu->dispatch_table.PushMatrix(); + cr_server.head_spu->dispatch_table.LoadIdentity(); + break; + + default: + cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW); + /* fall through */ + + case GL_MODELVIEW: + cr_server.head_spu->dispatch_table.PushMatrix(); + cr_server.head_spu->dispatch_table.LoadIdentity(); + cr_server.head_spu->dispatch_table.MatrixMode(GL_PROJECTION); + cr_server.head_spu->dispatch_table.PushMatrix(); + cr_server.head_spu->dispatch_table.LoadMatrixf((GLfloat *) baseProj); + cr_server.head_spu->dispatch_table.MultMatrixf(cr_server.unnormalized_alignment_matrix); + break; + } + + /* fix state */ + if (lighting) + cr_server.head_spu->dispatch_table.Disable(GL_LIGHTING); + if (fog) + cr_server.head_spu->dispatch_table.Disable(GL_FOG); + if (tex[0]) + cr_server.head_spu->dispatch_table.Disable(GL_TEXTURE_1D); + if (tex[1]) + cr_server.head_spu->dispatch_table.Disable(GL_TEXTURE_2D); + if (tex[2]) + cr_server.head_spu->dispatch_table.Disable(GL_TEXTURE_3D); + if (cull) + cr_server.head_spu->dispatch_table.Disable(GL_CULL_FACE); + + /* Regular Blending */ + if (cr_server.overlapBlending == 1) + { + if (!blend) + cr_server.head_spu->dispatch_table.Enable(GL_BLEND); + if ((blendSrc != GL_ZERO) && (blendDst != GL_SRC_ALPHA)) + cr_server.head_spu->dispatch_table.BlendFunc(GL_ZERO, GL_SRC_ALPHA); + + /* draw the blends */ + for (a=1; a<cr_server.num_overlap_levels; a++) + { + if (a-1 < cr_server.num_overlap_intens) + { + cr_server.head_spu->dispatch_table.Color4f(0, 0, 0, + cr_server.overlap_intens[a-1]); + } + else + { + cr_server.head_spu->dispatch_table.Color4f(0, 0, 0, 1); + } + + p = cr_server.overlap_geom[a]; + while (p) + { + /* hopefully this isnt concave... */ + __draw_poly(p); + p = p->next; + } + } + + if (!blend) + cr_server.head_spu->dispatch_table.Disable(GL_BLEND); + if ((blendSrc != GL_ZERO) && (blendDst != GL_SRC_ALPHA)) + cr_server.head_spu->dispatch_table.BlendFunc(blendSrc, blendDst); + } + else + /* Knockout Blending */ + { + cr_server.head_spu->dispatch_table.Color4f(0, 0, 0, 1); + + if (blend) + cr_server.head_spu->dispatch_table.Disable(GL_BLEND); + p = cr_server.overlap_knockout; + while (p) + { + __draw_poly(p); + p = p->next; + } + if (blend) + cr_server.head_spu->dispatch_table.Enable(GL_BLEND); + } + + + /* return things to normal */ + switch (mm) + { + case GL_PROJECTION: + cr_server.head_spu->dispatch_table.PopMatrix(); + cr_server.head_spu->dispatch_table.MatrixMode(GL_PROJECTION); + cr_server.head_spu->dispatch_table.PopMatrix(); + break; + case GL_MODELVIEW: + cr_server.head_spu->dispatch_table.PopMatrix(); + cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW); + cr_server.head_spu->dispatch_table.PopMatrix(); + break; + default: + cr_server.head_spu->dispatch_table.PopMatrix(); + cr_server.head_spu->dispatch_table.MatrixMode(GL_MODELVIEW); + cr_server.head_spu->dispatch_table.PopMatrix(); + cr_server.head_spu->dispatch_table.MatrixMode(mm); + break; + } + + if (lighting) + cr_server.head_spu->dispatch_table.Enable(GL_LIGHTING); + if (fog) + cr_server.head_spu->dispatch_table.Enable(GL_FOG); + if (tex[0]) + cr_server.head_spu->dispatch_table.Enable(GL_TEXTURE_1D); + if (tex[1]) + cr_server.head_spu->dispatch_table.Enable(GL_TEXTURE_2D); + if (tex[2]) + cr_server.head_spu->dispatch_table.Enable(GL_TEXTURE_3D); + if (cull) + cr_server.head_spu->dispatch_table.Enable(GL_CULL_FACE); + + cr_server.head_spu->dispatch_table.Color4f(col.r, col.g, col.b, col.a); + } +#endif + + /* Check if using a file network */ + if (!cr_server.clients[0]->conn->actual_network && window == MAGIC_OFFSET) + window = 0; + + ctx = crStateGetCurrent(); + + CRASSERT(cr_server.curClient && cr_server.curClient->currentMural == mural); + + if (ctx->framebufferobject.drawFB + || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT)) + mural->bFbDraw = GL_FALSE; + + CR_SERVER_DUMP_SWAPBUFFERS_ENTER(); + + if (crServerIsRedirectedToFBO()) + { + crServerMuralFBOSwapBuffers(mural); + crServerPresentFBO(mural); + } + else + { + cr_server.head_spu->dispatch_table.SwapBuffers( mural->spuWindow, flags ); + } + + CR_SERVER_DUMP_SWAPBUFFERS_LEAVE(); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchFlush(void) +{ + CRContext *ctx = crStateGetCurrent(); + cr_server.head_spu->dispatch_table.Flush(); + + if (cr_server.curClient && cr_server.curClient->currentMural) + { + CRMuralInfo *mural = cr_server.curClient->currentMural; + if (mural->bFbDraw) + { + if (crServerIsRedirectedToFBO()) + crServerPresentFBO(mural); + } + + if (ctx->framebufferobject.drawFB + || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT)) + mural->bFbDraw = GL_FALSE; + } +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchFinish(void) +{ + CRContext *ctx = crStateGetCurrent(); + + cr_server.head_spu->dispatch_table.Finish(); + + if (cr_server.curClient && cr_server.curClient->currentMural) + { + CRMuralInfo *mural = cr_server.curClient->currentMural; + if (mural->bFbDraw) + { + if (crServerIsRedirectedToFBO()) + crServerPresentFBO(mural); + } + + if (ctx->framebufferobject.drawFB + || (ctx->buffer.drawBuffer != GL_FRONT && ctx->buffer.drawBuffer != GL_FRONT_LEFT)) + mural->bFbDraw = GL_FALSE; + } +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clip.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clip.c new file mode 100644 index 00000000..03143b0d --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_clip.c @@ -0,0 +1,588 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +/* + * This code contributed by Karl Rasche <rkarl@vr.clemson.edu> + */ + + +#include <math.h> + +#include "cr_server.h" +#include "cr_mem.h" +#include "server.h" + + +static void +__find_intersection(double *s, double *e, double *clp, double *clp_next, + double *intr) +{ + double v1[2], v2[2]; + double A, B, T; + + v1[0] = e[0] - s[0]; + v1[1] = e[1] - s[1]; + v2[0] = clp_next[0] - clp[0]; + v2[1] = clp_next[1] - clp[1]; + + if ((v1[1]) && (v2[0])) + { + A = (clp[1]-s[1])/v1[1] + (v2[1]/v1[1])*(s[0]-clp[0])/v2[0]; + B = 1.-(v2[1]/v1[1])*(v1[0]/v2[0]); + if (B) + T = A/B; + else + { + T = 0; + } + + intr[0] = s[0]+T*v1[0]; + intr[1] = s[1]+T*v1[1]; + } + else + if (v1[1]) + { + /* clp -> clp_next is vertical */ + T = (clp[0]-s[0])/v1[0]; + + intr[0] = s[0]+T*v1[0]; + intr[1] = s[1]+T*v1[1]; + } + else + { + /* s -> e is horizontal */ + T = (s[1]-clp[1])/v2[1]; + + intr[0] = clp[0]+T*v2[0]; + intr[1] = clp[1]+T*v2[1]; + } + +} + +static void + __clip_one_side(double *poly, int npnts, double *clp, double *clp_next, + double *norm, + double **new_poly_in, int *new_npnts_in, + double **new_poly_out, int *new_npnts_out) +{ + int a, sin, ein; + double *s, *e, intr[2]; + + *new_poly_in = (double *)crAlloc(2*npnts*2*sizeof(double)); + *new_npnts_in = 0; + + *new_poly_out = (double *)crAlloc(2*npnts*2*sizeof(double)); + *new_npnts_out = 0; + + s = poly; + + for (a=0; a<npnts; a++) + { + e = poly+2*((a+1)%npnts); + + if (((e[0]-clp[0])*norm[0]) + ((e[1]-clp[1])*norm[1]) >= 0) + ein = 0; + else + ein = 1; + + if (((s[0]-clp[0])*norm[0]) + ((s[1]-clp[1])*norm[1]) >= 0) + sin = 0; + else + sin = 1; + + if (sin && ein) + { + /* case 1: */ + crMemcpy(*new_poly_in+2*(*new_npnts_in), e, 2*sizeof(double)); + (*new_npnts_in)++; + } + else + if (sin && (!ein)) + { + /* case 2: */ + + __find_intersection(s, e, clp, clp_next, intr); + + crMemcpy(*new_poly_in+2*(*new_npnts_in), intr, 2*sizeof(double)); + (*new_npnts_in)++; + + crMemcpy(*new_poly_out+2*(*new_npnts_out), intr, 2*sizeof(double)); + (*new_npnts_out)++; + crMemcpy(*new_poly_out+2*(*new_npnts_out), e, 2*sizeof(double)); + (*new_npnts_out)++; + } + else + if ((!sin) && ein) + { + /* case 4: */ + __find_intersection(s, e, clp, clp_next, intr); + + crMemcpy((*new_poly_in)+2*(*new_npnts_in), intr, 2*sizeof(double)); + (*new_npnts_in)++; + crMemcpy((*new_poly_in)+2*(*new_npnts_in), e, 2*sizeof(double)); + (*new_npnts_in)++; + + crMemcpy(*new_poly_out+2*(*new_npnts_out), intr, 2*sizeof(double)); + (*new_npnts_out)++; + } + else + { + crMemcpy(*new_poly_out+2*(*new_npnts_out), e, 2*sizeof(double)); + (*new_npnts_out)++; + } + + s = e; + } +} + +/* + * Sutherland/Hodgman clipping for interior & exterior regions. + * length_of((*new_vert_out)[a]) == nclip_to_vert + */ +static void +__clip(double *poly, int nvert, double *clip_to_poly, int nclip_to_vert, + double **new_vert_in, int *nnew_vert_in, + double ***new_vert_out, int **nnew_vert_out) +{ + int a, side, *nout; + double *clip_normals, *s, *e, *n, *new_vert_src; + double *norm, *clp, *clp_next; + double **out; + + *new_vert_out = (double **)crAlloc(nclip_to_vert*sizeof(double *)); + *nnew_vert_out = (int *)crAlloc(nclip_to_vert*sizeof(int)); + + /* + * First, compute normals for the clip poly. This + * breaks for multiple (3+) adjacent colinear vertices + */ + clip_normals = (double *)crAlloc(nclip_to_vert*2*sizeof(double)); + for (a=0; a<nclip_to_vert; a++) + { + s = clip_to_poly+2*a; + e = clip_to_poly+2*((a+1)%nclip_to_vert); + n = clip_to_poly+2*((a+2)%nclip_to_vert); + + norm = clip_normals+2*a; + norm[0] = e[1]-s[1]; + norm[1] = -1*(e[0]-s[0]); + + /* + * if dot(norm, n-e) > 0), the normals are backwards, + * assuming the clip region is convex + */ + if (norm[0]*(n[0]-e[0]) + norm[1]*(n[1]-e[1]) > 0) + { + norm[0] *= -1; + norm[1] *= -1; + } + } + + new_vert_src = (double *)crAlloc(nvert*nclip_to_vert*2*sizeof(double)); + crMemcpy(new_vert_src, poly, 2*nvert*sizeof(double)); + + for (side=0; side<nclip_to_vert; side++) + { + clp = clip_to_poly+2*side; + clp_next = clip_to_poly+2*((side+1)%nclip_to_vert); + norm = clip_normals+2*side; + *nnew_vert_in = 0; + + nout = (*nnew_vert_out)+side; + out = (*new_vert_out)+side; + + __clip_one_side(new_vert_src, nvert, clp, clp_next, norm, + new_vert_in, nnew_vert_in, + out, nout); + + crMemcpy(new_vert_src, (*new_vert_in), 2*(*nnew_vert_in)*sizeof(double)); + if (side != nclip_to_vert-1) + crFree(*new_vert_in); + nvert = *nnew_vert_in; + } +} + +/* + * Given a bitmap and a group of 'base' polygons [the quads we are testing], + * perform the unions and differences specified by the map and return + * the resulting geometry + */ +static void +__execute_combination(CRPoly **base, int n, int *mask, CRPoly **head) +{ + int a, b, got_intr; + int nin, *nout, last; + double *in, **out; + CRPoly *intr, *diff, *p; + + *head = NULL; + + intr = (CRPoly *)crAlloc(sizeof(CRPoly)); + intr->next = NULL; + + got_intr = 0; + + /* first, intersect the first 2 polys marked */ + for (a=0; a<n; a++) + if (mask[a]) break; + for (b=a+1; b<n; b++) + if (mask[b]) break; + + __clip(base[a]->points, base[a]->npoints, + base[b]->points, base[b]->npoints, + &in, &nin, &out, &nout); + last = b; + + crFree (nout); + for (a=0; a<base[last]->npoints; a++) + if (out[a]) + crFree(out[a]); + crFree(out); + + + if (nin) + { + intr->npoints = nin; + intr->points = in; + got_intr = 1; + } + + while (1) + { + for (a=last+1; a<n; a++) + if (mask[a]) break; + + if (a == n) break; + + if (got_intr) + { + __clip(base[a]->points, base[a]->npoints, + intr->points, intr->npoints, + &in, &nin, &out, &nout); + + crFree (nout); + for (b=0; b<intr->npoints; b++) + if (out[b]) + crFree(out[b]); + crFree(out); + + if (nin) + { + intr->npoints = nin; + intr->points = in; + } + else + { + got_intr = 0; + break; + } + } + else + { + __clip(base[a]->points, base[a]->npoints, + base[last]->points, base[last]->npoints, + &in, &nin, &out, &nout); + + crFree (nout); + for (b=0; b<base[last]->npoints; b++) + { + if (out[b]) + crFree(out[b]); + } + crFree(out); + + + if (nin) + { + intr->npoints = nin; + intr->points = in; + got_intr = 1; + } + } + + last = a; + if (a == n) break; + } + + /* can't subtract something from nothing! */ + if (got_intr) + *head = intr; + else + return; + + /* find the first item to subtract */ + for (a=0; a<n; a++) + if (!mask[a]) break; + + if (a == n) return; + last = a; + + /* and subtract it */ + diff = NULL; + __clip(intr->points, intr->npoints, + base[last]->points, base[last]->npoints, + &in, &nin, &out, &nout); + + crFree(in); + + for (a=0; a<base[last]->npoints; a++) + { + if (!nout[a]) continue; + + p = (CRPoly *)crAlloc(sizeof(CRPoly)); + p->npoints = nout[a]; + p->points = out[a]; + p->next = diff; + diff = p; + } + *head = diff; + + while (1) + { + intr = diff; + diff = NULL; + + for (a=last+1; a<n; a++) + if (!mask[a]) break; + if (a == n) return; + + last = a; + + /* subtract mask[a] from everything in intr and + * plop it into diff */ + while (intr) + { + __clip(intr->points, intr->npoints, + base[last]->points, base[last]->npoints, + &in, &nin, &out, &nout); + + crFree(in); + + for (a=0; a<base[last]->npoints; a++) + { + if (!nout[a]) continue; + + p = (CRPoly *)crAlloc(sizeof(CRPoly)); + p->npoints = nout[a]; + p->points = out[a]; + p->next = diff; + diff = p; + } + + intr = intr->next; + } + + *head = diff; + } + +} + +/* + * Here we generate all valid bitmaps to represent union/difference + * combinations. Each bitmap is N elements long, where N is the + * number of polys [quads] that we are testing for overlap + */ +static void +__generate_masks(int n, int ***mask, int *nmasks) +{ + int a, b, c, d, e; + int i, idx, isec_size, add; + + *mask = (int **)crAlloc((unsigned int)pow(2, n)*sizeof(int)); + for (a=0; a<pow(2, n); a++) + (*mask)[a] = (int *)crAlloc(n*sizeof(int)); + + /* compute combinations */ + idx = 0; + for (isec_size=1; isec_size<n; isec_size++) + { + for (a=0; a<n; a++) + { + for (b=a+1; b<n; b++) + { + crMemset((*mask)[idx], 0, n*sizeof(int)); + (*mask)[idx][a] = 1; + + add = 1; + for (c=0; c<isec_size; c++) + { + i = (b+c) % n; + if (i == a) add = 0; + + (*mask)[idx][i] = 1; + } + + /* dup check */ + if ((add) && (idx)) + { + for (d=0; d<idx; d++) + { + add = 0; + for (e=0; e<n; e++) + { + if ((*mask)[idx][e] != (*mask)[d][e]) + add = 1; + } + + if (!add) + break; + } + } + + if (add) + idx++; + } + } + } + + *nmasks = idx; +} + +/* + * To compute the overlap between a series of quads (This should work + * for n-gons, but we'll only need quads..), first generate a series of + * bitmaps that represent which elements to union together, and which + * to difference. This goes into 'mask'. We then evaluate each bitmap with + * Sutherland-Hodgman clipping to find the interior (union) and exterior + * (difference) regions. + * + * In the map, 1 == union, 0 == difference + * + * (*res)[a] is the head of a poly list for all the polys that convert + * regions of overlap between a+1 polys ((*res)[0] == NULL) + */ +void +crComputeOverlapGeom(double *quads, int nquad, CRPoly ***res) +{ + int a, b, idx, isec_size, **mask; + CRPoly *p, *next, **base; + + base = (CRPoly **)crAlloc(nquad*sizeof(CRPoly *)); + for (a=0; a<nquad; a++) + { + p = (CRPoly *)crAlloc(sizeof(CRPoly)); + p->npoints = 4; + p->points = (double *)crAlloc(8*sizeof(double)); + for (b=0; b<8; b++) + { + p->points[b] = quads[8*a+b]; + } + p->next = NULL; + base[a] = p; + } + + *res = (CRPoly **)crAlloc(nquad*sizeof(CRPoly *)); + for (a=0; a<nquad; a++) + (*res)[a] = NULL; + + __generate_masks(nquad, &mask, &idx); + + for (a=0; a<idx; a++) + { + isec_size = 0; + for (b=0; b<nquad; b++) + if (mask[a][b]) isec_size++; + isec_size--; + + __execute_combination(base, nquad, mask[a], &p); + + while (p) + { + next = p->next; + + p->next = (*res)[isec_size]; + (*res)[isec_size] = p; + + p = next; + } + } + + for (a=0; a<nquad; a++) + { + crFree(base[a]->points); + crFree(base[a]); + } + crFree(base); + +} + +/* + * This is similar to ComputeOverlapGeom above, but for "knockout" + * edge blending. + * + * my_quad_idx is an index of quads indicating which display tile + * we are computing geometry for. From this, we either generate + * geometry, or not, such that all geometry can be drawn in black + * and only one tile will show through the blend as non-black. + * + * To add a combination to our set of geom, we must test that: + * + mask[a][my_quad_idx] is set + * + mask[a][my_quad_idx] is not the first element set in + * mask[a]. + * If these conditions hold, execute mask[a] and draw the resulting + * geometry in black + * + * Unlike ComputeOverlapGeom, res is just a list of polys to draw in black + */ +void +crComputeKnockoutGeom(double *quads, int nquad, int my_quad_idx, CRPoly **res) +{ + int a, b, idx, first, **mask; + CRPoly *p, *next, **base; + + base = (CRPoly **) crAlloc(nquad*sizeof(CRPoly *)); + for (a=0; a<nquad; a++) + { + p = (CRPoly *) crAlloc(sizeof(CRPoly)); + p->npoints = 4; + p->points = (double *) crAlloc(8*sizeof(double)); + for (b=0; b<8; b++) + { + p->points[b] = quads[8*a+b]; + } + p->next = NULL; + base[a] = p; + } + + (*res) = NULL; + + __generate_masks(nquad, &mask, &idx); + + for (a=0; a<idx; a++) + { + /* test for above conditions */ + if (!mask[a][my_quad_idx]) continue; + + first = -1; + for (b=0; b<nquad; b++) + if (mask[a][b]) + { + first = b; + break; + } + if (first == my_quad_idx) continue; + + + __execute_combination(base, nquad, mask[a], &p); + + while (p) + { + next = p->next; + + p->next = *res; + *res = p; + + p = next; + } + } + + for (a=0; a<nquad; a++) + { + crFree(base[a]->points); + crFree(base[a]); + } + crFree(base); +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c new file mode 100644 index 00000000..ce1546a0 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_config.c @@ -0,0 +1,404 @@ + /* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include <string.h> +#include "cr_mem.h" +#include "cr_environment.h" +#include "cr_string.h" +#include "cr_error.h" +#include "cr_glstate.h" +#include "server.h" + +#ifdef WINDOWS +#pragma warning( disable: 4706 ) +#endif + +static void +setDefaults(void) +{ + if (!cr_server.tcpip_port) + cr_server.tcpip_port = DEFAULT_SERVER_PORT; + cr_server.run_queue = NULL; + cr_server.optimizeBucket = 1; + cr_server.useL2 = 0; + cr_server.maxBarrierCount = 0; + cr_server.ignore_papi = 0; + cr_server.only_swap_once = 0; + cr_server.overlapBlending = 0; + cr_server.debug_barriers = 0; + cr_server.sharedDisplayLists = 0; + cr_server.sharedTextureObjects = 0; + cr_server.sharedPrograms = 0; + cr_server.sharedWindows = 0; + cr_server.useDMX = 0; + cr_server.vpProjectionMatrixParameter = -1; + cr_server.vpProjectionMatrixVariable = NULL; + cr_server.currentProgram = 0; + + cr_server.num_overlap_intens = 0; + cr_server.overlap_intens = 0; + crMemset(&cr_server.MainContextInfo, 0, sizeof (cr_server.MainContextInfo)); + + crMatrixInit(&cr_server.viewMatrix[0]); + crMatrixInit(&cr_server.viewMatrix[1]); + crMatrixInit(&cr_server.projectionMatrix[0]); + crMatrixInit(&cr_server.projectionMatrix[1]); + cr_server.currentEye = -1; + + cr_server.uniqueWindows = 0; + + cr_server.screenCount = 0; + cr_server.bUsePBOForReadback = GL_FALSE; + cr_server.bWindowsInitiallyHidden = GL_FALSE; + + cr_server.pfnNotifyEventCB = NULL; +} + +/* Check if host reports minimal OpenGL capabilities. + * + * Require OpenGL 2.1 or later. + * + * For example, on Windows host this may happen if host has no graphics + * card drivers installed or drivers were not properly signed or VBox + * is running via remote desktop session etc. Currently, we take care + * about Windows host only when specific RENDERER and VERSION strings + * returned in this case. Later this check should be expanded to the + * rest of hosts. */ +static bool crServerHasInsufficientCaps() +{ + const char *pszRealVersion; + int rc; + uint32_t u32VerMajor = 0; + uint32_t u32VerMinor = 0; + char *pszNext = NULL; + + if (!cr_server.head_spu) + return true; + + pszRealVersion = (const char *)cr_server.head_spu->dispatch_table.GetString(GL_REAL_VERSION); + if (!pszRealVersion) + return true; /* No version == insufficient. */ + + rc = RTStrToUInt32Ex(pszRealVersion, &pszNext, 10, &u32VerMajor); + if ( RT_SUCCESS(rc) + && *pszNext == '.') + RTStrToUInt32Ex(pszNext + 1, NULL, 10, &u32VerMinor); + + crInfo("Host supports version %d.%d [%s]", u32VerMajor, u32VerMinor, pszRealVersion); + + if ( u32VerMajor > 2 + || (u32VerMajor == 2 && u32VerMinor >= 1)) + return false; /* >= 2.1, i.e. good enough. */ + + return true; /* Insufficient. */ +} + +void crServerSetVBoxConfiguration() +{ + CRMuralInfo *defaultMural; + char response[8096]; + + char **spuchain; + int num_spus; + int *spu_ids; + char **spu_names; + char *spu_dir = NULL; + int i; + /* Quadrics defaults */ + int my_rank = 0; + int low_context = CR_QUADRICS_DEFAULT_LOW_CONTEXT; + int high_context = CR_QUADRICS_DEFAULT_HIGH_CONTEXT; + unsigned char key[16]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + char hostname[1024]; + char **clientchain, **clientlist; + GLint dims[4]; + const char * env; + + defaultMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, 0); + CRASSERT(defaultMural); + + setDefaults(); + + /* + * Get my hostname + */ + if (crGetHostname(hostname, sizeof(hostname))) + { + crError("CRServer: Couldn't get my own hostname?"); + } + +#ifdef VBOX_WITH_CR_DISPLAY_LISTS + strcpy(response, "1 0 expando"); +#else + strcpy(response, "1 0 render"); +#endif + crDebug("CRServer: my SPU chain: %s", response); + + /* response will describe the SPU chain. + * Example "2 5 wet 6 render" + */ + spuchain = crStrSplit(response, " "); + num_spus = crStrToInt(spuchain[0]); + spu_ids = (int *) crAlloc(num_spus * sizeof(*spu_ids)); + spu_names = (char **) crAlloc((num_spus + 1) * sizeof(*spu_names)); + for (i = 0; i < num_spus; i++) + { + spu_ids[i] = crStrToInt(spuchain[2 * i + 1]); + spu_names[i] = crStrdup(spuchain[2 * i + 2]); + crDebug("SPU %d/%d: (%d) \"%s\"", i + 1, num_spus, spu_ids[i], + spu_names[i]); + } + spu_names[i] = NULL; + + crNetSetRank(0); + crNetSetContextRange(32, 35); + crNetSetNodeRange("iam0", "iamvis20"); + crNetSetKey(key,sizeof(key)); + crNetSetKey(key,sizeof(key)); + cr_server.tcpip_port = 7000; + + crDebug("CRServer: my port number is %d", cr_server.tcpip_port); + + /* + * Load the SPUs + */ + cr_server.head_spu = + crSPULoadChain(num_spus, spu_ids, spu_names, spu_dir, &cr_server); + + env = crGetenv( "CR_SERVER_DEFAULT_VISUAL_BITS" ); + if (env != NULL && env[0] != '\0') + { + unsigned int bits = (unsigned int)crStrParseI32(env, 0); + if (bits <= CR_ALL_BITS) + cr_server.fVisualBitsDefault = bits; + else + crWarning("invalid bits option %c", bits); + } + else + cr_server.fVisualBitsDefault = CR_RGB_BIT | CR_ALPHA_BIT | CR_DOUBLE_BIT; + + env = crGetenv("CR_SERVER_CAPS"); + if (env && env[0] != '\0') + { + cr_server.u32Caps = crStrParseI32(env, 0); + cr_server.u32Caps &= CR_VBOX_CAPS_ALL; + } + else + { + cr_server.u32Caps = CR_VBOX_CAP_TEX_PRESENT + | CR_VBOX_CAP_CMDVBVA + | CR_VBOX_CAP_CMDBLOCKS + | CR_VBOX_CAP_GETATTRIBSLOCATIONS + | CR_VBOX_CAP_CMDBLOCKS_FLUSH + ; + } + + if (crServerHasInsufficientCaps()) + { + crDebug("Cfg: report minimal OpenGL capabilities"); + cr_server.u32Caps |= CR_VBOX_CAP_HOST_CAPS_NOT_SUFFICIENT; + } + + crInfo("Cfg: u32Caps(%#x), fVisualBitsDefault(%#x)", + cr_server.u32Caps, + cr_server.fVisualBitsDefault); + + /* Need to do this as early as possible */ + + cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_POSITION_CR, 0, GL_INT, 2, &dims[0]); + cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, 0, GL_INT, 2, &dims[2]); + + defaultMural->gX = dims[0]; + defaultMural->gY = dims[1]; + defaultMural->width = dims[2]; + defaultMural->height = dims[3]; + + crFree(spu_ids); + crFreeStrings(spu_names); + crFreeStrings(spuchain); + if (spu_dir) + crFree(spu_dir); + + cr_server.mtu = 1024 * 30; + + /* + * Get a list of all the clients talking to me. + */ + if (cr_server.vncMode) { + /* we're inside a vnc viewer */ + /*if (!crMothershipSendString( conn, response, "getvncclient %s", hostname )) + crError( "Bad Mothership response: %s", response );*/ + } + else { + //crMothershipGetClients(conn, response); + strcpy(response, "1 tcpip 1"); + } + + crDebug("CRServer: my clients: %s", response); + + /* + * 'response' will now contain a number indicating the number of clients + * of this server, followed by a comma-separated list of protocol/SPU ID + * pairs. + * Example: "3 tcpip 1,gm 2,via 10" + */ + clientchain = crStrSplitn(response, " ", 1); + cr_server.numClients = crStrToInt(clientchain[0]); + if (cr_server.numClients == 0) + { + crError("I have no clients! What's a poor server to do?"); + } + clientlist = crStrSplit(clientchain[1], ","); + + /* + * Connect to initial set of clients. + * Call crNetAcceptClient() for each client. + * Also, look for a client that's _not_ using the file: protocol. + */ + for (i = 0; i < cr_server.numClients; i++) + { + CRClient *newClient = (CRClient *) crCalloc(sizeof(CRClient)); +#ifdef VBOX + sscanf(clientlist[i], "%1023s %d", cr_server.protocol, &(newClient->spu_id)); +#else + sscanf(clientlist[i], "%s %d", cr_server.protocol, &(newClient->spu_id)); +#endif + newClient->conn = crNetAcceptClient(cr_server.protocol, NULL, + cr_server.tcpip_port, + cr_server.mtu, 0); + newClient->currentCtxInfo = &cr_server.MainContextInfo; + crServerAddToRunQueue(newClient); + + cr_server.clients[i] = newClient; + } + + /* set default client and mural */ + if (cr_server.numClients > 0) { + cr_server.curClient = cr_server.clients[0]; + cr_server.curClient->currentMural = defaultMural; + cr_server.client_spu_id =cr_server.clients[0]->spu_id; + } + + crFreeStrings(clientchain); + crFreeStrings(clientlist); + + /* Ask the mothership for the tile info */ + //crServerGetTileInfoFromMothership(conn, defaultMural); + + if (cr_server.vncMode) { + /* In vnc mode, we reset the mothership configuration so that it can be + * used by subsequent OpenGL apps without having to spawn a new mothership + * on a new port. + */ + crDebug("CRServer: Resetting mothership to initial state"); + //crMothershipReset(conn); + } + + //crMothershipDisconnect(conn); +} + +void crServerSetVBoxConfigurationHGCM() +{ + CRMuralInfo *defaultMural; + +#ifdef VBOX_WITH_CR_DISPLAY_LISTS + int spu_ids[1] = {0}; + char *spu_names[1] = {"expando"}; +#else + int spu_ids[1] = {0}; + char *spu_names[1] = {"render"}; +#endif + char *spu_dir = NULL; + int i; + GLint dims[4]; + const char * env; + + defaultMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, 0); + CRASSERT(defaultMural); + + /// @todo should be moved to addclient so we have a chain for each client + + setDefaults(); + + /* Load the SPUs */ + cr_server.head_spu = crSPULoadChain(1, spu_ids, spu_names, spu_dir, &cr_server); + + if (!cr_server.head_spu) + return; + + + env = crGetenv( "CR_SERVER_DEFAULT_VISUAL_BITS" ); + if (env != NULL && env[0] != '\0') + { + unsigned int bits = (unsigned int)crStrParseI32(env, 0); + if (bits <= CR_ALL_BITS) + cr_server.fVisualBitsDefault = bits; + else + crWarning("invalid bits option %c", bits); + } + else + cr_server.fVisualBitsDefault = CR_RGB_BIT | CR_ALPHA_BIT | CR_DOUBLE_BIT; + + + env = crGetenv("CR_SERVER_CAPS"); + if (env && env[0] != '\0') + { + cr_server.u32Caps = crStrParseI32(env, 0); + cr_server.u32Caps &= CR_VBOX_CAPS_ALL; + } + else + { + cr_server.u32Caps = CR_VBOX_CAP_TEX_PRESENT + | CR_VBOX_CAP_CMDVBVA + | CR_VBOX_CAP_CMDBLOCKS + | CR_VBOX_CAP_GETATTRIBSLOCATIONS + | CR_VBOX_CAP_CMDBLOCKS_FLUSH + ; + } + + if (crServerHasInsufficientCaps()) + { + crDebug("Cfg: report minimal OpenGL capabilities"); + cr_server.u32Caps |= CR_VBOX_CAP_HOST_CAPS_NOT_SUFFICIENT; + } + + crInfo("Cfg: u32Caps(%#x), fVisualBitsDefault(%#x)", + cr_server.u32Caps, + cr_server.fVisualBitsDefault); + + cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_POSITION_CR, 0, GL_INT, 2, &dims[0]); + cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, 0, GL_INT, 2, &dims[2]); + + defaultMural->gX = dims[0]; + defaultMural->gY = dims[1]; + defaultMural->width = dims[2]; + defaultMural->height = dims[3]; + + cr_server.mtu = 1024 * 250; + + cr_server.numClients = 0; + strcpy(cr_server.protocol, "vboxhgcm"); + + for (i = 0; i < cr_server.numClients; i++) + { + CRClient *newClient = (CRClient *) crCalloc(sizeof(CRClient)); + newClient->spu_id = 0; + newClient->conn = crNetAcceptClient(cr_server.protocol, NULL, + cr_server.tcpip_port, + cr_server.mtu, 0); + newClient->currentCtxInfo = &cr_server.MainContextInfo; + crServerAddToRunQueue(newClient); + + cr_server.clients[i] = newClient; + } + + /* set default client and mural */ + if (cr_server.numClients > 0) { + cr_server.curClient = cr_server.clients[0]; + cr_server.curClient->currentMural = defaultMural; + cr_server.client_spu_id =cr_server.clients[0]->spu_id; + } +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c new file mode 100644 index 00000000..afa2a208 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_context.c @@ -0,0 +1,481 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved. + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "cr_net.h" +#include "cr_rand.h" +#include "server_dispatch.h" +#include "server.h" +#include "cr_mem.h" +#include "cr_string.h" + +GLint SERVER_DISPATCH_APIENTRY +crServerDispatchCreateContext(const char *dpyName, GLint visualBits, GLint shareCtx) +{ + return crServerDispatchCreateContextEx(dpyName, visualBits, shareCtx, -1, -1); +} + +GLint crServerDispatchCreateContextEx(const char *dpyName, GLint visualBits, GLint shareCtx, GLint preloadCtxID, int32_t internalID) +{ + GLint retVal = -1; + CRContext *newCtx; + CRContextInfo *pContextInfo; + GLboolean fFirst = GL_FALSE; + + dpyName = ""; + + if (shareCtx > 0) { + crWarning("CRServer: context sharing not implemented."); + shareCtx = 0; + } + + pContextInfo = (CRContextInfo *) crAlloc(sizeof (CRContextInfo)); + if (!pContextInfo) + { + crWarning("failed to alloc context info!"); + return -1; + } + + pContextInfo->currentMural = NULL; + + pContextInfo->CreateInfo.requestedVisualBits = visualBits; + + if (cr_server.fVisualBitsDefault) + visualBits = cr_server.fVisualBitsDefault; + + pContextInfo->CreateInfo.realVisualBits = visualBits; + + /* Since the Cr server serialized all incoming clients/contexts into + * one outgoing GL stream, we only need to create one context for the + * head SPU. We'll only have to make it current once too, below. + */ + if (cr_server.firstCallCreateContext) { + cr_server.MainContextInfo.CreateInfo.realVisualBits = visualBits; + cr_server.MainContextInfo.SpuContext = cr_server.head_spu->dispatch_table. + CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.realVisualBits, shareCtx); + if (cr_server.MainContextInfo.SpuContext < 0) { + crWarning("crServerDispatchCreateContext() failed."); + crFree(pContextInfo); + return -1; + } + cr_server.MainContextInfo.pContext = crStateCreateContext(&cr_server.limits, visualBits, NULL); + CRASSERT(cr_server.MainContextInfo.pContext); + cr_server.firstCallCreateContext = GL_FALSE; + fFirst = GL_TRUE; + + cr_server.head_spu->dispatch_table.ChromiumParameteriCR(GL_HH_SET_DEFAULT_SHARED_CTX, cr_server.MainContextInfo.SpuContext); + } + else { + /* second or third or ... context */ + if (!cr_server.bUseMultipleContexts && ((visualBits & cr_server.MainContextInfo.CreateInfo.realVisualBits) != visualBits)) { + int oldSpuContext; + /* should never be here */ + CRASSERT(0); + /* the new context needs new visual attributes */ + cr_server.MainContextInfo.CreateInfo.realVisualBits |= visualBits; + crWarning("crServerDispatchCreateContext requires new visual (0x%x).", + cr_server.MainContextInfo.CreateInfo.realVisualBits); + + /* Here, we used to just destroy the old rendering context. + * Unfortunately, this had the side effect of destroying + * all display lists and textures that had been loaded on + * the old context as well. + * + * Now, first try to create a new context, with a suitable + * visual, sharing display lists and textures with the + * old context. Then destroy the old context. + */ + + /* create new rendering context with suitable visual */ + oldSpuContext = cr_server.MainContextInfo.SpuContext; + cr_server.MainContextInfo.SpuContext = cr_server.head_spu->dispatch_table. + CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.realVisualBits, cr_server.MainContextInfo.SpuContext); + /* destroy old rendering context */ + cr_server.head_spu->dispatch_table.DestroyContext(oldSpuContext); + if (cr_server.MainContextInfo.SpuContext < 0) { + crWarning("crServerDispatchCreateContext() failed."); + crFree(pContextInfo); + return -1; + } + + /* we do not need to clean up the old default context explicitly, since the above cr_server.head_spu->dispatch_table.DestroyContext call + * will do that for us */ + cr_server.head_spu->dispatch_table.ChromiumParameteriCR(GL_HH_SET_DEFAULT_SHARED_CTX, cr_server.MainContextInfo.SpuContext); + } + } + + if (cr_server.bUseMultipleContexts) { + pContextInfo->SpuContext = cr_server.head_spu->dispatch_table. + CreateContext(dpyName, cr_server.MainContextInfo.CreateInfo.realVisualBits, cr_server.MainContextInfo.SpuContext); + if (pContextInfo->SpuContext < 0) { + crWarning("crServerDispatchCreateContext() failed."); + crStateEnableDiffOnMakeCurrent(GL_TRUE); + cr_server.bUseMultipleContexts = GL_FALSE; + if (!fFirst) + crError("creating shared context failed, while it is expected to work!"); + } + else if (fFirst) + { + crStateEnableDiffOnMakeCurrent(GL_FALSE); + } + } + else + { + pContextInfo->SpuContext = -1; + } + + /* Now create a new state-tracker context and initialize the + * dispatch function pointers. + */ + newCtx = crStateCreateContextEx(&cr_server.limits, visualBits, NULL, internalID); + if (newCtx) { + crStateSetCurrentPointers( newCtx, &(cr_server.current) ); + crStateResetCurrentPointers(&(cr_server.current)); + retVal = preloadCtxID<0 ? (GLint)crHashtableAllocKeys( cr_server.contextTable, 1 ) : preloadCtxID; + + pContextInfo->pContext = newCtx; + Assert(pContextInfo->CreateInfo.realVisualBits == visualBits); + pContextInfo->CreateInfo.externalID = retVal; + pContextInfo->CreateInfo.pszDpyName = dpyName ? crStrdup(dpyName) : NULL; + crHashtableAdd(cr_server.contextTable, retVal, pContextInfo); + } + + if (retVal != -1 && !cr_server.bIsInLoadingState) { + int pos; + for (pos = 0; pos < CR_MAX_CONTEXTS; pos++) { + if (cr_server.curClient->contextList[pos] == 0) { + cr_server.curClient->contextList[pos] = retVal; + break; + } + } + } + + crServerReturnValue( &retVal, sizeof(retVal) ); + + return retVal; +} + +static int crServerRemoveClientContext(CRClient *pClient, GLint ctx) +{ + int pos; + + for (pos = 0; pos < CR_MAX_CONTEXTS; ++pos) + { + if (pClient->contextList[pos] == ctx) + { + pClient->contextList[pos] = 0; + return true; + } + } + + return false; +} + +static void crServerCleanupMuralCtxUsageCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *mural = (CRMuralInfo *) data1; + CRContext *ctx = (CRContext *) data2; + + CR_STATE_SHAREDOBJ_USAGE_CLEAR(mural, ctx); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchDestroyContext( GLint ctx ) +{ + CRContextInfo *crCtxInfo; + CRContext *crCtx; + int32_t client; + CRClientNode *pNode; + int found=false; + + crCtxInfo = (CRContextInfo *) crHashtableSearch(cr_server.contextTable, ctx); + if (!crCtxInfo) { + crWarning("CRServer: DestroyContext invalid context %d", ctx); + return; + } + crCtx = crCtxInfo->pContext; + CRASSERT(crCtx); + + crDebug("CRServer: DestroyContext context %d", ctx); + + if (cr_server.currentCtxInfo == crCtxInfo) + { + CRMuralInfo *dummyMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + crServerPerformMakeCurrent(dummyMural, &cr_server.MainContextInfo); + CRASSERT(cr_server.currentCtxInfo == &cr_server.MainContextInfo); + } + + crHashtableWalk(cr_server.muralTable, crServerCleanupMuralCtxUsageCB, crCtx); + crCtxInfo->currentMural = NULL; + crHashtableDelete(cr_server.contextTable, ctx, NULL); + crStateDestroyContext( crCtx ); + + if (crCtxInfo->CreateInfo.pszDpyName) + crFree(crCtxInfo->CreateInfo.pszDpyName); + + if (crCtxInfo->SpuContext >= 0) + cr_server.head_spu->dispatch_table.DestroyContext(crCtxInfo->SpuContext); + + crFree(crCtxInfo); + + if (cr_server.curClient) + { + /* If we delete our current context, default back to the null context */ + if (cr_server.curClient->currentCtxInfo == crCtxInfo) { + cr_server.curClient->currentContextNumber = -1; + cr_server.curClient->currentCtxInfo = &cr_server.MainContextInfo; + } + + found = crServerRemoveClientContext(cr_server.curClient, ctx); + + /*Some application call destroy context not in a thread where it was created...have do deal with it.*/ + if (!found) + { + for (client=0; client<cr_server.numClients; ++client) + { + if (cr_server.clients[client]==cr_server.curClient) + continue; + + found = crServerRemoveClientContext(cr_server.clients[client], ctx); + + if (found) break; + } + } + + if (!found) + { + pNode=cr_server.pCleanupClient; + + while (pNode && !found) + { + found = crServerRemoveClientContext(pNode->pClient, ctx); + pNode = pNode->next; + } + } + + CRASSERT(found); + } + + /*Make sure this context isn't active in other clients*/ + for (client=0; client<cr_server.numClients; ++client) + { + if (cr_server.clients[client]->currentCtxInfo == crCtxInfo) + { + cr_server.clients[client]->currentContextNumber = -1; + cr_server.clients[client]->currentCtxInfo = &cr_server.MainContextInfo; + } + } + + pNode=cr_server.pCleanupClient; + while (pNode) + { + if (pNode->pClient->currentCtxInfo == crCtxInfo) + { + pNode->pClient->currentContextNumber = -1; + pNode->pClient->currentCtxInfo = &cr_server.MainContextInfo; + } + pNode = pNode->next; + } + + CRASSERT(cr_server.currentCtxInfo != crCtxInfo); +} + +void crServerPerformMakeCurrent( CRMuralInfo *mural, CRContextInfo *ctxInfo ) +{ + CRMuralInfo *oldMural; + CRContext *ctx, *oldCtx = NULL; + GLuint idDrawFBO, idReadFBO; + GLint context = ctxInfo->CreateInfo.externalID; + GLint window = mural->CreateInfo.externalID; + + cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE; + + ctx = ctxInfo->pContext; + CRASSERT(ctx); + + oldMural = cr_server.currentMural; + + /* Ubuntu 11.04 hosts misbehave if context window switch is + * done with non-default framebuffer object settings. + * crStateSwitchPrepare & crStateSwitchPostprocess are supposed to work around this problem + * crStateSwitchPrepare restores the FBO state to its default values before the context window switch, + * while crStateSwitchPostprocess restores it back to the original values */ + oldCtx = crStateGetCurrent(); + if (oldMural && oldMural->fRedirected && crServerSupportRedirMuralFBO()) + { + idDrawFBO = CR_SERVER_FBO_FOR_IDX(oldMural, oldMural->iCurDrawBuffer); + idReadFBO = CR_SERVER_FBO_FOR_IDX(oldMural, oldMural->iCurReadBuffer); + } + else + { + idDrawFBO = 0; + idReadFBO = 0; + } + crStateSwitchPrepare(cr_server.bUseMultipleContexts ? NULL : ctx, oldCtx, idDrawFBO, idReadFBO); + + if (cr_server.curClient) + { + /* + crDebug("**** %s client %d curCtx=%d curWin=%d", __func__, + cr_server.curClient->number, ctxPos, window); + */ + cr_server.curClient->currentContextNumber = context; + cr_server.curClient->currentCtxInfo = ctxInfo; + cr_server.curClient->currentMural = mural; + cr_server.curClient->currentWindow = window; + + CRASSERT(cr_server.curClient->currentCtxInfo); + CRASSERT(cr_server.curClient->currentCtxInfo->pContext); + } + + /* This is a hack to force updating the 'current' attribs */ + crStateUpdateColorBits(); + + if (ctx) + crStateSetCurrentPointers( ctx, &(cr_server.current) ); + + /* check if being made current for first time, update viewport */ +#if 0 + if (ctx) { + /* initialize the viewport */ + if (ctx->viewport.viewportW == 0) { + ctx->viewport.viewportW = mural->width; + ctx->viewport.viewportH = mural->height; + ctx->viewport.scissorW = mural->width; + ctx->viewport.scissorH = mural->height; + } + } +#endif + + /* + crDebug("**** %s currentWindow %d newWindow %d", __func__, + cr_server.currentWindow, window); + */ + + if (1/*cr_server.firstCallMakeCurrent || + cr_server.currentWindow != window || + cr_server.currentNativeWindow != nativeWindow*/) { + /* Since the cr server serialized all incoming contexts/clients into + * one output stream of GL commands, we only need to call the head + * SPU's MakeCurrent() function once. + * BUT, if we're rendering to multiple windows, we do have to issue + * MakeCurrent() calls sometimes. The same GL context will always be + * used though. + */ + cr_server.head_spu->dispatch_table.MakeCurrent( mural->spuWindow, + 0, + ctxInfo->SpuContext >= 0 + ? ctxInfo->SpuContext + : cr_server.MainContextInfo.SpuContext); + + CR_STATE_SHAREDOBJ_USAGE_SET(mural, ctx); + if (cr_server.currentCtxInfo) + cr_server.currentCtxInfo->currentMural = NULL; + ctxInfo->currentMural = mural; + + cr_server.firstCallMakeCurrent = GL_FALSE; + cr_server.currentCtxInfo = ctxInfo; + cr_server.currentWindow = window; + cr_server.currentNativeWindow = 0; + cr_server.currentMural = mural; + } + + /* This used to be earlier, after crStateUpdateColorBits() call */ + crStateMakeCurrent( ctx ); + + if (mural && mural->fRedirected && crServerSupportRedirMuralFBO()) + { + GLuint id = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.drawBuffer); + if (id != mural->iCurDrawBuffer) + { + crDebug("DBO draw buffer changed on make current"); + mural->iCurDrawBuffer = id; + } + + id = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.readBuffer); + if (id != mural->iCurReadBuffer) + { + crDebug("DBO read buffer changed on make current"); + mural->iCurReadBuffer = id; + } + + idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer); + idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer); + } + else + { + idDrawFBO = 0; + idReadFBO = 0; + } + crStateSwitchPostprocess(ctx, cr_server.bUseMultipleContexts ? NULL : oldCtx, idDrawFBO, idReadFBO); + + if (!ctx->framebufferobject.drawFB + && (ctx->buffer.drawBuffer == GL_FRONT || ctx->buffer.drawBuffer == GL_FRONT_LEFT) + && cr_server.curClient) + cr_server.curClient->currentMural->bFbDraw = GL_TRUE; + + if (!mural->fRedirected) + { + ctx->buffer.width = mural->width; + ctx->buffer.height = mural->height; + } + else + { + ctx->buffer.width = 0; + ctx->buffer.height = 0; + } +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchMakeCurrent( GLint window, GLint nativeWindow, GLint context ) +{ + CRMuralInfo *mural; + CRContextInfo *ctxInfo = NULL; + + if (context >= 0 && window >= 0) { + mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); + if (!mural) + { + crWarning("CRServer: invalid window %d passed to crServerDispatchMakeCurrent()", window); + return; + } + + /* Update the state tracker's current context */ + ctxInfo = (CRContextInfo *) crHashtableSearch(cr_server.contextTable, context); + if (!ctxInfo) { + crWarning("CRserver: NULL context in MakeCurrent %d", context); + return; + } + } + else { +#if 0 + oldMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, cr_server.currentWindow); + if (oldMural && oldMural->bUseFBO && crServerSupportRedirMuralFBO()) + { + if (!crStateGetCurrent()->framebufferobject.drawFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); + } + if (!crStateGetCurrent()->framebufferobject.readFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, 0); + } + } + + ctxInfo = &cr_server.MainContextInfo; + window = -1; + mural = NULL; +#endif + cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE; + return; + } + + crServerPerformMakeCurrent( mural, ctxInfo ); +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py new file mode 100755 index 00000000..de765f04 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch.py @@ -0,0 +1,137 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. + +from __future__ import print_function +import sys, string, re + +import apiutil + + + +apiutil.CopyrightC() + +print(""" +/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY server_dispatch.py SCRIPT */ +#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "server_dispatch.h" +#include "server.h" +#include "cr_unpack.h" + +CRCurrentStatePointers crServerCurrent; +""") + + +for func_name in apiutil.AllSpecials( sys.argv[1]+"/../state_tracker/state" ): + params = apiutil.Parameters(func_name) + if (apiutil.FindSpecial( "server", func_name ) or + "get" in apiutil.Properties(func_name)): + continue + + wrap = apiutil.GetCategoryWrapper(func_name) + if wrap: + print('#if defined(CR_%s)' % wrap) + print('void SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s)' % ( func_name, apiutil.MakeDeclarationString( params ) )) + print('{') + print('\tcrState%s(%s);' % (func_name, apiutil.MakeCallString( params ) )) + print('\tcr_server.head_spu->dispatch_table.%s(%s);' % (func_name, apiutil.MakeCallString( params ) )) + print('}') + if wrap: + print('#endif') + + +keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt") +for func_name in keys: + current = 0 + array = "" + condition = "" + m = re.search( r"^(Color|Normal)([1234])(ub|b|us|s|ui|i|f|d)$", func_name ) + if m : + current = 1 + name = m.group(1)[:1].lower() + m.group(1)[1:] + type = m.group(3) + m.group(2) + m = re.search( r"^(SecondaryColor)(3)(ub|b|us|s|ui|i|f|d)(EXT)$", func_name ) + if m : + current = 1 + name = m.group(1)[:1].lower() + m.group(1)[1:] + type = m.group(3) + m.group(2) + m = re.search( r"^(TexCoord)([1234])(ub|b|us|s|ui|i|f|d)$", func_name ) + if m : + current = 1 + name = m.group(1)[:1].lower() + m.group(1)[1:] + type = m.group(3) + m.group(2) + array = "[0]" + m = re.search( r"^(MultiTexCoord)([1234])(ub|b|us|s|ui|i|f|d)ARB$", func_name ) + if m : + current = 1 + name = "texCoord" + type = m.group(3) + m.group(2) + array = "[texture-GL_TEXTURE0_ARB]" + condition = "if (texture >= GL_TEXTURE0_ARB && texture < GL_TEXTURE0_ARB + CR_MAX_TEXTURE_UNITS)" + m = re.match( r"^(Index)(ub|b|us|s|ui|i|f|d)$", func_name ) + if m : + current = 1 + name = m.group(1)[:1].lower() + m.group(1)[1:] + type = m.group(2) + "1" + m = re.match( r"^(EdgeFlag)$", func_name ) + if m : + current = 1 + name = m.group(1)[:1].lower() + m.group(1)[1:] + type = "l1" + m = re.match( r"^(FogCoord)(f|d)(EXT)$", func_name) + if m : + current = 1 + name = m.group(1)[:1].lower() + m.group(1)[1:] + type = m.group(2) + "1" + + # Vertex attribute commands w/ some special cases + m = re.search( r"^(VertexAttrib)([1234])(s|i|f|d)ARB$", func_name ) + if m : + current = 1 + name = m.group(1)[:1].lower() + m.group(1)[1:] + type = m.group(3) + m.group(2) + array = "[index]" + condition = "if (index < CR_MAX_VERTEX_ATTRIBS)" + if func_name == "VertexAttrib4NubARB": + current = 1 + name = "vertexAttrib" + type = "ub4" + array = "[index]" + condition = "if (index < CR_MAX_VERTEX_ATTRIBS)" + + if current: + params = apiutil.Parameters(func_name) + print('void SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s)' % ( func_name, apiutil.MakeDeclarationString(params) )) + print('{') + print('\t%s' % (condition)) + print('\t{') + print('\t\tcr_server.head_spu->dispatch_table.%s(%s);' % (func_name, apiutil.MakeCallString(params) )) + print("\t\tcr_server.current.c.%s.%s%s = cr_unpackData;" % (name,type,array)) + print('\t}') + print('}\n') + +print(""" +void crServerInitDispatch(void) +{ + crSPUInitDispatchTable( &(cr_server.dispatch) ); + crSPUCopyDispatchTable( &(cr_server.dispatch), &(cr_server.head_spu->dispatch_table ) ); +""") + +for func_name in keys: + if ("get" in apiutil.Properties(func_name) or + apiutil.FindSpecial( "server", func_name ) or + apiutil.FindSpecial( sys.argv[1]+"/../state_tracker/state", func_name )): + + wrap = apiutil.GetCategoryWrapper(func_name) + if wrap: + print('#if defined(CR_%s)' % wrap) + + print('\tcr_server.dispatch.%s = crServerDispatch%s;' % (func_name, func_name)) + if wrap: + print('#endif') + +print('}') + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py new file mode 100755 index 00000000..7db1a1ef --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_dispatch_header.py @@ -0,0 +1,51 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. + +from __future__ import print_function +import sys + +import apiutil + +apiutil.CopyrightC() + +print(""" +/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY server_dispatch_header.py SCRIPT */ +#ifndef SERVER_DISPATCH_HEADER +#define SERVER_DISPATCH_HEADER + +#ifdef WINDOWS +#define SERVER_DISPATCH_APIENTRY __stdcall +#else +#define SERVER_DISPATCH_APIENTRY +#endif + +#include "chromium.h" +#include "state/cr_statetypes.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +""") + +keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt") + +for func_name in keys: + if ("get" in apiutil.Properties(func_name) or + apiutil.FindSpecial( "server", func_name ) or + apiutil.FindSpecial( sys.argv[1]+"/../state_tracker/state", func_name )): + + params = apiutil.Parameters(func_name) + return_type = apiutil.ReturnType(func_name) + + print('%s SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s);' % (return_type, func_name, apiutil.MakeDeclarationString( params ))) + +print(""" +#if defined(__cplusplus) +} +#endif + +#endif /* SERVER_DISPATCH_HEADER */ +""") diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c new file mode 100644 index 00000000..8f3886a6 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_framebuffer.c @@ -0,0 +1,237 @@ +/* $Id: server_framebuffer.c $ */ +/** @file + * VBox OpenGL: EXT_framebuffer_object + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +#include "cr_spu.h" +#include "chromium.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" +#include "cr_unpack.h" + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGenFramebuffersEXT(GLsizei n, GLuint *framebuffers) +{ + GLuint *local_buffers; + (void) framebuffers; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchGenFramebuffersEXT: parameter 'n' is out of range"); + return; + } + + local_buffers = (GLuint *)crCalloc(n * sizeof(*local_buffers)); + + crStateGenFramebuffersEXT(n, local_buffers); + + crServerReturnValue(local_buffers, n * sizeof(*local_buffers)); + crFree(local_buffers); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers) +{ + GLuint *local_buffers; + (void) renderbuffers; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchGenRenderbuffersEXT: parameter 'n' is out of range"); + return; + } + + local_buffers = (GLuint *)crCalloc(n * sizeof(*local_buffers)); + + crStateGenRenderbuffersEXT(n, local_buffers); + + crServerReturnValue(local_buffers, n * sizeof(*local_buffers)); + crFree(local_buffers); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchFramebufferTexture1DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ + crStateFramebufferTexture1DEXT(target, attachment, textarget, texture, level); + cr_server.head_spu->dispatch_table.FramebufferTexture1DEXT(target, attachment, textarget, crStateGetTextureHWID(texture), level); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchFramebufferTexture2DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +{ + crStateFramebufferTexture2DEXT(target, attachment, textarget, texture, level); + cr_server.head_spu->dispatch_table.FramebufferTexture2DEXT(target, attachment, textarget, crStateGetTextureHWID(texture), level); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchFramebufferTexture3DEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) +{ + crStateFramebufferTexture3DEXT(target, attachment, textarget, texture, level, zoffset); + cr_server.head_spu->dispatch_table.FramebufferTexture3DEXT(target, attachment, textarget, crStateGetTextureHWID(texture), level, zoffset); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchBindFramebufferEXT(GLenum target, GLuint framebuffer) +{ +#ifdef DEBUG_misha + GLint rfb = 0, dfb = 0; +#endif + crStateBindFramebufferEXT(target, framebuffer); + + if (0==framebuffer) + { + CRContext *ctx = crStateGetCurrent(); + if (ctx->buffer.drawBuffer == GL_FRONT || ctx->buffer.drawBuffer == GL_FRONT_LEFT || ctx->buffer.drawBuffer == GL_FRONT_RIGHT) + cr_server.curClient->currentMural->bFbDraw = GL_TRUE; + } + + if (0==framebuffer && crServerIsRedirectedToFBO()) + { + CRMuralInfo *mural = cr_server.curClient->currentMural; + if (target == GL_FRAMEBUFFER) + { + GLuint idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer); + GLuint idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer); + if (idDrawFBO == idReadFBO) + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_FRAMEBUFFER, idDrawFBO); + else + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, idReadFBO); + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, idDrawFBO); + } + } + else if (target == GL_READ_FRAMEBUFFER) + { + GLuint idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer); + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, idReadFBO); + } + else if (target == GL_DRAW_FRAMEBUFFER) + { + GLuint idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer); + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, idDrawFBO); + } + else + { + crWarning("unknown target %d", target); + } +#ifdef DEBUG_misha + cr_server.head_spu->dispatch_table.GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb); + cr_server.head_spu->dispatch_table.GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb); + if (GL_FRAMEBUFFER_EXT == target) + { + Assert(rfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer)); + Assert(dfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer)); + } + else if (GL_READ_FRAMEBUFFER_EXT == target) + { + Assert(rfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer)); + } + else if (GL_DRAW_FRAMEBUFFER_EXT == target) + { + Assert(dfb == CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer)); + } + else + { + Assert(0); + } +#endif + } + else + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(target, crStateGetFramebufferHWID(framebuffer)); +#ifdef DEBUG_misha + cr_server.head_spu->dispatch_table.GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb); + cr_server.head_spu->dispatch_table.GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb); + if (GL_FRAMEBUFFER_EXT == target) + { + Assert(rfb == crStateGetFramebufferHWID(framebuffer)); + Assert(dfb == crStateGetFramebufferHWID(framebuffer)); + } + else if (GL_READ_FRAMEBUFFER_EXT == target) + { + Assert(rfb == crStateGetFramebufferHWID(framebuffer)); + } + else if (GL_DRAW_FRAMEBUFFER_EXT == target) + { + Assert(dfb == crStateGetFramebufferHWID(framebuffer)); + } + else + { + Assert(0); + } +#endif + } +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchBindRenderbufferEXT(GLenum target, GLuint renderbuffer) +{ + crStateBindRenderbufferEXT(target, renderbuffer); + cr_server.head_spu->dispatch_table.BindRenderbufferEXT(target, crStateGetRenderbufferHWID(renderbuffer)); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteFramebuffersEXT(GLsizei n, const GLuint * framebuffers) +{ + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint) || !DATA_POINTER_CHECK(n * sizeof(GLuint))) + { + crError("crStateDeleteFramebuffersEXT: parameter 'n' is out of range"); + return; + } + + crStateDeleteFramebuffersEXT(n, framebuffers); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteRenderbuffersEXT(GLsizei n, const GLuint * renderbuffers) +{ + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint) || !DATA_POINTER_CHECK(n * sizeof(GLuint))) + { + crError("glDeleteRenderbuffersEXT: parameter 'n' is out of range"); + return; + } + + crStateDeleteRenderbuffersEXT(n, renderbuffers); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchFramebufferRenderbufferEXT(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +{ + crStateFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, renderbuffer); + cr_server.head_spu->dispatch_table.FramebufferRenderbufferEXT(target, attachment, renderbuffertarget, crStateGetRenderbufferHWID(renderbuffer)); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment, GLenum pname, GLint * params) +{ + GLint local_params[1]; + (void) params; + crStateGetFramebufferAttachmentParameterivEXT(target, attachment, pname, local_params); + + crServerReturnValue(&(local_params[0]), 1*sizeof(GLint)); +} + +GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsFramebufferEXT( GLuint framebuffer ) +{ + /* since GenFramebuffers/Renderbuffers issued to host ogl only on bind + some other ops, the host drivers may not know about them + * so use state data*/ + GLboolean retval = crStateIsFramebufferEXT(framebuffer); + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} + +GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsRenderbufferEXT( GLuint renderbuffer ) +{ + /* since GenFramebuffers/Renderbuffers issued to host ogl only on bind + some other ops, the host drivers may not know about them + * so use state data*/ + GLboolean retval = crStateIsRenderbufferEXT(renderbuffer); + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_gentextures.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_gentextures.c new file mode 100644 index 00000000..88cf6812 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_gentextures.c @@ -0,0 +1,142 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_spu.h" +#include "chromium.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" + +void SERVER_DISPATCH_APIENTRY crServerDispatchGenTextures( GLsizei n, GLuint *textures ) +{ + GLuint *local_textures; + (void) textures; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchGenTextures: parameter 'n' is out of range"); + return; + } + + local_textures = (GLuint *)crCalloc(n * sizeof(*local_textures)); + + if (!local_textures) + { + crError("crServerDispatchGenTextures: out of memory"); + return; + } + + crStateGenTextures(n, local_textures); + + crServerReturnValue(local_textures, n*sizeof(*local_textures)); + crFree( local_textures ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGenProgramsNV( GLsizei n, GLuint * ids ) +{ + GLuint *local_progs; + (void) ids; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchGenProgramsNV: parameter 'n' is out of range"); + return; + } + + local_progs = (GLuint *)crCalloc(n * sizeof(*local_progs)); + + if (!local_progs) + { + crError("crServerDispatchGenProgramsNV: out of memory"); + return; + } + + cr_server.head_spu->dispatch_table.GenProgramsNV( n, local_progs ); + crServerReturnValue( local_progs, n*sizeof( *local_progs ) ); + crFree( local_progs ); +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchGenFencesNV( GLsizei n, GLuint * ids ) +{ + GLuint *local_fences; + (void) ids; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchGenFencesNV: parameter 'n' is out of range"); + return; + } + + local_fences = (GLuint *)crCalloc(n * sizeof(*local_fences)); + + if (!local_fences) + { + crError("crServerDispatchGenFencesNV: out of memory"); + return; + } + + cr_server.head_spu->dispatch_table.GenFencesNV( n, local_fences ); + crServerReturnValue( local_fences, n*sizeof( *local_fences ) ); + crFree( local_fences ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGenProgramsARB( GLsizei n, GLuint * ids ) +{ + GLuint *local_progs; + GLsizei i; + (void) ids; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchGenProgramsARB: parameter 'n' is out of range"); + return; + } + + local_progs = (GLuint *)crCalloc(n * sizeof(*local_progs)); + + if (!local_progs) + { + crError("crServerDispatchGenProgramsARB: out of money"); + return; + } + + cr_server.head_spu->dispatch_table.GenProgramsARB( n, local_progs ); + + /* see comments in crServerDispatchGenTextures */ + for (i=0; i<n; ++i) + { + GLuint tID = crServerTranslateProgramID(local_progs[i]); + while (crStateIsProgramARB(tID)) + { + cr_server.head_spu->dispatch_table.GenProgramsARB(1, &tID); + local_progs[i] = tID; + tID = crServerTranslateProgramID(tID); + } + } + + crServerReturnValue( local_progs, n * sizeof( *local_progs ) ); + crFree( local_progs ); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchCopyTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) +{ + GLsizei tw, th; + + cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &tw); + cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &th); + + /* Workaround for a wine or ati bug. Host drivers crash unless we first provide texture bounds. */ + if (((tw!=width) || (th!=height)) && (internalFormat==GL_DEPTH_COMPONENT24)) + { + crServerDispatchTexImage2D(target, level, internalFormat, width, height, border, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); + } + + crStateCopyTexImage2D(target, level, internalFormat, x, y, width, height, border); + cr_server.head_spu->dispatch_table.CopyTexImage2D(target, level, internalFormat, x, y, width, height, border); +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py new file mode 100755 index 00000000..8463a1f7 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_get.py @@ -0,0 +1,145 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. + +from __future__ import print_function +import sys + +import apiutil + + +apiutil.CopyrightC() + +print(""" +#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" +""") + +max_components = { + 'GetClipPlane': 4, + 'GetCombinerStageParameterfvNV': 4, + 'GetCombinerStageParameterivNV': 4, + 'GetCombinerOutputParameterfvNV': 4, + 'GetCombinerOutputParameterivNV': 4, + 'GetCombinerInputParameterfvNV': 4, + 'GetCombinerInputParameterivNV': 4, + 'GetFinalCombinerInputParameterfvNV': 4, + 'GetFinalCombinerInputParameterivNV': 4, + 'GetLightfv': 4, + 'GetLightiv': 4, + 'GetMaterialfv': 4, + 'GetMaterialiv': 4, + 'GetPolygonStipple': 32*32/8, + 'GetTexEnvfv': 4, + 'GetTexEnviv': 4, + 'GetTexGendv': 4, + 'GetTexGenfv': 4, + 'GetTexGeniv': 4, + 'GetTexLevelParameterfv': 1, + 'GetTexLevelParameteriv': 1, + 'GetTexParameterfv': 4, + 'GetTexParameteriv': 4, + 'GetProgramParameterdvNV': 4, + 'GetProgramParameterfvNV': 4, + 'GetProgramivNV': 1, + 'GetTrackMatrixivNV': 1, + 'GetVertexAttribPointervNV': 1, + 'GetVertexAttribdvNV': 4, + 'GetVertexAttribfvNV': 4, + 'GetVertexAttribivNV': 4, + 'GetFenceivNV': 1, + 'GetVertexAttribdvARB': 4, + 'GetVertexAttribfvARB': 4, + 'GetVertexAttribivARB': 4, + 'GetVertexAttribPointervARB': 1, + 'GetProgramNamedParameterdvNV': 4, + 'GetProgramNamedParameterfvNV': 4, + 'GetProgramLocalParameterdvARB': 4, + 'GetProgramLocalParameterfvARB': 4, + 'GetProgramEnvParameterdvARB': 4, + 'GetProgramEnvParameterfvARB': 4, + 'GetProgramivARB': 1, + 'AreProgramsResidentNV': 1, + 'GetBufferParameterivARB': 1, + 'GetBufferPointervARB': 1, + 'GetQueryObjectivARB' : 1, + 'GetQueryObjectuivARB' : 1, + 'GetQueryivARB' : 1, + 'GetProgramiv' : 1, + 'GetShaderiv' : 1, + 'GetObjectParameterfvARB': 1, + 'GetObjectParameterivARB': 1, + 'GetRenderbufferParameterivEXT': 1, + 'GetFramebufferAttachmentParameterivEXT': 1 +} + +no_pnames = [ + 'GetClipPlane', + 'GetPolygonStipple', + 'GetProgramLocalParameterdvARB', + 'GetProgramLocalParameterfvARB', + 'GetProgramNamedParameterdvNV', + 'GetProgramNamedParameterfvNV', + 'GetProgramNamedParameterdvNV', + 'GetProgramNamedParameterfvNV', + 'GetProgramEnvParameterdvARB', + 'GetProgramEnvParameterfvARB', + 'GetProgramivARB', + 'AreProgramsResidentNV', + 'GetProgramiv', + 'GetShaderiv', + 'GetObjectParameterfvARB', + 'GetObjectParameterivARB', + 'GetRenderbufferParameterivEXT', + 'GetFramebufferAttachmentParameterivEXT' +]; + +convert_bufferid = [ + 'GetVertexAttribdvARB', + 'GetVertexAttribdvNV', + 'GetVertexAttribfvARB', + 'GetVertexAttribfvNV', + 'GetVertexAttribivARB', + 'GetVertexAttribivNV' +]; + +keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt") +for func_name in keys: + #(return_type, arg_names, arg_types) = gl_mapping[func_name] + if ("get" in apiutil.Properties(func_name) and + apiutil.ReturnType(func_name) == "void" and + not apiutil.FindSpecial( "server", func_name )): + + params = apiutil.Parameters(func_name) + + print('void SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s)' % (func_name, apiutil.MakeDeclarationString( params ) )) + print('{') + + lastParam = params[-1] + assert apiutil.IsPointer(lastParam[1]) + local_argtype = apiutil.PointerType(lastParam[1]) + local_argname = 'local_%s' % lastParam[0] + + print('\t%s %s[%d];' % ( local_argtype, local_argname, max_components[func_name] )) + print('\t(void) %s;' % lastParam[0]) + + params[-1] = (local_argname, local_argtype, 0) + + print('\tcr_server.head_spu->dispatch_table.%s(%s);' % ( func_name, apiutil.MakeCallString(params) )) + + if func_name in convert_bufferid: + print('\tif (pname==GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB){') + print('\t\tlocal_params[0]=(%s)crStateBufferHWIDtoID((GLint)local_params[0]);' % (local_argtype)) + print('\t}') + + if func_name in no_pnames: + print('\tcrServerReturnValue(&(%s[0]), %d*sizeof(%s));' % (local_argname, max_components[func_name], local_argtype )) + else: + print('\tcrServerReturnValue(&(%s[0]), crStateHlpComponentsCount(pname)*sizeof(%s));' % (local_argname, local_argtype )) + print ('}\n') diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getmap.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getmap.c new file mode 100644 index 00000000..b8123e32 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getmap.c @@ -0,0 +1,247 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" + +static GLuint __evaluator_components( GLenum target ) +{ + switch (target) { + case GL_MAP1_VERTEX_3: return 3; + case GL_MAP1_VERTEX_4: return 4; + case GL_MAP1_INDEX: return 1; + case GL_MAP1_COLOR_4: return 4; + case GL_MAP1_NORMAL: return 3; + case GL_MAP1_TEXTURE_COORD_1: return 1; + case GL_MAP1_TEXTURE_COORD_2: return 2; + case GL_MAP1_TEXTURE_COORD_3: return 3; + case GL_MAP1_TEXTURE_COORD_4: return 4; + case GL_MAP2_VERTEX_3: return 3; + case GL_MAP2_VERTEX_4: return 4; + case GL_MAP2_INDEX: return 1; + case GL_MAP2_COLOR_4: return 4; + case GL_MAP2_NORMAL: return 3; + case GL_MAP2_TEXTURE_COORD_1: return 1; + case GL_MAP2_TEXTURE_COORD_2: return 2; + case GL_MAP2_TEXTURE_COORD_3: return 3; + case GL_MAP2_TEXTURE_COORD_4: return 4; + default: return 0; + } +} + +static GLuint __evaluator_dimension( GLenum target ) +{ + switch( target ) + { + case GL_MAP1_COLOR_4: + case GL_MAP1_INDEX: + case GL_MAP1_NORMAL: + case GL_MAP1_TEXTURE_COORD_1: + case GL_MAP1_TEXTURE_COORD_2: + case GL_MAP1_TEXTURE_COORD_3: + case GL_MAP1_TEXTURE_COORD_4: + case GL_MAP1_VERTEX_3: + case GL_MAP1_VERTEX_4: + return 1; + + case GL_MAP2_COLOR_4: + case GL_MAP2_INDEX: + case GL_MAP2_NORMAL: + case GL_MAP2_TEXTURE_COORD_1: + case GL_MAP2_TEXTURE_COORD_2: + case GL_MAP2_TEXTURE_COORD_3: + case GL_MAP2_TEXTURE_COORD_4: + case GL_MAP2_VERTEX_3: + case GL_MAP2_VERTEX_4: + return 2; + + default: + return 0; + } +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetMapdv( GLenum target, GLenum query, GLdouble *v ) +{ + GLdouble *coeffs = NULL; + GLdouble *retptr = NULL; + GLdouble order[2] = {0}; + GLdouble domain[4] = {0}; + GLint tempOrder[2] = {0}; + int dimension, evalcomp; + unsigned int size = sizeof(GLdouble); + (void) v; + + evalcomp = __evaluator_components(target); + dimension = __evaluator_dimension(target); + + if (evalcomp == 0 || dimension == 0) + { + crError( "Bad target in crServerDispatchGetMapdv: %d", target ); + return; + } + + switch(query) + { + case GL_ORDER: + cr_server.head_spu->dispatch_table.GetMapdv( target, query, order ); + retptr = &(order[0]); + size *= dimension; + break; + case GL_DOMAIN: + cr_server.head_spu->dispatch_table.GetMapdv( target, query, domain ); + retptr = &(domain[0]); + size *= dimension * 2; + break; + case GL_COEFF: + cr_server.head_spu->dispatch_table.GetMapiv( target, GL_ORDER, tempOrder ); + size *= evalcomp * tempOrder[0]; + if (dimension == 2) + size *= tempOrder[1]; + + if (size) + coeffs = (GLdouble *) crCalloc( size ); + + if (coeffs) + { + cr_server.head_spu->dispatch_table.GetMapdv( target, query, coeffs ); + retptr = coeffs; + } + break; + default: + crError( "Bad query in crServerDispatchGetMapdv: %d", query ); + return; + } + + crServerReturnValue( retptr, size ); + if (coeffs) + { + crFree(coeffs); + } +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetMapfv( GLenum target, GLenum query, GLfloat *v ) +{ + GLfloat *coeffs = NULL; + GLfloat *retptr = NULL; + GLfloat order[2] = {0}; + GLfloat domain[4] = {0}; + GLint tempOrder[2] = {0}; + int dimension, evalcomp; + unsigned int size = sizeof(GLfloat); + (void) v; + + evalcomp = __evaluator_components(target); + dimension = __evaluator_dimension(target); + + if (evalcomp == 0 || dimension == 0) + { + crError( "Bad target in crServerDispatchGetMapfv: %d", target ); + return; + } + + switch(query) + { + case GL_ORDER: + cr_server.head_spu->dispatch_table.GetMapfv( target, query, order ); + retptr = &(order[0]); + size *= dimension; + break; + case GL_DOMAIN: + cr_server.head_spu->dispatch_table.GetMapfv( target, query, domain ); + retptr = &(domain[0]); + size *= dimension * 2; + break; + case GL_COEFF: + cr_server.head_spu->dispatch_table.GetMapiv( target, GL_ORDER, tempOrder ); + size *= evalcomp * tempOrder[0]; + if (dimension == 2) + size *= tempOrder[1]; + + if (size) + coeffs = (GLfloat *) crCalloc( size ); + + if (coeffs) + { + cr_server.head_spu->dispatch_table.GetMapfv( target, query, coeffs ); + retptr = coeffs; + } + break; + default: + crError( "Bad query in crServerDispatchGetMapfv: %d", query ); + return; + } + + crServerReturnValue( retptr, size ); + if (coeffs) + { + crFree(coeffs); + } +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetMapiv( GLenum target, GLenum query, GLint *v ) +{ + GLint *coeffs = NULL; + GLint *retptr = NULL; + GLint order[2] = {0}; + GLint domain[4] = {0}; + GLint tempOrder[2] = {0}; + int dimension, evalcomp; + unsigned int size = sizeof(GLint); + (void) v; + + evalcomp = __evaluator_components(target); + dimension = __evaluator_dimension(target); + + if (evalcomp == 0 || dimension == 0) + { + crError( "Bad target in crServerDispatchGetMapiv: %d", target ); + return; + } + + switch(query) + { + case GL_ORDER: + cr_server.head_spu->dispatch_table.GetMapiv( target, query, order ); + retptr = &(order[0]); + size *= dimension; + break; + case GL_DOMAIN: + cr_server.head_spu->dispatch_table.GetMapiv( target, query, domain ); + retptr = &(domain[0]); + size *= dimension * 2; + break; + case GL_COEFF: + cr_server.head_spu->dispatch_table.GetMapiv( target, GL_ORDER, tempOrder ); + size *= evalcomp * tempOrder[0]; + if (dimension == 2) + size *= tempOrder[1]; + + if (size) + coeffs = (GLint *) crCalloc( size ); + + if (coeffs) + { + cr_server.head_spu->dispatch_table.GetMapiv( target, query, coeffs ); + retptr = coeffs; + } + break; + default: + crError( "Bad query in crServerDispatchGetMapiv: %d", query ); + break; + } + + crServerReturnValue( retptr, size ); + if (coeffs) + { + crFree(coeffs); + } +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpixelmap.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpixelmap.c new file mode 100644 index 00000000..79c37866 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpixelmap.c @@ -0,0 +1,142 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" + +static GLint __sizeQuery( GLenum map ) +{ + GLint get_values; + /* Windows compiler gets mad if variables might be uninitialized */ + GLenum newmap = GL_PIXEL_MAP_I_TO_I_SIZE; + + switch( map ) + { + case GL_PIXEL_MAP_I_TO_I: + newmap = GL_PIXEL_MAP_I_TO_I_SIZE; + break; + case GL_PIXEL_MAP_S_TO_S: + newmap = GL_PIXEL_MAP_S_TO_S_SIZE; + break; + case GL_PIXEL_MAP_I_TO_R: + newmap = GL_PIXEL_MAP_I_TO_R_SIZE; + break; + case GL_PIXEL_MAP_I_TO_G: + newmap = GL_PIXEL_MAP_I_TO_G_SIZE; + break; + case GL_PIXEL_MAP_I_TO_B: + newmap = GL_PIXEL_MAP_I_TO_B_SIZE; + break; + case GL_PIXEL_MAP_I_TO_A: + newmap = GL_PIXEL_MAP_I_TO_A_SIZE; + break; + case GL_PIXEL_MAP_R_TO_R: + newmap = GL_PIXEL_MAP_R_TO_R_SIZE; + break; + case GL_PIXEL_MAP_G_TO_G: + newmap = GL_PIXEL_MAP_G_TO_G_SIZE; + break; + case GL_PIXEL_MAP_B_TO_B: + newmap = GL_PIXEL_MAP_B_TO_B_SIZE; + break; + case GL_PIXEL_MAP_A_TO_A: + newmap = GL_PIXEL_MAP_A_TO_A_SIZE; + break; + default: + crError( "Bad map in crServerDispatchGetPixelMap: %d", map ); + break; + } + + cr_server.head_spu->dispatch_table.GetIntegerv( newmap, &get_values ); + + return get_values; +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetPixelMapfv( GLenum map, GLfloat *values ) +{ +#ifdef CR_ARB_pixel_buffer_object + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + GLvoid *pbo_offset; + + pbo_offset = (GLfloat*) ((uintptr_t) *((GLint*)values)); + + cr_server.head_spu->dispatch_table.GetPixelMapfv( map, pbo_offset ); + } + else +#endif + { + int size = sizeof( GLfloat ); + int tabsize = __sizeQuery( map ); + GLfloat *local_values; + + size *= tabsize; + local_values = (GLfloat*)crAlloc( size ); + + cr_server.head_spu->dispatch_table.GetPixelMapfv( map, local_values ); + crServerReturnValue( local_values, size ); + crFree( local_values ); + } +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetPixelMapuiv( GLenum map, GLuint *values ) +{ +#ifdef CR_ARB_pixel_buffer_object + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + GLvoid *pbo_offset; + + pbo_offset = (GLuint*) ((uintptr_t) *((GLint*)values)); + + cr_server.head_spu->dispatch_table.GetPixelMapuiv( map, pbo_offset ); + } + else +#endif + { + int size = sizeof( GLuint ); + int tabsize = __sizeQuery( map ); + GLuint *local_values; + + size *= tabsize; + local_values = (GLuint*)crAlloc( size ); + + cr_server.head_spu->dispatch_table.GetPixelMapuiv( map, local_values ); + crServerReturnValue( local_values, size ); + crFree( local_values ); + } +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetPixelMapusv( GLenum map, GLushort *values ) +{ +#ifdef CR_ARB_pixel_buffer_object + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + GLvoid *pbo_offset; + + pbo_offset = (GLushort*) ((uintptr_t) *((GLint*)values)); + + cr_server.head_spu->dispatch_table.GetPixelMapusv( map, pbo_offset ); + } + else +#endif + { + int size = sizeof( GLushort ); + int tabsize = __sizeQuery( map ); + GLushort *local_values; + + size *= tabsize; + local_values = (GLushort*)crAlloc( size ); + + cr_server.head_spu->dispatch_table.GetPixelMapusv( map, local_values ); + crServerReturnValue( local_values, size ); + crFree( local_values ); + } +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpointer.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpointer.c new file mode 100644 index 00000000..bc14298a --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getpointer.c @@ -0,0 +1,32 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "chromium.h" +#include "cr_error.h" +#include "server_dispatch.h" +#include "server.h" + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetPointerv( GLenum pname, GLvoid **pointer ) +{ + crError( "glGetPointerv isn't *ever* allowed to be on the wire!" ); + (void) pname; + (void) pointer; +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetVertexAttribPointervNV( GLuint index, GLenum pname, GLvoid ** pointer ) +{ + crError( "glGetVertexAttribPointervNV isn't *ever* allowed to be on the wire!" ); + (void) pname; + (void) pointer; +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetVertexAttribPointervARB( GLuint index, GLenum pname, GLvoid ** pointer ) +{ + crError( "glGetVertexAttribPointervNV isn't *ever* allowed to be on the wire!" ); + (void) pname; + (void) pointer; +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c new file mode 100644 index 00000000..8a657d43 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c @@ -0,0 +1,395 @@ +/* $Id: server_getshaders.c $ */ +/** @file + * VBox OpenGL GLSL related get functions + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" + +#include <iprt/assert.h> + +#ifdef CR_OPENGL_VERSION_2_0 + +typedef struct _crGetActive_t +{ + GLsizei length; + GLint size; + GLenum type; +} crGetActive_t; + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, char *name) +{ + crGetActive_t *pLocal = NULL; + + if (bufSize > 0 && bufSize < INT32_MAX / 2) + pLocal = (crGetActive_t*)crCalloc(bufSize + sizeof(crGetActive_t)); + + if (!pLocal) + { + crGetActive_t zero; + zero.length = 0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + + cr_server.head_spu->dispatch_table.GetActiveAttrib(crStateGetProgramHWID(program), index, bufSize, &pLocal->length, &pLocal->size, &pLocal->type, (char*)&pLocal[1]); + crServerReturnValue(pLocal, pLocal->length+1+sizeof(crGetActive_t)); + crFree(pLocal); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, char *name) +{ + crGetActive_t *pLocal = NULL; + + if (bufSize > 0 && bufSize < INT32_MAX / 2) + pLocal = (crGetActive_t*) crCalloc(bufSize + sizeof(crGetActive_t)); + + if (!pLocal) + { + crGetActive_t zero; + zero.length = 0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + + cr_server.head_spu->dispatch_table.GetActiveUniform(crStateGetProgramHWID(program), index, bufSize, &pLocal->length, &pLocal->size, &pLocal->type, (char*)&pLocal[1]); + crServerReturnValue(pLocal, pLocal->length+1+sizeof(crGetActive_t)); + crFree(pLocal); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders) +{ + GLsizei *pLocal = NULL; + + if (maxCount > 0 && maxCount < INT32_MAX / sizeof(GLuint) / 2) + pLocal = (GLsizei*) crCalloc(maxCount * sizeof(GLuint) + sizeof(GLsizei)); + + if (!pLocal) + { + GLsizei zero=0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + /* initial (fallback )value */ + *pLocal = 0; + cr_server.head_spu->dispatch_table.GetAttachedShaders(crStateGetProgramHWID(program), maxCount, pLocal, (GLuint*)&pLocal[1]); + + { + GLsizei i; + GLuint *ids=(GLuint*)&pLocal[1]; + + for (i=0; i<*pLocal; ++i) + ids[i] = crStateGLSLShaderHWIDtoID(ids[i]); + } + + crServerReturnValue(pLocal, (*pLocal)*sizeof(GLuint)+sizeof(GLsizei)); + crFree(pLocal); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetAttachedObjectsARB(VBoxGLhandleARB containerObj, GLsizei maxCount, GLsizei * count, VBoxGLhandleARB * obj) +{ + GLsizei *pLocal = NULL; + + if (maxCount > 0 && maxCount < INT32_MAX / sizeof(VBoxGLhandleARB) / 2) + pLocal = (GLsizei*) crCalloc(maxCount * sizeof(VBoxGLhandleARB) + sizeof(GLsizei)); + + if (!pLocal) + { + GLsizei zero=0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + /* initial (fallback )value */ + *pLocal = 0; + cr_server.head_spu->dispatch_table.GetAttachedObjectsARB(crStateGetProgramHWID(containerObj), maxCount, pLocal, (VBoxGLhandleARB*)&pLocal[1]); + + { + GLsizei i; + GLuint *ids=(GLuint*)&pLocal[1]; + + for (i=0; i<*pLocal; ++i) + ids[i] = crStateGLSLShaderHWIDtoID(ids[i]); + } + + crServerReturnValue(pLocal, (*pLocal)*sizeof(VBoxGLhandleARB)+sizeof(GLsizei)); + crFree(pLocal); +} + +AssertCompile(sizeof(GLsizei) == 4); + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetInfoLogARB(VBoxGLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog) +{ + GLsizei *pLocal = NULL; + GLuint hwid; + + if (maxLength > 0 && maxLength < INT32_MAX / 2) + pLocal = (GLsizei*) crCalloc(maxLength + sizeof(GLsizei)); + + if (!pLocal) + { + GLsizei zero=0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + /* initial (fallback )value */ + *pLocal = 0; + /** @todo recheck*/ + hwid = crStateGetProgramHWID(obj); + if (!hwid) hwid = crStateGetShaderHWID(obj); + cr_server.head_spu->dispatch_table.GetInfoLogARB(hwid, maxLength, pLocal, (char*)&pLocal[1]); + CRASSERT((*pLocal) <= maxLength); + crServerReturnValue(pLocal, (*pLocal)+sizeof(GLsizei)); + crFree(pLocal); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, char *infoLog) +{ + GLsizei *pLocal = NULL; + + if (bufSize > 0 && bufSize < INT32_MAX / 2) + pLocal = (GLsizei*) crCalloc(bufSize + sizeof(GLsizei)); + + if (!pLocal) + { + GLsizei zero=0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + /* initial (fallback )value */ + *pLocal = 0; + cr_server.head_spu->dispatch_table.GetShaderInfoLog(crStateGetShaderHWID(shader), bufSize, pLocal, (char*)&pLocal[1]); + crServerReturnValue(pLocal, pLocal[0]+sizeof(GLsizei)); + crFree(pLocal); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length, char *infoLog) +{ + GLsizei *pLocal = NULL; + + if (bufSize > 0 && bufSize < INT32_MAX / 2) + pLocal = (GLsizei*) crCalloc(bufSize + sizeof(GLsizei)); + + if (!pLocal) + { + GLsizei zero=0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + /* initial (fallback )value */ + *pLocal = 0; + cr_server.head_spu->dispatch_table.GetProgramInfoLog(crStateGetProgramHWID(program), bufSize, pLocal, (char*)&pLocal[1]); + CRASSERT(pLocal[0] <= bufSize); + crServerReturnValue(pLocal, pLocal[0]+sizeof(GLsizei)); + crFree(pLocal); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetShaderSource(GLuint shader, GLsizei bufSize, GLsizei *length, char *source) +{ + GLsizei *pLocal = NULL; + + if (bufSize > 0 && bufSize < INT32_MAX / 2) + pLocal = (GLsizei*) crCalloc(bufSize + sizeof(GLsizei)); + + if (!pLocal) + { + GLsizei zero=0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + /* initial (fallback )value */ + *pLocal = 0; + cr_server.head_spu->dispatch_table.GetShaderSource(crStateGetShaderHWID(shader), bufSize, pLocal, (char*)&pLocal[1]); + CRASSERT(pLocal[0] <= bufSize); + crServerReturnValue(pLocal, pLocal[0]+sizeof(GLsizei)); + crFree(pLocal); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGetUniformsLocations(GLuint program, GLsizei maxcbData, GLsizei * cbData, GLvoid * pData) +{ + GLsizei *pLocal = NULL; + + (void) cbData; + (void) pData; + + if (maxcbData > 0 && maxcbData < INT32_MAX / 2) + pLocal = (GLsizei*) crCalloc(maxcbData + sizeof(GLsizei)); + + if (!pLocal) + { + GLsizei zero=0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + + /* initial (fallback )value */ + *pLocal = 0; + crStateGLSLProgramCacheUniforms(program, maxcbData, pLocal, (char*)&pLocal[1]); + + crServerReturnValue(pLocal, (*pLocal)+sizeof(GLsizei)); + crFree(pLocal); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGetAttribsLocations(GLuint program, GLsizei maxcbData, GLsizei * cbData, GLvoid * pData) +{ + GLsizei *pLocal = NULL; + + (void) cbData; + (void) pData; + + if (maxcbData > 0 && maxcbData < INT32_MAX / 2) + pLocal = (GLsizei*) crCalloc(maxcbData + sizeof(GLsizei)); + + if (!pLocal) + { + GLsizei zero=0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + + /* initial (fallback )value */ + *pLocal = 0; + crStateGLSLProgramCacheAttribs(program, maxcbData, pLocal, (char*)&pLocal[1]); + + crServerReturnValue(pLocal, (*pLocal)+sizeof(GLsizei)); + crFree(pLocal); +} + +static GLint __GetUniformSize(GLuint program, GLint location) +{ + GLint size = 0; + GLenum type = 0; + + /** @todo check if index and location is the same*/ + cr_server.head_spu->dispatch_table.GetActiveUniform(crStateGetProgramHWID(program), location, 0, NULL, &size, &type, NULL); + + return crStateGetUniformSize(type); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetUniformfv(GLuint program, GLint location, GLfloat *params) +{ + int size = __GetUniformSize(program, location) * sizeof(GLfloat); + GLfloat *pLocal; + + pLocal = (GLfloat*) crCalloc(size); + if (!pLocal) + { + GLsizei zero=0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + + cr_server.head_spu->dispatch_table.GetUniformfv(crStateGetProgramHWID(program), location, pLocal); + + crServerReturnValue(pLocal, size); + crFree(pLocal); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetUniformiv(GLuint program, GLint location, GLint *params) +{ + int size = __GetUniformSize(program, location) * sizeof(GLint); + GLint *pLocal; + + pLocal = (GLint*) crCalloc(size); + if (!pLocal) + { + GLsizei zero=0; + crServerReturnValue(&zero, sizeof(zero)); + return; + } + + cr_server.head_spu->dispatch_table.GetUniformiv(crStateGetProgramHWID(program), location, pLocal); + + crServerReturnValue(pLocal, size); + crFree(pLocal); +} + +GLuint SERVER_DISPATCH_APIENTRY crServerDispatchCreateShader(GLenum type) +{ + GLuint retval, hwVal; + hwVal = cr_server.head_spu->dispatch_table.CreateShader(type); + retval = crStateCreateShader(hwVal, type); + crServerReturnValue(&retval, sizeof(retval)); + return retval; /* ignored */ +} + +GLuint SERVER_DISPATCH_APIENTRY crServerDispatchCreateProgram(void) +{ + GLuint retval, hwVal; + hwVal = cr_server.head_spu->dispatch_table.CreateProgram(); + retval = crStateCreateProgram(hwVal); + crServerReturnValue(&retval, sizeof(retval)); + return retval; /* ignored */ +} + +GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsShader(GLuint shader) +{ + GLboolean retval; + retval = cr_server.head_spu->dispatch_table.IsShader(crStateGetShaderHWID(shader)); + crServerReturnValue(&retval, sizeof(retval)); + return retval; /* ignored */ +} + +GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsProgram(GLuint program) +{ + GLboolean retval; + retval = cr_server.head_spu->dispatch_table.IsProgram(crStateGetProgramHWID(program)); + crServerReturnValue(&retval, sizeof(retval)); + return retval; /* ignored */ +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetObjectParameterfvARB( VBoxGLhandleARB obj, GLenum pname, GLfloat * params ) +{ + GLfloat local_params[1]; + GLuint hwid = crStateGetProgramHWID(obj); + (void) params; + + if (!hwid) + { + hwid = crStateGetShaderHWID(obj); + if (!hwid) + { + crWarning("Unknown object %i, in crServerDispatchGetObjectParameterfvARB", obj); + } + } + + cr_server.head_spu->dispatch_table.GetObjectParameterfvARB( hwid, pname, local_params ); + crServerReturnValue( &(local_params[0]), 1*sizeof(GLfloat) ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetObjectParameterivARB( VBoxGLhandleARB obj, GLenum pname, GLint * params ) +{ + GLint local_params[1]; + GLuint hwid = crStateGetProgramHWID(obj); + if (!hwid) + { + hwid = crStateGetShaderHWID(obj); + if (!hwid) + { + crWarning("Unknown object %i, in crServerDispatchGetObjectParameterivARB", obj); + } + } + + (void) params; + cr_server.head_spu->dispatch_table.GetObjectParameterivARB( hwid, pname, local_params ); + crServerReturnValue( &(local_params[0]), 1*sizeof(GLint) ); +} +#endif /* #ifdef CR_OPENGL_VERSION_2_0 */ diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getstring.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getstring.c new file mode 100644 index 00000000..18fad4e6 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getstring.c @@ -0,0 +1,38 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "chromium.h" +#include "cr_error.h" +#include "server_dispatch.h" +#include "server.h" +#include "cr_string.h" + +const GLubyte * SERVER_DISPATCH_APIENTRY crServerDispatchGetString( GLenum name ) +{ + const GLubyte *retval; + retval = cr_server.head_spu->dispatch_table.GetString( name ); + if (retval) + crServerReturnValue( retval, crStrlen((char *)retval) + 1 ); + else + crServerReturnValue( "", 1 ); /* empty string */ + return retval; /* WILL PROBABLY BE IGNORED */ +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetProgramStringNV( GLuint id, GLenum pname, GLubyte * program ) +{ + crError( "glGetProgramStringNV isn't *ever* allowed to be on the wire!" ); + (void) id; + (void) pname; + (void) program; +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetProgramStringARB( GLuint id, GLenum pname, void * program ) +{ + crError( "glGetProgramStringARB isn't *ever* allowed to be on the wire!" ); + (void) id; + (void) pname; + (void) program; +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getteximage.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getteximage.c new file mode 100644 index 00000000..288de27b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getteximage.c @@ -0,0 +1,156 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "chromium.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_pixeldata.h" +#include "server_dispatch.h" +#include "server.h" + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGetTexImage(GLenum target, GLint level, GLenum format, + GLenum type, GLvoid * pixels) +{ + GLsizei width, height, depth, size; + GLvoid *buffer = NULL; + + +#ifdef CR_ARB_pixel_buffer_object + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + GLvoid *pbo_offset; + + /*pixels are actually a pointer to location of 8byte network pointer in hgcm buffer + regardless of guest/host bitness we're using only 4lower bytes as there're no + pbo>4gb (yet?) + */ + pbo_offset = (GLvoid*) ((uintptr_t) *((GLint*)pixels)); + + cr_server.head_spu->dispatch_table.GetTexImage(target, level, format, type, pbo_offset); + + return; + } +#endif + + cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_WIDTH, &width); + cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_HEIGHT, &height); + cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_DEPTH, &depth); + + size = crTextureSize(format, type, width, height, depth); + +#if 0 + { + CRContext *ctx = crStateGetCurrent(); + CRTextureObj *tobj; + CRTextureLevel *tl; + GLint id; + + crDebug("GetTexImage: %d, %i, %d, %d", target, level, format, type); + crDebug("===StateTracker==="); + crDebug("Current TU: %i", ctx->texture.curTextureUnit); + + if (target==GL_TEXTURE_2D) + { + tobj = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D; + CRASSERT(tobj); + tl = &tobj->level[0][level]; + crDebug("Texture %i(hw %i), w=%i, h=%i", tobj->id, tobj->hwid, tl->width, tl->height, tl->depth); + } + else + { + crDebug("Not 2D tex"); + } + + crDebug("===GPU==="); + cr_server.head_spu->dispatch_table.GetIntegerv(GL_ACTIVE_TEXTURE, &id); + crDebug("Current TU: %i", id); + if (target==GL_TEXTURE_2D) + { + cr_server.head_spu->dispatch_table.GetIntegerv(GL_TEXTURE_BINDING_2D, &id); + crDebug("Texture: %i, w=%i, h=%i, d=%i", id, width, height, depth); + } + } +#endif + + if (size && (buffer = crCalloc(size))) { + /* Note, the other pixel PACK parameters (default values) should + * be OK at this point. + */ + cr_server.head_spu->dispatch_table.PixelStorei(GL_PACK_ALIGNMENT, 1); + cr_server.head_spu->dispatch_table.GetTexImage(target, level, format, type, buffer); + crServerReturnValue( buffer, size ); + crFree(buffer); + } + else { + /* need to return _something_ to avoid blowing up */ + GLuint dummy = 0; + crServerReturnValue( (GLvoid *) &dummy, sizeof(dummy) ); + } +} + + +#if CR_ARB_texture_compression + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGetCompressedTexImageARB(GLenum target, GLint level, + GLvoid *img) +{ + GLint size; + GLvoid *buffer=NULL; + +#ifdef CR_ARB_pixel_buffer_object + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + GLvoid *pbo_offset; + + pbo_offset = (GLvoid*) ((uintptr_t) *((GLint*)img)); + + cr_server.head_spu->dispatch_table.GetCompressedTexImageARB(target, level, pbo_offset); + + return; + } +#endif + + cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(target, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &size); + + if (size && (buffer = crCalloc(size))) { + /* XXX the pixel PACK parameter should be OK at this point */ + cr_server.head_spu->dispatch_table.GetCompressedTexImageARB(target, level, buffer); + crServerReturnValue( buffer, size ); + crFree(buffer); + } + else { + /* need to return _something_ to avoid blowing up */ + GLuint dummy = 0; + crServerReturnValue( (GLvoid *) &dummy, sizeof(dummy) ); + } +} + +#endif /* CR_ARB_texture_compression */ + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetPolygonStipple( GLubyte * mask ) +{ +#ifdef CR_ARB_pixel_buffer_object + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + GLvoid *pbo_offset; + + pbo_offset = (GLubyte*) ((uintptr_t) *((GLint*)mask)); + + cr_server.head_spu->dispatch_table.GetPolygonStipple(pbo_offset); + } + else +#endif + { + GLubyte local_mask[128]; + + memset(local_mask, 0, sizeof(local_mask)); + + cr_server.head_spu->dispatch_table.GetPolygonStipple( local_mask ); + crServerReturnValue( &(local_mask[0]), 128*sizeof(GLubyte) ); + } +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c new file mode 100644 index 00000000..a5a915ab --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_glsl.c @@ -0,0 +1,262 @@ +/* $Id: server_glsl.c $ */ +/** @file + * VBox OpenGL - GLSL related functions + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" + +#ifdef CR_OPENGL_VERSION_2_0 + +void SERVER_DISPATCH_APIENTRY crServerDispatchShaderSource(GLuint shader, GLsizei count, const char ** string, const GLint * length) +{ + /*@todo?crStateShaderSource(shader...);*/ +#ifdef DEBUG_misha + GLenum err = cr_server.head_spu->dispatch_table.GetError(); +#endif + cr_server.head_spu->dispatch_table.ShaderSource(crStateGetShaderHWID(shader), count, string, length); +#ifdef DEBUG_misha + err = cr_server.head_spu->dispatch_table.GetError(); + CRASSERT(err == GL_NO_ERROR); +#endif + CR_SERVER_DUMP_SHADER_SOURCE(shader); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchCompileShader(GLuint shader) +{ +#ifdef DEBUG_misha + GLint iCompileStatus = GL_FALSE; +#endif + crStateCompileShader(shader); + cr_server.head_spu->dispatch_table.CompileShader(crStateGetShaderHWID(shader)); +#ifdef DEBUG_misha + cr_server.head_spu->dispatch_table.GetShaderiv(crStateGetShaderHWID(shader), GL_COMPILE_STATUS, &iCompileStatus); + Assert(iCompileStatus == GL_TRUE); +#endif + CR_SERVER_DUMP_COMPILE_SHADER(shader); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteShader(GLuint shader) +{ + GLuint shaderHW = crStateGetShaderHWID(shader); + crStateDeleteShader(shader); + if (shaderHW) + cr_server.head_spu->dispatch_table.DeleteShader(shaderHW); + else + crWarning("crServerDispatchDeleteShader: hwid not found for shader(%d)", shader); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchAttachShader(GLuint program, GLuint shader) +{ + crStateAttachShader(program, shader); + cr_server.head_spu->dispatch_table.AttachShader(crStateGetProgramHWID(program), crStateGetShaderHWID(shader)); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchDetachShader(GLuint program, GLuint shader) +{ + crStateDetachShader(program, shader); + cr_server.head_spu->dispatch_table.DetachShader(crStateGetProgramHWID(program), crStateGetShaderHWID(shader)); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchLinkProgram(GLuint program) +{ + crStateLinkProgram(program); + cr_server.head_spu->dispatch_table.LinkProgram(crStateGetProgramHWID(program)); + CR_SERVER_DUMP_LINK_PROGRAM(program); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchUseProgram(GLuint program) +{ + crStateUseProgram(program); + cr_server.head_spu->dispatch_table.UseProgram(crStateGetProgramHWID(program)); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteProgram(GLuint program) +{ + GLuint hwId = crStateGetProgramHWID(program); + crStateDeleteProgram(program); + if (hwId) + cr_server.head_spu->dispatch_table.DeleteProgram(hwId); + else + crWarning("crServerDispatchDeleteProgram: hwid not found for program(%d)", program); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchValidateProgram(GLuint program) +{ + crStateValidateProgram(program); + cr_server.head_spu->dispatch_table.ValidateProgram(crStateGetProgramHWID(program)); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchBindAttribLocation(GLuint program, GLuint index, const char * name) +{ + crStateBindAttribLocation(program, index, name); + cr_server.head_spu->dispatch_table.BindAttribLocation(crStateGetProgramHWID(program), index, name); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteObjectARB(VBoxGLhandleARB obj) +{ + GLuint hwid = crStateDeleteObjectARB(obj); + + if (hwid) + cr_server.head_spu->dispatch_table.DeleteObjectARB(hwid); + else + crWarning("zero hwid for object %d", obj); +} + +GLint SERVER_DISPATCH_APIENTRY crServerDispatchGetAttribLocation( GLuint program, const char * name ) +{ + GLint retval; + retval = cr_server.head_spu->dispatch_table.GetAttribLocation(crStateGetProgramHWID(program), name ); + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} + +VBoxGLhandleARB SERVER_DISPATCH_APIENTRY crServerDispatchGetHandleARB( GLenum pname ) +{ + VBoxGLhandleARB retval; + retval = cr_server.head_spu->dispatch_table.GetHandleARB(pname); + if (pname==GL_PROGRAM_OBJECT_ARB) + { + retval = crStateGLSLProgramHWIDtoID(retval); + } + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} + +GLint SERVER_DISPATCH_APIENTRY crServerDispatchGetUniformLocation(GLuint program, const char * name) +{ + GLint retval; + retval = cr_server.head_spu->dispatch_table.GetUniformLocation(crStateGetProgramHWID(program), name); + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetProgramiv( GLuint program, GLenum pname, GLint * params ) +{ + GLint local_params[1]; + (void) params; + cr_server.head_spu->dispatch_table.GetProgramiv(crStateGetProgramHWID(program), pname, local_params); + crServerReturnValue( &(local_params[0]), 1*sizeof(GLint) ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetShaderiv( GLuint shader, GLenum pname, GLint * params ) +{ + GLint local_params[1]; + (void) params; + cr_server.head_spu->dispatch_table.GetShaderiv( crStateGetShaderHWID(shader), pname, local_params ); + crServerReturnValue( &(local_params[0]), 1*sizeof(GLint) ); +} +#endif /* #ifdef CR_OPENGL_VERSION_2_0 */ + +/* XXXX Note: shared/separate Program ID numbers aren't totally implemented! */ +GLuint crServerTranslateProgramID( GLuint id ) +{ + if (!cr_server.sharedPrograms && id) { + int client = cr_server.curClient->number; + return id + client * 100000; + } + return id; +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteProgramsARB(GLsizei n, const GLuint * programs) +{ + GLuint *pLocalProgs; + GLint i; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchDeleteProgramsARB: parameter 'n' is out of range"); + return; + } + + pLocalProgs = (GLuint *)crAlloc(n * sizeof(GLuint)); + + if (!pLocalProgs) { + crError("crServerDispatchDeleteProgramsARB: out of memory"); + return; + } + for (i = 0; i < n; i++) { + pLocalProgs[i] = crServerTranslateProgramID(programs[i]); + } + crStateDeleteProgramsARB(n, pLocalProgs); + cr_server.head_spu->dispatch_table.DeleteProgramsARB(n, pLocalProgs); + crFree(pLocalProgs); +} + + +/** @todo will fail for progs loaded from snapshot */ +GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsProgramARB( GLuint program ) +{ + GLboolean retval; + program = crServerTranslateProgramID(program); + retval = cr_server.head_spu->dispatch_table.IsProgramARB( program ); + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} + + +GLboolean SERVER_DISPATCH_APIENTRY +crServerDispatchAreProgramsResidentNV(GLsizei n, const GLuint *programs, + GLboolean *residences) +{ + GLboolean retval = GL_FALSE; + GLboolean *res; + GLsizei i; + (void) residences; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchAreProgramsResidentNV: parameter 'n' is out of range"); + return GL_FALSE; + } + + res = (GLboolean *)crCalloc(n * sizeof(GLboolean)); + + if (!res) { + crError("crServerDispatchAreProgramsResidentNV: out of memory"); + return GL_FALSE; + } + + if (!cr_server.sharedTextureObjects) { + GLuint *programs2 = (GLuint *) crCalloc(n * sizeof(GLuint)); + if (programs2) + { + for (i = 0; i < n; i++) + programs2[i] = crServerTranslateProgramID(programs[i]); + + retval = cr_server.head_spu->dispatch_table.AreProgramsResidentNV(n, programs2, res); + crFree(programs2); + } + else + { + crError("crServerDispatchAreProgramsResidentNV: out of memory"); + } + } + else { + retval = cr_server.head_spu->dispatch_table.AreProgramsResidentNV(n, programs, res); + } + + crServerReturnValue(res, n * sizeof(GLboolean)); + crFree(res); + + return retval; /* WILL PROBABLY BE IGNORED */ +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c new file mode 100644 index 00000000..a7a47ab5 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_lists.c @@ -0,0 +1,284 @@ +/* Copyright (c) 2001-2003, Stanford University + All rights reserved. + + See the file LICENSE.txt for information on redistributing this software. */ + +#include "server_dispatch.h" +#include "server.h" +#include "cr_mem.h" + + +/* + * Notes on ID translation: + * + * If a server has multiple clients (in the case of parallel applications) + * and N of the clients all create a display list with ID K, does K name + * one display list or N different display lists? + * + * By default, there is one display list named K. If the clients put + * identical commands into list K, then this is fine. But if the clients + * each put something different into list K when they created it, then this + * is a serious problem. + * + * By zeroing the 'shared_display_lists' configuration option, we can tell + * the server to make list K be unique for all N clients. We do this by + * translating K into a new, unique ID dependent on which client we're + * talking to (curClient->number). + * + * Same story for texture objects, vertex programs, etc. + * + * The application can also dynamically switch between shared and private + * display lists with: + * glChromiumParameteri(GL_SHARED_DISPLAY_LISTS_CR, GL_TRUE) + * and + * glChromiumParameteri(GL_SHARED_DISPLAY_LISTS_CR, GL_FALSE) + * + */ + + + +static GLuint TranslateListID( GLuint id ) +{ +#ifndef VBOX_WITH_CR_DISPLAY_LISTS + if (!cr_server.sharedDisplayLists) { + int client = cr_server.curClient->number; + return id + client * 100000; + } +#endif + return id; +} + + +GLuint SERVER_DISPATCH_APIENTRY crServerDispatchGenLists( GLsizei range ) +{ + GLuint retval; + retval = cr_server.head_spu->dispatch_table.GenLists( range ); + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchNewList( GLuint list, GLenum mode ) +{ + if (mode == GL_COMPILE_AND_EXECUTE) + crWarning("using glNewList(GL_COMPILE_AND_EXECUTE) can confuse the crserver"); + + list = TranslateListID( list ); + crStateNewList( list, mode ); + cr_server.head_spu->dispatch_table.NewList( list, mode ); +} + +static void crServerQueryHWState() +{ + if (!cr_server.bUseMultipleContexts) + { + GLuint fbFbo, bbFbo; + CRClient *client = cr_server.curClient; + CRMuralInfo *mural = client ? client->currentMural : NULL; + if (mural && mural->fRedirected) + { + fbFbo = mural->aidFBOs[CR_SERVER_FBO_FB_IDX(mural)]; + bbFbo = mural->aidFBOs[CR_SERVER_FBO_BB_IDX(mural)]; + } + else + { + fbFbo = bbFbo = 0; + } + crStateQueryHWState(fbFbo, bbFbo); + } +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchEndList(void) +{ + CRContext *g = crStateGetCurrent(); + CRListsState *l = &(g->lists); + + cr_server.head_spu->dispatch_table.EndList(); + crStateEndList(); + +#ifndef IN_GUEST + if (l->mode==GL_COMPILE) + { + crServerQueryHWState(); + } +#endif +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchCallList( GLuint list ) +{ + list = TranslateListID( list ); + + if (cr_server.curClient->currentCtxInfo->pContext->lists.mode == 0) { + /* we're not compiling, so execute the list now */ + /* Issue the list as-is */ + cr_server.head_spu->dispatch_table.CallList( list ); + crServerQueryHWState(); + } + else { + /* we're compiling glCallList into another list - just pass it through */ + cr_server.head_spu->dispatch_table.CallList( list ); + } +} + + +#ifndef VBOX_WITH_CR_DISPLAY_LISTS +/** + * Translate an array of display list IDs from various datatypes to GLuint + * IDs while adding the per-client offset. + */ +static void +TranslateListIDs(GLsizei n, GLenum type, const GLvoid *lists, GLuint *newLists) +{ + int offset = cr_server.curClient->number * 100000; + GLsizei i; + switch (type) { + case GL_UNSIGNED_BYTE: + { + const GLubyte *src = (const GLubyte *) lists; + for (i = 0; i < n; i++) { + newLists[i] = src[i] + offset; + } + } + break; + case GL_BYTE: + { + const GLbyte *src = (const GLbyte *) lists; + for (i = 0; i < n; i++) { + newLists[i] = src[i] + offset; + } + } + break; + case GL_UNSIGNED_SHORT: + { + const GLushort *src = (const GLushort *) lists; + for (i = 0; i < n; i++) { + newLists[i] = src[i] + offset; + } + } + break; + case GL_SHORT: + { + const GLshort *src = (const GLshort *) lists; + for (i = 0; i < n; i++) { + newLists[i] = src[i] + offset; + } + } + break; + case GL_UNSIGNED_INT: + { + const GLuint *src = (const GLuint *) lists; + for (i = 0; i < n; i++) { + newLists[i] = src[i] + offset; + } + } + break; + case GL_INT: + { + const GLint *src = (const GLint *) lists; + for (i = 0; i < n; i++) { + newLists[i] = src[i] + offset; + } + } + break; + case GL_FLOAT: + { + const GLfloat *src = (const GLfloat *) lists; + for (i = 0; i < n; i++) { + newLists[i] = (GLuint) src[i] + offset; + } + } + break; + case GL_2_BYTES: + { + const GLubyte *src = (const GLubyte *) lists; + for (i = 0; i < n; i++) { + newLists[i] = (src[i*2+0] * 256 + + src[i*2+1]) + offset; + } + } + break; + case GL_3_BYTES: + { + const GLubyte *src = (const GLubyte *) lists; + for (i = 0; i < n; i++) { + newLists[i] = (src[i*3+0] * 256 * 256 + + src[i*3+1] * 256 + + src[i*3+2]) + offset; + } + } + break; + case GL_4_BYTES: + { + const GLubyte *src = (const GLubyte *) lists; + for (i = 0; i < n; i++) { + newLists[i] = (src[i*4+0] * 256 * 256 * 256 + + src[i*4+1] * 256 * 256 + + src[i*4+2] * 256 + + src[i*4+3]) + offset; + } + } + break; + default: + crWarning("CRServer: invalid display list datatype 0x%x", type); + } +} +#endif + +void SERVER_DISPATCH_APIENTRY +crServerDispatchCallLists( GLsizei n, GLenum type, const GLvoid *lists ) +{ + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchCallLists: parameter 'n' is out of range"); + return; + } + +#ifndef VBOX_WITH_CR_DISPLAY_LISTS + if (!cr_server.sharedDisplayLists) { + /* need to translate IDs */ + GLuint *newLists = (GLuint *) crAlloc(n * sizeof(GLuint)); + if (newLists) { + TranslateListIDs(n, type, lists, newLists); + } + lists = newLists; + type = GL_UNSIGNED_INT; + } +#endif + + if (cr_server.curClient->currentCtxInfo->pContext->lists.mode == 0) { + /* we're not compiling, so execute the list now */ + /* Issue the list as-is */ + cr_server.head_spu->dispatch_table.CallLists( n, type, lists ); + crServerQueryHWState(); + } + else { + /* we're compiling glCallList into another list - just pass it through */ + cr_server.head_spu->dispatch_table.CallLists( n, type, lists ); + } + +#ifndef VBOX_WITH_CR_DISPLAY_LISTS + if (!cr_server.sharedDisplayLists) { + crFree((void *) lists); /* malloc'd above */ + } +#endif +} + + +GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsList( GLuint list ) +{ + GLboolean retval; + list = TranslateListID( list ); + retval = cr_server.head_spu->dispatch_table.IsList( list ); + crServerReturnValue( &retval, sizeof(retval) ); + return retval; +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteLists( GLuint list, GLsizei range ) +{ + list = TranslateListID( list ); + crStateDeleteLists( list, range ); + cr_server.head_spu->dispatch_table.DeleteLists( list, range ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c new file mode 100644 index 00000000..5e1b7267 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_main.c @@ -0,0 +1,4039 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "server.h" +#include "cr_net.h" +#include "cr_unpack.h" +#include "cr_error.h" +#include "cr_glstate.h" +#include "cr_string.h" +#include "cr_mem.h" +#include "cr_hash.h" +#include "cr_vreg.h" +#include "cr_environment.h" +#include "cr_pixeldata.h" + +#ifdef VBOX_WITH_CR_DISPLAY_LISTS +# include "cr_dlm.h" +#endif + +#include "server_dispatch.h" +#include "state/cr_texture.h" +#include "render/renderspu.h" +#include <signal.h> +#include <stdlib.h> +#define DEBUG_FP_EXCEPTIONS 0 +#if DEBUG_FP_EXCEPTIONS +#include <fpu_control.h> +#include <math.h> +#endif +#include <iprt/assert.h> +#include <VBox/err.h> +#include <VBox/log.h> +#include <VBox/AssertGuest.h> + +#ifdef VBOXCR_LOGFPS +#include <iprt/timer.h> +#endif + +#ifdef VBOX_WITH_CRHGSMI +# include <VBox/HostServices/VBoxCrOpenGLSvc.h> +uint8_t* g_pvVRamBase = NULL; +uint32_t g_cbVRam = 0; +PPDMLED g_pLed = NULL; + +HCRHGSMICMDCOMPLETION g_hCrHgsmiCompletion = NULL; +PFNCRHGSMICMDCOMPLETION g_pfnCrHgsmiCompletion = NULL; +#endif + +/** + * \mainpage CrServerLib + * + * \section CrServerLibIntroduction Introduction + * + * Chromium consists of all the top-level files in the cr + * directory. The core module basically takes care of API dispatch, + * and OpenGL state management. + */ + + +/** + * CRServer global data + */ +CRServer cr_server; + +int tearingdown = 0; /* can't be static */ + +static DECLCALLBACK(int8_t) crVBoxCrCmdCmd(HVBOXCRCMDSVR hSvr, + const VBOXCMDVBVA_HDR RT_UNTRUSTED_VOLATILE_GUEST *pCmd, uint32_t cbCmd); + +DECLINLINE(CRClient*) crVBoxServerClientById(uint32_t u32ClientID) +{ + int32_t i; + + if (cr_server.fCrCmdEnabled) + return CrHTableGet(&cr_server.clientTable, u32ClientID); + + for (i = 0; i < cr_server.numClients; i++) + { + if (cr_server.clients[i] && cr_server.clients[i]->conn + && cr_server.clients[i]->conn->u32ClientID==u32ClientID) + { + return cr_server.clients[i]; + } + } + + return NULL; +} + +int32_t crVBoxServerClientGet(uint32_t u32ClientID, CRClient **ppClient) +{ + CRClient *pClient = NULL; + + pClient = crVBoxServerClientById(u32ClientID); + + if (!pClient) + { + WARN(("client not found!")); + *ppClient = NULL; + return VERR_INVALID_PARAMETER; + } + + if (!pClient->conn->vMajor) + { + WARN(("no major version specified for client!")); + *ppClient = NULL; + return VERR_NOT_SUPPORTED; + } + + *ppClient = pClient; + + return VINF_SUCCESS; +} + + +/** + * Return pointer to server's first SPU. + */ +SPU* +crServerHeadSPU(void) +{ + return cr_server.head_spu; +} + + + +static void DeleteBarrierCallback( void *data ) +{ + CRServerBarrier *barrier = (CRServerBarrier *) data; + crFree(barrier->waiting); + crFree(barrier); +} + + +static void deleteContextInfoCallback( void *data ) +{ + CRContextInfo *c = (CRContextInfo *) data; + crStateDestroyContext(c->pContext); + if (c->CreateInfo.pszDpyName) + crFree(c->CreateInfo.pszDpyName); + crFree(c); +} + +static void deleteMuralInfoCallback( void *data ) +{ + CRMuralInfo *m = (CRMuralInfo *) data; + if (m->spuWindow != CR_RENDER_DEFAULT_WINDOW_ID) /* <- do not do term for default mural as it does not contain any info to be freed, + * and renderspu will destroy it up itself*/ + { + crServerMuralTerm(m); + } + crFree(m); +} + +static int crVBoxServerCrCmdDisablePostProcess(VBOXCRCMDCTL_HGCMENABLE_DATA *pData); + +static void crServerTearDown( void ) +{ + GLint i; + CRClientNode *pNode, *pNext; + GLboolean fOldEnableDiff; + GLboolean fContextsDeleted = GL_FALSE; + + /* avoid a race condition */ + if (tearingdown) + return; + + tearingdown = 1; + + if (cr_server.fCrCmdEnabled) + { + VBOXCRCMDCTL_HGCMENABLE_DATA EnableData; + /* crVBoxServerHgcmEnable will erase the DisableData, preserve it here */ + VBOXCRCMDCTL_HGCMDISABLE_DATA DisableData = cr_server.DisableData; + int rc; + + CRASSERT(DisableData.pfnNotifyTerm); + rc = DisableData.pfnNotifyTerm(DisableData.hNotifyTerm, &EnableData); + if (!RT_SUCCESS(rc)) + { + WARN(("pfnNotifyTerm failed %d", rc)); + return; + } + + crVBoxServerCrCmdDisablePostProcess(&EnableData); + fContextsDeleted = GL_TRUE; + + CRASSERT(DisableData.pfnNotifyTermDone); + DisableData.pfnNotifyTermDone(DisableData.hNotifyTerm); + + Assert(!cr_server.fCrCmdEnabled); + } + + crStateSetCurrent( NULL ); + + cr_server.curClient = NULL; + cr_server.run_queue = NULL; + + crFree( cr_server.overlap_intens ); + cr_server.overlap_intens = NULL; + + /* needed to make sure window dummy mural not get created on mural destruction + * and generally this should be zeroed up */ + cr_server.currentCtxInfo = NULL; + cr_server.currentWindow = -1; + cr_server.currentNativeWindow = 0; + cr_server.currentMural = NULL; + + if (!fContextsDeleted) + { +#ifndef VBOX_WITH_CR_DISPLAY_LISTS + /* sync our state with renderspu, + * do it before mural & context deletion to avoid deleting currently set murals/contexts*/ + cr_server.head_spu->dispatch_table.MakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); +#endif + } + + /* Deallocate all semaphores */ + crFreeHashtable(cr_server.semaphores, crFree); + cr_server.semaphores = NULL; + + /* Deallocate all barriers */ + crFreeHashtable(cr_server.barriers, DeleteBarrierCallback); + cr_server.barriers = NULL; + +#if 0 /** @todo @bugref{8662} -- can trigger SEGFAULTs during savestate */ + /* Free all context info */ + crFreeHashtable(cr_server.contextTable, deleteContextInfoCallback); +#endif + + /* synchronize with reality */ + if (!fContextsDeleted) + { + fOldEnableDiff = crStateEnableDiffOnMakeCurrent(GL_FALSE); + if(cr_server.MainContextInfo.pContext) + crStateMakeCurrent(cr_server.MainContextInfo.pContext); + crStateEnableDiffOnMakeCurrent(fOldEnableDiff); + } + + /* Free vertex programs */ + crFreeHashtable(cr_server.programTable, crFree); + + /* Free murals */ + crFreeHashtable(cr_server.muralTable, deleteMuralInfoCallback); + + CrPMgrTerm(); + + if (CrBltIsInitialized(&cr_server.Blitter)) + { + CrBltTerm(&cr_server.Blitter); + } + + /* Free dummy murals */ + crFreeHashtable(cr_server.dummyMuralTable, deleteMuralInfoCallback); + + for (i = 0; i < cr_server.numClients; i++) { + if (cr_server.clients[i]) { + CRConnection *conn = cr_server.clients[i]->conn; + crNetFreeConnection(conn); + crFree(cr_server.clients[i]); + } + } + cr_server.numClients = 0; + + pNode = cr_server.pCleanupClient; + while (pNode) + { + pNext=pNode->next; + crFree(pNode->pClient); + crFree(pNode); + pNode=pNext; + } + cr_server.pCleanupClient = NULL; + + if (crServerRpwIsInitialized(&cr_server.RpwWorker)) + { + crServerRpwTerm(&cr_server.RpwWorker); + } + +#if 1 + /* disable these two lines if trying to get stack traces with valgrind */ + crSPUUnloadChain(cr_server.head_spu); + cr_server.head_spu = NULL; +#endif + + crStateDestroy(); + + crNetTearDown(); + + VBoxVrListClear(&cr_server.RootVr); + + VBoxVrTerm(); + + RTSemEventDestroy(cr_server.hCalloutCompletionEvent); +} + +static void crServerClose( unsigned int id ) +{ + crError( "Client disconnected!" ); + (void) id; +} + +static void crServerCleanup( int sigio ) +{ + crServerTearDown(); + + tearingdown = 0; +} + + +void +crServerSetPort(int port) +{ + cr_server.tcpip_port = port; +} + + + +static void +crPrintHelp(void) +{ + printf("Usage: crserver [OPTIONS]\n"); + printf("Options:\n"); + printf(" -mothership URL Specifies URL for contacting the mothership.\n"); + printf(" URL is of the form [protocol://]hostname[:port]\n"); + printf(" -port N Specifies the port number this server will listen to.\n"); + printf(" -help Prints this information.\n"); +} + + +/** + * Do CRServer initializations. After this, we can begin servicing clients. + */ +void +crServerInit(int argc, char *argv[]) +{ + int i; + const char*env; + char *mothership = NULL; + CRMuralInfo *defaultMural; + int rc = VBoxVrInit(); + if (!RT_SUCCESS(rc)) + { + crWarning("VBoxVrInit failed, rc %d", rc); + return; + } + + for (i = 1 ; i < argc ; i++) + { + if (!crStrcmp( argv[i], "-mothership" )) + { + if (i == argc - 1) + { + crError( "-mothership requires an argument" ); + } + mothership = argv[i+1]; + i++; + } + else if (!crStrcmp( argv[i], "-port" )) + { + /* This is the port on which we'll accept client connections */ + if (i == argc - 1) + { + crError( "-port requires an argument" ); + } + cr_server.tcpip_port = crStrToInt(argv[i+1]); + i++; + } + else if (!crStrcmp( argv[i], "-vncmode" )) + { + cr_server.vncMode = 1; + } + else if (!crStrcmp( argv[i], "-help" )) + { + crPrintHelp(); + exit(0); + } + } + + signal( SIGTERM, crServerCleanup ); + signal( SIGINT, crServerCleanup ); +#ifndef WINDOWS + signal( SIGPIPE, SIG_IGN ); +#endif + +#if DEBUG_FP_EXCEPTIONS + { + fpu_control_t mask; + _FPU_GETCW(mask); + mask &= ~(_FPU_MASK_IM | _FPU_MASK_DM | _FPU_MASK_ZM + | _FPU_MASK_OM | _FPU_MASK_UM); + _FPU_SETCW(mask); + } +#endif + + cr_server.fCrCmdEnabled = GL_FALSE; + cr_server.fProcessingPendedCommands = GL_FALSE; + CrHTableCreate(&cr_server.clientTable, CR_MAX_CLIENTS); + + cr_server.bUseMultipleContexts = (crGetenv( "CR_SERVER_ENABLE_MULTIPLE_CONTEXTS" ) != NULL); + + if (cr_server.bUseMultipleContexts) + { + crInfo("Info: using multiple contexts!"); + crDebug("Debug: using multiple contexts!"); + } + + cr_server.firstCallCreateContext = GL_TRUE; + cr_server.firstCallMakeCurrent = GL_TRUE; + cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE; + + /* + * Create default mural info and hash table. + */ + cr_server.muralTable = crAllocHashtable(); + defaultMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); + defaultMural->spuWindow = CR_RENDER_DEFAULT_WINDOW_ID; + crHashtableAdd(cr_server.muralTable, 0, defaultMural); + + cr_server.programTable = crAllocHashtable(); + + crNetInit(crServerRecv, crServerClose); + crStateInit(); + + crServerSetVBoxConfiguration(); + + crStateLimitsInit( &(cr_server.limits) ); + + /* + * Default context + */ + cr_server.contextTable = crAllocHashtable(); + cr_server.curClient->currentCtxInfo = &cr_server.MainContextInfo; + + cr_server.dummyMuralTable = crAllocHashtable(); + + CrPMgrInit(); + + cr_server.fRootVrOn = GL_FALSE; + VBoxVrListInit(&cr_server.RootVr); + crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint)); + + crMemset(&cr_server.RpwWorker, 0, sizeof (cr_server.RpwWorker)); + + env = crGetenv("CR_SERVER_BFB"); + if (env) + { + cr_server.fBlitterMode = env[0] - '0'; + } + else + { + cr_server.fBlitterMode = CR_SERVER_BFB_DISABLED; + } + crMemset(&cr_server.Blitter, 0, sizeof (cr_server.Blitter)); + + crServerInitDispatch(); + crServerInitTmpCtxDispatch(); + crStateDiffAPI( &(cr_server.head_spu->dispatch_table) ); + +#ifdef VBOX_WITH_CRSERVER_DUMPER + crMemset(&cr_server.Recorder, 0, sizeof (cr_server.Recorder)); + crMemset(&cr_server.RecorderBlitter, 0, sizeof (cr_server.RecorderBlitter)); + crMemset(&cr_server.DbgPrintDumper, 0, sizeof (cr_server.DbgPrintDumper)); + crMemset(&cr_server.HtmlDumper, 0, sizeof (cr_server.HtmlDumper)); + cr_server.pDumper = NULL; +#endif + + crUnpackSetReturnPointer( &(cr_server.return_ptr) ); + crUnpackSetWritebackPointer( &(cr_server.writeback_ptr) ); + + cr_server.barriers = crAllocHashtable(); + cr_server.semaphores = crAllocHashtable(); +} + +void crVBoxServerTearDown(void) +{ + crServerTearDown(); +} + +/** + * Do CRServer initializations. After this, we can begin servicing clients. + */ +GLboolean crVBoxServerInit(void) +{ + CRMuralInfo *defaultMural; + const char*env; + int rc = VBoxVrInit(); + if (!RT_SUCCESS(rc)) + { + crWarning("VBoxVrInit failed, rc %d", rc); + return GL_FALSE; + } + +#if DEBUG_FP_EXCEPTIONS + { + fpu_control_t mask; + _FPU_GETCW(mask); + mask &= ~(_FPU_MASK_IM | _FPU_MASK_DM | _FPU_MASK_ZM + | _FPU_MASK_OM | _FPU_MASK_UM); + _FPU_SETCW(mask); + } +#endif + + cr_server.fCrCmdEnabled = GL_FALSE; + cr_server.fProcessingPendedCommands = GL_FALSE; + CrHTableCreate(&cr_server.clientTable, CR_MAX_CLIENTS); + + cr_server.bUseMultipleContexts = (crGetenv( "CR_SERVER_ENABLE_MULTIPLE_CONTEXTS" ) != NULL); + + if (cr_server.bUseMultipleContexts) + { + crInfo("Info: using multiple contexts!"); + crDebug("Debug: using multiple contexts!"); + } + + crNetInit(crServerRecv, crServerClose); + + cr_server.firstCallCreateContext = GL_TRUE; + cr_server.firstCallMakeCurrent = GL_TRUE; + + cr_server.bIsInLoadingState = GL_FALSE; + cr_server.bIsInSavingState = GL_FALSE; + cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE; + + cr_server.pCleanupClient = NULL; + + rc = RTSemEventCreate(&cr_server.hCalloutCompletionEvent); + if (!RT_SUCCESS(rc)) + { + WARN(("RTSemEventCreate failed %d", rc)); + return GL_FALSE; + } + + /* + * Create default mural info and hash table. + */ + cr_server.muralTable = crAllocHashtable(); + defaultMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); + defaultMural->spuWindow = CR_RENDER_DEFAULT_WINDOW_ID; + crHashtableAdd(cr_server.muralTable, 0, defaultMural); + + cr_server.programTable = crAllocHashtable(); + + crStateInit(); + + crStateLimitsInit( &(cr_server.limits) ); + + cr_server.barriers = crAllocHashtable(); + cr_server.semaphores = crAllocHashtable(); + + crUnpackSetReturnPointer( &(cr_server.return_ptr) ); + crUnpackSetWritebackPointer( &(cr_server.writeback_ptr) ); + + /* + * Default context + */ + cr_server.contextTable = crAllocHashtable(); + + cr_server.dummyMuralTable = crAllocHashtable(); + + CrPMgrInit(); + + cr_server.fRootVrOn = GL_FALSE; + VBoxVrListInit(&cr_server.RootVr); + crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint)); + + crMemset(&cr_server.RpwWorker, 0, sizeof (cr_server.RpwWorker)); + + env = crGetenv("CR_SERVER_BFB"); + if (env) + { + cr_server.fBlitterMode = env[0] - '0'; + } + else + { + cr_server.fBlitterMode = CR_SERVER_BFB_DISABLED; + } + crMemset(&cr_server.Blitter, 0, sizeof (cr_server.Blitter)); + + crServerSetVBoxConfigurationHGCM(); + + if (!cr_server.head_spu) + { + crStateDestroy(); + return GL_FALSE; + } + + crServerInitDispatch(); + crServerInitTmpCtxDispatch(); + crStateDiffAPI( &(cr_server.head_spu->dispatch_table) ); + +#ifdef VBOX_WITH_CRSERVER_DUMPER + crMemset(&cr_server.Recorder, 0, sizeof (cr_server.Recorder)); + crMemset(&cr_server.RecorderBlitter, 0, sizeof (cr_server.RecorderBlitter)); + crMemset(&cr_server.DbgPrintDumper, 0, sizeof (cr_server.DbgPrintDumper)); + crMemset(&cr_server.HtmlDumper, 0, sizeof (cr_server.HtmlDumper)); + cr_server.pDumper = NULL; +#endif + + /*Check for PBO support*/ + if (crStateGetCurrent()->extensions.ARB_pixel_buffer_object) + { + cr_server.bUsePBOForReadback=GL_TRUE; + } + + return GL_TRUE; +} + +static int32_t crVBoxServerAddClientObj(uint32_t u32ClientID, CRClient **ppNewClient) +{ + CRClient *newClient; + + if (cr_server.numClients>=CR_MAX_CLIENTS) + { + if (ppNewClient) + *ppNewClient = NULL; + return VERR_MAX_THRDS_REACHED; + } + + newClient = (CRClient *) crCalloc(sizeof(CRClient)); + crDebug("crServer: AddClient u32ClientID=%d", u32ClientID); + + newClient->spu_id = 0; + newClient->currentCtxInfo = &cr_server.MainContextInfo; + newClient->currentContextNumber = -1; + newClient->conn = crNetAcceptClient(cr_server.protocol, NULL, + cr_server.tcpip_port, + cr_server.mtu, 0); + newClient->conn->u32ClientID = u32ClientID; + + cr_server.clients[cr_server.numClients++] = newClient; + + crServerAddToRunQueue(newClient); + + if (ppNewClient) + *ppNewClient = newClient; + + return VINF_SUCCESS; +} + +int32_t crVBoxServerAddClient(uint32_t u32ClientID) +{ + CRClient *newClient; + + if (cr_server.numClients>=CR_MAX_CLIENTS) + { + return VERR_MAX_THRDS_REACHED; + } + + newClient = (CRClient *) crCalloc(sizeof(CRClient)); + crDebug("crServer: AddClient u32ClientID=%d", u32ClientID); + + newClient->spu_id = 0; + newClient->currentCtxInfo = &cr_server.MainContextInfo; + newClient->currentContextNumber = -1; + newClient->conn = crNetAcceptClient(cr_server.protocol, NULL, + cr_server.tcpip_port, + cr_server.mtu, 0); + newClient->conn->u32ClientID = u32ClientID; + + cr_server.clients[cr_server.numClients++] = newClient; + + crServerAddToRunQueue(newClient); + + return VINF_SUCCESS; +} + +static void crVBoxServerRemoveClientObj(CRClient *pClient) +{ +#ifdef VBOX_WITH_CRHGSMI + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); +#endif + + /* Disconnect the client */ + pClient->conn->Disconnect(pClient->conn); + + /* Let server clear client from the queue */ + crServerDeleteClient(pClient); +} + +static void crVBoxServerRemoveAllClients() +{ + int32_t i; + for (i = cr_server.numClients - 1; i >= 0; --i) + { + Assert(cr_server.clients[i]); + crVBoxServerRemoveClientObj(cr_server.clients[i]); + } +} + +void crVBoxServerRemoveClient(uint32_t u32ClientID) +{ + CRClient *pClient=NULL; + int32_t i; + + crDebug("crServer: RemoveClient u32ClientID=%d", u32ClientID); + + for (i = 0; i < cr_server.numClients; i++) + { + if (cr_server.clients[i] && cr_server.clients[i]->conn + && cr_server.clients[i]->conn->u32ClientID==u32ClientID) + { + pClient = cr_server.clients[i]; + break; + } + } + //if (!pClient) return VERR_INVALID_PARAMETER; + if (!pClient) + { + WARN(("Invalid client id %u passed to crVBoxServerRemoveClient", u32ClientID)); + return; + } + + crVBoxServerRemoveClientObj(pClient); +} + +static void crVBoxServerInternalClientWriteRead(CRClient *pClient) +{ +#ifdef VBOXCR_LOGFPS + uint64_t tstart, tend; +#endif + + /*crDebug("=>crServer: ClientWrite u32ClientID=%d", u32ClientID);*/ + + +#ifdef VBOXCR_LOGFPS + tstart = RTTimeNanoTS(); +#endif + + /* This should be setup already */ + CRASSERT(pClient->conn->pBuffer); + CRASSERT(pClient->conn->cbBuffer); +#ifdef VBOX_WITH_CRHGSMI + CRVBOXHGSMI_CMDDATA_ASSERT_CONSISTENT(&pClient->conn->CmdData); +#endif + + if ( +#ifdef VBOX_WITH_CRHGSMI + !CRVBOXHGSMI_CMDDATA_IS_SET(&pClient->conn->CmdData) && +#endif + cr_server.run_queue->client != pClient + && crServerClientInBeginEnd(cr_server.run_queue->client)) + { + crDebug("crServer: client %d blocked, allow_redir_ptr = 0", pClient->conn->u32ClientID); + pClient->conn->allow_redir_ptr = 0; + } + else + { + pClient->conn->allow_redir_ptr = 1; + } + + crNetRecv(); + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + crServerServiceClients(); + crStateResetCurrentPointers(&cr_server.current); + +#ifndef VBOX_WITH_CRHGSMI + CRASSERT(!pClient->conn->allow_redir_ptr || crNetNumMessages(pClient->conn)==0); +#endif + +#ifdef VBOXCR_LOGFPS + tend = RTTimeNanoTS(); + pClient->timeUsed += tend-tstart; +#endif + /*crDebug("<=crServer: ClientWrite u32ClientID=%d", u32ClientID);*/ +} + + +int32_t crVBoxServerClientWrite(uint32_t u32ClientID, uint8_t *pBuffer, uint32_t cbBuffer) +{ + CRClient *pClient=NULL; + int32_t rc = crVBoxServerClientGet(u32ClientID, &pClient); + + if (RT_FAILURE(rc)) + return rc; + + CRASSERT(pBuffer); + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; +#ifdef VBOX_WITH_CRHGSMI + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); +#endif + + crVBoxServerInternalClientWriteRead(pClient); + + return VINF_SUCCESS; +} + +int32_t crVBoxServerInternalClientRead(CRClient *pClient, uint8_t *pBuffer, uint32_t *pcbBuffer) +{ + if (pClient->conn->cbHostBuffer > *pcbBuffer) + { + crDebug("crServer: [%lx] ClientRead u32ClientID=%d FAIL, host buffer too small %d of %d", + crThreadID(), pClient->conn->u32ClientID, *pcbBuffer, pClient->conn->cbHostBuffer); + + /* Return the size of needed buffer */ + *pcbBuffer = pClient->conn->cbHostBuffer; + + return VERR_BUFFER_OVERFLOW; + } + + *pcbBuffer = pClient->conn->cbHostBuffer; + + if (*pcbBuffer) + { + CRASSERT(pClient->conn->pHostBuffer); + + crMemcpy(pBuffer, pClient->conn->pHostBuffer, *pcbBuffer); + pClient->conn->cbHostBuffer = 0; + } + + return VINF_SUCCESS; +} + +int32_t crVBoxServerClientRead(uint32_t u32ClientID, uint8_t *pBuffer, uint32_t *pcbBuffer) +{ + CRClient *pClient=NULL; + int32_t rc = crVBoxServerClientGet(u32ClientID, &pClient); + + if (RT_FAILURE(rc)) + return rc; + +#ifdef VBOX_WITH_CRHGSMI + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); +#endif + + return crVBoxServerInternalClientRead(pClient, pBuffer, pcbBuffer); +} + +extern DECLEXPORT(int32_t) crVBoxServerClientGetCapsLegacy(uint32_t u32ClientID, uint32_t *pu32Caps) +{ + uint32_t u32Caps = cr_server.u32Caps; + u32Caps &= ~CR_VBOX_CAP_CMDVBVA; + *pu32Caps = u32Caps; + return VINF_SUCCESS; +} + +extern DECLEXPORT(int32_t) crVBoxServerClientGetCapsNew(uint32_t u32ClientID, CR_CAPS_INFO *pInfo) +{ + pInfo->u32Caps = cr_server.u32Caps; + pInfo->u32CmdVbvaVersion = CR_CMDVBVA_VERSION; + return VINF_SUCCESS; +} + +static int32_t crVBoxServerClientObjSetVersion(CRClient *pClient, uint32_t vMajor, uint32_t vMinor) +{ + pClient->conn->vMajor = vMajor; + pClient->conn->vMinor = vMinor; + + if (vMajor != CR_PROTOCOL_VERSION_MAJOR + || vMinor != CR_PROTOCOL_VERSION_MINOR) + return VERR_NOT_SUPPORTED; + return VINF_SUCCESS; +} + +int32_t crVBoxServerClientSetVersion(uint32_t u32ClientID, uint32_t vMajor, uint32_t vMinor) +{ + CRClient *pClient=NULL; + int32_t i; + + for (i = 0; i < cr_server.numClients; i++) + { + if (cr_server.clients[i] && cr_server.clients[i]->conn + && cr_server.clients[i]->conn->u32ClientID==u32ClientID) + { + pClient = cr_server.clients[i]; + break; + } + } + if (!pClient) return VERR_INVALID_PARAMETER; + + return crVBoxServerClientObjSetVersion(pClient, vMajor, vMinor); +} + +static int32_t crVBoxServerClientObjSetPID(CRClient *pClient, uint64_t pid) +{ + pClient->pid = pid; + + return VINF_SUCCESS; +} + +int32_t crVBoxServerClientSetPID(uint32_t u32ClientID, uint64_t pid) +{ + CRClient *pClient=NULL; + int32_t i; + + for (i = 0; i < cr_server.numClients; i++) + { + if (cr_server.clients[i] && cr_server.clients[i]->conn + && cr_server.clients[i]->conn->u32ClientID==u32ClientID) + { + pClient = cr_server.clients[i]; + break; + } + } + if (!pClient) return VERR_INVALID_PARAMETER; + + return crVBoxServerClientObjSetPID(pClient, pid); +} + +int +CRServerMain(int argc, char *argv[]) +{ + crServerInit(argc, argv); + + crServerSerializeRemoteStreams(); + + crServerTearDown(); + + tearingdown = 0; + + return 0; +} + +static void crVBoxServerSaveMuralCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMI = (CRMuralInfo*) data1; + PSSMHANDLE pSSM = (PSSMHANDLE) data2; + int32_t rc; + + CRASSERT(pMI && pSSM); + + /* Don't store default mural */ + if (!key) return; + + rc = SSMR3PutMem(pSSM, &key, sizeof(key)); + CRASSERT(rc == VINF_SUCCESS); + + rc = SSMR3PutMem(pSSM, pMI, RT_UOFFSETOF(CRMuralInfo, CreateInfo)); + CRASSERT(rc == VINF_SUCCESS); + + if (pMI->pVisibleRects) + { + rc = SSMR3PutMem(pSSM, pMI->pVisibleRects, 4*sizeof(GLint)*pMI->cVisibleRects); + } + + rc = SSMR3PutMem(pSSM, pMI->ctxUsage, sizeof (pMI->ctxUsage)); + CRASSERT(rc == VINF_SUCCESS); +} + +/** @todo add hashtable walker with result info and intermediate abort */ +static void crVBoxServerSaveCreateInfoCB(unsigned long key, void *data1, void *data2) +{ + CRCreateInfo_t *pCreateInfo = (CRCreateInfo_t *)data1; + PSSMHANDLE pSSM = (PSSMHANDLE) data2; + int32_t rc; + + CRASSERT(pCreateInfo && pSSM); + + /* Don't store default mural create info */ + if (!key) return; + + rc = SSMR3PutMem(pSSM, &key, sizeof(key)); + CRASSERT(rc == VINF_SUCCESS); + + rc = SSMR3PutMem(pSSM, pCreateInfo, sizeof(*pCreateInfo)); + CRASSERT(rc == VINF_SUCCESS); + + if (pCreateInfo->pszDpyName) + { + rc = SSMR3PutStrZ(pSSM, pCreateInfo->pszDpyName); + CRASSERT(rc == VINF_SUCCESS); + } +} + +static void crVBoxServerSaveCreateInfoFromMuralInfoCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMural = (CRMuralInfo *)data1; + CRCreateInfo_t CreateInfo; + CreateInfo.pszDpyName = pMural->CreateInfo.pszDpyName; + CreateInfo.visualBits = pMural->CreateInfo.requestedVisualBits; + CreateInfo.externalID = pMural->CreateInfo.externalID; + crVBoxServerSaveCreateInfoCB(key, &CreateInfo, data2); +} + +static void crVBoxServerSaveCreateInfoFromCtxInfoCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *)data1; + CRCreateInfo_t CreateInfo; + CreateInfo.pszDpyName = pContextInfo->CreateInfo.pszDpyName; + CreateInfo.visualBits = pContextInfo->CreateInfo.requestedVisualBits; + /* saved state contains internal id */ + CreateInfo.externalID = pContextInfo->pContext->id; + crVBoxServerSaveCreateInfoCB(key, &CreateInfo, data2); +} + +static void crVBoxServerSyncTextureCB(unsigned long key, void *data1, void *data2) +{ + CRTextureObj *pTexture = (CRTextureObj *) data1; + CRContext *pContext = (CRContext *) data2; + + CRASSERT(pTexture && pContext); + crStateTextureObjectDiff(pContext, NULL, NULL, pTexture, GL_TRUE); +} + +typedef struct CRVBOX_SAVE_STATE_GLOBAL +{ + /* context id -> mural association + * on context data save, each context will be made current with the corresponding mural from this table + * thus saving the mural front & back buffer data */ + CRHashTable *contextMuralTable; + /* mural id -> context info + * for murals that do not have associated context in contextMuralTable + * we still need to save*/ + CRHashTable *additionalMuralContextTable; + + PSSMHANDLE pSSM; + + int rc; +} CRVBOX_SAVE_STATE_GLOBAL, *PCRVBOX_SAVE_STATE_GLOBAL; + + +typedef struct CRVBOX_CTXWND_CTXWALKER_CB +{ + PCRVBOX_SAVE_STATE_GLOBAL pGlobal; + CRHashTable *usedMuralTable; + GLuint cAdditionalMurals; +} CRVBOX_CTXWND_CTXWALKER_CB, *PCRVBOX_CTXWND_CTXWALKER_CB; + +static void crVBoxServerBuildAdditionalWindowContextMapCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo * pMural = (CRMuralInfo *) data1; + PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2; + CRContextInfo *pContextInfo = NULL; + + if (!pMural->CreateInfo.externalID) + { + CRASSERT(!key); + return; + } + + if (crHashtableSearch(pData->usedMuralTable, pMural->CreateInfo.externalID)) + { + Assert(crHashtableGetDataKey(pData->pGlobal->contextMuralTable, pMural, NULL)); + return; + } + + Assert(!crHashtableGetDataKey(pData->pGlobal->contextMuralTable, pMural, NULL)); + + if (cr_server.MainContextInfo.CreateInfo.realVisualBits == pMural->CreateInfo.realVisualBits) + { + pContextInfo = &cr_server.MainContextInfo; + } + else + { + crWarning("different visual bits not implemented!"); + pContextInfo = &cr_server.MainContextInfo; + } + + crHashtableAdd(pData->pGlobal->additionalMuralContextTable, pMural->CreateInfo.externalID, pContextInfo); +} + + +typedef struct CRVBOX_CTXWND_WNDWALKER_CB +{ + PCRVBOX_SAVE_STATE_GLOBAL pGlobal; + CRHashTable *usedMuralTable; + CRContextInfo *pContextInfo; + CRMuralInfo * pMural; +} CRVBOX_CTXWND_WNDWALKER_CB, *PCRVBOX_CTXWND_WNDWALKER_CB; + +static void crVBoxServerBuildContextWindowMapWindowWalkerCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo * pMural = (CRMuralInfo *) data1; + PCRVBOX_CTXWND_WNDWALKER_CB pData = (PCRVBOX_CTXWND_WNDWALKER_CB)data2; + + Assert(pData->pMural != pMural); + Assert(pData->pContextInfo); + + if (pData->pMural) + return; + + if (!pMural->CreateInfo.externalID) + { + CRASSERT(!key); + return; + } + + if (!CR_STATE_SHAREDOBJ_USAGE_IS_SET(pMural, pData->pContextInfo->pContext)) + return; + + if (crHashtableSearch(pData->usedMuralTable, pMural->CreateInfo.externalID)) + return; + + CRASSERT(pMural->CreateInfo.realVisualBits == pData->pContextInfo->CreateInfo.realVisualBits); + pData->pMural = pMural; +} + +static void crVBoxServerBuildContextUsedWindowMapCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *)data1; + PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2; + + if (!pContextInfo->currentMural) + return; + + crHashtableAdd(pData->pGlobal->contextMuralTable, pContextInfo->CreateInfo.externalID, pContextInfo->currentMural); + crHashtableAdd(pData->usedMuralTable, pContextInfo->currentMural->CreateInfo.externalID, pContextInfo->currentMural); +} + +CRMuralInfo * crServerGetDummyMural(GLint visualBits) +{ + CRMuralInfo * pMural = (CRMuralInfo *)crHashtableSearch(cr_server.dummyMuralTable, visualBits); + if (!pMural) + { + GLint id; + pMural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); + if (!pMural) + { + crWarning("crCalloc failed!"); + return NULL; + } + id = crServerMuralInit(pMural, GL_FALSE, visualBits, 0); + if (id < 0) + { + crWarning("crServerMuralInit failed!"); + crFree(pMural); + return NULL; + } + + crHashtableAdd(cr_server.dummyMuralTable, visualBits, pMural); + } + + return pMural; +} + +static void crVBoxServerBuildContextUnusedWindowMapCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *)data1; + PCRVBOX_CTXWND_CTXWALKER_CB pData = (PCRVBOX_CTXWND_CTXWALKER_CB)data2; + CRMuralInfo * pMural = NULL; + + if (pContextInfo->currentMural) + return; + + Assert(crHashtableNumElements(pData->pGlobal->contextMuralTable) <= crHashtableNumElements(cr_server.muralTable) - 1); + if (crHashtableNumElements(pData->pGlobal->contextMuralTable) < crHashtableNumElements(cr_server.muralTable) - 1) + { + CRVBOX_CTXWND_WNDWALKER_CB MuralData; + MuralData.pGlobal = pData->pGlobal; + MuralData.usedMuralTable = pData->usedMuralTable; + MuralData.pContextInfo = pContextInfo; + MuralData.pMural = NULL; + + crHashtableWalk(cr_server.muralTable, crVBoxServerBuildContextWindowMapWindowWalkerCB, &MuralData); + + pMural = MuralData.pMural; + + } + + if (!pMural) + { + pMural = crServerGetDummyMural(pContextInfo->CreateInfo.realVisualBits); + if (!pMural) + { + crWarning("crServerGetDummyMural failed"); + return; + } + } + else + { + crHashtableAdd(pData->usedMuralTable, pMural->CreateInfo.externalID, pMural); + ++pData->cAdditionalMurals; + } + + crHashtableAdd(pData->pGlobal->contextMuralTable, pContextInfo->CreateInfo.externalID, pMural); +} + +static void crVBoxServerBuildSaveStateGlobal(PCRVBOX_SAVE_STATE_GLOBAL pGlobal) +{ + CRVBOX_CTXWND_CTXWALKER_CB Data; + GLuint cMurals; + pGlobal->contextMuralTable = crAllocHashtable(); + pGlobal->additionalMuralContextTable = crAllocHashtable(); + /* 1. go through all contexts and match all having currentMural set */ + Data.pGlobal = pGlobal; + Data.usedMuralTable = crAllocHashtable(); + Data.cAdditionalMurals = 0; + crHashtableWalk(cr_server.contextTable, crVBoxServerBuildContextUsedWindowMapCB, &Data); + + cMurals = crHashtableNumElements(pGlobal->contextMuralTable); + CRASSERT(cMurals <= crHashtableNumElements(cr_server.contextTable)); + CRASSERT(cMurals <= crHashtableNumElements(cr_server.muralTable) - 1); + CRASSERT(cMurals == crHashtableNumElements(Data.usedMuralTable)); + if (cMurals < crHashtableNumElements(cr_server.contextTable)) + { + Data.cAdditionalMurals = 0; + crHashtableWalk(cr_server.contextTable, crVBoxServerBuildContextUnusedWindowMapCB, &Data); + } + + CRASSERT(crHashtableNumElements(pGlobal->contextMuralTable) == crHashtableNumElements(cr_server.contextTable)); + CRASSERT(cMurals + Data.cAdditionalMurals <= crHashtableNumElements(cr_server.muralTable) - 1); + if (cMurals + Data.cAdditionalMurals < crHashtableNumElements(cr_server.muralTable) - 1) + { + crHashtableWalk(cr_server.muralTable, crVBoxServerBuildAdditionalWindowContextMapCB, &Data); + CRASSERT(cMurals + Data.cAdditionalMurals + crHashtableNumElements(pGlobal->additionalMuralContextTable) == crHashtableNumElements(cr_server.muralTable) - 1); + } + + crFreeHashtable(Data.usedMuralTable, NULL); +} + +static void crVBoxServerFBImageDataTerm(CRFBData *pData) +{ + GLuint i; + for (i = 0; i < pData->cElements; ++i) + { + CRFBDataElement * pEl = &pData->aElements[i]; + if (pEl->pvData) + { + crFree(pEl->pvData); + /* sanity */ + pEl->pvData = NULL; + } + } + pData->cElements = 0; +} + +static int crVBoxAddFBDataElement(CRFBData *pData, GLint idFBO, GLenum enmBuffer, GLint width, GLint height, GLenum enmFormat, GLenum enmType) +{ + CRFBDataElement *pEl; + + AssertCompile(sizeof (GLfloat) == 4); + AssertCompile(sizeof (GLuint) == 4); + + pEl = &pData->aElements[pData->cElements]; + pEl->idFBO = idFBO; + pEl->enmBuffer = enmBuffer; + pEl->posX = 0; + pEl->posY = 0; + pEl->width = width; + pEl->height = height; + pEl->enmFormat = enmFormat; + pEl->enmType = enmType; + pEl->cbData = width * height * 4; + + pEl->pvData = crCalloc(pEl->cbData); + if (!pEl->pvData) + { + crVBoxServerFBImageDataTerm(pData); + crWarning(": crCalloc failed"); + return VERR_NO_MEMORY; + } + + ++pData->cElements; + + return VINF_SUCCESS; +} + +/* Add framebuffer image elements arrording to SSM version. Please refer to cr_version.h + * in order to distinguish between versions. */ +static int crVBoxServerFBImageDataInitEx(CRFBData *pData, CRContextInfo *pCtxInfo, CRMuralInfo *pMural, GLboolean fWrite, uint32_t version, GLuint overrideWidth, GLuint overrideHeight) +{ + CRContext *pContext; + GLuint i; + GLfloat *pF; + GLuint width; + GLuint height; + int rc; + + crMemset(pData, 0, sizeof (*pData)); + + pContext = pCtxInfo->pContext; + + /* the version should be always actual when we do reads, + * i.e. it could differ on writes when snapshot is getting loaded */ + CRASSERT(fWrite || version == SHCROGL_SSM_VERSION); + + width = overrideWidth ? overrideWidth : pMural->width; + height = overrideHeight ? overrideHeight : pMural->height; + + if (!width || !height) + return VINF_SUCCESS; + + if (pMural) + { + if (fWrite) + { + if (!pContext->framebufferobject.drawFB) + pData->idOverrrideFBO = CR_SERVER_FBO_FOR_IDX(pMural, pMural->iCurDrawBuffer); + } + else + { + if (!pContext->framebufferobject.readFB) + pData->idOverrrideFBO = CR_SERVER_FBO_FOR_IDX(pMural, pMural->iCurReadBuffer); + } + } + + pData->u32Version = version; + + pData->cElements = 0; + + rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0, + pData->aElements[1].idFBO ? GL_COLOR_ATTACHMENT0 : GL_FRONT, width, height, GL_RGBA, GL_UNSIGNED_BYTE); + AssertReturn(rc == VINF_SUCCESS, rc); + + /* There is a lot of code that assumes we have double buffering, just assert here to print a warning in the log + * so that we know that something irregular is going on. */ + CRASSERT(pCtxInfo->CreateInfo.requestedVisualBits & CR_DOUBLE_BIT); + + if (( pCtxInfo->CreateInfo.requestedVisualBits & CR_DOUBLE_BIT) + || version < SHCROGL_SSM_VERSION_WITH_SINGLE_DEPTH_STENCIL) /* <- Older version had a typo which lead to back always being used, + * no matter what the visual bits are. */ + { + rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_BB_IDX(pMural)] : 0, + pData->aElements[1].idFBO ? GL_COLOR_ATTACHMENT0 : GL_BACK, width, height, GL_RGBA, GL_UNSIGNED_BYTE); + AssertReturn(rc == VINF_SUCCESS, rc); + } + + if (version < SHCROGL_SSM_VERSION_WITH_SAVED_DEPTH_STENCIL_BUFFER) + return VINF_SUCCESS; + + if (version < SHCROGL_SSM_VERSION_WITH_SINGLE_DEPTH_STENCIL) + { + rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0, + pMural ? pMural->idDepthStencilRB : 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT); + AssertReturn(rc == VINF_SUCCESS, rc); + + /* Init to default depth value, just in case. "pData->cElements - 1" because we incremented counter in crVBoxAddFBDataElement(). */ + pF = (GLfloat*)pData->aElements[pData->cElements - 1].pvData; + for (i = 0; i < width * height; ++i) + pF[i] = 1.; + + rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0, + pMural ? pMural->idDepthStencilRB : 0, width, height, GL_STENCIL_INDEX, GL_UNSIGNED_INT); + AssertReturn(rc == VINF_SUCCESS, rc); + + return VINF_SUCCESS; + } + + if (version < SHCROGL_SSM_VERSION_WITH_SEPARATE_DEPTH_STENCIL_BUFFERS) + { + /* Use GL_DEPTH_STENCIL only in case if both CR_STENCIL_BIT and CR_DEPTH_BIT specified. */ + if ( (pCtxInfo->CreateInfo.requestedVisualBits & CR_STENCIL_BIT) + && (pCtxInfo->CreateInfo.requestedVisualBits & CR_DEPTH_BIT)) + { + rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0, 0, + width, height, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8); + AssertReturn(rc == VINF_SUCCESS, rc); + } + + return VINF_SUCCESS; + } + + /* Current SSM verion (SHCROGL_SSM_VERSION_WITH_SEPARATE_DEPTH_STENCIL_BUFFERS). */ + + if (pCtxInfo->CreateInfo.requestedVisualBits & CR_DEPTH_BIT) + { + rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0, + pMural ? pMural->idDepthStencilRB : 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT); + AssertReturn(rc == VINF_SUCCESS, rc); + + /* Init to default depth value, just in case. "pData->cElements - 1" because we incremented counter in crVBoxAddFBDataElement(). */ + pF = (GLfloat*)pData->aElements[pData->cElements - 1].pvData; + for (i = 0; i < width * height; ++i) + pF[i] = 1.; + } + + if (pCtxInfo->CreateInfo.requestedVisualBits & CR_STENCIL_BIT) + { + rc = crVBoxAddFBDataElement(pData, pMural && pMural->fRedirected ? pMural->aidFBOs[CR_SERVER_FBO_FB_IDX(pMural)] : 0, + pMural ? pMural->idDepthStencilRB : 0, width, height, GL_STENCIL_INDEX, GL_UNSIGNED_INT); + AssertReturn(rc == VINF_SUCCESS, rc); + } + + return VINF_SUCCESS; +} + +static int crVBoxServerSaveFBImage(PSSMHANDLE pSSM) +{ + CRContextInfo *pCtxInfo; + CRContext *pContext; + CRMuralInfo *pMural; + int32_t rc; + GLuint i; + struct + { + CRFBData data; + CRFBDataElement buffer[3]; /* CRFBData::aElements[1] + buffer[3] gives 4: back, front, depth and stencil */ + } Data; + + Assert(sizeof (Data) >= RT_UOFFSETOF(CRFBData, aElements[4])); + + pCtxInfo = cr_server.currentCtxInfo; + pContext = pCtxInfo->pContext; + pMural = pCtxInfo->currentMural; + + rc = crVBoxServerFBImageDataInitEx(&Data.data, pCtxInfo, pMural, GL_FALSE, SHCROGL_SSM_VERSION, 0, 0); + if (!RT_SUCCESS(rc)) + { + crWarning("crVBoxServerFBImageDataInit failed rc %d", rc); + return rc; + } + + rc = crStateAcquireFBImage(pContext, &Data.data); + AssertRCReturn(rc, rc); + + for (i = 0; i < Data.data.cElements; ++i) + { + CRFBDataElement * pEl = &Data.data.aElements[i]; + rc = SSMR3PutMem(pSSM, pEl->pvData, pEl->cbData); + AssertRCReturn(rc, rc); + } + + crVBoxServerFBImageDataTerm(&Data.data); + + return VINF_SUCCESS; +} + +#define CRSERVER_ASSERTRC_RETURN_VOID(_rc) do { \ + if(!RT_SUCCESS((_rc))) { \ + AssertFailed(); \ + return; \ + } \ + } while (0) + +static void crVBoxServerSaveAdditionalMuralsCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *) data1; + PCRVBOX_SAVE_STATE_GLOBAL pData = (PCRVBOX_SAVE_STATE_GLOBAL)data2; + CRMuralInfo *pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, key); + PSSMHANDLE pSSM = pData->pSSM; + CRbitvalue initialCtxUsage[CR_MAX_BITARRAY]; + CRMuralInfo *pInitialCurMural = pContextInfo->currentMural; + + crMemcpy(initialCtxUsage, pMural->ctxUsage, sizeof (initialCtxUsage)); + + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + pData->rc = SSMR3PutMem(pSSM, &key, sizeof(key)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + pData->rc = SSMR3PutMem(pSSM, &pContextInfo->CreateInfo.externalID, sizeof(pContextInfo->CreateInfo.externalID)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + crServerPerformMakeCurrent(pMural, pContextInfo); + + pData->rc = crVBoxServerSaveFBImage(pSSM); + + /* restore the reference data, we synchronize it with the HW state in a later crServerPerformMakeCurrent call */ + crMemcpy(pMural->ctxUsage, initialCtxUsage, sizeof (initialCtxUsage)); + pContextInfo->currentMural = pInitialCurMural; + + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); +} + +static void crVBoxServerSaveContextStateCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *pContextInfo = (CRContextInfo *) data1; + CRContext *pContext = pContextInfo->pContext; + PCRVBOX_SAVE_STATE_GLOBAL pData = (PCRVBOX_SAVE_STATE_GLOBAL)data2; + PSSMHANDLE pSSM = pData->pSSM; + CRMuralInfo *pMural = (CRMuralInfo*)crHashtableSearch(pData->contextMuralTable, key); + CRMuralInfo *pContextCurrentMural = pContextInfo->currentMural; + const int32_t i32Dummy = 0; + + AssertCompile(sizeof (i32Dummy) == sizeof (pMural->CreateInfo.externalID)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + CRASSERT(pContext && pSSM); + CRASSERT(pMural); + CRASSERT(pMural->CreateInfo.externalID); + + /* We could have skipped saving the key and use similar callback to load context states back, + * but there's no guarantee we'd traverse hashtable in same order after loading. + */ + pData->rc = SSMR3PutMem(pSSM, &key, sizeof(key)); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + +#ifdef DEBUG_misha + { + unsigned long id; + if (!crHashtableGetDataKey(cr_server.contextTable, pContextInfo, &id)) + crWarning("No client id for server ctx %d", pContextInfo->CreateInfo.externalID); + else + CRASSERT(id == key); + } +#endif + +#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE + if (pContextInfo->currentMural + || crHashtableSearch(cr_server.muralTable, pMural->CreateInfo.externalID) /* <- this is not a dummy mural */ + ) + { + CRASSERT(pMural->CreateInfo.externalID); + CRASSERT(!crHashtableSearch(cr_server.dummyMuralTable, pMural->CreateInfo.externalID)); + pData->rc = SSMR3PutMem(pSSM, &pMural->CreateInfo.externalID, sizeof(pMural->CreateInfo.externalID)); + } + else + { + /* this is a dummy mural */ + CRASSERT(!pMural->width); + CRASSERT(!pMural->height); + CRASSERT(crHashtableSearch(cr_server.dummyMuralTable, pMural->CreateInfo.externalID)); + pData->rc = SSMR3PutMem(pSSM, &i32Dummy, sizeof(pMural->CreateInfo.externalID)); + } + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + CRASSERT(CR_STATE_SHAREDOBJ_USAGE_IS_SET(pMural, pContext)); + CRASSERT(pContextInfo->currentMural == pMural || !pContextInfo->currentMural); + CRASSERT(cr_server.curClient); + + crServerPerformMakeCurrent(pMural, pContextInfo); +#endif + + pData->rc = crStateSaveContext(pContext, pSSM); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + pData->rc = crVBoxServerSaveFBImage(pSSM); + CRSERVER_ASSERTRC_RETURN_VOID(pData->rc); + + /* restore the initial current mural */ + pContextInfo->currentMural = pContextCurrentMural; +} + +static uint32_t g_hackVBoxServerSaveLoadCallsLeft = 0; + +static int32_t crVBoxServerSaveStatePerform(PSSMHANDLE pSSM) +{ + int32_t rc, i; + uint32_t ui32; + GLboolean b; + unsigned long key; + GLenum err; +#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE + CRClient *curClient; + CRMuralInfo *curMural = NULL; + CRContextInfo *curCtxInfo = NULL; +#endif + CRVBOX_SAVE_STATE_GLOBAL Data; + + crMemset(&Data, 0, sizeof (Data)); + +#if 0 + crVBoxServerCheckConsistency(); +#endif + + /* We shouldn't be called if there's no clients at all*/ + CRASSERT(cr_server.numClients > 0); + + /** @todo it's hack atm */ + /* We want to be called only once to save server state but atm we're being called from svcSaveState + * for every connected client (e.g. guest opengl application) + */ + if (!cr_server.bIsInSavingState) /* It's first call */ + { + cr_server.bIsInSavingState = GL_TRUE; + + /* Store number of clients */ + rc = SSMR3PutU32(pSSM, (uint32_t) cr_server.numClients); + AssertRCReturn(rc, rc); + + /* we get called only once for CrCmd case, so disable the hack */ + g_hackVBoxServerSaveLoadCallsLeft = cr_server.fCrCmdEnabled ? 1 : cr_server.numClients; + } + + g_hackVBoxServerSaveLoadCallsLeft--; + + /* Do nothing until we're being called last time */ + if (g_hackVBoxServerSaveLoadCallsLeft>0) + { + return VINF_SUCCESS; + } + +#ifdef DEBUG_misha +#define CR_DBG_STR_STATE_SAVE_START "VBox.Cr.StateSaveStart" +#define CR_DBG_STR_STATE_SAVE_STOP "VBox.Cr.StateSaveStop" + + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_SAVE_START), CR_DBG_STR_STATE_SAVE_START); +#endif + + /* Save rendering contexts creation info */ + ui32 = crHashtableNumElements(cr_server.contextTable); + rc = SSMR3PutU32(pSSM, (uint32_t) ui32); + AssertRCReturn(rc, rc); + crHashtableWalk(cr_server.contextTable, crVBoxServerSaveCreateInfoFromCtxInfoCB, pSSM); + +#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE + curClient = cr_server.curClient; + /* Save current win and ctx IDs, as we'd rebind contexts when saving textures */ + if (curClient) + { + curCtxInfo = cr_server.curClient->currentCtxInfo; + curMural = cr_server.curClient->currentMural; + } + else if (cr_server.numClients) + { + cr_server.curClient = cr_server.clients[0]; + } +#endif + + /* first save windows info */ + /* Save windows creation info */ + ui32 = crHashtableNumElements(cr_server.muralTable); + /* There should be default mural always */ + CRASSERT(ui32>=1); + rc = SSMR3PutU32(pSSM, (uint32_t) ui32-1); + AssertRCReturn(rc, rc); + crHashtableWalk(cr_server.muralTable, crVBoxServerSaveCreateInfoFromMuralInfoCB, pSSM); + + /* Save cr_server.muralTable + * @todo we don't need it all, just geometry info actually + */ + rc = SSMR3PutU32(pSSM, (uint32_t) ui32-1); + AssertRCReturn(rc, rc); + crHashtableWalk(cr_server.muralTable, crVBoxServerSaveMuralCB, pSSM); + + /* we need to save front & backbuffer data for each mural first create a context -> mural association */ + crVBoxServerBuildSaveStateGlobal(&Data); + + rc = crStateSaveGlobals(pSSM); + AssertRCReturn(rc, rc); + + Data.pSSM = pSSM; + /* Save contexts state tracker data */ + /** @todo For now just some blind data dumps, + * but I've a feeling those should be saved/restored in a very strict sequence to + * allow diff_api to work correctly. + * Should be tested more with multiply guest opengl apps working when saving VM snapshot. + */ + crHashtableWalk(cr_server.contextTable, crVBoxServerSaveContextStateCB, &Data); + AssertRCReturn(Data.rc, Data.rc); + + ui32 = crHashtableNumElements(Data.additionalMuralContextTable); + rc = SSMR3PutU32(pSSM, (uint32_t) ui32); + AssertRCReturn(rc, rc); + + crHashtableWalk(Data.additionalMuralContextTable, crVBoxServerSaveAdditionalMuralsCB, &Data); + AssertRCReturn(Data.rc, Data.rc); + +#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE + cr_server.curClient = curClient; + /* Restore original win and ctx IDs*/ + if (curClient && curMural && curCtxInfo) + { + crServerPerformMakeCurrent(curMural, curCtxInfo); + } + else + { + cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE; + } +#endif + + /* Save clients info */ + for (i = 0; i < cr_server.numClients; i++) + { + if (cr_server.clients[i] && cr_server.clients[i]->conn) + { + CRClient *pClient = cr_server.clients[i]; + + rc = SSMR3PutU32(pSSM, pClient->conn->u32ClientID); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, pClient->conn->vMajor); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, pClient->conn->vMinor); + AssertRCReturn(rc, rc); + + rc = SSMR3PutMem(pSSM, pClient, sizeof(*pClient)); + AssertRCReturn(rc, rc); + + if (pClient->currentCtxInfo && pClient->currentCtxInfo->pContext && pClient->currentContextNumber > 0) + { + b = crHashtableGetDataKey(cr_server.contextTable, pClient->currentCtxInfo, &key); + CRASSERT(b); + rc = SSMR3PutMem(pSSM, &key, sizeof(key)); + AssertRCReturn(rc, rc); + } + + if (pClient->currentMural && pClient->currentWindow > 0) + { + b = crHashtableGetDataKey(cr_server.muralTable, pClient->currentMural, &key); + CRASSERT(b); + rc = SSMR3PutMem(pSSM, &key, sizeof(key)); + AssertRCReturn(rc, rc); + } + } + } + + rc = crServerPendSaveState(pSSM); + AssertRCReturn(rc, rc); + + rc = CrPMgrSaveState(pSSM); + AssertRCReturn(rc, rc); + +#ifdef VBOX_WITH_CR_DISPLAY_LISTS + if (cr_server.head_spu->dispatch_table.spu_save_state) + { + rc = cr_server.head_spu->dispatch_table.spu_save_state((void *)pSSM); + AssertRCReturn(rc, rc); + } + else + crDebug("Do not save %s SPU state: no interface exported.", cr_server.head_spu->name); +#endif + + /* all context gl error states should have now be synced with chromium erro states, + * reset the error if any */ + while ((err = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) + crWarning("crServer: glGetError %d after saving snapshot", err); + + cr_server.bIsInSavingState = GL_FALSE; + +#ifdef DEBUG_misha + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_SAVE_STOP), CR_DBG_STR_STATE_SAVE_STOP); +#endif + + return VINF_SUCCESS; +} + +DECLEXPORT(int32_t) crVBoxServerSaveState(PSSMHANDLE pSSM) +{ + if (cr_server.fCrCmdEnabled) + { + WARN(("we should not be called with cmd enabled!")); + return VERR_INTERNAL_ERROR; + } + + return crVBoxServerSaveStatePerform(pSSM); +} + +static DECLCALLBACK(CRContext*) crVBoxServerGetContextCB(void* pvData) +{ + CRContextInfo* pContextInfo = (CRContextInfo*)pvData; + CRASSERT(pContextInfo); + CRASSERT(pContextInfo->pContext); + return pContextInfo->pContext; +} + +typedef struct CR_SERVER_LOADSTATE_READER +{ + PSSMHANDLE pSSM; + uint32_t cbBuffer; + uint32_t cbData; + uint32_t offData; + uint8_t *pu8Buffer; +} CR_SERVER_LOADSTATE_READER; + +static void crServerLsrInit(CR_SERVER_LOADSTATE_READER *pReader, PSSMHANDLE pSSM) +{ + memset(pReader, 0, sizeof (*pReader)); + pReader->pSSM = pSSM; +} + +static void crServerLsrTerm(CR_SERVER_LOADSTATE_READER *pReader) +{ + if (pReader->pu8Buffer) + RTMemFree(pReader->pu8Buffer); + + /* sanity */ + memset(pReader, 0, sizeof (*pReader)); +} + +static int crServerLsrDataGetMem(CR_SERVER_LOADSTATE_READER *pReader, void *pvBuffer, uint32_t cbBuffer) +{ + int rc = VINF_SUCCESS; + uint32_t cbRemaining = cbBuffer; + if (pReader->cbData) + { + uint8_t cbData = RT_MIN(pReader->cbData, cbBuffer); + memcpy(pvBuffer, pReader->pu8Buffer + pReader->offData, cbData); + pReader->cbData -= cbData; + pReader->offData += cbData; + + cbRemaining -= cbData; + pvBuffer = ((uint8_t*)pvBuffer) + cbData; + } + + if (cbRemaining) + { + rc = SSMR3GetMem(pReader->pSSM, pvBuffer, cbRemaining); + AssertRC(rc); + } + + return rc; +} + +static int crServerLsrDataGetU32(CR_SERVER_LOADSTATE_READER *pReader, uint32_t *pu32) +{ + return crServerLsrDataGetMem(pReader, pu32, sizeof (*pu32)); +} + +static int crServerLsrDataPutMem(CR_SERVER_LOADSTATE_READER *pReader, void *pvBuffer, uint32_t cbBuffer) +{ + if (!pReader->cbData && pReader->cbBuffer >= cbBuffer) + { + pReader->offData = 0; + pReader->cbData = cbBuffer; + memcpy(pReader->pu8Buffer, pvBuffer, cbBuffer); + } + else if (pReader->offData >= cbBuffer) + { + pReader->offData -= cbBuffer; + pReader->cbData += cbBuffer; + memcpy(pReader->pu8Buffer + pReader->offData, pvBuffer, cbBuffer); + } + else + { + uint8_t *pu8Buffer = pReader->pu8Buffer; + + pReader->pu8Buffer = (uint8_t*)RTMemAlloc(cbBuffer + pReader->cbData); + if (!pReader->pu8Buffer) + { + crWarning("failed to allocate mem %d", cbBuffer + pReader->cbData); + return VERR_NO_MEMORY; + } + + memcpy(pReader->pu8Buffer, pvBuffer, cbBuffer); + if (pu8Buffer) + { + memcpy(pReader->pu8Buffer + cbBuffer, pu8Buffer + pReader->offData, pReader->cbData); + RTMemFree(pu8Buffer); + } + else + { + Assert(!pReader->cbData); + } + pReader->offData = 0; + pReader->cbData += cbBuffer; + } + + return VINF_SUCCESS; +} + +/* data to be skipped */ + +typedef struct CR_SERVER_BUGGY_MURAL_DATA_2 +{ + void*ListHead_pNext; + void*ListHead_pPrev; + uint32_t cEntries; +} CR_SERVER_BUGGY_MURAL_DATA_2; +typedef struct CR_SERVER_BUGGY_MURAL_DATA_1 +{ + /* VBOXVR_COMPOSITOR_ENTRY Ce; */ + void*Ce_Node_pNext; + void*Ce_Node_pPrev; + CR_SERVER_BUGGY_MURAL_DATA_2 Vr; + /* VBOXVR_TEXTURE Tex; */ + uint32_t Tex_width; + uint32_t Tex_height; + uint32_t Tex_target; + uint32_t Tex_hwid; + /* RTPOINT Pos; */ + uint32_t Pos_x; + uint32_t Pos_y; + uint32_t fChanged; + uint32_t cRects; + void* paSrcRects; + void* paDstRects; +} CR_SERVER_BUGGY_MURAL_DATA_1; + +typedef struct CR_SERVER_BUGGY_MURAL_DATA_4 +{ + uint32_t u32Magic; + int32_t cLockers; + RTNATIVETHREAD NativeThreadOwner; + int32_t cNestings; + uint32_t fFlags; + void* EventSem; + R3R0PTRTYPE(PRTLOCKVALRECEXCL) pValidatorRec; + RTHCPTR Alignment; +} CR_SERVER_BUGGY_MURAL_DATA_4; + +typedef struct CR_SERVER_BUGGY_MURAL_DATA_3 +{ + void*Compositor_List_pNext; + void*Compositor_List_pPrev; + void*Compositor_pfnEntryRemoved; + float StretchX; + float StretchY; + uint32_t cRects; + uint32_t cRectsBuffer; + void*paSrcRects; + void*paDstRects; + CR_SERVER_BUGGY_MURAL_DATA_4 CritSect; +} CR_SERVER_BUGGY_MURAL_DATA_3; + +typedef struct CR_SERVER_BUGGY_MURAL_DATA +{ + uint8_t fRootVrOn; + CR_SERVER_BUGGY_MURAL_DATA_1 RootVrCEntry; + CR_SERVER_BUGGY_MURAL_DATA_3 RootVrCompositor; +} CR_SERVER_BUGGY_MURAL_DATA; + +AssertCompile(sizeof (CR_SERVER_BUGGY_MURAL_DATA) < sizeof (CRClient)); + +static int32_t crVBoxServerLoadMurals(CR_SERVER_LOADSTATE_READER *pReader, uint32_t version) +{ + unsigned long key; + uint32_t ui, uiNumElems; + bool fBuggyMuralData = false; + /* Load windows */ + int32_t rc = crServerLsrDataGetU32(pReader, &uiNumElems); + AssertLogRelRCReturn(rc, rc); + for (ui=0; ui<uiNumElems; ++ui) + { + CRCreateInfo_t createInfo; + char psz[200]; + GLint winID; + unsigned long key; + + rc = crServerLsrDataGetMem(pReader, &key, sizeof(key)); + AssertLogRelRCReturn(rc, rc); + rc = crServerLsrDataGetMem(pReader, &createInfo, sizeof(createInfo)); + AssertLogRelRCReturn(rc, rc); + + CRASSERT(!pReader->cbData); + + if (createInfo.pszDpyName) + { + rc = SSMR3GetStrZEx(pReader->pSSM, psz, 200, NULL); + AssertLogRelRCReturn(rc, rc); + createInfo.pszDpyName = psz; + } + + winID = crServerDispatchWindowCreateEx(createInfo.pszDpyName, createInfo.visualBits, key); + CRASSERT((int64_t)winID == (int64_t)key); + } + + /* Load cr_server.muralTable */ + rc = SSMR3GetU32(pReader->pSSM, &uiNumElems); + AssertLogRelRCReturn(rc, rc); + for (ui=0; ui<uiNumElems; ++ui) + { + CRMuralInfo muralInfo; + CRMuralInfo *pActualMural = NULL; + + rc = crServerLsrDataGetMem(pReader, &key, sizeof(key)); + AssertLogRelRCReturn(rc, rc); + rc = crServerLsrDataGetMem(pReader, &muralInfo, RT_UOFFSETOF(CRMuralInfo, CreateInfo)); + AssertLogRelRCReturn(rc, rc); + + if (version <= SHCROGL_SSM_VERSION_BEFORE_FRONT_DRAW_TRACKING) + muralInfo.bFbDraw = GL_TRUE; + + if (!ui && version == SHCROGL_SSM_VERSION_WITH_BUGGY_MURAL_INFO) + { + /* Lookahead buffer used to determine whether the data erroneously stored root visible regions data */ + union + { + void * apv[1]; + CR_SERVER_BUGGY_MURAL_DATA Data; + /* need to chak spuWindow, so taking the offset of filed following it*/ + uint8_t au8[RT_UOFFSETOF(CRMuralInfo, screenId)]; + RTRECT aVisRects[sizeof (CR_SERVER_BUGGY_MURAL_DATA) / sizeof (RTRECT)]; + } LaBuf; + + do { + /* first value is bool (uint8_t) value followed by pointer-size-based alignment. + * the mural memory is zero-initialized initially, so we can be sure the padding is zeroed */ + rc = crServerLsrDataGetMem(pReader, &LaBuf, sizeof (LaBuf)); + AssertLogRelRCReturn(rc, rc); + if (LaBuf.apv[0] != NULL && LaBuf.apv[0] != ((void *)(uintptr_t)1)) + break; + + /* check that the pointers are either valid or NULL */ + if(LaBuf.Data.RootVrCEntry.Ce_Node_pNext && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Ce_Node_pNext)) + break; + if(LaBuf.Data.RootVrCEntry.Ce_Node_pPrev && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Ce_Node_pPrev)) + break; + if(LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext)) + break; + if(LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev && !RT_VALID_PTR(LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev)) + break; + + /* the entry can can be the only one within the (mural) compositor, + * so its compositor entry node can either contain NULL pNext and pPrev, + * or both of them pointing to compositor's list head */ + if (LaBuf.Data.RootVrCEntry.Ce_Node_pNext != LaBuf.Data.RootVrCEntry.Ce_Node_pPrev) + break; + + /* can either both or none be NULL */ + if (!LaBuf.Data.RootVrCEntry.Ce_Node_pNext != !LaBuf.Data.RootVrCEntry.Ce_Node_pPrev) + break; + + if (!LaBuf.Data.fRootVrOn) + { + if (LaBuf.Data.RootVrCEntry.Ce_Node_pNext || LaBuf.Data.RootVrCEntry.Ce_Node_pPrev) + break; + + /* either non-initialized (zeroed) or empty list */ + if (LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext != LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev) + break; + + if (LaBuf.Data.RootVrCEntry.Vr.cEntries) + break; + } + else + { + /* the entry should be initialized */ + if (!LaBuf.Data.RootVrCEntry.Vr.ListHead_pNext) + break; + if (!LaBuf.Data.RootVrCEntry.Vr.ListHead_pPrev) + break; + + if (LaBuf.Data.RootVrCEntry.Vr.cEntries) + { + /* entry should be in compositor list*/ + if (LaBuf.Data.RootVrCEntry.Ce_Node_pPrev == NULL) + break; + CRASSERT(LaBuf.Data.RootVrCEntry.Ce_Node_pNext); + } + else + { + /* entry should NOT be in compositor list*/ + if (LaBuf.Data.RootVrCEntry.Ce_Node_pPrev != NULL) + break; + CRASSERT(!LaBuf.Data.RootVrCEntry.Ce_Node_pNext); + } + } + + /* fExpectPtr == true, the valid pointer values should not match possible mural width/height/position */ + fBuggyMuralData = true; + break; + + } while (0); + + rc = crServerLsrDataPutMem(pReader, &LaBuf, sizeof (LaBuf)); + AssertLogRelRCReturn(rc, rc); + } + + if (fBuggyMuralData) + { + CR_SERVER_BUGGY_MURAL_DATA Tmp; + rc = crServerLsrDataGetMem(pReader, &Tmp, sizeof (Tmp)); + AssertLogRelRCReturn(rc, rc); + } + + if (muralInfo.pVisibleRects) + { + muralInfo.pVisibleRects = crAlloc(4*sizeof(GLint)*muralInfo.cVisibleRects); + if (!muralInfo.pVisibleRects) + { + return VERR_NO_MEMORY; + } + + rc = crServerLsrDataGetMem(pReader, muralInfo.pVisibleRects, 4*sizeof(GLint)*muralInfo.cVisibleRects); + AssertLogRelRCReturn(rc, rc); + } + + pActualMural = (CRMuralInfo *)crHashtableSearch(cr_server.muralTable, key); + CRASSERT(pActualMural); + + if (version >= SHCROGL_SSM_VERSION_WITH_WINDOW_CTX_USAGE) + { + rc = crServerLsrDataGetMem(pReader, pActualMural->ctxUsage, sizeof (pActualMural->ctxUsage)); + CRASSERT(rc == VINF_SUCCESS); + } + + /* Restore windows geometry info */ + crServerDispatchWindowSize(key, muralInfo.width, muralInfo.height); + crServerDispatchWindowPosition(key, muralInfo.gX, muralInfo.gY); + /* Same workaround as described in stub.c:stubUpdateWindowVisibileRegions for compiz on a freshly booted VM*/ + if (muralInfo.bReceivedRects) + { + crServerDispatchWindowVisibleRegion(key, muralInfo.cVisibleRects, muralInfo.pVisibleRects); + } + crServerDispatchWindowShow(key, muralInfo.bVisible); + + if (muralInfo.pVisibleRects) + { + crFree(muralInfo.pVisibleRects); + } + } + + CRASSERT(RT_SUCCESS(rc)); + return VINF_SUCCESS; +} + +static int crVBoxServerLoadFBImage(PSSMHANDLE pSSM, uint32_t version, + CRContextInfo* pContextInfo, CRMuralInfo *pMural) +{ + CRContext *pContext = pContextInfo->pContext; + int32_t rc = VINF_SUCCESS; + GLuint i; + /* can apply the data right away */ + struct + { + CRFBData data; + CRFBDataElement buffer[3]; /* CRFBData::aElements[1] + buffer[3] gives 4: back, front, depth and stencil */ + } Data; + + Assert(sizeof (Data) >= RT_UOFFSETOF(CRFBData, aElements[4])); + + if (version >= SHCROGL_SSM_VERSION_WITH_SAVED_DEPTH_STENCIL_BUFFER) + { + if (!pMural->width || !pMural->height) + return VINF_SUCCESS; + + rc = crVBoxServerFBImageDataInitEx(&Data.data, pContextInfo, pMural, GL_TRUE, version, 0, 0); + if (!RT_SUCCESS(rc)) + { + crWarning("crVBoxServerFBImageDataInit failed rc %d", rc); + return rc; + } + } + else + { + GLint storedWidth, storedHeight; + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + CRASSERT(cr_server.currentCtxInfo == pContextInfo); + CRASSERT(cr_server.currentMural == pMural); + storedWidth = pMural->width; + storedHeight = pMural->height; + } + else + { + storedWidth = pContext->buffer.storedWidth; + storedHeight = pContext->buffer.storedHeight; + } + + if (!storedWidth || !storedHeight) + return VINF_SUCCESS; + + rc = crVBoxServerFBImageDataInitEx(&Data.data, pContextInfo, pMural, GL_TRUE, version, storedWidth, storedHeight); + if (!RT_SUCCESS(rc)) + { + crWarning("crVBoxServerFBImageDataInit failed rc %d", rc); + return rc; + } + } + + CRASSERT(Data.data.cElements); + + for (i = 0; i < Data.data.cElements; ++i) + { + CRFBDataElement * pEl = &Data.data.aElements[i]; + rc = SSMR3GetMem(pSSM, pEl->pvData, pEl->cbData); + AssertLogRelRCReturn(rc, rc); + } + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + CRBufferState *pBuf = &pContext->buffer; + /* can apply the data right away */ + CRASSERT(cr_server.currentCtxInfo == &cr_server.MainContextInfo); + CRASSERT(cr_server.currentMural); + + cr_server.head_spu->dispatch_table.MakeCurrent( pMural->spuWindow, + 0, + pContextInfo->SpuContext >= 0 + ? pContextInfo->SpuContext + : cr_server.MainContextInfo.SpuContext); + crStateApplyFBImage(pContext, &Data.data); + CRASSERT(!pBuf->pFrontImg); + CRASSERT(!pBuf->pBackImg); + crVBoxServerFBImageDataTerm(&Data.data); + + crServerPresentFBO(pMural); + + CRASSERT(cr_server.currentMural); + cr_server.head_spu->dispatch_table.MakeCurrent( cr_server.currentMural->spuWindow, + 0, + cr_server.currentCtxInfo->SpuContext >= 0 + ? cr_server.currentCtxInfo->SpuContext + : cr_server.MainContextInfo.SpuContext); + } + else + { + CRBufferState *pBuf = &pContext->buffer; + CRASSERT(!pBuf->pFrontImg); + CRASSERT(!pBuf->pBackImg); + CRASSERT(Data.data.cElements); /* <- older versions always saved front and back, and we filtered out the null-sized buffers above */ + + if (Data.data.cElements) + { + CRFBData *pLazyData = crAlloc(RT_UOFFSETOF_DYN(CRFBData, aElements[Data.data.cElements])); + if (!RT_SUCCESS(rc)) + { + crVBoxServerFBImageDataTerm(&Data.data); + crWarning("crAlloc failed"); + return VERR_NO_MEMORY; + } + + crMemcpy(pLazyData, &Data.data, RT_UOFFSETOF_DYN(CRFBData, aElements[Data.data.cElements])); + pBuf->pFrontImg = pLazyData; + } + } + + CRASSERT(RT_SUCCESS(rc)); + return VINF_SUCCESS; +} + +static int32_t crVBoxServerLoadStatePerform(PSSMHANDLE pSSM, uint32_t version) +{ + int32_t rc, i; + uint32_t ui, uiNumElems; + unsigned long key; + GLenum err; + CR_SERVER_LOADSTATE_READER Reader; + + if (!cr_server.bIsInLoadingState) + { + /* AssertRCReturn(...) will leave us in loading state, but it doesn't matter as we'd be failing anyway */ + cr_server.bIsInLoadingState = GL_TRUE; + + /* Read number of clients */ + rc = SSMR3GetU32(pSSM, &g_hackVBoxServerSaveLoadCallsLeft); + AssertLogRelRCReturn(rc, rc); + + Assert(g_hackVBoxServerSaveLoadCallsLeft); + /* we get called only once for CrCmd */ + if (cr_server.fCrCmdEnabled) + g_hackVBoxServerSaveLoadCallsLeft = 1; + } + + g_hackVBoxServerSaveLoadCallsLeft--; + + /* Do nothing until we're being called last time */ + if (g_hackVBoxServerSaveLoadCallsLeft>0) + { + return VINF_SUCCESS; + } + + if (version < SHCROGL_SSM_VERSION_BEFORE_CTXUSAGE_BITS) + { + return VERR_SSM_DATA_UNIT_FORMAT_CHANGED; + } + + crServerLsrInit(&Reader, pSSM); + +#ifdef DEBUG_misha +#define CR_DBG_STR_STATE_LOAD_START "VBox.Cr.StateLoadStart" +#define CR_DBG_STR_STATE_LOAD_STOP "VBox.Cr.StateLoadStop" + + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_LOAD_START), CR_DBG_STR_STATE_LOAD_START); +#endif + + /* Load and recreate rendering contexts */ + rc = SSMR3GetU32(pSSM, &uiNumElems); + AssertLogRelRCReturn(rc, rc); + for (ui = 0; ui < uiNumElems; ++ui) + { + CRCreateInfo_t createInfo; + char psz[200]; + GLint ctxID; + CRContextInfo* pContextInfo; + CRContext* pContext; + + rc = SSMR3GetMem(pSSM, &key, sizeof(key)); + AssertLogRelRCReturn(rc, rc); + rc = SSMR3GetMem(pSSM, &createInfo, sizeof(createInfo)); + AssertLogRelRCReturn(rc, rc); + + if (createInfo.pszDpyName) + { + rc = SSMR3GetStrZEx(pSSM, psz, 200, NULL); + AssertLogRelRCReturn(rc, rc); + createInfo.pszDpyName = psz; + } + + ctxID = crServerDispatchCreateContextEx(createInfo.pszDpyName, createInfo.visualBits, 0, key, createInfo.externalID /* <-saved state stores internal id here*/); + CRASSERT((int64_t)ctxID == (int64_t)key); + + pContextInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, key); + CRASSERT(pContextInfo); + CRASSERT(pContextInfo->pContext); + pContext = pContextInfo->pContext; + pContext->shared->id=-1; + } + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + CRASSERT(!Reader.pu8Buffer); + /* we have a mural data here */ + rc = crVBoxServerLoadMurals(&Reader, version); + AssertLogRelRCReturn(rc, rc); + CRASSERT(!Reader.pu8Buffer); + } + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA && uiNumElems) + { + /* set the current client to allow doing crServerPerformMakeCurrent later */ + CRASSERT(cr_server.numClients); + cr_server.curClient = cr_server.clients[0]; + } + + rc = crStateLoadGlobals(pSSM, version); + AssertLogRelRCReturn(rc, rc); + + if (uiNumElems) + { + /* ensure we have main context set up as current */ + CRMuralInfo *pMural; + CRASSERT(cr_server.MainContextInfo.SpuContext > 0); + CRASSERT(!cr_server.currentCtxInfo); + CRASSERT(!cr_server.currentMural); + pMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + CRASSERT(pMural); + crServerPerformMakeCurrent(pMural, &cr_server.MainContextInfo); + } + + /* Restore context state data */ + for (ui=0; ui<uiNumElems; ++ui) + { + CRContextInfo* pContextInfo; + CRContext *pContext; + CRMuralInfo *pMural = NULL; + int32_t winId = 0; + + rc = SSMR3GetMem(pSSM, &key, sizeof(key)); + AssertLogRelRCReturn(rc, rc); + + pContextInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, key); + CRASSERT(pContextInfo); + CRASSERT(pContextInfo->pContext); + pContext = pContextInfo->pContext; + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + rc = SSMR3GetMem(pSSM, &winId, sizeof(winId)); + AssertLogRelRCReturn(rc, rc); + + if (winId) + { + pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, winId); + CRASSERT(pMural); + } + else + { + /* null winId means a dummy mural, get it */ + pMural = crServerGetDummyMural(pContextInfo->CreateInfo.realVisualBits); + CRASSERT(pMural); + } + } + + rc = crStateLoadContext(pContext, cr_server.contextTable, crVBoxServerGetContextCB, pSSM, version); + AssertLogRelRCReturn(rc, rc); + + /*Restore front/back buffer images*/ + rc = crVBoxServerLoadFBImage(pSSM, version, pContextInfo, pMural); + AssertLogRelRCReturn(rc, rc); + } + + if (version > SHCROGL_SSM_VERSION_WITH_BUGGY_FB_IMAGE_DATA) + { + CRContextInfo *pContextInfo; + CRMuralInfo *pMural; + GLint ctxId; + + rc = SSMR3GetU32(pSSM, &uiNumElems); + AssertLogRelRCReturn(rc, rc); + for (ui=0; ui<uiNumElems; ++ui) + { + CRbitvalue initialCtxUsage[CR_MAX_BITARRAY]; + CRMuralInfo *pInitialCurMural; + + rc = SSMR3GetMem(pSSM, &key, sizeof(key)); + AssertLogRelRCReturn(rc, rc); + + rc = SSMR3GetMem(pSSM, &ctxId, sizeof(ctxId)); + AssertLogRelRCReturn(rc, rc); + + pMural = (CRMuralInfo*)crHashtableSearch(cr_server.muralTable, key); + CRASSERT(pMural); + if (ctxId) + { + pContextInfo = (CRContextInfo *)crHashtableSearch(cr_server.contextTable, ctxId); + CRASSERT(pContextInfo); + } + else + pContextInfo = &cr_server.MainContextInfo; + + crMemcpy(initialCtxUsage, pMural->ctxUsage, sizeof (initialCtxUsage)); + pInitialCurMural = pContextInfo->currentMural; + + rc = crVBoxServerLoadFBImage(pSSM, version, pContextInfo, pMural); + AssertLogRelRCReturn(rc, rc); + + /* restore the reference data, we synchronize it with the HW state in a later crServerPerformMakeCurrent call */ + crMemcpy(pMural->ctxUsage, initialCtxUsage, sizeof (initialCtxUsage)); + pContextInfo->currentMural = pInitialCurMural; + } + + CRASSERT(!uiNumElems || cr_server.currentCtxInfo == &cr_server.MainContextInfo); + + cr_server.curClient = NULL; + cr_server.bForceMakeCurrentOnClientSwitch = GL_TRUE; + } + else + { + CRServerFreeIDsPool_t dummyIdsPool; + + CRASSERT(!Reader.pu8Buffer); + + /* we have a mural data here */ + rc = crVBoxServerLoadMurals(&Reader, version); + AssertLogRelRCReturn(rc, rc); + + /* not used any more, just read it out and ignore */ + rc = crServerLsrDataGetMem(&Reader, &dummyIdsPool, sizeof(dummyIdsPool)); + CRASSERT(rc == VINF_SUCCESS); + } + + /* Load clients info */ + for (i = 0; i < cr_server.numClients; i++) + { + if (cr_server.clients[i] && cr_server.clients[i]->conn) + { + CRClient *pClient = cr_server.clients[i]; + CRClient client; + unsigned long ctxID=-1, winID=-1; + + rc = crServerLsrDataGetU32(&Reader, &ui); + AssertLogRelRCReturn(rc, rc); + /* If this assert fires, then we should search correct client in the list first*/ + CRASSERT(ui == pClient->conn->u32ClientID); + + if (version>=4) + { + rc = crServerLsrDataGetU32(&Reader, &pClient->conn->vMajor); + AssertLogRelRCReturn(rc, rc); + + rc = crServerLsrDataGetU32(&Reader, &pClient->conn->vMinor); + AssertLogRelRCReturn(rc, rc); + } + + rc = crServerLsrDataGetMem(&Reader, &client, sizeof(client)); + CRASSERT(rc == VINF_SUCCESS); + + client.conn = pClient->conn; + /* We can't reassign client number, as we'd get wrong results in TranslateTextureID + * and fail to bind old textures. + */ + /*client.number = pClient->number;*/ + *pClient = client; + + pClient->currentContextNumber = -1; + pClient->currentCtxInfo = &cr_server.MainContextInfo; + pClient->currentMural = NULL; + pClient->currentWindow = -1; + + cr_server.curClient = pClient; + + if (client.currentCtxInfo && client.currentContextNumber > 0) + { + rc = crServerLsrDataGetMem(&Reader, &ctxID, sizeof(ctxID)); + AssertLogRelRCReturn(rc, rc); + client.currentCtxInfo = (CRContextInfo*) crHashtableSearch(cr_server.contextTable, ctxID); + CRASSERT(client.currentCtxInfo); + CRASSERT(client.currentCtxInfo->pContext); + //pClient->currentCtx = client.currentCtx; + //pClient->currentContextNumber = ctxID; + } + + if (client.currentMural && client.currentWindow > 0) + { + rc = crServerLsrDataGetMem(&Reader, &winID, sizeof(winID)); + AssertLogRelRCReturn(rc, rc); + client.currentMural = (CRMuralInfo*) crHashtableSearch(cr_server.muralTable, winID); + CRASSERT(client.currentMural); + //pClient->currentMural = client.currentMural; + //pClient->currentWindow = winID; + } + + CRASSERT(!Reader.cbData); + + /* Restore client active context and window */ + crServerDispatchMakeCurrent(winID, 0, ctxID); + } + } + + cr_server.curClient = NULL; + + rc = crServerPendLoadState(pSSM, version); + AssertLogRelRCReturn(rc, rc); + + if (version >= SHCROGL_SSM_VERSION_WITH_SCREEN_INFO) + { + rc = CrPMgrLoadState(pSSM, version); + AssertLogRelRCReturn(rc, rc); + } + +#ifdef VBOX_WITH_CR_DISPLAY_LISTS + if (version >= SHCROGL_SSM_VERSION_WITH_DISPLAY_LISTS) + { + if (cr_server.head_spu->dispatch_table.spu_load_state) + { + rc = cr_server.head_spu->dispatch_table.spu_load_state((void *)pSSM); + AssertLogRelRCReturn(rc, rc); + } + else + crDebug("Do not load %s SPU state: no interface exported.", cr_server.head_spu->name); + } +#endif + + while ((err = cr_server.head_spu->dispatch_table.GetError()) != GL_NO_ERROR) + crWarning("crServer: glGetError %d after loading snapshot", err); + + cr_server.bIsInLoadingState = GL_FALSE; + +#ifdef DEBUG_misha + if (cr_server.head_spu->dispatch_table.StringMarkerGREMEDY) + cr_server.head_spu->dispatch_table.StringMarkerGREMEDY(sizeof (CR_DBG_STR_STATE_LOAD_STOP), CR_DBG_STR_STATE_LOAD_STOP); +#endif + + CRASSERT(!Reader.cbData); + crServerLsrTerm(&Reader); + + return VINF_SUCCESS; +} + +DECLEXPORT(int32_t) crVBoxServerLoadState(PSSMHANDLE pSSM, uint32_t version) +{ + if (cr_server.fCrCmdEnabled) + { + WARN(("CrCmd enabled")); + return VERR_INTERNAL_ERROR; + } + + return crVBoxServerLoadStatePerform(pSSM, version); +} + +#define SCREEN(i) (cr_server.screen[i]) +#define MAPPED(screen) ((screen).winID != 0) + +extern DECLEXPORT(void) crServerVBoxSetNotifyEventCB(PFNCRSERVERNOTIFYEVENT pfnCb) +{ + cr_server.pfnNotifyEventCB = pfnCb; +} + +void crVBoxServerNotifyEvent(int32_t idScreen, uint32_t uEvent, void* pvData, uint32_t cbData) +{ + /* this is something unexpected, but just in case */ + if (idScreen >= cr_server.screenCount) + { + crWarning("invalid screen id %d", idScreen); + return; + } + + cr_server.pfnNotifyEventCB(idScreen, uEvent, pvData, cbData); +} + +void crServerWindowReparent(CRMuralInfo *pMural) +{ + pMural->fHasParentWindow = !!cr_server.screen[pMural->screenId].winID; + + renderspuReparentWindow(pMural->spuWindow); +} + +DECLEXPORT(void) crServerSetUnscaledHiDPI(bool fEnable) +{ + renderspuSetUnscaledHiDPI(fEnable); +} + +static void crVBoxServerReparentMuralCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMI = (CRMuralInfo*) data1; + int *sIndex = (int*) data2; + + if (pMI->screenId == *sIndex) + { + crServerWindowReparent(pMI); + } +} + +DECLEXPORT(int32_t) crVBoxServerSetScreenCount(int sCount) +{ + int i; + + if (sCount > CR_MAX_GUEST_MONITORS) + return VERR_INVALID_PARAMETER; + + /*Shouldn't happen yet, but to be safe in future*/ + for (i = 0; i < cr_server.screenCount; /*++i - unreachable code*/) + { + if (MAPPED(SCREEN(i))) + WARN(("Screen count is changing, but screen[%i] is still mapped", i)); + return VERR_NOT_IMPLEMENTED; + } + + cr_server.screenCount = sCount; + + for (i=0; i<sCount; ++i) + { + SCREEN(i).winID = 0; + } + + return VINF_SUCCESS; +} + +DECLEXPORT(int32_t) crVBoxServerUnmapScreen(int sIndex) +{ + crDebug("crVBoxServerUnmapScreen(%i)", sIndex); + + if (sIndex<0 || sIndex>=cr_server.screenCount) + return VERR_INVALID_PARAMETER; + + if (MAPPED(SCREEN(sIndex))) + { + SCREEN(sIndex).winID = 0; + renderspuSetWindowId(0); + + crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex); + + crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerReparentMuralCB, &sIndex); + + CrPMgrScreenChanged((uint32_t)sIndex); + } + + renderspuSetWindowId(SCREEN(0).winID); + + return VINF_SUCCESS; +} + +DECLEXPORT(int32_t) crVBoxServerMapScreen(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h, uint64_t winID) +{ + crDebug("crVBoxServerMapScreen(%i) [%i,%i:%u,%u %x]", sIndex, x, y, w, h, winID); + + if (sIndex<0 || sIndex>=cr_server.screenCount) + return VERR_INVALID_PARAMETER; + + if (MAPPED(SCREEN(sIndex)) && SCREEN(sIndex).winID!=winID) + { + crDebug("Mapped screen[%i] is being remapped.", sIndex); + crVBoxServerUnmapScreen(sIndex); + } + + SCREEN(sIndex).winID = winID; + SCREEN(sIndex).x = x; + SCREEN(sIndex).y = y; + SCREEN(sIndex).w = w; + SCREEN(sIndex).h = h; + + renderspuSetWindowId(SCREEN(sIndex).winID); + crHashtableWalk(cr_server.muralTable, crVBoxServerReparentMuralCB, &sIndex); + + crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerReparentMuralCB, &sIndex); + renderspuSetWindowId(SCREEN(0).winID); + +#ifndef WINDOWS + /*Restore FB content for clients, which have current window on a screen being remapped*/ + { + GLint i; + + for (i = 0; i < cr_server.numClients; i++) + { + cr_server.curClient = cr_server.clients[i]; + if (cr_server.curClient->currentCtxInfo + && cr_server.curClient->currentCtxInfo->pContext + && (cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg) + && cr_server.curClient->currentMural + && cr_server.curClient->currentMural->screenId == sIndex + && cr_server.curClient->currentCtxInfo->pContext->buffer.storedHeight == h + && cr_server.curClient->currentCtxInfo->pContext->buffer.storedWidth == w) + { + int clientWindow = cr_server.curClient->currentWindow; + int clientContext = cr_server.curClient->currentContextNumber; + CRFBData *pLazyData = (CRFBData *)cr_server.curClient->currentCtxInfo->pContext->buffer.pFrontImg; + + if (clientWindow && clientWindow != cr_server.currentWindow) + { + crServerDispatchMakeCurrent(clientWindow, 0, clientContext); + } + + crStateApplyFBImage(cr_server.curClient->currentCtxInfo->pContext, pLazyData); + crStateFreeFBImageLegacy(cr_server.curClient->currentCtxInfo->pContext); + } + } + cr_server.curClient = NULL; + } +#endif + + CrPMgrScreenChanged((uint32_t)sIndex); + + return VINF_SUCCESS; +} + +DECLEXPORT(int32_t) crVBoxServerSetRootVisibleRegion(GLint cRects, const RTRECT *pRects) +{ + int32_t rc = VINF_SUCCESS; + GLboolean fOldRootVrOn = cr_server.fRootVrOn; + + /* non-zero rects pointer indicate rects are present and switched on + * i.e. cRects==0 and pRects!=NULL means root visible regioning is ON and there are no visible regions, + * while pRects==NULL means root visible regioning is OFF, i.e. everything is visible */ + if (pRects) + { + crMemset(&cr_server.RootVrCurPoint, 0, sizeof (cr_server.RootVrCurPoint)); + rc = VBoxVrListRectsSet(&cr_server.RootVr, cRects, pRects, NULL); + if (!RT_SUCCESS(rc)) + { + crWarning("VBoxVrListRectsSet failed! rc %d", rc); + return rc; + } + + cr_server.fRootVrOn = GL_TRUE; + } + else + { + if (!cr_server.fRootVrOn) + return VINF_SUCCESS; + + VBoxVrListClear(&cr_server.RootVr); + + cr_server.fRootVrOn = GL_FALSE; + } + + if (!fOldRootVrOn != !cr_server.fRootVrOn) + { + rc = CrPMgrModeRootVr(cr_server.fRootVrOn); + if (!RT_SUCCESS(rc)) + { + crWarning("CrPMgrModeRootVr failed rc %d", rc); + return rc; + } + } + else if (cr_server.fRootVrOn) + { + rc = CrPMgrRootVrUpdate(); + if (!RT_SUCCESS(rc)) + { + crWarning("CrPMgrRootVrUpdate failed rc %d", rc); + return rc; + } + } + + return VINF_SUCCESS; +} + +DECLEXPORT(int32_t) crVBoxServerSetOffscreenRendering(GLboolean value) +{ + return CrPMgrModeVrdp(value); +} + +DECLEXPORT(int32_t) crVBoxServerOutputRedirectSet(const CROutputRedirect *pCallbacks) +{ + /* No need for a synchronization as this is single threaded. */ + if (pCallbacks) + { + cr_server.outputRedirect = *pCallbacks; + } + else + { + memset (&cr_server.outputRedirect, 0, sizeof (cr_server.outputRedirect)); + } + + return VINF_SUCCESS; +} + +DECLEXPORT(int32_t) crVBoxServerSetScreenViewport(int sIndex, int32_t x, int32_t y, uint32_t w, uint32_t h) +{ + CRScreenViewportInfo *pViewport; + RTRECT NewRect; + int rc; + + crDebug("crVBoxServerSetScreenViewport(%i)", sIndex); + + if (sIndex<0 || sIndex>=cr_server.screenCount) + { + crWarning("crVBoxServerSetScreenViewport: invalid screen id %d", sIndex); + return VERR_INVALID_PARAMETER; + } + + NewRect.xLeft = x; + NewRect.yTop = y; + NewRect.xRight = x + w; + NewRect.yBottom = y + h; + + pViewport = &cr_server.screenVieport[sIndex]; + /*always do viewport updates no matter whether the rectangle actually changes, + * this is needed to ensure window is adjusted properly on OSX */ + pViewport->Rect = NewRect; + rc = CrPMgrViewportUpdate((uint32_t)sIndex); + if (!RT_SUCCESS(rc)) + { + crWarning("CrPMgrViewportUpdate failed %d", rc); + return rc; + } + + return VINF_SUCCESS; +} + +static void crVBoxServerDeleteMuralCb(unsigned long key, void *data1, void *data2) +{ + CRHashTable *h = (CRHashTable*)data2; + CRMuralInfo *m = (CRMuralInfo *) data1; + if (m->spuWindow == CR_RENDER_DEFAULT_WINDOW_ID) + return; + + crHashtableDelete(h, key, NULL); + crServerMuralTerm(m); + crFree(m); +} + +static void crVBoxServerDefaultContextClear() +{ + HCR_FRAMEBUFFER hFb; + int rc = CrPMgrDisable(); + if (RT_FAILURE(rc)) + { + WARN(("CrPMgrDisable failed %d", rc)); + return; + } + + for (hFb = CrPMgrFbGetFirstEnabled(); hFb; hFb = CrPMgrFbGetNextEnabled(hFb)) + { + int rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + CrFbRegionsClear(hFb); + CrFbUpdateEnd(hFb); + } + else + WARN(("CrFbUpdateBegin failed %d", rc)); + } + + cr_server.head_spu->dispatch_table.MakeCurrent(0, 0, 0); + crStateCleanupCurrent(); + + /* note: we need to clean all contexts, since otherwise renderspu leanup won't work, + * i.e. renderspu would need to clean up its own internal windows, it won't be able to do that if + * some those windows is associated with any context. */ + if (cr_server.MainContextInfo.SpuContext) + { + cr_server.head_spu->dispatch_table.DestroyContext(cr_server.MainContextInfo.SpuContext); + crStateDestroyContext(cr_server.MainContextInfo.pContext); + if (cr_server.MainContextInfo.CreateInfo.pszDpyName) + crFree(cr_server.MainContextInfo.CreateInfo.pszDpyName); + + memset(&cr_server.MainContextInfo, 0, sizeof (cr_server.MainContextInfo)); + } + + cr_server.firstCallCreateContext = GL_TRUE; + cr_server.firstCallMakeCurrent = GL_TRUE; + cr_server.bForceMakeCurrentOnClientSwitch = GL_FALSE; + + CRASSERT(!cr_server.curClient); + + cr_server.currentCtxInfo = NULL; + cr_server.currentWindow = 0; + cr_server.currentNativeWindow = 0; + cr_server.currentMural = NULL; + + crStateDestroy(); +// crStateCleanupCurrent(); + + if (CrBltIsInitialized(&cr_server.Blitter)) + { + CrBltTerm(&cr_server.Blitter); + Assert(!CrBltIsInitialized(&cr_server.Blitter)); + } + + crHashtableWalk(cr_server.dummyMuralTable, crVBoxServerDeleteMuralCb, cr_server.dummyMuralTable); + + cr_server.head_spu->dispatch_table.ChromiumParameteriCR(GL_HH_RENDERTHREAD_INFORM, 0); +} + +static void crVBoxServerDefaultContextSet() +{ + cr_server.head_spu->dispatch_table.ChromiumParameteriCR(GL_HH_RENDERTHREAD_INFORM, 1); + + CRASSERT(!cr_server.MainContextInfo.SpuContext); + +// crStateSetCurrent(NULL); + crStateInit(); + crStateDiffAPI( &(cr_server.head_spu->dispatch_table) ); + + CrPMgrEnable(); +} + +#ifdef VBOX_WITH_CRHGSMI + +/** @todo RT_UNTRUSTED_VOLATILE_GUEST */ +static int32_t crVBoxServerCmdVbvaCrCmdProcess(VBOXCMDVBVA_CRCMD_CMD const RT_UNTRUSTED_VOLATILE_GUEST *pCmdTodo, uint32_t cbCmd) +{ + VBOXCMDVBVA_CRCMD_CMD const *pCmd = (VBOXCMDVBVA_CRCMD_CMD const *)pCmdTodo; + int32_t rc; + uint32_t cBuffers = pCmd->cBuffers; + uint32_t cParams; + uint32_t cbHdr; + CRVBOXHGSMIHDR *pHdr; + uint32_t u32Function; + uint32_t u32ClientID; + CRClient *pClient; + + if (!g_pvVRamBase) + { + WARN(("g_pvVRamBase is not initialized")); + return VERR_INVALID_STATE; + } + + if (!cBuffers) + { + WARN(("zero buffers passed in!")); + return VERR_INVALID_PARAMETER; + } + + cParams = cBuffers-1; + + if (cbCmd < RT_UOFFSETOF_DYN(VBOXCMDVBVA_CRCMD_CMD, aBuffers[cBuffers])) + { + WARN(("invalid buffer size")); + return VERR_INVALID_PARAMETER; + } + + cbHdr = pCmd->aBuffers[0].cbBuffer; + pHdr = VBOXCRHGSMI_PTR_SAFE(pCmd->aBuffers[0].offBuffer, cbHdr, CRVBOXHGSMIHDR); + if (!pHdr) + { + WARN(("invalid header buffer!")); + return VERR_INVALID_PARAMETER; + } + + if (cbHdr < sizeof (*pHdr)) + { + WARN(("invalid header buffer size!")); + return VERR_INVALID_PARAMETER; + } + + u32Function = pHdr->u32Function; + u32ClientID = pHdr->u32ClientID; + + switch (u32Function) + { + case SHCRGL_GUEST_FN_WRITE: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE\n")); + + /** @todo Verify */ + if (cParams == 1) + { + CRVBOXHGSMIWRITE* pFnCmd = (CRVBOXHGSMIWRITE*)pHdr; + const VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + WARN(("invalid write cmd buffer size!")); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbBuffer); + if (!pBuffer) + { + WARN(("invalid buffer data received from guest!")); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + WARN(("crVBoxServerClientGet failed %d", rc)); + break; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, false); + crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return VINF_SUCCESS; + } + + WARN(("invalid number of args")); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_INJECT: + { + WARN(("svcCall: SHCRGL_GUEST_FN_INJECT\n")); + + /** @todo Verify */ + if (cParams == 1) + { + CRVBOXHGSMIINJECT *pFnCmd = (CRVBOXHGSMIINJECT*)pHdr; + /* Fetch parameters. */ + uint32_t u32InjectClientID = pFnCmd->u32ClientID; + const VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + WARN(("invalid inject cmd buffer size!")); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbBuffer); + if (!pBuffer) + { + WARN(("invalid buffer data received from guest!")); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32InjectClientID, &pClient); + if (RT_FAILURE(rc)) + { + WARN(("crVBoxServerClientGet failed %d", rc)); + break; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, false); + crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return VINF_SUCCESS; + } + + WARN(("invalid number of args")); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_READ: + { + Log(("svcCall: SHCRGL_GUEST_FN_READ\n")); + + /** @todo Verify */ + if (cParams == 1) + { + CRVBOXHGSMIREAD *pFnCmd = (CRVBOXHGSMIREAD*)pHdr; + const VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + WARN(("invalid read cmd buffer size!")); + rc = VERR_INVALID_PARAMETER; + break; + } + + if (!pBuffer) + { + WARN(("invalid buffer data received from guest!")); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + WARN(("crVBoxServerClientGet failed %d", rc)); + break; + } + + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + rc = crVBoxServerInternalClientRead(pClient, pBuffer, &cbBuffer); + + /* Return the required buffer size always */ + pFnCmd->cbBuffer = cbBuffer; + + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + /* the read command is never pended, complete it right away */ + if (RT_FAILURE(rc)) + { + WARN(("crVBoxServerInternalClientRead failed %d", rc)); + break; + } + + break; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_WRITE_READ: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n")); + + /** @todo Verify */ + if (cParams == 2) + { + CRVBOXHGSMIWRITEREAD *pFnCmd = (CRVBOXHGSMIWRITEREAD*)pHdr; + const VBOXCMDVBVA_CRCMD_BUFFER *pBuf = &pCmd->aBuffers[1]; + const VBOXCMDVBVA_CRCMD_BUFFER *pWbBuf = &pCmd->aBuffers[2]; + + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + uint32_t cbWriteback = pWbBuf->cbBuffer; + char *pWriteback = VBOXCRHGSMI_PTR_SAFE(pWbBuf->offBuffer, cbWriteback, char); + + if (cbHdr < sizeof (*pFnCmd)) + { + WARN(("invalid write_read cmd buffer size!")); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbBuffer); + if (!pBuffer) + { + WARN(("invalid write buffer data received from guest!")); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbWriteback); + if (!pWriteback) + { + WARN(("invalid writeback buffer data received from guest!")); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + WARN(("crVBoxServerClientGet failed %d", rc)); + break; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback, false); + crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return VINF_SUCCESS; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_SET_VERSION: + { + WARN(("SHCRGL_GUEST_FN_SET_VERSION: invalid function")); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + case SHCRGL_GUEST_FN_SET_PID: + { + WARN(("SHCRGL_GUEST_FN_SET_PID: invalid function")); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + default: + { + WARN(("invalid function, %d", u32Function)); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + } + + pHdr->result = rc; + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) crVBoxCrCmdEnable(HVBOXCRCMDSVR hSvr, VBOXCRCMD_SVRENABLE_INFO *pInfo) +{ + Assert(!cr_server.fCrCmdEnabled); + Assert(!cr_server.numClients); + + cr_server.CrCmdClientInfo = *pInfo; + + crVBoxServerDefaultContextSet(); + + cr_server.fCrCmdEnabled = GL_TRUE; + + crInfo("crCmd ENABLED"); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) crVBoxCrCmdDisable(HVBOXCRCMDSVR hSvr) +{ + Assert(cr_server.fCrCmdEnabled); + + crVBoxServerRemoveAllClients(); + + CrHTableEmpty(&cr_server.clientTable); + + crVBoxServerDefaultContextClear(); + + memset(&cr_server.CrCmdClientInfo, 0, sizeof (cr_server.CrCmdClientInfo)); + + cr_server.fCrCmdEnabled = GL_FALSE; + + crInfo("crCmd DISABLED"); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) crVBoxCrCmdHostCtl(HVBOXCRCMDSVR hSvr, uint8_t* pCmd, uint32_t cbCmd) +{ + return crVBoxServerHostCtl((VBOXCRCMDCTL*)pCmd, cbCmd); +} + +static int crVBoxCrDisconnect(uint32_t u32Client) +{ + CRClient *pClient = (CRClient*)CrHTableRemove(&cr_server.clientTable, u32Client); + if (!pClient) + { + WARN(("invalid client id")); + return VERR_INVALID_PARAMETER; + } + + crVBoxServerRemoveClientObj(pClient); + + return VINF_SUCCESS; +} + +static int crVBoxCrConnectEx(VBOXCMDVBVA_3DCTL_CONNECT RT_UNTRUSTED_VOLATILE_GUEST *pConnect, uint32_t u32ClientId) +{ + CRClient *pClient; + int rc; + uint32_t const uMajorVersion = pConnect->u32MajorVersion; + uint32_t const uMinorVersion = pConnect->u32MinorVersion; + uint64_t const uPid = pConnect->u64Pid; + RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); + + if (u32ClientId == CRHTABLE_HANDLE_INVALID) + { + /* allocate client id */ + u32ClientId = CrHTablePut(&cr_server.clientTable, (void *)(uintptr_t)1); + if (u32ClientId == CRHTABLE_HANDLE_INVALID) + { + WARN(("CrHTablePut failed")); + return VERR_NO_MEMORY; + } + } + + rc = crVBoxServerAddClientObj(u32ClientId, &pClient); + if (RT_SUCCESS(rc)) + { + rc = crVBoxServerClientObjSetVersion(pClient, uMajorVersion, uMinorVersion); + if (RT_SUCCESS(rc)) + { + rc = crVBoxServerClientObjSetPID(pClient, uPid); + if (RT_SUCCESS(rc)) + { + rc = CrHTablePutToSlot(&cr_server.clientTable, u32ClientId, pClient); + if (RT_SUCCESS(rc)) + { + pConnect->Hdr.u32CmdClientId = u32ClientId; + return VINF_SUCCESS; + } + WARN(("CrHTablePutToSlot failed %d", rc)); + } + else + WARN(("crVBoxServerClientObjSetPID failed %d", rc)); + } + else + WARN(("crVBoxServerClientObjSetVersion failed %d", rc)); + + crVBoxServerRemoveClientObj(pClient); + } + else + WARN(("crVBoxServerAddClientObj failed %d", rc)); + + CrHTableRemove(&cr_server.clientTable, u32ClientId); + + return rc; +} + +static int crVBoxCrConnect(VBOXCMDVBVA_3DCTL_CONNECT RT_UNTRUSTED_VOLATILE_GUEST *pConnect) +{ + return crVBoxCrConnectEx(pConnect, CRHTABLE_HANDLE_INVALID); +} + +/** + * @interface_method_impl{VBOXCRCMD_SVRINFO,pfnGuestCtl} + */ +static DECLCALLBACK(int) crVBoxCrCmdGuestCtl(HVBOXCRCMDSVR hSvr, uint8_t RT_UNTRUSTED_VOLATILE_GUEST *pbCmd, uint32_t cbCmd) +{ + /* + * Toplevel input validation. + */ + ASSERT_GUEST_LOGREL_RETURN(cbCmd >= sizeof(VBOXCMDVBVA_3DCTL), VERR_INVALID_PARAMETER); + { + VBOXCMDVBVA_3DCTL RT_UNTRUSTED_VOLATILE_GUEST *pCtl = (VBOXCMDVBVA_3DCTL RT_UNTRUSTED_VOLATILE_GUEST*)pbCmd; + const uint32_t uType = pCtl->u32Type; + RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); + + ASSERT_GUEST_LOGREL_RETURN( uType == VBOXCMDVBVA3DCTL_TYPE_CMD + || uType == VBOXCMDVBVA3DCTL_TYPE_CONNECT + || uType == VBOXCMDVBVA3DCTL_TYPE_DISCONNECT + , VERR_INVALID_PARAMETER); + RT_UNTRUSTED_VALIDATED_FENCE(); + + /* + * Call worker abd process the request. + */ + switch (uType) + { + case VBOXCMDVBVA3DCTL_TYPE_CMD: + ASSERT_GUEST_LOGREL_RETURN(cbCmd >= sizeof(VBOXCMDVBVA_3DCTL_CMD), VERR_INVALID_PARAMETER); + { + VBOXCMDVBVA_3DCTL_CMD RT_UNTRUSTED_VOLATILE_GUEST *p3DCmd + = (VBOXCMDVBVA_3DCTL_CMD RT_UNTRUSTED_VOLATILE_GUEST *)pbCmd; + return crVBoxCrCmdCmd(NULL, &p3DCmd->Cmd, cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_3DCTL_CMD, Cmd)); + } + + case VBOXCMDVBVA3DCTL_TYPE_CONNECT: + ASSERT_GUEST_LOGREL_RETURN(cbCmd == sizeof(VBOXCMDVBVA_3DCTL_CONNECT), VERR_INVALID_PARAMETER); + return crVBoxCrConnect((VBOXCMDVBVA_3DCTL_CONNECT RT_UNTRUSTED_VOLATILE_GUEST *)pCtl); + + case VBOXCMDVBVA3DCTL_TYPE_DISCONNECT: + ASSERT_GUEST_LOGREL_RETURN(cbCmd == sizeof(VBOXCMDVBVA_3DCTL), VERR_INVALID_PARAMETER); + { + uint32_t idClient = pCtl->u32CmdClientId; + RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); + return crVBoxCrDisconnect(idClient); + } + + default: + AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE); + } + } +} + +static DECLCALLBACK(int) crVBoxCrCmdResize(HVBOXCRCMDSVR hSvr, const struct VBVAINFOSCREEN *pScreen, const uint32_t *pTargetMap) +{ + CRASSERT(cr_server.fCrCmdEnabled); + return CrPMgrResize(pScreen, NULL, pTargetMap); +} + +static const char* gszVBoxOGLSSMMagic = "***OpenGL state data***"; + +static int crVBoxCrCmdSaveClients(PSSMHANDLE pSSM) +{ + int i; + int rc = SSMR3PutU32(pSSM, cr_server.numClients); + AssertRCReturn(rc, rc); + + for (i = 0; i < cr_server.numClients; i++) + { + CRClient * pClient = cr_server.clients[i]; + Assert(pClient); + + rc = SSMR3PutU32(pSSM, pClient->conn->u32ClientID); + AssertRCReturn(rc, rc); + rc = SSMR3PutU32(pSSM, pClient->conn->vMajor); + AssertRCReturn(rc, rc); + rc = SSMR3PutU32(pSSM, pClient->conn->vMinor); + AssertRCReturn(rc, rc); + rc = SSMR3PutU64(pSSM, pClient->pid); + AssertRCReturn(rc, rc); + } + + return VINF_SUCCESS; +} + +static int crVBoxCrCmdLoadClients(PSSMHANDLE pSSM, uint32_t u32Version) +{ + uint32_t i; + uint32_t u32; + VBOXCMDVBVA_3DCTL_CONNECT Connect; + int rc = SSMR3GetU32(pSSM, &u32); + AssertLogRelRCReturn(rc, rc); + + for (i = 0; i < u32; i++) + { + uint32_t u32ClientID; + Connect.Hdr.u32Type = VBOXCMDVBVA3DCTL_TYPE_CONNECT; + Connect.Hdr.u32CmdClientId = 0; + + rc = SSMR3GetU32(pSSM, &u32ClientID); + AssertLogRelRCReturn(rc, rc); + rc = SSMR3GetU32(pSSM, &Connect.u32MajorVersion); + AssertLogRelRCReturn(rc, rc); + rc = SSMR3GetU32(pSSM, &Connect.u32MinorVersion); + AssertLogRelRCReturn(rc, rc); + rc = SSMR3GetU64(pSSM, &Connect.u64Pid); + AssertLogRelRCReturn(rc, rc); + + rc = crVBoxCrConnectEx(&Connect, u32ClientID); + AssertLogRelRCReturn(rc, rc); + } + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) crVBoxCrCmdSaveState(HVBOXCRCMDSVR hSvr, PSSMHANDLE pSSM) +{ + int rc = VINF_SUCCESS; + + Assert(cr_server.fCrCmdEnabled); + + /* Start*/ + rc = SSMR3PutStrZ(pSSM, gszVBoxOGLSSMMagic); + AssertRCReturn(rc, rc); + + if (!cr_server.numClients) + { + rc = SSMR3PutU32(pSSM, 0); + AssertRCReturn(rc, rc); + + rc = SSMR3PutStrZ(pSSM, gszVBoxOGLSSMMagic); + AssertRCReturn(rc, rc); + + return VINF_SUCCESS; + } + + rc = SSMR3PutU32(pSSM, 1); + AssertRCReturn(rc, rc); + + /* Version */ + rc = SSMR3PutU32(pSSM, (uint32_t) SHCROGL_SSM_VERSION); + AssertRCReturn(rc, rc); + + rc = crVBoxCrCmdSaveClients(pSSM); + AssertRCReturn(rc, rc); + + /* The state itself */ + rc = crVBoxServerSaveStatePerform(pSSM); + AssertRCReturn(rc, rc); + + /* Save svc buffers info */ + { + rc = SSMR3PutU32(pSSM, 0); + AssertRCReturn(rc, rc); + + rc = SSMR3PutU32(pSSM, 0); + AssertRCReturn(rc, rc); + } + + /* End */ + rc = SSMR3PutStrZ(pSSM, gszVBoxOGLSSMMagic); + AssertRCReturn(rc, rc); + + return VINF_SUCCESS; +} + +static DECLCALLBACK(int) crVBoxCrCmdLoadState(HVBOXCRCMDSVR hSvr, PSSMHANDLE pSSM, uint32_t u32Version) +{ + int rc = VINF_SUCCESS; + + char szBuf[2000]; + uint32_t ui32; + + Assert(cr_server.fCrCmdEnabled); + + /* Start of data */ + rc = SSMR3GetStrZEx(pSSM, szBuf, sizeof(szBuf), NULL); + AssertLogRelRCReturn(rc, rc); + AssertLogRelMsgReturn(!strcmp(gszVBoxOGLSSMMagic, szBuf), ("Unexpected data1: '%s'\n", szBuf), VERR_SSM_UNEXPECTED_DATA); + + /* num clients */ + rc = SSMR3GetU32(pSSM, &ui32); + AssertLogRelRCReturn(rc, rc); + + if (!ui32) + { + /* no clients, dummy stub */ + rc = SSMR3GetStrZEx(pSSM, szBuf, sizeof(szBuf), NULL); + AssertLogRelRCReturn(rc, rc); + AssertLogRelMsgReturn(!strcmp(gszVBoxOGLSSMMagic, szBuf), ("Unexpected data2: '%s'\n", szBuf), VERR_SSM_UNEXPECTED_DATA); + + return VINF_SUCCESS; + } + AssertLogRelMsgReturn(ui32 == 1, ("Invalid client count: %#x\n", ui32), VERR_SSM_UNEXPECTED_DATA); + + /* Version */ + rc = SSMR3GetU32(pSSM, &ui32); + AssertLogRelRCReturn(rc, rc); + AssertLogRelMsgReturn(ui32 >= SHCROGL_SSM_VERSION_CRCMD, ("Unexpected version: %#x\n", ui32), VERR_SSM_UNEXPECTED_DATA); + + rc = crVBoxCrCmdLoadClients(pSSM, u32Version); + AssertLogRelRCReturn(rc, rc); + + /* The state itself */ + rc = crVBoxServerLoadStatePerform(pSSM, ui32); + AssertLogRelRCReturn(rc, rc); + + /* Save svc buffers info */ + { + rc = SSMR3GetU32(pSSM, &ui32); + AssertLogRelRCReturn(rc, rc); + AssertLogRelMsgReturn(ui32 == 0, ("Unexpected data3: %#x\n", ui32), VERR_SSM_UNEXPECTED_DATA); + + rc = SSMR3GetU32(pSSM, &ui32); + AssertLogRelRCReturn(rc, rc); + AssertLogRelMsgReturn(ui32 == 0, ("Unexpected data4: %#x\n", ui32), VERR_SSM_UNEXPECTED_DATA); + } + + /* End */ + rc = SSMR3GetStrZEx(pSSM, szBuf, sizeof(szBuf), NULL); + AssertLogRelRCReturn(rc, rc); + AssertLogRelMsgReturn(!strcmp(gszVBoxOGLSSMMagic, szBuf), ("Unexpected data5: '%s'\n", szBuf), VERR_SSM_UNEXPECTED_DATA); + + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{VBOXCRCMD_SVRINFO,pfnCmd} + */ +static DECLCALLBACK(int8_t) crVBoxCrCmdCmd(HVBOXCRCMDSVR hSvr, + const VBOXCMDVBVA_HDR RT_UNTRUSTED_VOLATILE_GUEST *pCmd, uint32_t cbCmd) +{ + uint8_t bOpcode = pCmd->u8OpCode; + RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); + ASSERT_GUEST_LOGREL_MSG_RETURN( bOpcode == VBOXCMDVBVA_OPTYPE_CRCMD + || bOpcode == VBOXCMDVBVA_OPTYPE_FLIP + || bOpcode == VBOXCMDVBVA_OPTYPE_BLT + || bOpcode == VBOXCMDVBVA_OPTYPE_CLRFILL, + ("%#x\n", bOpcode), -1); + RT_UNTRUSTED_VALIDATED_FENCE(); + + switch (bOpcode) + { + case VBOXCMDVBVA_OPTYPE_CRCMD: + ASSERT_GUEST_LOGREL_MSG_RETURN(cbCmd >= sizeof(VBOXCMDVBVA_CRCMD), ("cbCmd=%u\n", cbCmd), -1); + { + VBOXCMDVBVA_CRCMD const RT_UNTRUSTED_VOLATILE_GUEST *pCrCmdDr + = (VBOXCMDVBVA_CRCMD const RT_UNTRUSTED_VOLATILE_GUEST *)pCmd; + VBOXCMDVBVA_CRCMD_CMD const RT_UNTRUSTED_VOLATILE_GUEST *pCrCmd = &pCrCmdDr->Cmd; + int rc = crVBoxServerCmdVbvaCrCmdProcess(pCrCmd, cbCmd - RT_UOFFSETOF(VBOXCMDVBVA_CRCMD, Cmd)); + ASSERT_GUEST_LOGREL_RC_RETURN(rc, -1); + return 0; + } + + case VBOXCMDVBVA_OPTYPE_FLIP: + ASSERT_GUEST_LOGREL_MSG_RETURN(cbCmd >= VBOXCMDVBVA_SIZEOF_FLIPSTRUCT_MIN, ("cbCmd=%u\n", cbCmd), -1); + { + VBOXCMDVBVA_FLIP const RT_UNTRUSTED_VOLATILE_GUEST *pFlip + = (VBOXCMDVBVA_FLIP const RT_UNTRUSTED_VOLATILE_GUEST *)pCmd; + return crVBoxServerCrCmdFlipProcess(pFlip, cbCmd); + } + + case VBOXCMDVBVA_OPTYPE_BLT: + ASSERT_GUEST_LOGREL_MSG_RETURN(cbCmd >= sizeof(VBOXCMDVBVA_BLT_HDR), ("cbCmd=%u\n", cbCmd), -1); + return crVBoxServerCrCmdBltProcess((VBOXCMDVBVA_BLT_HDR const RT_UNTRUSTED_VOLATILE_GUEST *)pCmd, cbCmd); + + case VBOXCMDVBVA_OPTYPE_CLRFILL: + ASSERT_GUEST_LOGREL_MSG_RETURN(cbCmd >= sizeof(VBOXCMDVBVA_CLRFILL_HDR), ("cbCmd=%u\n", cbCmd), -1); + return crVBoxServerCrCmdClrFillProcess((VBOXCMDVBVA_CLRFILL_HDR const RT_UNTRUSTED_VOLATILE_GUEST *)pCmd, cbCmd); + + default: + AssertFailedReturn(-1); + } + /* not reached */ +} + +/* We moved all CrHgsmi command processing to crserverlib to keep the logic of dealing with CrHgsmi commands in one place. + * + * For now we need the notion of CrHgdmi commands in the crserver_lib to be able to complete it asynchronously once it is really processed. + * This help avoiding the "blocked-client" issues. The client is blocked if another client is doing begin-end stuff. + * For now we eliminated polling that could occur on block, which caused a higher-priority thread (in guest) polling for the blocked command complition + * to block the lower-priority thread trying to complete the blocking command. + * And removed extra memcpy done on blocked command arrival. + * + * In the future we will extend CrHgsmi functionality to maintain texture data directly in CrHgsmi allocation to avoid extra memcpy-ing with PBO, + * implement command completion and stuff necessary for GPU scheduling to work properly for WDDM Windows guests, etc. + * + * NOTE: it is ALWAYS responsibility of the crVBoxServerCrHgsmiCmd to complete the command! + * */ + + +int32_t crVBoxServerCrHgsmiCmd(struct VBOXVDMACMD_CHROMIUM_CMD *pCmd, uint32_t cbCmd) +{ + + int32_t rc; + uint32_t cBuffers = pCmd->cBuffers; + uint32_t cParams; + uint32_t cbHdr; + CRVBOXHGSMIHDR *pHdr; + uint32_t u32Function; + uint32_t u32ClientID; + CRClient *pClient; + + if (!g_pvVRamBase) + { + WARN(("g_pvVRamBase is not initialized")); + + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_STATE); + return VINF_SUCCESS; + } + + if (!cBuffers) + { + WARN(("zero buffers passed in!")); + + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER); + return VINF_SUCCESS; + } + + cParams = cBuffers-1; + + cbHdr = pCmd->aBuffers[0].cbBuffer; + pHdr = VBOXCRHGSMI_PTR_SAFE(pCmd->aBuffers[0].offBuffer, cbHdr, CRVBOXHGSMIHDR); + if (!pHdr) + { + WARN(("invalid header buffer!")); + + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER); + return VINF_SUCCESS; + } + + if (cbHdr < sizeof (*pHdr)) + { + WARN(("invalid header buffer size!")); + + crServerCrHgsmiCmdComplete(pCmd, VERR_INVALID_PARAMETER); + return VINF_SUCCESS; + } + + u32Function = pHdr->u32Function; + u32ClientID = pHdr->u32ClientID; + + switch (u32Function) + { + case SHCRGL_GUEST_FN_WRITE: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE\n")); + + /** @todo Verify */ + if (cParams == 1) + { + CRVBOXHGSMIWRITE* pFnCmd = (CRVBOXHGSMIWRITE*)pHdr; + VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1]; + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid write cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbBuffer); + if (!pBuffer) + { + crWarning("invalid buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + break; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, true); + crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return VINF_SUCCESS; + } + else + { + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + break; + } + + case SHCRGL_GUEST_FN_INJECT: + { + Log(("svcCall: SHCRGL_GUEST_FN_INJECT\n")); + + /** @todo Verify */ + if (cParams == 1) + { + CRVBOXHGSMIINJECT *pFnCmd = (CRVBOXHGSMIINJECT*)pHdr; + /* Fetch parameters. */ + uint32_t u32InjectClientID = pFnCmd->u32ClientID; + VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1]; + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid inject cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbBuffer); + if (!pBuffer) + { + crWarning("invalid buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32InjectClientID, &pClient); + if (RT_FAILURE(rc)) + { + break; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SET(&pClient->conn->CmdData, pCmd, pHdr, true); + crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return VINF_SUCCESS; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_READ: + { + Log(("svcCall: SHCRGL_GUEST_FN_READ\n")); + + /** @todo Verify */ + if (cParams == 1) + { + CRVBOXHGSMIREAD *pFnCmd = (CRVBOXHGSMIREAD*)pHdr; + VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1]; + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid read cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + + if (!pBuffer) + { + crWarning("invalid buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + break; + } + + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + rc = crVBoxServerInternalClientRead(pClient, pBuffer, &cbBuffer); + + /* Return the required buffer size always */ + pFnCmd->cbBuffer = cbBuffer; + + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + /* the read command is never pended, complete it right away */ + pHdr->result = rc; + + crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS); + return VINF_SUCCESS; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_WRITE_READ: + { + Log(("svcCall: SHCRGL_GUEST_FN_WRITE_READ\n")); + + /** @todo Verify */ + if (cParams == 2) + { + CRVBOXHGSMIWRITEREAD *pFnCmd = (CRVBOXHGSMIWRITEREAD*)pHdr; + VBOXVDMACMD_CHROMIUM_BUFFER *pBuf = &pCmd->aBuffers[1]; + VBOXVDMACMD_CHROMIUM_BUFFER *pWbBuf = &pCmd->aBuffers[2]; + + /* Fetch parameters. */ + uint32_t cbBuffer = pBuf->cbBuffer; + uint8_t *pBuffer = VBOXCRHGSMI_PTR_SAFE(pBuf->offBuffer, cbBuffer, uint8_t); + + uint32_t cbWriteback = pWbBuf->cbBuffer; + char *pWriteback = VBOXCRHGSMI_PTR_SAFE(pWbBuf->offBuffer, cbWriteback, char); + + if (cbHdr < sizeof (*pFnCmd)) + { + crWarning("invalid write_read cmd buffer size!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + + CRASSERT(cbBuffer); + if (!pBuffer) + { + crWarning("invalid write buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + + CRASSERT(cbWriteback); + if (!pWriteback) + { + crWarning("invalid writeback buffer data received from guest!"); + rc = VERR_INVALID_PARAMETER; + break; + } + rc = crVBoxServerClientGet(u32ClientID, &pClient); + if (RT_FAILURE(rc)) + { + pHdr->result = rc; + crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS); + return rc; + } + + /* This should never fire unless we start to multithread */ + CRASSERT(pClient->conn->pBuffer==NULL && pClient->conn->cbBuffer==0); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + + pClient->conn->pBuffer = pBuffer; + pClient->conn->cbBuffer = cbBuffer; + CRVBOXHGSMI_CMDDATA_SETWB(&pClient->conn->CmdData, pCmd, pHdr, pWriteback, cbWriteback, &pFnCmd->cbWriteback, true); + crVBoxServerInternalClientWriteRead(pClient); + CRVBOXHGSMI_CMDDATA_ASSERT_CLEANED(&pClient->conn->CmdData); + return VINF_SUCCESS; + } + + crWarning("invalid number of args"); + rc = VERR_INVALID_PARAMETER; + break; + } + + case SHCRGL_GUEST_FN_SET_VERSION: + { + WARN(("crVBoxServerCrHgsmiCmd, SHCRGL_GUEST_FN_SET_VERSION: invalid function")); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + case SHCRGL_GUEST_FN_SET_PID: + { + WARN(("crVBoxServerCrHgsmiCmd, SHCRGL_GUEST_FN_SET_PID: invalid function")); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + default: + { + WARN(("crVBoxServerCrHgsmiCmd: invalid functionm %d", u32Function)); + rc = VERR_NOT_IMPLEMENTED; + break; + } + + } + + /* we can be on fail only here */ + CRASSERT(RT_FAILURE(rc)); + pHdr->result = rc; + + crServerCrHgsmiCmdComplete(pCmd, VINF_SUCCESS); + return rc; + +} + + +static DECLCALLBACK(bool) crVBoxServerHasDataForScreen(uint32_t u32ScreenID) +{ + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabledForScreen(u32ScreenID); + if (hFb) + return CrFbHas3DData(hFb); + + return false; +} + + +static DECLCALLBACK(bool) crVBoxServerHasData(void) +{ + HCR_FRAMEBUFFER hFb = CrPMgrFbGetFirstEnabled(); + for (; + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + if (CrFbHas3DData(hFb)) + return true; + } + + return false; +} + +int32_t crVBoxServerCrHgsmiCtl(struct VBOXVDMACMD_CHROMIUM_CTL *pCtl, uint32_t cbCtl) +{ + int rc = VINF_SUCCESS; + + switch (pCtl->enmType) + { + case VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP: + { + PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP)pCtl; + g_pvVRamBase = (uint8_t*)pSetup->pvVRamBase; + g_cbVRam = pSetup->cbVRam; + + g_pLed = pSetup->pLed; + + cr_server.ClientInfo = pSetup->CrClientInfo; + + pSetup->CrCmdServerInfo.hSvr = NULL; + pSetup->CrCmdServerInfo.pfnEnable = crVBoxCrCmdEnable; + pSetup->CrCmdServerInfo.pfnDisable = crVBoxCrCmdDisable; + pSetup->CrCmdServerInfo.pfnCmd = crVBoxCrCmdCmd; + pSetup->CrCmdServerInfo.pfnHostCtl = crVBoxCrCmdHostCtl; + pSetup->CrCmdServerInfo.pfnGuestCtl = crVBoxCrCmdGuestCtl; + pSetup->CrCmdServerInfo.pfnResize = crVBoxCrCmdResize; + pSetup->CrCmdServerInfo.pfnSaveState = crVBoxCrCmdSaveState; + pSetup->CrCmdServerInfo.pfnLoadState = crVBoxCrCmdLoadState; + rc = VINF_SUCCESS; + break; + } + case VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_BEGIN: + case VBOXVDMACMD_CHROMIUM_CTL_TYPE_SAVESTATE_END: + rc = VINF_SUCCESS; + break; + case VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB: + { + PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB pSetup = (PVBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB)pCtl; + g_hCrHgsmiCompletion = pSetup->hCompletion; + g_pfnCrHgsmiCompletion = pSetup->pfnCompletion; + + pSetup->MainInterface.pfnHasData = crVBoxServerHasData; + pSetup->MainInterface.pfnHasDataForScreen = crVBoxServerHasDataForScreen; + + rc = VINF_SUCCESS; + break; + } + default: + AssertMsgFailed(("invalid param %d", pCtl->enmType)); + rc = VERR_INVALID_PARAMETER; + } + + /* NOTE: Control commands can NEVER be pended here, this is why its a task of a caller (Main) + * to complete them accordingly. + * This approach allows using host->host and host->guest commands in the same way here + * making the command completion to be the responsibility of the command originator. + * E.g. ctl commands can be both Hgcm Host synchronous commands that do not require completion at all, + * or Hgcm Host Fast Call commands that do require completion. All this details are hidden here */ + return rc; +} + +static int crVBoxServerCrCmdDisablePostProcess(VBOXCRCMDCTL_HGCMENABLE_DATA *pData) +{ + int rc = VINF_SUCCESS; + uint8_t* pCtl; + uint32_t cbCtl; + HVBOXCRCMDCTL_REMAINING_HOST_COMMAND hRHCmd = pData->hRHCmd; + PFNVBOXCRCMDCTL_REMAINING_HOST_COMMAND pfnRHCmd = pData->pfnRHCmd; + + Assert(!cr_server.fCrCmdEnabled); + + if (cr_server.numClients) + { + WARN(("cr_server.numClients(%d) is not NULL", cr_server.numClients)); + return VERR_INVALID_STATE; + } + + for (pCtl = pfnRHCmd(hRHCmd, &cbCtl, rc); pCtl; pCtl = pfnRHCmd(hRHCmd, &cbCtl, rc)) + { + rc = crVBoxCrCmdHostCtl(NULL, pCtl, cbCtl); + } + + memset(&cr_server.DisableData, 0, sizeof (cr_server.DisableData)); + + return VINF_SUCCESS; +} + +int32_t crVBoxServerHgcmEnable(VBOXCRCMDCTL_HGCMENABLE_DATA *pData) +{ + int rc = crVBoxServerCrCmdDisablePostProcess(pData); + if (RT_FAILURE(rc)) + { + WARN(("crVBoxServerCrCmdDisablePostProcess failed %d", rc)); + return rc; + } + + crVBoxServerDefaultContextSet(); + + return VINF_SUCCESS; +} + +int32_t crVBoxServerHgcmDisable(VBOXCRCMDCTL_HGCMDISABLE_DATA *pData) +{ + Assert(!cr_server.fCrCmdEnabled); + + Assert(!cr_server.numClients); + + crVBoxServerRemoveAllClients(); + + CRASSERT(!cr_server.numClients); + + crVBoxServerDefaultContextClear(); + + cr_server.DisableData = *pData; + + return VINF_SUCCESS; +} + +#endif diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c new file mode 100644 index 00000000..b9b53008 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_misc.c @@ -0,0 +1,2016 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "server_dispatch.h" +#include "server.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_string.h" +#include "cr_pixeldata.h" +#ifdef VBOX_WITH_CRDUMPER +# include "cr_dump.h" +#endif + +void SERVER_DISPATCH_APIENTRY crServerDispatchSelectBuffer( GLsizei size, GLuint *buffer ) +{ + (void) size; + (void) buffer; + crError( "Unsupported network glSelectBuffer call." ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetChromiumParametervCR(GLenum target, GLuint index, GLenum type, GLsizei count, GLvoid *values) +{ + GLubyte local_storage[4096]; + GLint bytes = 0; + GLint cbType = 1; /* One byte by default. */ + + memset(local_storage, 0, sizeof(local_storage)); + + switch (type) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + cbType = sizeof(GLbyte); + break; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + cbType = sizeof(GLshort); + break; + case GL_INT: + case GL_UNSIGNED_INT: + cbType = sizeof(GLint); + break; + case GL_FLOAT: + cbType = sizeof(GLfloat); + break; + case GL_DOUBLE: + cbType = sizeof(GLdouble); + break; + default: + crError("Bad type in crServerDispatchGetChromiumParametervCR"); + } + + if (count < 0) /* 'GLsizei' is usually an 'int'. */ + count = 0; + else if ((size_t)count > sizeof(local_storage) / cbType) + count = sizeof(local_storage) / cbType; + + bytes = count * cbType; + + CRASSERT(bytes >= 0); + CRASSERT(bytes < 4096); + + switch (target) + { + case GL_DBG_CHECK_BREAK_CR: + { + if (bytes > 0) + { + GLubyte *pbRc = local_storage; + GLuint *puRc = (GLuint *)(bytes >=4 ? local_storage : NULL); + int rc; + memset(local_storage, 0, bytes); + if (cr_server.RcToGuestOnce) + { + rc = cr_server.RcToGuestOnce; + cr_server.RcToGuestOnce = 0; + } + else + { + rc = cr_server.RcToGuest; + } + if (puRc) + *puRc = rc; + else + *pbRc = !!rc; + } + else + { + crWarning("zero bytes for GL_DBG_CHECK_BREAK_CR"); + } + break; + } + case GL_HH_SET_DEFAULT_SHARED_CTX: + WARN(("Recieved GL_HH_SET_DEFAULT_SHARED_CTX from guest, ignoring")); + break; + case GL_HH_SET_CLIENT_CALLOUT: + WARN(("Recieved GL_HH_SET_CLIENT_CALLOUT from guest, ignoring")); + break; + default: + cr_server.head_spu->dispatch_table.GetChromiumParametervCR( target, index, type, count, local_storage ); + break; + } + + crServerReturnValue( local_storage, bytes ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParametervCR(GLenum target, GLenum type, GLsizei count, const GLvoid *values) +{ + CRMuralInfo *mural = cr_server.curClient->currentMural; + static int gather_connect_count = 0; + + switch (target) { + case GL_SHARE_LISTS_CR: + { + CRContextInfo *pCtx[2]; + GLint *ai32Values; + int i; + if (count != 2) + { + WARN(("GL_SHARE_LISTS_CR invalid cound %d", count)); + return; + } + + if (type != GL_UNSIGNED_INT && type != GL_INT) + { + WARN(("GL_SHARE_LISTS_CR invalid type %d", type)); + return; + } + + ai32Values = (GLint*)values; + + for (i = 0; i < 2; ++i) + { + const int32_t val = ai32Values[i]; + + if (val == 0) + { + WARN(("GL_SHARE_LISTS_CR invalid value[%d] %d", i, val)); + return; + } + + pCtx[i] = (CRContextInfo *) crHashtableSearch(cr_server.contextTable, val); + if (!pCtx[i]) + { + WARN(("GL_SHARE_LISTS_CR invalid pCtx1 for value[%d] %d", i, val)); + return; + } + + if (!pCtx[i]->pContext) + { + WARN(("GL_SHARE_LISTS_CR invalid pCtx1 pContext for value[%d] %d", i, val)); + return; + } + } + + crStateShareLists(pCtx[0]->pContext, pCtx[1]->pContext); + + break; + } + + case GL_SET_MAX_VIEWPORT_CR: + { + GLint *maxDims = (GLint *)values; + cr_server.limits.maxViewportDims[0] = maxDims[0]; + cr_server.limits.maxViewportDims[1] = maxDims[1]; + } + break; + + case GL_TILE_INFO_CR: + /* message from tilesort SPU to set new tile bounds */ + { + GLint numTiles, muralWidth, muralHeight, server, tiles; + GLint *tileBounds; + CRASSERT(count >= 4); + CRASSERT((count - 4) % 4 == 0); /* must be multiple of four */ + CRASSERT(type == GL_INT); + numTiles = (count - 4) / 4; + tileBounds = (GLint *) values; + server = tileBounds[0]; + muralWidth = tileBounds[1]; + muralHeight = tileBounds[2]; + tiles = tileBounds[3]; + CRASSERT(tiles == numTiles); + tileBounds += 4; /* skip over header values */ + /*crServerNewMuralTiling(mural, muralWidth, muralHeight, numTiles, tileBounds); + mural->viewportValidated = GL_FALSE;*/ + } + break; + + case GL_GATHER_DRAWPIXELS_CR: + if (cr_server.only_swap_once && cr_server.curClient != cr_server.clients[0]) + break; + cr_server.head_spu->dispatch_table.ChromiumParametervCR( target, type, count, values ); + break; + + case GL_GATHER_CONNECT_CR: + /* + * We want the last connect to go through, + * otherwise we might deadlock in CheckWindowSize() + * in the readback spu + */ + gather_connect_count++; + if (cr_server.only_swap_once && (gather_connect_count != cr_server.numClients)) + { + break; + } + cr_server.head_spu->dispatch_table.ChromiumParametervCR( target, type, count, values ); + gather_connect_count = 0; + break; + + case GL_SERVER_VIEW_MATRIX_CR: + /* Set this server's view matrix which will get premultiplied onto the + * modelview matrix. For non-planar tilesort and stereo. + */ + CRASSERT(count == 18); + CRASSERT(type == GL_FLOAT); + /* values[0] is the server index. Ignored here but used in tilesort SPU */ + /* values[1] is the left/right eye index (0 or 1) */ + { + const GLfloat *v = (const GLfloat *) values; + const int eye = v[1] == 0.0 ? 0 : 1; + crMatrixInitFromFloats(&cr_server.viewMatrix[eye], v + 2); + + crDebug("Got GL_SERVER_VIEW_MATRIX_CR:\n" + " %f %f %f %f\n" + " %f %f %f %f\n" + " %f %f %f %f\n" + " %f %f %f %f", + cr_server.viewMatrix[eye].m00, + cr_server.viewMatrix[eye].m10, + cr_server.viewMatrix[eye].m20, + cr_server.viewMatrix[eye].m30, + cr_server.viewMatrix[eye].m01, + cr_server.viewMatrix[eye].m11, + cr_server.viewMatrix[eye].m21, + cr_server.viewMatrix[eye].m31, + cr_server.viewMatrix[eye].m02, + cr_server.viewMatrix[eye].m12, + cr_server.viewMatrix[eye].m22, + cr_server.viewMatrix[eye].m32, + cr_server.viewMatrix[eye].m03, + cr_server.viewMatrix[eye].m13, + cr_server.viewMatrix[eye].m23, + cr_server.viewMatrix[eye].m33); + } + cr_server.viewOverride = GL_TRUE; + break; + + case GL_SERVER_PROJECTION_MATRIX_CR: + /* Set this server's projection matrix which will get replace the user's + * projection matrix. For non-planar tilesort and stereo. + */ + CRASSERT(count == 18); + CRASSERT(type == GL_FLOAT); + /* values[0] is the server index. Ignored here but used in tilesort SPU */ + /* values[1] is the left/right eye index (0 or 1) */ + { + const GLfloat *v = (const GLfloat *) values; + const int eye = v[1] == 0.0 ? 0 : 1; + crMatrixInitFromFloats(&cr_server.projectionMatrix[eye], v + 2); + + crDebug("Got GL_SERVER_PROJECTION_MATRIX_CR:\n" + " %f %f %f %f\n" + " %f %f %f %f\n" + " %f %f %f %f\n" + " %f %f %f %f", + cr_server.projectionMatrix[eye].m00, + cr_server.projectionMatrix[eye].m10, + cr_server.projectionMatrix[eye].m20, + cr_server.projectionMatrix[eye].m30, + cr_server.projectionMatrix[eye].m01, + cr_server.projectionMatrix[eye].m11, + cr_server.projectionMatrix[eye].m21, + cr_server.projectionMatrix[eye].m31, + cr_server.projectionMatrix[eye].m02, + cr_server.projectionMatrix[eye].m12, + cr_server.projectionMatrix[eye].m22, + cr_server.projectionMatrix[eye].m32, + cr_server.projectionMatrix[eye].m03, + cr_server.projectionMatrix[eye].m13, + cr_server.projectionMatrix[eye].m23, + cr_server.projectionMatrix[eye].m33); + + if (cr_server.projectionMatrix[eye].m33 == 0.0f) { + float x = cr_server.projectionMatrix[eye].m00; + float y = cr_server.projectionMatrix[eye].m11; + float a = cr_server.projectionMatrix[eye].m20; + float b = cr_server.projectionMatrix[eye].m21; + float c = cr_server.projectionMatrix[eye].m22; + float d = cr_server.projectionMatrix[eye].m32; + float znear = -d / (1.0f - c); + float zfar = (c - 1.0f) * znear / (c + 1.0f); + float left = znear * (a - 1.0f) / x; + float right = 2.0f * znear / x + left; + float bottom = znear * (b - 1.0f) / y; + float top = 2.0f * znear / y + bottom; + crDebug("Frustum: left, right, bottom, top, near, far: %f, %f, %f, %f, %f, %f", left, right, bottom, top, znear, zfar); + } + else { + /** @todo Add debug output for orthographic projection*/ + } + + } + cr_server.projectionOverride = GL_TRUE; + break; + + case GL_HH_SET_TMPCTX_MAKE_CURRENT: + /*we should not receive it from the guest! */ + break; + + case GL_HH_SET_CLIENT_CALLOUT: + WARN(("Recieved GL_HH_SET_CLIENT_CALLOUT from guest, ignoring")); + break; + + default: + /* Pass the parameter info to the head SPU */ + cr_server.head_spu->dispatch_table.ChromiumParametervCR( target, type, count, values ); + break; + } +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParameteriCR(GLenum target, GLint value) +{ + switch (target) { + case GL_SHARE_CONTEXT_RESOURCES_CR: + crStateShareContext(value); + break; + case GL_RCUSAGE_TEXTURE_SET_CR: + crStateSetTextureUsed(value, GL_TRUE); + break; + case GL_RCUSAGE_TEXTURE_CLEAR_CR: + crStateSetTextureUsed(value, GL_FALSE); + break; + case GL_PIN_TEXTURE_SET_CR: + crStatePinTexture(value, GL_TRUE); + break; + case GL_PIN_TEXTURE_CLEAR_CR: + crStatePinTexture(value, GL_FALSE); + break; + case GL_SHARED_DISPLAY_LISTS_CR: + cr_server.sharedDisplayLists = value; + break; + case GL_SHARED_TEXTURE_OBJECTS_CR: + cr_server.sharedTextureObjects = value; + break; + case GL_SHARED_PROGRAMS_CR: + cr_server.sharedPrograms = value; + break; + case GL_SERVER_CURRENT_EYE_CR: + cr_server.currentEye = value ? 1 : 0; + break; + case GL_HOST_WND_CREATED_HIDDEN_CR: + cr_server.bWindowsInitiallyHidden = value ? 1 : 0; + break; + case GL_HH_SET_DEFAULT_SHARED_CTX: + WARN(("Recieved GL_HH_SET_DEFAULT_SHARED_CTX from guest, ignoring")); + break; + case GL_HH_RENDERTHREAD_INFORM: + WARN(("Recieved GL_HH_RENDERTHREAD_INFORM from guest, ignoring")); + break; + default: + /* Pass the parameter info to the head SPU */ + cr_server.head_spu->dispatch_table.ChromiumParameteriCR( target, value ); + } +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchChromiumParameterfCR(GLenum target, GLfloat value) +{ + switch (target) { + case GL_SHARED_DISPLAY_LISTS_CR: + cr_server.sharedDisplayLists = (int) value; + break; + case GL_SHARED_TEXTURE_OBJECTS_CR: + cr_server.sharedTextureObjects = (int) value; + break; + case GL_SHARED_PROGRAMS_CR: + cr_server.sharedPrograms = (int) value; + break; + default: + /* Pass the parameter info to the head SPU */ + cr_server.head_spu->dispatch_table.ChromiumParameterfCR( target, value ); + } +} + +GLint crServerGenerateID(GLint *pCounter) +{ + return (*pCounter)++; +} + +/*#define CR_DUMP_BLITS*/ + +#ifdef CR_DUMP_BLITS +static int blitnum=0; +static int copynum=0; +#endif + +# ifdef DEBUG_misha +//# define CR_CHECK_BLITS +# include <iprt/assert.h> +# undef CRASSERT /* iprt assert's int3 are inlined that is why are more convenient to use since they can be easily disabled individually */ +# define CRASSERT Assert +# endif + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) +{ + /** @todo pbo/fbo disabled for now as it's slower, check on other gpus*/ + static int siHavePBO = 0; + static int siHaveFBO = 0; + + if ((target!=GL_TEXTURE_2D) || (height>=0)) + { + cr_server.head_spu->dispatch_table.CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + +#ifdef CR_DUMP_BLITS + { + SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; + void *img; + GLint w, h; + char fname[200]; + + copynum++; + + gl->GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); + gl->GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); + + img = crAlloc(w*h*4); + CRASSERT(img); + + gl->GetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, img); + sprintf(fname, "copy_blit%i_copy_%i.tga", blitnum, copynum); + crDumpNamedTGA(fname, w, h, img); + crFree(img); + } +#endif + } + else /* negative height, means we have to Yinvert the source pixels while copying */ + { + SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; + + if (siHavePBO<0) + { + const char *ext = (const char*)gl->GetString(GL_EXTENSIONS); + siHavePBO = crStrstr(ext, "GL_ARB_pixel_buffer_object") ? 1:0; + } + + if (siHaveFBO<0) + { + const char *ext = (const char*)gl->GetString(GL_EXTENSIONS); + siHaveFBO = crStrstr(ext, "GL_EXT_framebuffer_object") ? 1:0; + } + + if (siHavePBO==0 && siHaveFBO==0) + { +#if 1 + GLint dRow, sRow; + for (dRow=yoffset, sRow=y-height-1; dRow<yoffset-height; dRow++, sRow--) + { + gl->CopyTexSubImage2D(target, level, xoffset, dRow, x, sRow, width, 1); + } +#else + { + GLint w, h, i; + char *img1, *img2, *sPtr, *dPtr; + CRContext *ctx = crStateGetCurrent(); + + w = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->level[0][level].width; + h = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->level[0][level].height; + + img1 = crAlloc(4*w*h); + img2 = crAlloc(4*width*(-height)); + CRASSERT(img1 && img2); + + gl->CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, -height); + gl->GetTexImage(target, level, GL_RGBA, GL_UNSIGNED_BYTE, img1); + + sPtr=img1+4*xoffset+4*w*yoffset; + dPtr=img2+4*width*(-height-1); + + for (i=0; i<-height; ++i) + { + crMemcpy(dPtr, sPtr, 4*width); + sPtr += 4*w; + dPtr -= 4*width; + } + + gl->TexSubImage2D(target, level, xoffset, yoffset, width, -height, GL_RGBA, GL_UNSIGNED_BYTE, img2); + + crFree(img1); + crFree(img2); + } +#endif + } + else if (siHaveFBO==1) /** @todo more states to set and restore here*/ + { + GLuint tID, fboID; + GLenum status; + CRContext *ctx = crStateGetCurrent(); + + gl->GenTextures(1, &tID); + gl->BindTexture(target, tID); + gl->CopyTexImage2D(target, level, GL_RGBA, x, y, width, -height, 0); + gl->GenFramebuffersEXT(1, &fboID); + gl->BindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID); + gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, + ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid, level); + status = gl->CheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + crWarning("Framebuffer status 0x%x", status); + } + + gl->Enable(target); + gl->PushAttrib(GL_VIEWPORT_BIT); + gl->Viewport(xoffset, yoffset, width, -height); + gl->MatrixMode(GL_PROJECTION); + gl->PushMatrix(); + gl->LoadIdentity(); + gl->MatrixMode(GL_MODELVIEW); + gl->PushMatrix(); + gl->LoadIdentity(); + + gl->Disable(GL_DEPTH_TEST); + gl->Disable(GL_CULL_FACE); + gl->Disable(GL_STENCIL_TEST); + gl->Disable(GL_SCISSOR_TEST); + + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + gl->Begin(GL_QUADS); + gl->TexCoord2f(0.0f, 1.0f); + gl->Vertex2f(-1.0, -1.0); + + gl->TexCoord2f(0.0f, 0.0f); + gl->Vertex2f(-1.0f, 1.0f); + + gl->TexCoord2f(1.0f, 0.0f); + gl->Vertex2f(1.0f, 1.0f); + + gl->TexCoord2f(1.0f, 1.0f); + gl->Vertex2f(1.0f, -1.0f); + gl->End(); + + gl->PopMatrix(); + gl->MatrixMode(GL_PROJECTION); + gl->PopMatrix(); + gl->PopAttrib(); + + gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, 0, level); + gl->BindFramebufferEXT(GL_FRAMEBUFFER_EXT, ctx->framebufferobject.drawFB ? ctx->framebufferobject.drawFB->hwid:0); + gl->BindTexture(target, ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid); + gl->DeleteFramebuffersEXT(1, &fboID); + gl->DeleteTextures(1, &tID); + +#if 0 + { + GLint dRow, sRow, w, h; + void *img1, *img2; + + w = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->level[0][level].width; + h = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->level[0][level].height; + + img1 = crAlloc(4*w*h); + img2 = crAlloc(4*w*h); + CRASSERT(img1 && img2); + + gl->GetTexImage(target, level, GL_BGRA, GL_UNSIGNED_BYTE, img1); + + + for (dRow=yoffset, sRow=y-height-1; dRow<yoffset-height; dRow++, sRow--) + { + gl->CopyTexSubImage2D(target, level, xoffset, dRow, x, sRow, width, 1); + } + + gl->GetTexImage(target, level, GL_BGRA, GL_UNSIGNED_BYTE, img2); + + if (crMemcmp(img1, img2, 4*w*h)) + { + crDebug("MISMATCH! (%x, %i, ->%i,%i <-%i, %i [%ix%i])", target, level, xoffset, yoffset, x, y, width, height); + crDumpTGA(w, h, img1); + crDumpTGA(w, h, img2); + DebugBreak(); + } + crFree(img1); + crFree(img2); + } +#endif + } + else + { + GLint dRow; + GLuint pboId, sRow; + CRContext *ctx = crStateGetCurrent(); + + gl->GenBuffersARB(1, &pboId); + gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboId); + gl->BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, -width*height*4, 0, GL_STATIC_COPY_ARB); + +#if 1 + gl->ReadPixels(x, y, width, -height, GL_RGBA, GL_UNSIGNED_BYTE, 0); + gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid); + + gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboId); + for (dRow=yoffset, sRow=-height-1; dRow<yoffset-height; dRow++, sRow--) + { + gl->TexSubImage2D(target, level, xoffset, dRow, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)((uintptr_t)sRow*width*4)); + } +#else /*few times slower again*/ + for (dRow=0, sRow=y-height-1; dRow<-height; dRow++, sRow--) + { + gl->ReadPixels(x, sRow, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)((uintptr_t)dRow*width*4)); + } + gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid); + + gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboId); + gl->TexSubImage2D(target, level, xoffset, yoffset, width, -height, GL_RGBA, GL_UNSIGNED_BYTE, 0); +#endif + + gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, ctx->bufferobject.unpackBuffer->hwid); + gl->DeleteBuffersARB(1, &pboId); + } + } +} + +#ifdef CR_CHECK_BLITS +void crDbgFree(void *pvData) +{ + crFree(pvData); +} + +void crDbgGetTexImage2D(GLint texTarget, GLint texName, GLvoid **ppvImage, GLint *pw, GLint *ph) +{ + SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; + GLint ppb, pub, dstw, dsth, otex; + GLint pa, pr, psp, psr, ua, ur, usp, usr; + GLvoid *pvImage; + GLint rfb, dfb, rb, db; + + gl->GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb); + gl->GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb); + gl->GetIntegerv(GL_READ_BUFFER, &rb); + gl->GetIntegerv(GL_DRAW_BUFFER, &db); + + gl->BindFramebufferEXT(GL_READ_FRAMEBUFFER_BINDING_EXT, 0); + gl->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_BINDING_EXT, 0); + gl->ReadBuffer(GL_BACK); + gl->DrawBuffer(GL_BACK); + + gl->GetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &ppb); + gl->GetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &pub); + gl->GetIntegerv(GL_TEXTURE_BINDING_2D, &otex); + + gl->GetIntegerv(GL_PACK_ROW_LENGTH, &pr); + gl->GetIntegerv(GL_PACK_ALIGNMENT, &pa); + gl->GetIntegerv(GL_PACK_SKIP_PIXELS, &psp); + gl->GetIntegerv(GL_PACK_SKIP_ROWS, &psr); + + gl->GetIntegerv(GL_UNPACK_ROW_LENGTH, &ur); + gl->GetIntegerv(GL_UNPACK_ALIGNMENT, &ua); + gl->GetIntegerv(GL_UNPACK_SKIP_PIXELS, &usp); + gl->GetIntegerv(GL_UNPACK_SKIP_ROWS, &usr); + + gl->BindTexture(texTarget, texName); + gl->GetTexLevelParameteriv(texTarget, 0, GL_TEXTURE_WIDTH, &dstw); + gl->GetTexLevelParameteriv(texTarget, 0, GL_TEXTURE_HEIGHT, &dsth); + + gl->PixelStorei(GL_PACK_ROW_LENGTH, 0); + gl->PixelStorei(GL_PACK_ALIGNMENT, 1); + gl->PixelStorei(GL_PACK_SKIP_PIXELS, 0); + gl->PixelStorei(GL_PACK_SKIP_ROWS, 0); + + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); + gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); + gl->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + gl->PixelStorei(GL_UNPACK_SKIP_ROWS, 0); + + gl->BindBufferARB(GL_PIXEL_PACK_BUFFER, 0); + gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER, 0); + + pvImage = crAlloc(4*dstw*dsth); + gl->GetTexImage(texTarget, 0, GL_BGRA, GL_UNSIGNED_BYTE, pvImage); + + gl->BindTexture(texTarget, otex); + + gl->PixelStorei(GL_PACK_ROW_LENGTH, pr); + gl->PixelStorei(GL_PACK_ALIGNMENT, pa); + gl->PixelStorei(GL_PACK_SKIP_PIXELS, psp); + gl->PixelStorei(GL_PACK_SKIP_ROWS, psr); + + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, ur); + gl->PixelStorei(GL_UNPACK_ALIGNMENT, ua); + gl->PixelStorei(GL_UNPACK_SKIP_PIXELS, usp); + gl->PixelStorei(GL_UNPACK_SKIP_ROWS, usr); + + gl->BindBufferARB(GL_PIXEL_PACK_BUFFER, ppb); + gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER, pub); + + gl->BindFramebufferEXT(GL_READ_FRAMEBUFFER_BINDING_EXT, rfb); + gl->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_BINDING_EXT, dfb); + gl->ReadBuffer(rb); + gl->DrawBuffer(db); + + *ppvImage = pvImage; + *pw = dstw; + *ph = dsth; +} + +DECLEXPORT(void) crDbgPrint(const char *format, ... ) +{ + va_list args; + static char txt[8092]; + + va_start(args, format); + vsprintf(txt, format, args); + va_end(args); + + OutputDebugString(txt); +} + +void crDbgDumpImage2D(const char* pszDesc, const void *pvData, uint32_t width, uint32_t height, uint32_t bpp, uint32_t pitch) +{ + crDbgPrint("<?dml?><exec cmd=\"!vbvdbg.ms 0x%p 0n%d 0n%d 0n%d 0n%d\">%s</exec>, ( !vbvdbg.ms 0x%p 0n%d 0n%d 0n%d 0n%d )\n", + pvData, width, height, bpp, pitch, + pszDesc, + pvData, width, height, bpp, pitch); +} + +void crDbgDumpTexImage2D(const char* pszDesc, GLint texTarget, GLint texName, GLboolean fBreak) +{ + GLvoid *pvImage; + GLint w, h; + crDbgGetTexImage2D(texTarget, texName, &pvImage, &w, &h); + crDbgPrint("%s target(%d), name(%d), width(%d), height(%d)", pszDesc, texTarget, texName, w, h); + crDbgDumpImage2D("texture data", pvImage, w, h, 32, (32 * w)/8); + if (fBreak) + { + CRASSERT(0); + } + crDbgFree(pvImage); +} +#endif + +PCR_BLITTER crServerVBoxBlitterGet() +{ + if (!CrBltIsInitialized(&cr_server.Blitter)) + { + CR_BLITTER_CONTEXT Ctx; + int rc; + CRASSERT(cr_server.MainContextInfo.SpuContext); + Ctx.Base.id = cr_server.MainContextInfo.SpuContext; + Ctx.Base.visualBits = cr_server.MainContextInfo.CreateInfo.realVisualBits; + rc = CrBltInit(&cr_server.Blitter, &Ctx, true, true, NULL, &cr_server.TmpCtxDispatch); + if (RT_SUCCESS(rc)) + { + CRASSERT(CrBltIsInitialized(&cr_server.Blitter)); + } + else + { + crWarning("CrBltInit failed, rc %d", rc); + CRASSERT(!CrBltIsInitialized(&cr_server.Blitter)); + return NULL; + } + } + + if (!CrBltMuralGetCurrentInfo(&cr_server.Blitter)->Base.id) + { + CRMuralInfo *dummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + CR_BLITTER_WINDOW DummyInfo; + CRASSERT(dummy); + crServerVBoxBlitterWinInit(&DummyInfo, dummy); + CrBltMuralSetCurrentInfo(&cr_server.Blitter, &DummyInfo); + } + + return &cr_server.Blitter; +} + +PCR_BLITTER crServerVBoxBlitterGetInitialized() +{ + if (CrBltIsInitialized(&cr_server.Blitter)) + return &cr_server.Blitter; + return NULL; +} + + +int crServerVBoxBlitterTexInit(CRContext *ctx, CRMuralInfo *mural, PVBOXVR_TEXTURE pTex, GLboolean fDraw) +{ + CRTextureObj *tobj; + CRFramebufferObjectState *pBuf = &ctx->framebufferobject; + GLenum enmBuf; + CRFBOAttachmentPoint *pAp; + GLuint idx; + CRTextureLevel *tl; + CRFramebufferObject *pFBO = fDraw ? pBuf->drawFB : pBuf->readFB; + + if (!pFBO) + { + GLuint hwid; + + if (!mural->fRedirected) + { + WARN(("mural not redirected!")); + return VERR_NOT_IMPLEMENTED; + } + + enmBuf = fDraw ? ctx->buffer.drawBuffer : ctx->buffer.readBuffer; + switch (enmBuf) + { + case GL_BACK: + case GL_BACK_RIGHT: + case GL_BACK_LEFT: + hwid = mural->aidColorTexs[CR_SERVER_FBO_BB_IDX(mural)]; + break; + case GL_FRONT: + case GL_FRONT_RIGHT: + case GL_FRONT_LEFT: + hwid = mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)]; + break; + default: + WARN(("unsupported enum buf %d", enmBuf)); + return VERR_NOT_IMPLEMENTED; + break; + } + + if (!hwid) + { + crWarning("offscreen render tex hwid is null"); + return VERR_INVALID_STATE; + } + + pTex->width = mural->width; + pTex->height = mural->height; + pTex->target = GL_TEXTURE_2D; + pTex->hwid = hwid; + return VINF_SUCCESS; + } + + enmBuf = fDraw ? pFBO->drawbuffer[0] : pFBO->readbuffer; + idx = enmBuf - GL_COLOR_ATTACHMENT0_EXT; + if (idx >= CR_MAX_COLOR_ATTACHMENTS) + { + crWarning("idx is invalid %d, using 0", idx); + } + + pAp = &pFBO->color[idx]; + + if (!pAp->name) + { + crWarning("no collor draw attachment"); + return VERR_INVALID_STATE; + } + + if (pAp->level) + { + WARN(("non-zero level not implemented")); + return VERR_NOT_IMPLEMENTED; + } + + tobj = (CRTextureObj*)crHashtableSearch(ctx->shared->textureTable, pAp->name); + if (!tobj) + { + crWarning("no texture object found for name %d", pAp->name); + return VERR_INVALID_STATE; + } + + if (tobj->target != GL_TEXTURE_2D && tobj->target != GL_TEXTURE_RECTANGLE_NV) + { + WARN(("non-texture[rect|2d] not implemented")); + return VERR_NOT_IMPLEMENTED; + } + + CRASSERT(tobj->hwid); + + tl = tobj->level[0]; + pTex->width = tl->width; + pTex->height = tl->height; + pTex->target = tobj->target; + pTex->hwid = tobj->hwid; + + return VINF_SUCCESS; +} + +int crServerVBoxBlitterBlitCurrentCtx(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) +{ + PCR_BLITTER pBlitter; + CR_BLITTER_CONTEXT Ctx; + CRMuralInfo *mural; + CRContext *ctx = crStateGetCurrent(); + PVBOXVR_TEXTURE pDrawTex, pReadTex; + VBOXVR_TEXTURE DrawTex, ReadTex; + int rc; + GLuint idDrawFBO, idReadFBO; + CR_BLITTER_WINDOW BltInfo; + + if (mask != GL_COLOR_BUFFER_BIT) + { + WARN(("not supported blit mask %d", mask)); + return VERR_NOT_IMPLEMENTED; + } + + if (!cr_server.curClient) + { + crWarning("no current client"); + return VERR_INVALID_STATE; + } + mural = cr_server.curClient->currentMural; + if (!mural) + { + crWarning("no current mural"); + return VERR_INVALID_STATE; + } + + rc = crServerVBoxBlitterTexInit(ctx, mural, &DrawTex, GL_TRUE); + if (RT_SUCCESS(rc)) + { + pDrawTex = &DrawTex; + } + else + { + crWarning("crServerVBoxBlitterTexInit failed for draw"); + return rc; + } + + rc = crServerVBoxBlitterTexInit(ctx, mural, &ReadTex, GL_FALSE); + if (RT_SUCCESS(rc)) + { + pReadTex = &ReadTex; + } + else + { +// crWarning("crServerVBoxBlitterTexInit failed for read"); + return rc; + } + + pBlitter = crServerVBoxBlitterGet(); + if (!pBlitter) + { + crWarning("crServerVBoxBlitterGet failed"); + return VERR_GENERAL_FAILURE; + } + + crServerVBoxBlitterWinInit(&BltInfo, mural); + + crServerVBoxBlitterCtxInit(&Ctx, cr_server.curClient->currentCtxInfo); + + CrBltMuralSetCurrentInfo(pBlitter, &BltInfo); + + idDrawFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer); + idReadFBO = CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer); + + crStateSwitchPrepare(NULL, ctx, idDrawFBO, idReadFBO); + + rc = CrBltEnter(pBlitter); + if (RT_SUCCESS(rc)) + { + RTRECT ReadRect, DrawRect; + ReadRect.xLeft = srcX0; + ReadRect.yTop = srcY0; + ReadRect.xRight = srcX1; + ReadRect.yBottom = srcY1; + DrawRect.xLeft = dstX0; + DrawRect.yTop = dstY0; + DrawRect.xRight = dstX1; + DrawRect.yBottom = dstY1; + CrBltBlitTexTex(pBlitter, pReadTex, &ReadRect, pDrawTex, &DrawRect, 1, CRBLT_FLAGS_FROM_FILTER(filter)); + CrBltLeave(pBlitter); + } + else + { + crWarning("CrBltEnter failed rc %d", rc); + } + + crStateSwitchPostprocess(ctx, NULL, idDrawFBO, idReadFBO); + + return rc; +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchBlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter) +{ + CRContext *ctx = crStateGetCurrent(); + bool fTryBlitter = false; +#ifdef CR_CHECK_BLITS +// { + SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; + GLint rfb=0, dfb=0, dtex=0, dlev=-1, rtex=0, rlev=-1, rb=0, db=0, ppb=0, pub=0, vp[4], otex, dstw, dsth; + GLint sdtex=0, srtex=0; + GLenum dStatus, rStatus; + + CRTextureObj *tobj = 0; + CRTextureLevel *tl = 0; + GLint id, tuId, pbufId, pbufIdHw, ubufId, ubufIdHw, width, height, depth; + + crDebug("===StateTracker==="); + crDebug("Current TU: %i", ctx->texture.curTextureUnit); + + tobj = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D; + CRASSERT(tobj); + tl = &tobj->level[0][0]; + crDebug("Texture %i(hw %i), w=%i, h=%i", tobj->id, tobj->hwid, tl->width, tl->height, tl->depth); + + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + pbufId = ctx->bufferobject.packBuffer->hwid; + } + else + { + pbufId = 0; + } + crDebug("Pack BufferId %i", pbufId); + + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + { + ubufId = ctx->bufferobject.unpackBuffer->hwid; + } + else + { + ubufId = 0; + } + crDebug("Unpack BufferId %i", ubufId); + + crDebug("===GPU==="); + cr_server.head_spu->dispatch_table.GetIntegerv(GL_ACTIVE_TEXTURE, &tuId); + crDebug("Current TU: %i", tuId - GL_TEXTURE0_ARB); + CRASSERT(tuId - GL_TEXTURE0_ARB == ctx->texture.curTextureUnit); + + cr_server.head_spu->dispatch_table.GetIntegerv(GL_TEXTURE_BINDING_2D, &id); + cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + cr_server.head_spu->dispatch_table.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_DEPTH, &depth); + crDebug("Texture: %i, w=%i, h=%i, d=%i", id, width, height, depth); + CRASSERT(id == tobj->hwid); + CRASSERT(width == tl->width); + CRASSERT(height == tl->height); + CRASSERT(depth == tl->depth); + + cr_server.head_spu->dispatch_table.GetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &pbufIdHw); + crDebug("Hw Pack BufferId %i", pbufIdHw); + CRASSERT(pbufIdHw == pbufId); + + cr_server.head_spu->dispatch_table.GetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &ubufIdHw); + crDebug("Hw Unpack BufferId %i", ubufIdHw); + CRASSERT(ubufIdHw == ubufId); + + gl->GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb); + gl->GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb); + gl->GetIntegerv(GL_READ_BUFFER, &rb); + gl->GetIntegerv(GL_DRAW_BUFFER, &db); + + gl->GetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &ppb); + gl->GetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &pub); + + gl->GetIntegerv(GL_VIEWPORT, &vp[0]); + + gl->GetIntegerv(GL_TEXTURE_BINDING_2D, &otex); + + gl->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &dtex); + gl->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT, &dlev); + dStatus = gl->CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT); + + gl->GetFramebufferAttachmentParameterivEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &rtex); + gl->GetFramebufferAttachmentParameterivEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT, &rlev); + rStatus = gl->CheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT); + + if (dtex) + { + CRASSERT(!dlev); + } + + if (rtex) + { + CRASSERT(!rlev); + } + + if (ctx->framebufferobject.drawFB) + { + CRASSERT(dfb); + CRASSERT(ctx->framebufferobject.drawFB->hwid == dfb); + CRASSERT(ctx->framebufferobject.drawFB->drawbuffer[0] == db); + + CRASSERT(dStatus==GL_FRAMEBUFFER_COMPLETE_EXT); + CRASSERT(db==GL_COLOR_ATTACHMENT0_EXT); + + CRASSERT(ctx->framebufferobject.drawFB->color[0].type == GL_TEXTURE); + CRASSERT(ctx->framebufferobject.drawFB->color[0].level == 0); + sdtex = ctx->framebufferobject.drawFB->color[0].name; + sdtex = crStateGetTextureHWID(sdtex); + + CRASSERT(sdtex); + } + else + { + CRASSERT(!dfb); + } + + if (ctx->framebufferobject.readFB) + { + CRASSERT(rfb); + CRASSERT(ctx->framebufferobject.readFB->hwid == rfb); + + CRASSERT(rStatus==GL_FRAMEBUFFER_COMPLETE_EXT); + + CRASSERT(ctx->framebufferobject.readFB->color[0].type == GL_TEXTURE); + CRASSERT(ctx->framebufferobject.readFB->color[0].level == 0); + srtex = ctx->framebufferobject.readFB->color[0].name; + srtex = crStateGetTextureHWID(srtex); + + CRASSERT(srtex); + } + else + { + CRASSERT(!rfb); + } + + CRASSERT(sdtex == dtex); + CRASSERT(srtex == rtex); + +// crDbgDumpTexImage2D("==> src tex:", GL_TEXTURE_2D, rtex, true); +// crDbgDumpTexImage2D("==> dst tex:", GL_TEXTURE_2D, dtex, true); + +// } +#endif +#ifdef CR_DUMP_BLITS + SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; + GLint rfb=0, dfb=0, dtex=0, dlev=-1, rb=0, db=0, ppb=0, pub=0, vp[4], otex, dstw, dsth; + GLenum status; + char fname[200]; + void *img; + + blitnum++; + + crDebug("[%i]BlitFramebufferEXT(%i, %i, %i, %i, %i, %i, %i, %i, %x, %x)", blitnum, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + crDebug("%i, %i <-> %i, %i", srcX1-srcX0, srcY1-srcY0, dstX1-dstX0, dstY1-dstY0); + + gl->GetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &rfb); + gl->GetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &dfb); + gl->GetIntegerv(GL_READ_BUFFER, &rb); + gl->GetIntegerv(GL_DRAW_BUFFER, &db); + + gl->GetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &ppb); + gl->GetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &pub); + + gl->GetIntegerv(GL_VIEWPORT, &vp[0]); + + gl->GetIntegerv(GL_TEXTURE_BINDING_2D, &otex); + + CRASSERT(!rfb && dfb); + gl->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, &dtex); + gl->GetFramebufferAttachmentParameterivEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT, &dlev); + status = gl->CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT); + + CRASSERT(status==GL_FRAMEBUFFER_COMPLETE_EXT + && db==GL_COLOR_ATTACHMENT0_EXT + && (rb==GL_FRONT || rb==GL_BACK) + && !rfb && dfb && dtex && !dlev + && !ppb && !pub); + + crDebug("Src[rb 0x%x, fbo %i] Dst[db 0x%x, fbo %i(0x%x), tex %i.%i]", rb, rfb, db, dfb, status, dtex, dlev); + crDebug("Viewport [%i, %i, %i, %i]", vp[0], vp[1], vp[2], vp[3]); + + gl->PixelStorei(GL_PACK_ROW_LENGTH, 0); + gl->PixelStorei(GL_PACK_ALIGNMENT, 1); + gl->PixelStorei(GL_PACK_SKIP_PIXELS, 0); + gl->PixelStorei(GL_PACK_SKIP_ROWS, 0); + + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); + gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); + gl->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + gl->PixelStorei(GL_UNPACK_SKIP_ROWS, 0); + + gl->BindTexture(GL_TEXTURE_2D, dtex); + gl->GetTexLevelParameteriv(GL_TEXTURE_2D, dlev, GL_TEXTURE_WIDTH, &dstw); + gl->GetTexLevelParameteriv(GL_TEXTURE_2D, dlev, GL_TEXTURE_HEIGHT, &dsth); + gl->BindTexture(GL_TEXTURE_2D, otex); + crDebug("Dst is %i, %i", dstw, dsth); + + CRASSERT(vp[2]>=dstw && vp[3]>=dsth); + img = crAlloc(vp[2]*vp[3]*4); + CRASSERT(img); + + gl->ReadPixels(0, 0, vp[2], vp[3], GL_BGRA, GL_UNSIGNED_BYTE, img); + sprintf(fname, "blit%iA_src.tga", blitnum); + crDumpNamedTGA(fname, vp[2], vp[3], img); + + gl->BindTexture(GL_TEXTURE_2D, dtex); + gl->GetTexImage(GL_TEXTURE_2D, dlev, GL_BGRA, GL_UNSIGNED_BYTE, img); + sprintf(fname, "blit%iB_dst.tga", blitnum); + crDumpNamedTGA(fname, dstw, dsth, img); + gl->BindTexture(GL_TEXTURE_2D, otex); +#endif + + if (srcY0 > srcY1) + { + /* work around Intel driver bug on Linux host */ + if (1 || dstY0 > dstY1) + { + /* use srcY1 < srcY2 && dstY1 < dstY2 whenever possible to avoid GPU driver bugs */ + int32_t tmp = srcY0; + srcY0 = srcY1; + srcY1 = tmp; + tmp = dstY0; + dstY0 = dstY1; + dstY1 = tmp; + } + } + + if (srcX0 > srcX1) + { + if (dstX0 > dstX1) + { + /* use srcX1 < srcX2 && dstX1 < dstX2 whenever possible to avoid GPU driver bugs */ + int32_t tmp = srcX0; + srcX0 = srcX1; + srcX1 = tmp; + tmp = dstX0; + dstX0 = dstX1; + dstX1 = tmp; + } + } + + if (cr_server.fBlitterMode) + { + fTryBlitter = true; + } + + if (fTryBlitter) + { + int rc = crServerVBoxBlitterBlitCurrentCtx(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + if (RT_SUCCESS(rc)) + goto my_exit; + } + + if (ctx->viewport.scissorTest) + cr_server.head_spu->dispatch_table.Disable(GL_SCISSOR_TEST); + + cr_server.head_spu->dispatch_table.BlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, + dstX0, dstY0, dstX1, dstY1, + mask, filter); + + if (ctx->viewport.scissorTest) + cr_server.head_spu->dispatch_table.Enable(GL_SCISSOR_TEST); + + +my_exit: + +//#ifdef CR_CHECK_BLITS +// crDbgDumpTexImage2D("<== src tex:", GL_TEXTURE_2D, rtex, true); +// crDbgDumpTexImage2D("<== dst tex:", GL_TEXTURE_2D, dtex, true); +//#endif +#ifdef CR_DUMP_BLITS + gl->BindTexture(GL_TEXTURE_2D, dtex); + gl->GetTexImage(GL_TEXTURE_2D, dlev, GL_BGRA, GL_UNSIGNED_BYTE, img); + sprintf(fname, "blit%iC_res.tga", blitnum); + crDumpNamedTGA(fname, dstw, dsth, img); + gl->BindTexture(GL_TEXTURE_2D, otex); + crFree(img); +#endif + return; +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchDrawBuffer( GLenum mode ) +{ + crStateDrawBuffer( mode ); + + if (!crStateGetCurrent()->framebufferobject.drawFB) + { + if (mode == GL_FRONT || mode == GL_FRONT_LEFT || mode == GL_FRONT_RIGHT) + cr_server.curClient->currentMural->bFbDraw = GL_TRUE; + + if (crServerIsRedirectedToFBO() + && cr_server.curClient->currentMural->aidFBOs[0]) + { + CRMuralInfo *mural = cr_server.curClient->currentMural; + GLint iBufferNeeded = -1; + switch (mode) + { + case GL_BACK: + case GL_BACK_LEFT: + case GL_BACK_RIGHT: + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + case GL_FRONT: + case GL_FRONT_LEFT: + case GL_FRONT_RIGHT: + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_FB_IDX(mural); + break; + case GL_NONE: + crDebug("DrawBuffer: GL_NONE"); + break; + case GL_AUX0: + crDebug("DrawBuffer: GL_AUX0"); + break; + case GL_AUX1: + crDebug("DrawBuffer: GL_AUX1"); + break; + case GL_AUX2: + crDebug("DrawBuffer: GL_AUX2"); + break; + case GL_AUX3: + crDebug("DrawBuffer: GL_AUX3"); + break; + case GL_LEFT: + crWarning("DrawBuffer: GL_LEFT not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + case GL_RIGHT: + crWarning("DrawBuffer: GL_RIGHT not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + case GL_FRONT_AND_BACK: + crWarning("DrawBuffer: GL_FRONT_AND_BACK not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + default: + crWarning("DrawBuffer: unexpected mode! 0x%x", mode); + iBufferNeeded = mural->iCurDrawBuffer; + break; + } + + if (iBufferNeeded != mural->iCurDrawBuffer) + { + mural->iCurDrawBuffer = iBufferNeeded; + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, iBufferNeeded)); + } + } + } + + cr_server.head_spu->dispatch_table.DrawBuffer( mode ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchDrawBuffers( GLsizei n, const GLenum* bufs ) +{ + if (n == 1) + { + crServerDispatchDrawBuffer( bufs[0] ); + } + else + { + /** @todo State tracker. */ + cr_server.head_spu->dispatch_table.DrawBuffers( n, bufs ); + } +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchReadBuffer( GLenum mode ) +{ + crStateReadBuffer( mode ); + + if (crServerIsRedirectedToFBO() + && cr_server.curClient->currentMural->aidFBOs[0] + && !crStateGetCurrent()->framebufferobject.readFB) + { + CRMuralInfo *mural = cr_server.curClient->currentMural; + GLint iBufferNeeded = -1; + switch (mode) + { + case GL_BACK: + case GL_BACK_LEFT: + case GL_BACK_RIGHT: + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + case GL_FRONT: + case GL_FRONT_LEFT: + case GL_FRONT_RIGHT: + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_FB_IDX(mural); + break; + case GL_NONE: + crDebug("ReadBuffer: GL_NONE"); + break; + case GL_AUX0: + crDebug("ReadBuffer: GL_AUX0"); + break; + case GL_AUX1: + crDebug("ReadBuffer: GL_AUX1"); + break; + case GL_AUX2: + crDebug("ReadBuffer: GL_AUX2"); + break; + case GL_AUX3: + crDebug("ReadBuffer: GL_AUX3"); + break; + case GL_LEFT: + crWarning("ReadBuffer: GL_LEFT not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + case GL_RIGHT: + crWarning("ReadBuffer: GL_RIGHT not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + case GL_FRONT_AND_BACK: + crWarning("ReadBuffer: GL_FRONT_AND_BACK not supported properly"); + mode = GL_COLOR_ATTACHMENT0; + iBufferNeeded = CR_SERVER_FBO_BB_IDX(mural); + break; + default: + crWarning("ReadBuffer: unexpected mode! 0x%x", mode); + iBufferNeeded = mural->iCurDrawBuffer; + break; + } + + Assert(CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer)); + if (iBufferNeeded != mural->iCurReadBuffer) + { + mural->iCurReadBuffer = iBufferNeeded; + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, iBufferNeeded)); + } + } + cr_server.head_spu->dispatch_table.ReadBuffer( mode ); +} + +GLenum SERVER_DISPATCH_APIENTRY crServerDispatchGetError( void ) +{ + GLenum retval, err; + CRContext *ctx = crStateGetCurrent(); + retval = ctx->error; + + err = cr_server.head_spu->dispatch_table.GetError(); + if (retval == GL_NO_ERROR) + retval = err; + else + ctx->error = GL_NO_ERROR; + + /* our impl has a single error flag, so we just loop here to reset all error flags to no_error */ + while (err != GL_NO_ERROR) + err = cr_server.head_spu->dispatch_table.GetError(); + + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} + +void SERVER_DISPATCH_APIENTRY +crServerMakeTmpCtxCurrent( GLint window, GLint nativeWindow, GLint context ) +{ + CRContext *pCtx = crStateGetCurrent(); + CRContext *pCurCtx = NULL; + GLuint idDrawFBO = 0, idReadFBO = 0; + int fDoPrePostProcess = 0; + + if (pCtx) + { + CRMuralInfo *pCurrentMural = cr_server.currentMural; + + pCurCtx = cr_server.currentCtxInfo ? cr_server.currentCtxInfo->pContext : cr_server.MainContextInfo.pContext; + Assert(pCurCtx == pCtx); + + if (!context) + { + if (pCurrentMural) + { + Assert(cr_server.currentCtxInfo); + context = cr_server.currentCtxInfo->SpuContext > 0 ? cr_server.currentCtxInfo->SpuContext : cr_server.MainContextInfo.SpuContext; + window = pCurrentMural->spuWindow; + } + else + { + CRMuralInfo * pDummy; + Assert(!cr_server.currentCtxInfo); + pDummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + context = cr_server.MainContextInfo.SpuContext; + window = pDummy->spuWindow; + } + + + fDoPrePostProcess = -1; + } + else + { + fDoPrePostProcess = 1; + } + + if (pCurrentMural) + { + idDrawFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurDrawBuffer); + idReadFBO = CR_SERVER_FBO_FOR_IDX(pCurrentMural, pCurrentMural->iCurReadBuffer); + } + else + { + idDrawFBO = 0; + idReadFBO = 0; + } + } + else + { + /* this is a GUI thread, so no need to do anything here */ + } + + if (fDoPrePostProcess > 0) + crStateSwitchPrepare(NULL, pCurCtx, idDrawFBO, idReadFBO); + + cr_server.head_spu->dispatch_table.MakeCurrent( window, nativeWindow, context); + + if (fDoPrePostProcess < 0) + crStateSwitchPostprocess(pCurCtx, NULL, idDrawFBO, idReadFBO); +} + +void crServerInitTmpCtxDispatch() +{ + MakeCurrentFunc_t pfnMakeCurrent; + + crSPUInitDispatchTable(&cr_server.TmpCtxDispatch); + crSPUCopyDispatchTable(&cr_server.TmpCtxDispatch, &cr_server.head_spu->dispatch_table); + cr_server.TmpCtxDispatch.MakeCurrent = crServerMakeTmpCtxCurrent; + + pfnMakeCurrent = crServerMakeTmpCtxCurrent; + cr_server.head_spu->dispatch_table.ChromiumParametervCR(GL_HH_SET_TMPCTX_MAKE_CURRENT, GL_BYTE, sizeof (void*), &pfnMakeCurrent); + +} + +/* dump stuff */ +#ifdef VBOX_WITH_CRSERVER_DUMPER + +# ifndef VBOX_WITH_CRDUMPER +# error "VBOX_WITH_CRDUMPER undefined!" +# endif + +/* first four bits are buffer dump config + * second four bits are texture dump config + * config flags: + * 1 - blit on enter + * 2 - blit on exit + * + * + * Example: + * + * 0x03 - dump buffer on enter and exit + * 0x22 - dump texture and buffer on exit */ + +int64_t g_CrDbgDumpPid = 0; +unsigned long g_CrDbgDumpEnabled = 0; +unsigned long g_CrDbgDumpDraw = 0 +#if 0 + | CR_SERVER_DUMP_F_COMPILE_SHADER + | CR_SERVER_DUMP_F_LINK_PROGRAM +#endif + ; +#if 0 + | CR_SERVER_DUMP_F_DRAW_BUFF_ENTER + | CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE + | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER + | CR_SERVER_DUMP_F_DRAW_TEX_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER + | CR_SERVER_DUMP_F_DRAW_STATE_ENTER + | CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER + | CR_SERVER_DUMP_F_DRAWEL + | CR_SERVER_DUMP_F_SHADER_SOURCE + ; +#endif +unsigned long g_CrDbgDumpDrawFramesSettings = CR_SERVER_DUMP_F_DRAW_BUFF_ENTER + | CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE + | CR_SERVER_DUMP_F_DRAW_TEX_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER + | CR_SERVER_DUMP_F_COMPILE_SHADER + | CR_SERVER_DUMP_F_LINK_PROGRAM + | CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER; +unsigned long g_CrDbgDumpDrawFramesAppliedSettings = 0; +unsigned long g_CrDbgDumpDrawFramesSavedInitSettings = 0; +unsigned long g_CrDbgDumpDrawFramesCount = 0; + +uint32_t g_CrDbgDumpDrawCount = 0; +uint32_t g_CrDbgDumpDumpOnCount = 10; +uint32_t g_CrDbgDumpDumpOnCountEnabled = 0; +uint32_t g_CrDbgDumpDumpOnCountPerform = 0; +uint32_t g_CrDbgDumpDrawFlags = CR_SERVER_DUMP_F_COMPILE_SHADER + | CR_SERVER_DUMP_F_SHADER_SOURCE + | CR_SERVER_DUMP_F_COMPILE_SHADER + | CR_SERVER_DUMP_F_LINK_PROGRAM + | CR_SERVER_DUMP_F_DRAW_BUFF_ENTER + | CR_SERVER_DUMP_F_DRAW_BUFF_LEAVE + | CR_SERVER_DUMP_F_DRAW_TEX_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_UNIFORMS_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ATTRIBS_ENTER + | CR_SERVER_DUMP_F_DRAW_PROGRAM_ENTER + | CR_SERVER_DUMP_F_DRAW_STATE_ENTER + | CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER + | CR_SERVER_DUMP_F_DRAWEL + | CR_SERVER_DUMP_F_TEXPRESENT; + +void crServerDumpCheckTerm() +{ + if (!CrBltIsInitialized(&cr_server.RecorderBlitter)) + return; + + CrBltTerm(&cr_server.RecorderBlitter); +} + +int crServerDumpCheckInit() +{ + int rc; + CR_BLITTER_WINDOW BltWin; + CR_BLITTER_CONTEXT BltCtx; + CRMuralInfo *pBlitterMural; + + if (!CrBltIsInitialized(&cr_server.RecorderBlitter)) + { + pBlitterMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + if (!pBlitterMural) + { + crWarning("crServerGetDummyMural failed"); + return VERR_GENERAL_FAILURE; + } + + crServerVBoxBlitterWinInit(&BltWin, pBlitterMural); + crServerVBoxBlitterCtxInit(&BltCtx, &cr_server.MainContextInfo); + + rc = CrBltInit(&cr_server.RecorderBlitter, &BltCtx, true, true, NULL, &cr_server.TmpCtxDispatch); + if (!RT_SUCCESS(rc)) + { + crWarning("CrBltInit failed rc %d", rc); + return rc; + } + + rc = CrBltMuralSetCurrentInfo(&cr_server.RecorderBlitter, &BltWin); + if (!RT_SUCCESS(rc)) + { + crWarning("CrBltMuralSetCurrentInfo failed rc %d", rc); + return rc; + } + } + +#if 0 + crDmpDbgPrintInit(&cr_server.DbgPrintDumper); + cr_server.pDumper = &cr_server.DbgPrintDumper.Base; +#else + if (!crDmpHtmlIsInited(&cr_server.HtmlDumper)) + { + static int cCounter = 0; +// crDmpHtmlInit(&cr_server.HtmlDumper, "S:\\projects\\virtualbox\\3d\\dumps\\1", "index.html"); + crDmpHtmlInitF(&cr_server.HtmlDumper, "/Users/oracle-mac/vbox/dump/1", "index%d.html", cCounter); + cr_server.pDumper = &cr_server.HtmlDumper.Base; + ++cCounter; + } +#endif + + crRecInit(&cr_server.Recorder, &cr_server.RecorderBlitter, &cr_server.TmpCtxDispatch, cr_server.pDumper); + return VINF_SUCCESS; +} + +void crServerDumpShader(GLint id) +{ + CRContext *ctx = crStateGetCurrent(); + crRecDumpShader(&cr_server.Recorder, ctx, id, 0); +} + +void crServerDumpProgram(GLint id) +{ + CRContext *ctx = crStateGetCurrent(); + crRecDumpProgram(&cr_server.Recorder, ctx, id, 0); +} + +void crServerDumpCurrentProgram() +{ + CRContext *ctx = crStateGetCurrent(); + crRecDumpCurrentProgram(&cr_server.Recorder, ctx); +} + +void crServerDumpRecompileDumpCurrentProgram() +{ + crDmpStrF(cr_server.Recorder.pDumper, "==Dump(1)=="); + crServerRecompileCurrentProgram(); + crServerDumpCurrentProgramUniforms(); + crServerDumpCurrentProgramAttribs(); + crDmpStrF(cr_server.Recorder.pDumper, "Done Dump(1)"); + crServerRecompileCurrentProgram(); + crDmpStrF(cr_server.Recorder.pDumper, "Dump(2)"); + crServerRecompileCurrentProgram(); + crServerDumpCurrentProgramUniforms(); + crServerDumpCurrentProgramAttribs(); + crDmpStrF(cr_server.Recorder.pDumper, "Done Dump(2)"); +} + +void crServerRecompileCurrentProgram() +{ + CRContext *ctx = crStateGetCurrent(); + crRecRecompileCurrentProgram(&cr_server.Recorder, ctx); +} + +void crServerDumpCurrentProgramUniforms() +{ + CRContext *ctx = crStateGetCurrent(); + crDmpStrF(cr_server.Recorder.pDumper, "==Uniforms=="); + crRecDumpCurrentProgramUniforms(&cr_server.Recorder, ctx); + crDmpStrF(cr_server.Recorder.pDumper, "==Done Uniforms=="); +} + +void crServerDumpCurrentProgramAttribs() +{ + CRContext *ctx = crStateGetCurrent(); + crDmpStrF(cr_server.Recorder.pDumper, "==Attribs=="); + crRecDumpCurrentProgramAttribs(&cr_server.Recorder, ctx); + crDmpStrF(cr_server.Recorder.pDumper, "==Done Attribs=="); +} + +void crServerDumpState() +{ + CRContext *ctx = crStateGetCurrent(); + crRecDumpGlGetState(&cr_server.Recorder, ctx); + crRecDumpGlEnableState(&cr_server.Recorder, ctx); +} + +void crServerDumpDrawel(const char*pszFormat, ...) +{ + CRContext *ctx = crStateGetCurrent(); + va_list pArgList; + va_start(pArgList, pszFormat); + crRecDumpVertAttrV(&cr_server.Recorder, ctx, pszFormat, pArgList); + va_end(pArgList); +} + +void crServerDumpDrawelv(GLuint idx, const char*pszElFormat, uint32_t cbEl, const void *pvVal, uint32_t cVal) +{ + CRContext *ctx = crStateGetCurrent(); + crRecDumpVertAttrv(&cr_server.Recorder, ctx, idx, pszElFormat, cbEl, pvVal, cVal); +} + +void crServerDumpBuffer(int idx) +{ + CRContextInfo *pCtxInfo = cr_server.currentCtxInfo; + CRContext *ctx = crStateGetCurrent(); + GLint idFBO; + GLint idTex; + VBOXVR_TEXTURE RedirTex; + int rc = crServerDumpCheckInit(); + idx = idx >= 0 ? idx : crServerMuralFBOIdxFromBufferName(cr_server.currentMural, pCtxInfo->pContext->buffer.drawBuffer); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerDumpCheckInit failed, rc %d", rc); + return; + } + + if (idx < 0) + { + crWarning("neg idx, unsupported"); + return; + } + + idFBO = CR_SERVER_FBO_FOR_IDX(cr_server.currentMural, idx); + idTex = CR_SERVER_FBO_TEX_FOR_IDX(cr_server.currentMural, idx); + + RedirTex.width = cr_server.currentMural->fboWidth; + RedirTex.height = cr_server.currentMural->fboHeight; + RedirTex.target = GL_TEXTURE_2D; + RedirTex.hwid = idTex; + + crRecDumpBuffer(&cr_server.Recorder, ctx, idFBO, idTex ? &RedirTex : NULL); +} + +void crServerDumpTexture(const VBOXVR_TEXTURE *pTex) +{ + CRContextInfo *pCtxInfo = cr_server.currentCtxInfo; + CR_BLITTER_WINDOW BltWin; + CR_BLITTER_CONTEXT BltCtx; + CRContext *ctx = crStateGetCurrent(); + int rc = crServerDumpCheckInit(); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerDumpCheckInit failed, rc %d", rc); + return; + } + + crServerVBoxBlitterWinInit(&BltWin, cr_server.currentMural); + crServerVBoxBlitterCtxInit(&BltCtx, pCtxInfo); + + crRecDumpTextureF(&cr_server.Recorder, pTex, &BltCtx, &BltWin, "Tex (%d x %d), hwid (%d) target %#x", pTex->width, pTex->height, pTex->hwid, pTex->target); +} + +void crServerDumpTextures() +{ + CRContextInfo *pCtxInfo = cr_server.currentCtxInfo; + CRContext *ctx = crStateGetCurrent(); + int rc = crServerDumpCheckInit(); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerDumpCheckInit failed, rc %d", rc); + return; + } + + crRecDumpTextures(&cr_server.Recorder, ctx); +} + +void crServerDumpFilterOpLeave(unsigned long event, CR_DUMPER *pDumper) +{ + if (CR_SERVER_DUMP_F_DRAW_LEAVE_ALL & event) + { + g_CrDbgDumpDumpOnCountPerform = 0; + } +} + +bool crServerDumpFilterOpEnter(unsigned long event, CR_DUMPER *pDumper) +{ + if ((CR_SERVER_DUMP_F_SWAPBUFFERS_ENTER & event) + || (CR_SERVER_DUMP_F_TEXPRESENT & event)) + { + if (g_CrDbgDumpDumpOnCountEnabled == 1) + g_CrDbgDumpDumpOnCountEnabled = 2; + else if (g_CrDbgDumpDumpOnCountEnabled) + { + g_CrDbgDumpDumpOnCountEnabled = 0; + if (cr_server.pDumper == &cr_server.HtmlDumper.Base) + { + crDmpHtmlTerm(&cr_server.HtmlDumper); + cr_server.pDumper = NULL; + } + } + + g_CrDbgDumpDrawCount = 0; + } + else if (CR_SERVER_DUMP_F_DRAW_ENTER_ALL & event) + { + if (g_CrDbgDumpDumpOnCountEnabled == 2) + { + if (g_CrDbgDumpDumpOnCount == g_CrDbgDumpDrawCount) + { + g_CrDbgDumpDumpOnCountPerform = 1; + } + ++g_CrDbgDumpDrawCount; + } + } + if (g_CrDbgDumpDumpOnCountPerform) + { + if (g_CrDbgDumpDrawFlags & event) + return true; + } + return CR_SERVER_DUMP_DEFAULT_FILTER_OP(event); +} + +bool crServerDumpFilterDmp(unsigned long event, CR_DUMPER *pDumper) +{ + if (g_CrDbgDumpDumpOnCountPerform) + { + if (g_CrDbgDumpDrawFlags & event) + return true; + } + return CR_SERVER_DUMP_DEFAULT_FILTER_DMP(event); +} + +void crServerDumpFramesCheck() +{ + if (!g_CrDbgDumpDrawFramesCount) + return; + + if (!g_CrDbgDumpDrawFramesAppliedSettings) + { + if (!g_CrDbgDumpDrawFramesSettings) + { + crWarning("g_CrDbgDumpDrawFramesSettings is NULL, bump will not be started"); + g_CrDbgDumpDrawFramesCount = 0; + return; + } + + g_CrDbgDumpDrawFramesSavedInitSettings = g_CrDbgDumpDraw; + g_CrDbgDumpDrawFramesAppliedSettings = g_CrDbgDumpDrawFramesSettings; + g_CrDbgDumpDraw = g_CrDbgDumpDrawFramesSettings; + crDmpStrF(cr_server.Recorder.pDumper, "***Starting draw dump for %d frames, settings(0x%x)", g_CrDbgDumpDrawFramesCount, g_CrDbgDumpDraw); + return; + } + + --g_CrDbgDumpDrawFramesCount; + + if (!g_CrDbgDumpDrawFramesCount) + { + crDmpStrF(cr_server.Recorder.pDumper, "***Stop draw dump"); + g_CrDbgDumpDraw = g_CrDbgDumpDrawFramesSavedInitSettings; + g_CrDbgDumpDrawFramesAppliedSettings = 0; + } +} +#endif + +GLvoid crServerSpriteCoordReplEnable(GLboolean fEnable) +{ + CRContext *g = crStateGetCurrent(); + CRTextureState *t = &(g->texture); + GLuint curTextureUnit = t->curTextureUnit; + GLuint curTextureUnitRestore = curTextureUnit; + GLuint i; + + for (i = 0; i < g->limits.maxTextureUnits; ++i) + { + if (g->point.coordReplacement[i]) + { + if (i != curTextureUnit) + { + curTextureUnit = i; + cr_server.head_spu->dispatch_table.ActiveTextureARB( i + GL_TEXTURE0_ARB ); + } + + cr_server.head_spu->dispatch_table.TexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, (GLint)fEnable); + } + } + + if (curTextureUnit != curTextureUnitRestore) + { + cr_server.head_spu->dispatch_table.ActiveTextureARB( curTextureUnitRestore + GL_TEXTURE0_ARB ); + } +} + +GLvoid SERVER_DISPATCH_APIENTRY crServerDispatchDrawArrays(GLenum mode, GLint first, GLsizei count) +{ +#ifdef DEBUG + GLenum status = cr_server.head_spu->dispatch_table.CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT); + Assert(GL_FRAMEBUFFER_COMPLETE == status); +#endif + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_TRUE); + CR_SERVER_DUMP_DRAW_ENTER(); + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.DrawArrays(mode, first, count);); + CR_SERVER_DUMP_DRAW_LEAVE(); + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_FALSE); +} + +GLvoid SERVER_DISPATCH_APIENTRY crServerDispatchDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices) +{ +#ifdef DEBUG + GLenum status = cr_server.head_spu->dispatch_table.CheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT); + Assert(GL_FRAMEBUFFER_COMPLETE == status); +#endif + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_TRUE); + CR_SERVER_DUMP_DRAW_ENTER(); + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.DrawElements(mode, count, type, indices);); + CR_SERVER_DUMP_DRAW_LEAVE(); + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_FALSE); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchEnd( void ) +{ + CRContext *g = crStateGetCurrent(); + GLenum mode = g->current.mode; + + crStateEnd(); + cr_server.head_spu->dispatch_table.End(); + + CR_SERVER_DUMP_DRAW_LEAVE(); + + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_FALSE); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchBegin(GLenum mode) +{ +#ifdef DEBUG + CRContext *ctx = crStateGetCurrent(); + SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; + + if (ctx->program.vpProgramBinding) + { + AssertRelease(ctx->program.currentVertexProgram); + + if (ctx->program.currentVertexProgram->isARBprogram) + { + GLint pid=-1; + gl->GetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_BINDING_ARB, &pid); + + if (pid != ctx->program.currentVertexProgram->id) + { + crWarning("pid(%d) != ctx->program.currentVertexProgram->id(%d)", pid, ctx->program.currentVertexProgram->id); + } + AssertRelease(pid == ctx->program.currentVertexProgram->id); + } + else + { + GLint pid=-1; + + gl->GetIntegerv(GL_VERTEX_PROGRAM_BINDING_NV, &pid); + if (pid != ctx->program.currentVertexProgram->id) + { + crWarning("pid(%d) != ctx->program.currentVertexProgram->id(%d)", pid, ctx->program.currentVertexProgram->id); + } + AssertRelease(pid == ctx->program.currentVertexProgram->id); + } + } + else if (ctx->glsl.activeProgram) + { + GLint pid=-1; + + gl->GetIntegerv(GL_CURRENT_PROGRAM, &pid); + //crDebug("pid %i, state: id %i, hwid %i", pid, ctx->glsl.activeProgram->id, ctx->glsl.activeProgram->hwid); + if (pid != ctx->glsl.activeProgram->hwid) + { + crWarning("pid(%d) != ctx->glsl.activeProgram->hwid(%d)", pid, ctx->glsl.activeProgram->hwid); + } + AssertRelease(pid == ctx->glsl.activeProgram->hwid); + } +#endif + + if (mode == GL_POINTS) + crServerSpriteCoordReplEnable(GL_TRUE); + + CR_SERVER_DUMP_DRAW_ENTER(); + + crStateBegin(mode); + cr_server.head_spu->dispatch_table.Begin(mode); +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.cpp new file mode 100644 index 00000000..d4617026 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_muralfbo.cpp @@ -0,0 +1,840 @@ +/* $Id: server_muralfbo.cpp $ */ + +/** @file + * VBox crOpenGL: Window to FBO redirect support. + */ + +/* + * Copyright (C) 2010-2019 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. + */ + +#include "server.h" +#include "cr_string.h" +#include "cr_mem.h" +#include "cr_vreg.h" +#include "render/renderspu.h" + +static void crServerRedirMuralFbSync(CRMuralInfo *mural); + +void crServerCheckMuralGeometry(CRMuralInfo *mural) +{ + if (!mural->CreateInfo.externalID) + return; + + CRASSERT(mural->spuWindow); + CRASSERT(mural->spuWindow != CR_RENDER_DEFAULT_WINDOW_ID); + + if (!mural->width || !mural->height + || mural->fboWidth != mural->width + || mural->fboHeight != mural->height) + { + crServerRedirMuralFbClear(mural); + crServerRedirMuralFBO(mural, false); + crServerDeleteMuralFBO(mural); + } + + if (!mural->width || !mural->height) + return; + + crServerRedirMuralFBO(mural, true); + crServerRedirMuralFbSync(mural); +} + +static void crServerCheckMuralGeometryCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMI = (CRMuralInfo*) data1; + + if (!pMI->fRedirected || pMI == data2) + return; + + crServerCheckMuralGeometry(pMI); +} + + +void crServerCheckAllMuralGeometry(CRMuralInfo *pMI) +{ + CR_FBMAP Map; + int rc = CrPMgrHlpGlblUpdateBegin(&Map); + if (!RT_SUCCESS(rc)) + { + WARN(("CrPMgrHlpGlblUpdateBegin failed %d", rc)); + return; + } + + crHashtableWalk(cr_server.muralTable, crServerCheckMuralGeometryCB, pMI); + + if (pMI) + crServerCheckMuralGeometry(pMI); + + CrPMgrHlpGlblUpdateEnd(&Map); +} + +GLboolean crServerSupportRedirMuralFBO(void) +{ + static GLboolean fInited = GL_FALSE; + static GLboolean fSupported = GL_FALSE; + if (!fInited) + { + const GLubyte* pExt = cr_server.head_spu->dispatch_table.GetString(GL_REAL_EXTENSIONS); + + fSupported = ( NULL!=crStrstr((const char*)pExt, "GL_ARB_framebuffer_object") + || NULL!=crStrstr((const char*)pExt, "GL_EXT_framebuffer_object")) + && NULL!=crStrstr((const char*)pExt, "GL_ARB_texture_non_power_of_two"); + fInited = GL_TRUE; + } + return fSupported; +} + +static void crServerCreateMuralFBO(CRMuralInfo *mural); + +void crServerRedirMuralFbClear(CRMuralInfo *mural) +{ + uint32_t i; + for (i = 0; i < mural->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = mural->apUsedFBDatas[i]; + int rc = CrFbUpdateBegin(pData->hFb); + if (RT_SUCCESS(rc)) + { + CrFbEntryRegionsSet(pData->hFb, pData->hFbEntry, NULL, 0, NULL, false); + CrFbUpdateEnd(pData->hFb); + } + else + WARN(("CrFbUpdateBegin failed rc %d", rc)); + } + mural->cUsedFBDatas = 0; + + for (i = 0; i < (uint32_t)cr_server.screenCount; ++i) + { + GLuint j; + CR_FBDATA *pData = &mural->aFBDatas[i]; + if (!pData->hFb) + continue; + + if (pData->hFbEntry != NULL) + { + CrFbEntryRelease(pData->hFb, pData->hFbEntry); + pData->hFbEntry = NULL; + } + + /* Release all valid texture data structures in the array. + * Do not rely on mural->cBuffers because it might be already + * set to zero in crServerDeleteMuralFBO. + */ + for (j = 0; j < RT_ELEMENTS(pData->apTexDatas); ++j) + { + if (pData->apTexDatas[j] != NULL) + { + CrTdRelease(pData->apTexDatas[j]); + pData->apTexDatas[j] = NULL; + } + } + + pData->hFb = NULL; + } +} + +static int crServerRedirMuralDbSyncFb(CRMuralInfo *mural, HCR_FRAMEBUFFER hFb, CR_FBDATA **ppData) +{ + CR_FBDATA *pData; + const struct VBVAINFOSCREEN* pScreenInfo = CrFbGetScreenInfo(hFb); + const struct VBOXVR_SCR_COMPOSITOR* pCompositor = CrFbGetCompositor(hFb); + RTRECT FbRect = *CrVrScrCompositorRectGet(pCompositor); + RTRECT DefaultRegionsRect; + const RTRECT * pRegions; + uint32_t cRegions; + RTPOINT Pos; + RTRECT MuralRect; + int rc; + + CRASSERT(mural->fRedirected); + + *ppData = NULL; + + if (!mural->bVisible) + return VINF_SUCCESS; + + MuralRect.xLeft = mural->gX; + MuralRect.yTop = mural->gY; + MuralRect.xRight = MuralRect.xLeft + mural->width; + MuralRect.yBottom = MuralRect.yTop + mural->height; + + Pos.x = mural->gX - pScreenInfo->i32OriginX; + Pos.y = mural->gY - pScreenInfo->i32OriginY; + + VBoxRectTranslate(&FbRect, pScreenInfo->i32OriginX, pScreenInfo->i32OriginY); + + VBoxRectIntersect(&FbRect, &MuralRect); + + if (VBoxRectIsZero(&FbRect)) + return VINF_SUCCESS; + + if (mural->bReceivedRects) + { + pRegions = (const RTRECT*)mural->pVisibleRects; + cRegions = mural->cVisibleRects; + } + else + { + DefaultRegionsRect.xLeft = 0; + DefaultRegionsRect.yTop = 0; + DefaultRegionsRect.xRight = mural->width; + DefaultRegionsRect.yBottom = mural->height; + pRegions = &DefaultRegionsRect; + cRegions = 1; + } + + if (!cRegions) + return VINF_SUCCESS; + + pData = &mural->aFBDatas[pScreenInfo->u32ViewIndex]; + + if (!pData->hFb) + { + /* Guard against modulo-by-zero when calling CrFbEntryCreateForTexData + below. Observed when failing to load atig6pxx.dll and similar. */ + if (RT_UNLIKELY(mural->cBuffers == 0)) + { + WARN(("crServerRedirMuralDbSyncFb: cBuffers == 0 (crServerSupportRedirMuralFBO=%d)", crServerSupportRedirMuralFBO())); + return VERR_NOT_SUPPORTED; + } + + pData->hFb = hFb; + + RT_ZERO(pData->apTexDatas); + for (uint32_t i = 0; i < mural->cBuffers; ++i) + { + VBOXVR_TEXTURE Tex; + Tex.width = mural->width; + Tex.height = mural->height; + Tex.hwid = mural->aidColorTexs[i]; + Tex.target = GL_TEXTURE_2D; + + pData->apTexDatas[i] = CrFbTexDataCreate(&Tex); + } + + rc = CrFbEntryCreateForTexData(hFb, pData->apTexDatas[CR_SERVER_FBO_FB_IDX(mural)], 0, &pData->hFbEntry); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbEntryCreateForTexData failed rc %d", rc)); + } + } + else + { + CRASSERT(pData->hFb == hFb); + } + + rc = CrFbUpdateBegin(hFb); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbUpdateBegin failed rc %d", rc)); + return rc; + } + + rc = CrFbEntryRegionsSet(hFb, pData->hFbEntry, &Pos, cRegions, pRegions, true); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbEntryRegionsSet failed rc %d", rc)); + } + + CrFbUpdateEnd(hFb); + + const struct VBOXVR_SCR_COMPOSITOR_ENTRY* pCEntry = CrFbEntryGetCompositorEntry(pData->hFbEntry); + if (CrVrScrCompositorEntryIsUsed(pCEntry)) + *ppData = pData; + + return rc; +} + +static void crServerRedirMuralFbSync(CRMuralInfo *mural) +{ + uint32_t i; + uint32_t cUsedFBs = 0; + HCR_FRAMEBUFFER ahUsedFbs[CR_MAX_GUEST_MONITORS]; + HCR_FRAMEBUFFER hFb; + + for (i = 0; i < mural->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = mural->apUsedFBDatas[i]; + int rc = CrFbUpdateBegin(pData->hFb); + if (RT_SUCCESS(rc)) + { + ahUsedFbs[cUsedFBs] = pData->hFb; + CrFbEntryRegionsSet(pData->hFb, pData->hFbEntry, NULL, 0, NULL, false); + ++cUsedFBs; + } + else + WARN(("CrFbUpdateBegin failed rc %d", rc)); + } + mural->cUsedFBDatas = 0; + + if (!mural->width + || !mural->height + || !mural->bVisible + ) + goto end; + + CRASSERT(mural->fRedirected); + + for (hFb = CrPMgrFbGetFirstEnabled(); + hFb; + hFb = CrPMgrFbGetNextEnabled(hFb)) + { + CR_FBDATA *pData = NULL; + int rc = crServerRedirMuralDbSyncFb(mural, hFb, &pData); + if (!RT_SUCCESS(rc)) + { + WARN(("crServerRedirMuralDbSyncFb failed %d", rc)); + continue; + } + + if (!pData) + continue; + + mural->apUsedFBDatas[mural->cUsedFBDatas] = pData; + ++mural->cUsedFBDatas; + } + +end: + + for (i = 0; i < cUsedFBs; ++i) + { + CrFbUpdateEnd(ahUsedFbs[i]); + } +} + +static void crVBoxServerMuralFbCleanCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMI = (CRMuralInfo*) data1; + HCR_FRAMEBUFFER hFb = (HCR_FRAMEBUFFER)data2; + uint32_t i; + for (i = 0; i < pMI->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = pMI->apUsedFBDatas[i]; + if (hFb != pData->hFb) + continue; + + CrFbEntryRegionsSet(pData->hFb, pData->hFbEntry, NULL, 0, NULL, false); + break; + } +} + +static void crVBoxServerMuralFbSetCB(unsigned long key, void *data1, void *data2) +{ + CRMuralInfo *pMI = (CRMuralInfo*) data1; + HCR_FRAMEBUFFER hFb = (HCR_FRAMEBUFFER)data2; + uint32_t i; + CR_FBDATA *pData = NULL; + bool fFbWasUsed = false; + + Assert(hFb); + + if (!pMI->fRedirected) + { + Assert(!pMI->cUsedFBDatas); + return; + } + + for (i = 0; i < pMI->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = pMI->apUsedFBDatas[i]; + if (hFb != pData->hFb) + continue; + + fFbWasUsed = true; + break; + } + + if (CrFbIsEnabled(hFb)) + { + int rc = crServerRedirMuralDbSyncFb(pMI, hFb, &pData); + if (!RT_SUCCESS(rc)) + { + WARN(("crServerRedirMuralDbSyncFb failed %d", rc)); + pData = NULL; + } + } + + if (pData) + { + if (!fFbWasUsed) + { + uint32_t idScreen = CrFbGetScreenInfo(hFb)->u32ViewIndex; + for (i = 0; i < pMI->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = pMI->apUsedFBDatas[i]; + uint32_t idCurScreen = CrFbGetScreenInfo(pData->hFb)->u32ViewIndex; + if (idCurScreen > idScreen) + break; + + Assert(idCurScreen != idScreen); + } + + for (uint32_t j = pMI->cUsedFBDatas; j > i; --j) + { + pMI->apUsedFBDatas[j] = pMI->apUsedFBDatas[j-1]; + } + + pMI->apUsedFBDatas[i] = pData; + ++pMI->cUsedFBDatas; + } + /* else - nothing to do */ + } + else + { + if (fFbWasUsed) + { + for (uint32_t j = i; j < pMI->cUsedFBDatas - 1; ++j) + { + pMI->apUsedFBDatas[j] = pMI->apUsedFBDatas[j+1]; + } + --pMI->cUsedFBDatas; + } + /* else - nothing to do */ + } +} + +void crVBoxServerMuralFbResizeEnd(HCR_FRAMEBUFFER hFb) +{ + crHashtableWalk(cr_server.muralTable, crVBoxServerMuralFbSetCB, hFb); +} + +void crVBoxServerMuralFbResizeBegin(HCR_FRAMEBUFFER hFb) +{ + crHashtableWalk(cr_server.muralTable, crVBoxServerMuralFbCleanCB, hFb); +} + +DECLEXPORT(int) crVBoxServerNotifyResize(const struct VBVAINFOSCREEN *pScreen, void *pvVRAM) +{ + if (cr_server.fCrCmdEnabled) + { + WARN(("crVBoxServerNotifyResize for enabled CrCmd")); + return VERR_INVALID_STATE; + } + + if (pScreen->u32ViewIndex >= (uint32_t)cr_server.screenCount) + { + WARN(("invalid view index")); + return VERR_INVALID_PARAMETER; + } + + VBOXCMDVBVA_SCREENMAP_DECL(uint32_t, aTargetMap); + + memset(aTargetMap, 0, sizeof (aTargetMap)); + + ASMBitSet(aTargetMap, pScreen->u32ViewIndex); + + int rc = CrPMgrResize(pScreen, pvVRAM, aTargetMap); + if (!RT_SUCCESS(rc)) + { + WARN(("err")); + return rc; + } + + return VINF_SUCCESS; +} + +void crServerRedirMuralFBO(CRMuralInfo *mural, bool fEnabled) +{ + if (!mural->fRedirected == !fEnabled) + { + return; + } + + if (!mural->CreateInfo.externalID) + { + WARN(("trying to change redir setting for internal mural %d", mural->spuWindow)); + return; + } + + if (fEnabled) + { + if (!crServerSupportRedirMuralFBO()) + { + WARN(("FBO not supported, can't redirect window output")); + return; + } + + if (mural->aidFBOs[0]==0) + { + crServerCreateMuralFBO(mural); + } + + if (cr_server.curClient && cr_server.curClient->currentMural == mural) + { + if (!crStateGetCurrent()->framebufferobject.drawFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer)); + } + if (!crStateGetCurrent()->framebufferobject.readFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer)); + } + + crStateGetCurrent()->buffer.width = 0; + crStateGetCurrent()->buffer.height = 0; + } + } + else + { + if (cr_server.curClient && cr_server.curClient->currentMural == mural) + { + if (!crStateGetCurrent()->framebufferobject.drawFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); + } + if (!crStateGetCurrent()->framebufferobject.readFB) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, 0); + } + + crStateGetCurrent()->buffer.width = mural->width; + crStateGetCurrent()->buffer.height = mural->height; + } + } + + mural->fRedirected = !!fEnabled; +} + +static void crServerCreateMuralFBO(CRMuralInfo *mural) +{ + CRContext *ctx = crStateGetCurrent(); + GLuint uid, i; + GLenum status; + SPUDispatchTable *gl = &cr_server.head_spu->dispatch_table; + CRContextInfo *pMuralContextInfo; + + CRASSERT(mural->aidFBOs[0]==0); + CRASSERT(mural->aidFBOs[1]==0); + + pMuralContextInfo = cr_server.currentCtxInfo; + if (!pMuralContextInfo) + { + /* happens on saved state load */ + CRASSERT(cr_server.MainContextInfo.SpuContext); + pMuralContextInfo = &cr_server.MainContextInfo; + cr_server.head_spu->dispatch_table.MakeCurrent(mural->spuWindow, 0, cr_server.MainContextInfo.SpuContext); + } + + if (pMuralContextInfo->CreateInfo.realVisualBits != mural->CreateInfo.realVisualBits) + { + WARN(("mural visual bits do not match with current context visual bits!")); + } + + mural->cBuffers = 2; + mural->iBbBuffer = 0; + /*Color texture*/ + + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + { + gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + } + + for (i = 0; i < mural->cBuffers; ++i) + { + gl->GenTextures(1, &mural->aidColorTexs[i]); + gl->BindTexture(GL_TEXTURE_2D, mural->aidColorTexs[i]); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mural->width, mural->height, + 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + } + + /* Depth & Stencil. */ + gl->GenRenderbuffersEXT(1, &mural->idDepthStencilRB); + gl->BindRenderbufferEXT(GL_RENDERBUFFER_EXT, mural->idDepthStencilRB); + gl->RenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, + mural->width, mural->height); + + /*FBO*/ + for (i = 0; i < mural->cBuffers; ++i) + { + gl->GenFramebuffersEXT(1, &mural->aidFBOs[i]); + gl->BindFramebufferEXT(GL_FRAMEBUFFER_EXT, mural->aidFBOs[i]); + + gl->FramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, mural->aidColorTexs[i], 0); + gl->FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, mural->idDepthStencilRB); + gl->FramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, mural->idDepthStencilRB); + + status = gl->CheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status!=GL_FRAMEBUFFER_COMPLETE_EXT) + { + WARN(("FBO status(0x%x) isn't complete", status)); + } + } + + mural->iCurDrawBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.drawBuffer); + mural->iCurReadBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.readBuffer); + + mural->fboWidth = mural->width; + mural->fboHeight = mural->height; + + mural->iCurDrawBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.drawBuffer); + mural->iCurReadBuffer = crServerMuralFBOIdxFromBufferName(mural, ctx->buffer.readBuffer); + + /*Restore gl state*/ + uid = ctx->texture.unit[ctx->texture.curTextureUnit].currentTexture2D->hwid; + gl->BindTexture(GL_TEXTURE_2D, uid); + + uid = ctx->framebufferobject.renderbuffer ? ctx->framebufferobject.renderbuffer->hwid:0; + gl->BindRenderbufferEXT(GL_RENDERBUFFER_EXT, uid); + + uid = ctx->framebufferobject.drawFB ? ctx->framebufferobject.drawFB->hwid:0; + gl->BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, uid); + + uid = ctx->framebufferobject.readFB ? ctx->framebufferobject.readFB->hwid:0; + gl->BindFramebufferEXT(GL_READ_FRAMEBUFFER, uid); + + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + { + gl->BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, ctx->bufferobject.unpackBuffer->hwid); + } + + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, ctx->bufferobject.packBuffer->hwid); + } + else + { + gl->BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); + } + + CRASSERT(mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)]); +} + +void crServerDeleteMuralFBO(CRMuralInfo *mural) +{ + if (mural->aidFBOs[0]!=0) + { + GLuint i; + for (i = 0; i < mural->cBuffers; ++i) + { + cr_server.head_spu->dispatch_table.DeleteTextures(1, &mural->aidColorTexs[i]); + mural->aidColorTexs[i] = 0; + } + + cr_server.head_spu->dispatch_table.DeleteRenderbuffersEXT(1, &mural->idDepthStencilRB); + mural->idDepthStencilRB = 0; + + for (i = 0; i < mural->cBuffers; ++i) + { + cr_server.head_spu->dispatch_table.DeleteFramebuffersEXT(1, &mural->aidFBOs[i]); + mural->aidFBOs[i] = 0; + } + } + + mural->cBuffers = 0; +} + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +static GLboolean crServerIntersectRect(CRrecti *a, CRrecti *b, CRrecti *rect) +{ + CRASSERT(a && b && rect); + + rect->x1 = MAX(a->x1, b->x1); + rect->x2 = MIN(a->x2, b->x2); + rect->y1 = MAX(a->y1, b->y1); + rect->y2 = MIN(a->y2, b->y2); + + return (rect->x2>rect->x1) && (rect->y2>rect->y1); +} + +DECLEXPORT(void) crServerVBoxCompositionSetEnableStateGlobal(GLboolean fEnable) +{ +} + +DECLEXPORT(void) crServerVBoxScreenshotRelease(CR_SCREENSHOT *pScreenshot) +{ + if (pScreenshot->fDataAllocated) + { + RTMemFree(pScreenshot->Img.pvData); + pScreenshot->fDataAllocated = 0; + } +} + +DECLEXPORT(int) crServerVBoxScreenshotGet(uint32_t u32Screen, uint32_t width, uint32_t height, uint32_t pitch, void *pvBuffer, CR_SCREENSHOT *pScreenshot) +{ + HCR_FRAMEBUFFER hFb = CrPMgrFbGetEnabledForScreen(u32Screen); + if (!hFb) + return VERR_INVALID_STATE; + + const VBVAINFOSCREEN *pScreen = CrFbGetScreenInfo(hFb); + + if (!width) + width = pScreen->u32Width; + if (!height) + height = pScreen->u32Height; + if (!pitch) + pitch = pScreen->u32LineSize; + + if (CrFbHas3DData(hFb) + || pScreen->u32Width != width + || pScreen->u32Height != height + || pScreen->u32LineSize != pitch + || pScreen->u16BitsPerPixel != 32) + { + RTRECTSIZE SrcRectSize; + RTRECT DstRect; + + pScreenshot->Img.cbData = pScreen->u32LineSize * pScreen->u32Height; + if (!pvBuffer) + { + pScreenshot->Img.pvData = RTMemAlloc(pScreenshot->Img.cbData); + if (!pScreenshot->Img.pvData) + { + WARN(("RTMemAlloc failed")); + return VERR_NO_MEMORY; + } + pScreenshot->fDataAllocated = 1; + } + else + { + pScreenshot->Img.pvData = pvBuffer; + pScreenshot->fDataAllocated = 0; + } + + pScreenshot->Img.enmFormat = GL_BGRA; + pScreenshot->Img.width = width; + pScreenshot->Img.height = height; + pScreenshot->Img.bpp = 32; + pScreenshot->Img.pitch = pitch; + SrcRectSize.cx = pScreen->u32Width; + SrcRectSize.cy = pScreen->u32Height; + DstRect.xLeft = 0; + DstRect.yTop = 0; + DstRect.xRight = width; + DstRect.yBottom = height; + int rc = CrFbBltGetContentsEx(hFb, &SrcRectSize, &DstRect, 1, &DstRect, &pScreenshot->Img); + if (!RT_SUCCESS(rc)) + { + WARN(("CrFbBltGetContents failed %d", rc)); + crServerVBoxScreenshotRelease(pScreenshot); + return rc; + } + } + else + { + pScreenshot->Img.cbData = pScreen->u32LineSize * pScreen->u32Height; + if (!pvBuffer) + pScreenshot->Img.pvData = CrFbGetVRAM(hFb); + else + { + pScreenshot->Img.pvData = pvBuffer; + memcpy(pvBuffer, CrFbGetVRAM(hFb), pScreenshot->Img.cbData); + } + pScreenshot->Img.enmFormat = GL_BGRA; + pScreenshot->Img.width = pScreen->u32Width; + pScreenshot->Img.height = pScreen->u32Height; + pScreenshot->Img.bpp = pScreen->u16BitsPerPixel; + pScreenshot->Img.pitch = pScreen->u32LineSize; + + pScreenshot->fDataAllocated = 0; + } + + pScreenshot->u32Screen = u32Screen; + + return VINF_SUCCESS; +} + +extern DECLEXPORT(int) crServerVBoxWindowsShow(bool fShow) +{ + return CrPMgrModeWinVisible(fShow); +} + +void crServerPresentFBO(CRMuralInfo *mural) +{ + uint32_t i; + for (i = 0; i < mural->cUsedFBDatas; ++i) + { + CR_FBDATA *pData = mural->apUsedFBDatas[i]; + int rc = CrFbUpdateBegin(pData->hFb); + if (RT_SUCCESS(rc)) + { + CrFbEntryTexDataUpdate(pData->hFb, pData->hFbEntry, pData->apTexDatas[CR_SERVER_FBO_FB_IDX(mural)]); + CrFbUpdateEnd(pData->hFb); + } + else + WARN(("CrFbUpdateBegin failed rc %d", rc)); + } +} + +GLboolean crServerIsRedirectedToFBO() +{ +#ifdef DEBUG_misha + Assert(cr_server.curClient); + if (cr_server.curClient) + { + Assert(cr_server.curClient->currentMural == cr_server.currentMural); + Assert(cr_server.curClient->currentCtxInfo == cr_server.currentCtxInfo); + } +#endif + return cr_server.curClient + && cr_server.curClient->currentMural + && cr_server.curClient->currentMural->fRedirected; +} + +GLint crServerMuralFBOIdxFromBufferName(CRMuralInfo *mural, GLenum buffer) +{ + switch (buffer) + { + case GL_FRONT: + case GL_FRONT_LEFT: + case GL_FRONT_RIGHT: + return CR_SERVER_FBO_FB_IDX(mural); + case GL_BACK: + case GL_BACK_LEFT: + case GL_BACK_RIGHT: + return CR_SERVER_FBO_BB_IDX(mural); + case GL_NONE: + case GL_AUX0: + case GL_AUX1: + case GL_AUX2: + case GL_AUX3: + case GL_LEFT: + case GL_RIGHT: + case GL_FRONT_AND_BACK: + return -1; + default: + WARN(("crServerMuralFBOIdxFromBufferName: invalid buffer passed 0x%x", buffer)); + return -2; + } +} + +void crServerMuralFBOSwapBuffers(CRMuralInfo *mural) +{ + CRContext *ctx = crStateGetCurrent(); + GLuint iOldCurDrawBuffer = mural->iCurDrawBuffer; + GLuint iOldCurReadBuffer = mural->iCurReadBuffer; + mural->iBbBuffer = ((mural->iBbBuffer + 1) % (mural->cBuffers)); + if (mural->iCurDrawBuffer >= 0) + mural->iCurDrawBuffer = ((mural->iCurDrawBuffer + 1) % (mural->cBuffers)); + if (mural->iCurReadBuffer >= 0) + mural->iCurReadBuffer = ((mural->iCurReadBuffer + 1) % (mural->cBuffers)); + Assert(iOldCurDrawBuffer != mural->iCurDrawBuffer || mural->cBuffers == 1 || mural->iCurDrawBuffer < 0); + Assert(iOldCurReadBuffer != mural->iCurReadBuffer || mural->cBuffers == 1 || mural->iCurReadBuffer < 0); + if (!ctx->framebufferobject.drawFB && iOldCurDrawBuffer != mural->iCurDrawBuffer) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_DRAW_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurDrawBuffer)); + } + if (!ctx->framebufferobject.readFB && iOldCurReadBuffer != mural->iCurReadBuffer) + { + cr_server.head_spu->dispatch_table.BindFramebufferEXT(GL_READ_FRAMEBUFFER, CR_SERVER_FBO_FOR_IDX(mural, mural->iCurReadBuffer)); + } + Assert(mural->aidColorTexs[CR_SERVER_FBO_FB_IDX(mural)]); +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_occlude.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_occlude.c new file mode 100644 index 00000000..6d479369 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_occlude.c @@ -0,0 +1,37 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "chromium.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "server_dispatch.h" +#include "server.h" + +void SERVER_DISPATCH_APIENTRY +crServerDispatchGenQueriesARB(GLsizei n, GLuint *queries) +{ + GLuint *local_queries; + (void) queries; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchGenQueriesARB: parameter 'n' is out of range"); + return; + } + + local_queries = (GLuint *)crCalloc(n * sizeof(*local_queries)); + + if (!local_queries) + { + crError("crServerDispatchGenQueriesARB: out of memory"); + return; + } + + cr_server.head_spu->dispatch_table.GenQueriesARB( n, local_queries ); + + crServerReturnValue( local_queries, n * sizeof(*local_queries) ); + crFree( local_queries ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_papi.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_papi.c new file mode 100644 index 00000000..84a4e3e6 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_papi.c @@ -0,0 +1,272 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "server_dispatch.h" +#include "server.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "state/cr_statetypes.h" + +#define DEBUG_BARRIERS 1 + +void SERVER_DISPATCH_APIENTRY crServerDispatchBarrierCreateCR( GLuint name, GLuint count ) +{ + CRServerBarrier *barrier; +#if DEBUG_BARRIERS + char debug_buf[4096]; +#endif + + if (cr_server.ignore_papi) + { + cr_server.head_spu->dispatch_table.BarrierCreateCR( name, count ); + return; + } + + barrier = (CRServerBarrier *) crHashtableSearch( cr_server.barriers, name ); + +#if DEBUG_BARRIERS + sprintf( debug_buf, "BarrierCreateCR( %d, %d )", name, count ); + cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf ); +#endif + if (count == 0) + { + count = cr_server.numClients; +#if DEBUG_BARRIERS + sprintf( debug_buf, "changing count to %d", count ); + cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf ); +#endif + } + + + /* we use maxBarrierCount in Clear() and SwapBuffers() and also use it + * in __getNextClient() for deadlock detection. The issue is that all + * the existing clients may be blocked, but we might soon get another + * client connection to break the apparent deadlock. + */ + if (count > cr_server.maxBarrierCount) + cr_server.maxBarrierCount = count; + + if ( barrier == NULL ) + { + barrier = (CRServerBarrier *) crAlloc( sizeof(*barrier) ); + barrier->count = count; + barrier->num_waiting = 0; + barrier->waiting = (RunQueue **) + crAlloc( count * sizeof(*(barrier->waiting)) ); + + crHashtableAdd( cr_server.barriers, name, barrier ); +#if DEBUG_BARRIERS + sprintf( debug_buf, "This was a new barrier!" ); + cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf ); +#endif + } + else + { + /* HACK -- this allows everybody to create a barrier, and all + but the first creation are ignored, assuming the count + match. */ +#if DEBUG_BARRIERS + sprintf( debug_buf, "I already knew about this barrier." ); + cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf ); +#endif + if ( barrier->count != count ) + { +#if DEBUG_BARRIERS + sprintf( debug_buf, "And someone messed up the count!." ); + cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf ); +#endif + crError( "Barrier name=%u created with count=%u, but already " + "exists with count=%u", name, count, barrier->count ); + } + } + + if (cr_server.debug_barriers) + crDebug("crserver: BarrierCreate(id=%d, count=%d)", name, barrier->count); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchBarrierDestroyCR( GLuint name ) +{ + if (cr_server.ignore_papi) + { + cr_server.head_spu->dispatch_table.BarrierDestroyCR( name ); + return; + } + + crError( "NO BARRIER DESTROY FOR YOU! (name=%u)", name ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchBarrierExecCR( GLuint name ) +{ + CRServerBarrier *barrier; +#if DEBUG_BARRIERS + char debug_buf[4096]; +#endif + + if (cr_server.ignore_papi) + { + cr_server.head_spu->dispatch_table.BarrierExecCR( name ); + return; + } + + barrier = (CRServerBarrier *) crHashtableSearch( cr_server.barriers, name ); + if ( barrier == NULL ) + { + crError( "crServerDispatchBarrierExec: No such barrier: %d", name ); + } + +#if DEBUG_BARRIERS + sprintf( debug_buf, "BarrierExec( %d )", name ); + cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf ); + sprintf( debug_buf, "num_waiting = %d", barrier->num_waiting ); + cr_server.head_spu->dispatch_table.ChromiumParametervCR( GL_PRINT_STRING_CR, GL_UNSIGNED_BYTE, sizeof(debug_buf), debug_buf ); +#endif + + barrier->waiting[barrier->num_waiting++] = cr_server.run_queue; + + cr_server.run_queue->blocked = 1; + + if ( barrier->num_waiting == barrier->count ) + { + GLuint i; + + if (cr_server.debug_barriers) + crDebug("crserver: BarrierExec(client=%p, id=%d, num_waiting=%d/%d) - release", + cr_server.curClient, name, barrier->num_waiting, + barrier->count); + + for ( i = 0; i < barrier->count; i++ ) + { + barrier->waiting[i]->blocked = 0; + } + barrier->num_waiting = 0; + } + else if (cr_server.debug_barriers) + crDebug("crserver: BarrierExec(client=%p, id=%d, num_waiting=%d/%d) - block", + cr_server.curClient, name, barrier->num_waiting, + barrier->count); + +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchSemaphoreCreateCR( GLuint name, GLuint count ) +{ + CRServerSemaphore *sema; + + if (cr_server.ignore_papi) + { + cr_server.head_spu->dispatch_table.SemaphoreCreateCR( name, count ); + return; + } + + sema = crHashtableSearch(cr_server.semaphores, name); + if (sema) + return; /* already created */ + + sema = (CRServerSemaphore *) crAlloc( sizeof( *sema ) ); + crHashtableAdd( cr_server.semaphores, name, sema ); + sema->count = count; + sema->waiting = sema->tail = NULL; + if (cr_server.debug_barriers) + crDebug("crserver: SemaphoreCreate(id=%d, count=%d)", name, count); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchSemaphoreDestroyCR( GLuint name ) +{ + if (cr_server.ignore_papi) + { + cr_server.head_spu->dispatch_table.SemaphoreDestroyCR( name ); + return; + } + + crError( "NO DESTROY FOR YOU! (name=%u)", name ); +} + +/* Semaphore wait */ +void SERVER_DISPATCH_APIENTRY crServerDispatchSemaphorePCR( GLuint name ) +{ + CRServerSemaphore *sema; + + if (cr_server.ignore_papi) + { + cr_server.head_spu->dispatch_table.SemaphorePCR( name ); + return; + } + + sema = (CRServerSemaphore *) crHashtableSearch( cr_server.semaphores, name ); + if (!sema) + { + crError( "No such semaphore: %d", name ); + } + if (sema->count) + { + /* go */ + if (cr_server.debug_barriers) + crDebug("crserver: SemaphoreP(client=%p, id=%d, count=%d) decrement to %d", + cr_server.curClient, name, sema->count, sema->count - 1); + sema->count--; + } + else + { + /* block */ + wqnode *node; + if (cr_server.debug_barriers) + crDebug("crserver: SemaphoreP(client=%p, id=%d, count=%d) - block.", + cr_server.curClient, name, sema->count); + cr_server.run_queue->blocked = 1; + node = (wqnode *) crAlloc( sizeof( *node ) ); + node->q = cr_server.run_queue; + node->next = NULL; + if (sema->tail) + { + sema->tail->next = node; + } + else + { + sema->waiting = node; + } + sema->tail = node; + } +} + +/* Semaphore signal */ +void SERVER_DISPATCH_APIENTRY crServerDispatchSemaphoreVCR( GLuint name ) +{ + CRServerSemaphore *sema; + + if (cr_server.ignore_papi) + { + cr_server.head_spu->dispatch_table.SemaphoreVCR( name ); + return; + } + + sema = (CRServerSemaphore *) crHashtableSearch( cr_server.semaphores, name ); + if (!sema) + { + crError( "No such semaphore: %d", name ); + } + if (sema->waiting) + { + wqnode *temp = sema->waiting; + if (cr_server.debug_barriers) + crDebug("crserver: SemaphoreV(client=%p, id=%d, count=%d) - unblock.", + cr_server.curClient, name, sema->count); + /* unblock one waiter */ + temp->q->blocked = 0; + sema->waiting = temp->next; + crFree( temp ); + if (!sema->waiting) + { + sema->tail = NULL; + } + } + else + { + /* nobody's waiting */ + if (cr_server.debug_barriers) + crDebug("crserver: SemaphoreV(client=%p, id=%d, count=%d) - increment to %d", + cr_server.curClient, name, sema->count, sema->count + 1); + sema->count++; + } +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_projmatrix.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_projmatrix.c new file mode 100644 index 00000000..a4598509 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_projmatrix.c @@ -0,0 +1,402 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "server_dispatch.h" +#include "server.h" +#include "cr_error.h" +#include "state/cr_statetypes.h" +#include "cr_mem.h" +#include "cr_string.h" + +/* + * This file provides implementations of the basic OpenGL matrix functions. + * We often need to twiddle with their operation in order to make tilesorting + * and non-planar projections work. + */ + + + +/* + * Determine which view and projection matrices to use when in stereo mode. + * Return 0 = left eye, 1 = right eye. + */ +int crServerGetCurrentEye(void) +{ + if (cr_server.currentEye != -1) { + /* current eye was specified by tilesort SPU */ + return cr_server.currentEye; + } + else { + /* we have a quad-buffered window and we're watching glDrawBuffer */ + GLenum drawBuffer = cr_server.curClient->currentCtxInfo->pContext->buffer.drawBuffer; + int eye = drawBuffer == GL_BACK_RIGHT || drawBuffer == GL_FRONT_RIGHT + || drawBuffer == GL_RIGHT; + return eye; + } +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchLoadMatrixf( const GLfloat *m ) +{ + const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode; + const CRMuralInfo *mural = cr_server.curClient->currentMural; + + crStateLoadMatrixf( m ); + + if (matMode == GL_MODELVIEW && cr_server.viewOverride) { + int eye = crServerGetCurrentEye(); + crServerApplyViewMatrix(&cr_server.viewMatrix[eye]); + } + else { + cr_server.head_spu->dispatch_table.LoadMatrixf( m ); + } +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchLoadMatrixd( const GLdouble *m ) +{ + const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode; + const CRMuralInfo *mural = cr_server.curClient->currentMural; + + crStateLoadMatrixd( m ); + + if (matMode == GL_MODELVIEW && cr_server.viewOverride) { + int eye = crServerGetCurrentEye(); + crServerApplyViewMatrix(&cr_server.viewMatrix[eye]); + } + else { + cr_server.head_spu->dispatch_table.LoadMatrixd( m ); + } +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchMultMatrixf( const GLfloat *m ) +{ + const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode; + + if (matMode == GL_PROJECTION && cr_server.projectionOverride) { + /* load the overriding projection matrix */ + int eye = crServerGetCurrentEye(); + crStateLoadMatrix( &cr_server.projectionMatrix[eye] ); + } + else { + /* the usual case */ + crStateMultMatrixf( m ); + cr_server.head_spu->dispatch_table.MultMatrixf( m ); + } +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchMultMatrixd( const GLdouble *m ) +{ + const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode; + + if (matMode == GL_PROJECTION && cr_server.projectionOverride) { + /* load the overriding projection matrix */ + int eye = crServerGetCurrentEye(); + crStateLoadMatrix( &cr_server.projectionMatrix[eye] ); + } + else { + /* the usual case */ + crStateMultMatrixd( m ); + cr_server.head_spu->dispatch_table.MultMatrixd( m ); + } +} + + + +void SERVER_DISPATCH_APIENTRY crServerDispatchLoadIdentity( void ) +{ + const GLenum matMode = cr_server.curClient->currentCtxInfo->pContext->transform.matrixMode; + const CRMuralInfo *mural = cr_server.curClient->currentMural; + + crStateLoadIdentity(); + + if (matMode == GL_MODELVIEW && cr_server.viewOverride) { + int eye = crServerGetCurrentEye(); + crServerApplyViewMatrix(&cr_server.viewMatrix[eye]); + } + else { + cr_server.head_spu->dispatch_table.LoadIdentity( ); + } +} + + + +/* + * The following code is used to deal with vertex programs. + * Basically, vertex programs might not directly use the usual + * OpenGL projection matrix to project vertices from eye coords to + * clip coords. + * + * If you're using Cg then the vertex programs it generates will have + * some comments that we can scan to figure out which program parameters + * contain the projection matrix. + * In this case, look at the Cg program code for a string like + * "ModelViewProj". Then set the crserver's 'vertprog_projection_param' + * config option to this name. + * + * If you're not using Cg, you may have to tell Chromium which program + * parameters contain the projection matrix. + * In this case, look at the OpenGL application's vertex program code to + * determine which program parameters contain the projection matrix. + * Then set the crserver's 'vertprog_projection_param' config option to + * the number of the parameter which holds the first row of the matrix. + * + * Yup, this is complicated. + * + */ + + +static void matmul(GLfloat r[16], const GLfloat p[16], const GLfloat q[16]) +{ + GLfloat tmp[16]; + int i, j, k; + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + GLfloat dot = 0.0; + for (k = 0; k < 4; k++) { + dot += p[i+4*k] * q[k+4*j]; + } + tmp[i+4*j] = dot; + } + } + for (i = 0; i < 16; i++) + r[i] = tmp[i]; +} + + +static CRServerProgram * +LookupProgram(GLuint id) +{ + CRServerProgram *prog = crHashtableSearch(cr_server.programTable, id); + if (!prog) { + prog = (CRServerProgram *) crAlloc(sizeof(CRServerProgram)); + if (!prog) + return NULL; + prog->id = id; + prog->projParamStart = cr_server.vpProjectionMatrixParameter; + crHashtableAdd(cr_server.programTable, id, prog); + } + return prog; +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchProgramLocalParameter4fARB(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ +#if 0 + if (target == GL_VERTEX_PROGRAM_ARB) { + CRServerProgram *prog = LookupProgram(cr_server.currentProgram); + + if (prog && prog->projParamStart != -1) { + if (index >= (GLuint) prog->projParamStart && index <= (GLuint) prog->projParamStart + 3) { + /* save the parameters as rows in the matrix */ + const int i = index - prog->projParamStart; + prog->projMat[4*0+i] = x; + prog->projMat[4*1+i] = y; + prog->projMat[4*2+i] = z; + prog->projMat[4*3+i] = w; + } + + /* When we get the 4th row (row==3) of the projection matrix we can + * then pre-multiply it by the base matrix and update the program + * parameters with the new matrix. + */ + if (index == (GLuint) (prog->projParamStart + 3)) { + const CRMuralInfo *mural = cr_server.curClient->currentMural; + const GLfloat *baseMat = (const GLfloat *) &(mural->extents[mural->curExtent].baseProjection); + int i; + GLfloat mat[16]; + + /* pre-mult the projection matrix by the base projection */ + matmul(mat, baseMat, prog->projMat); + /* update the program parameters with the new matrix */ + for (i = 0; i < 4; i++) { + cr_server.head_spu->dispatch_table.ProgramLocalParameter4fARB(target, index + i - 3, mat[4*0+i], mat[4*1+i], mat[4*2+i], mat[4*3+i]); + } + return; /* done */ + } + } + } +#endif + + /* if we get here, pass the call through unchanged */ + cr_server.head_spu->dispatch_table.ProgramLocalParameter4fARB(target, index, x, y, z, w); +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchProgramLocalParameter4dARB(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w) +{ + crServerDispatchProgramLocalParameter4fARB(target, index, (GLfloat) x, (GLfloat) y, (GLfloat) z, (GLfloat) w); +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchProgramParameter4fNV(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ +#if 0 + if (target == GL_VERTEX_PROGRAM_NV) { + CRServerProgram *prog = LookupProgram(cr_server.currentProgram); + + if (prog && prog->projParamStart != -1) { + if (index >= (GLuint) prog->projParamStart && index <= (GLuint) prog->projParamStart + 3) { + /* save the parameters as rows in the matrix */ + const int i = index - prog->projParamStart; + prog->projMat[4*0+i] = x; + prog->projMat[4*1+i] = y; + prog->projMat[4*2+i] = z; + prog->projMat[4*3+i] = w; + } + + /* When we get the 4th row (row==3) of the projection matrix we can + * then pre-multiply it by the base matrix and update the program + * parameters with the new matrix. + */ + if (index == (GLuint) (prog->projParamStart + 3)) { + const CRMuralInfo *mural = cr_server.curClient->currentMural; + const GLfloat *baseMat = (const GLfloat *) &(mural->extents[mural->curExtent].baseProjection); + int i; + GLfloat mat[16]; + + /* pre-mult the projection matrix by the base projection */ + matmul(mat, baseMat, prog->projMat); + /* update the program parameters with the new matrix */ + for (i = 0; i < 4; i++) { + cr_server.head_spu->dispatch_table.ProgramParameter4fNV(target, index + i - 3, mat[4*0+i], mat[4*1+i], mat[4*2+i], mat[4*3+i]); + } + return; /* done */ + } + } + } +#endif + + /* if we get here, pass the call through unchanged */ + cr_server.head_spu->dispatch_table.ProgramParameter4fNV(target, index, x, y, z, w); +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchProgramParameter4dNV(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w) +{ + crServerDispatchProgramParameter4fNV(target, index, (GLfloat) x, (GLfloat) y, (GLfloat) z, (GLfloat) w); +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchProgramStringARB(GLenum target, GLenum format, GLsizei len, const GLvoid *string) +{ + if (target == GL_VERTEX_PROGRAM_ARB && + cr_server.vpProjectionMatrixVariable != NULL) { + /* scan the program string looking for 'vertprog_projection' + * If the program was generated by Cg, the info we want will look + * something like this: + * #var float4x4 ModelViewProj : : c[0], 4 : 1 : 1 + */ + CRServerProgram *prog = LookupProgram(cr_server.currentProgram); + CRASSERT(prog); + if (prog) { + const char *varPos, *paramPos; + varPos = crStrstr((const char *) string, cr_server.vpProjectionMatrixVariable); + if (varPos) { + paramPos = crStrstr(varPos, "c["); + if (paramPos) { + char number[10]; + int i = 0; + paramPos += 2; /* skip "c[" */ + while (crIsDigit(paramPos[i])) { + number[i] = paramPos[i]; + i++; + } + number[i] = 0; + prog->projParamStart = crStrToInt(number); + } + } + else { + crWarning("Didn't find %s parameter in vertex program string", + cr_server.vpProjectionMatrixVariable); + } + } + } + + /* pass through */ + crStateProgramStringARB(target, format, len, string); + cr_server.head_spu->dispatch_table.ProgramStringARB(target, format, len, string); +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchLoadProgramNV(GLenum target, GLuint id, GLsizei len, const GLubyte *string) +{ + if (target == GL_VERTEX_PROGRAM_NV && + cr_server.vpProjectionMatrixVariable != NULL) { + /* scan the program string looking for 'vertprog_projection' + * If the program was generated by Cg, the info we want will look + * something like this: + * #var float4x4 ModelViewProj : : c[0], 4 : 1 : 1 + */ + CRServerProgram *prog = LookupProgram(id); + CRASSERT(prog); + if (prog) { + const char *varPos, *paramPos; + varPos = crStrstr((const char *) string, cr_server.vpProjectionMatrixVariable); + if (varPos) { + paramPos = crStrstr(varPos, "c["); + if (paramPos) { + char number[10]; + int i = 0; + paramPos += 2; /* skip "c[" */ + while (crIsDigit(paramPos[i])) { + number[i] = paramPos[i]; + i++; + } + number[i] = 0; + prog->projParamStart = crStrToInt(number); + } + } + else { + crWarning("Didn't find %s parameter in vertex program string", + cr_server.vpProjectionMatrixVariable); + } + } + } + + /* pass through */ + crStateLoadProgramNV(target, id, len, string); + cr_server.head_spu->dispatch_table.LoadProgramNV(target, id, len, string); +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchBindProgramARB(GLenum target, GLuint id) +{ + id = crServerTranslateProgramID(id); + + if (target == GL_VERTEX_PROGRAM_ARB) { + CRServerProgram *prog = LookupProgram(id); + (void) prog; + cr_server.currentProgram = id; + } + + /* pass through */ + crStateBindProgramARB(target, id); + cr_server.head_spu->dispatch_table.BindProgramARB(target, id); +} + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchBindProgramNV(GLenum target, GLuint id) +{ + if (target == GL_VERTEX_PROGRAM_NV) { + CRServerProgram *prog = LookupProgram(id); + (void) prog; + cr_server.currentProgram = id; + } + /* pass through */ + crStateBindProgramNV(target, id); + cr_server.head_spu->dispatch_table.BindProgramNV(target, id); +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_readpixels.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_readpixels.c new file mode 100644 index 00000000..b53ab02b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_readpixels.c @@ -0,0 +1,91 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "cr_pixeldata.h" +#include "cr_unpack.h" +#include "server_dispatch.h" +#include "server.h" + + +void SERVER_DISPATCH_APIENTRY +crServerDispatchReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid *pixels) +{ + const GLint stride = READ_DATA( 24, GLint ); + const GLint alignment = READ_DATA( 28, GLint ); + const GLint skipRows = READ_DATA( 32, GLint ); + const GLint skipPixels = READ_DATA( 36, GLint ); + const GLint bytes_per_row = READ_DATA( 40, GLint ); + const GLint rowLength = READ_DATA( 44, GLint ); + + CRASSERT(bytes_per_row > 0); + +#ifdef CR_ARB_pixel_buffer_object + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + GLvoid *pbo_offset; + + /*pixels are actually a pointer to location of 8byte network pointer in hgcm buffer + regardless of guest/host bitness we're using only 4lower bytes as there're no + pbo>4gb (yet?) + */ + pbo_offset = (GLvoid*) ((uintptr_t) *((GLint*)pixels)); + + cr_server.head_spu->dispatch_table.ReadPixels(x, y, width, height, + format, type, pbo_offset); + } + else +#endif + { + CRMessageReadPixels *rp; + uint32_t msg_len; + + if (bytes_per_row <= 0 || height <= 0 || bytes_per_row > INT32_MAX / height) + { + crError("crServerDispatchReadPixels: parameters out of range"); + return; + } + + msg_len = sizeof(*rp) + (uint32_t)bytes_per_row * height; + + rp = (CRMessageReadPixels *) crAlloc( msg_len ); + if (!rp) + { + crError("crServerDispatchReadPixels: out of memory"); + return; + } + + /* Note: the ReadPixels data gets densely packed into the buffer + * (no skip pixels, skip rows, etc. It's up to the receiver (pack spu, + * tilesort spu, etc) to apply the real PixelStore packing parameters. + */ + cr_server.head_spu->dispatch_table.ReadPixels(x, y, width, height, + format, type, rp + 1); + + rp->header.type = CR_MESSAGE_READ_PIXELS; + rp->width = width; + rp->height = height; + rp->bytes_per_row = bytes_per_row; + rp->stride = stride; + rp->format = format; + rp->type = type; + rp->alignment = alignment; + rp->skipRows = skipRows; + rp->skipPixels = skipPixels; + rp->rowLength = rowLength; + + /* <pixels> points to the 8-byte network pointer */ + crMemcpy( &rp->pixels, pixels, sizeof(rp->pixels) ); + + crNetSend( cr_server.curClient->conn, NULL, rp, msg_len ); + crFree( rp ); + } +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py new file mode 100755 index 00000000..421f3e8d --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_retval.py @@ -0,0 +1,83 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. + +from __future__ import print_function +import sys + +import apiutil + + +apiutil.CopyrightC() + +print(""" +/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY server_retval.py SCRIPT */ +#include "chromium.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" + +void crServerReturnValue( const void *payload, unsigned int payload_len ) +{ + if (!cr_server.fProcessingPendedCommands) + { + CRMessageReadback *rb; + int msg_len; + + /* Don't reply to client if we're loading VM snapshot*/ + if (cr_server.bIsInLoadingState) + return; + + if (cr_server.curClient->conn->type == CR_FILE) + { + return; + } + + if (payload_len >= INT32_MAX - sizeof( *rb )) + { + return; + } + + msg_len = sizeof( *rb ) + payload_len; + rb = (CRMessageReadback *) crAlloc( msg_len ); + + rb->header.type = CR_MESSAGE_READBACK; + CRDBGPTR_PRINTRB(cr_server.curClient->conn->u32ClientID, &cr_server.writeback_ptr); + CRDBGPTR_CHECKNZ(&cr_server.writeback_ptr); + CRDBGPTR_CHECKNZ(&cr_server.return_ptr); + crMemcpy( &(rb->writeback_ptr), &(cr_server.writeback_ptr), sizeof( rb->writeback_ptr ) ); + crMemcpy( &(rb->readback_ptr), &(cr_server.return_ptr), sizeof( rb->readback_ptr ) ); + crMemcpy( rb+1, payload, payload_len ); + crNetSend( cr_server.curClient->conn, NULL, rb, msg_len ); + CRDBGPTR_SETZ(&cr_server.writeback_ptr); + CRDBGPTR_SETZ(&cr_server.return_ptr); + crFree( rb ); + return; + } +#ifdef DEBUG_misha + WARN(("Pending command returns value")); +#endif + CRDBGPTR_SETZ(&cr_server.writeback_ptr); + CRDBGPTR_SETZ(&cr_server.return_ptr); +} +""") + +keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt") + +for func_name in keys: + params = apiutil.Parameters(func_name) + return_type = apiutil.ReturnType(func_name) + if apiutil.FindSpecial( "server", func_name ): + continue + if "VBox" == apiutil.Category(func_name): + continue + if return_type != 'void': + print('%s SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s)' % ( return_type, func_name, apiutil.MakeDeclarationString(params))) + print('{') + print('\t%s retval;' % return_type) + print('\tretval = cr_server.head_spu->dispatch_table.%s(%s);' % (func_name, apiutil.MakeCallString(params) )) + print('\tcrServerReturnValue( &retval, sizeof(retval) );') + print('\treturn retval; /* WILL PROBABLY BE IGNORED */') + print('}') diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp new file mode 100644 index 00000000..5569e497 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_rpw.cpp @@ -0,0 +1,755 @@ +/* $Id: server_rpw.cpp $ */ + +/** @file + * VBox crOpenGL: Read Pixels worker + */ + +/* + * Copyright (C) 2010-2019 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. + */ +#include "server.h" +#include "cr_string.h" +#include "cr_mem.h" +#include "cr_vreg.h" +#include "render/renderspu.h" + +static void crServerRpwWorkerGpuSubmit(PRTLISTNODE pWorkList) +{ + CR_SERVER_RPW_ENTRY *pCurEntry; + RTListForEach(pWorkList, pCurEntry, CR_SERVER_RPW_ENTRY, WorkerWorkEntry) + { + cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, CR_SERVER_RPW_ENTRY_TEX(pCurEntry, Worker)); + + if (CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pCurEntry)) + { + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, CR_SERVER_RPW_ENTRY_PBO_CUR(pCurEntry)); + /*read the texture, note pixels are NULL for PBO case as it's offset in the buffer*/ + cr_server.head_spu->dispatch_table.GetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); + CR_SERVER_RPW_ENTRY_PBO_FLIP(pCurEntry); + } + else + { + void *pvData = crAlloc(4*pCurEntry->Size.cx*pCurEntry->Size.cy); + if (pvData) + { + cr_server.head_spu->dispatch_table.GetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, pvData); + + pCurEntry->pfnData(pCurEntry, pvData); + + crFree(pvData); + } + else + { + crWarning("crAlloc failed"); + } + } + + cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, 0); + } +} + +static void crServerRpwWorkerGpuComplete(PRTLISTNODE pGpuSubmitedList) +{ + CR_SERVER_RPW_ENTRY *pCurEntry; + RTListForEach(pGpuSubmitedList, pCurEntry, CR_SERVER_RPW_ENTRY, GpuSubmittedEntry) + { + Assert(CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pCurEntry)); + + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, CR_SERVER_RPW_ENTRY_PBO_COMPLETED(pCurEntry)); + + void *pvData = cr_server.head_spu->dispatch_table.MapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY); + + pCurEntry->pfnData(pCurEntry, pvData); + + cr_server.head_spu->dispatch_table.UnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); + + cr_server.head_spu->dispatch_table.BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, pCurEntry->Size.cx*pCurEntry->Size.cy*4, 0, GL_STREAM_READ_ARB); + + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); + } +} + +static void crServerRpwWorkerGpuMarkGpuCompletedSubmitedLocked(PRTLISTNODE pGpuSubmitedList, PRTLISTNODE pWorkList) +{ + CR_SERVER_RPW_ENTRY *pCurEntry, *pNextEntry; + RTListForEachSafe(pGpuSubmitedList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, GpuSubmittedEntry) + { + CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pCurEntry, Gpu); + RTListNodeRemove(&pCurEntry->GpuSubmittedEntry); + } + + Assert(RTListIsEmpty(pGpuSubmitedList)); + + RTListForEachSafe(pWorkList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, WorkerWorkEntry) + { + Assert(CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pCurEntry, Worker)); + RTListNodeRemove(&pCurEntry->WorkerWorkEntry); + if (CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pCurEntry)) + { + /* PBO mode, put to the GPU submitted queue*/ + RTListAppend(pGpuSubmitedList, &pCurEntry->GpuSubmittedEntry); + CR_SERVER_RPW_ENTRY_TEX_PROMOTE(pCurEntry, Worker, Gpu); + } + else + { + /* no PBO, we are already done entry data processing, free it right away */ + Assert(!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pCurEntry, Gpu)); + CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pCurEntry, Worker); + } + } +} + +static void crServerRpwWorkerGetWorkLocked(CR_SERVER_RPW *pWorker, PRTLISTNODE pWorkList) +{ + CR_SERVER_RPW_ENTRY *pCurEntry, *pNextEntry; + RTListForEachSafe(&pWorker->WorkList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, WorkEntry) + { + RTListNodeRemove(&pCurEntry->WorkEntry); + RTListAppend(pWorkList, &pCurEntry->WorkerWorkEntry); + CR_SERVER_RPW_ENTRY_TEX_PROMOTE(pCurEntry, Submitted, Worker); + } +} + +static DECLCALLBACK(int) crServerRpwWorkerThread(RTTHREAD ThreadSelf, void *pvUser) +{ + CR_SERVER_RPW *pWorker = (CR_SERVER_RPW *)pvUser; + RTMSINTERVAL cWaitMillis = RT_INDEFINITE_WAIT; + RTLISTNODE WorkList, GpuSubmittedList; + CR_SERVER_RPW_CTL_TYPE enmCtlType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED; + CR_SERVER_RPW_ENTRY *pCtlEntry = NULL; + CRMuralInfo *pDummyMural = crServerGetDummyMural(pWorker->ctxVisBits); + bool fExit = false; + bool fForceComplete = false; + bool fNotifyCmdCompleted = false; + + CRASSERT(pDummyMural); + + int rc = RTSemEventSignal(pWorker->Ctl.hCompleteEvent); + if (!RT_SUCCESS(rc)) + { + crWarning("RTSemEventSignal failed rc %d", rc); + return rc; + } + + RTListInit(&WorkList); + RTListInit(&GpuSubmittedList); + + cr_server.head_spu->dispatch_table.MakeCurrent(pDummyMural->spuWindow, 0, pWorker->ctxId); + + rc = RTCritSectEnter(&pWorker->CritSect); + if (!RT_SUCCESS(rc)) + { + crWarning("RTCritSectEnter failed, rc %d", rc); + goto end; + } + + for (;;) + { + /* the crit sect is locked here */ + + if (pWorker->Ctl.enmType != CR_SERVER_RPW_CTL_TYPE_UNDEFINED) + { + enmCtlType = pWorker->Ctl.enmType; + pCtlEntry = pWorker->Ctl.pEntry; + pWorker->Ctl.enmType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED; + pWorker->Ctl.pEntry = NULL; + } + + crServerRpwWorkerGetWorkLocked(pWorker, &WorkList); + + RTCritSectLeave(&pWorker->CritSect); + + if (enmCtlType != CR_SERVER_RPW_CTL_TYPE_UNDEFINED) + { + switch (enmCtlType) + { + case CR_SERVER_RPW_CTL_TYPE_WAIT_COMPLETE: + break; + case CR_SERVER_RPW_CTL_TYPE_TERM: + fExit = true; + break; + default: + crWarning("unexpected CtlType %d", enmCtlType); + break; + } + enmCtlType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED; + pCtlEntry = NULL; + fNotifyCmdCompleted = true; + } + + bool fNewItems = !RTListIsEmpty(&WorkList); + bool fCompleted = false; + + if (fNewItems) + { + crServerRpwWorkerGpuSubmit(&WorkList); + } + + if (!RTListIsEmpty(&GpuSubmittedList)) + { + if (fForceComplete || fNewItems) + { + crServerRpwWorkerGpuComplete(&GpuSubmittedList); + fForceComplete = false; + fCompleted = true; + } + } + + rc = RTCritSectEnter(&pWorker->CritSect); + if (!RT_SUCCESS(rc)) + { + crWarning("RTCritSectEnter failed, rc %d", rc); + break; + } + + /* fNewGpuItems means new entries arrived. WorkList contains new GPU submitted data + * fCompleted means completion was performed, GpuSubmittedList contains old GPU submitted data, + * which is now completed and should be released */ + if (fNewItems || fCompleted) + { + crServerRpwWorkerGpuMarkGpuCompletedSubmitedLocked(&GpuSubmittedList, &WorkList); + } + + if (fExit || !fNewItems) + { + RTCritSectLeave(&pWorker->CritSect); + + if (fNotifyCmdCompleted) + { + rc = RTSemEventSignal(pWorker->Ctl.hCompleteEvent); + if (!RT_SUCCESS(rc)) + { + crWarning("RTSemEventSignal failed rc %d", rc); + break; + } + fNotifyCmdCompleted = false; + } + + if (fExit) + break; + + if (!RTListIsEmpty(&GpuSubmittedList)) + cWaitMillis = 17; /* ~60Hz */ + else + cWaitMillis = RT_INDEFINITE_WAIT; + + rc = RTSemEventWait(pWorker->hSubmitEvent, cWaitMillis); + if (!RT_SUCCESS(rc) && rc != VERR_TIMEOUT) + { + crWarning("RTSemEventWait failed, rc %d", rc); + break; + } + + if (rc == VERR_TIMEOUT) + { + Assert(!RTListIsEmpty(&GpuSubmittedList)); + fForceComplete = true; + } + + rc = RTCritSectEnter(&pWorker->CritSect); + if (!RT_SUCCESS(rc)) + { + crWarning("RTCritSectEnter failed, rc %d", rc); + break; + } + } + } + +end: + cr_server.head_spu->dispatch_table.MakeCurrent(0, 0, 0); + + return rc; +} + +static int crServerRpwCtlNotify(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry) +{ + int rc = RTSemEventSignal(pWorker->hSubmitEvent); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventWait(pWorker->Ctl.hCompleteEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + rc = pWorker->Ctl.rc; + if (!RT_SUCCESS(rc)) + { + crWarning("WdCtl command failed rc %d", rc); + } + } + else + { + crWarning("RTSemEventWait failed rc %d", rc); + } + } + else + { + int tmpRc; + crWarning("RTSemEventSignal failed rc %d", rc); + tmpRc = RTCritSectEnter(&pWorker->CritSect); + if (RT_SUCCESS(tmpRc)) + { + pWorker->Ctl.enmType = CR_SERVER_RPW_CTL_TYPE_UNDEFINED; + pWorker->Ctl.pEntry = NULL; + RTCritSectLeave(&pWorker->CritSect); + } + else + { + crWarning("RTSemEventSignal failed tmpRc %d", tmpRc); + } + } + + return rc; +} + +static int crServerRpwCtl(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_CTL_TYPE enmType, CR_SERVER_RPW_ENTRY *pEntry) +{ + int rc = RTCritSectEnter(&pWorker->CritSect); + if (RT_SUCCESS(rc)) + { + pWorker->Ctl.enmType = enmType; + pWorker->Ctl.pEntry = pEntry; + RTCritSectLeave(&pWorker->CritSect); + } + else + { + crWarning("RTCritSectEnter failed rc %d", rc); + return rc; + } + + rc = crServerRpwCtlNotify(pWorker, pEntry); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwCtlNotify failed rc %d", rc); + return rc; + } + return VINF_SUCCESS; +} + +int crServerRpwInit(CR_SERVER_RPW *pWorker) +{ + int rc; + + memset(pWorker, 0, sizeof (*pWorker)); + + RTListInit(&pWorker->WorkList); + + rc = RTCritSectInit(&pWorker->CritSect); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventCreate(&pWorker->hSubmitEvent); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventCreate(&pWorker->Ctl.hCompleteEvent); + if (RT_SUCCESS(rc)) + { + CRASSERT(cr_server.MainContextInfo.CreateInfo.realVisualBits); + CRASSERT(cr_server.MainContextInfo.SpuContext); + + pWorker->ctxId = cr_server.head_spu->dispatch_table.CreateContext("", cr_server.MainContextInfo.CreateInfo.realVisualBits, cr_server.MainContextInfo.SpuContext); + if (pWorker->ctxId) + { + CRMuralInfo *pDummyMural; + pWorker->ctxVisBits = cr_server.MainContextInfo.CreateInfo.realVisualBits; + pDummyMural = crServerGetDummyMural(pWorker->ctxVisBits); + if (pDummyMural) + { + /* since CreateContext does not actually create it on some platforms, e.g. on win, + * we need to do MakeCurrent to ensure it is created. + * There is some black magic in doing that to work around ogl driver bugs + * (i.e. we need to switch offscreen rendering off before doing make current) */ + CR_SERVER_CTX_SWITCH CtxSwitch; + + crServerCtxSwitchPrepare(&CtxSwitch, NULL); + + cr_server.head_spu->dispatch_table.Flush(); + + cr_server.head_spu->dispatch_table.MakeCurrent(pDummyMural->spuWindow, 0, pWorker->ctxId); + + if (cr_server.currentCtxInfo) + { + CRASSERT(cr_server.currentMural); + cr_server.head_spu->dispatch_table.MakeCurrent(cr_server.currentMural->spuWindow, 0, + cr_server.currentCtxInfo->SpuContext > 0 ? cr_server.currentCtxInfo->SpuContext : cr_server.MainContextInfo.SpuContext); + } + else + cr_server.head_spu->dispatch_table.MakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); + + crServerCtxSwitchPostprocess(&CtxSwitch); + + rc = RTThreadCreate(&pWorker->hThread, crServerRpwWorkerThread, pWorker, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "CrServerDw"); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventWait(pWorker->Ctl.hCompleteEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + return VINF_SUCCESS; + } + else + { + crWarning("RTSemEventWait failed rc %d", rc); + } + } + else + { + crWarning("RTThreadCreate failed rc %d", rc); + } + } + else + { + crWarning("Failed to get dummy mural"); + rc = VERR_GENERAL_FAILURE; + } + cr_server.head_spu->dispatch_table.DestroyContext(pWorker->ctxId); + } + else + { + crWarning("CreateContext failed rc %d", rc); + } + + RTSemEventDestroy(pWorker->Ctl.hCompleteEvent); + } + else + { + crWarning("RTSemEventCreate failed rc %d", rc); + } + RTSemEventDestroy(pWorker->hSubmitEvent); + } + else + { + crWarning("RTSemEventCreate failed rc %d", rc); + } + + RTCritSectDelete(&pWorker->CritSect); + } + else + { + crWarning("RTCritSectInit failed rc %d", rc); + } + + return rc; +} + +int crServerRpwEntryResizeCleaned(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height) +{ + CRContext *pContext; + if (!width || !height) + { + return VINF_SUCCESS; + } + + if (!cr_server.currentCtxInfo) + { + CRMuralInfo *pDummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + if (!pDummy) + { + crWarning("crServerGetDummyMural failed"); + return VERR_GENERAL_FAILURE; + } + + + crServerPerformMakeCurrent(pDummy, &cr_server.MainContextInfo); + } + + Assert(width); + Assert(height); + + pContext = cr_server.currentCtxInfo->pContext; + + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + { + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + } + + for (int i = 0; i < 4; ++i) + { + cr_server.head_spu->dispatch_table.GenTextures(1, &pEntry->aidWorkerTexs[i]); + + cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, pEntry->aidWorkerTexs[i]); + cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + cr_server.head_spu->dispatch_table.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + cr_server.head_spu->dispatch_table.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, + 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + } + + pEntry->iTexDraw = -pEntry->iTexDraw; + + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + { + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pContext->bufferobject.unpackBuffer->hwid); + } + + if (cr_server.bUsePBOForReadback) + { + for (int i = 0; i < 2; ++i) + { + cr_server.head_spu->dispatch_table.GenBuffersARB(1, &pEntry->aidPBOs[i]); + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pEntry->aidPBOs[i]); + cr_server.head_spu->dispatch_table.BufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, width*height*4, 0, GL_STREAM_READ_ARB); + } + + if (crStateIsBufferBound(GL_PIXEL_PACK_BUFFER_ARB)) + { + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pContext->bufferobject.packBuffer->hwid); + } + else + { + cr_server.head_spu->dispatch_table.BindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); + } + pEntry->iCurPBO = 0; + } + + + GLuint uid = pContext->texture.unit[pContext->texture.curTextureUnit].currentTexture2D->hwid; + cr_server.head_spu->dispatch_table.BindTexture(GL_TEXTURE_2D, uid); + + + pEntry->Size.cx = width; + pEntry->Size.cy = height; + + crServerRpwEntryDbgVerify(pEntry); + + return VINF_SUCCESS; +} + +int crServerRpwEntryCleanup(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry) +{ + if (!pEntry->Size.cx) + return VINF_SUCCESS; + + int rc = crServerRpwEntryCancel(pWorker, pEntry); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwEntryCancel failed rc %d", rc); + return rc; + } + + if (!cr_server.currentCtxInfo) + { + CRMuralInfo *pDummy = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + if (!pDummy) + { + crWarning("crServerGetDummyMural failed"); + return VERR_GENERAL_FAILURE; + } + + + crServerPerformMakeCurrent(pDummy, &cr_server.MainContextInfo); + } + + cr_server.head_spu->dispatch_table.DeleteTextures(4, pEntry->aidWorkerTexs); + + if (CR_SERVER_RPW_ENTRY_PBO_IS_ACTIVE(pEntry)) + { + cr_server.head_spu->dispatch_table.DeleteBuffersARB(2, pEntry->aidPBOs); + memset(pEntry->aidPBOs, 0, sizeof (pEntry->aidPBOs)); + pEntry->iCurPBO = -1; + } + + memset(pEntry->aidWorkerTexs, 0, sizeof (pEntry->aidWorkerTexs)); + CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Submitted); + CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Worker); + CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Gpu); + pEntry->iTexDraw = -1; + pEntry->iTexSubmitted = -2; + pEntry->iTexWorker = -3; + pEntry->iTexGpu = -4; + pEntry->Size.cx = 0; + pEntry->Size.cy = 0; + return VINF_SUCCESS; +} + +int crServerRpwEntryResize(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height) +{ + if (!width || !height) + { + width = 0; + height = 0; + } + + if (width == pEntry->Size.cx && width == pEntry->Size.cy) + return VINF_SUCCESS; + + int rc = crServerRpwEntryCleanup(pWorker, pEntry); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwEntryCleanup failed rc %d", rc); + return rc; + } + + rc = crServerRpwEntryResizeCleaned(pWorker, pEntry, width, height); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwEntryResizeCleaned failed rc %d", rc); + } + return rc; +} + +int crServerRpwEntryInit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, uint32_t width, uint32_t height, PFNCR_SERVER_RPW_DATA pfnData) +{ + memset(pEntry, 0, sizeof (*pEntry)); + + pEntry->iTexDraw = -1; + pEntry->iTexSubmitted = -2; + pEntry->iTexWorker = -3; + pEntry->iTexGpu = -4; + pEntry->iCurPBO = -1; + pEntry->pfnData = pfnData; + int rc = crServerRpwEntryResizeCleaned(pWorker, pEntry, width, height); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwEntryResizeCleaned failed rc %d", rc); + return rc; + } + return VINF_SUCCESS; +} + +int crServerRpwEntrySubmit(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry) +{ + if (!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Draw)) + { + crWarning("submitting empty entry, ignoting"); + Assert(!pEntry->Size.cx); + Assert(!pEntry->Size.cy); + return VERR_INVALID_PARAMETER; + } + + Assert(pEntry->Size.cx); + Assert(pEntry->Size.cy); + + int rc = RTCritSectEnter(&pWorker->CritSect); + if (RT_SUCCESS(rc)) + { + Assert(pWorker->Ctl.enmType == CR_SERVER_RPW_CTL_TYPE_UNDEFINED); + if (!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Submitted)) + { + CR_SERVER_RPW_ENTRY_TEX_PROMOTE_KEEPVALID(pEntry, Draw, Submitted); + RTListAppend(&pWorker->WorkList, &pEntry->WorkEntry); + } + else + { + CR_SERVER_RPW_ENTRY_TEX_XCHG_VALID(pEntry, Draw, Submitted); + } + RTCritSectLeave(&pWorker->CritSect); + + RTSemEventSignal(pWorker->hSubmitEvent); + } + else + { + crWarning("RTCritSectEnter failed rc %d", rc); + return rc; + } + + return rc; +} + +static int crServerRpwEntryCancelCtl(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry, CR_SERVER_RPW_CTL_TYPE enmType) +{ + if (CR_SERVER_RPW_CTL_TYPE_TERM == enmType && pEntry) + { + crWarning("Entry should be null for term request"); + pEntry = NULL; + } + + int rc = RTCritSectEnter(&pWorker->CritSect); + if (RT_SUCCESS(rc)) + { + if (pEntry) + { + if (CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Submitted)) + { + CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pEntry, Submitted); + RTListNodeRemove(&pEntry->WorkEntry); + } + + if (!CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Worker) && !CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pEntry, Gpu)) + { + /* can cancel it wight away */ + RTCritSectLeave(&pWorker->CritSect); + return VINF_SUCCESS; + } + } + else + { + CR_SERVER_RPW_ENTRY *pCurEntry, *pNextEntry; + RTListForEachSafe(&pWorker->WorkList, pCurEntry, pNextEntry, CR_SERVER_RPW_ENTRY, WorkEntry) + { + CR_SERVER_RPW_ENTRY_TEX_IS_VALID(pCurEntry, Submitted); + CR_SERVER_RPW_ENTRY_TEX_INVALIDATE(pEntry, Submitted); + RTListNodeRemove(&pCurEntry->WorkEntry); + } + } + pWorker->Ctl.enmType = enmType; + pWorker->Ctl.pEntry = pEntry; + RTCritSectLeave(&pWorker->CritSect); + } + else + { + crWarning("RTCritSectEnter failed rc %d", rc); + return rc; + } + + rc = crServerRpwCtlNotify(pWorker, pEntry); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwCtlNotify failed rc %d", rc); + } + return VINF_SUCCESS; +} + +int crServerRpwEntryWaitComplete(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry) +{ + int rc = crServerRpwCtl(pWorker, CR_SERVER_RPW_CTL_TYPE_WAIT_COMPLETE, pEntry); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwCtl failed rc %d", rc); + } + return rc; +} + +int crServerRpwEntryCancel(CR_SERVER_RPW *pWorker, CR_SERVER_RPW_ENTRY *pEntry) +{ + return crServerRpwEntryCancelCtl(pWorker, pEntry, CR_SERVER_RPW_CTL_TYPE_WAIT_COMPLETE); +} + +static int crServerRpwCtlTerm(CR_SERVER_RPW *pWorker) +{ + int rc = crServerRpwEntryCancelCtl(pWorker, NULL, CR_SERVER_RPW_CTL_TYPE_TERM); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwCtl failed rc %d", rc); + } + return rc; +} + +int crServerRpwTerm(CR_SERVER_RPW *pWorker) +{ + int rc = crServerRpwCtlTerm(pWorker); + if (!RT_SUCCESS(rc)) + { + crWarning("crServerRpwCtlTerm failed rc %d", rc); + return rc; + } + + rc = RTThreadWait(pWorker->hThread, RT_INDEFINITE_WAIT, NULL); + if (!RT_SUCCESS(rc)) + { + crWarning("RTThreadWait failed rc %d", rc); + return rc; + } + + RTSemEventDestroy(pWorker->Ctl.hCompleteEvent); + RTSemEventDestroy(pWorker->hSubmitEvent); + RTCritSectDelete(&pWorker->CritSect); + + return VINF_SUCCESS; +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py new file mode 100755 index 00000000..3ccda91c --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_simpleget.py @@ -0,0 +1,152 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. + +from __future__ import print_function +import sys + +import apiutil + + +apiutil.CopyrightC() + +print("""#include "cr_spu.h" +#include "chromium.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" +""") + +from get_sizes import *; + + +funcs = [ 'GetIntegerv', 'GetFloatv', 'GetDoublev', 'GetBooleanv' ] +types = [ 'GLint', 'GLfloat', 'GLdouble', 'GLboolean' ] + +for index in range(len(funcs)): + func_name = funcs[index] + params = apiutil.Parameters(func_name) + print('void SERVER_DISPATCH_APIENTRY crServerDispatch%s(%s)' % ( func_name, apiutil.MakeDeclarationString(params))) + print('{') + print('\t%s *get_values;' % types[index]) + print('\tint tablesize;') + print(""" + #ifdef CR_ARB_texture_compression + if (GL_COMPRESSED_TEXTURE_FORMATS_ARB == pname) + { + GLint numtexfmts = 0; + cr_server.head_spu->dispatch_table.GetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB, &numtexfmts); + tablesize = numtexfmts * sizeof(%s); + } + else + #endif + { + tablesize = __numValues( pname ) * sizeof(%s); + } + """ % (types[index], types[index])) + print('\t(void) params;') + print('\tget_values = (%s *) crAlloc( tablesize );' % types[index]) + print('\tif (tablesize>0)') + print('\tcr_server.head_spu->dispatch_table.%s( pname, get_values );' % func_name) + print(""" + if (GL_TEXTURE_BINDING_1D==pname + || GL_TEXTURE_BINDING_2D==pname + || GL_TEXTURE_BINDING_3D==pname + || GL_TEXTURE_BINDING_RECTANGLE_ARB==pname + || GL_TEXTURE_BINDING_CUBE_MAP_ARB==pname) + { + GLuint texid; + CRASSERT(tablesize/sizeof(%s)==1); + texid = (GLuint) *get_values; + *get_values = (%s) crStateTextureHWIDtoID(texid); + } + else if (GL_CURRENT_PROGRAM==pname) + { + GLuint programid; + CRASSERT(tablesize/sizeof(%s)==1); + programid = (GLuint) *get_values; + *get_values = (%s) crStateGLSLProgramHWIDtoID(programid); + } + else if (GL_FRAMEBUFFER_BINDING_EXT==pname + ||GL_READ_FRAMEBUFFER_BINDING==pname) + { + GLuint fboid; + CRASSERT(tablesize/sizeof(%s)==1); + fboid = (GLuint) *get_values; + if (crServerIsRedirectedToFBO() + && (fboid==cr_server.curClient->currentMural->aidFBOs[0] + || fboid==cr_server.curClient->currentMural->aidFBOs[1])) + { + fboid = 0; + } + else + { + fboid = crStateFBOHWIDtoID(fboid); + } + *get_values = (%s) fboid; + } + else if (GL_READ_BUFFER==pname) + { + if (crServerIsRedirectedToFBO() + && CR_SERVER_FBO_FOR_IDX(cr_server.curClient->currentMural, cr_server.curClient->currentMural->iCurReadBuffer) + && !crStateGetCurrent()->framebufferobject.readFB) + { + *get_values = (%s) crStateGetCurrent()->buffer.readBuffer; + Assert(crStateGetCurrent()->buffer.readBuffer == GL_BACK || crStateGetCurrent()->buffer.readBuffer == GL_FRONT); + } + } + else if (GL_DRAW_BUFFER==pname) + { + if (crServerIsRedirectedToFBO() + && CR_SERVER_FBO_FOR_IDX(cr_server.curClient->currentMural, cr_server.curClient->currentMural->iCurDrawBuffer) + && !crStateGetCurrent()->framebufferobject.drawFB) + { + *get_values = (%s) crStateGetCurrent()->buffer.drawBuffer; + Assert(crStateGetCurrent()->buffer.drawBuffer == GL_BACK || crStateGetCurrent()->buffer.drawBuffer == GL_FRONT); + } + } + else if (GL_RENDERBUFFER_BINDING_EXT==pname) + { + GLuint rbid; + CRASSERT(tablesize/sizeof(%s)==1); + rbid = (GLuint) *get_values; + *get_values = (%s) crStateRBOHWIDtoID(rbid); + } + else if (GL_ARRAY_BUFFER_BINDING_ARB==pname + || GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB==pname + || GL_VERTEX_ARRAY_BUFFER_BINDING_ARB==pname + || GL_NORMAL_ARRAY_BUFFER_BINDING_ARB==pname + || GL_COLOR_ARRAY_BUFFER_BINDING_ARB==pname + || GL_INDEX_ARRAY_BUFFER_BINDING_ARB==pname + || GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB==pname + || GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB==pname + || GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB==pname + || GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB==pname + || GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB==pname) + { + GLuint bufid; + CRASSERT(tablesize/sizeof(%s)==1); + bufid = (GLuint) *get_values; + *get_values = (%s) crStateBufferHWIDtoID(bufid); + } + else if (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS==pname) + { + if (CR_MAX_TEXTURE_UNITS < (GLuint)*get_values) + { + *get_values = (%s)CR_MAX_TEXTURE_UNITS; + } + } + else if (GL_MAX_VERTEX_ATTRIBS_ARB==pname) + { + if (CR_MAX_VERTEX_ATTRIBS < (GLuint)*get_values) + { + *get_values = (%s)CR_MAX_VERTEX_ATTRIBS; + } + } + """ % (types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index], types[index])) + print('\tcrServerReturnValue( get_values, tablesize );') + print('\tcrFree(get_values);') + print('}\n') diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special new file mode 100644 index 00000000..e640a8b6 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special @@ -0,0 +1,268 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. +BoundsInfoCR +Clear +GetBooleanv +GetDoublev +GetFloatv +GetIntegerv +GetString +GenTextures +GetTexImage +GetMapdv +GetMapiv +GetMapfv +GetPixelMapfv +GetPixelMapuiv +GetPixelMapusv +GetPointerv +GenLists +Writeback +Viewport +LoadMatrixf +LoadMatrixd +MultMatrixf +MultMatrixd +LoadIdentity +Color3ub +Color3b +Color3us +Color3s +Color3ui +Color3i +Color3f +Color3d +Color4ub +Color4b +Color4us +Color4s +Color4ui +Color4i +Color4f +Color4d +Normal3b +Normal3d +Normal3f +Normal3i +Normal3s +TexCoord1d +TexCoord1f +TexCoord1i +TexCoord1s +TexCoord2d +TexCoord2f +TexCoord2i +TexCoord2s +TexCoord3d +TexCoord3f +TexCoord3i +TexCoord3s +TexCoord4d +TexCoord4f +TexCoord4i +TexCoord4s +Indexd +Indexf +Indexi +Indexs +Indexub +EdgeFlag +SelectBuffer +BarrierCreateCR +BarrierDestroyCR +SemaphoreCreateCR +SemaphoreDestroyCR +SemaphoreVCR +SemaphorePCR +BarrierExecCR +SecondaryColor3ubEXT +SecondaryColor3bEXT +SecondaryColor3usEXT +SecondaryColor3sEXT +SecondaryColor3uiEXT +SecondaryColor3iEXT +SecondaryColor3fEXT +SecondaryColor3dEXT +MakeCurrent +CreateContext +DestroyContext +WindowCreate +WindowDestroy +WindowSize +WindowPosition +WindowVisibleRegion +WindowShow +ReadPixels +GetChromiumParametervCR +ChromiumParametervCR +ChromiumParameteriCR +ChromiumParameterfCR +SwapBuffers +GenProgramsNV +GetProgramStringNV +GetVertexAttribPointervNV +GenFencesNV +MultiTexCoord1dARB +MultiTexCoord1fARB +MultiTexCoord1iARB +MultiTexCoord1sARB +MultiTexCoord2dARB +MultiTexCoord2fARB +MultiTexCoord2iARB +MultiTexCoord2sARB +MultiTexCoord3dARB +MultiTexCoord3fARB +MultiTexCoord3iARB +MultiTexCoord3sARB +MultiTexCoord4dARB +MultiTexCoord4fARB +MultiTexCoord4iARB +MultiTexCoord4sARB +FogCoordfEXT +FogCoorddEXT +NewList +CallList +CallLists +IsList +DeleteLists +BindTexture +DeleteTextures +IsTexture +AreTexturesResident +PrioritizeTextures +AreProgramsResidentNV +GenProgramsARB +IsProgramARB +GetProgramStringARB +GetVertexAttribPointervARB +VertexAttrib1dARB +VertexAttrib2dARB +VertexAttrib3dARB +VertexAttrib4dARB +VertexAttrib1fARB +VertexAttrib2fARB +VertexAttrib3fARB +VertexAttrib4fARB +VertexAttrib1sARB +VertexAttrib2sARB +VertexAttrib3sARB +VertexAttrib4sARB +VertexAttrib4NubARB +ProgramStringARB +ProgramLocalParameter4fARB +ProgramLocalParameter4dARB +BindProgramARB +DeleteProgramsARB +LoadProgramNV +BindProgramNV +ProgramParameter4fNV +ProgramParameter4dNV +GetCompressedTexImageARB +GenBuffersARB +DeleteBuffersARB +GetBufferSubDataARB +GetBufferPointervARB +MapBufferARB +UnmapBufferARB +GenQueriesARB +WindowPos2dARB +WindowPos2dvARB +WindowPos2fARB +WindowPos2fvARB +WindowPos2iARB +WindowPos2ivARB +WindowPos2sARB +WindowPos2svARB +WindowPos3dARB +WindowPos3dvARB +WindowPos3fARB +WindowPos3fvARB +WindowPos3iARB +WindowPos3ivARB +WindowPos3sARB +WindowPos3svARB +GetActiveAttrib +GetActiveUniform +GetAttachedShaders +GetShaderInfoLog +GetProgramInfoLog +GetShaderSource +GetUniformfv +GetUniformiv +GetAttachedObjectsARB +GetInfoLogARB +GenFramebuffersEXT +GenRenderbuffersEXT +FramebufferTexture1DEXT +FramebufferTexture2DEXT +FramebufferTexture3DEXT +CopyTexImage2D +BindFramebufferEXT +BindRenderbufferEXT +DeleteFramebuffersEXT +DeleteRenderbuffersEXT +FramebufferRenderbufferEXT +GetFramebufferAttachmentParameterivEXT +CreateShader +CreateProgram +ShaderSource +IsShader +IsProgram +CompileShader +DeleteShader +AttachShader +DetachShader +LinkProgram +UseProgram +DeleteProgram +ValidateProgram +BindAttribLocation +DeleteObjectARB +GetUniformsLocations +GetAttribsLocations +GetPolygonStipple +Flush +Finish +CompressedTexSubImage1DARB +CompressedTexSubImage2DARB +CompressedTexSubImage3DARB +TexSubImage1D +TexSubImage2D +TexSubImage3D +CompressedTexImage1DARB +CompressedTexImage2DARB +CompressedTexImage3DARB +TexImage1D +TexImage2D +TexImage3D +BindBufferARB +IsBufferARB +IsFramebufferEXT +IsRenderbufferEXT +GetAttribLocation +GetHandleARB +GetUniformLocation +CopyTexSubImage2D +GetObjectParameterivARB +GetObjectParameterfvARB +BlitFramebufferEXT +EndList +DrawBuffer +ReadBuffer +VBoxTexPresent +GetError +GetProgramiv +GetShaderiv +Begin +DrawArrays +DrawElements +End +TexEnvf +TexEnvfv +TexEnvi +TexEnviv +GetTexEnvfv +GetTexEnviv +DrawBuffers
\ No newline at end of file diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c new file mode 100644 index 00000000..9cdbe71f --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_stream.c @@ -0,0 +1,934 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "server.h" +#include "cr_unpack.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "server_dispatch.h" + + +/** + * Accept a new client connection, create a new CRClient and add to run queue. + */ +void +crServerAddNewClient(void) +{ + CRClient *newClient = (CRClient *) crCalloc(sizeof(CRClient)); + + if (newClient) { + newClient->spu_id = cr_server.client_spu_id; + newClient->conn = crNetAcceptClient( cr_server.protocol, NULL, + cr_server.tcpip_port, + cr_server.mtu, 1 ); + + newClient->currentCtxInfo = &cr_server.MainContextInfo; + + /* add to array */ + cr_server.clients[cr_server.numClients++] = newClient; + + crServerAddToRunQueue( newClient ); + } +} + + +/** + * Check if client is in the run queue. + */ +static GLboolean +FindClientInQueue(CRClient *client) +{ + RunQueue *q = cr_server.run_queue; + while (q) { + if (q->client == client) { + return 1; + } + q = q->next; + if (q == cr_server.run_queue) + return 0; /* back head */ + } + return 0; +} + + +#if 0 +static int +PrintQueue(void) +{ + RunQueue *q = cr_server.run_queue; + int count = 0; + crDebug("Queue entries:"); + while (q) { + count++; + crDebug("Entry: %p client: %p", q, q->client); + q = q->next; + if (q == cr_server.run_queue) + return count; + } + return count; +} +#endif + + +void crServerAddToRunQueue( CRClient *client ) +{ + RunQueue *q = (RunQueue *) crAlloc( sizeof( *q ) ); + +#ifdef VBOX_WITH_CRHGSMI + client->conn->pClient = client; + CRVBOXHGSMI_CMDDATA_CLEANUP(&client->conn->CmdData); +#endif + + /* give this client a unique number if needed */ + if (!client->number) { + client->number = client->conn->u32ClientID; + } + + crDebug("Adding client %p to the run queue", client); + + if (FindClientInQueue(client)) { + crError("CRServer: client %p already in the queue!", client); + } + + q->client = client; + q->blocked = 0; + + if (!cr_server.run_queue) + { + /* adding to empty queue */ + cr_server.run_queue = q; + q->next = q; + q->prev = q; + } + else + { + /* insert in doubly-linked list */ + q->next = cr_server.run_queue->next; + cr_server.run_queue->next->prev = q; + + q->prev = cr_server.run_queue; + cr_server.run_queue->next = q; + } +} + +static void crServerCleanupClient(CRClient *client) +{ + int32_t pos; + CRClient *oldclient = cr_server.curClient; + + cr_server.curClient = client; + + /* Destroy any windows created by the client */ + for (pos = 0; pos<CR_MAX_WINDOWS; pos++) + { + if (client->windowList[pos]) + { + cr_server.dispatch.WindowDestroy(client->windowList[pos]); + } + } + + /* Check if we have context(s) made by this client left, could happen if client side code is lazy */ + for (pos = 0; pos<CR_MAX_CONTEXTS; pos++) + { + if (client->contextList[pos]) + { + cr_server.dispatch.DestroyContext(client->contextList[pos]); + } + } + + cr_server.curClient = oldclient; +} + +static void crServerCleanupByPID(uint64_t pid) +{ + CRClientNode *pNode=cr_server.pCleanupClient, *pNext; + + while (pNode) + { + if (pNode->pClient->pid==pid) + { + crServerCleanupClient(pNode->pClient); + crFree(pNode->pClient); + if (pNode->prev) + { + pNode->prev->next=pNode->next; + } + else + { + cr_server.pCleanupClient=pNode->next; + } + if (pNode->next) + { + pNode->next->prev = pNode->prev; + } + + pNext=pNode->next; + crFree(pNode); + pNode=pNext; + } + else + { + pNode=pNode->next; + } + } +} + +void +crServerDeleteClient( CRClient *client ) +{ + int i, j; + int cleanup=1; + + crDebug("Deleting client %p (%d msgs left)", client, crNetNumMessages(client->conn)); + +#if 0 + if (crNetNumMessages(client->conn) > 0) { + crDebug("Delay destroying client: message still pending"); + return; + } +#endif + + if (!FindClientInQueue(client)) { + /* this should never happen */ + crError("CRServer: client %p not found in the queue!", client); + } + + /* remove from clients[] array */ + for (i = 0; i < cr_server.numClients; i++) { + if (cr_server.clients[i] == client) { + /* found it */ + for (j = i; j < cr_server.numClients - 1; j++) + cr_server.clients[j] = cr_server.clients[j + 1]; + cr_server.numClients--; + break; + } + } + + /* check if there're any other guest threads in same process */ + for (i=0; i < cr_server.numClients; i++) + { + if (cr_server.clients[i]->pid==client->pid) + { + cleanup=0; + break; + } + } + + if (cleanup) + { + crServerCleanupClient(client); + } + + /* remove from the run queue */ + if (cr_server.run_queue) + { + RunQueue *q = cr_server.run_queue; + RunQueue *qStart = cr_server.run_queue; + do { + if (q->client == client) + { + /* this test seems a bit excessive */ + if ((q->next == q->prev) && (q->next == q) && (cr_server.run_queue == q)) + { + /* We're removing/deleting the only client */ + CRASSERT(cr_server.numClients == 0); + crFree(q); + cr_server.run_queue = NULL; + cr_server.curClient = NULL; + crDebug("Last client deleted - empty run queue."); + } + else + { + /* remove from doubly linked list and free the node */ + if (cr_server.curClient == q->client) + cr_server.curClient = NULL; + if (cr_server.run_queue == q) + cr_server.run_queue = q->next; + q->prev->next = q->next; + q->next->prev = q->prev; + crFree(q); + } + break; + } + q = q->next; + } while (q != qStart); + } + + crNetFreeConnection(client->conn); + client->conn = NULL; + + if (cleanup) + { + crServerCleanupByPID(client->pid); + crFree(client); + } + else + { + CRClientNode *pNode = (CRClientNode *)crAlloc(sizeof(CRClientNode)); + if (!pNode) + { + crWarning("Not enough memory, forcing client cleanup"); + crServerCleanupClient(client); + crServerCleanupByPID(client->pid); + crFree(client); + return; + } + pNode->pClient = client; + pNode->prev = NULL; + pNode->next = cr_server.pCleanupClient; + cr_server.pCleanupClient = pNode; + } + + if (!cr_server.numClients) + { + /* if no clients, the guest driver may be unloaded, + * and thus the visible regions situation might not be under control anymore, + * so cleanup the 3D framebuffer data here + * @todo: what really should happen is that guest driver on unload + * posts some request to host that would copy the current framebuffer 3D data to the 2D buffer + * (i.e. to the memory used by the standard IFramebuffer API) */ + HCR_FRAMEBUFFER hFb; + for (hFb = CrPMgrFbGetFirstEnabled(); hFb; hFb = CrPMgrFbGetNextEnabled(hFb)) + { + int rc = CrFbUpdateBegin(hFb); + if (RT_SUCCESS(rc)) + { + CrFbRegionsClear(hFb); + CrFbUpdateEnd(hFb); + } + else + WARN(("CrFbUpdateBegin failed %d", rc)); + } + } +} + +/** + * Test if the given client is in the middle of a glBegin/End or + * glNewList/EndList pair. + * This is used to test if we can advance to the next client. + * \return GL_TRUE if so, GL_FALSE otherwise. + */ +GLboolean +crServerClientInBeginEnd(const CRClient *client) +{ + if (client->currentCtxInfo + && client->currentCtxInfo->pContext + && (client->currentCtxInfo->pContext->lists.currentIndex != 0 || + client->currentCtxInfo->pContext->current.inBeginEnd || + client->currentCtxInfo->pContext->occlusion.currentQueryObject)) { + return GL_TRUE; + } + else { + return GL_FALSE; + } +} + + +/** + * Find the next client in the run queue that's not blocked and has a + * waiting message. + * Check if all clients are blocked (on barriers, semaphores), if so we've + * deadlocked! + * If no clients have a waiting message, call crNetRecv to get something + * if 'block' is true, else return NULL if 'block' if false. + */ +static RunQueue * +getNextClient(GLboolean block) +{ + while (1) + { + if (cr_server.run_queue) + { + GLboolean all_blocked = GL_TRUE; + GLboolean done_something = GL_FALSE; + RunQueue *start = cr_server.run_queue; + + /* check if this client's connection has gone away */ + if (!cr_server.run_queue->client->conn + || (cr_server.run_queue->client->conn->type == CR_NO_CONNECTION + && crNetNumMessages(cr_server.run_queue->client->conn) == 0)) + { + crServerDeleteClient( cr_server.run_queue->client ); + start = cr_server.run_queue; + } + + if (cr_server.run_queue == NULL) { + /* empty queue */ + return NULL; + } + + if (crServerClientInBeginEnd(cr_server.run_queue->client)) { + /* We _must_ service this client and no other. + * If we've got a message waiting on this client's connection we'll + * service it. Else, return NULL. + */ + if (crNetNumMessages(cr_server.run_queue->client->conn) > 0) + return cr_server.run_queue; + else + return NULL; + } + + /* loop over entries in run queue, looking for next one that's ready */ + while (!done_something || cr_server.run_queue != start) + { + done_something = GL_TRUE; + if (!cr_server.run_queue->blocked) + { + all_blocked = GL_FALSE; + } + if (!cr_server.run_queue->blocked + && cr_server.run_queue->client->conn + && crNetNumMessages(cr_server.run_queue->client->conn) > 0) + { + /* OK, this client isn't blocked and has a queued message */ + return cr_server.run_queue; + } + cr_server.run_queue = cr_server.run_queue->next; + } + + if (all_blocked) + { + /* XXX crError is fatal? Should this be an info/warning msg? */ + crError( "crserver: DEADLOCK! (numClients=%d, all blocked)", + cr_server.numClients ); + if (cr_server.numClients < (int) cr_server.maxBarrierCount) { + crError("Waiting for more clients!!!"); + while (cr_server.numClients < (int) cr_server.maxBarrierCount) { + crNetRecv(); + } + } + } + } + + if (!block) + return NULL; + + /* no one had any work, get some! */ + crNetRecv(); + + } /* while */ + + /* UNREACHED */ + /* return NULL; */ +} + +typedef struct CR_SERVER_PENDING_MSG +{ + RTLISTNODE Node; + uint32_t cbMsg; + CRMessage Msg; +} CR_SERVER_PENDING_MSG; + +static int crServerPendMsg(CRConnection *conn, const CRMessage *msg, int cbMsg) +{ + CR_SERVER_PENDING_MSG *pMsg; + + if (!cbMsg) + { + WARN(("cbMsg is null!")); + return VERR_INVALID_PARAMETER; + } + + pMsg = (CR_SERVER_PENDING_MSG*)RTMemAlloc(cbMsg + RT_UOFFSETOF(CR_SERVER_PENDING_MSG, Msg)); + if (!pMsg) + { + WARN(("RTMemAlloc failed")); + return VERR_NO_MEMORY; + } + + pMsg->cbMsg = cbMsg; + + memcpy(&pMsg->Msg, msg, cbMsg); + + RTListAppend(&conn->PendingMsgList, &pMsg->Node); + + return VINF_SUCCESS; +} + +int crServerPendSaveState(PSSMHANDLE pSSM) +{ + int i, rc; + + for (i = 0; i < cr_server.numClients; i++) + { + CR_SERVER_PENDING_MSG *pIter; + CRClient *pClient = cr_server.clients[i]; + CRConnection *pConn; + if (!pClient || !pClient->conn) + { + WARN(("invalid client state")); + continue; + } + + pConn = pClient->conn; + + if (RTListIsEmpty(&pConn->PendingMsgList)) + continue; + + CRASSERT(pConn->u32ClientID); + + rc = SSMR3PutU32(pSSM, pConn->u32ClientID); + AssertRCReturn(rc, rc); + + RTListForEach(&pConn->PendingMsgList, pIter, CR_SERVER_PENDING_MSG, Node) + { + CRASSERT(pIter->cbMsg); + + rc = SSMR3PutU32(pSSM, pIter->cbMsg); + AssertRCReturn(rc, rc); + + rc = SSMR3PutMem(pSSM, &pIter->Msg, pIter->cbMsg); + AssertRCReturn(rc, rc); + } + + rc = SSMR3PutU32(pSSM, 0); + AssertRCReturn(rc, rc); + } + + rc = SSMR3PutU32(pSSM, 0); + AssertRCReturn(rc, rc); + + return VINF_SUCCESS; +} + +int crServerPendLoadState(PSSMHANDLE pSSM, uint32_t u32Version) +{ + int rc; + uint32_t u32; + CRClient *pClient; + + if (u32Version < SHCROGL_SSM_VERSION_WITH_PEND_CMD_INFO) + return VINF_SUCCESS; + + rc = SSMR3GetU32(pSSM, &u32); + AssertRCReturn(rc, rc); + + if (!u32) + return VINF_SUCCESS; + + do { + rc = crVBoxServerClientGet(u32, &pClient); + AssertRCReturn(rc, rc); + + for(;;) + { + CR_SERVER_PENDING_MSG *pMsg; + + rc = SSMR3GetU32(pSSM, &u32); + AssertRCReturn(rc, rc); + + if (!u32) + break; + + pMsg = (CR_SERVER_PENDING_MSG*)RTMemAlloc(u32 + RT_UOFFSETOF(CR_SERVER_PENDING_MSG, Msg)); + if (!pMsg) + { + WARN(("RTMemAlloc failed")); + return VERR_NO_MEMORY; + } + + pMsg->cbMsg = u32; + rc = SSMR3GetMem(pSSM, &pMsg->Msg, u32); + AssertRCReturn(rc, rc); + + RTListAppend(&pClient->conn->PendingMsgList, &pMsg->Node); + } + + rc = SSMR3GetU32(pSSM, &u32); + AssertRCReturn(rc, rc); + } while (u32); + + rc = SSMR3GetU32(pSSM, &u32); + AssertRCReturn(rc, rc); + + return VINF_SUCCESS; +} + +static void crServerPendProcess(CRConnection *conn) +{ + CR_SERVER_PENDING_MSG *pIter, *pNext; + + cr_server.fProcessingPendedCommands = GL_TRUE; + + RTListForEachSafe(&conn->PendingMsgList, pIter, pNext, CR_SERVER_PENDING_MSG, Node) + { + CRMessage *msg = &pIter->Msg; + const CRMessageOpcodes *msg_opcodes; + int opcodeBytes; + const char *data_ptr, *data_ptr_end; + + RTListNodeRemove(&pIter->Node); + + CRASSERT(msg->header.type == CR_MESSAGE_OPCODES); + + msg_opcodes = (const CRMessageOpcodes *) msg; + opcodeBytes = (msg_opcodes->numOpcodes + 3) & ~0x03; + + data_ptr = (const char *) msg_opcodes + sizeof (CRMessageOpcodes) + opcodeBytes; + data_ptr_end = (const char *)msg_opcodes + pIter->cbMsg; + + crUnpack(data_ptr, /* first command's operands */ + data_ptr_end, /* first byte after command's operands*/ + data_ptr - 1, /* first command's opcode */ + msg_opcodes->numOpcodes, /* how many opcodes */ + &(cr_server.dispatch)); /* the CR dispatch table */ + + RTMemFree(pIter); + } + + cr_server.fProcessingPendedCommands = GL_FALSE; +} + +/** + * This function takes the given message (which should be a buffer of + * rendering commands) and executes it. + */ +static void +crServerDispatchMessage(CRConnection *conn, CRMessage *msg, int cbMsg) +{ + const CRMessageOpcodes *msg_opcodes; + int opcodeBytes; + const char *data_ptr, *data_ptr_end; +#ifdef VBOX_WITH_CRHGSMI + PCRVBOXHGSMI_CMDDATA pCmdData = NULL; +#endif + CR_UNPACK_BUFFER_TYPE enmType; + bool fUnpack = true; + + if (msg->header.type == CR_MESSAGE_REDIR_PTR) + { +#ifdef VBOX_WITH_CRHGSMI + pCmdData = &msg->redirptr.CmdData; +#endif + msg = (CRMessage *) msg->redirptr.pMessage; + } + + CRASSERT(msg->header.type == CR_MESSAGE_OPCODES); + + msg_opcodes = (const CRMessageOpcodes *) msg; + opcodeBytes = (msg_opcodes->numOpcodes + 3) & ~0x03; + +#ifdef VBOXCR_LOGFPS + CRASSERT(cr_server.curClient && cr_server.curClient->conn && cr_server.curClient->conn->id == msg->header.conn_id); + cr_server.curClient->conn->opcodes_count += msg_opcodes->numOpcodes; +#endif + + data_ptr = (const char *) msg_opcodes + sizeof(CRMessageOpcodes) + opcodeBytes; + data_ptr_end = (const char *)msg_opcodes + cbMsg; // Pointer to the first byte after message data + + enmType = crUnpackGetBufferType(data_ptr - 1, /* first command's opcode */ + msg_opcodes->numOpcodes /* how many opcodes */); + switch (enmType) + { + case CR_UNPACK_BUFFER_TYPE_GENERIC: + { + if (RTListIsEmpty(&conn->PendingMsgList)) + break; + + if (RT_SUCCESS(crServerPendMsg(conn, msg, cbMsg))) + { + fUnpack = false; + break; + } + + WARN(("crServerPendMsg failed")); + crServerPendProcess(conn); + break; + } + case CR_UNPACK_BUFFER_TYPE_CMDBLOCK_BEGIN: + { + if (RTListIsEmpty(&conn->PendingMsgList)) + { + if (RT_SUCCESS(crServerPendMsg(conn, msg, cbMsg))) + { + Assert(!RTListIsEmpty(&conn->PendingMsgList)); + fUnpack = false; + break; + } + else + WARN(("crServerPendMsg failed")); + } + else + WARN(("Pend List is NOT empty, drain the current list, and ignore this command")); + + crServerPendProcess(conn); + break; + } + case CR_UNPACK_BUFFER_TYPE_CMDBLOCK_FLUSH: /* just flush for now */ + { + CrPMgrClearRegionsGlobal(); /* clear regions to ensure we don't do MakeCurrent and friends */ + crServerPendProcess(conn); + Assert(RTListIsEmpty(&conn->PendingMsgList)); + break; + } + case CR_UNPACK_BUFFER_TYPE_CMDBLOCK_END: + { +// CRASSERT(!RTListIsEmpty(&conn->PendingMsgList)); + crServerPendProcess(conn); + Assert(RTListIsEmpty(&conn->PendingMsgList)); + break; + } + default: + WARN(("unsupported buffer type")); + break; + } + + if (fUnpack) + { + crUnpack(data_ptr, /* first command's operands */ + data_ptr_end, /* first byte after command's operands*/ + data_ptr - 1, /* first command's opcode */ + msg_opcodes->numOpcodes, /* how many opcodes */ + &(cr_server.dispatch)); /* the CR dispatch table */ + } + +#ifdef VBOX_WITH_CRHGSMI + if (pCmdData) + { + int rc = VINF_SUCCESS; + CRVBOXHGSMI_CMDDATA_ASSERT_CONSISTENT(pCmdData); + if (CRVBOXHGSMI_CMDDATA_IS_SETWB(pCmdData)) + { + uint32_t cbWriteback = pCmdData->cbWriteback; + rc = crVBoxServerInternalClientRead(conn->pClient, (uint8_t*)pCmdData->pWriteback, &cbWriteback); + Assert(rc == VINF_SUCCESS || rc == VERR_BUFFER_OVERFLOW); + *pCmdData->pcbWriteback = cbWriteback; + } + VBOXCRHGSMI_CMD_CHECK_COMPLETE(pCmdData, rc); + } +#endif +} + + +typedef enum +{ + CLIENT_GONE = 1, /* the client has disconnected */ + CLIENT_NEXT = 2, /* we can advance to next client */ + CLIENT_MORE = 3 /* we need to keep servicing current client */ +} ClientStatus; + + +/** + * Process incoming/pending message for the given client (queue entry). + * \return CLIENT_GONE if this client has gone away/exited, + * CLIENT_NEXT if we can advance to the next client + * CLIENT_MORE if we have to process more messages for this client. + */ +static ClientStatus +crServerServiceClient(const RunQueue *qEntry) +{ + CRMessage *msg; + CRConnection *conn; + + /* set current client pointer */ + cr_server.curClient = qEntry->client; + + conn = cr_server.run_queue->client->conn; + + /* service current client as long as we can */ + while (conn && conn->type != CR_NO_CONNECTION && + crNetNumMessages(conn) > 0) { + unsigned int len; + + /* + crDebug("%d messages on %p", + crNetNumMessages(conn), (void *) conn); + */ + + /* Don't use GetMessage, because we want to do our own crNetRecv() calls + * here ourself. + * Note that crNetPeekMessage() DOES remove the message from the queue + * if there is one. + */ + len = crNetPeekMessage( conn, &msg ); + CRASSERT(len > 0); + if (msg->header.type != CR_MESSAGE_OPCODES + && msg->header.type != CR_MESSAGE_REDIR_PTR) { + crError( "SPU %d sent me CRAP (type=0x%x)", + cr_server.curClient->spu_id, msg->header.type ); + } + + /* Do the context switch here. No sense in switching before we + * really have any work to process. This is a no-op if we're + * not really switching contexts. + * + * XXX This isn't entirely sound. The crStateMakeCurrent() call + * will compute the state difference and dispatch it using + * the head SPU's dispatch table. + * + * This is a problem if this is the first buffer coming in, + * and the head SPU hasn't had a chance to do a MakeCurrent() + * yet (likely because the MakeCurrent() command is in the + * buffer itself). + * + * At best, in this case, the functions are no-ops, and + * are essentially ignored by the SPU. In the typical + * case, things aren't too bad; if the SPU just calls + * crState*() functions to update local state, everything + * will work just fine. + * + * In the worst (but unusual) case where a nontrivial + * SPU is at the head of a crserver's SPU chain (say, + * in a multiple-tiered "tilesort" arrangement, as + * seen in the "multitilesort.conf" configuration), the + * SPU may rely on state set during the MakeCurrent() that + * may not be present yet, because no MakeCurrent() has + * yet been dispatched. + * + * This headache will have to be revisited in the future; + * for now, SPUs that could head a crserver's SPU chain + * will have to detect the case that their functions are + * being called outside of a MakeCurrent(), and will have + * to handle the situation gracefully. (This is currently + * the case with the "tilesort" SPU.) + */ + +#if 0 + crStateMakeCurrent( cr_server.curClient->currentCtx ); +#else + /* Check if the current window is the one that the client wants to + * draw into. If not, dispatch a MakeCurrent to activate the proper + * window. + */ + if (cr_server.curClient) { + int clientWindow = cr_server.curClient->currentWindow; + int clientContext = cr_server.curClient->currentContextNumber; + CRContextInfo *clientCtxInfo = cr_server.curClient->currentCtxInfo; + if (clientCtxInfo != cr_server.currentCtxInfo + || clientWindow != cr_server.currentWindow + || cr_server.bForceMakeCurrentOnClientSwitch) { + crServerDispatchMakeCurrent(clientWindow, 0, clientContext); + /* + CRASSERT(cr_server.currentWindow == clientWindow); + */ + } + } +#endif + + /* Force scissor, viewport and projection matrix update in + * crServerSetOutputBounds(). + */ + cr_server.currentSerialNo = 0; + + /* Commands get dispatched here */ + crServerDispatchMessage( conn, msg, len ); + + crNetFree( conn, msg ); + + if (qEntry->blocked) { + /* Note/assert: we should not be inside a glBegin/End or glNewList/ + * glEndList pair at this time! + */ + CRASSERT(0); + return CLIENT_NEXT; + } + + } /* while */ + + /* + * Check if client/connection is gone + */ + if (!conn || conn->type == CR_NO_CONNECTION) { + crDebug("Delete client %p at %d", cr_server.run_queue->client, __LINE__); + crServerDeleteClient( cr_server.run_queue->client ); + return CLIENT_GONE; + } + + /* + * Determine if we can advance to next client. + * If we're currently inside a glBegin/End primitive or building a display + * list we can't service another client until we're done with the + * primitive/list. + */ + if (crServerClientInBeginEnd(cr_server.curClient)) { + /* The next message has to come from the current client's connection. */ + CRASSERT(!qEntry->blocked); + return CLIENT_MORE; + } + else { + /* get next client */ + return CLIENT_NEXT; + } +} + + + +/** + * Check if any of the clients need servicing. + * If so, service one client and return. + * Else, just return. + */ +void +crServerServiceClients(void) +{ + RunQueue *q; + + q = getNextClient(GL_FALSE); /* don't block */ + while (q) + { + ClientStatus stat = crServerServiceClient(q); + if (stat == CLIENT_NEXT && cr_server.run_queue->next) { + /* advance to next client */ + cr_server.run_queue = cr_server.run_queue->next; + } + q = getNextClient(GL_FALSE); + } +} + + + + +/** + * Main crserver loop. Service connections from all connected clients. + * XXX add a config option to specify whether the crserver + * should exit when there's no more clients. + */ +void +crServerSerializeRemoteStreams(void) +{ + /*MSG msg;*/ + + while (cr_server.run_queue) + { + crServerServiceClients(); + /*if (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) + { + if (msg.message == WM_QUIT) + { + PostQuitMessage((int)msg.wParam); + break; + } + TranslateMessage( &msg ); + DispatchMessage( &msg ); + }*/ + } +} + + +/** + * This will be called by the network layer when it's received a new message. + */ +int +crServerRecv( CRConnection *conn, CRMessage *msg, unsigned int len ) +{ + CRMessage *pRealMsg; + (void) len; + + pRealMsg = (msg->header.type!=CR_MESSAGE_REDIR_PTR) ? msg : (CRMessage*) msg->redirptr.pMessage; + + switch( pRealMsg->header.type ) + { + /* Called when using multiple threads */ + case CR_MESSAGE_NEWCLIENT: + crServerAddNewClient(); + return 1; /* msg handled */ + default: + /*crWarning( "Why is the crserver getting a message of type 0x%x?", + msg->header.type ); */ + ; + } + return 0; /* not handled */ +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c new file mode 100644 index 00000000..7e779cf8 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_texture.c @@ -0,0 +1,323 @@ +/* $Id: server_texture.c $ */ +/** @file + * VBox crOpenGL - teximage functions. + */ + +/* + * Copyright (C) 2010-2019 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. + */ + +#include "chromium.h" +#include "cr_error.h" +#include "server_dispatch.h" +#include "server.h" +#include "cr_mem.h" + +#define CR_NOTHING() + +#define CR_CHECKPTR(name) \ + if (!realptr) \ + { \ + crWarning(#name " with NULL ptr, ignored!"); \ + return; \ + } + +#if !defined(CR_STATE_NO_TEXTURE_IMAGE_STORE) +# define CR_FIXPTR() (uintptr_t) realptr += (uintptr_t) cr_server.head_spu->dispatch_table.MapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_ONLY_ARB) +#else +# define CR_FIXPTR() +#endif + +#if defined(CR_ARB_pixel_buffer_object) +# define CR_CHECKBUFFER(name, checkptr) \ + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) \ + { \ + CR_FIXPTR(); \ + } \ + else \ + { \ + checkptr \ + } +#else +# define CR_CHECKBUFFER(name, checkptr) checkptr +#endif + +#if defined(CR_ARB_pixel_buffer_object) && !defined(CR_STATE_NO_TEXTURE_IMAGE_STORE) +# define CR_FINISHBUFFER() \ + if (crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) \ + { \ + if (!cr_server.head_spu->dispatch_table.UnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB)) \ + { \ + crWarning("UnmapBufferARB failed"); \ + } \ + } +#else +#define CR_FINISHBUFFER() +#endif + +#define CR_FUNC_SUBIMAGE(name, def, call, ptrname) \ +void SERVER_DISPATCH_APIENTRY \ +crServerDispatch##name def \ +{ \ + const GLvoid *realptr = ptrname; \ + CR_CHECKBUFFER(name, CR_CHECKPTR(name)) \ + crState##name call; \ + CR_FINISHBUFFER() \ + realptr = ptrname; \ + cr_server.head_spu->dispatch_table.name call; \ +} + +#define CR_FUNC_IMAGE(name, def, call, ptrname) \ +void SERVER_DISPATCH_APIENTRY \ +crServerDispatch##name def \ +{ \ + const GLvoid *realptr = ptrname; \ + CR_CHECKBUFFER(name, CR_NOTHING()) \ + crState##name call; \ + CR_FINISHBUFFER() \ + realptr = ptrname; \ + cr_server.head_spu->dispatch_table.name call; \ +} + +#if defined(CR_ARB_texture_compression) +CR_FUNC_SUBIMAGE(CompressedTexSubImage1DARB, + (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imagesize, const GLvoid * data), + (target, level, xoffset, width, format, imagesize, realptr), data) + +CR_FUNC_SUBIMAGE(CompressedTexSubImage2DARB, + (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imagesize, const GLvoid * data), + (target, level, xoffset, yoffset, width, height, format, imagesize, realptr), data) + +CR_FUNC_SUBIMAGE(CompressedTexSubImage3DARB, + (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imagesize, const GLvoid * data), + (target, level, xoffset, yoffset, zoffset, width, height, depth, format, imagesize, realptr), data) + +CR_FUNC_IMAGE(CompressedTexImage1DARB, + (GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLsizei imagesize, const GLvoid * data), + (target, level, internalFormat, width, border, imagesize, realptr), data) + +CR_FUNC_IMAGE(CompressedTexImage2DARB, + (GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imagesize, const GLvoid * data), + (target, level, internalFormat, width, height, border, imagesize, realptr), data) + +CR_FUNC_IMAGE(CompressedTexImage3DARB, + (GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imagesize, const GLvoid * data), + (target, level, internalFormat, width, height, depth, border, imagesize, realptr), data) +#endif + +CR_FUNC_SUBIMAGE(TexSubImage1D, + (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid * pixels), + (target, level, xoffset, width, format, type, realptr), pixels) + +CR_FUNC_SUBIMAGE(TexSubImage2D, + (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * pixels), + (target, level, xoffset, yoffset, width, height, format, type, realptr), pixels) + +CR_FUNC_SUBIMAGE(TexSubImage3D, + (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid * pixels), + (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, realptr), pixels) + +CR_FUNC_IMAGE(TexImage1D, + (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid * pixels), + (target, level, internalFormat, width, border, format, type, realptr), pixels) + +CR_FUNC_IMAGE(TexImage2D, + (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid * pixels), + (target, level, internalFormat, width, height, border, format, type, realptr), pixels) + +CR_FUNC_IMAGE(TexImage3D, + (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid * pixels), + (target, level, internalFormat, width, height, depth, border, format, type, realptr), pixels) + + +void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnvf( GLenum target, GLenum pname, GLfloat param ) +{ + crStateTexEnvf( target, pname, param ); + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnvf( target, pname, param );); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnvfv( GLenum target, GLenum pname, const GLfloat * params ) +{ + crStateTexEnvfv( target, pname, params ); + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnvfv( target, pname, params );); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnvi( GLenum target, GLenum pname, GLint param ) +{ + crStateTexEnvi( target, pname, param ); + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnvi( target, pname, param );); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchTexEnviv( GLenum target, GLenum pname, const GLint * params ) +{ + crStateTexEnviv( target, pname, params ); + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + CR_GLERR_CHECK(cr_server.head_spu->dispatch_table.TexEnviv( target, pname, params );); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetTexEnvfv( GLenum target, GLenum pname, GLfloat * params ) +{ + GLfloat local_params[4] = {0}; + (void) params; + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + cr_server.head_spu->dispatch_table.GetTexEnvfv( target, pname, local_params ); + else + crStateGetTexEnvfv( target, pname, local_params ); + + crServerReturnValue( &(local_params[0]), crStateHlpComponentsCount(pname)*sizeof (GLfloat) ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchGetTexEnviv( GLenum target, GLenum pname, GLint * params ) +{ + GLint local_params[4] = {0}; + (void) params; + if (GL_POINT_SPRITE != target && pname != GL_COORD_REPLACE) + cr_server.head_spu->dispatch_table.GetTexEnviv( target, pname, local_params ); + else + crStateGetTexEnviv( target, pname, local_params ); + + crServerReturnValue( &(local_params[0]), crStateHlpComponentsCount(pname)*sizeof (GLint) ); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchBindTexture( GLenum target, GLuint texture ) +{ + crStateBindTexture( target, texture ); + cr_server.head_spu->dispatch_table.BindTexture(target, crStateGetTextureHWID(texture)); +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchDeleteTextures( GLsizei n, const GLuint *textures) +{ + GLuint *newTextures; + GLint i; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchDeleteTextures: parameter 'n' is out of range"); + return; + } + + newTextures = (GLuint *)crAlloc(n * sizeof(GLuint)); + + if (!newTextures) + { + crError("crServerDispatchDeleteTextures: out of memory"); + return; + } + + for (i = 0; i < n; i++) + { + newTextures[i] = crStateGetTextureHWID(textures[i]); + } + +// for (i = 0; i < n; ++i) +// { +// crDebug("DeleteTexture: %d, pid %d, ctx %d", textures[i], (uint32_t)cr_server.curClient->pid, cr_server.currentCtxInfo->pContext->id); +// } + + + crStateDeleteTextures(n, textures); + cr_server.head_spu->dispatch_table.DeleteTextures(n, newTextures); + crFree(newTextures); +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchPrioritizeTextures( GLsizei n, const GLuint * textures, const GLclampf * priorities ) +{ + GLuint *newTextures; + GLint i; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchPrioritizeTextures: parameter 'n' is out of range"); + return; + } + + newTextures = (GLuint *)crAlloc(n * sizeof(GLuint)); + + if (!newTextures) + { + crError("crServerDispatchPrioritizeTextures: out of memory"); + return; + } + + crStatePrioritizeTextures(n, textures, priorities); + + for (i = 0; i < n; i++) + { + newTextures[i] = crStateGetTextureHWID(textures[i]); + } + + cr_server.head_spu->dispatch_table.PrioritizeTextures(n, newTextures, priorities); + crFree(newTextures); +} + + +/** @todo will fail for textures loaded from snapshot */ +GLboolean SERVER_DISPATCH_APIENTRY crServerDispatchIsTexture( GLuint texture ) +{ + GLboolean retval; + retval = cr_server.head_spu->dispatch_table.IsTexture(crStateGetTextureHWID(texture)); + crServerReturnValue( &retval, sizeof(retval) ); + return retval; /* WILL PROBABLY BE IGNORED */ +} + + +GLboolean SERVER_DISPATCH_APIENTRY +crServerDispatchAreTexturesResident(GLsizei n, const GLuint *textures, + GLboolean *residences) +{ + GLboolean retval = GL_FALSE; + GLsizei i; + GLboolean *res; + GLuint *textures2; + (void) residences; + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crServerDispatchAreTexturesResident: parameter 'n' is out of range"); + return GL_FALSE; + } + + res = (GLboolean *)crCalloc(n * sizeof(GLboolean)); + if (!res) + { + crError("crServerDispatchAreTexturesResident: out of memory"); + return GL_FALSE; + } + + textures2 = (GLuint *)crAlloc(n * sizeof(GLuint)); + + if (!textures2) + { + crError("crServerDispatchAreTexturesResident: out of memory"); + crFree(res); + return GL_FALSE; + } + + for (i = 0; i < n; i++) + { + textures2[i] = crStateGetTextureHWID(textures[i]); + } + retval = cr_server.head_spu->dispatch_table.AreTexturesResident(n, textures2, res); + + crFree(textures2); + + crServerReturnValue(res, n * sizeof(GLboolean)); + + crFree(res); + + return retval; /* WILL PROBABLY BE IGNORED */ +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_viewport.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_viewport.c new file mode 100644 index 00000000..c583faee --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_viewport.c @@ -0,0 +1,288 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "server_dispatch.h" +#include "server.h" +#include "cr_error.h" +#include "state/cr_statetypes.h" + + +static const CRmatrix identity_matrix = { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 +}; + + +/* + * Clip the rectangle q against the given image window. + */ +static void +crServerViewportClipToWindow( const CRrecti *imagewindow, CRrecti *q) +{ + if (q->x1 < imagewindow->x1) q->x1 = imagewindow->x1; + if (q->x1 > imagewindow->x2) q->x1 = imagewindow->x2; + + if (q->x2 > imagewindow->x2) q->x2 = imagewindow->x2; + if (q->x2 < imagewindow->x1) q->x2 = imagewindow->x1; + + if (q->y1 < imagewindow->y1) q->y1 = imagewindow->y1; + if (q->y1 > imagewindow->y2) q->y1 = imagewindow->y2; + + if (q->y2 > imagewindow->y2) q->y2 = imagewindow->y2; + if (q->y2 < imagewindow->y1) q->y2 = imagewindow->y1; +} + + +/* + * Translate the rectangle q from the image window space to the outputwindow + * space. + */ +static void +crServerConvertToOutput( const CRrecti *imagewindow, + const CRrecti *outputwindow, + CRrecti *q ) +{ + q->x1 = q->x1 - imagewindow->x1 + outputwindow->x1; + q->x2 = q->x2 - imagewindow->x2 + outputwindow->x2; + q->y1 = q->y1 - imagewindow->y1 + outputwindow->y1; + q->y2 = q->y2 - imagewindow->y2 + outputwindow->y2; +} + + +/* + * Compute clipped image window, scissor, viewport and base projection + * info for each tile in the mural. + * Need to call this when either the viewport or mural is changed. + */ +void +crServerComputeViewportBounds(const CRViewportState *v, CRMuralInfo *mural) +{ +#if 0 + static GLuint serialNo = 1; + int i; + + for (i = 0; i < mural->numExtents; i++) { + CRExtent *extent = &mural->extents[i]; + CRrecti q; + + /* If the scissor is disabled set it to the whole output. + ** We might as well use the actual scissorTest rather than + ** scissorValid - it never gets reset anyway. + */ + if (!v->scissorTest) + { + extent->scissorBox = extent->outputwindow; + } + else + { + q.x1 = v->scissorX; + q.x2 = v->scissorX + v->scissorW; + q.y1 = v->scissorY; + q.y2 = v->scissorY + v->scissorH; + + crServerViewportClipToWindow(&(extent->imagewindow), &q); + crServerConvertToOutput(&(extent->imagewindow), + &(extent->outputwindow), &q); + extent->scissorBox = q; + } + + /* if the viewport is not valid, + ** set it to the entire output. + */ + if (!v->viewportValid) + { + extent->clippedImagewindow = extent->imagewindow; + extent->viewport = extent->outputwindow; + } + else + { + q.x1 = v->viewportX; + q.x2 = v->viewportX + v->viewportW; + q.y1 = v->viewportY; + q.y2 = v->viewportY + v->viewportH; + + /* This is where the viewport gets clamped to the max size. */ + crServerViewportClipToWindow(&(extent->imagewindow), &q); + + extent->clippedImagewindow = q; + + crServerConvertToOutput(&(extent->imagewindow), + &(extent->outputwindow), &q); + + extent->viewport = q; + } + + /* + ** Now, compute the base projection. + */ + if (extent->clippedImagewindow.x1 == extent->clippedImagewindow.x2 || + extent->clippedImagewindow.y1 == extent->clippedImagewindow.y2) { + /* zero-area extent, use identity matrix (doesn't really matter) */ + extent->baseProjection = identity_matrix; + } + else + { + const int vpx = v->viewportX; + const int vpy = v->viewportY; + const int vpw = v->viewportW; + const int vph = v->viewportH; + GLfloat xscale, yscale; + GLfloat xtrans, ytrans; + CRrectf p; + + /* + * We need to take account of the current viewport parameters, + * and they are passed to this function as x, y, w, h. + * In the default case (from main.c) we pass the the + * full muralsize of 0, 0, width, height + */ + p.x1 = (GLfloat) (extent->clippedImagewindow.x1 - vpx) / vpw; + p.y1 = (GLfloat) (extent->clippedImagewindow.y1 - vpy) / vph; + p.x2 = (GLfloat) (extent->clippedImagewindow.x2 - vpx) / vpw; + p.y2 = (GLfloat) (extent->clippedImagewindow.y2 - vpy) / vph; + + /* XXX not sure this clamping is really need anymore + */ + if (p.x1 < 0.0) { + p.x1 = 0.0; + if (p.x2 > 1.0) p.x2 = 1.0; + } + + if (p.y1 < 0.0) { + p.y1 = 0.0; + if (p.y2 > 1.0) p.y2 = 1.0; + } + + /* Rescale [0,1] -> [-1,1] */ + p.x1 = p.x1 * 2.0f - 1.0f; + p.x2 = p.x2 * 2.0f - 1.0f; + p.y1 = p.y1 * 2.0f - 1.0f; + p.y2 = p.y2 * 2.0f - 1.0f; + + xscale = 2.0f / (p.x2 - p.x1); + yscale = 2.0f / (p.y2 - p.y1); + xtrans = -(p.x2 + p.x1) / 2.0f; + ytrans = -(p.y2 + p.y1) / 2.0f; + + CRASSERT(xscale == xscale); /* NaN test */ + CRASSERT(yscale == yscale); + + extent->baseProjection = identity_matrix; + extent->baseProjection.m00 = xscale; + extent->baseProjection.m11 = yscale; + extent->baseProjection.m30 = xtrans * xscale; + extent->baseProjection.m31 = ytrans * yscale; + } + + extent->serialNo = serialNo++; + } + mural->viewportValidated = GL_TRUE; +#endif +} + + +/* + * Issue the glScissor, glViewport and projection matrix needed for + * rendering the tile specified by extNum. We computed the scissor, + * viewport and projection parameters above in crServerComputeViewportBounds. + */ +void +crServerSetOutputBounds( const CRMuralInfo *mural, int extNum ) +{ +#if 0 + const CRExtent *extent = mural->extents + extNum; + CRASSERT(mural->viewportValidated); + + /* + * Serial Number info: + * Everytime we compute new scissor, viewport, projection matrix info for + * a tile, we give that tile a new serial number. + * When we're about to render into a tile, we only update the scissor, + * viewport and projection matrix if the tile's serial number doesn't match + * the current serial number. This avoids a _LOT_ of redundant calls to + * those three functions. + */ + if (extent->serialNo != cr_server.currentSerialNo) { + cr_server.head_spu->dispatch_table.Scissor(extent->scissorBox.x1, + extent->scissorBox.y1, + extent->scissorBox.x2 - extent->scissorBox.x1, + extent->scissorBox.y2 - extent->scissorBox.y1); + + cr_server.head_spu->dispatch_table.Viewport(extent->viewport.x1, + extent->viewport.y1, + extent->viewport.x2 - extent->viewport.x1, + extent->viewport.y2 - extent->viewport.y1); + + crServerApplyBaseProjection(&(extent->baseProjection)); + cr_server.currentSerialNo = extent->serialNo; + } +#endif +} + + +/* + * Pre-multiply the current projection matrix with the current client's + * base projection. I.e. P' = b * P. Note that OpenGL's glMultMatrix + * POST-multiplies. + */ +void +crServerApplyBaseProjection(const CRmatrix *baseProj) +{ + const CRmatrix *projMatrix; + if (cr_server.projectionOverride) { + int eye = crServerGetCurrentEye(); + projMatrix = &cr_server.projectionMatrix[eye]; + } + else + projMatrix = cr_server.curClient->currentCtxInfo->pContext->transform.projectionStack.top; + + cr_server.head_spu->dispatch_table.PushAttrib( GL_TRANSFORM_BIT ); + cr_server.head_spu->dispatch_table.MatrixMode( GL_PROJECTION ); + cr_server.head_spu->dispatch_table.LoadMatrixf( (const GLfloat *) baseProj ); + cr_server.head_spu->dispatch_table.MultMatrixf( cr_server.alignment_matrix ); + cr_server.head_spu->dispatch_table.MultMatrixf( (const GLfloat *) projMatrix ); + cr_server.head_spu->dispatch_table.PopAttrib(); +} + + +void +crServerApplyViewMatrix(const CRmatrix *view) +{ + const CRmatrix *modelview = cr_server.curClient->currentCtxInfo->pContext->transform.modelViewStack.top; + + cr_server.head_spu->dispatch_table.PushAttrib( GL_TRANSFORM_BIT ); + cr_server.head_spu->dispatch_table.MatrixMode( GL_MODELVIEW ); + cr_server.head_spu->dispatch_table.LoadMatrixf( (const GLfloat *) view ); + cr_server.head_spu->dispatch_table.MultMatrixf( (const GLfloat *) modelview ); + cr_server.head_spu->dispatch_table.PopAttrib(); +} + + +/* + * Called via unpacker module. + * Note: when there's a tilesort SPU upstream, the viewport dimensions + * will typically match the mural size. That is, the viewport dimensions + * probably won't be the same values that the application issues. + */ +void SERVER_DISPATCH_APIENTRY crServerDispatchViewport( GLint x, GLint y, GLsizei width, GLsizei height ) +{ + CRMuralInfo *mural = cr_server.curClient->currentMural; + CRContext *ctx = crStateGetCurrent(); + + if (ctx->viewport.viewportX != x || + ctx->viewport.viewportY != y || + ctx->viewport.viewportW != width || + ctx->viewport.viewportH != height) { + /* Note -- If there are tiles, this will be overridden in the + * process of decoding the BoundsInfo packet, so no worries. */ + crStateViewport( x, y, width, height ); + } + + /* always dispatch to be safe */ + cr_server.head_spu->dispatch_table.Viewport( x, y, width, height ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c new file mode 100644 index 00000000..6558ea50 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_window.c @@ -0,0 +1,484 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "server.h" +#include "server_dispatch.h" +#include "cr_mem.h" +#include "cr_rand.h" +#include "cr_string.h" + +#include "render/renderspu.h" + +GLint SERVER_DISPATCH_APIENTRY +crServerDispatchWindowCreate(const char *dpyName, GLint visBits) +{ + return crServerDispatchWindowCreateEx(dpyName, visBits, -1); +} + +GLint crServerMuralInit(CRMuralInfo *mural, GLboolean fGuestWindow, GLint visBits, GLint preloadWinID) +{ + CRMuralInfo *defaultMural; + GLint dims[2]; + GLint windowID = -1; + GLint spuWindow = 0; + GLint realVisBits = visBits; + const char *dpyName = ""; + + crMemset(mural, 0, sizeof (*mural)); + + if (cr_server.fVisualBitsDefault) + realVisBits = cr_server.fVisualBitsDefault; + +#ifdef RT_OS_DARWIN + if (fGuestWindow) + { + CRMuralInfo *dummy = crServerGetDummyMural(realVisBits); + if (!dummy) + { + WARN(("crServerGetDummyMural failed")); + return -1; + } + spuWindow = dummy->spuWindow; + mural->fIsDummyRefference = GL_TRUE; + + dims[0] = dummy->width; + dims[1] = dummy->height; + } + else +#endif + { + /* + * Have first SPU make a new window. + */ + spuWindow = cr_server.head_spu->dispatch_table.WindowCreate( dpyName, realVisBits ); + if (spuWindow < 0) { + return spuWindow; + } + mural->fIsDummyRefference = GL_FALSE; + + /* get initial window size */ + cr_server.head_spu->dispatch_table.GetChromiumParametervCR(GL_WINDOW_SIZE_CR, spuWindow, GL_INT, 2, dims); + } + + defaultMural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, 0); + CRASSERT(defaultMural); + mural->gX = 0; + mural->gY = 0; + mural->width = dims[0]; + mural->height = dims[1]; + + mural->spuWindow = spuWindow; + mural->screenId = 0; + mural->fHasParentWindow = !!cr_server.screen[0].winID; + mural->bVisible = !cr_server.bWindowsInitiallyHidden; + + mural->cVisibleRects = 0; + mural->pVisibleRects = NULL; + mural->bReceivedRects = GL_FALSE; + + /* generate ID for this new window/mural (special-case for file conns) */ + if (cr_server.curClient && cr_server.curClient->conn->type == CR_FILE) + windowID = spuWindow; + else + windowID = preloadWinID<0 ? (GLint)crHashtableAllocKeys( cr_server.muralTable, 1 ) : preloadWinID; + + mural->CreateInfo.realVisualBits = realVisBits; + mural->CreateInfo.requestedVisualBits = visBits; + mural->CreateInfo.externalID = windowID; + mural->CreateInfo.pszDpyName = dpyName ? crStrdup(dpyName) : NULL; + + CR_STATE_SHAREDOBJ_USAGE_INIT(mural); + + return windowID; +} + +GLint crServerDispatchWindowCreateEx(const char *dpyName, GLint visBits, GLint preloadWinID) +{ + CRMuralInfo *mural; + GLint windowID = -1; + + NOREF(dpyName); + + if (cr_server.sharedWindows) { + int pos, j; + + /* find empty position in my (curclient) windowList */ + for (pos = 0; pos < CR_MAX_WINDOWS; pos++) { + if (cr_server.curClient->windowList[pos] == 0) { + break; + } + } + if (pos == CR_MAX_WINDOWS) { + crWarning("Too many windows in crserver!"); + return -1; + } + + /* Look if any other client has a window for this slot */ + for (j = 0; j < cr_server.numClients; j++) { + if (cr_server.clients[j]->windowList[pos] != 0) { + /* use that client's window */ + windowID = cr_server.clients[j]->windowList[pos]; + cr_server.curClient->windowList[pos] = windowID; + crServerReturnValue( &windowID, sizeof(windowID) ); /* real return value */ + crDebug("CRServer: client %p sharing window %d", + cr_server.curClient, windowID); + return windowID; + } + } + } + + + /* + * Create a new mural for the new window. + */ + mural = (CRMuralInfo *) crCalloc(sizeof(CRMuralInfo)); + if (!mural) + { + crWarning("crCalloc failed!"); + return -1; + } + + windowID = crServerMuralInit(mural, GL_TRUE, visBits, preloadWinID); + if (windowID < 0) + { + crWarning("crServerMuralInit failed!"); + crServerReturnValue( &windowID, sizeof(windowID) ); + crFree(mural); + return windowID; + } + + crHashtableAdd(cr_server.muralTable, windowID, mural); + + crDebug("CRServer: client %p created new window %d (SPU window %d)", + cr_server.curClient, windowID, mural->spuWindow); + + if (windowID != -1 && !cr_server.bIsInLoadingState) { + int pos; + for (pos = 0; pos < CR_MAX_WINDOWS; pos++) { + if (cr_server.curClient->windowList[pos] == 0) { + cr_server.curClient->windowList[pos] = windowID; + break; + } + } + } + + /* ensure we have a dummy mural created right away to avoid potential deadlocks on VM shutdown */ + crServerGetDummyMural(mural->CreateInfo.realVisualBits); + + crServerReturnValue( &windowID, sizeof(windowID) ); + return windowID; +} + +static int crServerRemoveClientWindow(CRClient *pClient, GLint window) +{ + int pos; + + for (pos = 0; pos < CR_MAX_WINDOWS; ++pos) + { + if (pClient->windowList[pos] == window) + { + pClient->windowList[pos] = 0; + return true; + } + } + + return false; +} + +void crServerMuralTerm(CRMuralInfo *mural) +{ + PCR_BLITTER pBlitter; + crServerRedirMuralFBO(mural, false); + crServerDeleteMuralFBO(mural); + + if (cr_server.currentMural == mural) + { + CRMuralInfo *dummyMural = crServerGetDummyMural(cr_server.MainContextInfo.CreateInfo.realVisualBits); + /* reset the current context to some dummy values to ensure render spu does not switch to a default "0" context, + * which might lead to muralFBO (offscreen rendering) gl entities being created in a scope of that context */ + cr_server.head_spu->dispatch_table.MakeCurrent(dummyMural->spuWindow, 0, cr_server.MainContextInfo.SpuContext); + cr_server.currentWindow = -1; + cr_server.currentMural = dummyMural; + } + else + { + CRASSERT(cr_server.currentMural != mural); + } + + pBlitter = crServerVBoxBlitterGetInitialized(); + if (pBlitter) + { + const CR_BLITTER_WINDOW * pWindow = CrBltMuralGetCurrentInfo(pBlitter); + if (pWindow && pWindow->Base.id == mural->spuWindow) + { + CRMuralInfo *dummy = crServerGetDummyMural(mural->CreateInfo.realVisualBits); + CR_BLITTER_WINDOW DummyInfo; + CRASSERT(dummy); + crServerVBoxBlitterWinInit(&DummyInfo, dummy); + CrBltMuralSetCurrentInfo(pBlitter, &DummyInfo); + } + } + + if (!mural->fIsDummyRefference) + cr_server.head_spu->dispatch_table.WindowDestroy( mural->spuWindow ); + + mural->spuWindow = 0; + + if (mural->pVisibleRects) + { + crFree(mural->pVisibleRects); + } + + if (mural->CreateInfo.pszDpyName) + crFree(mural->CreateInfo.pszDpyName); + + crServerRedirMuralFbClear(mural); +} + +static void crServerCleanupCtxMuralRefsCB(unsigned long key, void *data1, void *data2) +{ + CRContextInfo *ctxInfo = (CRContextInfo *) data1; + CRMuralInfo *mural = (CRMuralInfo *) data2; + + if (ctxInfo->currentMural == mural) + ctxInfo->currentMural = NULL; +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchWindowDestroy( GLint window ) +{ + CRMuralInfo *mural; + int32_t client; + CRClientNode *pNode; + int found=false; + + if (!window) + { + crWarning("Unexpected attempt to delete default mural, ignored!"); + return; + } + + mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); + if (!mural) { + crWarning("CRServer: invalid window %d passed to WindowDestroy()", window); + return; + } + + crDebug("CRServer: Destroying window %d (spu window %d)", window, mural->spuWindow); + + crHashtableWalk(cr_server.contextTable, crServerCleanupCtxMuralRefsCB, mural); + + crServerMuralTerm(mural); + + CRASSERT(cr_server.currentWindow != window); + + if (cr_server.curClient) + { + if (cr_server.curClient->currentMural == mural) + { + cr_server.curClient->currentMural = NULL; + cr_server.curClient->currentWindow = -1; + } + + found = crServerRemoveClientWindow(cr_server.curClient, window); + + /*Same as with contexts, some apps destroy it not in a thread where it was created*/ + if (!found) + { + for (client=0; client<cr_server.numClients; ++client) + { + if (cr_server.clients[client]==cr_server.curClient) + continue; + + found = crServerRemoveClientWindow(cr_server.clients[client], window); + + if (found) break; + } + } + + if (!found) + { + pNode=cr_server.pCleanupClient; + + while (pNode && !found) + { + found = crServerRemoveClientWindow(pNode->pClient, window); + pNode = pNode->next; + } + } + + CRASSERT(found); + } + + /*Make sure this window isn't active in other clients*/ + for (client=0; client<cr_server.numClients; ++client) + { + if (cr_server.clients[client]->currentMural == mural) + { + cr_server.clients[client]->currentMural = NULL; + cr_server.clients[client]->currentWindow = -1; + } + } + + pNode=cr_server.pCleanupClient; + while (pNode) + { + if (pNode->pClient->currentMural == mural) + { + pNode->pClient->currentMural = NULL; + pNode->pClient->currentWindow = -1; + } + pNode = pNode->next; + } + + crHashtableDelete(cr_server.muralTable, window, crFree); + + crServerCheckAllMuralGeometry(NULL); +} + +GLboolean crServerMuralSize(CRMuralInfo *mural, GLint width, GLint height) +{ + if (mural->width == width && mural->height == height) + return GL_FALSE; + + mural->width = width; + mural->height = height; + + if (cr_server.curClient && cr_server.curClient->currentMural == mural + && !mural->fRedirected) + { + crStateGetCurrent()->buffer.width = mural->width; + crStateGetCurrent()->buffer.height = mural->height; + } + + crServerCheckAllMuralGeometry(mural); + + return GL_TRUE; +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchWindowSize( GLint window, GLint width, GLint height ) +{ + CRMuralInfo *mural; + + /* crDebug("CRServer: Window %d size %d x %d", window, width, height);*/ + mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); + if (!mural) { +#if EXTRA_WARN + crWarning("CRServer: invalid window %d passed to WindowSize()", window); +#endif + return; + } + + crServerMuralSize(mural, width, height); + + if (cr_server.currentMural == mural) + { + crServerPerformMakeCurrent( mural, cr_server.currentCtxInfo ); + } +} + +void crServerMuralPosition(CRMuralInfo *mural, GLint x, GLint y) +{ + if (mural->gX == x && mural->gY == y) + return; + + mural->gX = x; + mural->gY = y; + + crServerCheckAllMuralGeometry(mural); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchWindowPosition( GLint window, GLint x, GLint y ) +{ + CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); + if (!mural) { +#if EXTRA_WARN + crWarning("CRServer: invalid window %d passed to WindowPosition()", window); +#endif + return; + } + crServerMuralPosition(mural, x, y); +} + +void crServerMuralVisibleRegion( CRMuralInfo *mural, GLint cRects, const GLint *pRects ) +{ + if (mural->pVisibleRects) + { + crFree(mural->pVisibleRects); + mural->pVisibleRects = NULL; + } + + mural->cVisibleRects = cRects; + mural->bReceivedRects = GL_TRUE; + if (cRects) + { + mural->pVisibleRects = (GLint*) crAlloc(4*sizeof(GLint)*cRects); + if (!mural->pVisibleRects) + { + crError("Out of memory in crServerDispatchWindowVisibleRegion"); + } + crMemcpy(mural->pVisibleRects, pRects, 4*sizeof(GLint)*cRects); + } + + crServerCheckAllMuralGeometry(mural); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchWindowVisibleRegion( GLint window, GLint cRects, const GLint *pRects ) +{ + CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); + if (!mural) { +#if EXTRA_WARN + crWarning("CRServer: invalid window %d passed to WindowVisibleRegion()", window); +#endif + return; + } + + crServerMuralVisibleRegion( mural, cRects, pRects ); +} + +void crServerMuralShow( CRMuralInfo *mural, GLint state ) +{ + if (!mural->bVisible == !state) + return; + + mural->bVisible = !!state; + + if (mural->bVisible) + crServerCheckMuralGeometry(mural); + else + crServerCheckAllMuralGeometry(mural); +} + +void SERVER_DISPATCH_APIENTRY +crServerDispatchWindowShow( GLint window, GLint state ) +{ + CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, window); + if (!mural) { +#if EXTRA_WARN + crWarning("CRServer: invalid window %d passed to WindowShow()", window); +#endif + return; + } + + crServerMuralShow( mural, state ); +} + +GLint +crServerSPUWindowID(GLint serverWindow) +{ + CRMuralInfo *mural = (CRMuralInfo *) crHashtableSearch(cr_server.muralTable, serverWindow); + if (!mural) { +#if EXTRA_WARN + crWarning("CRServer: invalid window %d passed to crServerSPUWindowID()", + serverWindow); +#endif + return -1; + } + return mural->spuWindow; +} diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_winpos.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_winpos.c new file mode 100644 index 00000000..8026b962 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_winpos.c @@ -0,0 +1,95 @@ + +#include "server_dispatch.h" +#include "server.h" + + +/** + * All glWindowPos commands go through here. + */ +static void crServerWindowPos( GLfloat x, GLfloat y, GLfloat z ) +{ + crStateWindowPos3fARB(x, y, z); + cr_server.head_spu->dispatch_table.WindowPos3fARB(x, y, z); +} + + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2dARB( GLdouble x, GLdouble y ) +{ + crServerWindowPos((GLfloat) x, (GLfloat) y, 0.0F); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2dvARB( const GLdouble * v ) +{ + crServerWindowPos((GLfloat) v[0], (GLfloat) v[1], 0.0F); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2fARB( GLfloat x, GLfloat y ) +{ + crServerWindowPos(x, y, 0.0F); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2fvARB( const GLfloat * v ) +{ + crServerWindowPos(v[0], v[1], 0.0F); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2iARB( GLint x, GLint y ) +{ + crServerWindowPos((GLfloat)x, (GLfloat)y, 0.0F); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2ivARB( const GLint * v ) +{ + crServerWindowPos((GLfloat)v[0], (GLfloat)v[1], 0.0F); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2sARB( GLshort x, GLshort y ) +{ + crServerWindowPos(x, y, 0.0F); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos2svARB( const GLshort * v ) +{ + crServerWindowPos(v[0], v[1], 0.0F); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3dARB( GLdouble x, GLdouble y, GLdouble z ) +{ + crServerWindowPos((GLfloat) x, (GLfloat) y, (GLfloat) z); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3dvARB( const GLdouble * v ) +{ + crServerWindowPos((GLfloat) v[0], (GLfloat) v[1], (GLfloat) v[2]); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3fARB( GLfloat x, GLfloat y, GLfloat z ) +{ + crServerWindowPos(x, y, z); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3fvARB( const GLfloat * v ) +{ + crServerWindowPos(v[0], v[1], v[2]); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3iARB( GLint x, GLint y, GLint z ) +{ + crServerWindowPos((GLfloat)x,(GLfloat)y, (GLfloat)z); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3ivARB( const GLint * v ) +{ + crServerWindowPos((GLfloat)v[0], (GLfloat)v[1], (GLfloat)v[2]); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3sARB( GLshort x, GLshort y, GLshort z ) +{ + crServerWindowPos(x, y, z); +} + +void SERVER_DISPATCH_APIENTRY crServerDispatchWindowPos3svARB( const GLshort * v ) +{ + crServerWindowPos(v[0], v[1], v[2]); +} + diff --git a/src/VBox/HostServices/SharedOpenGL/crserverlib/server_writeback.c b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_writeback.c new file mode 100644 index 00000000..8af28cd5 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/crserverlib/server_writeback.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_mem.h" +#include "cr_net.h" +#include "server_dispatch.h" +#include "server.h" + +void SERVER_DISPATCH_APIENTRY crServerDispatchWriteback( GLint *writeback ) +{ + (void) writeback; + crServerWriteback( ); +} + +void crServerWriteback(void) +{ + CRMessageWriteback *wb = (CRMessageWriteback *) crAlloc( sizeof( *wb ) ); + wb->header.type = CR_MESSAGE_WRITEBACK; + CRDBGPTR_PRINTWB(cr_server.curClient->conn->u32ClientID, &cr_server.writeback_ptr); + CRDBGPTR_CHECKNZ(&cr_server.writeback_ptr); + crMemcpy( &(wb->writeback_ptr), &(cr_server.writeback_ptr), sizeof( wb->writeback_ptr ) ); + crNetSend( cr_server.curClient->conn, NULL, wb, sizeof( *wb ) ); + CRDBGPTR_SETZ(&cr_server.writeback_ptr); + crFree( wb ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/dlm/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/Makefile.kup diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c new file mode 100644 index 00000000..8ea6c69c --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm.c @@ -0,0 +1,575 @@ +/* $Id: dlm.c $ */ + +#include <float.h> +#include "cr_dlm.h" +#include "cr_mem.h" +#include "dlm.h" + +/** + * \mainpage Dlm + * + * \section DlmIntroduction Introduction + * + * Chromium consists of all the top-level files in the cr + * directory. The dlm module basically takes care of API dispatch, + * and OpenGL state management. + * + */ + +/** + * Module globals: the current DLM state, bound either to each thread, or + * to a global. + */ +#ifdef CHROMIUM_THREADSAFE +CRtsd CRDLMTSDKey; +#else +CRDLMContextState *CRDLMCurrentState = NULL; +#endif + +#define MIN(a,b) ((a)<(b)?(a):(b)) + + +/*************************************************************************/ + +#ifdef CHROMIUM_THREADSAFE +/** + * This is the thread-specific destructor function for the + * data used in the DLM. It's very simple: if a thread exits + * that has DLM-specific data, the data represents the listState + * for the thread. All data and buffers associated with the list + * can be deleted, and the structure itself can be freed. + * + * Most Chromium threads don't have such things; but then, + * if a thread dies elsewhere in Chromium, huge buffers + * of information won't still be floating around in + * unrecoverable allocated areas, either. + */ +static void threadDestructor(void *tsd) +{ + CRDLMContextState *listState = (CRDLMContextState *)tsd; + + if (listState) + { + //if (listState->currentListInfo) + // crdlm_free_list(listState->currentListInfo); + + crFree(listState); + } +} +#endif + +/** + * This function creates and initializes a new display list + * manager. It returns a pointer to the manager, or NULL in + * the case of insufficient memory. The dispatch table pointer + * is passed in to allow the utilities to muck with the table + * to gain functional control when GL calls are made. + */ +CRDLM DLM_APIENTRY *crDLMNewDLM(unsigned int userConfigSize, const CRDLMConfig *userConfig) +{ + CRDLM *dlm; + + /* This is the default configuration. We'll overwrite it later + * with user-supplied configuration information. + */ + CRDLMConfig config = { + CRDLM_DEFAULT_BUFFERSIZE, + }; + + dlm = crAlloc(sizeof(*dlm)); + if (!dlm) { + return NULL; + } + + /* Start off by initializing all entries that require further + * memory allocation, so we can free up all the memory if there's + * a problem. + */ + if (!(dlm->displayLists = crAllocHashtable())) { + crFree(dlm); + return NULL; + } + + /* The creator counts as the first user. */ + dlm->userCount = 1; + +#ifdef CHROMIUM_THREADSAFE + /* This mutex ensures that only one thread is changing the displayLists + * hash at a time. Note that we may also need a mutex to guarantee that + * the hash is not changed by one thread while another thread is + * traversing it; this issue has not yet been resolved. + */ + crInitMutex(&(dlm->dlMutex)); + + /* Although the thread-specific data (TSD) functions will initialize + * the thread key themselves when needed, those functions do not allow + * us to specify a thread destructor. Since a thread could potentially + * exit with considerable memory allocated (e.g. if a thread exits + * after it has issued NewList but before EndList, and while there + * are considerable content buffers allocated), I do the initialization + * myself, in order to be able to reclaim those resources if a thread + * exits. + */ + crInitTSDF(&(dlm->tsdKey), threadDestructor); + crInitTSD(&CRDLMTSDKey); +#endif + + /* Copy over any appropriate configuration values */ + if (userConfig != NULL) { + /* Copy over as much configuration information as is provided. + * Note that if the CRDLMConfig structure strictly grows, this + * allows forward compatability - routines compiled with + * older versions of the structure will only initialize that + * section of the structure that they know about. + */ + crMemcpy((void *)&config, (void *) userConfig, + MIN(userConfigSize, sizeof(config))); + } + dlm->bufferSize = config.bufferSize; + + /* Return the pointer to the newly-allocated display list manager */ + return dlm; +} + +void DLM_APIENTRY crDLMUseDLM(CRDLM *dlm) +{ + DLM_LOCK(dlm); + dlm->userCount++; + DLM_UNLOCK(dlm); +} + +/** + * This routine is called when a context or thread is done with a DLM. + * It maintains an internal count of users, and will only actually destroy + * itself when no one is still using the DLM. + */ +void DLM_APIENTRY crDLMFreeDLM(CRDLM *dlm, SPUDispatchTable *dispatchTable) +{ + /* We're about to change the displayLists hash; lock it first */ + DLM_LOCK(dlm) + + /* Decrement the user count. If the user count has gone to + * 0, then free the rest of the DLM. Otherwise, other + * contexts or threads are still using this DLM; keep + * it around. + */ + dlm->userCount--; + if (dlm->userCount == 0) { + + crFreeHashtableEx(dlm->displayLists, crdlmFreeDisplayListResourcesCb, dispatchTable); + dlm->displayLists = NULL; + + /* Must unlock before freeing the mutex */ + DLM_UNLOCK(dlm) + +#ifdef CHROMIUM_THREADSAFE + /* We release the mutex here; we really should delete the + * thread data key, but there's no utility in Chromium to + * do this. + * + * Note that, should one thread release the entire DLM + * while other threads still believe they are using it, + * any other threads that have current display lists (i.e. + * have issued glNewList more recently than glEndList) + * will be unable to reclaim their (likely very large) + * content buffers, as there will be no way to reclaim + * the thread-specific data. + * + * On the other hand, if one thread really does release + * the DLM while other threads still believe they are + * using it, unreclaimed memory is the least of the + * application's problems... + */ + crFreeMutex(&(dlm->dlMutex)); + + /* We free the TSD key here as well. Note that this will + * strand any threads that still have thread-specific data + * tied to this key; but as stated above, if any threads + * still do have thread-specific data attached to this DLM, + * they're in big trouble anyway. + */ + crFreeTSD(&(dlm->tsdKey)); + crFreeTSD(&CRDLMTSDKey); +#endif + + /* Free the master record, and we're all done. */ + crFree(dlm); + } + else { + /* We're keeping the DLM around for other users. Unlock it, + * but retain its memory and display lists. + */ + DLM_UNLOCK(dlm) + } +} + +/** + * The actual run-time state of a DLM is bound to a context + * (because each context can be used by at most one thread at + * a time, and a thread can only use one context at a time, + * while multiple contexts can use the same DLM). + * This creates the structure required to hold the state, and + * returns it to the caller, who should store it with any other + * context-specific information. + */ + +CRDLMContextState DLM_APIENTRY *crDLMNewContext(CRDLM *dlm) +{ + CRDLMContextState *state; + + /* Get a record for our own internal state structure */ + state = (CRDLMContextState *)crAlloc(sizeof(CRDLMContextState)); + if (!state) { + return NULL; + } + + state->dlm = dlm; + state->currentListIdentifier = 0; + state->currentListInfo = NULL; + state->currentListMode = GL_FALSE; + state->listBase = 0; + + /* Increment the use count of the DLM provided. This guarantees that + * the DLM won't be released until all the contexts have released it. + */ + crDLMUseDLM(dlm); + + return state; +} + + +/** + * This routine should be called when a MakeCurrent changes the current + * context. It sets the thread data (or global data, in an unthreaded + * environment) appropriately; this in turn changes the behavior of + * the installed DLM API functions. + */ +void DLM_APIENTRY crDLMSetCurrentState(CRDLMContextState *state) +{ + CRDLMContextState *currentState = CURRENT_STATE(); + if (currentState != state) { + SET_CURRENT_STATE(state); + } +} + +CRDLMContextState DLM_APIENTRY *crDLMGetCurrentState(void) +{ + return CURRENT_STATE(); +} + +/** + * This routine, of course, is used to release a DLM context when it + * is no longer going to be used. + */ + +void DLM_APIENTRY crDLMFreeContext(CRDLMContextState *state, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + + /* If we're currently using this context, release it first */ + if (listState == state) + crDLMSetCurrentState(NULL); + + /* Try to free the DLM. This will either decrement the use count, + * or will actually free the DLM, if we were the last user. + */ + crDLMFreeDLM(state->dlm, dispatchTable); + state->dlm = NULL; + + /* If any buffers still remain (e.g. because there was an open + * display list), remove those as well. + */ + if (state->currentListInfo) + { + crdlmFreeDisplayListResourcesCb((void *)state->currentListInfo, (void *)dispatchTable); + state->currentListInfo = NULL; + } + state->currentListIdentifier = 0; + + /* Free the state record itself */ + crFree(state); +} + + +/** + * This function can be used if the caller wishes to free up the + * potentially considerable resources used to store the display list + * content, without losing the rest of the display list management. + * For one example, consider an SPU that conditionally sends its + * input stream to multiple servers. It could broadcast all display + * lists to all servers, or it could only send display lists to servers + * that need them. After all servers have the display list, the SPU + * may wish to release the resources used to manage the content. + */ +CRDLMError DLM_APIENTRY crDLMDeleteListContent(CRDLM *dlm, unsigned long listIdentifier) +{ + DLMListInfo *listInfo; + DLMInstanceList *instance; + + listInfo = (DLMListInfo *) crHashtableSearch(dlm->displayLists, listIdentifier); + if (listInfo && (instance = listInfo->first)) { + while (instance) { + DLMInstanceList *nextInstance; + nextInstance = instance->next; + crFree(instance); + instance = nextInstance; + } + listInfo->first = listInfo->last = NULL; + } + return GL_NO_ERROR; +} + +/** + * + * Playback/execute a list. + * dlm - the display list manager context + * listIdentifier - the display list ID (as specified by app) to playback + * dispatchTable - the GL dispatch table to jump through as we execute commands + */ +void DLM_APIENTRY crDLMReplayDLMList(CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable) +{ + DLMListInfo *listInfo; + + listInfo = (DLMListInfo *)crHashtableSearch(dlm->displayLists, listIdentifier); + if (listInfo) { + DLMInstanceList *instance = listInfo->first; + while (instance) { + /* mutex, to make sure another thread doesn't change the list? */ + /* For now, leave it alone. */ + (*instance->execute)(instance, dispatchTable); + instance = instance->next; + } + } +} + +/* Playback/execute a list in the current DLM */ +void DLM_APIENTRY crDLMReplayList(unsigned long listIdentifier, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + if (listState) + crDLMReplayDLMList(listState->dlm, listIdentifier, dispatchTable); +} + +/* + * Playback/execute the state changing portions of a list. + * dlm - the display list manager context + * listIdentifier - the display list ID (as specified by app) to playback + * dispatchTable - the GL dispatch table to jump through as we execute commands + */ +void DLM_APIENTRY crDLMReplayDLMListState(CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable) +{ + DLMListInfo *listInfo; + + listInfo = (DLMListInfo *)crHashtableSearch(dlm->displayLists, listIdentifier); + if (listInfo) { + DLMInstanceList *instance = listInfo->stateFirst; + while (instance) { + /* mutex, to make sure another thread doesn't change the list? */ + /* For now, leave it alone. */ + (*instance->execute)(instance, dispatchTable); + instance = instance->stateNext; + } + } +} + +void DLM_APIENTRY crDLMReplayListState(unsigned long listIdentifier, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + if (listState) + crDLMReplayDLMListState(listState->dlm, listIdentifier, dispatchTable); +} + +/* This is a switch statement that lists every "type" value valid for a + * glCallLists() function call, with code for decoding the subsequent + * values correctly. It uses the current value of the EXPAND() macro, + * which must expand into an appropriate action to be taken. + * Its codification here allows for multiple uses. + */ +#define CALL_LISTS_SWITCH(type, defaultAction) \ + switch (type) {\ + EXPAND(GL_BYTE, GLbyte *, *p, p++)\ + EXPAND(GL_UNSIGNED_BYTE, GLubyte *, *p, p++)\ + EXPAND(GL_SHORT, GLshort *, *p, p++)\ + EXPAND(GL_UNSIGNED_SHORT, GLushort *, *p, p++)\ + EXPAND(GL_INT, GLint *, *p, p++)\ + EXPAND(GL_FLOAT, GLfloat *, *p, p++)\ + EXPAND(GL_2_BYTES, unsigned char *, 256*p[0] + p[1], p += 2)\ + EXPAND(GL_3_BYTES, unsigned char *, 65536*p[0] + 256*p[1] + p[2], p += 3)\ + EXPAND(GL_4_BYTES, unsigned char *, 16777216*p[0] + 65536*p[1] + 256*p[2] + p[3], p += 4)\ + default:\ + defaultAction;\ + } + +void DLM_APIENTRY crDLMReplayDLMLists(CRDLM *dlm, GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable) +{ + unsigned long listId; + CRDLMContextState *listState = CURRENT_STATE(); + +#define EXPAND(TYPENAME, TYPE, REFERENCE, INCREMENT) \ + case TYPENAME: {\ + TYPE p = (TYPE)lists;\ + while (n--) {\ + listId = listState->listBase + (unsigned long) (REFERENCE);\ + crDLMReplayDLMList(dlm, listId, dispatchTable);\ + INCREMENT;\ + }\ + break;\ + } + + CALL_LISTS_SWITCH(type, break) +#undef EXPAND + +} + +void DLM_APIENTRY crDLMReplayLists(GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + if (listState) { + crDLMReplayDLMLists(listState->dlm, n, type, lists, dispatchTable); + } +} + +void DLM_APIENTRY crDLMReplayDLMListsState(CRDLM *dlm, GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable) +{ + unsigned long listId; + CRDLMContextState *listState = CURRENT_STATE(); + +#define EXPAND(TYPENAME, TYPE, REFERENCE, INCREMENT) \ + case TYPENAME: {\ + TYPE p = (TYPE)lists;\ + while (n--) {\ + listId = listState->listBase + (unsigned long) (REFERENCE);\ + crDLMReplayDLMListState(dlm, listId, dispatchTable);\ + INCREMENT;\ + }\ + break;\ + } + + CALL_LISTS_SWITCH(type, break) +#undef EXPAND + +} + +void DLM_APIENTRY crDLMReplayListsState(GLsizei n, GLenum type, const GLvoid * lists, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + if (listState) { + crDLMReplayDLMListsState(listState->dlm, n, type, lists, dispatchTable); + } +} + +/* When we compiled the display list, we packed all pixel data + * tightly. When we execute the display list, we have to make + * sure that the client state reflects that the pixel data is + * tightly packed, or it will be interpreted incorrectly. + */ +void DLM_APIENTRY crDLMSetupClientState(SPUDispatchTable *dispatchTable) +{ + dispatchTable->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); + dispatchTable->PixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + dispatchTable->PixelStorei(GL_UNPACK_SKIP_ROWS, 0); + dispatchTable->PixelStorei(GL_UNPACK_ALIGNMENT, 1); +} + +void DLM_APIENTRY crDLMRestoreClientState(CRClientState *clientState, SPUDispatchTable *dispatchTable) +{ + if (clientState) { + dispatchTable->PixelStorei(GL_UNPACK_ROW_LENGTH, clientState->unpack.rowLength); + dispatchTable->PixelStorei(GL_UNPACK_SKIP_PIXELS, clientState->unpack.skipPixels); + dispatchTable->PixelStorei(GL_UNPACK_SKIP_ROWS, clientState->unpack.skipRows); + dispatchTable->PixelStorei(GL_UNPACK_ALIGNMENT, clientState->unpack.alignment); + } +} + +void DLM_APIENTRY crDLMSendDLMList(CRDLM *dlm, unsigned long listIdentifier, + SPUDispatchTable *dispatchTable) +{ + dispatchTable->NewList(listIdentifier, GL_COMPILE); + crDLMReplayDLMList(dlm, listIdentifier, dispatchTable); + dispatchTable->EndList(); +} + +void DLM_APIENTRY crDLMSendList(unsigned long listIdentifier, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + if (listState) { + crDLMSendDLMList(listState->dlm, listIdentifier, dispatchTable); + } +} + +struct sendListsCallbackParms { + CRDLM *dlm; + SPUDispatchTable *dispatchTable; +}; + +static void sendListsCallback(unsigned long key, void *data, void *dataPtr2) +{ + struct sendListsCallbackParms *parms = (struct sendListsCallbackParms *)dataPtr2; + + crDLMSendDLMList(parms->dlm, key, parms->dispatchTable); +} + +void DLM_APIENTRY crDLMSendAllDLMLists(CRDLM *dlm, SPUDispatchTable *dispatchTable) +{ + struct sendListsCallbackParms parms; + + /* This is how we pass our parameter information to the callback routine - + * through a pointer to this local structure. + */ + parms.dlm = dlm; + parms.dispatchTable = dispatchTable; + + crHashtableWalk(dlm->displayLists, sendListsCallback, (void *)&parms); +} + +void DLM_APIENTRY crDLMSendAllLists(SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + if (listState) { + crDLMSendAllDLMLists(listState->dlm, dispatchTable); + } +} + +/** Another clever callback arrangement to get the desired data. */ +struct getRefsCallbackParms { + int remainingOffset; + int remainingCount; + unsigned int *buffer; + int totalCount; +}; + +/* + * Return id of list currently being compiled. Returns 0 of there's no + * current DLM state, or if no list is being compiled. + */ +GLuint DLM_APIENTRY crDLMGetCurrentList(void) +{ + CRDLMContextState *listState = CURRENT_STATE(); + return listState ? listState->currentListIdentifier : 0; +} + +/* + * Return mode of list currently being compiled. Should be + * GL_FALSE if no list is being compiled, or GL_COMPILE if a + * list is being compiled but not executed, or GL_COMPILE_AND_EXECUTE + * if a list is being compiled and executed. + */ +GLenum DLM_APIENTRY crDLMGetCurrentMode(void) +{ + CRDLMContextState *listState = CURRENT_STATE(); + return listState ? listState->currentListMode : 0; +} + + +static CRDLMErrorCallback ErrorCallback = NULL; + +void DLM_APIENTRY crDLMErrorFunction(CRDLMErrorCallback callback) +{ + ErrorCallback = callback; +} + +void crdlm_error(int line, const char *file, GLenum error, const char *info) +{ + if (ErrorCallback) + (*ErrorCallback)(line, file, error, info); +} diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm.h b/src/VBox/HostServices/SharedOpenGL/dlm/dlm.h new file mode 100644 index 00000000..2e6db46e --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm.h @@ -0,0 +1,31 @@ +/* $Id: dlm.h $ */ + +#ifndef _DLM_H +#define _DLM_H + +#include "cr_dlm.h" +#include "cr_spu.h" + +#ifdef CHROMIUM_THREADSAFE +#define DLM_LOCK(dlm) crLockMutex(&(dlm->dlMutex)); +#define DLM_UNLOCK(dlm) crUnlockMutex(&(dlm->dlMutex)); +extern CRtsd CRDLMTSDKey; +#define SET_CURRENT_STATE(state) crSetTSD(&CRDLMTSDKey, (void *)state); +#define CURRENT_STATE() ((CRDLMContextState *)crGetTSD(&CRDLMTSDKey)) +#else +#define DLM_LOCK(dlm) +#define DLM_UNLOCK(dlm) +extern CRDLMContextState *CRDLMCurrentState; +#define SET_CURRENT_STATE(state) CRDLMCurrentState = (state); +#define CURRENT_STATE() (CRDLMCurrentState) +#endif + +/* These routines are intended to be used within the DLM library, across + * the modules therein, but not as an API into the DLM library from + * outside. + */ +extern void crdlmWarning( int line, char *file, GLenum error, char *format, ... ); +extern void crdlmFreeDisplayListResourcesCb(void *pParm1, void *pParam2); +extern void crdlm_error(int line, const char *file, GLenum error, const char *info); + +#endif diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_arrays.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_arrays.c new file mode 100644 index 00000000..d14d7198 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_arrays.c @@ -0,0 +1,387 @@ +/* $Id: dlm_arrays.c $ */ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include <stdio.h> +#include <stdarg.h> +#include "chromium.h" +#include "cr_dlm.h" +#include "dlm.h" + +/* + * XXX this code is awfully similar to the code in arrayspu.c + * We should try to write something reusable. + */ + +void DLM_APIENTRY crDLMCompileArrayElement (GLint index, CRClientState *c) +{ + unsigned char *p; + int unit; + + if (c->array.e.enabled) + { + crDLMCompileEdgeFlagv(c->array.e.p + index*c->array.e.stride); + } + for (unit = 0; unit < CR_MAX_TEXTURE_UNITS; unit++) + { + if (c->array.t[unit].enabled) + { + p = c->array.t[unit].p + index*c->array.t[unit].stride; + switch (c->array.t[unit].type) + { + case GL_SHORT: + switch (c->array.t[c->curClientTextureUnit].size) + { + case 1: crDLMCompileMultiTexCoord1svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break; + case 2: crDLMCompileMultiTexCoord2svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break; + case 3: crDLMCompileMultiTexCoord3svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break; + case 4: crDLMCompileMultiTexCoord4svARB(GL_TEXTURE0_ARB + unit, (GLshort *)p); break; + } + break; + case GL_INT: + switch (c->array.t[c->curClientTextureUnit].size) + { + case 1: crDLMCompileMultiTexCoord1ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break; + case 2: crDLMCompileMultiTexCoord2ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break; + case 3: crDLMCompileMultiTexCoord3ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break; + case 4: crDLMCompileMultiTexCoord4ivARB(GL_TEXTURE0_ARB + unit, (GLint *)p); break; + } + break; + case GL_FLOAT: + switch (c->array.t[c->curClientTextureUnit].size) + { + case 1: crDLMCompileMultiTexCoord1fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break; + case 2: crDLMCompileMultiTexCoord2fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break; + case 3: crDLMCompileMultiTexCoord3fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break; + case 4: crDLMCompileMultiTexCoord4fvARB(GL_TEXTURE0_ARB + unit, (GLfloat *)p); break; + } + break; + case GL_DOUBLE: + switch (c->array.t[c->curClientTextureUnit].size) + { + case 1: crDLMCompileMultiTexCoord1dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break; + case 2: crDLMCompileMultiTexCoord2dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break; + case 3: crDLMCompileMultiTexCoord3dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break; + case 4: crDLMCompileMultiTexCoord4dvARB(GL_TEXTURE0_ARB + unit, (GLdouble *)p); break; + } + break; + } + } + } /* loop over texture units */ + + if (c->array.i.enabled) + { + p = c->array.i.p + index*c->array.i.stride; + switch (c->array.i.type) + { + case GL_SHORT: crDLMCompileIndexsv((GLshort *)p); break; + case GL_INT: crDLMCompileIndexiv((GLint *)p); break; + case GL_FLOAT: crDLMCompileIndexfv((GLfloat *)p); break; + case GL_DOUBLE: crDLMCompileIndexdv((GLdouble *)p); break; + } + } + if (c->array.c.enabled) + { + p = c->array.c.p + index*c->array.c.stride; + switch (c->array.c.type) + { + case GL_BYTE: + switch (c->array.c.size) + { + case 3: crDLMCompileColor3bv((GLbyte *)p); break; + case 4: crDLMCompileColor4bv((GLbyte *)p); break; + } + break; + case GL_UNSIGNED_BYTE: + switch (c->array.c.size) + { + case 3: crDLMCompileColor3ubv((GLubyte *)p); break; + case 4: crDLMCompileColor4ubv((GLubyte *)p); break; + } + break; + case GL_SHORT: + switch (c->array.c.size) + { + case 3: crDLMCompileColor3sv((GLshort *)p); break; + case 4: crDLMCompileColor4sv((GLshort *)p); break; + } + break; + case GL_UNSIGNED_SHORT: + switch (c->array.c.size) + { + case 3: crDLMCompileColor3usv((GLushort *)p); break; + case 4: crDLMCompileColor4usv((GLushort *)p); break; + } + break; + case GL_INT: + switch (c->array.c.size) + { + case 3: crDLMCompileColor3iv((GLint *)p); break; + case 4: crDLMCompileColor4iv((GLint *)p); break; + } + break; + case GL_UNSIGNED_INT: + switch (c->array.c.size) + { + case 3: crDLMCompileColor3uiv((GLuint *)p); break; + case 4: crDLMCompileColor4uiv((GLuint *)p); break; + } + break; + case GL_FLOAT: + switch (c->array.c.size) + { + case 3: crDLMCompileColor3fv((GLfloat *)p); break; + case 4: crDLMCompileColor4fv((GLfloat *)p); break; + } + break; + case GL_DOUBLE: + switch (c->array.c.size) + { + case 3: crDLMCompileColor3dv((GLdouble *)p); break; + case 4: crDLMCompileColor4dv((GLdouble *)p); break; + } + break; + } + } + if (c->array.n.enabled) + { + p = c->array.n.p + index*c->array.n.stride; + switch (c->array.n.type) + { + case GL_BYTE: crDLMCompileNormal3bv((GLbyte *)p); break; + case GL_SHORT: crDLMCompileNormal3sv((GLshort *)p); break; + case GL_INT: crDLMCompileNormal3iv((GLint *)p); break; + case GL_FLOAT: crDLMCompileNormal3fv((GLfloat *)p); break; + case GL_DOUBLE: crDLMCompileNormal3dv((GLdouble *)p); break; + } + } +#ifdef CR_EXT_secondary_color + if (c->array.s.enabled) + { + p = c->array.s.p + index*c->array.s.stride; + switch (c->array.s.type) + { + case GL_BYTE: + crDLMCompileSecondaryColor3bvEXT((GLbyte *)p); break; + case GL_UNSIGNED_BYTE: + crDLMCompileSecondaryColor3ubvEXT((GLubyte *)p); break; + case GL_SHORT: + crDLMCompileSecondaryColor3svEXT((GLshort *)p); break; + case GL_UNSIGNED_SHORT: + crDLMCompileSecondaryColor3usvEXT((GLushort *)p); break; + case GL_INT: + crDLMCompileSecondaryColor3ivEXT((GLint *)p); break; + case GL_UNSIGNED_INT: + crDLMCompileSecondaryColor3uivEXT((GLuint *)p); break; + case GL_FLOAT: + crDLMCompileSecondaryColor3fvEXT((GLfloat *)p); break; + case GL_DOUBLE: + crDLMCompileSecondaryColor3dvEXT((GLdouble *)p); break; + } + } +#endif + if (c->array.v.enabled) + { + p = c->array.v.p + (index*c->array.v.stride); + + switch (c->array.v.type) + { + case GL_SHORT: + switch (c->array.v.size) + { + case 2: crDLMCompileVertex2sv((GLshort *)p); break; + case 3: crDLMCompileVertex3sv((GLshort *)p); break; + case 4: crDLMCompileVertex4sv((GLshort *)p); break; + } + break; + case GL_INT: + switch (c->array.v.size) + { + case 2: crDLMCompileVertex2iv((GLint *)p); break; + case 3: crDLMCompileVertex3iv((GLint *)p); break; + case 4: crDLMCompileVertex4iv((GLint *)p); break; + } + break; + case GL_FLOAT: + switch (c->array.v.size) + { + case 2: crDLMCompileVertex2fv((GLfloat *)p); break; + case 3: crDLMCompileVertex3fv((GLfloat *)p); break; + case 4: crDLMCompileVertex4fv((GLfloat *)p); break; + } + break; + case GL_DOUBLE: + switch (c->array.v.size) + { + case 2: crDLMCompileVertex2dv((GLdouble *)p); break; + case 3: crDLMCompileVertex3dv((GLdouble *)p); break; + case 4: crDLMCompileVertex4dv((GLdouble *)p); break; + } + break; + } + } +} + +void DLM_APIENTRY crDLMCompileDrawArrays(GLenum mode, GLint first, GLsizei count, CRClientState *c) +{ + int i; + + if (count < 0) + { + crdlmWarning(__LINE__, __FILE__, GL_INVALID_VALUE, "DLM DrawArrays(negative count)"); + return; + } + + if (mode > GL_POLYGON) + { + crdlmWarning(__LINE__, __FILE__, GL_INVALID_ENUM, "DLM DrawArrays(bad mode)"); + return; + } + + crDLMCompileBegin(mode); + for (i=0; i<count; i++) + { + crDLMCompileArrayElement(first + i, c); + } + crDLMCompileEnd(); +} + +void DLM_APIENTRY crDLMCompileDrawElements(GLenum mode, GLsizei count, + GLenum type, const GLvoid *indices, CRClientState *c) +{ + int i; + GLubyte *p = (GLubyte *)indices; + + if (count < 0) + { + crdlmWarning(__LINE__, __FILE__, GL_INVALID_VALUE, "DLM DrawElements(negative count)"); + return; + } + + if (mode > GL_POLYGON) + { + crdlmWarning(__LINE__, __FILE__, GL_INVALID_ENUM, "DLM DrawElements(bad mode)"); + return; + } + + if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT && type != GL_UNSIGNED_INT) + { + crdlmWarning(__LINE__, __FILE__, GL_INVALID_ENUM, "DLM DrawElements(bad type)"); + return; + } + + crDLMCompileBegin(mode); + switch (type) + { + case GL_UNSIGNED_BYTE: + for (i=0; i<count; i++) + { + crDLMCompileArrayElement((GLint) *p++, c); + } + break; + case GL_UNSIGNED_SHORT: + for (i=0; i<count; i++) + { + crDLMCompileArrayElement((GLint) * (GLushort *) p, c); + p+=sizeof (GLushort); + } + break; + case GL_UNSIGNED_INT: + for (i=0; i<count; i++) + { + crDLMCompileArrayElement((GLint) * (GLuint *) p, c); + p+=sizeof (GLuint); + } + break; + default: + crError( "this can't happen: DLM DrawElements" ); + break; + } + crDLMCompileEnd(); +} + +void DLM_APIENTRY crDLMCompileDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, + GLenum type, const GLvoid *indices, CRClientState *c) +{ + int i; + GLubyte *p = (GLubyte *)indices; + + (void) end; + + if (count < 0) + { + crdlmWarning(__LINE__, __FILE__, GL_INVALID_VALUE, "DLM DrawRangeElements(negative count)"); + return; + } + + if (mode > GL_POLYGON) + { + crdlmWarning(__LINE__, __FILE__, GL_INVALID_ENUM, "DLM DrawRangeElements(bad mode)"); + return; + } + + if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT && type != GL_UNSIGNED_INT) + { + crdlmWarning(__LINE__, __FILE__, GL_INVALID_ENUM, "DLM DrawRangeElements(bad type)"); + return; + } + + crDLMCompileBegin(mode); + switch (type) + { + case GL_UNSIGNED_BYTE: + for (i=start; i<count; i++) + { + crDLMCompileArrayElement((GLint) *p++, c); + } + break; + case GL_UNSIGNED_SHORT: + for (i=start; i<count; i++) + { + crDLMCompileArrayElement((GLint) * (GLushort *) p, c); + p+=sizeof (GLushort); + } + break; + case GL_UNSIGNED_INT: + for (i=start; i<count; i++) + { + crDLMCompileArrayElement((GLint) * (GLuint *) p, c); + p+=sizeof (GLuint); + } + break; + default: + crError( "this can't happen: DLM DrawRangeElements" ); + break; + } + crDLMCompileEnd(); +} + +#ifdef CR_EXT_multi_draw_arrays +void DLM_APIENTRY crDLMCompileMultiDrawArraysEXT( GLenum mode, GLint *first, + GLsizei *count, GLsizei primcount, CRClientState *c) +{ + GLint i; + + for (i = 0; i < primcount; i++) { + if (count[i] > 0) { + crDLMCompileDrawArrays(mode, first[i], count[i], c); + } + } +} + + +void DLM_APIENTRY crDLMCompileMultiDrawElementsEXT( GLenum mode, const GLsizei *count, GLenum type, + const GLvoid **indices, GLsizei primcount, CRClientState *c) +{ + GLint i; + + for (i = 0; i < primcount; i++) { + if (count[i] > 0) { + crDLMCompileDrawElements(mode, count[i], type, indices[i], c); + } + } +} +#endif /* CR_EXT_multi_draw_arrays */ diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_checklist.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_checklist.c new file mode 100644 index 00000000..e631c02e --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_checklist.c @@ -0,0 +1,51 @@ +/* $Id: dlm_checklist.c $ */ +#include "cr_dlm.h" +#include "cr_mem.h" +#include "cr_pixeldata.h" +#include "cr_string.h" +#include "dlm.h" + +/***************************************************************************** + * These helper functions are used for GL functions that are listed in + * the APIspec.txt file as "checklist", meaning that sometimes they + * represent functions that can be stored in a display list, and sometimes + * they represent control functions that must be executed immediately. + * + * The calling SPU must use these check functions (or their equivalents) + * before asking the DLM to compile any elements of these types. + * They return nonzero (TRUE) if the element goes into a display list. + */ + +int DLM_APIENTRY crDLMCheckListTexImage1D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + return (target != GL_PROXY_TEXTURE_1D); +} + +int DLM_APIENTRY crDLMCheckListCompressedTexImage1DARB(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imagesize, const GLvoid *data) +{ + return (target != GL_PROXY_TEXTURE_1D); +} +int DLM_APIENTRY crDLMCheckListTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + return (target != GL_PROXY_TEXTURE_2D); +} + +int DLM_APIENTRY crDLMCheckListCompressedTexImage2DARB(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imagesize, const GLvoid *data) +{ + return (target != GL_PROXY_TEXTURE_2D); +} + +int DLM_APIENTRY crDLMCheckListTexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + return (target != GL_PROXY_TEXTURE_3D); +} + +int DLM_APIENTRY crDLMCheckListTexImage3DEXT(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels) +{ + return (target != GL_PROXY_TEXTURE_3D); +} + +int DLM_APIENTRY crDLMCheckListCompressedTexImage3DARB(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imagesize, const GLvoid *data) +{ + return (target != GL_PROXY_TEXTURE_3D); +} diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_error.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_error.c new file mode 100644 index 00000000..90590e76 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_error.c @@ -0,0 +1,56 @@ +/* $Id: dlm_error.c $ */ +#include <stdio.h> +#include <stdarg.h> +#include "chromium.h" +#include "cr_mem.h" +#include "dlm.h" +#include "cr_environment.h" +#include "cr_error.h" + +#define GLCLIENT_LIST_ALLOC 1024 + +void crdlmWarning( int line, char *file, GLenum error, char *format, ... ) +{ + char errstr[8096]; + va_list args; + + if (crGetenv("CR_DEBUG")) { + char *glerr; + va_start( args, format ); + vsprintf( errstr, format, args ); + va_end( args ); + + switch (error) { + case GL_NO_ERROR: + glerr = "GL_NO_ERROR"; + break; + case GL_INVALID_VALUE: + glerr = "GL_INVALID_VALUE"; + break; + case GL_INVALID_ENUM: + glerr = "GL_INVALID_ENUM"; + break; + case GL_INVALID_OPERATION: + glerr = "GL_INVALID_OPERATION"; + break; + case GL_STACK_OVERFLOW: + glerr = "GL_STACK_OVERFLOW"; + break; + case GL_STACK_UNDERFLOW: + glerr = "GL_STACK_UNDERFLOW"; + break; + case GL_OUT_OF_MEMORY: + glerr = "GL_OUT_OF_MEMORY"; + break; + case GL_TABLE_TOO_LARGE: + glerr = "GL_TABLE_TOO_LARGE"; + break; + default: + glerr = "unknown"; + break; + } + + crWarning( "DLM error in %s, line %d: %s: %s\n", + file, line, glerr, errstr ); + } +} diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_generated.py b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_generated.py new file mode 100755 index 00000000..e5468e2b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_generated.py @@ -0,0 +1,355 @@ +# $Id: dlm_generated.py $ +import sys, cPickle, re + +sys.path.append( "../glapi_parser" ) +import apiutil + +# A routine that can create call strings from instance names +def InstanceCallString( params ): + output = '' + for index in range(0,len(params)): + if index > 0: + output += ", " + if params[index][0] != '': + output += 'instance->' + params[index][0] + return output + +def GetPointerType(basetype): + words = basetype.split() + if words[0] == 'const': + words = words[1:] + if words[-1].endswith('*'): + words[-1] = words[-1][:-1].strip() + if words[-1] == '': + words = words[:-1] + if words[0] == 'void' or words[0] == 'GLvoid': + words[0] = 'int' + return ' '.join(words) + + +def GetPointerInfo(functionName): + # We'll keep track of all the parameters that require pointers. + # They'll require special handling later. + params = apiutil.Parameters(functionName) + pointers = [] + pointername='' + pointerarg='' + pointertype='' + pointersize=0 + pointercomment='' + + index = 0 + for (name, type, vecSize) in params: + # Watch out for the word "const" (which should be ignored) + # and for types that end in "*" (which are pointers and need + # special treatment) + words = type.split() + if words[-1].endswith('*'): + pointers.append(index) + index += 1 + + # If any argument was a pointer, we need a special pointer data + # array. The pointer data will be stored into this array, and + # references to the array will be generated as parameters. + if len(pointers) == 1: + index = pointers[0] + pointername = params[index][0] + pointerarg = pointername + 'Data' + pointertype = GetPointerType(params[index][1]) + pointersize = params[index][2] + if pointersize == 0: + pointersize = "special" + elif len(pointers) > 1: + pointerarg = 'data'; + pointertype = GetPointerType(params[pointers[0]][1]) + for index in range(1,len(pointers)): + if GetPointerType(params[pointers[index]][1]) != pointertype: + pointertype = 'GLvoid *' + + return (pointers,pointername,pointerarg,pointertype,pointersize,pointercomment) + +def wrap_struct(functionName): + params = apiutil.Parameters(functionName) + argstring = apiutil.MakeDeclarationString(params) + extendedArgstring = argstring + props = apiutil.Properties(functionName) + if "useclient" in props or "pixelstore" in props: + extendedArgstring += ", CRClientState *c" + + # We'll keep track of all the parameters that require pointers. + # They'll require special handling later. + (pointers, pointername, pointerarg, pointertype, pointersize, pointercomment) = GetPointerInfo(functionName) + + # Start writing the header + print 'struct instance%s {' % (functionName) + print ' DLMInstanceList *next;' + print ' DLMInstanceList *stateNext;' + print ' int cbInstance;' + print ' VBoxDLOpCode iVBoxOpCode;' + print ' void (DLM_APIENTRY *execute)(DLMInstanceList *instance, SPUDispatchTable *dispatchTable);' + for (name, type, vecSize) in params: + # Watch out for the word "const" (which should be ignored) + # and for types that end in "*" (which are pointers and need + # special treatment) + words = type.split() + if words[0] == 'const': + words = words[1:] + if words[0] != "void": + print ' %s %s;' % (' '.join(words), name) + + # If any argument was a pointer, we need a special pointer data + # array. The pointer data will be stored into this array, and + # references to the array will be generated as parameters. + if len(pointers) == 1: + if pointersize == None: + print " /* Oh no - pointer parameter %s found, but no pointer class specified and can't guess */" % pointername + else: + if pointersize == 'special': + print ' %s %s[1];%s' % (pointertype, pointerarg, pointercomment) + else: + print ' %s %s[%s];%s' % (pointertype, pointerarg, pointersize,pointercomment) + elif len(pointers) > 1: + print ' %s %s[1];%s' % (pointertype, pointerarg,pointercomment) + + print '};' + + # Pointers only happen with instances + if len(pointers) > 1 or (len(pointers) == 1 and pointersize == 'special'): + print 'int crdlm_pointers_%s(struct instance%s *instance, %s);' % (functionName, functionName, extendedArgstring) + + # See if the GL function must sometimes allow passthrough even + # if the display list is open + if "checklist" in apiutil.ChromiumProps(functionName): + print 'int crdlm_checklist_%s(%s);' % (functionName, argstring) + + return + +def wrap_execute(functionName): + + params = apiutil.Parameters(functionName) + (pointers, _, pointerarg, _, _, _) = GetPointerInfo(functionName) + + print 'static void execute%s(DLMInstanceList *x, SPUDispatchTable *dispatchTable)' % functionName + print '{' + if len(params) > 0: + print ' struct instance%s *instance = (struct instance%s *)x;' % (functionName, functionName) + + if len(pointers) == 1: + print ' instance->%s = instance->%s;' % (params[pointers[0]][0], pointerarg) + + print ' if (dispatchTable->%s != NULL)' % (functionName) + print ' dispatchTable->%s(%s);' % (functionName, InstanceCallString(params)) + print ' else' + print ' crWarning("DLM warning: execute%s called with NULL dispatch entry");' % (functionName) + print '}' + +# These code snippets isolate the code required to add a given instance +# to the display list correctly. They are used during generation, to +# generate correct code, and also to create useful utilities. +def AddInstanceToList(pad): + print '%s/* Add this instance to the current display list. */' % pad + print '%sinstance->next = NULL;' % pad + print '%sinstance->stateNext = NULL;' % pad + print '%sif (!state->currentListInfo->first) {' % pad + print '%s state->currentListInfo->first = (DLMInstanceList *)instance;' % pad + print '%s}' % pad + print '%selse {' % pad + print '%s state->currentListInfo->last->next = (DLMInstanceList *)instance;' % pad + print '%s}' % pad + print '%sstate->currentListInfo->last = (DLMInstanceList *)instance;' % pad + print '%sstate->currentListInfo->numInstances++;' % pad + +def AddInstanceToStateList(pad): + print '%s/* Instances that change state have to be added to the state list as well. */' % pad + print '%sif (!state->currentListInfo->stateFirst) {' % pad + print '%s state->currentListInfo->stateFirst = (DLMInstanceList *)instance;' % pad + print '%s}' % pad + print '%selse {' % pad + print '%s state->currentListInfo->stateLast->stateNext = (DLMInstanceList *)instance;' % pad + print '%s}' % pad + print '%sstate->currentListInfo->stateLast = (DLMInstanceList *)instance;' % pad + + +# The compile wrapper collects the parameters into a DLMInstanceList +# element, and adds that element to the end of the display list currently +# being compiled. +def wrap_compile(functionName): + params = apiutil.Parameters(functionName) + return_type = apiutil.ReturnType(functionName) + # Make sure the return type is void. It's nonsensical to compile + # an element with any other return type. + if return_type != 'void': + print '/* Nonsense: DL function %s has a %s return type?!? */' % (functionName, return_type) + + # Define a structure to hold all the parameters. Note that the + # top parameters must exactly match the DLMInstanceList structure + # in include/cr_dlm.h, or everything will break horribly. + # Start off by getting all the pointer info we could ever use + # from the parameters + (pointers, pointername, pointerarg, pointertype, pointersize, pointercomment) = GetPointerInfo(functionName) + + # Finally, the compile wrapper. This one will diverge strongly + # depending on whether or not there are pointer parameters. + callstring = apiutil.MakeCallString(params) + argstring = apiutil.MakeDeclarationString(params) + props = apiutil.Properties(functionName) + if "useclient" in props or "pixelstore" in props: + callstring += ", c" + argstring += ", CRClientState *c" + print 'void DLM_APIENTRY crDLMCompile%s(%s)' % (functionName, argstring) + print '{' + print ' CRDLMContextState *state = CURRENT_STATE();' + print ' struct instance%s *instance;' % (functionName) + + # The calling SPU is supposed to verify that the element is supposed to be + # compiled before it is actually compiled; typically, this is done based + # on whether a glNewList has been executed more recently than a glEndList. + # But some functions are dual-natured, sometimes being compiled, and sometimes + # being executed immediately. We can check for this here. + if "checklist" in apiutil.ChromiumProps(functionName): + print ' if (crDLMCheckList%s(%s))' % (functionName, apiutil.MakeCallString(params)) + print ' {' + print ' crdlm_error(__LINE__, __FILE__, GL_INVALID_OPERATION,' + print ' "this instance of function %s should not be compiled");' % functionName; + print ' return;' + print ' }' + + if len(pointers) > 1 or pointersize == 'special': + # Pass NULL, to just allocate space + print ' instance = crCalloc(sizeof(struct instance%s) + crdlm_pointers_%s(NULL, %s));' % (functionName, functionName, callstring) + else: + print ' instance = crCalloc(sizeof(struct instance%s));' % (functionName) + print ' if (!instance)' + print ' {' + print ' crdlm_error(__LINE__, __FILE__, GL_OUT_OF_MEMORY,' + print ' "out of memory adding %s to display list");' % (functionName) + print ' return;' + print ' }' + + # Put in the fields that must always exist + print ' instance->execute = execute%s;' % functionName + + # Apply all the simple (i.e. non-pointer) parameters + for index in range(len(params)): + if index not in pointers: + name = params[index][0] + print ' instance->%s = %s;' % (name, name) + + # We need to know instance size in bytes in order to save its state later. + print ' instance->cbInstance = sizeof(struct instance%s);' % functionName + + # Set OPCODE. + print ' instance->iVBoxOpCode = VBOX_DL_OPCODE_%s;' % functionName + + # If there's a pointer parameter, apply it. + if len(pointers) == 1: + + print ' if (%s == NULL)' % (params[pointers[0]][0]) + print ' instance->%s = NULL;' % (params[pointers[0]][0]) + print ' else' + print ' instance->%s = instance->%s;' % (params[pointers[0]][0], pointerarg) + + if pointersize == 'special': + print ' instance->cbInstance += crdlm_pointers_%s(instance, %s);' % (functionName, callstring) + else: + print ' crMemcpy((void *)instance->%s, (void *) %s, %s*sizeof(%s));' % (params[pointers[0]][0], params[pointers[0]][0], pointersize, pointertype) + elif len(pointers) == 2: + # this seems to work + print ' instance->cbInstance += crdlm_pointers_%s(instance, %s);' % (functionName, callstring) + elif len(pointers) > 2: + print "#error don't know how to handle pointer parameters for %s" % (functionName) + + # Add the element to the current display list + AddInstanceToList(' ') + # If the element is a state-changing element, add it to the current state list + if apiutil.SetsTrackedState(functionName): + AddInstanceToStateList(' ') + print '}' + +whichfile=sys.argv[1] +if whichfile == 'headers': + print """#ifndef _DLM_GENERATED_H +#define _DLM_GENERATED_H + +#include <VBoxUhgsmi.h> + +/* DO NOT EDIT. This file is auto-generated by dlm_generated.py. */ +""" +else: + print """#include <stdio.h> +#include "cr_spu.h" +#include "cr_dlm.h" +#include "cr_mem.h" +#include "cr_error.h" +#include "state/cr_statefuncs.h" +#include "dlm.h" +#include "dlm_pointers.h" +#include "dlm_generated.h" + +/* DO NOT EDIT. This file is auto-generated by dlm_generated.py. */ +""" + +# Add in the "add_to_dl" utility function, which will be used by +# external (i.e. non-generated) functions. The utility ensures that +# any external functions that are written for compiling elements +# don't have to be rewritten if the conventions for adding to display +# lists are changed. +print """ +void crdlm_add_to_list( + DLMInstanceList *instance, + void (*executeFunc)(DLMInstanceList *x, SPUDispatchTable *dispatchTable)""" + +if (whichfile == 'headers'): + print ");" +else: + print """) { + CRDLMContextState *state = CURRENT_STATE(); + instance->execute = executeFunc;""" + + # Add in the common code for adding the instance to the display list + AddInstanceToList(" ") + + print '}' + print '' + +# Now generate the functions that won't use the crdlm_add_to_list utility. +# These all directly add their own instances to the current display list +# themselves, without using the crdlm_add_to_list() function. +keys = apiutil.GetDispatchedFunctions(sys.argv[3]+"/APIspec.txt") +for func_name in keys: + if apiutil.CanCompile(func_name): + print "\n/*** %s ***/" % func_name + # Auto-generate an appropriate DL function. First, functions + # that go into the display list but that rely on state will + # have to have their argument strings expanded, to take pointers + # to that appropriate state. + if whichfile == "headers": + wrap_struct(func_name) + elif not apiutil.FindSpecial("dlm", func_name): + wrap_execute(func_name) + wrap_compile(func_name) + + +# Generate mapping between OPCODE and routines to be executed. + +if whichfile == "headers": + # Execute routine prototype needed to add static array of routines. + print '' + print 'struct DLMInstanceList;' + print 'typedef void (*VBoxDLMExecuteFn)(struct DLMInstanceList *instance, SPUDispatchTable *dispatchTable);' + print '' + print 'extern VBoxDLMExecuteFn g_VBoxDLMExecuteFns[VBOX_DL_OPCODE_MAX];' + print '' +else: + print '' + print 'VBoxDLMExecuteFn g_VBoxDLMExecuteFns[] = {' + + for func_name in keys: + if apiutil.CanCompile(func_name) and not apiutil.FindSpecial("dlm", func_name): + print ' execute%s,' % func_name + + print '};' + print '' + +if whichfile == 'headers': + print "#endif /* _DLM_GENERATED_H */" diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_header.py b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_header.py new file mode 100644 index 00000000..f68ccaa0 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_header.py @@ -0,0 +1,274 @@ +# $Id: dlm_header.py $ +import sys, cPickle, re, os + +sys.path.append( "../glapi_parser" ) +import apiutil + +# mode is "header" or "defs" +mode = sys.argv[1] + +keys = apiutil.GetDispatchedFunctions(sys.argv[3]+"/APIspec.txt") + +# Any new function implemented in the DLM has to have an entry added here. +# Each function has its return type, function name, and parameters provided. +# We'll use these to generate both a header file, and a definition file. +additionalFunctions = [ + ('CRDLM DLM_APIENTRY *', 'crDLMNewDLM', 'unsigned int configSize, const CRDLMConfig *config'), + ('CRDLMContextState DLM_APIENTRY *', 'crDLMNewContext', 'CRDLM *dlm'), + ('void DLM_APIENTRY', 'crDLMFreeContext', 'CRDLMContextState *state, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMUseDLM', 'CRDLM *dlm'), + ('void DLM_APIENTRY','crDLMFreeDLM', 'CRDLM *dlm, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMSetCurrentState', 'CRDLMContextState *state'), + ('CRDLMContextState DLM_APIENTRY *', 'crDLMGetCurrentState', 'void'), + ('void DLM_APIENTRY', 'crDLMSetupClientState', 'SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMRestoreClientState', 'CRClientState *clientState, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMSendAllDLMLists', 'CRDLM *dlm, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMSendAllLists', 'SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMSendDLMList', 'CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMSendList', 'unsigned long listIdentifier, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMReplayDLMList', 'CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMReplayList', 'unsigned long listIdentifier, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMReplayDLMListState', 'CRDLM *dlm, unsigned long listIdentifier, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMReplayListState', 'unsigned long listIdentifier, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMReplayDLMLists', 'CRDLM *dlm, GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMReplayLists', 'GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMReplayDLMListsState', 'CRDLM *dlm, GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMReplayListsState', 'GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable'), + ('CRDLMError DLM_APIENTRY', 'crDLMDeleteListContent', 'CRDLM *dlm, unsigned long listIdentifier'), + ('void DLM_APIENTRY', 'crDLMComputeBoundingBox', 'unsigned long listId'), + ('GLuint DLM_APIENTRY', 'crDLMGetCurrentList', 'void'), + ('GLenum DLM_APIENTRY', 'crDLMGetCurrentMode', 'void'), + ('void DLM_APIENTRY', 'crDLMErrorFunction', 'CRDLMErrorCallback callback'), + ('void DLM_APIENTRY', 'crDLMNewList', 'GLuint list, GLenum mode, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMEndList', 'SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMCallList', 'GLuint list, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMCallLists', 'GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMDeleteLists', 'GLuint list, GLsizei range, SPUDispatchTable *dispatchTable'), + ('void DLM_APIENTRY', 'crDLMListBase', 'GLuint base, SPUDispatchTable *dispatchTable'), + ('GLboolean DLM_APIENTRY', 'crDLMIsList', 'GLuint list, SPUDispatchTable *dispatchTable'), + ('GLuint DLM_APIENTRY', 'crDLMGenLists', 'GLsizei range, SPUDispatchTable *dispatchTable'), + ('int32_t DLM_APIENTRY', 'crDLMSaveState', 'CRDLM *dlm, PSSMHANDLE pSSM'), + ('bool DLM_APIENTRY', 'crDLMLoadState', 'CRDLM *dlm, PSSMHANDLE pSSM, SPUDispatchTable *dispatchTable'), + #('void DLM_APIENTRY', 'crDLMListSent', 'CRDLM *dlm, unsigned long listIdentifier'), + #('GLboolean DLM_APIENTRY', 'crDLMIsListSent', 'CRDLM *dlm, unsigned long listIdentifier'), + #('GLint DLM_APIENTRY', 'crDLMListSize', 'CRDLM *dlm, unsigned long listIdentifier'), +] + +if mode == 'header': + print """#ifndef CR_DLM_H + +/* DO NOT EDIT. This file is auto-generated by %s. */ +#define CR_DLM_H + +#if defined(WINDOWS) +#define DLM_APIENTRY +#else +#define DLM_APIENTRY +#endif + +#include "chromium.h" +#include "state/cr_client.h" +#include "cr_spu.h" +#include "cr_hash.h" +#include "cr_threads.h" +#include "cr_pack.h" +#ifdef CHROMIUM_THREADSAFE +#include "cr_threads.h" +#endif +#include <VBox/types.h> +""" % os.path.basename(sys.argv[0]) + + # Generate operation codes enum to be used for saving and restoring lists. + print "/* OpCodes codes enum to be used for saving and restoring lists. */" + print "typedef enum {" + + for func_name in keys: + if apiutil.CanCompile(func_name) and not apiutil.FindSpecial("dlm", func_name): + print " VBOX_DL_OPCODE_%s," % func_name + + print " VBOX_DL_OPCODE_MAX," + print "} VBoxDLOpCode;" + + print """ +/* 3D bounding box */ +typedef struct { + double xmin, xmax, ymin, ymax, zmin, zmax; +} CRDLMBounds; + +/* Indicates whether we're currently involved in playback or not */ +typedef enum { + CRDLM_IMMEDIATE = 0, + CRDLM_REPLAY_STATE_FUNCTIONS = 1, + CRDLM_REPLAY_ALL_FUNCTIONS = 2 +} CRDLMReplayState; + +/* This is enough information to hold an instance of a single function call. */ +typedef struct DLMInstanceList { + struct DLMInstanceList *next; + struct DLMInstanceList *stateNext; + int cbInstance; + VBoxDLOpCode iVBoxOpCode; /* This field name should not interfere w/ OpenGL function parameters names (for example w/ param 'opcode' for glLogicOp()). */ + void (*execute)(struct DLMInstanceList *instance, SPUDispatchTable *dispatchTable); +} DLMInstanceList; + +typedef struct { + DLMInstanceList *first, *last; + uint32_t numInstances; + DLMInstanceList *stateFirst, *stateLast; + GLuint hwid; +} DLMListInfo; + +typedef struct { + /* This holds all the display list information, hashed by list identifier. */ + CRHashTable *displayLists; + + /* This is a count of the number of contexts/users that are using + * this DLM. + */ + unsigned int userCount; + +#ifdef CHROMIUM_THREADSAFE + /* This mutex protects the displayLists hash table from simultaneous + * updates by multiple contexts. + */ + CRmutex dlMutex; + CRtsd tsdKey; +#endif + + /* Configuration information - see the CRDLMConfig structure below + * for details. + */ + unsigned int bufferSize; +} CRDLM; + +/* This structure holds thread-specific state. Each thread can be + * associated with one (and only one) context; and each context can + * be associated with one (and only one) DLM. Making things interesting, + * though, is that each DLM can be associated with multiple contexts. + * + * So the thread-specific data key is associated with each context, not + * with each DLM. Two different threads can, through two different + * contexts that share a single DLM, each have independent state and + * conditions. + */ + +typedef struct { + CRDLM *dlm; /* the DLM associated with this state */ + unsigned long currentListIdentifier; /* open display list */ + DLMListInfo *currentListInfo; /* open display list data */ + GLenum currentListMode; /* GL_COMPILE or GL_COMPILE_AND_EXECUTE */ + GLuint listBase; + +} CRDLMContextState; + +/* These additional structures are for passing information to and from the + * CRDLM interface routines. + */ +typedef struct { + /* The size, in bytes, that the packer will initially allocate for + * each new buffer. + */ +#define CRDLM_DEFAULT_BUFFERSIZE (1024*1024) + unsigned int bufferSize; /* this will be allocated for each buffer */ +} CRDLMConfig; + +/* Positive values match GL error values. + * 0 (GL_NO_ERROR) is returned for success + * Negative values are internal errors. + * Possible positive values (from GL/gl.h) are: + * GL_NO_ERROR (0x0) + * GL_INVALID_ENUM (0x0500) + * GL_INVALID_VALUE (0x0501) + * GL_INVALID_OPERATION (0x0502) + * GL_STACK_OVERFLOW (0x0503) + * GL_STACK_UNDERFLOW (0x0504) + * GL_OUT_OF_MEMORY (0x0505) + */ +typedef int CRDLMError; + +/* This error reported if there's no current state. The caller is responsible + * for appropriately allocating context state with crDLMNewContext(), and + * for making it current with crDLMMakeCurrent(). + */ +#define CRDLM_ERROR_STATE (-1) + + +typedef void (*CRDLMErrorCallback)(int line, const char *file, GLenum error, const char *info); + + +#ifdef __cplusplus +extern "C" { +#endif +""" +elif mode == 'defs': + apiutil.CopyrightDef() + print '''\t; DO NOT EDIT. This code is generated by %s. + +EXPORTS''' % os.path.basename(sys.argv[0]) +else: + raise "unknown generation mode '%s'" % mode + +# Generate the list of functions, starting with those coded into +# the module +for (returnValue, name, parameters) in additionalFunctions: + if mode == 'header': + print "extern %s %s(%s);" % (returnValue, name, parameters) + elif mode == 'defs': + print "%s" % name + +# Continue with functions that are auto-generated. + +if mode == 'header': + print + print "/* auto-generated compilation functions begin here */" + + + +for func_name in keys: + props = apiutil.Properties(func_name) + # We're interested in intercepting all calls that: + # - can be put into a display list (i.e. "not ("nolist" in props)") + # - change client-side state that affects saving DL elements (i.e. "setclient" in props) + + if apiutil.CanCompile(func_name): + params = apiutil.Parameters(func_name) + argstring = apiutil.MakeDeclarationString(params) + if "useclient" in props or "pixelstore" in props: + argstring = argstring + ", CRClientState *c" + + if mode == 'header': + print 'extern void DLM_APIENTRY crDLMCompile%s(%s);' % (func_name, argstring) + elif mode == 'defs': + print "crDLMCompile%s" % func_name + +# Next make declarations for all the checklist functions. +if mode == 'header': + print """ +/* auto-generated CheckList functions begin here. There is one for each + * function that has a dual nature: even when there's an active glNewList, + * sometimes they are compiled into the display list, and sometimes they + * are treated like a control function. The CheckList function will + * return TRUE if the function should really be compiled into a display + * list. The calling SPU is responsible for checking this; but the + * DLM will also print an error if it detects an invalid use. + */ +""" +elif mode == 'defs': + pass + +for func_name in keys: + if "checklist" in apiutil.ChromiumProps(func_name): + params = apiutil.Parameters(func_name) + argstring = apiutil.MakeDeclarationString(params) + if mode == 'header': + print 'int DLM_APIENTRY crDLMCheckList%s(%s);' % (func_name, argstring) + elif mode == 'defs': + print "crDLMCheckList%s" % func_name + +if mode == 'header': + print """ +#ifdef __cplusplus +} +#endif + +#endif /* CR_DLM_H */""" diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_lists.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_lists.c new file mode 100644 index 00000000..495819a1 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_lists.c @@ -0,0 +1,452 @@ +/* $Id: dlm_lists.c $ */ +/** @file + * Implementation of all the Display Lists related routines: + * + * glGenLists, glDeleteLists, glNewList, glEndList, glCallList, glCallLists, + * glListBase and glIsList. + * + * Provide OpenGL IDs mapping between host and guest. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <float.h> +#include "cr_dlm.h" +#include "cr_mem.h" +#include "dlm.h" + + +/** + * Destroy each list entry. + */ +static void crdlmFreeDisplayListElements(DLMInstanceList *instance) +{ + while (instance) + { + DLMInstanceList *nextInstance = instance->next; + crFree(instance); + instance = nextInstance; + } +} + + +/** + * A callback routine used when iterating over all + * available lists in order to remove them. + * + * NOTE: @param pParam2 might be NULL. + */ +void crdlmFreeDisplayListResourcesCb(void *pParm1, void *pParam2) +{ + DLMListInfo *pListInfo = (DLMListInfo *)pParm1; + SPUDispatchTable *dispatchTable = (SPUDispatchTable *)pParam2; + + if (pListInfo) + { + crdlmFreeDisplayListElements(pListInfo->first); + pListInfo->first = pListInfo->last = NULL; + + /* Free host OpenGL resources. */ + if (dispatchTable) + dispatchTable->DeleteLists(pListInfo->hwid, 1); + + crFree(pListInfo); + } +} + + +/** + * Generate host and guest IDs, setup IDs mapping between host and guest. + */ +GLuint DLM_APIENTRY crDLMGenLists(GLsizei range, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + GLuint idHostRangeStart = 0; + GLuint idGuestRangeStart = 0; + + crDebug("DLM: GenLists(%d) (DLM=%p).", range, listState ? listState->dlm : 0); + + if (listState) + { + idHostRangeStart = dispatchTable->GenLists(range); + if (idHostRangeStart > 0) + { + idGuestRangeStart = crHashtableAllocKeys(listState->dlm->displayLists, range); + if (idGuestRangeStart > 0) + { + GLuint i; + bool fSuccess = true; + + /* Now have successfully generated IDs range for host and guest. Let's make IDs association. */ + for (i = 0; i < (GLuint)range; i++) + { + DLMListInfo *pListInfo; + + pListInfo = (DLMListInfo *)crCalloc(sizeof(DLMListInfo)); + if (pListInfo) + { + crMemset(pListInfo, 0, sizeof(DLMListInfo)); + pListInfo->hwid = idHostRangeStart + i; + + /* Insert pre-initialized list data which contains IDs mapping into the hash. */ + crHashtableReplace(listState->dlm->displayLists, idGuestRangeStart + i, pListInfo, NULL); + } + else + { + fSuccess = false; + break; + } + } + + /* All structures allocated and initialized successfully. */ + if (fSuccess) + return idGuestRangeStart; + + /* Rollback some data was not allocated. */ + crDLMDeleteLists(idGuestRangeStart, range, NULL /* we do DeleteLists() later in this routine */ ); + } + else + crDebug("DLM: Can't allocate Display List IDs range for the guest."); + + dispatchTable->DeleteLists(idHostRangeStart, range); + } + else + crDebug("DLM: Can't allocate Display List IDs range on the host side."); + } + else + crDebug("DLM: GenLists(%u) called with no current state.", range); + + /* Can't reserve IDs range. */ + return 0; +} + + +/** + * Release host and guest IDs, free memory resources. + */ +void DLM_APIENTRY crDLMDeleteLists(GLuint list, GLsizei range, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + + crDebug("DLM: DeleteLists(%u, %d) (DLM=%p).", list, range, listState ? listState->dlm : 0); + + if (listState) + { + if (range >= 0) + { + int i; + + /* Free resources: host memory, host IDs and guest IDs. */ + DLM_LOCK(listState->dlm) + for (i = 0; i < range; i++) + crHashtableDeleteEx(listState->dlm->displayLists, list + i, crdlmFreeDisplayListResourcesCb, dispatchTable); + DLM_UNLOCK(listState->dlm) + } + else + crDebug("DLM: DeleteLists(%u, %d) not allowed.", list, range); + } + else + crDebug("DLM: DeleteLists(%u, %d) called with no current state.", list, range); +} + + +/** + * Start recording a list. + */ +void DLM_APIENTRY +crDLMNewList(GLuint list, GLenum mode, SPUDispatchTable *dispatchTable) +{ + DLMListInfo *listInfo; + CRDLMContextState *listState = CURRENT_STATE(); + + crDebug("DLM: NewList(%u, %u) (DLM=%p).", list, mode, listState ? listState->dlm : 0); + + if (listState) + { + /* Valid list ID should be > 0. */ + if (list > 0) + { + if (listState->currentListInfo == NULL) + { + listInfo = (DLMListInfo *)crHashtableSearch(listState->dlm->displayLists, list); + if (listInfo) + { + listInfo->first = listInfo->last = NULL; + listInfo->stateFirst = listInfo->stateLast = NULL; + + listInfo->numInstances = 0; + + listState->currentListInfo = listInfo; + listState->currentListIdentifier = list; + listState->currentListMode = mode; + + dispatchTable->NewList(listInfo->hwid, mode); + + crDebug("DLM: create new list with [guest, host] ID pair [%u, %u].", list, listInfo->hwid); + + return; + } + else + crDebug("DLM: Requested Display List %u was not previously reserved with glGenLists().", list); + } + else + crDebug("DLM: NewList called with display list %u while display list %u was already open.", list, listState->currentListIdentifier); + } + else + crDebug("DLM: NewList called with a list identifier of 0."); + } + else + crDebug("DLM: NewList(%u, %u) called with no current state.\n", list, mode); +} + + +/** + * Stop recording a list. + */ +void DLM_APIENTRY crDLMEndList(SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + + crDebug("DLM: EndList() (DLM=%p).", listState ? listState->dlm : 0); + + if (listState) + { + /* Check if list was ever started. */ + if (listState->currentListInfo) + { + /* reset the current state to show the list had been ended */ + listState->currentListIdentifier = 0; + listState->currentListInfo = NULL; + listState->currentListMode = GL_FALSE; + + dispatchTable->EndList(); + } + else + crDebug("DLM: glEndList() is assuming glNewList() was issued previously."); + } + else + crDebug("DLM: EndList called with no current state."); +} + + +/** + * Execute list on hardware and cach ethis call if we currently recording a list. + */ +void DLM_APIENTRY crDLMCallList(GLuint list, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + + //crDebug("DLM: CallList(%u).", list); + + if (listState) + { + DLMListInfo *listInfo; + + /* Add to calls cache if we recording a list. */ + if (listState->currentListInfo) + crDLMCompileCallList(list); + + /* Find hwid for list. + * We need to take into account listBase: + * - displayLists hash table contains absolute IDs, so we need to add offset in order to resolve guest ID; + * - we also need to substract from hwid in order to execute correct list. */ + listInfo = (DLMListInfo *)crHashtableSearch(listState->dlm->displayLists, list + listState->listBase); + if (listInfo) + dispatchTable->CallList(listInfo->hwid - listState->listBase); + else + crDebug("DLM: CallList(%u) issued for non-existent list.", list); + } + else + crDebug("DLM: CallList(%u) called with no current state.", list); +} + + +/* This routine translates guest Display List IDs in given format to host IDs + * and return resulting IDs as an array of elements of type GL_UNSIGNED_INT. + * It is based on TranslateListIDs() function from crserverlib/server_lists.c. */ +static bool +crDLMConvertListIDs(CRDLMContextState *pListState, GLsizei n, GLenum type, const GLvoid *aGuest, GLuint *aHost) +{ +#define CRDLM_HANDLE_CONVERSION_CASE(_type, _item) \ + { \ + const _type *src = (const _type *)aGuest; \ + for (i = 0; i < n; i++) \ + { \ + GLuint idGuest = (GLuint)(_item) + pListState->listBase; \ + pListInfo = (DLMListInfo *)crHashtableSearch(pListState->dlm->displayLists, idGuest); \ + if (pListInfo) \ + { \ + aHost[i] = pListInfo->hwid - pListState->listBase; \ + } \ + else \ + { \ + crDebug("DLM: CallLists() cannot resolve host list ID for guest ID %u.", idGuest); \ + fSuccess = false; \ + break; \ + } \ + } \ + } + + GLsizei i; + DLMListInfo *pListInfo; + bool fSuccess = true; + + switch (type) + { + case GL_UNSIGNED_BYTE: CRDLM_HANDLE_CONVERSION_CASE(GLubyte, src[i]); break; + case GL_BYTE: CRDLM_HANDLE_CONVERSION_CASE(GLbyte, src[i]); break; + case GL_UNSIGNED_SHORT: CRDLM_HANDLE_CONVERSION_CASE(GLushort, src[i]); break; + case GL_SHORT: CRDLM_HANDLE_CONVERSION_CASE(GLshort, src[i]); break; + case GL_UNSIGNED_INT: CRDLM_HANDLE_CONVERSION_CASE(GLuint, src[i]); break; + case GL_INT: CRDLM_HANDLE_CONVERSION_CASE(GLint, src[i]); break; + case GL_FLOAT: CRDLM_HANDLE_CONVERSION_CASE(GLfloat, src[i]); break; + + case GL_2_BYTES: + { + CRDLM_HANDLE_CONVERSION_CASE(GLubyte, src[i * 2 + 0] * 256 + + src[i * 2 + 1]); + break; + } + + case GL_3_BYTES: + { + CRDLM_HANDLE_CONVERSION_CASE(GLubyte, src[i * 3 + 0] * 256 * 256 + + src[i * 3 + 1] * 256 + + src[i * 3 + 2]); + break; + } + + case GL_4_BYTES: + { + CRDLM_HANDLE_CONVERSION_CASE(GLubyte, src[i * 4 + 0] * 256 * 256 * 256 + + src[i * 4 + 1] * 256 * 256 + + src[i * 4 + 2] * 256 + + src[i * 4 + 3]); + break; + } + + default: + crWarning("DLM: attempt to pass to crDLMCallLists() an unknown type: 0x%x.", type); + } + + return fSuccess; +#undef CRDLM_HANDLE_CONVERSION_CASE +} + + +/** + * Execute lists on hardware and cache this call if we currently recording a list. + */ +void DLM_APIENTRY crDLMCallLists(GLsizei n, GLenum type, const GLvoid *lists, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *pListState = CURRENT_STATE(); + + crDebug("DLM: CallLists(%d, %u, %p).", n, type, lists); + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint)) + { + crError("crDLMCallLists: parameter 'n' is out of range"); + return; + } + + if (pListState) + { + GLsizei i; + GLuint *aHostIDs; + + /* Add to calls cache if we recording a list. */ + if (pListState->currentListInfo) + crDLMCompileCallLists(n, type, lists); + + aHostIDs = (GLuint *)crAlloc(n * sizeof(GLuint)); + if (aHostIDs) + { + /* Convert IDs. Resulting array contains elements of type of GL_UNSIGNED_INT. */ + if (crDLMConvertListIDs(pListState, n, type, lists, aHostIDs)) + dispatchTable->CallLists(n, GL_UNSIGNED_INT, aHostIDs); + else + crDebug("DLM: CallLists() failed."); + + crFree(aHostIDs); + } + else + crDebug("DLM: no memory on CallLists()."); + } + else + crDebug("DLM: CallLists(%d, %u, %p) called with no current state.", n, type, lists); +} + + +/** + * Set list base, remember its value and add call to the cache. + */ +void DLM_APIENTRY crDLMListBase(GLuint base, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *pListState = CURRENT_STATE(); + + crDebug("DLM: ListBase(%u).", base); + + if (pListState) + { + pListState->listBase = base; + + /* Only add to cache if we are currently recording a list. */ + /** @todo Do we really need to chache it? */ + if (pListState->currentListInfo) + crDLMCompileListBase(base); + + dispatchTable->ListBase(base); + } + else + crDebug("DLM: ListBase(%u) called with no current state.", base); +} + + +/** + * Check if specified list ID belongs to valid Display List. + * Positive result is only returned in case both conditions below are satisfied: + * + * - given list found in DLM hash table (i.e., it was previously allocated + * with crDLMGenLists and still not released with crDLMDeleteLists); + * + * - list is valid on the host side. + */ +GLboolean DLM_APIENTRY crDLMIsList(GLuint list, SPUDispatchTable *dispatchTable) +{ + CRDLMContextState *listState = CURRENT_STATE(); + + crDebug("DLM: IsList(%u).", list); + + if (listState) + { + if (list > 0) + { + DLMListInfo *listInfo = (DLMListInfo *)crHashtableSearch(listState->dlm->displayLists, list); + if (listInfo) + { + if (dispatchTable->IsList(listInfo->hwid)) + return true; + else + crDebug("DLM: list [%u, %u] not found on the host side.", list, listInfo->hwid); + } + else + crDebug("DLM: list %u not found in guest cache.", list); + } + else + crDebug("DLM: IsList(%u) is not allowed.", list); + } + else + crDebug("DLM: IsList(%u) called with no current state.", list); + + return false; +} diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.c new file mode 100644 index 00000000..718ebdec --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.c @@ -0,0 +1,1125 @@ +/* $Id: dlm_pointers.c $ */ +#include "cr_dlm.h" +#include "cr_mem.h" +#include "cr_pixeldata.h" +#include "cr_string.h" +#include "dlm.h" +#include "dlm_pointers.h" + +/** + * These helper functions are used for GL functions that take a pointers, + * if the size of the arrays that the pointers refer to is not constant. + * These helper functions will determine, on a case-by-case basis, + * how much space is needed to store the array. If the buffer + * parameter is not NULL, they will also pack the data into the given + * array. + * + * Many of the functions included deal with pixel state (Bitmap, DrawPixels, + * etc.). In all these cases, when the function instance is stored in a + * display list, its data is read from memory (as per the parameters + * to PixelStore) and is stored in a tightly packed format (with no + * excess row length, no pixels skipped, no rows, skipped, and a byte + * alignment). + * + * When the instances are executed again, care must be taken to ensure + * that the PixelStore client state that unpacks them is set to reflect + * the tight packing actually used, instead of whatever the current + * client state indicates. + * + * So to do this, client PixelStore state is forced to known values + * before any instances in the display list are executed. The client + * state is then restored to known values afterwards. (The difficulty + * of this is somewhat mollified by the observation that PixelStore + * instances affect client state, and cannot be stored in a display list.) + * + */ + +int crdlm_pointers_Bitmap( struct instanceBitmap *instance, GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap, CRClientState *c) +{ + unsigned int size = ((int)((width + 7) / 8)) * height; + /* glBitmap can be called with a NULL size 0 bitmap, say for + * an empty glyph that only moves the current raster position. + * crMemcpy will raise an exception with a NULL source pointer, even if + * the size to copy is 0. So make sure we don't ram into this. + * Also, the bitmap isn't necessarily just sitting in memory; the PixelStore + * client-side state affects how it is read from memory. It's easiest to just + * use the utility. + */ + if (instance && size > 0) { + crBitmapCopy(width, height, instance->bitmap, bitmap, + &c->unpack); + } + + return size; +} + +int crdlm_pointers_DrawPixels( struct instanceDrawPixels *instance, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ) +{ + unsigned int size = crImageSize(format, type, width, height); + + if (instance && size > 0) { + crPixelCopy2D(width, height, + instance->pixels, format, type, NULL, + pixels, format, type, &c->unpack); + } + + return size; +} +int crdlm_pointers_Fogfv( struct instanceFogfv *instance, GLenum pname, const GLfloat *params ) +{ + unsigned int size = (pname == GL_FOG_COLOR?4:1)*sizeof(GLfloat); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_Fogiv( struct instanceFogiv *instance, GLenum pname, const GLint *params ) +{ + unsigned int size = (pname == GL_FOG_COLOR?4:1)*sizeof(GLint); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_LightModelfv( struct instanceLightModelfv *instance, GLenum pname, const GLfloat *params ) +{ + unsigned int size = (pname == GL_LIGHT_MODEL_AMBIENT?4:1)*sizeof(GLfloat); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_LightModeliv( struct instanceLightModeliv *instance, GLenum pname, const GLint *params ) +{ + unsigned int size = (pname == GL_LIGHT_MODEL_AMBIENT?4:1)*sizeof(GLfloat); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_Lightfv( struct instanceLightfv *instance, GLenum light, GLenum pname, const GLfloat *params ) +{ + unsigned int size; + switch(pname) { + case GL_AMBIENT: case GL_DIFFUSE: case GL_SPECULAR: case GL_POSITION: + size = 4 * sizeof(GLfloat); + break; + case GL_SPOT_DIRECTION: + size = 3 * sizeof(GLfloat); + break; + default: + size = 1 * sizeof(GLfloat); + break; + } + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_Lightiv( struct instanceLightiv *instance, GLenum light, GLenum pname, const GLint *params ) +{ + unsigned int size; + switch(pname) { + case GL_AMBIENT: case GL_DIFFUSE: case GL_SPECULAR: case GL_POSITION: + size = 4 * sizeof(GLint); + break; + case GL_SPOT_DIRECTION: + size = 3 * sizeof(GLint); + break; + default: + size = 1 * sizeof(GLint); + break; + } + if (instance) crMemcpy(instance->params, params, size); + return size; +} + +/* This utility routine returns the number of components per + * mapping point for all the glMap* functions. + */ +static int map_num_components(GLenum target) +{ + switch(target) { + case GL_MAP1_INDEX: case GL_MAP1_TEXTURE_COORD_1: + return 1; + case GL_MAP1_TEXTURE_COORD_2: + return 2; + case GL_MAP1_VERTEX_3: case GL_MAP1_NORMAL: + case GL_MAP1_TEXTURE_COORD_3: + return 3; + case GL_MAP1_VERTEX_4: case GL_MAP1_COLOR_4: + case GL_MAP1_TEXTURE_COORD_4: + return 4; + + case GL_MAP2_INDEX: case GL_MAP2_TEXTURE_COORD_1: + return 1; + case GL_MAP2_TEXTURE_COORD_2: + return 2; + case GL_MAP2_VERTEX_3: case GL_MAP2_NORMAL: + case GL_MAP2_TEXTURE_COORD_3: + return 3; + case GL_MAP2_VERTEX_4: case GL_MAP2_COLOR_4: + case GL_MAP2_TEXTURE_COORD_4: + return 4; + } + return 0; +} + + +int crdlm_pointers_Map1d( struct instanceMap1d *instance, GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points ) +{ + unsigned int numValues = map_num_components(target); + unsigned int size = order * numValues * sizeof(GLdouble); + if (instance) { + /* This one's a little different - we rearrange the order to + * compress it, and change the instance's stride value to + * match. + */ + const GLdouble *src = points; + GLdouble *dest = instance->points; + register int i; + for (i = 0; i < order; i++) { + crMemcpy(dest, src, numValues * sizeof(GLdouble)); + dest += numValues; + src += stride; + } + + /* We override the stride to show we've compressed the data */ + instance->stride = numValues; + } + return size; +} +int crdlm_pointers_Map1f( struct instanceMap1f *instance, GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points ) +{ + unsigned int numValues = map_num_components(target); + unsigned int size = order * numValues * sizeof(GLfloat); + if (instance) { + /* This one's a little different - we rearrange the order to + * compress it, and change the instance's stride value to + * match. + */ + const GLfloat *src = points; + GLfloat *dest = instance->points; + register int i; + for (i = 0; i < order; i++) { + crMemcpy(dest, src, numValues * sizeof(GLfloat)); + dest += numValues; + src += stride; + } + + /* We override the stride to show we've compressed the data */ + instance->stride = numValues; + } + return size; +} +int crdlm_pointers_Map2d( struct instanceMap2d *instance, GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points ) +{ + unsigned int numValues = map_num_components(target); + unsigned int size = uorder * vorder * numValues * sizeof(GLdouble); + if (instance) { + register int v, u; + const GLdouble *src = points; + GLdouble *dest = instance->points; + for (v = 0; v < vorder; v++) { + for (u = 0; u < uorder; u++) { + crMemcpy(dest, src, numValues * sizeof(GLdouble)); + dest += numValues; + src += ustride; + } + src += vstride - ustride*uorder; + } + /* We override the stride to show we've compressed the data */ + instance->ustride = numValues; + instance->vstride = ustride * uorder; + } + return size; +} +int crdlm_pointers_Map2f( struct instanceMap2f *instance, GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points ) +{ + unsigned int numValues = map_num_components(target); + unsigned int size = uorder * vorder * numValues * sizeof(GLfloat); + if (instance) { + register int v, u; + const GLfloat *src = points; + GLfloat *dest = instance->points; + for (v = 0; v < vorder; v++) { + for (u = 0; u < uorder; u++) { + crMemcpy(dest, src, numValues * sizeof(GLfloat)); + dest += numValues; + src += ustride; + } + src += vstride - ustride*uorder; + } + /* We override the stride to show we've compressed the data */ + instance->ustride = numValues; + instance->vstride = ustride * uorder; + } + return size; +} + +int crdlm_pointers_Materialfv(struct instanceMaterialfv *instance, GLenum face, GLenum pname, const GLfloat *params) +{ + unsigned int size = 0; + switch(pname) { + case GL_AMBIENT_AND_DIFFUSE: + size = 8 * sizeof(GLfloat); + break; + case GL_AMBIENT: + case GL_DIFFUSE: + case GL_SPECULAR: + case GL_EMISSION: + size = 4 * sizeof(GLfloat); + break; + case GL_SHININESS: + size = 1 * sizeof(GLfloat); + break; + case GL_COLOR_INDEXES: + size = 3 * sizeof(GLfloat); + break; + default: + break; + } + if (instance && size > 0) crMemcpy(instance->params, params, size); + return size; +} + +int crdlm_pointers_Materialiv(struct instanceMaterialiv *instance, GLenum face, GLenum pname, const GLint *params) +{ + unsigned int size = 0; + switch(pname) { + case GL_AMBIENT_AND_DIFFUSE: + size = 8 * sizeof(GLint); + break; + case GL_AMBIENT: + case GL_DIFFUSE: + case GL_SPECULAR: + case GL_EMISSION: + size = 4 * sizeof(GLint); + break; + case GL_SHININESS: + size = 1 * sizeof(GLint); + break; + case GL_COLOR_INDEXES: + size = 3 * sizeof(GLint); + break; + default: + break; + } + if (instance && size > 0) crMemcpy(instance->params, params, size); + return size; +} + +int crdlm_pointers_PixelMapfv( struct instancePixelMapfv *instance, GLenum map, GLsizei mapsize, const GLfloat *values ) +{ + unsigned int size = mapsize * sizeof(GLfloat); + if (instance && size > 0) crMemcpy(instance->values, values, size); + return size; +} +int crdlm_pointers_PixelMapuiv( struct instancePixelMapuiv *instance, GLenum map, GLsizei mapsize, const GLuint *values ) +{ + unsigned int size = mapsize * sizeof(GLuint); + if (instance && size > 0) crMemcpy(instance->values, values, size); + return size; +} +int crdlm_pointers_PixelMapusv( struct instancePixelMapusv *instance, GLenum map, GLsizei mapsize, const GLushort *values ) +{ + unsigned int size = mapsize * sizeof(GLushort); + if (instance && size > 0) crMemcpy(instance->values, values, size); + return size; +} + +int crdlm_pointers_PointParameterfvARB( struct instancePointParameterfvARB *instance, GLenum pname, const GLfloat *params) +{ + unsigned int size = 0; + switch(pname) { + case GL_POINT_DISTANCE_ATTENUATION_ARB: + size = 3 * sizeof(GLfloat); + break; + default: + size = 1 * sizeof(GLfloat); + break; + } + return size; +} + +int crdlm_pointers_PointParameteriv( struct instancePointParameteriv *instance, GLenum pname, const GLint *params) +{ + unsigned int size = 0; + switch(pname) { + case GL_POINT_DISTANCE_ATTENUATION_ARB: + size = 3 * sizeof(GLint); + break; + default: + size = 1 * sizeof(GLint); + break; + } + return size; +} + +int crdlm_pointers_TexEnvfv( struct instanceTexEnvfv *instance, GLenum target, GLenum pname, const GLfloat *params ) +{ + unsigned int size = (pname == GL_TEXTURE_ENV_COLOR?4:1)*sizeof(GLfloat); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_TexEnviv( struct instanceTexEnviv *instance, GLenum target, GLenum pname, const GLint *params ) +{ + unsigned int size = (pname == GL_TEXTURE_ENV_COLOR?4:1)*sizeof(GLint); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_TexGendv( struct instanceTexGendv *instance, GLenum coord, GLenum pname, const GLdouble *params ) +{ + unsigned int size = (pname == GL_OBJECT_PLANE||pname==GL_EYE_PLANE?4:1)*sizeof(GLdouble); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_TexGenfv( struct instanceTexGenfv *instance, GLenum coord, GLenum pname, const GLfloat *params ) +{ + unsigned int size = (pname == GL_OBJECT_PLANE||pname==GL_EYE_PLANE?4:1)*sizeof(GLfloat); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_TexGeniv( struct instanceTexGeniv *instance, GLenum coord, GLenum pname, const GLint *params ) +{ + unsigned int size = (pname == GL_OBJECT_PLANE||pname==GL_EYE_PLANE?4:1)*sizeof(GLint); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_TexImage1D( struct instanceTexImage1D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ) +{ + unsigned int size = crImageSize(format, type, width, 1); + + if (instance && size > 0) { + crPixelCopy1D(instance->pixels, format, type, + pixels, format, type, width, &c->unpack); + } + + return size; +} +int crdlm_pointers_CompressedTexImage1DARB(struct instanceCompressedTexImage1DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imagesize, const GLvoid *data) +{ + unsigned int size = imagesize; + + if (instance && size > 0) { + crMemcpy(instance->data, data, size); + } + + return size; +} + +int crdlm_pointers_TexImage2D( struct instanceTexImage2D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ) +{ + unsigned int size = crImageSize(format, type, width, height); + + if (instance && size > 0) { + crPixelCopy2D(width, height, + instance->pixels, format, type, NULL, + pixels, format, type, &c->unpack); + } + + return size; +} +int crdlm_pointers_CompressedTexImage2DARB(struct instanceCompressedTexImage2DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imagesize, const GLvoid *data) +{ + unsigned int size = imagesize; + + if (instance && size > 0) { + crMemcpy(instance->data, data, size); + } + + return size; +} + +int crdlm_pointers_TexImage3D( struct instanceTexImage3D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ) +{ + unsigned int size; + int is_distrib = ((type == GL_TRUE) || (type == GL_FALSE)); + + if (pixels == NULL) { + size = 0; + } + else if (is_distrib) { + size = crStrlen(pixels) + 1 + (type==GL_TRUE?width*height*3:0); + } + else { + size = crTextureSize(format, type, width, height, depth); + } + + if (instance && size > 0) { + if (is_distrib) { + crMemcpy(instance->pixels, pixels, size); + } + else { + crPixelCopy3D(width, height, depth, + instance->pixels, format, type, NULL, + pixels, format, type, &c->unpack); + } + } + + return size; +} +int crdlm_pointers_TexImage3DEXT( struct instanceTexImage3DEXT *instance, GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ) +{ + unsigned int size; + int is_distrib = ((type == GL_TRUE) || (type == GL_FALSE)); + + if (pixels == NULL) { + size = 0; + } + else if (is_distrib) { + size = crStrlen(pixels) + 1 + (type==GL_TRUE?width*height*3:0); + } + else { + size = crTextureSize(format, type, width, height, depth); + } + + if (instance && size > 0) { + if (is_distrib) { + crMemcpy(instance->pixels, pixels, size); + } + else { + crPixelCopy3D(width, height, depth, + instance->pixels, format, type, NULL, + pixels, format, type, &c->unpack); + } + } + + return size; +} + +int crdlm_pointers_CompressedTexImage3DARB(struct instanceCompressedTexImage3DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imagesize, const GLvoid *data) +{ + unsigned int size = imagesize; + + if (instance && size > 0) { + crMemcpy(instance->data, data, size); + } + + return size; +} + +int crdlm_pointers_TexParameterfv( struct instanceTexParameterfv *instance, GLenum target, GLenum pname, const GLfloat *params ) +{ + unsigned int size = (pname == GL_TEXTURE_BORDER_COLOR?4:1)*sizeof(GLfloat); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_TexParameteriv( struct instanceTexParameteriv *instance, GLenum target, GLenum pname, const GLint *params ) +{ + unsigned int size = (pname == GL_TEXTURE_BORDER_COLOR?4:1)*sizeof(GLfloat); + if (instance) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_TexSubImage1D( struct instanceTexSubImage1D *instance, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ) +{ + unsigned int size = crImageSize(format, type, width, 1); + + if (instance && size > 0) { + crPixelCopy1D(instance->pixels, format, type, + pixels, format, type, width, &c->unpack); + } + + return size; +} +int crdlm_pointers_TexSubImage2D( struct instanceTexSubImage2D *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ) +{ + unsigned int size = crImageSize(format, type, width, height); + + if (instance && size > 0) { + crPixelCopy2D(width, height, + instance->pixels, format, type, NULL, + pixels, format, type, &c->unpack); + } + + return size; +} +int crdlm_pointers_TexSubImage3D( struct instanceTexSubImage3D *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ) +{ + unsigned int size; + int is_distrib = ((type == GL_TRUE) || (type == GL_FALSE)); + + if (pixels == NULL) { + size = 0; + } + else if (is_distrib) { + size = crStrlen(pixels) + 1 + (type==GL_TRUE?width*height*3:0); + } + else { + size = crTextureSize(format, type, width, height, depth); + } + + if (instance && size > 0) { + if (is_distrib) { + crMemcpy(instance->pixels, pixels, size); + } + else { + crPixelCopy3D(width, height, depth, + instance->pixels, format, type, NULL, + pixels, format, type, &c->unpack); + } + } + + return size; +} + +int crdlm_pointers_CompressedTexSubImage1DARB(struct instanceCompressedTexSubImage1DARB *instance, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imagesize, const GLvoid *data) +{ + unsigned int size = imagesize; + + if (instance && size > 0) { + crMemcpy(instance->data, data, size); + } + + return size; +} + +int crdlm_pointers_CompressedTexSubImage2DARB(struct instanceCompressedTexSubImage2DARB *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imagesize, const GLvoid *data) +{ + unsigned int size = imagesize; + + if (instance && size > 0) { + crMemcpy(instance->data, data, size); + } + + return size; +} +int crdlm_pointers_CompressedTexSubImage3DARB(struct instanceCompressedTexSubImage3DARB *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imagesize, const GLvoid *data) +{ + unsigned int size = imagesize; + + if (instance && size > 0) { + crMemcpy(instance->data, data, size); + } + + return size; +} + +int crdlm_pointers_Rectdv(struct instanceRectdv *instance, const GLdouble *v1, const GLdouble *v2) +{ + unsigned int size = 4 * sizeof(GLdouble); + if (instance) { + instance->data[0] = v1[0]; + instance->data[1] = v1[1]; + instance->data[2] = v2[0]; + instance->data[3] = v2[1]; + instance->v1 = &instance->data[0]; + instance->v2 = &instance->data[2]; + } + return size; +} +int crdlm_pointers_Rectfv(struct instanceRectfv *instance, const GLfloat *v1, const GLfloat *v2) +{ + unsigned int size = 4 * sizeof(GLfloat); + if (instance) { + instance->data[0] = v1[0]; + instance->data[1] = v1[1]; + instance->data[2] = v2[0]; + instance->data[3] = v2[1]; + instance->v1 = &instance->data[0]; + instance->v2 = &instance->data[2]; + } + return size; +} +int crdlm_pointers_Rectiv(struct instanceRectiv *instance, const GLint *v1, const GLint *v2) +{ + unsigned int size = 4 * sizeof(GLint); + if (instance) { + instance->data[0] = v1[0]; + instance->data[1] = v1[1]; + instance->data[2] = v2[0]; + instance->data[3] = v2[1]; + instance->v1 = &instance->data[0]; + instance->v2 = &instance->data[2]; + } + return size; +} +int crdlm_pointers_Rectsv(struct instanceRectsv *instance, const GLshort *v1, const GLshort *v2) +{ + unsigned int size = 4 * sizeof(GLshort); + if (instance) { + instance->data[0] = v1[0]; + instance->data[1] = v1[1]; + instance->data[2] = v2[0]; + instance->data[3] = v2[1]; + instance->v1 = &instance->data[0]; + instance->v2 = &instance->data[2]; + } + return size; +} + +int crdlm_pointers_PrioritizeTextures(struct instancePrioritizeTextures *instance, GLsizei n, const GLuint *textures, const GLclampf *priorities) +{ + unsigned int size = n * (sizeof(GLuint) + sizeof(GLclampf)); + if (instance) { + instance->textures = (GLuint *)&instance->data[0]; + instance->priorities = (GLclampf *)(((char *)&instance->data[0]) + n * sizeof(GLuint)); + if (size > 0) { + crMemcpy(instance->textures, textures, n * sizeof(GLuint)); + crMemcpy(instance->priorities, priorities, n * sizeof(GLclampf)); + } + } + + return size; +} + +static int combiner_num_components(GLenum pname) +{ + switch(pname) { + case GL_CONSTANT_COLOR0_NV: + case GL_CONSTANT_COLOR1_NV: + return 4; + case GL_NUM_GENERAL_COMBINERS_NV: + case GL_COLOR_SUM_CLAMP_NV: + return 1; + } + return 0; +} +int crdlm_pointers_CombinerParameterivNV(struct instanceCombinerParameterivNV *instance, GLenum pname, const GLint *params) +{ + unsigned int size = combiner_num_components(pname) * sizeof(GLint); + if (instance && size > 0) crMemcpy(instance->params, params, size); + return size; +} +int crdlm_pointers_CombinerParameterfvNV(struct instanceCombinerParameterfvNV *instance, GLenum pname, const GLfloat *params) +{ + unsigned int size = combiner_num_components(pname) * sizeof(GLfloat); + if (instance && size > 0) crMemcpy(instance->params, params, size); + return size; +} + +static int combinerstage_num_components(GLenum pname) +{ + switch(pname) { + case GL_CONSTANT_COLOR0_NV: + case GL_CONSTANT_COLOR1_NV: + return 4; + } + return 0; +} +int crdlm_pointers_CombinerStageParameterfvNV(struct instanceCombinerStageParameterfvNV *instance, GLenum stage, GLenum pname, const GLfloat *params) +{ + unsigned int size = combinerstage_num_components(pname) * sizeof(GLfloat); + if (instance && size > 0) crMemcpy(instance->params, params, size); + return size; +} + +static int program_num_components(GLenum target) +{ + switch(target) { + case GL_VERTEX_STATE_PROGRAM_NV: + return 4; + } + return 0; +} +int crdlm_pointers_ExecuteProgramNV(struct instanceExecuteProgramNV *instance, GLenum target, GLuint id, const GLfloat *params) +{ + unsigned int size = program_num_components(target) * sizeof(GLfloat); + if (instance && size > 0) crMemcpy(instance->params, params, size); + return size; +} + +int crdlm_pointers_RequestResidentProgramsNV(struct instanceRequestResidentProgramsNV *instance, GLsizei n, const GLuint *ids) +{ + unsigned int size = 4*sizeof(GLuint); + if (instance && size > 0) crMemcpy(instance->ids, ids, size); + return size; +} + +int crdlm_pointers_LoadProgramNV(struct instanceLoadProgramNV *instance, GLenum target, GLuint id, GLsizei len, const GLubyte *program) +{ + unsigned int size = len*sizeof(GLubyte); + if (instance && size > 0) crMemcpy(instance->program, program, size); + return size; +} + +int crdlm_pointers_ProgramNamedParameter4dNV(struct instanceProgramNamedParameter4dNV *instance, GLuint id, GLsizei len, const GLubyte * name, GLdouble x, GLdouble y, GLdouble z, GLdouble w) +{ + unsigned int size = len * sizeof(GLubyte); + /* XXX */ + return size; +} + +int crdlm_pointers_ProgramNamedParameter4dvNV(struct instanceProgramNamedParameter4dvNV *instance, GLuint id, GLsizei len, const GLubyte * name, const GLdouble * v) +{ + unsigned int size = len * sizeof(GLubyte); + /* XXX */ + return size; +} + +int crdlm_pointers_ProgramNamedParameter4fNV(struct instanceProgramNamedParameter4fNV *instance, GLuint id, GLsizei len, const GLubyte * name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + unsigned int size = len * sizeof(GLubyte); + /* XXX */ + return size; +} + +int crdlm_pointers_ProgramNamedParameter4fvNV(struct instanceProgramNamedParameter4fvNV *instance, GLuint id, GLsizei len, const GLubyte * name, const GLfloat * v) +{ + unsigned int size = len * sizeof(GLubyte); + /* XXX */ + return size; +} + +int crdlm_pointers_ProgramStringARB(struct instanceProgramStringARB *instance, GLenum target, GLenum format, GLsizei len, const GLvoid * string) +{ + unsigned int size = len*sizeof(GLubyte); + if (instance && size > 0) crMemcpy(instance->string, string, size); + return size; +} + +int crdlm_pointers_CallLists(struct instanceCallLists *instance, GLsizei n, GLenum type, const GLvoid *lists ) +{ + unsigned int size; + switch (type) { + case GL_BYTE: + size = sizeof(GLbyte); + break; + case GL_UNSIGNED_BYTE: + size = sizeof(GLubyte); + break; + case GL_SHORT: + size = sizeof(GLshort); + break; + case GL_UNSIGNED_SHORT: + size = sizeof(GLushort); + break; + case GL_INT: + size = sizeof(GLint); + break; + case GL_UNSIGNED_INT: + size = sizeof(GLuint); + break; + case GL_FLOAT: + size = sizeof(GLfloat); + break; + case GL_2_BYTES: + size = 2 * sizeof(GLbyte); + break; + case GL_3_BYTES: + size = 3 * sizeof(GLbyte); + break; + case GL_4_BYTES: + size = 4 * sizeof(GLbyte); + break; + default: + size = 0; + } + size *= n; + if (instance && size > 0) crMemcpy(instance->lists, lists, size); + return size; +} + + +int crdlm_pointers_VertexAttribs1dvNV(struct instanceVertexAttribs1dvNV *instance, GLuint index, GLsizei n, const GLdouble *v) +{ + return 1 * n * sizeof(GLdouble); +} + +int crdlm_pointers_VertexAttribs1fvNV(struct instanceVertexAttribs1fvNV *instance, GLuint index, GLsizei n, const GLfloat *v) +{ + return 1 * n * sizeof(GLfloat); +} + +int crdlm_pointers_VertexAttribs1svNV(struct instanceVertexAttribs1svNV *instance, GLuint index, GLsizei n, const GLshort *v) +{ + return 1 * n * sizeof(GLshort); +} + +int crdlm_pointers_VertexAttribs2dvNV(struct instanceVertexAttribs2dvNV *instance, GLuint index, GLsizei n, const GLdouble *v) +{ + return 2 * n * sizeof(GLdouble); +} + +int crdlm_pointers_VertexAttribs2fvNV(struct instanceVertexAttribs2fvNV *instance, GLuint index, GLsizei n, const GLfloat *v) +{ + return 2 * n * sizeof(GLfloat); +} + +int crdlm_pointers_VertexAttribs2svNV(struct instanceVertexAttribs2svNV *instance, GLuint index, GLsizei n, const GLshort *v) +{ + return 2 * n * sizeof(GLshort); +} + +int crdlm_pointers_VertexAttribs3dvNV(struct instanceVertexAttribs3dvNV *instance, GLuint index, GLsizei n, const GLdouble *v) +{ + return 3 * n * sizeof(GLdouble); +} + +int crdlm_pointers_VertexAttribs3fvNV(struct instanceVertexAttribs3fvNV *instance, GLuint index, GLsizei n, const GLfloat *v) +{ + return 3 * n * sizeof(GLfloat); +} + +int crdlm_pointers_VertexAttribs3svNV(struct instanceVertexAttribs3svNV *instance, GLuint index, GLsizei n, const GLshort *v) +{ + return 3 * n * sizeof(GLshort); +} + +int crdlm_pointers_VertexAttribs4dvNV(struct instanceVertexAttribs4dvNV *instance, GLuint index, GLsizei n, const GLdouble *v) +{ + return 4 * n * sizeof(GLdouble); +} + +int crdlm_pointers_VertexAttribs4fvNV(struct instanceVertexAttribs4fvNV *instance, GLuint index, GLsizei n, const GLfloat *v) +{ + return 4 * n * sizeof(GLfloat); +} + +int crdlm_pointers_VertexAttribs4svNV(struct instanceVertexAttribs4svNV *instance, GLuint index, GLsizei n, const GLshort *v) +{ + return 4 * n * sizeof(GLshort); +} + +int crdlm_pointers_VertexAttribs4ubvNV(struct instanceVertexAttribs4ubvNV *instance, GLuint index, GLsizei n, const GLubyte *v) +{ + return 4 * n * sizeof(GLubyte); +} + +int crdlm_pointers_ZPixCR( struct instanceZPixCR *instance, GLsizei width, + GLsizei height, GLenum format, GLenum type, + GLenum ztype, GLint zparm, GLint length, + const GLvoid *pixels, CRClientState *c) +{ + unsigned int size = length; + if (instance && size > 0) { + crMemcpy(instance->pixels,pixels,length); + } + + return size; +} + + +/* + * Prototypes for functions below are auto-generated and definded at out/<os.arch>/<build type>/obj/VBoxOGLgen/dlm_generated.h. + * + * All non-pointer structure fields are already assifned to *instance in out/<os.arch>/<build type>/obj/VBoxOGLgen/dlm_generated.c. + * Here we need to specify the additional size which is required to store data from pointers. + * This size will be added to sizeof(*instance) when dlm_generated.c will dynamically allocate memory for it. Also, + * data from pointers shouls be copied to *instance in case if instance != NULL. Each of functions below is called + * twice from dlm_generated.c: + * - first time with instance = NULL in order to get actual size of data provided by pointer + * - the second time with valid instance in order to copy data into it. + */ + +int crdlm_pointers_BindAttribLocation(struct instanceBindAttribLocation *instance, GLuint program, GLuint index, const char * name) +{ + int cbExtraSpace = (name ? crStrlen(name) + 1 : 0); + if (instance && name && cbExtraSpace) + { + crMemcpy(instance->name, name, cbExtraSpace); + } + + return cbExtraSpace; +} + +int crdlm_pointers_DeleteFramebuffersEXT(struct instanceDeleteFramebuffersEXT *instance, GLsizei n, const GLuint * framebuffers) +{ + int cbExtraSpace = n * sizeof(GLuint); + + if (instance && framebuffers && cbExtraSpace) + crMemcpy(instance->framebuffers, framebuffers, cbExtraSpace); + + return cbExtraSpace; +} + +int crdlm_pointers_DeleteRenderbuffersEXT(struct instanceDeleteRenderbuffersEXT *instance, GLsizei n, const GLuint * renderbuffers) +{ + int cbExtraSpace = n * sizeof(GLuint); + + if (instance && renderbuffers && cbExtraSpace) + crMemcpy(instance->renderbuffers, renderbuffers, cbExtraSpace); + + return cbExtraSpace; +} + +int crdlm_pointers_DrawBuffers(struct instanceDrawBuffers *instance, GLsizei n, const GLenum* bufs) +{ + int cbExtraSpace = n * sizeof(GLenum); + + if (instance && bufs && cbExtraSpace) + crMemcpy(instance->bufs, bufs, cbExtraSpace); + + return cbExtraSpace; +} + +int crdlm_pointers_ShaderSource(struct instanceShaderSource *instance, GLuint shader, GLsizei count, const char ** string, const GLint * length) +{ + int cbExtraSpace = 0; + int cbStrings = 0; + int cbLenghts = 0; + int i; + + /* Calculate reported source code size. */ + if (length && count) + for (i = 0; i < count; i++) + cbStrings += length[i] + /* termination character */ 1; + + /* Calculate size of the rest of parameters. */ + cbLenghts = count * sizeof(GLint); + + /* Resulting size is a summ. */ + cbExtraSpace = cbStrings + cbLenghts; + + /* Copy data if requested. */ + if (instance) + { + if (string && *string && cbStrings) + crMemcpy(instance->string, *string, cbStrings); + if (length && cbLenghts) + crMemcpy(instance->length, length, cbLenghts); + } + + return cbExtraSpace; +} + +int crdlm_pointers_StringMarkerGREMEDY(struct instanceStringMarkerGREMEDY *instance, GLsizei len, const GLvoid* string) +{ + /* @param len assumed to indicate string lenght in bytes. No termination character assumed. */ + int cbExtraSpace = (string && len) ? len : 0; + + if (instance && string && cbExtraSpace) + crMemcpy(instance->string, string, cbExtraSpace); + + return cbExtraSpace; +} + +/* Simplify things a bit. Use this macro instead of copy/paste to similar functions. */ +#define _VBOX_crdlm_pointers_UniformX(_uniformType) \ + int cbExtraSpace = count * sizeof(_uniformType); \ + if (instance && cbExtraSpace && value) \ + crMemcpy(instance->value, value, cbExtraSpace); \ + return cbExtraSpace; + +int crdlm_pointers_Uniform1fv(struct instanceUniform1fv *instance, GLint location, GLsizei count, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformX(GLfloat); +} + +int crdlm_pointers_Uniform1iv(struct instanceUniform1iv *instance, GLint location, GLsizei count, const GLint * value) +{ + _VBOX_crdlm_pointers_UniformX(GLint); +} + +int crdlm_pointers_Uniform2fv(struct instanceUniform2fv *instance, GLint location, GLsizei count, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformX(GLfloat); +} + +int crdlm_pointers_Uniform2iv(struct instanceUniform2iv *instance, GLint location, GLsizei count, const GLint * value) +{ + _VBOX_crdlm_pointers_UniformX(GLint); +} + +int crdlm_pointers_Uniform3fv(struct instanceUniform3fv *instance, GLint location, GLsizei count, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformX(GLfloat); +} + +int crdlm_pointers_Uniform3iv(struct instanceUniform3iv *instance, GLint location, GLsizei count, const GLint * value) +{ + _VBOX_crdlm_pointers_UniformX(GLint); +} + +int crdlm_pointers_Uniform4fv(struct instanceUniform4fv *instance, GLint location, GLsizei count, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformX(GLfloat); +} + +int crdlm_pointers_Uniform4iv(struct instanceUniform4iv *instance, GLint location, GLsizei count, const GLint * value) +{ + _VBOX_crdlm_pointers_UniformX(GLint); +} + +#undef crdlm_pointers_Uniform4iv + +/* Now do the same for UniformMatrix. */ +#define _VBOX_crdlm_pointers_UniformMatrixX(_uniformMatrixType) \ + int cbExtraSpace = count * sizeof(_uniformMatrixType); \ + if (instance && value && cbExtraSpace) \ + crMemcpy(instance->value, value, cbExtraSpace); \ + return cbExtraSpace; + +int crdlm_pointers_UniformMatrix2fv(struct instanceUniformMatrix2fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformMatrixX(GLfloat); +} + +int crdlm_pointers_UniformMatrix2x3fv(struct instanceUniformMatrix2x3fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformMatrixX(GLfloat); +} + +int crdlm_pointers_UniformMatrix2x4fv(struct instanceUniformMatrix2x4fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformMatrixX(GLfloat); +} + +int crdlm_pointers_UniformMatrix3fv(struct instanceUniformMatrix3fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformMatrixX(GLfloat); +} + +int crdlm_pointers_UniformMatrix3x2fv(struct instanceUniformMatrix3x2fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformMatrixX(GLfloat); +} + +int crdlm_pointers_UniformMatrix3x4fv(struct instanceUniformMatrix3x4fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformMatrixX(GLfloat); +} + +int crdlm_pointers_UniformMatrix4fv(struct instanceUniformMatrix4fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformMatrixX(GLfloat); +} + +int crdlm_pointers_UniformMatrix4x2fv(struct instanceUniformMatrix4x2fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformMatrixX(GLfloat); +} + +int crdlm_pointers_UniformMatrix4x3fv(struct instanceUniformMatrix4x3fv *instance, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value) +{ + _VBOX_crdlm_pointers_UniformMatrixX(GLfloat); +} + +#undef _VBOX_crdlm_pointers_UniformMatrixX + +#if 0 +VBoxConCreate +VBoxCreateContext +VBoxPackSetInjectThread +VBoxPresentComposition +VBoxWindowCreate +#endif + +int crdlm_pointers_VBoxConCreate(struct instanceVBoxConCreate *instance, struct VBOXUHGSMI * pHgsmi) +{ + CRASSERT(0); + return 0; +} + +int crdlm_pointers_VBoxCreateContext(struct instanceVBoxCreateContext *instance, GLint con, const char * dpyName, GLint visual, GLint shareCtx) +{ + int cbExtraSpace = (dpyName ? crStrlen(dpyName) + 1 : 0); + + if (instance && dpyName && cbExtraSpace) + crMemcpy(instance->dpyName, dpyName, cbExtraSpace); + + return cbExtraSpace; +} + +int crdlm_pointers_VBoxPackSetInjectThread(struct instanceVBoxPackSetInjectThread *instance, struct VBOXUHGSMI * pHgsmi) +{ + CRASSERT(0); + return 0; +} + +int crdlm_pointers_VBoxPresentComposition(struct instanceVBoxPresentComposition *instance, GLint win, + const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY * pChangedEntry) +{ + CRASSERT(0); + return 0; +} + +int crdlm_pointers_VBoxWindowCreate(struct instanceVBoxWindowCreate *instance, GLint con, const char * dpyName, GLint visBits) +{ + int cbExtraSpace = (dpyName ? crStrlen(dpyName) + 1 : 0); + + if (instance && dpyName && cbExtraSpace) + crMemcpy(instance->dpyName, dpyName, cbExtraSpace); + + return cbExtraSpace; +} diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.h b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.h new file mode 100644 index 00000000..51ac2085 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_pointers.h @@ -0,0 +1,81 @@ +/* $Id: dlm_pointers.h $ */ +#include <VBoxUhgsmi.h> + +#include "cr_dlm.h" +#include "dlm_generated.h" + +#ifndef _DLM_POINTERS_H +#define _DLM_POINTERS_H + +extern int crdlm_pointers_Bitmap( struct instanceBitmap *instance, GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap, CRClientState *c); +extern int crdlm_pointers_DrawPixels( struct instanceDrawPixels *instance, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ); +extern int crdlm_pointers_Fogfv( struct instanceFogfv *instance, GLenum pname, const GLfloat *params ); +extern int crdlm_pointers_Fogiv( struct instanceFogiv *instance, GLenum pname, const GLint *params ); +extern int crdlm_pointers_LightModelfv( struct instanceLightModelfv *instance, GLenum pname, const GLfloat *params ); +extern int crdlm_pointers_LightModeliv( struct instanceLightModeliv *instance, GLenum pname, const GLint *params ); +extern int crdlm_pointers_Lightfv( struct instanceLightfv *instance, GLenum light, GLenum pname, const GLfloat *params ); +extern int crdlm_pointers_Lightiv( struct instanceLightiv *instance, GLenum light, GLenum pname, const GLint *params ); +extern int crdlm_pointers_Map1d( struct instanceMap1d *instance, GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points ); +extern int crdlm_pointers_Map1f( struct instanceMap1f *instance, GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points ); +extern int crdlm_pointers_Map2d( struct instanceMap2d *instance, GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points ); +extern int crdlm_pointers_Map2f( struct instanceMap2f *instance, GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points ); +extern int crdlm_pointers_Materialfv(struct instanceMaterialfv *instance, GLenum face, GLenum pname, const GLfloat *params); +extern int crdlm_pointers_Materialiv(struct instanceMaterialiv *instance, GLenum face, GLenum pname, const GLint *params); +extern int crdlm_pointers_PixelMapfv( struct instancePixelMapfv *instance, GLenum map, GLsizei mapsize, const GLfloat *values ); +extern int crdlm_pointers_PixelMapuiv( struct instancePixelMapuiv *instance, GLenum map, GLsizei mapsize, const GLuint *values ); +extern int crdlm_pointers_PixelMapusv( struct instancePixelMapusv *instance, GLenum map, GLsizei mapsize, const GLushort *values ); +extern int crdlm_pointers_PointParameterfvARB( struct instancePointParameterfvARB *instance, GLenum pname, const GLfloat *params); +extern int crdlm_pointers_PointParameteriv( struct instancePointParameteriv *instance, GLenum pname, const GLint *params); +extern int crdlm_pointers_TexEnvfv( struct instanceTexEnvfv *instance, GLenum target, GLenum pname, const GLfloat *params ); +extern int crdlm_pointers_TexEnviv( struct instanceTexEnviv *instance, GLenum target, GLenum pname, const GLint *params ); +extern int crdlm_pointers_TexGendv( struct instanceTexGendv *instance, GLenum coord, GLenum pname, const GLdouble *params ); +extern int crdlm_pointers_TexGenfv( struct instanceTexGenfv *instance, GLenum coord, GLenum pname, const GLfloat *params ); +extern int crdlm_pointers_TexGeniv( struct instanceTexGeniv *instance, GLenum coord, GLenum pname, const GLint *params ); +extern int crdlm_pointers_TexImage1D( struct instanceTexImage1D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ); +extern int crdlm_pointers_CompressedTexImage1DARB(struct instanceCompressedTexImage1DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imagesize, const GLvoid *data); +extern int crdlm_pointers_TexImage2D( struct instanceTexImage2D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ); +extern int crdlm_pointers_CompressedTexImage2DARB(struct instanceCompressedTexImage2DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imagesize, const GLvoid *data); +extern int crdlm_pointers_TexImage3D( struct instanceTexImage3D *instance, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ); +extern int crdlm_pointers_TexImage3DEXT( struct instanceTexImage3DEXT *instance, GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ); +extern int crdlm_pointers_CompressedTexImage3DARB(struct instanceCompressedTexImage3DARB *instance, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imagesize, const GLvoid *data); +extern int crdlm_pointers_TexParameterfv( struct instanceTexParameterfv *instance, GLenum target, GLenum pname, const GLfloat *params ); +extern int crdlm_pointers_TexParameteriv( struct instanceTexParameteriv *instance, GLenum target, GLenum pname, const GLint *params ); +extern int crdlm_pointers_TexSubImage1D( struct instanceTexSubImage1D *instance, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ); +extern int crdlm_pointers_TexSubImage2D( struct instanceTexSubImage2D *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ); +extern int crdlm_pointers_TexSubImage3D( struct instanceTexSubImage3D *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels, CRClientState *c ); +extern int crdlm_pointers_CompressedTexSubImage1DARB(struct instanceCompressedTexSubImage1DARB *instance, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imagesize, const GLvoid *data); +extern int crdlm_pointers_CompressedTexSubImage2DARB(struct instanceCompressedTexSubImage2DARB *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imagesize, const GLvoid *data); +extern int crdlm_pointers_CompressedTexSubImage3DARB(struct instanceCompressedTexSubImage3DARB *instance, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imagesize, const GLvoid *data); +extern int crdlm_pointers_Rectdv(struct instanceRectdv *instance, const GLdouble *v1, const GLdouble *v2); +extern int crdlm_pointers_Rectfv(struct instanceRectfv *instance, const GLfloat *v1, const GLfloat *v2); +extern int crdlm_pointers_Rectiv(struct instanceRectiv *instance, const GLint *v1, const GLint *v2); +extern int crdlm_pointers_Rectsv(struct instanceRectsv *instance, const GLshort *v1, const GLshort *v2); +extern int crdlm_pointers_PrioritizeTextures(struct instancePrioritizeTextures *instance, GLsizei n, const GLuint *textures, const GLclampf *priorities); +extern int crdlm_pointers_CombinerParameterivNV(struct instanceCombinerParameterivNV *instance, GLenum pname, const GLint *params); +extern int crdlm_pointers_CombinerParameterfvNV(struct instanceCombinerParameterfvNV *instance, GLenum pname, const GLfloat *params); +extern int crdlm_pointers_CombinerStageParameterfvNV(struct instanceCombinerStageParameterfvNV *instance, GLenum stage, GLenum pname, const GLfloat *params); +extern int crdlm_pointers_ExecuteProgramNV(struct instanceExecuteProgramNV *instance, GLenum target, GLuint id, const GLfloat *params); +extern int crdlm_pointers_RequestResidentProgramsNV(struct instanceRequestResidentProgramsNV *instance, GLsizei n, const GLuint *ids); +extern int crdlm_pointers_LoadProgramNV(struct instanceLoadProgramNV *instance, GLenum target, GLuint id, GLsizei len, const GLubyte *program); +extern int crdlm_pointers_ProgramNamedParameter4dNV(struct instanceProgramNamedParameter4dNV *instance, GLuint id, GLsizei len, const GLubyte * name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +extern int crdlm_pointers_ProgramNamedParameter4dvNV(struct instanceProgramNamedParameter4dvNV *instance, GLuint id, GLsizei len, const GLubyte * name, const GLdouble * v); +extern int crdlm_pointers_ProgramNamedParameter4fNV(struct instanceProgramNamedParameter4fNV *instance, GLuint id, GLsizei len, const GLubyte * name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +extern int crdlm_pointers_ProgramNamedParameter4fvNV(struct instanceProgramNamedParameter4fvNV *instance, GLuint id, GLsizei len, const GLubyte * name, const GLfloat * v); +extern int crdlm_pointers_ProgramStringARB(struct instanceProgramStringARB *instance, GLenum target, GLenum format, GLsizei len, const GLvoid * string); +extern int crdlm_pointers_CallLists(struct instanceCallLists *instance, GLsizei n, GLenum type, const GLvoid *lists ); +extern int crdlm_pointers_VertexAttribs1dvNV(struct instanceVertexAttribs1dvNV *instance, GLuint index, GLsizei n, const GLdouble *v); +extern int crdlm_pointers_VertexAttribs1fvNV(struct instanceVertexAttribs1fvNV *instance, GLuint index, GLsizei n, const GLfloat *v); +extern int crdlm_pointers_VertexAttribs1svNV(struct instanceVertexAttribs1svNV *instance, GLuint index, GLsizei n, const GLshort *v); +extern int crdlm_pointers_VertexAttribs2dvNV(struct instanceVertexAttribs2dvNV *instance, GLuint index, GLsizei n, const GLdouble *v); +extern int crdlm_pointers_VertexAttribs2fvNV(struct instanceVertexAttribs2fvNV *instance, GLuint index, GLsizei n, const GLfloat *v); +extern int crdlm_pointers_VertexAttribs2svNV(struct instanceVertexAttribs2svNV *instance, GLuint index, GLsizei n, const GLshort *v); +extern int crdlm_pointers_VertexAttribs3dvNV(struct instanceVertexAttribs3dvNV *instance, GLuint index, GLsizei n, const GLdouble *v); +extern int crdlm_pointers_VertexAttribs3fvNV(struct instanceVertexAttribs3fvNV *instance, GLuint index, GLsizei n, const GLfloat *v); +extern int crdlm_pointers_VertexAttribs3svNV(struct instanceVertexAttribs3svNV *instance, GLuint index, GLsizei n, const GLshort *v); +extern int crdlm_pointers_VertexAttribs4dvNV(struct instanceVertexAttribs4dvNV *instance, GLuint index, GLsizei n, const GLdouble *v); +extern int crdlm_pointers_VertexAttribs4fvNV(struct instanceVertexAttribs4fvNV *instance, GLuint index, GLsizei n, const GLfloat *v); +extern int crdlm_pointers_VertexAttribs4svNV(struct instanceVertexAttribs4svNV *instance, GLuint index, GLsizei n, const GLshort *v); +extern int crdlm_pointers_VertexAttribs4ubvNV(struct instanceVertexAttribs4ubvNV *instance, GLuint index, GLsizei n, const GLubyte *v); +extern int crdlm_pointers_ZPixCR( struct instanceZPixCR *instance, GLsizei width, GLsizei height, GLenum format, GLenum type, GLenum ztype, GLint zparm, GLint length, const GLvoid *pixels, CRClientState *c ); + +#endif diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_special b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_special new file mode 100644 index 00000000..bcbd643e --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_special @@ -0,0 +1,21 @@ +# dlm_arrays.c: these have to be expanded out into +# their components before being stored in a display list +ArrayElement +DrawArrays +DrawElements +DrawRangeElements +MultiDrawArraysEXT +MultiDrawElementsEXT + +# dlm_calllist.c: since the DLM can manage state stored +# inside display lists, we can manage state updates for +# these sorts of elements. +#CallList +#CallLists + +# Calls to be ignored. +#VBoxConCreate +#VBoxCreateContext +#VBoxPackSetInjectThread +#VBoxPresentComposition +#VBoxWindowCreate diff --git a/src/VBox/HostServices/SharedOpenGL/dlm/dlm_state.c b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_state.c new file mode 100644 index 00000000..341d62e6 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/dlm/dlm_state.c @@ -0,0 +1,280 @@ +/* $Id: dlm_state.c $ */ +/** @file + * Implementation of saving and restoring Display Lists. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include "cr_mem.h" +#include "cr_dlm.h" +#include "dlm.h" +#include "dlm_generated.h" + +#include "VBox/vmm/ssm.h" +#include <iprt/errcore.h> + + +typedef struct { + + PSSMHANDLE pSSM; + uint32_t err; + +} CRDLMSaveListsCbArg; + +static void crDLMSaveListsCb(unsigned long key, void *pData1, void *pData2) +{ + DLMListInfo *pListInfo = (DLMListInfo*)pData1; + CRDLMSaveListsCbArg *pArg = (CRDLMSaveListsCbArg *)pData2; + PSSMHANDLE pSSM = pArg->pSSM; + DLMInstanceList *pInstance = pListInfo->first; + uint32_t cInstanceCheck = 0; + int32_t rc; + + crDebug("Saving Display Lists: found ID=%u, numInstances=%d.", key, pListInfo->numInstances); + + /* Store Display List length. */ + rc = SSMR3PutU32(pSSM, pListInfo->numInstances); + if (RT_SUCCESS(rc)) + { + /* Store Display List (guest) ID. */ + rc = SSMR3PutU32(pSSM, (uint32_t)key); + if (RT_SUCCESS(rc)) + { + /* Store each Display List item one by one. */ + while (pInstance) + { + /* Let's count each list item and compare total number with pListInfo->numInstances. + * This is simple consistency check. */ + cInstanceCheck++; + + /* Store instance data size. */ + rc = SSMR3PutU32(pSSM, (uint32_t)pInstance->cbInstance); + if (RT_SUCCESS(rc)) + { + rc = SSMR3PutMem(pSSM, pInstance, pInstance->cbInstance); + if (RT_SUCCESS(rc)) + { + /* We just stored all we need. Let's move on to the next list element. */ + pInstance = pInstance->next; + continue; + } + } + + crError("Saving Display Lists: can't store data."); + + pArg->err = 1; + return; + } + + if (cInstanceCheck == pListInfo->numInstances) + return; + + crError("Saving Display Lists: list currupted."); + } + } + + pArg->err = 1; +} + +int32_t DLM_APIENTRY crDLMSaveState(CRDLM *dlm, PSSMHANDLE pSSM) +{ + uint32_t ui32; + int32_t rc; + + CRDLMSaveListsCbArg arg; + + arg.pSSM = pSSM; + arg.err = 0; + + /* Save number of Display Lists assigned to current DLM context. */ + ui32 = (uint32_t)crHashtableNumElements(dlm->displayLists); + rc = SSMR3PutU32(pSSM, ui32); AssertRCReturn(rc, rc); + + crHashtableWalk(dlm->displayLists, crDLMSaveListsCb, (void *)&arg); + + return arg.err == 0; +} + +static VBoxDLMExecuteFn crDLMGetExecuteRoutine(VBoxDLOpCode opcode) +{ + if (opcode < VBOX_DL_OPCODE_MAX) + return g_VBoxDLMExecuteFns[opcode]; + + crError("Restoring Display Lists: Invalid opcode %u.", opcode); + + return NULL; +} + +static bool +crDLMLoadListInstance(PSSMHANDLE pSSM, DLMListInfo *pListInfo, SPUDispatchTable *dispatchTable) +{ + uint32_t cbInstance = 0; + DLMInstanceList *pInstance; + int32_t rc; + + /* Get Display List item size. */ + rc = SSMR3GetU32(pSSM, &cbInstance); + if (RT_SUCCESS(rc)) + { + /* Allocate memory for the item, initialize it and put into the list. */ + pInstance = crCalloc(cbInstance); + if (pInstance) + { + crMemset(pInstance, 0, cbInstance); + + rc = SSMR3GetMem(pSSM, pInstance, cbInstance); AssertRCReturn(rc, rc); + if (RT_SUCCESS(rc)) + { + pInstance->execute = crDLMGetExecuteRoutine(pInstance->iVBoxOpCode); + if (pInstance->execute) + { + pInstance->execute(pInstance, dispatchTable); + + pInstance->next = NULL; + pInstance->stateNext = NULL; + pInstance->cbInstance = cbInstance; + + pListInfo->numInstances++; + + if (!pListInfo->first) + pListInfo->first = pInstance; + + if (pListInfo->last) + pListInfo->last->next = pInstance; + + pListInfo->last = pInstance; + + return true; + } + else + crError("Restoring Display Lists: unknown list item (opcode=%u).", pInstance->iVBoxOpCode); + } + else + crError("Restoring Display Lists: can't read list element size."); + } + else + crError("Restoring Display Lists: not enough memory, aborting."); + } + else + crError("Restoring Display Lists: saved state file might be corrupted."); + + return false; +} + +static bool +crDLMLoadList(CRDLM *dlm, PSSMHANDLE pSSM, SPUDispatchTable *dispatchTable) +{ + uint32_t cElements = 0; + uint32_t idList = 0; + uint32_t i; + int32_t rc; + + /* Restore Display List length. */ + rc = SSMR3GetU32(pSSM, &cElements); + if (RT_SUCCESS(rc)) + { + /* Restore Display List ID. */ + rc = SSMR3GetU32(pSSM, &idList); + if (RT_SUCCESS(rc)) + { + /* Initialize new list data and start recording it. */ + DLMListInfo *pListInfo; + + pListInfo = (DLMListInfo *)crCalloc(sizeof(DLMListInfo)); + if (pListInfo) + { + GLuint hwid; + + crMemset(pListInfo, 0, sizeof(DLMListInfo)); + + hwid = dispatchTable->GenLists(1); + if (hwid > 0) + { + bool fSuccess = true; + CRDLMContextState *pDLMContextState; + + pListInfo->numInstances = 0; + pListInfo->stateFirst = pListInfo->stateLast = NULL; + pListInfo->hwid = hwid; + + dispatchTable->NewList(hwid, GL_COMPILE); + + /* Fake list state in order to prevent expando SPU from double caching. */ + pDLMContextState = crDLMGetCurrentState(); + pDLMContextState->currentListMode = GL_FALSE; + + crDebug("Restoring Display Lists:\t%u elements to restore.", cElements); + + /* Iterate over list instances. */ + for (i = 0; i < cElements; i++) + { + fSuccess = crDLMLoadListInstance(pSSM, pListInfo, dispatchTable); + if (!fSuccess) + break; + } + + dispatchTable->EndList(); + + if (fSuccess) + { + /* Add list to cache. */ + crHashtableReplace(dlm->displayLists, idList, pListInfo, NULL); + return true; + } + else + crError("Restoring Display Lists: some elements could not be restored."); + } + else + crError("Restoring Display Lists: can't allocate hwid for list %u.", idList); + + crFree(pListInfo); + } + else + crError("Restoring Display Lists: can't allocate memory."); + } + else + crError("Restoring Display Lists: can't get list ID."); + } + else + crError("Restoring Display Lists: can't get number of elements in list."); + + return false; +} + + +bool DLM_APIENTRY +crDLMLoadState(CRDLM *dlm, PSSMHANDLE pSSM, SPUDispatchTable *dispatchTable) +{ + uint32_t cLists = 0; + uint32_t i; + int32_t rc; + bool fSuccess = true; + + /* Get number of Display Lists assigned to current DLM context. */ + rc = SSMR3GetU32(pSSM, &cLists); + if (RT_SUCCESS(rc)) + { + crDebug("Restoring Display Lists: %u lists to restore.", cLists); + + for (i = 0; i < cLists; i++) + { + fSuccess = crDLMLoadList(dlm, pSSM, dispatchTable); + if (!fSuccess) + break; + } + } + else + crError("Restoring Display Lists: can't get number of lists."); + + return fSuccess; +} diff --git a/src/VBox/HostServices/SharedOpenGL/expando/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/expando/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/expando/Makefile.kup diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expando.py b/src/VBox/HostServices/SharedOpenGL/expando/expando.py new file mode 100644 index 00000000..86c45950 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/expando/expando.py @@ -0,0 +1,91 @@ +# $Id: expando.py $ +# This script generates calls for display list compilation +# and state management. +import sys + +sys.path.append( "../../glapi_parser" ) +import apiutil + +apiutil.CopyrightC() + +print """ +/* DO NOT EDIT - THIS FILE AUTOMATICALLY GENERATED BY expando.py SCRIPT */ +#include <stdio.h> +#include "cr_error.h" +#include "cr_spu.h" +#include "cr_dlm.h" +#include "expandospu.h" +""" + +allFunctions = [] +generatedFunctions = [] + +for func_name in apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt"): + if apiutil.FindSpecial("expando", func_name): + allFunctions.append(func_name) + elif apiutil.CanCompile(func_name) or apiutil.SetsClientState(func_name): + generatedFunctions.append(func_name) + allFunctions.append(func_name) + +for func_name in generatedFunctions: + params = apiutil.Parameters(func_name) + return_type = apiutil.ReturnType(func_name) + basicCallString = apiutil.MakeCallString(params) + declarationString = apiutil.MakeDeclarationString(params) + dlmCallString = basicCallString + chromiumProps = apiutil.ChromiumProps(func_name) + + needClientState = 0 + if apiutil.UsesClientState(func_name): + dlmCallString = basicCallString + ", clientState" + needClientState = 1 + + needDL = 0 + if apiutil.CanCompile(func_name): + needDL = 1 + + print 'static %s EXPANDOSPU_APIENTRY expando%s(%s)' % ( return_type, func_name, declarationString) + print '{' + if needDL: + print '\tGLenum dlMode = crDLMGetCurrentMode();' + if needClientState: + print '\tCRContext *stateContext = crStateGetCurrent();' + print '\tCRClientState *clientState = NULL;' + print '\tif (stateContext != NULL) {' + print '\t\tclientState = &(stateContext->client);' + print '\t}' + + if needDL: + if "checklist" in chromiumProps: + print '\tif (dlMode != GL_FALSE && crDLMCheckList%s(%s)) {' % (func_name, basicCallString) + else: + print '\tif (dlMode != GL_FALSE) {' + print '\t\tcrDLMCompile%s(%s);' % (func_name, dlmCallString) + # If we're only compiling, return now. + print '\t\tif (dlMode == GL_COMPILE) return %s;' % '0' if return_type != "void" else "" + print '\t}' + + # If it gets this far, we're either just executing, or executing + # and compiling. Either way, pass the call to the super SPU, + # and to the state tracker (if appropriate; note that we only + # track client-side state, not all state). + if return_type != "void": + print '\t%s rc = expando_spu.super.%s(%s);' % (return_type, func_name, basicCallString) + else: + print '\texpando_spu.super.%s(%s);' % (func_name, basicCallString) + if apiutil.SetsClientState(func_name): + print '\tcrState%s(%s);' % (func_name, basicCallString) + + if return_type != "void": + print "\treturn rc;" + + print '}' + print '' + +# Generate the table of named functions. including all the static generated +# functions as well as the special functions. +print 'SPUNamedFunctionTable _cr_expando_table[] = {' +for func_name in allFunctions: + print '\t{ "%s", (SPUGenericFunction) expando%s },' % (func_name, func_name ) +print '\t{ NULL, NULL }' +print '};' diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expando_special b/src/VBox/HostServices/SharedOpenGL/expando/expando_special new file mode 100644 index 00000000..9a8cd569 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/expando/expando_special @@ -0,0 +1,18 @@ +CreateContext +DestroyContext +MakeCurrent +NewList +EndList +DeleteLists +GenLists +ListBase +IsList +CallList +CallLists + +# Calls to be ignored. +#VBoxConCreate +#VBoxCreateContext +#VBoxPackSetInjectThread +#VBoxPresentComposition +#VBoxWindowCreate diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expandospu.c b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.c new file mode 100644 index 00000000..8db885a5 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.c @@ -0,0 +1,182 @@ +/* $Id: expandospu.c $ */ +/** @file + * Implementation of routines which Expando SPU explicitly overrides. + */ + +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <stdio.h> +#include "cr_spu.h" +#include "cr_dlm.h" +#include "cr_mem.h" +#include "expandospu.h" + +extern GLint EXPANDOSPU_APIENTRY +expandoCreateContext(const char *displayName, GLint visBits, GLint shareCtx) +{ + ExpandoContextState *contextState; + + /* Allocate our own per-context record */ + contextState = crCalloc(sizeof(ExpandoContextState)); + if (contextState) + { + GLint contextId; + + /* Get an official context ID from our super */ + contextId = expando_spu.super.CreateContext(displayName, visBits, shareCtx); + + /* Supplement that with our DLM. In a more correct situation, we should + * see if we've been called through glXCreateContext, which has a parameter + * for sharing DLMs. We don't currently get that information, so for now + * give each context its own DLM. + */ + contextState->dlm = crDLMNewDLM(0, NULL); + if (contextState->dlm) + { + contextState->dlmContext = crDLMNewContext(contextState->dlm); + if (contextState->dlmContext) + { + /* The DLM needs us to use the state tracker to track client + * state, so we can compile client-state-using functions correctly. + */ + contextState->State = crStateCreateContext(NULL, visBits, NULL); + + /* Associate the Expando context with the user context. */ + crHashtableAdd(expando_spu.contextTable, contextId, (void *)contextState); + + crDebug("Expando SPU: created context %d (contextState=%p, contextState->dlm=%p, " + "contextState->dlmContext=%p, contextState->State=%p).", + contextId, contextState, contextState->dlm, contextState->dlmContext, contextState->State); + + return contextId; + } + else + crError("Expando SPU: can't allocate new DLM context."); + + crDLMFreeDLM(contextState->dlm, &expando_spu.super); + } + else + crError("Expando SPU: can't allocate new DLM."); + + crFree(contextState); + } + else + crError("Expando SPU: couldn't allocate per-context state"); + + return 0; +} + +void expando_free_context_state(void *data) +{ + ExpandoContextState *contextState = (ExpandoContextState *)data; + + crDebug("Expando SPU: destroying context internals: " + "contextState=%p, contextState->dlm=%p, contextState->dlmContext=%p, contextState->State=%p", + contextState, contextState->dlm, contextState->dlmContext, contextState->State); + + crDLMFreeContext(contextState->dlmContext, &expando_spu.super); + crDLMFreeDLM(contextState->dlm, &expando_spu.super); + crStateDestroyContext(contextState->State); + crFree(contextState); +} + +void EXPANDOSPU_APIENTRY +expandoDestroyContext(GLint contextId) +{ + crDebug("Expando SPU: destroy context %d.", contextId); + + /* Destroy our context information */ + crHashtableDelete(expando_spu.contextTable, contextId, expando_free_context_state); + + /* Pass along the destruction to our super. */ + expando_spu.super.DestroyContext(contextId); +} + +void EXPANDOSPU_APIENTRY +expandoMakeCurrent(GLint crWindow, GLint nativeWindow, GLint contextId) +{ + ExpandoContextState *expandoContextState; + + expando_spu.super.MakeCurrent(crWindow, nativeWindow, contextId); + + expandoContextState = crHashtableSearch(expando_spu.contextTable, contextId); + if (expandoContextState) + { + crDebug("Expando SPU: switch to context %d.", contextId); + + crDLMSetCurrentState(expandoContextState->dlmContext); + crStateMakeCurrent(expandoContextState->State); + } + else + { + crDebug("Expando SPU: can't switch to context %d: not found.", contextId); + + crDLMSetCurrentState(NULL); + crStateMakeCurrent(NULL); + } +} + +extern void EXPANDOSPU_APIENTRY +expandoNewList(GLuint list, GLenum mode) +{ + crDLMNewList(list, mode, &expando_spu.super); +} + +extern void EXPANDOSPU_APIENTRY +expandoEndList(void) +{ + crDLMEndList(&expando_spu.super); +} + +extern void EXPANDOSPU_APIENTRY +expandoDeleteLists(GLuint first, GLsizei range) +{ + crDLMDeleteLists(first, range, &expando_spu.super); +} + +extern GLuint EXPANDOSPU_APIENTRY +expandoGenLists(GLsizei range) +{ + return crDLMGenLists(range, &expando_spu.super); +} + +void EXPANDOSPU_APIENTRY +expandoListBase(GLuint base) +{ + crDLMListBase(base, &expando_spu.super); +} + +extern GLboolean EXPANDOSPU_APIENTRY +expandoIsList(GLuint list) +{ + return crDLMIsList(list, &expando_spu.super); +} + +extern void EXPANDOSPU_APIENTRY +expandoCallList(GLuint list) +{ + crDLMCallList(list, &expando_spu.super); +} + +extern void EXPANDOSPU_APIENTRY +expandoCallLists(GLsizei n, GLenum type, const GLvoid *lists) +{ + crDLMCallLists(n, type, lists, &expando_spu.super); +} diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expandospu.def b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.def new file mode 100644 index 00000000..9edc7163 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.def @@ -0,0 +1,6 @@ +; Copyright (c) 2001, Stanford University +; All rights reserved. +; +; See the file LICENSE.txt for information on redistributing this software. +EXPORTS +SPULoad diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expandospu.h b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.h new file mode 100644 index 00000000..dad5e120 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/expando/expandospu.h @@ -0,0 +1,69 @@ +/* $Id: expandospu.h $ */ +/* Copyright (c) 2001, Stanford University + * All rights reserved. + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#ifndef EXPANDO_SPU_H +#define EXPANDO_SPU_H + +#ifdef WINDOWS +#define EXPANDOSPU_APIENTRY __stdcall +#else +#define EXPANDOSPU_APIENTRY +#endif + +#include "cr_glstate.h" +#include "cr_spu.h" +#include "cr_server.h" +#include "cr_dlm.h" + +typedef struct { + int id; + int has_child; + SPUDispatchTable self, child, super; + CRServer *server; + + /* Expando-specific variables */ + CRHashTable *contextTable; +} ExpandoSPU; + +typedef struct { + /* Local copy of state, needed by DLM to compile client-side stuff. + * We only collect client-side state; we ignore all server-side + * state (we just don't need it). + */ + CRContext *State; + + /* The DLM, and the per-context state for a DLM. Right now, every + * context will have its own DLM; it's possible in OpenGL to share + * DLMs, but the Chromium interface doesn't allow it yet. + */ + CRDLM *dlm; + CRDLMContextState *dlmContext; +} ExpandoContextState; + +extern ExpandoSPU expando_spu; + +extern SPUNamedFunctionTable _cr_expando_table[]; + +extern SPUOptions expandoSPUOptions[]; + +extern void expandospuGatherConfiguration( void ); + +extern void expando_free_context_state(void *data); + +extern GLint EXPANDOSPU_APIENTRY expandoCreateContext(const char *displayName, GLint visBits, GLint shareCtx); +extern void EXPANDOSPU_APIENTRY expandoDestroyContext(GLint contextId); +extern void EXPANDOSPU_APIENTRY expandoMakeCurrent(GLint crWindow, GLint nativeWindow, GLint contextId); +extern void EXPANDOSPU_APIENTRY expandoNewList(GLuint list, GLenum mode); +extern void EXPANDOSPU_APIENTRY expandoEndList(void); +extern void EXPANDOSPU_APIENTRY expandoDeleteLists(GLuint first, GLsizei range); +extern GLuint EXPANDOSPU_APIENTRY expandoGenLists(GLsizei range); +extern void EXPANDOSPU_APIENTRY expandoListBase(GLuint base); +extern GLboolean EXPANDOSPU_APIENTRY expandoIsList(GLuint list); +extern void EXPANDOSPU_APIENTRY expandoCallList(GLuint list); +extern void EXPANDOSPU_APIENTRY expandoCallLists(GLsizei n, GLenum type, const GLvoid *lists); + +#endif /* EXPANDO_SPU_H */ diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expandospu_config.c b/src/VBox/HostServices/SharedOpenGL/expando/expandospu_config.c new file mode 100644 index 00000000..9843f486 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/expando/expandospu_config.c @@ -0,0 +1,48 @@ +/* $Id: expandospu_config.c $ */ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "expandospu.h" + +//#include "cr_mothership.h" +#include "cr_string.h" + +#include <stdio.h> + +static void __setDefaults( void ) +{ +} + +/* option, type, nr, default, min, max, title, callback + */ +SPUOptions expandoSPUOptions[] = { + { NULL, CR_BOOL, 0, NULL, NULL, NULL, NULL, NULL }, +}; + + +void expandospuGatherConfiguration( void ) +{ + CRConnection *conn; + + __setDefaults(); +#if 0 + /* Connect to the mothership and identify ourselves. */ + + conn = crMothershipConnect( ); + if (!conn) + { + /* The mothership isn't running. Some SPU's can recover gracefully, some + * should issue an error here. */ + crSPUSetDefaultParams( &expando_spu, expandoSPUOptions ); + return; + } + crMothershipIdentifySPU( conn, expando_spu.id ); + + crSPUGetMothershipParams( conn, &expando_spu, expandoSPUOptions ); + + crMothershipDisconnect( conn ); +#endif +} diff --git a/src/VBox/HostServices/SharedOpenGL/expando/expandospu_init.c b/src/VBox/HostServices/SharedOpenGL/expando/expandospu_init.c new file mode 100644 index 00000000..f49dffbb --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/expando/expandospu_init.c @@ -0,0 +1,289 @@ +/* $Id: expandospu_init.c $ */ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include <stdio.h> +#include "cr_spu.h" +#include "cr_dlm.h" +#include "cr_hash.h" +#include "cr_mem.h" +#include "expandospu.h" + +/* This magic number is used for SSM data consistency check. */ +#define VBOX_EXPANDOSPU_SSM_MAGIC 0x3d3d3d3d +/* Modify VBox Expando SPU SSM version if SSM data structure changed. */ +#define VBOX_EXPANDOSPU_SSM_VERSION_ONE 1 +#define VBOX_EXPANDOSPU_SSM_VERSION VBOX_EXPANDOSPU_SSM_VERSION_ONE + +ExpandoSPU expando_spu; + +static SPUFunctions expando_functions = { + NULL, /* CHILD COPY */ + NULL, /* DATA */ + _cr_expando_table /* THE ACTUAL FUNCTIONS */ +}; + +/* + * Structure of SSM data: + * + * <VBOX_EXPANDOSPU_SSM_MAGIC> + * <VBOX_EXPANDOSPU_SSM_VERSION> + * <Number of Expando SPU contexts> + * + * <Context ID> + * <CRDLMContextState structure> + * <DLM module data> + * + * <Next context...> + * + * <VBOX_EXPANDOSPU_SSM_MAGIC> + */ + +static void +expandoSPUSaveContextCb(unsigned long id, void *pData1, void *pData2) +{ + uint32_t ui32 = (uint32_t)id; + PSSMHANDLE pSSM = (PSSMHANDLE)pData2; + int32_t rc; + + ExpandoContextState *pExpandoContextState = (ExpandoContextState *)pData1; + CRDLMContextState dlmContextState; + + /* Save context ID. */ + rc = SSMR3PutU32(pSSM, ui32); AssertRCReturnVoid(rc); + + /* Save DLM context state. Clean fields which will not be valid on restore (->dlm and ->currentListInfo). + * We interested only in fields: currentListIdentifier, currentListMode and listBase. */ + crMemcpy(&dlmContextState, pExpandoContextState->dlmContext, sizeof(CRDLMContextState)); + dlmContextState.dlm = NULL; + dlmContextState.currentListInfo = NULL; + rc = SSMR3PutMem(pSSM, &dlmContextState, sizeof(CRDLMContextState)); AssertRCReturnVoid(rc); + + /* Delegate the rest of work to DLM module. */ + crDLMSaveState(pExpandoContextState->dlmContext->dlm, pSSM); +} + +static int +expandoSPUSaveState(void *pData) +{ + uint32_t magic = VBOX_EXPANDOSPU_SSM_MAGIC; + uint32_t version = VBOX_EXPANDOSPU_SSM_VERSION; + PSSMHANDLE pSSM = (PSSMHANDLE)pData; + int32_t rc; + uint32_t cStates; + + crDebug("Saving state of Expando SPU."); + + AssertReturn(pSSM, 1); + + /* Magic & version first. */ + rc = SSMR3PutU32(pSSM, magic); AssertRCReturn(rc, rc); + rc = SSMR3PutU32(pSSM, version); AssertRCReturn(rc, rc); + + /* Store number of Expando SPU contexts. */ + cStates = (uint32_t)crHashtableNumElements(expando_spu.contextTable); + rc = SSMR3PutU32(pSSM, cStates); AssertRCReturn(rc, rc); + + /* Walk over context table and store required data. */ + crHashtableWalk(expando_spu.contextTable, expandoSPUSaveContextCb, pSSM); + + /* Expando SPU and DLM data should end with magic (consistency check). */ + rc = SSMR3PutU32(pSSM, magic); AssertRCReturn(rc, rc); + + return 0; +} + +static int +expandoSPULoadState(void *pData) +{ + uint32_t magic = 0; + uint32_t version = 0; + PSSMHANDLE pSSM = (PSSMHANDLE)pData; + int32_t rc; + + crDebug("Loading state of Expando SPU."); + + AssertReturn(pSSM, 1); + + /* Check magic and version. */ + rc = SSMR3GetU32(pSSM, &magic); + AssertRCReturn(rc, rc); + + if (magic == VBOX_EXPANDOSPU_SSM_MAGIC) + { + rc = SSMR3GetU32(pSSM, &version); + AssertRCReturn(rc, rc); + + if (version >= VBOX_EXPANDOSPU_SSM_VERSION_ONE) + { + uint32_t cStates = 0; + uint32_t i; + bool fSuccess = false; + + CRDLMContextState *pCurrentDLMState; + CRContext *pCurrentCRState; + + /* Remember current state. */ + pCurrentDLMState = crDLMGetCurrentState(); + pCurrentCRState = crStateGetCurrent(); + + /* Restore number of Expando SPU contexts. */ + rc = SSMR3GetU32(pSSM, &cStates); + AssertRCReturn(rc, rc); + + /* Restore and update Expando SPU contexts one by one. */ + for (i = 0; i < cStates; i++) + { + uint32_t idContext = 0; + ExpandoContextState *pExpandoContextState; + + rc = SSMR3GetU32(pSSM, &idContext); + AssertRCReturn(rc, rc); + + /* Find context which was previously created by CR Server. */ + pExpandoContextState = crHashtableSearch(expando_spu.contextTable, idContext); + if (pExpandoContextState) + { + CRDLMContextState dlmContextState; + + /* Restore and update DLM context state. */ + rc = SSMR3GetMem(pSSM, &dlmContextState, sizeof(CRDLMContextState)); + if (RT_SUCCESS(rc)) + { + pExpandoContextState->dlmContext->currentListIdentifier = dlmContextState.currentListIdentifier; + pExpandoContextState->dlmContext->currentListMode = dlmContextState.currentListMode; + pExpandoContextState->dlmContext->listBase = dlmContextState.listBase; + + crDLMSetCurrentState(pExpandoContextState->dlmContext); + crStateMakeCurrent(pExpandoContextState->State); + + /* Delegate the rest of work to DLM module. */ + fSuccess = crDLMLoadState(pExpandoContextState->dlmContext->dlm, pSSM, &expando_spu.server->dispatch); + if (fSuccess) + { + continue; + } + else + { + crError("Expando SPU: stop restoring Display Lists."); + break; + } + } + else + { + crError("Expando SPU: unable to load state: state file structure error (1)."); + break; + } + } + else + { + crError("Expando SPU: unable to load state: no context ID %u found.", idContext); + break; + } + } + + /* Restore original state. */ + crDLMSetCurrentState(pCurrentDLMState); + crStateMakeCurrent(pCurrentCRState); + + if (fSuccess) + { + /* Expando SPU and DLM data should end with magic (consistency check). */ + magic = 0; + rc = SSMR3GetU32(pSSM, &magic); + if (RT_SUCCESS(rc)) + { + if (magic == VBOX_EXPANDOSPU_SSM_MAGIC) + { + crInfo("Expando SPU state loaded."); + return 0; + } + else + crError("Expando SPU: unable to load state: SSM data corrupted."); + } + else + crError("Expando SPU: unable to load state: state file structure error (2): no magic."); + } + else + crError("Expando SPU: unable to load state: some list(s) could not be restored."); + } + else + crError("Expando SPU: unable to load state: unexpected SSM version (0x%x).", version); + } + else + crError("Expando SPU: unable to load state: SSM data possibly corrupted."); + + return VERR_SSM_UNEXPECTED_DATA; +} + +static SPUFunctions * +expandoSPUInit(int id, SPU *child, SPU *self, unsigned int context_id, unsigned int num_contexts) +{ + + (void)self; + (void)context_id; + (void)num_contexts; + + expando_spu.id = id; + expando_spu.has_child = 0; + expando_spu.server = NULL; + + if (child) + { + crSPUInitDispatchTable(&(expando_spu.child)); + crSPUCopyDispatchTable(&(expando_spu.child), &(child->dispatch_table)); + expando_spu.has_child = 1; + } + + crSPUInitDispatchTable(&(expando_spu.super)); + crSPUCopyDispatchTable(&(expando_spu.super), &(self->superSPU->dispatch_table)); + expandospuGatherConfiguration(); + + /* Expando-specific initialization */ + expando_spu.contextTable = crAllocHashtable(); + + /* We'll be using the state tracker for each context */ + crStateInit(); + + /* Export optional interfaces for SPU save/restore. */ + self->dispatch_table.spu_save_state = expandoSPUSaveState; + self->dispatch_table.spu_load_state = expandoSPULoadState; + + return &expando_functions; +} + +static void +expandoSPUSelfDispatch(SPUDispatchTable *self) +{ + crSPUInitDispatchTable(&(expando_spu.self)); + crSPUCopyDispatchTable(&(expando_spu.self), self); + + expando_spu.server = (CRServer *)(self->server); +} + + +static int +expandoSPUCleanup(void) +{ + crFreeHashtable(expando_spu.contextTable, expando_free_context_state); + crStateDestroy(); + return 1; +} + +int +SPULoad(char **name, char **super, SPUInitFuncPtr *init, SPUSelfDispatchFuncPtr *self, + SPUCleanupFuncPtr *cleanup, SPUOptionsPtr *options, int *flags) +{ + *name = "expando"; + *super = "render"; + *init = expandoSPUInit; + *self = expandoSPUSelfDispatch; + *cleanup = expandoSPUCleanup; + *options = expandoSPUOptions; + *flags = (SPU_NO_PACKER|SPU_NOT_TERMINAL|SPU_MAX_SERVERS_ZERO); + + return 1; +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/render/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/Makefile.kup diff --git a/src/VBox/HostServices/SharedOpenGL/render/VBoxOGLrenderspu.rc b/src/VBox/HostServices/SharedOpenGL/render/VBoxOGLrenderspu.rc new file mode 100644 index 00000000..2cfbc4d9 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/VBoxOGLrenderspu.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxOGLrenderspu.rc $ */ +/** @file + * VBoxOGLrenderspu - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox crOpenGL ICD\0" + VALUE "InternalName", "VBoxOGLrenderspu\0" + VALUE "OriginalFilename", "VBoxOGLrenderspu.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/SharedOpenGL/render/render.def b/src/VBox/HostServices/SharedOpenGL/render/render.def new file mode 100644 index 00000000..870d7494 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/render.def @@ -0,0 +1,7 @@ +; Copyright (c) 2001, Stanford University +; All rights reserved. +; +; See the file LICENSE.txt for information on redistributing this software. +EXPORTS +SPULoad +renderspuSetWindowId diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu.c new file mode 100644 index 00000000..52a1dfca --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu.c @@ -0,0 +1,1960 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_environment.h" +#include "cr_string.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_spu.h" +#include "cr_environment.h" +#include "renderspu.h" +#include "cr_extstring.h" + +#include <iprt/asm.h> + +uint32_t renderspuContextRelease(ContextInfo *context); +uint32_t renderspuContextRetain(ContextInfo *context); + +static void +DoSync(void) +{ + CRMessage *in, out; + + out.header.type = CR_MESSAGE_OOB; + + if (render_spu.is_swap_master) + { + int a; + + for (a = 0; a < render_spu.num_swap_clients; a++) + { + crNetGetMessage( render_spu.swap_conns[a], &in ); + crNetFree( render_spu.swap_conns[a], in); + } + + for (a = 0; a < render_spu.num_swap_clients; a++) + crNetSend( render_spu.swap_conns[a], NULL, &out, sizeof(CRMessage)); + } + else + { + crNetSend( render_spu.swap_conns[0], NULL, &out, sizeof(CRMessage)); + + crNetGetMessage( render_spu.swap_conns[0], &in ); + crNetFree( render_spu.swap_conns[0], in); + } +} + + + +/* + * Visual functions + */ + +/** + * used for debugging and giving info to the user. + */ +void +renderspuMakeVisString( GLbitfield visAttribs, char *s ) +{ + s[0] = 0; + + if (visAttribs & CR_RGB_BIT) + crStrcat(s, "RGB"); + if (visAttribs & CR_ALPHA_BIT) + crStrcat(s, "A"); + if (visAttribs & CR_DOUBLE_BIT) + crStrcat(s, ", Doublebuffer"); + if (visAttribs & CR_STEREO_BIT) + crStrcat(s, ", Stereo"); + if (visAttribs & CR_DEPTH_BIT) + crStrcat(s, ", Z"); + if (visAttribs & CR_STENCIL_BIT) + crStrcat(s, ", Stencil"); + if (visAttribs & CR_ACCUM_BIT) + crStrcat(s, ", Accum"); + if (visAttribs & CR_MULTISAMPLE_BIT) + crStrcat(s, ", Multisample"); + if (visAttribs & CR_OVERLAY_BIT) + crStrcat(s, ", Overlay"); + if (visAttribs & CR_PBUFFER_BIT) + crStrcat(s, ", PBuffer"); +} + +GLboolean renderspuInitVisual(VisualInfo *pVisInfo, const char *displayName, GLbitfield visAttribs) +{ + pVisInfo->displayName = crStrdup(displayName); + pVisInfo->visAttribs = visAttribs; + return renderspu_SystemInitVisual(pVisInfo); +} + +/* + * Find a VisualInfo which matches the given display name and attribute + * bitmask, or return a pointer to a new visual. + */ +VisualInfo * +renderspuFindVisual(const char *displayName, GLbitfield visAttribs) +{ + int i; + + if (!displayName) + displayName = ""; + + /* first, try to find a match */ +#if defined(WINDOWS) || defined(DARWIN) + for (i = 0; i < render_spu.numVisuals; i++) { + if (visAttribs == render_spu.visuals[i].visAttribs) { + return &(render_spu.visuals[i]); + } + } +#elif defined(GLX) + for (i = 0; i < render_spu.numVisuals; i++) { + if (crStrcmp(displayName, render_spu.visuals[i].displayName) == 0 + && visAttribs == render_spu.visuals[i].visAttribs) { + return &(render_spu.visuals[i]); + } + } +#endif + + if (render_spu.numVisuals >= MAX_VISUALS) + { + crWarning("Render SPU: Couldn't create a visual, too many visuals already"); + return NULL; + } + + /* create a new visual */ + i = render_spu.numVisuals; + if (renderspuInitVisual(&(render_spu.visuals[i]), displayName, visAttribs)) { + render_spu.numVisuals++; + return &(render_spu.visuals[i]); + } + else { + crWarning("Render SPU: Couldn't get a visual, renderspu_SystemInitVisual failed"); + return NULL; + } +} + +static ContextInfo * renderspuCreateContextInternal(const char *dpyName, GLint visBits, GLint idCtx, ContextInfo * sharedContext) +{ + ContextInfo *context; + VisualInfo *visual; + + if (idCtx <= 0) + { + idCtx = (GLint)crHashtableAllocKeys(render_spu.contextTable, 1); + if (idCtx <= 0) + { + crWarning("failed to allocate context id"); + return NULL; + } + } + else + { + if (crHashtableIsKeyUsed(render_spu.contextTable, idCtx)) + { + crWarning("the specified ctx key %d is in use", idCtx); + return NULL; + } + } + + + if (!dpyName || crStrlen(render_spu.display_string)>0) + dpyName = render_spu.display_string; + + visual = renderspuFindVisual(dpyName, visBits); + if (!visual) + return NULL; + + context = (ContextInfo *) crCalloc(sizeof(ContextInfo)); + if (!context) + return NULL; + context->BltInfo.Base.id = idCtx; + context->shared = sharedContext; + if (!renderspu_SystemCreateContext(visual, context, sharedContext)) + return NULL; + + crHashtableAdd(render_spu.contextTable, idCtx, context); + + context->BltInfo.Base.visualBits = visual->visAttribs; + /* + crDebug("Render SPU: CreateContext(%s, 0x%x) returning %d", + dpyName, visBits, context->BltInfo.Base.id); + */ + + if (sharedContext) + renderspuContextRetain(sharedContext); + + context->cRefs = 1; + + return context; +} + +GLint renderspuCreateContextEx(const char *dpyName, GLint visBits, GLint id, GLint shareCtx) +{ + ContextInfo *context, *sharedContext = NULL; + + if (shareCtx) { + sharedContext + = (ContextInfo *) crHashtableSearch(render_spu.contextTable, shareCtx); + CRASSERT(sharedContext); + } + + context = renderspuCreateContextInternal(dpyName, visBits, id, sharedContext); + if (context) + return context->BltInfo.Base.id; + return -1; +} + +/* + * Context functions + */ + +GLint RENDER_APIENTRY +renderspuCreateContext(const char *dpyName, GLint visBits, GLint shareCtx) +{ + return renderspuCreateContextEx(dpyName, visBits, 0, shareCtx); +} + +static void renderspuDestroyContextTerminate( ContextInfo *context ) +{ + CRASSERT(context->BltInfo.Base.id == -1); + renderspu_SystemDestroyContext( context ); + if (context->extensionString) { + crFree(context->extensionString); + context->extensionString = NULL; + } + + if (context->shared) + renderspuContextRelease( context->shared ); + + crFree(context); +} + +uint32_t renderspuContextRetain( ContextInfo *context ) +{ + Assert(context->cRefs); + return ASMAtomicIncU32(&context->cRefs); +} + +uint32_t renderspuContextRelease( ContextInfo *context ) +{ + uint32_t cRefs = ASMAtomicDecU32(&context->cRefs); + if (!cRefs) + renderspuDestroyContextTerminate( context ); + else + CRASSERT(cRefs < UINT32_MAX/2); + return cRefs; +} + +uint32_t renderspuContextMarkDeletedAndRelease( ContextInfo *context ) +{ + /* invalidate the context id to mark it as deleted */ + context->BltInfo.Base.id = -1; + + /* some drivers do not like when the base (shared) context is deleted before its referals, + * this is why we keep a context refference counting the base (shared) context will be destroyed as soon as*/ + return renderspuContextRelease( context ); +} + +ContextInfo * renderspuDefaultSharedContextAcquire() +{ + ContextInfo * pCtx = render_spu.defaultSharedContext; + if (!pCtx) + return NULL; + + renderspuContextRetain(pCtx); + return pCtx; +} + +void renderspuDefaultSharedContextRelease(ContextInfo * pCtx) +{ + renderspuContextRelease(pCtx); +} + + +static void RENDER_APIENTRY +renderspuDestroyContext( GLint ctx ) +{ + ContextInfo *context, *curCtx; + + CRASSERT(ctx); + + if (ctx == CR_RENDER_DEFAULT_CONTEXT_ID) + { + crWarning("request to destroy a default context, ignoring"); + return; + } + + context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx); + + if (!context) + { + crWarning("request to delete inexistent context"); + return; + } + + if (render_spu.defaultSharedContext == context) + { + renderspuSetDefaultSharedContext(NULL); + } + + curCtx = GET_CONTEXT_VAL(); +// CRASSERT(curCtx); + if (curCtx == context) + { + renderspuMakeCurrent( CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID ); + curCtx = GET_CONTEXT_VAL(); + Assert(curCtx); + Assert(curCtx != context); + } + + crHashtableDelete(render_spu.contextTable, ctx, NULL); + + renderspuContextMarkDeletedAndRelease(context); +} + +WindowInfo* renderspuWinCreate(GLint visBits, GLint id) +{ + WindowInfo* window = (WindowInfo *)crAlloc(sizeof (*window)); + if (!window) + { + crWarning("crAlloc failed"); + return NULL; + } + + if (!renderspuWinInit(window, NULL, visBits, id)) + { + crWarning("renderspuWinInit failed"); + crFree(window); + return NULL; + } + + return window; +} + +void renderspuWinTermOnShutdown(WindowInfo *window) +{ + renderspuVBoxCompositorSet(window, NULL); + renderspuVBoxPresentBlitterCleanup(window); + window->BltInfo.Base.id = -1; + renderspu_SystemDestroyWindow( window ); +} + +static void renderspuCheckCurrentCtxWindowCB(unsigned long key, void *data1, void *data2) +{ + ContextInfo *pCtx = (ContextInfo *) data1; + WindowInfo *pWindow = data2; + (void) key; + + if (pCtx->currentWindow==pWindow) + { + WindowInfo* pDummy = renderspuGetDummyWindow(pCtx->BltInfo.Base.visualBits); + if (pDummy) + { + renderspuPerformMakeCurrent(pDummy, 0, pCtx); + } + else + { + crWarning("failed to get dummy window"); + renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, pCtx->BltInfo.Base.id); + } + } +} + +void renderspuWinTerm( WindowInfo *window ) +{ + if (!renderspuWinIsTermed(window)) + { + + GET_CONTEXT(pOldCtx); + WindowInfo * pOldWindow = pOldCtx ? pOldCtx->currentWindow : NULL; + CRASSERT(!pOldCtx == !pOldWindow); + /* ensure no concurrent draws can take place */ + renderspuWinTermOnShutdown(window); + /* check if this window is bound to some ctx. Note: window pointer is already freed here */ + crHashtableWalk(render_spu.contextTable, renderspuCheckCurrentCtxWindowCB, window); + /* restore current context */ + { + GET_CONTEXT(pNewCtx); + WindowInfo * pNewWindow = pNewCtx ? pNewCtx->currentWindow : NULL; + CRASSERT(!pNewCtx == !pNewWindow); + + if (pOldWindow == window) + renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); + else if (pNewCtx != pOldCtx || pOldWindow != pNewWindow) + { + if (pOldCtx) + renderspuPerformMakeCurrent(pOldWindow, 0, pOldCtx); + else + renderspuMakeCurrent(CR_RENDER_DEFAULT_WINDOW_ID, 0, CR_RENDER_DEFAULT_CONTEXT_ID); + } + } + + } +} + +void renderspuWinCleanup(WindowInfo *window) +{ + renderspuWinTerm( window ); + RTCritSectDelete(&window->CompositorLock); +} + +void renderspuWinDestroy(WindowInfo *window) +{ + renderspuWinCleanup(window); + crFree(window); +} + +WindowInfo* renderspuGetDummyWindow(GLint visBits) +{ + WindowInfo *window = (WindowInfo *) crHashtableSearch(render_spu.dummyWindowTable, visBits); + if (!window) + { + window = renderspuWinCreate(visBits, -1); + if (!window) + { + WARN(("renderspuWinCreate failed")); + return NULL; + } + + crHashtableAdd(render_spu.dummyWindowTable, visBits, window); + } + + return window; +} + +/* Check that OpenGL extensions listed in pszRequiredExts string also exist in the pszAvailableExts string. */ +static void renderCompareGLExtensions(const char *pszAvailableExts, const char *pszRequiredExts) +{ + unsigned char fPrintHeader = 1; + const char *pszExt = pszRequiredExts; + + for (;;) + { + const char *pszSrc = pszAvailableExts; + size_t offExtEnd; + + while (*pszExt == ' ') + ++pszExt; + + if (!*pszExt) + break; + + offExtEnd = RTStrOffCharOrTerm(pszExt, ' '); + + for (;;) + { + size_t offSrcEnd; + + while (*pszSrc == ' ') + ++pszSrc; + + if (!*pszSrc) + break; + + offSrcEnd = RTStrOffCharOrTerm(pszSrc, ' '); + + if ( offSrcEnd == offExtEnd + && memcmp(pszSrc, pszExt, offSrcEnd) == 0) + break; + + pszSrc += offSrcEnd; + } + + if (!*pszSrc) + { + if (fPrintHeader) + { + fPrintHeader = 0; + crInfo("Host does not support OpenGL extension(s):"); + } + crInfo(" %.*s", offExtEnd, pszExt); + } + + pszExt += offExtEnd; + } +} + +void renderspuPerformMakeCurrent(WindowInfo *window, GLint nativeWindow, ContextInfo *context) +{ + if (window && context) + { +#ifdef CHROMIUM_THREADSAFE + crSetTSD(&_RenderTSD, context); +#else + render_spu.currentContext = context; +#endif + context->currentWindow = window; + + renderspu_SystemMakeCurrent( window, nativeWindow, context ); + if (!context->everCurrent) { + static volatile uint32_t u32ExtCompared = 0; + /* print OpenGL info */ + const char *extString = (const char *) render_spu.ws.glGetString( GL_EXTENSIONS ); + /* + crDebug( "Render SPU: GL_EXTENSIONS: %s", render_spu.ws.glGetString( GL_EXTENSIONS ) ); + */ + crInfo( "Render SPU: GL_VENDOR: %s", render_spu.ws.glGetString( GL_VENDOR ) ); + crInfo( "Render SPU: GL_RENDERER: %s", render_spu.ws.glGetString( GL_RENDERER ) ); + crInfo( "Render SPU: GL_VERSION: %s", render_spu.ws.glGetString( GL_VERSION ) ); + crInfo( "Render SPU: GL_EXTENSIONS: %s", render_spu.ws.glGetString( GL_EXTENSIONS ) ); + + if (ASMAtomicCmpXchgU32(&u32ExtCompared, 1, 0)) + renderCompareGLExtensions(extString, crExtensions); + + if (crStrstr(extString, "GL_ARB_window_pos")) + context->haveWindowPosARB = GL_TRUE; + else + context->haveWindowPosARB = GL_FALSE; + context->everCurrent = GL_TRUE; + } + if (window->BltInfo.Base.id == CR_RENDER_DEFAULT_WINDOW_ID && window->mapPending && + !render_spu.render_to_app_window && !render_spu.render_to_crut_window) { + /* Window[CR_RENDER_DEFAULT_CONTEXT_ID] is special, it's the default window and normally hidden. + * If the mapPending flag is set, then we should now make the window + * visible. + */ + /*renderspu_SystemShowWindow( window, GL_TRUE );*/ + window->mapPending = GL_FALSE; + } + window->everCurrent = GL_TRUE; + } + else if (!window && !context) + { + renderspu_SystemMakeCurrent( NULL, 0, NULL ); +#ifdef CHROMIUM_THREADSAFE + crSetTSD(&_RenderTSD, NULL); +#else + render_spu.currentContext = NULL; +#endif + } + else + { + crError("renderspuMakeCurrent invalid ids: crWindow(%d), ctx(%d)", + window ? window->BltInfo.Base.id : 0, + context ? context->BltInfo.Base.id : 0); + } +} + +void RENDER_APIENTRY +renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx) +{ + WindowInfo *window = NULL; + ContextInfo *context = NULL; + + /* + crDebug("%s win=%d native=0x%x ctx=%d", __FUNCTION__, crWindow, (int) nativeWindow, ctx); + */ + + if (crWindow) + { + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, crWindow); + if (!window) + { + crWarning("invalid window %d specified", crWindow); + return; + } + } + + if (ctx) + { + context = (ContextInfo *) crHashtableSearch(render_spu.contextTable, ctx); + if (!context) + { + crWarning("invalid context %d specified", ctx); + return; + } + } + + if (!context != !window) + { + crWarning("either window %d or context %d are zero", crWindow, ctx); + return; + } + + renderspuPerformMakeCurrent(window, nativeWindow, context); +} + +GLboolean renderspuWinInitWithVisual( WindowInfo *window, VisualInfo *visual, GLboolean showIt, GLint id ) +{ + crMemset(window, 0, sizeof (*window)); + RTCritSectInit(&window->CompositorLock); + window->pCompositor = NULL; + + window->BltInfo.Base.id = id; + + window->x = render_spu.defaultX; + window->y = render_spu.defaultY; + window->BltInfo.width = render_spu.defaultWidth; + window->BltInfo.height = render_spu.defaultHeight; + + /* Set window->title, replacing %i with the window ID number */ + { + const char *s = crStrstr(render_spu.window_title, "%i"); + if (s) { + int i, j, k; + window->title = crAlloc(crStrlen(render_spu.window_title) + 10); + for (i = 0; render_spu.window_title[i] != '%'; i++) + window->title[i] = render_spu.window_title[i]; + k = sprintf(window->title + i, "%d", window->BltInfo.Base.id); + CRASSERT(k < 10); + i++; /* skip the 'i' after the '%' */ + j = i + k; + for (; (window->title[j] = s[i]) != 0; i++, j++) + ; + } + else { + window->title = crStrdup(render_spu.window_title); + } + } + + window->BltInfo.Base.visualBits = visual->visAttribs; + + window->cRefs = 1; + + /* + crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->BltInfo.Base.id); + */ + /* Have GLX/WGL/AGL create the window */ + if (!renderspu_SystemVBoxCreateWindow( visual, showIt, window )) + { + crWarning( "Render SPU: Couldn't create a window, renderspu_SystemCreateWindow failed" ); + return GL_FALSE; + } + + window->visible = !!showIt; + + CRASSERT(window->visual == visual); + return GL_TRUE; +} + +/* + * Window functions + */ +GLboolean renderspuWinInit(WindowInfo *pWindow, const char *dpyName, GLint visBits, GLint id) +{ + VisualInfo *visual; + + crMemset(pWindow, 0, sizeof (*pWindow)); + + if (!dpyName || crStrlen(render_spu.display_string) > 0) + dpyName = render_spu.display_string; + + visual = renderspuFindVisual( dpyName, visBits ); + if (!visual) + { + crWarning( "Render SPU: Couldn't create a window, renderspuFindVisual returned NULL" ); + return GL_FALSE; + } + + /* + crDebug("Render SPU: Creating window (visBits=0x%x, id=%d)", visBits, window->BltInfo.Base.id); + */ + /* Have GLX/WGL/AGL create the window */ + if (!renderspuWinInitWithVisual( pWindow, visual, 0, id )) + { + crWarning( "Render SPU: Couldn't create a window, renderspu_SystemCreateWindow failed" ); + return GL_FALSE; + } + + return GL_TRUE; +} + +GLint renderspuWindowCreateEx( const char *dpyName, GLint visBits, GLint id ) +{ + WindowInfo *window; + + if (id <= 0) + { + id = (GLint)crHashtableAllocKeys(render_spu.windowTable, 1); + if (id <= 0) + { + crWarning("failed to allocate window id"); + return -1; + } + } + else + { + if (crHashtableIsKeyUsed(render_spu.windowTable, id)) + { + crWarning("the specified window key %d is in use", id); + return -1; + } + } + + /* Allocate WindowInfo */ + window = renderspuWinCreate(visBits, id); + + if (!window) + { + crWarning("renderspuWinCreate failed"); + crFree(window); + return -1; + } + + crHashtableAdd(render_spu.windowTable, id, window); + return window->BltInfo.Base.id; +} + +GLint RENDER_APIENTRY +renderspuWindowCreate( const char *dpyName, GLint visBits ) +{ + return renderspuWindowCreateEx( dpyName, visBits, 0 ); +} + +void renderspuWinReleaseCb(void*pvWindow) +{ + renderspuWinRelease((WindowInfo*)pvWindow); +} + +void +RENDER_APIENTRY renderspuWindowDestroy( GLint win ) +{ + WindowInfo *window; + + CRASSERT(win >= 0); + if (win == CR_RENDER_DEFAULT_WINDOW_ID) + { + crWarning("request to destroy a default mural, ignoring"); + return; + } + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + crDebug("Render SPU: Destroy window (%d)", win); + /* since os-specific backend can hold its own reference to the window object (e.g. on OSX), + * we need to explicitly issue a window destroy command + * this ensures the backend will eventually release the reference, + * the window object itself will remain valid until its ref count reaches zero */ + renderspuWinTerm( window ); + + /* remove window info from hash table, and free it */ + crHashtableDelete(render_spu.windowTable, win, renderspuWinReleaseCb); + + } + else { + crDebug("Render SPU: Attempt to destroy invalid window (%d)", win); + } +} + + +static void RENDER_APIENTRY +renderspuWindowSize( GLint win, GLint w, GLint h ) +{ + WindowInfo *window; + CRASSERT(win >= 0); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + if (w != window->BltInfo.width + || h != window->BltInfo.height) + { + /* window is resized, compositor data is no longer valid + * this set also ensures all redraw operations are done in the redraw thread + * and that no redraw is started until new Present request comes containing a valid presentation data */ + renderspuVBoxCompositorSet( window, NULL); + renderspu_SystemWindowSize( window, w, h ); + window->BltInfo.width = w; + window->BltInfo.height = h; + } + } + else { + WARN(("Render SPU: Attempt to resize invalid window (%d)", win)); + } +} + + +static void RENDER_APIENTRY +renderspuWindowPosition( GLint win, GLint x, GLint y ) +{ + if (!render_spu.ignore_window_moves) { + WindowInfo *window; + CRASSERT(win >= 0); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + renderspu_SystemWindowPosition( window, x, y ); + window->x = x; + window->y = y; + } + else { + crDebug("Render SPU: Attempt to move invalid window (%d)", win); + } + } +} + +#ifdef DEBUG_misha +# define CR_DBG_DUMP_VISIBLE_REGIONS +#endif + +#ifdef CR_DBG_DUMP_VISIBLE_REGIONS +static void renderspuDbgDumpVisibleRegion(GLint win, GLint cRects, const GLint *pRects) +{ + GLint i; + const RTRECT *pRtRects = (const RTRECT *)((const void*)pRects); + + crInfo("Window %d, Vidible Regions%d", win, cRects); + for (i = 0; i < cRects; ++i) + { + crInfo("%d: (%d,%d), (%d,%d)", i, pRtRects[i].xLeft, pRtRects[i].yTop, pRtRects[i].xRight, pRtRects[i].yBottom); + } + crInfo("======"); +} +#endif + +static void RENDER_APIENTRY +renderspuWindowVisibleRegion(GLint win, GLint cRects, const GLint *pRects) +{ + WindowInfo *window; + CRASSERT(win >= 0); + +#ifdef CR_DBG_DUMP_VISIBLE_REGIONS + renderspuDbgDumpVisibleRegion(win, cRects, pRects); +#endif + + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + renderspu_SystemWindowVisibleRegion( window, cRects, pRects ); + } + else { + crWarning("Render SPU: Attempt to set VisibleRegion for invalid window (%d)", win); + } +} + +static void RENDER_APIENTRY +renderspuWindowShow( GLint win, GLint flag ) +{ + WindowInfo *window; + CRASSERT(win >= 0); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + GLboolean visible; + if (window->nativeWindow) { + /* We're rendering back to the native app window instead of the + * new window which we (the Render SPU) created earlier. + * So, we never want to show the Render SPU's window. + */ + flag = 0; + } + + visible = !!flag; + +// if (window->visible != visible) + { + renderspu_SystemShowWindow( window, visible ); + window->visible = visible; + } + } + else { + crDebug("Render SPU: Attempt to hide/show invalid window (%d)", win); + } +} + +static void RENDER_APIENTRY +renderspuVBoxPresentComposition( GLint win, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + WindowInfo *window; + CRASSERT(win >= 0); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, win); + if (window) { + if (renderspuVBoxCompositorSet(window, pCompositor)) + { + renderspu_SystemVBoxPresentComposition(window, pChangedEntry); + } + } + else { + crDebug("Render SPU: Attempt to PresentComposition for invalid window (%d)", win); + } +} + +void renderspuVBoxCompositorBlitStretched ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter, GLfloat scaleX, GLfloat scaleY) +{ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + uint32_t i; + for (i = 0; i < cRegions; ++i) + { + RTRECT DstRect; + const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + DstRect.xLeft = paDstRegions[i].xLeft * scaleX; + DstRect.yTop = paDstRegions[i].yTop * scaleY; + DstRect.xRight = paDstRegions[i].xRight * scaleX; + DstRect.yBottom = paDstRegions[i].yBottom * scaleY; + CrBltBlitTexMural(pBlitter, true, CrTdTexGet(pTexData), &paSrcRegions[i], &DstRect, 1, fFlags); + } + } + else + { + crWarning("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d", rc); + } + } +} + +void renderspuVBoxCompositorBlit ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter) +{ + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + const CR_TEXDATA *pTexData = CrVrScrCompositorEntryTexGet(pEntry); + CrBltBlitTexMural(pBlitter, true, CrTdTexGet(pTexData), paSrcRegions, paDstRegions, cRegions, fFlags); + } + else + { + crWarning("Blit: CrVrScrCompositorEntryRegionsGet failed rc %d", rc); + } + } +} + +void renderspuVBoxPresentBlitterCleanup( WindowInfo *window ) +{ + if (!window->pBlitter) + return; + + if (render_spu.blitterTable) + { + const CR_BLITTER_WINDOW * pBltInfo = CrBltMuralGetCurrentInfo(window->pBlitter); + if (pBltInfo && pBltInfo->Base.id == window->BltInfo.Base.id) + { + CrBltMuralSetCurrentInfo(window->pBlitter, NULL); + } + } + else + { + CRASSERT(CrBltMuralGetCurrentInfo(window->pBlitter)->Base.id == window->BltInfo.Base.id); + CrBltMuralSetCurrentInfo(window->pBlitter, NULL); + CrBltTerm(window->pBlitter); + } + window->pBlitter = NULL; +} + +PCR_BLITTER renderspuVBoxPresentBlitterGet( WindowInfo *window ) +{ + PCR_BLITTER pBlitter = window->pBlitter; + if (!pBlitter) + { + if (render_spu.blitterTable) + { + crHashtableLock(render_spu.blitterTable); + pBlitter = (PCR_BLITTER)crHashtableSearch(render_spu.blitterTable, window->visual->visAttribs); + } + + if (!pBlitter) + { + int rc; + ContextInfo * pDefaultCtxInfo; + + pBlitter = (PCR_BLITTER)crCalloc(sizeof (*pBlitter)); + if (!pBlitter) + { + crWarning("failed to allocate blitter"); + return NULL; + } + + pDefaultCtxInfo = renderspuDefaultSharedContextAcquire(); + if (!pDefaultCtxInfo) + { + crWarning("no default ctx info!"); + crFree(pBlitter); + return NULL; + } + + rc = CrBltInit(pBlitter, &pDefaultCtxInfo->BltInfo, true, true, NULL, &render_spu.blitterDispatch); + + /* we can release it either way, since it will be retained when used as a shared context */ + renderspuDefaultSharedContextRelease(pDefaultCtxInfo); + + if (!RT_SUCCESS(rc)) + { + crWarning("CrBltInit failed, rc %d", rc); + crFree(pBlitter); + return NULL; + } + + if (render_spu.blitterTable) + { + crHashtableAdd( render_spu.blitterTable, window->visual->visAttribs, pBlitter ); + } + } + + if (render_spu.blitterTable) + crHashtableUnlock(render_spu.blitterTable); + + Assert(pBlitter); + window->pBlitter = pBlitter; + } + + CrBltMuralSetCurrentInfo(pBlitter, &window->BltInfo); + return pBlitter; +} + +int renderspuVBoxPresentBlitterEnter( PCR_BLITTER pBlitter, int32_t i32MakeCurrentUserData) +{ + int rc; + + CrBltSetMakeCurrentUserData(pBlitter, i32MakeCurrentUserData); + + rc = CrBltEnter(pBlitter); + if (!RT_SUCCESS(rc)) + { + crWarning("CrBltEnter failed, rc %d", rc); + return rc; + } + return VINF_SUCCESS; +} + +PCR_BLITTER renderspuVBoxPresentBlitterGetAndEnter( WindowInfo *window, int32_t i32MakeCurrentUserData, bool fRedraw ) +{ + PCR_BLITTER pBlitter = fRedraw ? window->pBlitter : renderspuVBoxPresentBlitterGet(window); + if (pBlitter) + { + int rc = renderspuVBoxPresentBlitterEnter(pBlitter, i32MakeCurrentUserData); + if (RT_SUCCESS(rc)) + { + return pBlitter; + } + } + return NULL; +} + +PCR_BLITTER renderspuVBoxPresentBlitterEnsureCreated( WindowInfo *window, int32_t i32MakeCurrentUserData ) +{ + if (!window->pBlitter) + { + const struct VBOXVR_SCR_COMPOSITOR * pTmpCompositor; + /* just use compositor lock to synchronize */ + pTmpCompositor = renderspuVBoxCompositorAcquire(window); + CRASSERT(pTmpCompositor); + if (pTmpCompositor) + { + PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGet( window ); + if (pBlitter) + { + if (!CrBltIsEverEntered(pBlitter)) + { + int rc = renderspuVBoxPresentBlitterEnter(pBlitter, i32MakeCurrentUserData); + if (RT_SUCCESS(rc)) + { + CrBltLeave(pBlitter); + } + else + { + crWarning("renderspuVBoxPresentBlitterEnter failed rc %d", rc); + } + } + } + else + { + crWarning("renderspuVBoxPresentBlitterGet failed"); + } + + renderspuVBoxCompositorRelease(window); + } + else + { + crWarning("renderspuVBoxCompositorAcquire failed"); + } + } + return window->pBlitter; +} + +void renderspuVBoxPresentCompositionGeneric( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, + const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry, int32_t i32MakeCurrentUserData, + bool fRedraw ) +{ + PCR_BLITTER pBlitter = renderspuVBoxPresentBlitterGetAndEnter(window, i32MakeCurrentUserData, fRedraw); + if (!pBlitter) + return; + + renderspuVBoxCompositorBlit(pCompositor, pBlitter); + + renderspu_SystemSwapBuffers(window, 0); + + CrBltLeave(pBlitter); +} + +GLboolean renderspuVBoxCompositorSet( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor) +{ + int rc; + GLboolean fEmpty = pCompositor && CrVrScrCompositorIsEmpty(pCompositor); + GLboolean fNeedPresent; + + /* renderspuVBoxCompositorSet can be invoked from the chromium thread only and is not reentrant, + * no need to synch here + * the lock is actually needed to ensure we're in synch with the redraw thread */ + if (window->pCompositor == pCompositor && !fEmpty) + return !!pCompositor; + + rc = RTCritSectEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + if (!fEmpty) + fNeedPresent = !!pCompositor; + else + { + fNeedPresent = renderspu_SystemWindowNeedEmptyPresent(window); + pCompositor = NULL; + } + + window->pCompositor = !fEmpty ? pCompositor : NULL; + RTCritSectLeave(&window->CompositorLock); + return fNeedPresent; + } + else + { + WARN(("RTCritSectEnter failed rc %d", rc)); + } + + return GL_FALSE; +} + +static void renderspuVBoxCompositorClearAllCB(unsigned long key, void *data1, void *data2) +{ + WindowInfo *window = (WindowInfo *) data1; + renderspuVBoxCompositorSet(window, NULL); +} + +void renderspuVBoxCompositorClearAll() +{ + /* we need to clear window compositor, which is not that trivial though, + * since the lock order used in presentation thread is compositor lock() -> hash table lock (aquired for id->window resolution) + * this is why, to prevent potential deadlocks, we use crHashtableWalkUnlocked that does not hold the table lock + * we are can be sure noone will modify the table here since renderspuVBoxCompositorClearAll can be called in the command (hgcm) thread only, + * and the table can be modified from that thread only as well */ + crHashtableWalkUnlocked(render_spu.windowTable, renderspuVBoxCompositorClearAllCB, NULL); +} + +const struct VBOXVR_SCR_COMPOSITOR * renderspuVBoxCompositorAcquire( WindowInfo *window) +{ + int rc = RTCritSectEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + const VBOXVR_SCR_COMPOSITOR * pCompositor = window->pCompositor; + if (pCompositor) + { + Assert(!CrVrScrCompositorIsEmpty(window->pCompositor)); + return pCompositor; + } + + /* if no compositor is set, release the lock and return */ + RTCritSectLeave(&window->CompositorLock); + } + else + { + crWarning("RTCritSectEnter failed rc %d", rc); + } + return NULL; +} + +int renderspuVBoxCompositorLock(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor) +{ + int rc = RTCritSectEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + if (ppCompositor) + *ppCompositor = window->pCompositor; + } + else + WARN(("RTCritSectEnter failed %d", rc)); + return rc; +} + +int renderspuVBoxCompositorUnlock(WindowInfo *window) +{ + int rc = RTCritSectLeave(&window->CompositorLock); + AssertRC(rc); + return rc; +} + +int renderspuVBoxCompositorTryAcquire(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor) +{ + int rc = RTCritSectTryEnter(&window->CompositorLock); + if (RT_SUCCESS(rc)) + { + *ppCompositor = window->pCompositor; + if (*ppCompositor) + { + Assert(!CrVrScrCompositorIsEmpty(window->pCompositor)); + return VINF_SUCCESS; + } + + /* if no compositor is set, release the lock and return */ + RTCritSectLeave(&window->CompositorLock); + rc = VERR_INVALID_STATE; + } + else + { + *ppCompositor = NULL; + } + return rc; +} + +void renderspuVBoxCompositorRelease( WindowInfo *window) +{ + int rc; + Assert(window->pCompositor); + Assert(!CrVrScrCompositorIsEmpty(window->pCompositor)); + rc = RTCritSectLeave(&window->CompositorLock); + if (!RT_SUCCESS(rc)) + { + crWarning("RTCritSectLeave failed rc %d", rc); + } +} + + +/* + * Set the current raster position to the given window coordinate. + */ +static void +SetRasterPos( GLint winX, GLint winY ) +{ + GLfloat fx, fy; + + /* Push current matrix mode and viewport attributes */ + render_spu.self.PushAttrib( GL_TRANSFORM_BIT | GL_VIEWPORT_BIT ); + + /* Setup projection parameters */ + render_spu.self.MatrixMode( GL_PROJECTION ); + render_spu.self.PushMatrix(); + render_spu.self.LoadIdentity(); + render_spu.self.MatrixMode( GL_MODELVIEW ); + render_spu.self.PushMatrix(); + render_spu.self.LoadIdentity(); + + render_spu.self.Viewport( winX - 1, winY - 1, 2, 2 ); + + /* set the raster (window) position */ + /* huh ? */ + fx = (GLfloat) (winX - (int) winX); + fy = (GLfloat) (winY - (int) winY); + render_spu.self.RasterPos4f( fx, fy, 0.0, 1.0 ); + + /* restore matrices, viewport and matrix mode */ + render_spu.self.PopMatrix(); + render_spu.self.MatrixMode( GL_PROJECTION ); + render_spu.self.PopMatrix(); + + render_spu.self.PopAttrib(); +} + + +/* + * Draw the mouse pointer bitmap at (x,y) in window coords. + */ +static void DrawCursor( GLint x, GLint y ) +{ +#define POINTER_WIDTH 32 +#define POINTER_HEIGHT 32 + /* Somebody artistic could probably do better here */ + static const char *pointerImage[POINTER_HEIGHT] = + { + "XX..............................", + "XXXX............................", + ".XXXXX..........................", + ".XXXXXXX........................", + "..XXXXXXXX......................", + "..XXXXXXXXXX....................", + "...XXXXXXXXXXX..................", + "...XXXXXXXXXXXXX................", + "....XXXXXXXXXXXXXX..............", + "....XXXXXXXXXXXXXXXX............", + ".....XXXXXXXXXXXXXXXXX..........", + ".....XXXXXXXXXXXXXXXXXXX........", + "......XXXXXXXXXXXXXXXXXXXX......", + "......XXXXXXXXXXXXXXXXXXXXXX....", + ".......XXXXXXXXXXXXXXXXXXXXXXX..", + ".......XXXXXXXXXXXXXXXXXXXXXXXX.", + "........XXXXXXXXXXXXX...........", + "........XXXXXXXX.XXXXX..........", + ".........XXXXXX...XXXXX.........", + ".........XXXXX.....XXXXX........", + "..........XXX.......XXXXX.......", + "..........XX.........XXXXX......", + "......................XXXXX.....", + ".......................XXXXX....", + "........................XXX.....", + ".........................X......", + "................................", + "................................", + "................................", + "................................", + "................................", + "................................" + + }; + static GLubyte pointerBitmap[POINTER_HEIGHT][POINTER_WIDTH / 8]; + static GLboolean firstCall = GL_TRUE; + GLboolean lighting, depthTest, scissorTest; + + if (firstCall) { + /* Convert pointerImage into pointerBitmap */ + GLint i, j; + for (i = 0; i < POINTER_HEIGHT; i++) { + for (j = 0; j < POINTER_WIDTH; j++) { + if (pointerImage[POINTER_HEIGHT - i - 1][j] == 'X') { + GLubyte bit = 128 >> (j & 0x7); + pointerBitmap[i][j / 8] |= bit; + } + } + } + firstCall = GL_FALSE; + } + + render_spu.self.GetBooleanv(GL_LIGHTING, &lighting); + render_spu.self.GetBooleanv(GL_DEPTH_TEST, &depthTest); + render_spu.self.GetBooleanv(GL_SCISSOR_TEST, &scissorTest); + render_spu.self.Disable(GL_LIGHTING); + render_spu.self.Disable(GL_DEPTH_TEST); + render_spu.self.Disable(GL_SCISSOR_TEST); + render_spu.self.PixelStorei(GL_UNPACK_ALIGNMENT, 1); + + render_spu.self.Color3f(1, 1, 1); + + /* save current raster pos */ + render_spu.self.PushAttrib(GL_CURRENT_BIT); + SetRasterPos(x, y); + render_spu.self.Bitmap(POINTER_WIDTH, POINTER_HEIGHT, 1.0, 31.0, 0, 0, + (const GLubyte *) pointerBitmap); + /* restore current raster pos */ + render_spu.self.PopAttrib(); + + if (lighting) + render_spu.self.Enable(GL_LIGHTING); + if (depthTest) + render_spu.self.Enable(GL_DEPTH_TEST); + if (scissorTest) + render_spu.self.Enable(GL_SCISSOR_TEST); +} + +void RENDER_APIENTRY renderspuSwapBuffers( GLint window, GLint flags ) +{ + WindowInfo *w = (WindowInfo *) crHashtableSearch(render_spu.windowTable, window); + + if (!w) + { + crDebug("Render SPU: SwapBuffers invalid window id: %d", window); + return; + } + + if (flags & CR_SUPPRESS_SWAP_BIT) + { + render_spu.self.Finish(); + return; + } + + if (render_spu.drawCursor) + DrawCursor( render_spu.cursorX, render_spu.cursorY ); + + if (render_spu.swap_master_url) + DoSync(); + + renderspu_SystemSwapBuffers( w, flags ); +} + + +/* + * Barrier functions + * Normally, we'll have a crserver somewhere that handles the barrier calls. + * However, if we're running the render SPU on the client node, then we + * should handle barriers here. The threadtest demo illustrates this. + * If we have N threads calling using this SPU we need these barrier + * functions to synchronize them. + */ + +static void RENDER_APIENTRY renderspuBarrierCreateCR( GLuint name, GLuint count ) +{ + Barrier *b; + + if (render_spu.ignore_papi) + return; + + b = (Barrier *) crHashtableSearch( render_spu.barrierHash, name ); + if (b) { + /* HACK -- this allows everybody to create a barrier, and all + but the first creation are ignored, assuming the count + match. */ + if ( b->count != count ) { + crError( "Render SPU: Barrier name=%u created with count=%u, but already " + "exists with count=%u", name, count, b->count ); + } + } + else { + b = (Barrier *) crAlloc( sizeof(Barrier) ); + b->count = count; + crInitBarrier( &b->barrier, count ); + crHashtableAdd( render_spu.barrierHash, name, b ); + } +} + +static void RENDER_APIENTRY renderspuBarrierDestroyCR( GLuint name ) +{ + if (render_spu.ignore_papi) + return; + crHashtableDelete( render_spu.barrierHash, name, crFree ); +} + +static void RENDER_APIENTRY renderspuBarrierExecCR( GLuint name ) +{ + Barrier *b; + + if (render_spu.ignore_papi) + return; + + b = (Barrier *) crHashtableSearch( render_spu.barrierHash, name ); + if (b) { + crWaitBarrier( &(b->barrier) ); + } + else { + crWarning("Render SPU: Bad barrier name %d in BarrierExec()", name); + } +} + + +/* + * Semaphore functions + * XXX we should probably implement these too, for the same reason as + * barriers (see above). + */ + +static void RENDER_APIENTRY renderspuSemaphoreCreateCR( GLuint name, GLuint count ) +{ + (void) name; + (void) count; +} + +static void RENDER_APIENTRY renderspuSemaphoreDestroyCR( GLuint name ) +{ + (void) name; +} + +static void RENDER_APIENTRY renderspuSemaphorePCR( GLuint name ) +{ + (void) name; +} + +static void RENDER_APIENTRY renderspuSemaphoreVCR( GLuint name ) +{ + (void) name; +} + + +/* + * Misc functions + */ +void renderspuSetDefaultSharedContext(ContextInfo *pCtx) +{ + if (pCtx == render_spu.defaultSharedContext) + return; + + renderspu_SystemDefaultSharedContextChanged(render_spu.defaultSharedContext, pCtx); + + if (render_spu.defaultSharedContext) + renderspuContextRelease(render_spu.defaultSharedContext); + + if (pCtx) + renderspuContextRetain(pCtx); + render_spu.defaultSharedContext = pCtx; +} + +static void RENDER_APIENTRY renderspuChromiumParameteriCR(GLenum target, GLint value) +{ + switch (target) + { + case GL_HH_SET_DEFAULT_SHARED_CTX: + { + ContextInfo * pCtx = NULL; + if (value) + pCtx = (ContextInfo *)crHashtableSearch(render_spu.contextTable, value); + else + crWarning("invalid default shared context id %d", value); + + renderspuSetDefaultSharedContext(pCtx); + break; + } + case GL_HH_RENDERTHREAD_INFORM: + { + if (value) + { + int rc = renderspuDefaultCtxInit(); + if (RT_FAILURE(rc)) + { + WARN(("renderspuDefaultCtxInit failed")); + break; + } + } + else + { + renderspuCleanupBase(false); + } + break; + } + default: +// crWarning("Unhandled target in renderspuChromiumParameteriCR()"); + break; + } +} + +static void RENDER_APIENTRY +renderspuChromiumParameterfCR(GLenum target, GLfloat value) +{ + (void) target; + (void) value; + +#if 0 + switch (target) { + default: + crWarning("Unhandled target in renderspuChromiumParameterfCR()"); + break; + } +#endif +} + +bool renderspuCalloutAvailable() +{ + return render_spu.pfnClientCallout != NULL; +} + +bool renderspuCalloutClient(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void *pvCb) +{ + if (render_spu.pfnClientCallout) + { + render_spu.pfnClientCallout(pfnCb, pvCb); + return true; + } + return false; +} + +static void RENDER_APIENTRY +renderspuChromiumParametervCR(GLenum target, GLenum type, GLsizei count, + const GLvoid *values) +{ + int client_num; + unsigned short port; + CRMessage *msg, pingback; + unsigned char *privbuf = NULL; + + switch (target) { + case GL_HH_SET_CLIENT_CALLOUT: + render_spu.pfnClientCallout = (PFNVCRSERVER_CLIENT_CALLOUT)values; + break; + case GL_GATHER_CONNECT_CR: + if (render_spu.gather_userbuf_size) + privbuf = (unsigned char *)crAlloc(1024*768*4); + + port = ((GLint *) values)[0]; + + if (render_spu.gather_conns == NULL) + render_spu.gather_conns = crAlloc(render_spu.server->numClients*sizeof(CRConnection *)); + else + { + crError("Oh bother! duplicate GL_GATHER_CONNECT_CR getting through"); + } + + for (client_num=0; client_num< render_spu.server->numClients; client_num++) + { + switch (render_spu.server->clients[client_num]->conn->type) + { + case CR_TCPIP: + crDebug("Render SPU: AcceptClient from %s on %d", + render_spu.server->clients[client_num]->conn->hostname, render_spu.gather_port); + render_spu.gather_conns[client_num] = + crNetAcceptClient("tcpip", NULL, port, 1024*1024, 1); + break; + + case CR_GM: + render_spu.gather_conns[client_num] = + crNetAcceptClient("gm", NULL, port, 1024*1024, 1); + break; + + default: + crError("Render SPU: Unknown Network Type to Open Gather Connection"); + } + + + if (render_spu.gather_userbuf_size) + { + render_spu.gather_conns[client_num]->userbuf = privbuf; + render_spu.gather_conns[client_num]->userbuf_len = render_spu.gather_userbuf_size; + } + else + { + render_spu.gather_conns[client_num]->userbuf = NULL; + render_spu.gather_conns[client_num]->userbuf_len = 0; + } + + if (render_spu.gather_conns[client_num]) + { + crDebug("Render SPU: success! from %s", render_spu.gather_conns[client_num]->hostname); + } + } + + break; + + case GL_GATHER_DRAWPIXELS_CR: + pingback.header.type = CR_MESSAGE_OOB; + + for (client_num=0; client_num< render_spu.server->numClients; client_num++) + { + crNetGetMessage(render_spu.gather_conns[client_num], &msg); + if (msg->header.type == CR_MESSAGE_GATHER) + { + crNetFree(render_spu.gather_conns[client_num], msg); + } + else + { + crError("Render SPU: expecting MESSAGE_GATHER. got crap! (%d of %d)", + client_num, render_spu.server->numClients-1); + } + } + + /* + * We're only hitting the case if we're not actually calling + * child.SwapBuffers from readback, so a switch about which + * call to DoSync() we really want [this one, or the one + * in SwapBuffers above] is not necessary -- karl + */ + + if (render_spu.swap_master_url) + DoSync(); + + for (client_num=0; client_num< render_spu.server->numClients; client_num++) + crNetSend(render_spu.gather_conns[client_num], NULL, &pingback, + sizeof(CRMessageHeader)); + + render_spu.self.RasterPos2i(((GLint *)values)[0], ((GLint *)values)[1]); + render_spu.self.DrawPixels( ((GLint *)values)[2], ((GLint *)values)[3], + ((GLint *)values)[4], ((GLint *)values)[5], + render_spu.gather_conns[0]->userbuf); + + + render_spu.self.SwapBuffers(((GLint *)values)[6], 0); + break; + + case GL_CURSOR_POSITION_CR: + if (type == GL_INT && count == 2) { + render_spu.cursorX = ((GLint *) values)[0]; + render_spu.cursorY = ((GLint *) values)[1]; + crDebug("Render SPU: GL_CURSOR_POSITION_CR (%d, %d)", render_spu.cursorX, render_spu.cursorY); + } + else { + crWarning("Render SPU: Bad type or count for ChromiumParametervCR(GL_CURSOR_POSITION_CR)"); + } + break; + + case GL_WINDOW_SIZE_CR: + /* XXX this is old code that should be removed. + * NOTE: we can only resize the default (id=CR_RENDER_DEFAULT_WINDOW_ID) window!!! + */ + { + GLint w, h; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 2); + CRASSERT(values); + w = ((GLint*)values)[0]; + h = ((GLint*)values)[1]; + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, CR_RENDER_DEFAULT_WINDOW_ID); + if (window) + { + renderspu_SystemWindowSize(window, w, h); + } + } + break; + + case GL_HH_SET_TMPCTX_MAKE_CURRENT: + if (type == GL_BYTE && count == sizeof (void*)) + memcpy(&render_spu.blitterDispatch.MakeCurrent, values, count); + else + WARN(("unexpected type(%#x) - count(%d) pair", type, count)); + break; + + default: +#if 0 + WARN(("Unhandled target in renderspuChromiumParametervCR(0x%x)", (int) target)); +#endif + break; + } +} + + +static void RENDER_APIENTRY +renderspuGetChromiumParametervCR(GLenum target, GLuint index, GLenum type, + GLsizei count, GLvoid *values) +{ + switch (target) { + case GL_WINDOW_SIZE_CR: + { + GLint x, y, w, h, *size = (GLint *) values; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 2); + CRASSERT(values); + size[0] = size[1] = 0; /* default */ + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index); + if (window) + { + renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h); + size[0] = w; + size[1] = h; + } + } + break; + case GL_WINDOW_POSITION_CR: + /* return window position, as a screen coordinate */ + { + GLint *pos = (GLint *) values; + GLint x, y, w, h; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 2); + CRASSERT(values); + pos[0] = pos[1] = 0; /* default */ + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index); + if (window) + { + renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h); + pos[0] = x;/*window->x;*/ + pos[1] = y;/*window->y;*/ + } + } + break; + case GL_MAX_WINDOW_SIZE_CR: + { + GLint *maxSize = (GLint *) values; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 2); + CRASSERT(values); + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index); + if (window) + { + renderspu_SystemGetMaxWindowSize(window, maxSize + 0, maxSize + 1); + } + } + break; + case GL_WINDOW_VISIBILITY_CR: + { + GLint *vis = (GLint *) values; + WindowInfo *window; + CRASSERT(type == GL_INT); + CRASSERT(count == 1); + CRASSERT(values); + vis[0] = 0; /* default */ + window = (WindowInfo *) crHashtableSearch(render_spu.windowTable, index); + if (window) + { + vis[0] = window->visible; + } + } + break; + default: + ; /* nothing - silence compiler */ + } +} + + +static void RENDER_APIENTRY +renderspuBoundsInfoCR( CRrecti *bounds, GLbyte *payload, GLint len, + GLint num_opcodes ) +{ + (void) bounds; + (void) payload; + (void) len; + (void) num_opcodes; + /* draw the bounding box */ + if (render_spu.draw_bbox) { + GET_CONTEXT(context); + WindowInfo *window = context->currentWindow; + GLint x, y, w, h; + + renderspu_SystemGetWindowGeometry(window, &x, &y, &w, &h); + + render_spu.self.PushMatrix(); + render_spu.self.LoadIdentity(); + render_spu.self.MatrixMode(GL_PROJECTION); + render_spu.self.PushMatrix(); + render_spu.self.LoadIdentity(); + render_spu.self.Ortho(0, w, 0, h, -1, 1); + render_spu.self.Color3f(1, 1, 1); + render_spu.self.Begin(GL_LINE_LOOP); + render_spu.self.Vertex2i(bounds->x1, bounds->y1); + render_spu.self.Vertex2i(bounds->x2, bounds->y1); + render_spu.self.Vertex2i(bounds->x2, bounds->y2); + render_spu.self.Vertex2i(bounds->x1, bounds->y2); + render_spu.self.End(); + render_spu.self.PopMatrix(); + render_spu.self.MatrixMode(GL_MODELVIEW); + render_spu.self.PopMatrix(); + } +} + + +static void RENDER_APIENTRY +renderspuWriteback( GLint *writeback ) +{ + (void) writeback; +} + + +static void +remove_trailing_space(char *s) +{ + int k = crStrlen(s); + while (k > 0 && s[k-1] == ' ') + k--; + s[k] = 0; +} + +static const GLubyte * RENDER_APIENTRY +renderspuGetString(GLenum pname) +{ + static char tempStr[1000]; + GET_CONTEXT(context); + + if (pname == GL_EXTENSIONS) + { + const char *nativeExt; + char *crExt, *s1, *s2; + + if (!render_spu.ws.glGetString) + return NULL; + + nativeExt = (const char *) render_spu.ws.glGetString(GL_EXTENSIONS); + if (!nativeExt) { + /* maybe called w/out current context. */ + return NULL; + } + + if (!context) + return (const GLubyte *)nativeExt; + + crExt = crStrjoin3(crExtensions, " ", crAppOnlyExtensions); + s1 = crStrIntersect(nativeExt, crExt); + remove_trailing_space(s1); + s2 = crStrjoin3(s1, " ", crChromiumExtensions); + remove_trailing_space(s2); + crFree(crExt); + crFree(s1); + if (context->extensionString) + crFree(context->extensionString); + context->extensionString = s2; + return (const GLubyte *) s2; + } + else if (pname == GL_VENDOR) + return (const GLubyte *) CR_VENDOR; + else if (pname == GL_VERSION) + return render_spu.ws.glGetString(GL_VERSION); + else if (pname == GL_RENDERER) { +#ifdef VBOX + snprintf(tempStr, sizeof(tempStr), "Chromium (%s)", (char *) render_spu.ws.glGetString(GL_RENDERER)); +#else + sprintf(tempStr, "Chromium (%s)", (char *) render_spu.ws.glGetString(GL_RENDERER)); +#endif + return (const GLubyte *) tempStr; + } +#ifdef CR_OPENGL_VERSION_2_0 + else if (pname == GL_SHADING_LANGUAGE_VERSION) + return render_spu.ws.glGetString(GL_SHADING_LANGUAGE_VERSION); +#endif +#ifdef GL_CR_real_vendor_strings + else if (pname == GL_REAL_VENDOR) + return render_spu.ws.glGetString(GL_VENDOR); + else if (pname == GL_REAL_VERSION) + return render_spu.ws.glGetString(GL_VERSION); + else if (pname == GL_REAL_RENDERER) + return render_spu.ws.glGetString(GL_RENDERER); + else if (pname == GL_REAL_EXTENSIONS) + return render_spu.ws.glGetString(GL_EXTENSIONS); +#endif + else + return NULL; +} + +static void renderspuReparentWindowCB(unsigned long key, void *data1, void *data2) +{ + WindowInfo *pWindow = (WindowInfo *)data1; + + renderspu_SystemReparentWindow(pWindow); +} + +DECLEXPORT(void) renderspuReparentWindow(GLint window) +{ + WindowInfo *pWindow; + CRASSERT(window >= 0); + + pWindow = (WindowInfo *) crHashtableSearch(render_spu.windowTable, window); + + if (!pWindow) + { + crDebug("Render SPU: Attempt to reparent invalid window (%d)", window); + return; + } + + renderspu_SystemReparentWindow(pWindow); + + /* special case: reparent all internal windows as well */ + if (window == CR_RENDER_DEFAULT_WINDOW_ID) + { + crHashtableWalk(render_spu.dummyWindowTable, renderspuReparentWindowCB, NULL); + } +} + +DECLEXPORT(void) renderspuSetUnscaledHiDPI(bool fEnable) +{ + render_spu.fUnscaledHiDPI = fEnable; +} + +#define FILLIN( NAME, FUNC ) \ + table[i].name = crStrdup(NAME); \ + table[i].fn = (SPUGenericFunction) FUNC; \ + i++; + + +/* These are the functions which the render SPU implements, not OpenGL. + */ +int +renderspuCreateFunctions(SPUNamedFunctionTable table[]) +{ + int i = 0; + FILLIN( "SwapBuffers", renderspuSwapBuffers ); + FILLIN( "CreateContext", renderspuCreateContext ); + FILLIN( "DestroyContext", renderspuDestroyContext ); + FILLIN( "MakeCurrent", renderspuMakeCurrent ); + FILLIN( "WindowCreate", renderspuWindowCreate ); + FILLIN( "WindowDestroy", renderspuWindowDestroy ); + FILLIN( "WindowSize", renderspuWindowSize ); + FILLIN( "WindowPosition", renderspuWindowPosition ); + FILLIN( "WindowVisibleRegion", renderspuWindowVisibleRegion ); + FILLIN( "WindowShow", renderspuWindowShow ); + FILLIN( "BarrierCreateCR", renderspuBarrierCreateCR ); + FILLIN( "BarrierDestroyCR", renderspuBarrierDestroyCR ); + FILLIN( "BarrierExecCR", renderspuBarrierExecCR ); + FILLIN( "BoundsInfoCR", renderspuBoundsInfoCR ); + FILLIN( "SemaphoreCreateCR", renderspuSemaphoreCreateCR ); + FILLIN( "SemaphoreDestroyCR", renderspuSemaphoreDestroyCR ); + FILLIN( "SemaphorePCR", renderspuSemaphorePCR ); + FILLIN( "SemaphoreVCR", renderspuSemaphoreVCR ); + FILLIN( "Writeback", renderspuWriteback ); + FILLIN( "ChromiumParameteriCR", renderspuChromiumParameteriCR ); + FILLIN( "ChromiumParameterfCR", renderspuChromiumParameterfCR ); + FILLIN( "ChromiumParametervCR", renderspuChromiumParametervCR ); + FILLIN( "GetChromiumParametervCR", renderspuGetChromiumParametervCR ); + FILLIN( "GetString", renderspuGetString ); + FILLIN( "VBoxPresentComposition", renderspuVBoxPresentComposition ); + return i; +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu.h b/src/VBox/HostServices/SharedOpenGL/render/renderspu.h new file mode 100644 index 00000000..dfd591e8 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu.h @@ -0,0 +1,506 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved. + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#ifndef CR_RENDERSPU_H +#define CR_RENDERSPU_H + +#ifdef WINDOWS +#define WIN32_LEAN_AND_MEAN +#include <iprt/win/windows.h> +#define RENDER_APIENTRY __stdcall +#define snprintf _snprintf +#elif defined(DARWIN) +# ifndef VBOX_WITH_COCOA_QT +# include <AGL/AGL.h> +# else +# include "renderspu_cocoa_helper.h" +# endif +#define RENDER_APIENTRY +#else +#include <GL/glx.h> +#define RENDER_APIENTRY +#endif +#include "cr_threads.h" +#include "cr_spu.h" +#include "cr_hash.h" +#include "cr_server.h" +#include "cr_blitter.h" +#include "cr_compositor.h" + +#include <iprt/cdefs.h> +#include <iprt/critsect.h> +#if defined(GLX) /* @todo: unify windows and glx thread creation code */ +#include <iprt/thread.h> +#include <iprt/semaphore.h> + +/* special window id used for representing the command window CRWindowInfo */ +#define CR_RENDER_WINCMD_ID (INT32_MAX-2) +AssertCompile(CR_RENDER_WINCMD_ID != CR_RENDER_DEFAULT_WINDOW_ID); +/* CRHashTable is using unsigned long keys, we use it to trore X Window -> CRWindowInfo association */ +AssertCompile(sizeof (Window) == sizeof (unsigned long)); +#endif + + +#define MAX_VISUALS 32 + +#ifdef RT_OS_DARWIN +# ifndef VBOX_WITH_COCOA_QT +enum +{ + /* Event classes */ + kEventClassVBox = 'vbox', + /* Event kinds */ + kEventVBoxShowWindow = 'swin', + kEventVBoxHideWindow = 'hwin', + kEventVBoxMoveWindow = 'mwin', + kEventVBoxResizeWindow = 'rwin', + kEventVBoxDisposeWindow = 'dwin', + kEventVBoxUpdateDock = 'udck', + kEventVBoxUpdateContext = 'uctx', + kEventVBoxBoundsChanged = 'bchg' +}; +pascal OSStatus windowEvtHndlr(EventHandlerCallRef myHandler, EventRef event, void* userData); +# endif +#endif /* RT_OS_DARWIN */ + +/** + * Visual info + */ +typedef struct { + GLbitfield visAttribs; + const char *displayName; +#if defined(WINDOWS) +// HDC device_context; +#elif defined(DARWIN) +# ifndef VBOX_WITH_COCOA_QT + WindowRef window; +# endif +#elif defined(GLX) + Display *dpy; + XVisualInfo *visual; +#ifdef GLX_VERSION_1_3 + GLXFBConfig fbconfig; +#endif /* GLX_VERSION_1_3 */ +#endif +} VisualInfo; + +/** + * Window info + */ +typedef struct WindowInfo { + int x, y; +// int width, height; +// int id; /**< integer window ID */ + CR_BLITTER_WINDOW BltInfo; + + VisualInfo *visual; + + volatile uint32_t cRefs; + + GLboolean mapPending; + GLboolean visible; + GLboolean everCurrent; /**< has this window ever been bound? */ + char *title; + + const VBOXVR_SCR_COMPOSITOR *pCompositor; + /* the composotor lock is used to synchronize the current compositor access, + * i.e. the compositor can be accessed by a gui refraw thread, + * while chromium thread might try to set a new compositor + * note that the compositor internally has its own lock to be used for accessing its data + * see CrVrScrCompositorLock/Unlock; renderspu and crserverlib would use it for compositor data access */ + RTCRITSECT CompositorLock; + PCR_BLITTER pBlitter; +#if defined(WINDOWS) + HDC nativeWindow; /**< for render_to_app_window */ + HWND hWnd; + HDC device_context; + HDC redraw_device_context; + HRGN hRgn; +#elif defined(DARWIN) +# ifndef VBOX_WITH_COCOA_QT + WindowRef window; + WindowRef nativeWindow; /**< for render_to_app_window */ + WindowRef appWindow; + EventHandlerUPP event_handler; + GLint bufferName; + AGLContext dummyContext; + RgnHandle hVisibleRegion; + /* unsigned long context_ptr; */ +# else + NativeNSViewRef window; + NativeNSViewRef nativeWindow; /**< for render_to_app_window */ + NativeNSOpenGLContextRef *currentCtx; +# endif +#elif defined(GLX) + Window window; + Window nativeWindow; /**< for render_to_app_window */ + Window appWindow; /**< Same as nativeWindow but for garbage collections purposes */ +#endif + int nvSwapGroup; + +#ifdef USE_OSMESA + GLubyte *buffer; /**< for rendering to off screen buffer. */ + int in_buffer_width; + int in_buffer_height; +#endif + +} WindowInfo; + +/** + * Context Info + */ +typedef struct _ContextInfo { +// int id; /**< integer context ID */ + CR_BLITTER_CONTEXT BltInfo; + VisualInfo *visual; + GLboolean everCurrent; + GLboolean haveWindowPosARB; + WindowInfo *currentWindow; +#if defined(WINDOWS) + HGLRC hRC; +#elif defined(DARWIN) +# ifndef VBOX_WITH_COCOA_QT + AGLContext context; +# else + NativeNSOpenGLContextRef context; +# endif +#elif defined(GLX) + GLXContext context; +#endif + struct _ContextInfo *shared; + char *extensionString; + volatile uint32_t cRefs; +} ContextInfo; + +/** + * Barrier info + */ +typedef struct { + CRbarrier barrier; + GLuint count; +} Barrier; + +#ifdef GLX +typedef enum +{ + CR_RENDER_WINCMD_TYPE_UNDEFINED = 0, + /* create the window (not used for now) */ + CR_RENDER_WINCMD_TYPE_WIN_CREATE, + /* destroy the window (not used for now) */ + CR_RENDER_WINCMD_TYPE_WIN_DESTROY, + /* notify the WinCmd thread about window creation */ + CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE, + /* notify the WinCmd thread about window destroy */ + CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY, + /* nop used to synchronize with the WinCmd thread */ + CR_RENDER_WINCMD_TYPE_NOP, + /* exit Win Cmd thread */ + CR_RENDER_WINCMD_TYPE_EXIT, +} CR_RENDER_WINCMD_TYPE; + +typedef struct CR_RENDER_WINCMD +{ + /* command type */ + CR_RENDER_WINCMD_TYPE enmCmd; + /* command result */ + int rc; + /* valid for WIN_CREATE & WIN_DESTROY only */ + WindowInfo *pWindow; +} CR_RENDER_WINCMD, *PCR_RENDER_WINCMD; +#endif + +#ifdef RT_OS_DARWIN +typedef void (*PFNDELETE_OBJECT)(GLhandleARB obj); +typedef void (*PFNGET_ATTACHED_OBJECTS)( GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, GLhandleARB * obj ); +typedef GLhandleARB (*PFNGET_HANDLE)(GLenum pname); +typedef void (*PFNGET_INFO_LOG)( GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog ); +typedef void (*PFNGET_OBJECT_PARAMETERFV)( GLhandleARB obj, GLenum pname, GLfloat * params ); +typedef void (*PFNGET_OBJECT_PARAMETERIV)( GLhandleARB obj, GLenum pname, GLint * params ); +#endif + +typedef DECLCALLBACKPTR(void, PFNVCRSERVER_CLIENT_CALLOUT_CB)(void *pvCb); +typedef DECLCALLBACKPTR(void, PFNVCRSERVER_CLIENT_CALLOUT)(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void*pvCb); + + +/** + * Renderspu state info + */ +typedef struct { + SPUDispatchTable self; + int id; + + /** config options */ + /*@{*/ + char *window_title; + int defaultX, defaultY; + unsigned int defaultWidth, defaultHeight; + int default_visual; + int use_L2; + int fullscreen, ontop; + char display_string[100]; +#if defined(GLX) + int try_direct; + int force_direct; + int sync; +#endif + int force_present_main_thread; + int render_to_app_window; + int render_to_crut_window; + int crut_drawable; + int resizable; + int use_lut8, lut8[3][256]; + int borderless; + int nvSwapGroup; + int ignore_papi; + int ignore_window_moves; + int pbufferWidth, pbufferHeight; + int use_glxchoosevisual; + int draw_bbox; + /*@}*/ + + CRServer *server; + int gather_port; + int gather_userbuf_size; + CRConnection **gather_conns; + + GLint drawCursor; + GLint cursorX, cursorY; + + int numVisuals; + VisualInfo visuals[MAX_VISUALS]; + + CRHashTable *windowTable; + CRHashTable *contextTable; + + CRHashTable *dummyWindowTable; + + ContextInfo *defaultSharedContext; + +#ifndef CHROMIUM_THREADSAFE + ContextInfo *currentContext; +#endif + + crOpenGLInterface ws; /**< Window System interface */ + + CRHashTable *barrierHash; + + int is_swap_master, num_swap_clients; + int swap_mtu; + char *swap_master_url; + CRConnection **swap_conns; + + SPUDispatchTable blitterDispatch; + CRHashTable *blitterTable; + + PFNVCRSERVER_CLIENT_CALLOUT pfnClientCallout; + +#ifdef USE_OSMESA + /** Off screen rendering hooks. */ + int use_osmesa; + + OSMesaContext (*OSMesaCreateContext)( GLenum format, OSMesaContext sharelist ); + GLboolean (* OSMesaMakeCurrent)( OSMesaContext ctx, + GLubyte *buffer, + GLenum type, + GLsizei width, + GLsizei height ); + void (*OSMesaDestroyContext)( OSMesaContext ctx ); +#endif + +#if defined(GLX) + RTTHREAD hWinCmdThread; + VisualInfo WinCmdVisual; + WindowInfo WinCmdWindow; + RTSEMEVENT hWinCmdCompleteEvent; + /* display connection used to send data to the WinCmd thread */ + Display *pCommunicationDisplay; + Atom WinCmdAtom; + /* X Window -> CRWindowInfo table */ + CRHashTable *pWinToInfoTable; +#endif + +#ifdef RT_OS_WINDOWS + DWORD dwWinThreadId; + HANDLE hWinThreadReadyEvent; +#endif + +#ifdef RT_OS_DARWIN +# ifdef VBOX_WITH_COCOA_QT + PFNDELETE_OBJECT pfnDeleteObject; + PFNGET_ATTACHED_OBJECTS pfnGetAttachedObjects; + PFNGET_HANDLE pfnGetHandle; + PFNGET_INFO_LOG pfnGetInfoLog; + PFNGET_OBJECT_PARAMETERFV pfnGetObjectParameterfv; + PFNGET_OBJECT_PARAMETERIV pfnGetObjectParameteriv; + + CR_GLSL_CACHE GlobalShaders; +# else + RgnHandle hRootVisibleRegion; + RTSEMFASTMUTEX syncMutex; + EventHandlerUPP hParentEventHandler; + WindowGroupRef pParentGroup; + WindowGroupRef pMasterGroup; + GLint currentBufferName; + uint64_t uiDockUpdateTS; + bool fInit; +# endif +#endif /* RT_OS_DARWIN */ + /* If TRUE, render should tell window server to prevent artificial content + * up-scaling when displayed on HiDPI monitor. */ + bool fUnscaledHiDPI; +} RenderSPU; + +#ifdef RT_OS_WINDOWS + +/* Asks window thread to create new window. + msg.lParam - holds pointer to CREATESTRUCT structure + note that lpCreateParams is used to specify address to store handle of created window + msg.wParam - unused, should be NULL +*/ +#define WM_VBOX_RENDERSPU_CREATE_WINDOW (WM_APP+1) + +typedef struct _VBOX_RENDERSPU_DESTROY_WINDOW { + HWND hWnd; /* handle to window to destroy */ +} VBOX_RENDERSPU_DESTROY_WINDOW; + +/* Asks window thread to destroy previously created window. + msg.lParam - holds pointer to RENDERSPU_VBOX_WINDOW_DESTROY structure + msg.wParam - unused, should be NULL +*/ +#define WM_VBOX_RENDERSPU_DESTROY_WINDOW (WM_APP+2) + +#endif + +extern RenderSPU render_spu; + +/* @todo remove this hack */ +extern uint64_t render_spu_parent_window_id; + +#ifdef CHROMIUM_THREADSAFE +extern CRtsd _RenderTSD; +#define GET_CONTEXT_VAL() ((ContextInfo *) crGetTSD(&_RenderTSD)) +#define SET_CONTEXT_VAL(_v) do { \ + crSetTSD(&_RenderTSD, (_v)); \ + } while (0) +#else +#define GET_CONTEXT_VAL() (render_spu.currentContext) +#define SET_CONTEXT_VAL(_v) do { \ + render_spu.currentContext = (_v); \ + } while (0) + +#endif + +#define GET_CONTEXT(T) ContextInfo *T = GET_CONTEXT_VAL() + + +extern void renderspuSetDefaultSharedContext(ContextInfo *pCtx); +extern void renderspuSetVBoxConfiguration( RenderSPU *spu ); +extern void renderspuMakeVisString( GLbitfield visAttribs, char *s ); +extern VisualInfo *renderspuFindVisual(const char *displayName, GLbitfield visAttribs ); +extern GLboolean renderspu_SystemInitVisual( VisualInfo *visual ); +extern GLboolean renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext ); +extern void renderspu_SystemDestroyContext( ContextInfo *context ); +extern GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ); +extern GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ); +extern void renderspu_SystemDestroyWindow( WindowInfo *window ); +extern void renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ); +extern void renderspu_SystemGetWindowGeometry( WindowInfo *window, GLint *x, GLint *y, GLint *w, GLint *h ); +extern void renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h ); +extern void renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y ); +extern void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects); +extern GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window); +extern int renderspu_SystemInit(); +extern int renderspu_SystemTerm(); +extern void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext); +extern void renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt ); +extern void renderspu_SystemMakeCurrent( WindowInfo *window, GLint windowInfor, ContextInfo *context ); +extern void renderspu_SystemSwapBuffers( WindowInfo *window, GLint flags ); +extern void renderspu_SystemReparentWindow(WindowInfo *window); +extern void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ); +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable); +extern void renderspu_GCWindow(void); +extern int renderspuCreateFunctions( SPUNamedFunctionTable table[] ); +extern GLboolean renderspuVBoxCompositorSet( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor); +extern void renderspuVBoxCompositorClearAll(); +extern int renderspuVBoxCompositorLock(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor); +extern int renderspuVBoxCompositorUnlock(WindowInfo *window); +extern const struct VBOXVR_SCR_COMPOSITOR * renderspuVBoxCompositorAcquire( WindowInfo *window); +extern int renderspuVBoxCompositorTryAcquire(WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR **ppCompositor); +extern void renderspuVBoxCompositorRelease( WindowInfo *window); +extern void renderspuVBoxPresentCompositionGeneric( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, + const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry, int32_t i32MakeCurrentUserData, + bool fRedraw); +extern PCR_BLITTER renderspuVBoxPresentBlitterGet( WindowInfo *window ); +void renderspuVBoxPresentBlitterCleanup( WindowInfo *window ); +extern int renderspuVBoxPresentBlitterEnter( PCR_BLITTER pBlitter, int32_t i32MakeCurrentUserData ); +extern PCR_BLITTER renderspuVBoxPresentBlitterGetAndEnter( WindowInfo *window, int32_t i32MakeCurrentUserData, bool fRedraw ); +extern PCR_BLITTER renderspuVBoxPresentBlitterEnsureCreated( WindowInfo *window, int32_t i32MakeCurrentUserData ); +WindowInfo* renderspuWinCreate(GLint visBits, GLint id); +void renderspuWinTermOnShutdown(WindowInfo *window); +void renderspuWinTerm( WindowInfo *window ); +void renderspuWinCleanup(WindowInfo *window); +void renderspuWinDestroy(WindowInfo *window); +GLboolean renderspuWinInitWithVisual( WindowInfo *window, VisualInfo *visual, GLboolean showIt, GLint id ); +GLboolean renderspuWinInit(WindowInfo *pWindow, const char *dpyName, GLint visBits, GLint id); + +DECLINLINE(void) renderspuWinRetain(WindowInfo *window) +{ + ASMAtomicIncU32(&window->cRefs); +} + +DECLINLINE(bool) renderspuWinIsTermed(WindowInfo *window) +{ + return window->BltInfo.Base.id < 0; +} + +DECLINLINE(void) renderspuWinRelease(WindowInfo *window) +{ + uint32_t cRefs = ASMAtomicDecU32(&window->cRefs); + if (!cRefs) + { + renderspuWinDestroy(window); + } +} + +extern WindowInfo* renderspuGetDummyWindow(GLint visBits); +extern void renderspuPerformMakeCurrent(WindowInfo *window, GLint nativeWindow, ContextInfo *context); +extern GLboolean renderspuInitVisual(VisualInfo *pVisInfo, const char *displayName, GLbitfield visAttribs); +extern void renderspuVBoxCompositorBlit ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter); +extern void renderspuVBoxCompositorBlitStretched ( const struct VBOXVR_SCR_COMPOSITOR * pCompositor, PCR_BLITTER pBlitter, GLfloat scaleX, GLfloat scaleY); +extern GLint renderspuCreateContextEx(const char *dpyName, GLint visBits, GLint id, GLint shareCtx); +extern GLint renderspuWindowCreateEx( const char *dpyName, GLint visBits, GLint id ); + +extern GLint RENDER_APIENTRY renderspuWindowCreate( const char *dpyName, GLint visBits ); +void RENDER_APIENTRY renderspuWindowDestroy( GLint win ); +extern GLint RENDER_APIENTRY renderspuCreateContext( const char *dpyname, GLint visBits, GLint shareCtx ); +extern void RENDER_APIENTRY renderspuMakeCurrent(GLint crWindow, GLint nativeWindow, GLint ctx); +extern void RENDER_APIENTRY renderspuSwapBuffers( GLint window, GLint flags ); + +extern uint32_t renderspuContextMarkDeletedAndRelease( ContextInfo *context ); + +int renderspuDefaultCtxInit(); +void renderspuCleanupBase(bool fDeleteTables); + +ContextInfo * renderspuDefaultSharedContextAcquire(); +void renderspuDefaultSharedContextRelease(ContextInfo * pCtx); +uint32_t renderspuContextRelease(ContextInfo *context); +uint32_t renderspuContextRetain(ContextInfo *context); + +bool renderspuCalloutAvailable(); +bool renderspuCalloutClient(PFNVCRSERVER_CLIENT_CALLOUT_CB pfnCb, void *pvCb); + + +#ifdef __cplusplus +extern "C" { +#endif +DECLEXPORT(void) renderspuSetWindowId(uint64_t winId); +DECLEXPORT(void) renderspuReparentWindow(GLint window); +DECLEXPORT(void) renderspuSetUnscaledHiDPI(bool fEnable); +#ifdef __cplusplus +} +#endif + +#endif /* CR_RENDERSPU_H */ diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c new file mode 100644 index 00000000..de55160e --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_agl.c @@ -0,0 +1,907 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include <Carbon/Carbon.h> +#include <AGL/agl.h> +#include <OpenGL/OpenGL.h> + +#include <iprt/time.h> +#include <iprt/assert.h> +#include <iprt/semaphore.h> + +#include <stdio.h> + +#include "cr_environment.h" +#include "cr_error.h" +#include "cr_string.h" +#include "cr_mem.h" +#include "renderspu.h" + +#ifdef __LP64__ /** @todo port to 64-bit darwin. */ +# define renderspuSetWindowContext(w, c) \ + AssertFailed() +# define renderspuGetWindowContext(w) \ + ( (ContextInfo *) GetWRefCon( ((w)->nativeWindow ? (w)->nativeWindow : (w)->window) ) ) +#else +# define renderspuSetWindowContext(w, c) \ + ( SetWRefCon( (w), (unsigned long) (c) ) ) +# define renderspuGetWindowContext(w) \ + ( (ContextInfo *) GetWRefCon( ((w)->nativeWindow ? (w)->nativeWindow : (w)->window) ) ) +#endif + +/* Debug macros */ +#ifdef DEBUG_poetzsch +#define DEBUG_MSG_POETZSCH(text) \ + printf text +#else +#define DEBUG_MSG_POETZSCH(text) \ + do {} while (0) +#endif + +#define DEBUG_MSG_RESULT(result, text) \ + crDebug(text" (%d; %s:%d)", (int)(result), __FILE__, __LINE__) + +#define CHECK_CARBON_RC(result, text) \ + if((result) != noErr) \ + DEBUG_MSG_RESULT(result, text); + +#define CHECK_CARBON_RC_RETURN(result, text, ret) \ + if((result) != noErr) \ + { \ + DEBUG_MSG_RESULT(result, text); \ + return ret; \ + } + +#define CHECK_CARBON_RC_RETURN_VOID(result, text) \ + CHECK_CARBON_RC_RETURN(result, text,) + +#define CHECK_AGL_RC(result, text) \ + if(!(result)) \ + { \ + GLenum error = render_spu.ws.aglGetError(); \ + DEBUG_MSG_RESULT(result, text); \ + } + +static void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window); +static void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects); + +/* In some case (like compiz which doesn't provide us with clipping regions) we + * have to make sure that *all* open OpenGL windows are clipped to the main + * application window. This is done here when called from the event handler + * which monitor bounding changes of the main window. */ +static void crClipRootHelper(unsigned long key, void *data1, void *data2) +{ + /* The window with id zero is the base window, which isn't displayed at + * all. So ignore it. */ + if (key > 0) + { + /* Fetch the actually window info & the user data */ + WindowInfo *pWin = (WindowInfo *) data1; + /* We need to assign the context with this window */ + ContextInfo *context = renderspuGetWindowContext(pWin); + if (context && + context->context) + { + RTSemFastMutexRequest(render_spu.syncMutex); + GLboolean result = render_spu.ws.aglSetCurrentContext(context->context); + CHECK_AGL_RC (result, "Render SPU (crClipRootHelper): SetCurrentContext Failed"); + if (result) + { + result = render_spu.ws.aglUpdateContext(context->context); + CHECK_AGL_RC (result, "Render SPU (crClipRootHelper): UpdateContext Failed"); + /* Update the clipping region */ + renderspu_SystemWindowApplyVisibleRegion(pWin); + } + RTSemFastMutexRelease(render_spu.syncMutex); + /* Make sure that the position is updated relative to the Qt main + * view */ + renderspu_SystemWindowPosition(pWin, pWin->x, pWin->y); + } + } +} + +/* Window event handler */ +pascal OSStatus +windowEvtHndlr(EventHandlerCallRef myHandler, EventRef event, void* userData) +{ + WindowRef window = NULL; + OSStatus eventResult = eventNotHandledErr; + UInt32 class = GetEventClass (event); + UInt32 kind = GetEventKind (event); + + /* If we aren't initialized or even deinitialized already (as on VM + * shutdown) do nothing. */ + if (!render_spu.fInit) + return eventNotHandledErr; + + /* Fetch the sender of the event */ + GetEventParameter(event, kEventParamDirectObject, typeWindowRef, + NULL, sizeof(WindowRef), NULL, &window); + switch (class) + { + case kEventClassVBox: + { + switch (kind) + { + case kEventVBoxUpdateContext: + { +#ifndef __LP64__ /** @todo port to 64-bit darwin! Need to check if this event is generated or not (it probably isn't). */ + WindowInfo *wi1; + GetEventParameter(event, kEventParamUserData, typeVoidPtr, + NULL, sizeof(wi1), NULL, &wi1); + ContextInfo *context = renderspuGetWindowContext(wi1); + if (context && + context->context) + { + AGLContext tmpContext = render_spu.ws.aglGetCurrentContext(); + DEBUG_MSG_POETZSCH (("kEventVBoxUpdateContext %x %x\n", wi1, context->context)); + RTSemFastMutexRequest(render_spu.syncMutex); + GLboolean result = render_spu.ws.aglSetCurrentContext(context->context); + if (result) + { + result = render_spu.ws.aglUpdateContext(context->context); + CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): UpdateContext Failed"); + renderspu_SystemWindowApplyVisibleRegion(wi1); + /* Reapply the last active context */ + if (tmpContext) + { + result = render_spu.ws.aglSetCurrentContext(tmpContext); + CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): SetCurrentContext Failed"); + if (result) + { + result = render_spu.ws.aglUpdateContext(tmpContext); + CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): UpdateContext Failed"); + } + } + } + RTSemFastMutexRelease(render_spu.syncMutex); + } + eventResult = noErr; +#endif + break; + } + case kEventVBoxBoundsChanged: + { +#ifndef __LP64__ /** @todo port to 64-bit darwin! Need to check if this event is generated or not (it probably isn't). */ + HIPoint p; + GetEventParameter(event, kEventParamOrigin, typeHIPoint, + NULL, sizeof(p), NULL, &p); + HISize s; + GetEventParameter(event, kEventParamDimensions, typeHISize, + NULL, sizeof(s), NULL, &s); + HIRect r = CGRectMake (0, 0, s.width, s.height); + DEBUG_MSG_POETZSCH (("kEventVBoxBoundsChanged %f %f %f %f\n", p.x, p.y, s.width, s.height)); + GLint l[4] = { 0, + 0, + r.size.width, + r.size.height }; + /* Update the root window clip region */ + renderspu_SystemSetRootVisibleRegion(1, l); + /* Temporary save the current active context */ + AGLContext tmpContext = render_spu.ws.aglGetCurrentContext(); + crHashtableWalk(render_spu.windowTable, crClipRootHelper, NULL); + /* Reapply the last active context */ + if (tmpContext) + { + RTSemFastMutexRequest(render_spu.syncMutex); + GLboolean result = render_spu.ws.aglSetCurrentContext(tmpContext); + CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): SetCurrentContext Failed"); + /* Doesn't work with DirectX; Anyway doesn't */ +/* if (result)*/ +/* {*/ +/* result = render_spu.ws.aglUpdateContext(tmpContext);*/ +/* CHECK_AGL_RC (result, "Render SPU (windowEvtHndlr): UpdateContext Failed");*/ +/* }*/ + RTSemFastMutexRelease(render_spu.syncMutex); + } + eventResult = noErr; +#endif + break; + } + }; + break; + } + break; + }; + + return eventResult; +} + +GLboolean +renderspu_SystemInitVisual(VisualInfo *visual) +{ + if(visual->visAttribs & CR_PBUFFER_BIT) + crWarning("Render SPU (renderspu_SystemInitVisual): PBuffers not support on Darwin/AGL yet."); + + return GL_TRUE; +} + +GLboolean +renderspuChoosePixelFormat(ContextInfo *context, AGLPixelFormat *pix) +{ + GLbitfield visAttribs = context->visual->visAttribs; + GLint attribs[32]; + GLint ind = 0; + +#define ATTR_ADD(s) ( attribs[ind++] = (s) ) +#define ATTR_ADDV(s,v) ( ATTR_ADD((s)), ATTR_ADD((v)) ) + + CRASSERT(render_spu.ws.aglChoosePixelFormat); + + ATTR_ADD(AGL_RGBA); +/* ATTR_ADDV(AGL_RED_SIZE, 1); + ATTR_ADDV(AGL_GREEN_SIZE, 1); + ATTR_ADDV(AGL_BLUE_SIZE, 1); */ + +/* if( render_spu.fullscreen )*/ +/* ATTR_ADD(AGL_FULLSCREEN);*/ + + if( visAttribs & CR_ALPHA_BIT ) + ATTR_ADDV(AGL_ALPHA_SIZE, 1); + + if( visAttribs & CR_DOUBLE_BIT ) + ATTR_ADD(AGL_DOUBLEBUFFER); + + if( visAttribs & CR_STEREO_BIT ) + ATTR_ADD(AGL_STEREO); + + if( visAttribs & CR_DEPTH_BIT ) + ATTR_ADDV(AGL_DEPTH_SIZE, 1); + + if( visAttribs & CR_STENCIL_BIT ) + ATTR_ADDV(AGL_STENCIL_SIZE, 1); + + if( visAttribs & CR_ACCUM_BIT ) { + ATTR_ADDV(AGL_ACCUM_RED_SIZE, 1); + ATTR_ADDV(AGL_ACCUM_GREEN_SIZE, 1); + ATTR_ADDV(AGL_ACCUM_BLUE_SIZE, 1); + if( visAttribs & CR_ALPHA_BIT ) + ATTR_ADDV(AGL_ACCUM_ALPHA_SIZE, 1); + } + + if( visAttribs & CR_MULTISAMPLE_BIT ) { + ATTR_ADDV(AGL_SAMPLE_BUFFERS_ARB, 1); + ATTR_ADDV(AGL_SAMPLES_ARB, 4); + } + + if( visAttribs & CR_OVERLAY_BIT ) + ATTR_ADDV(AGL_LEVEL, 1); + + ATTR_ADD(AGL_NONE); + + *pix = render_spu.ws.aglChoosePixelFormat( NULL, 0, attribs ); + + return (*pix != NULL); +} + +void +renderspuDestroyPixelFormat(ContextInfo *context, AGLPixelFormat *pix) +{ + render_spu.ws.aglDestroyPixelFormat( *pix ); + *pix = NULL; +} + +GLboolean +renderspu_SystemCreateContext(VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext) +{ + AGLPixelFormat pix; + + (void) sharedContext; + CRASSERT(visual); + CRASSERT(context); + + context->visual = visual; + + if( !renderspuChoosePixelFormat(context, &pix) ) { + crError( "Render SPU (renderspu_SystemCreateContext): Unable to create pixel format" ); + return GL_FALSE; + } + + context->context = render_spu.ws.aglCreateContext( pix, NULL ); + renderspuDestroyPixelFormat( context, &pix ); + + if( !context->context ) { + crError( "Render SPU (renderspu_SystemCreateContext): Could not create rendering context" ); + return GL_FALSE; + } + + return GL_TRUE; +} + +void +renderspu_SystemDestroyContext(ContextInfo *context) +{ + if(!context) + return; + + render_spu.ws.aglSetDrawable(context->context, NULL); + render_spu.ws.aglSetCurrentContext(NULL); + if(context->context) + { + render_spu.ws.aglDestroyContext(context->context); + context->context = NULL; + } + + context->visual = NULL; +} + +void +renderspuFullscreen(WindowInfo *window, GLboolean fullscreen) +{ + /* Real fullscreen isn't supported by VirtualBox */ +} + +GLboolean +renderspuWindowAttachContext(WindowInfo *wi, WindowRef window, + ContextInfo *context) +{ + GLboolean result; + + if(!context || !wi) + return render_spu.ws.aglSetCurrentContext( NULL ); + +/* DEBUG_MSG_POETZSCH (("WindowAttachContext %d\n", wi->BltInfo.Base.id));*/ + + /* Flush old context first */ + if (context->currentWindow->window != window) + render_spu.self.Flush(); + /* If the window buffer name is uninitialized we have to create a new + * dummy context. */ + if (wi->bufferName == -1) + { + DEBUG_MSG_POETZSCH (("WindowAttachContext: create context %d\n", wi->BltInfo.Base.id)); + /* Use the same visual bits as those in the context structure */ + AGLPixelFormat pix; + if( !renderspuChoosePixelFormat(context, &pix) ) + { + crError( "Render SPU (renderspuWindowAttachContext): Unable to create pixel format" ); + return GL_FALSE; + } + /* Create the dummy context */ + wi->dummyContext = render_spu.ws.aglCreateContext( pix, NULL ); + renderspuDestroyPixelFormat( context, &pix ); + if( !wi->dummyContext ) + { + crError( "Render SPU (renderspuWindowAttachContext): Could not create rendering context" ); + return GL_FALSE; + } + AGLDrawable drawable; +#ifdef __LP64__ /** @todo port to 64-bit darwin. */ + drawable = NULL; +#else + drawable = (AGLDrawable) GetWindowPort(window); +#endif + /* New global buffer name */ + wi->bufferName = render_spu.currentBufferName++; + /* Set the new buffer name to the dummy context. This enable the + * sharing of the same hardware buffer afterwards. */ + result = render_spu.ws.aglSetInteger(wi->dummyContext, AGL_BUFFER_NAME, &wi->bufferName); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetInteger Failed"); + /* Assign the dummy context to the window */ + result = render_spu.ws.aglSetDrawable(wi->dummyContext, drawable); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetDrawable Failed"); + } + + AGLDrawable oldDrawable; + AGLDrawable newDrawable; + + oldDrawable = render_spu.ws.aglGetDrawable(context->context); +#ifdef __LP64__ /** @todo port to 64-bit darwin. */ + newDrawable = oldDrawable; +#else + newDrawable = (AGLDrawable) GetWindowPort(window); +#endif + RTSemFastMutexRequest(render_spu.syncMutex); + /* Only switch the context if the drawable has changed */ + if (oldDrawable != newDrawable) + { + /* Reset the current context */ + result = render_spu.ws.aglSetDrawable(context->context, NULL); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetDrawable Failed"); + /* Set the buffer name of the dummy context to the current context + * also. After that both share the same hardware buffer. */ + render_spu.ws.aglSetInteger (context->context, AGL_BUFFER_NAME, &wi->bufferName); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetInteger Failed"); + /* Set the new drawable */ +#ifdef __LP64__ /** @todo port to 64-bit darwin. */ + result = -1; +#else + result = render_spu.ws.aglSetDrawable(context->context, newDrawable); +#endif + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetDrawable Failed"); + renderspuSetWindowContext(window, context); + } + result = render_spu.ws.aglSetCurrentContext(context->context); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): SetCurrentContext Failed"); + result = render_spu.ws.aglUpdateContext(context->context); + CHECK_AGL_RC (result, "Render SPU (renderspuWindowAttachContext): UpdateContext Failed"); + RTSemFastMutexRelease(render_spu.syncMutex); + + return result; +} + +GLboolean +renderspu_SystemCreateWindow(VisualInfo *visual, GLboolean showIt, + WindowInfo *window) +{ + return GL_TRUE; +} + +void renderspu_SystemReparentWindow(WindowInfo *) +{ + /* stub only */ +} + +void +renderspu_SystemDestroyWindow(WindowInfo *window) +{ + CRASSERT(window); + CRASSERT(window->visual); + + if(!window->nativeWindow) + { + EventRef evt; + OSStatus status = CreateEvent(NULL, kEventClassVBox, kEventVBoxDisposeWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemDestroyWindow): CreateEvent Failed"); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemDestroyWindow): SetEventParameter Failed"); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemDestroyWindow): PostEventToQueue Failed"); + } + + /* Delete the dummy context */ + if(window->dummyContext) + { + render_spu.ws.aglSetDrawable(window->dummyContext, NULL); + render_spu.ws.aglDestroyContext(window->dummyContext); + window->dummyContext = NULL; + } + + /* Reset some values */ + window->bufferName = -1; + window->visual = NULL; + window->window = NULL; + + if (window->hVisibleRegion) + { + DisposeRgn(window->hVisibleRegion); + window->hVisibleRegion = 0; + } +} + +void +renderspu_SystemWindowPosition(WindowInfo *window, + GLint x, GLint y) +{ + CRASSERT(window); + CRASSERT(window->window); + + OSStatus status = noErr; + /* Send a event to the main thread, cause some function of Carbon aren't + * thread safe */ + EventRef evt; + status = CreateEvent(NULL, kEventClassVBox, kEventVBoxMoveWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): CreateEvent Failed"); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof(window->window), &window->window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): SetEventParameter Failed"); + HIPoint p = CGPointMake (x, y); + status = SetEventParameter(evt, kEventParamOrigin, typeHIPoint, sizeof (p), &p); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): SetEventParameter Failed"); + status = SetEventParameter(evt, kEventParamUserData, typeVoidPtr, sizeof (window), &window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): SetEventParameter Failed"); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowPosition): PostEventToQueue Failed"); + + /* save the new pos */ + window->x = x; + window->y = y; +} + +void +renderspu_SystemWindowSize(WindowInfo *window, GLint w, GLint h) +{ + CRASSERT(window); + CRASSERT(window->window); + + OSStatus status = noErr; + /* Send a event to the main thread, cause some function of Carbon aren't + * thread safe */ + EventRef evt; + status = CreateEvent(NULL, kEventClassVBox, kEventVBoxResizeWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): CreateEvent Failed "); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof(window->window), &window->window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SetEventParameter Failed"); + HISize s = CGSizeMake (w, h); + status = SetEventParameter(evt, kEventParamDimensions, typeHISize, sizeof (s), &s); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SetEventParameter Failed"); + status = SetEventParameter(evt, kEventParamUserData, typeVoidPtr, sizeof (window), &window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SetEventParameter Failed"); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowSize): SendEventToEventTarget Failed"); + + DEBUG_MSG_POETZSCH (("Size %d visible %d\n", window->BltInfo.Base.id, IsWindowVisible (window->window))); + /* save the new size */ + window->BltInfo.width = w; + window->BltInfo.height = h; +} + +void +renderspu_SystemGetWindowGeometry(WindowInfo *window, + GLint *x, GLint *y, + GLint *w, GLint *h) +{ + CRASSERT(window); + CRASSERT(window->window); + + OSStatus status = noErr; + Rect r; + status = GetWindowBounds(window->window, kWindowStructureRgn, &r); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemGetWindowGeometry): GetWindowBounds Failed"); + + *x = (int) r.left; + *y = (int) r.top; + *w = (int) (r.right - r.left); + *h = (int) (r.bottom - r.top); +} + +void +renderspu_SystemGetMaxWindowSize(WindowInfo *window, + GLint *w, GLint *h) +{ + CRASSERT(window); + CRASSERT(window->window); + + OSStatus status = noErr; + HISize s; +#ifdef __LP64__ /** @todo port to 64-bit darwin. */ + status = -1; +#else + status = GetWindowResizeLimits (window->window, NULL, &s); +#endif + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemGetMaxWindowSize): GetWindowResizeLimits Failed"); + + *w = s.width; + *h = s.height; +} + +/* Either show or hide the render SPU's window. */ +void +renderspu_SystemShowWindow(WindowInfo *window, GLboolean showIt) +{ + CRASSERT(window); + CRASSERT(window->window); + + if (!IsValidWindowPtr(window->window)) + return; + + if(showIt) + { + /* Force moving the win to the right position before we show it */ + renderspu_SystemWindowPosition (window, window->x, window->y); + OSStatus status = noErr; + /* Send a event to the main thread, cause some function of Carbon + * aren't thread safe */ + EventRef evt; + status = CreateEvent(NULL, kEventClassVBox, kEventVBoxShowWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): CreateEvent Failed"); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): SetEventParameter Failed"); + status = SetEventParameter(evt, kEventParamUserData, typeVoidPtr, sizeof (window), &window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemWindowShow): SetEventParameter Failed"); + //status = SendEventToEventTarget (evt, GetWindowEventTarget (HIViewGetWindow ((HIViewRef)render_spu_parent_window_id))); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): PostEventToQueue Failed"); + } + else + { + EventRef evt; + OSStatus status = CreateEvent(NULL, kEventClassVBox, kEventVBoxHideWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): CreateEvent Failed"); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): SetEventParameter Failed"); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemShowWindow): PostEventToQueue Failed"); + } +} + +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0, false); +} + +void +renderspu_SystemMakeCurrent(WindowInfo *window, GLint nativeWindow, + ContextInfo *context) +{ + Boolean result; +/* DEBUG_MSG_POETZSCH (("makecurrent %d: \n", window->BltInfo.Base.id));*/ + + CRASSERT(render_spu.ws.aglSetCurrentContext); + //crDebug( "renderspu_SystemMakeCurrent( %x, %i, %x )", window, nativeWindow, context ); + + nativeWindow = 0; + + if(window && context) + { + CRASSERT(window->window); + CRASSERT(context->context); + + if(window->visual != context->visual) + { + crDebug("Render SPU (renderspu_SystemMakeCurrent): MakeCurrent visual mismatch (0x%x != 0x%x); remaking window.", + (uint)window->visual->visAttribs, (uint)context->visual->visAttribs); + /* + * XXX have to revisit this issue!!! + * + * But for now we destroy the current window + * and re-create it with the context's visual abilities + */ + renderspu_SystemDestroyWindow(window); + renderspu_SystemCreateWindow(context->visual, window->visible, + window); + } + + /* This is the normal case: rendering to the render SPU's own window */ + result = renderspuWindowAttachContext(window, window->window, + context); + /* XXX this is a total hack to work around an NVIDIA driver bug */ + if(render_spu.self.GetFloatv && context->haveWindowPosARB) + { + GLfloat f[4]; + render_spu.self.GetFloatv(GL_CURRENT_RASTER_POSITION, f); + if (!window->everCurrent || f[1] < 0.0) + { + crDebug("Render SPU (renderspu_SystemMakeCurrent): Resetting raster pos"); + render_spu.self.WindowPos2iARB(0, 0); + } + } + /* Reapply the visible regions */ + renderspu_SystemWindowApplyVisibleRegion(window); + } + else + renderspuWindowAttachContext (0, 0, 0); +} + +void +renderspu_SystemSwapBuffers(WindowInfo *window, GLint flags) +{ + CRASSERT(window); + CRASSERT(window->window); + + ContextInfo *context = renderspuGetWindowContext(window); + + if(!context) + crError("Render SPU (renderspu_SystemSwapBuffers): SwapBuffers got a null context from the window"); + + RTSemFastMutexRequest(render_spu.syncMutex); +// DEBUG_MSG_POETZSCH (("Swapped %d context %x visible: %d\n", window->BltInfo.Base.id, context->context, IsWindowVisible (window->window))); + if (context->visual && + context->visual->visAttribs & CR_DOUBLE_BIT) + render_spu.ws.aglSwapBuffers(context->context); + else + glFlush(); + RTSemFastMutexRelease(render_spu.syncMutex); + + /* This method seems called very often. To prevent the dock using all free + * resources we update the dock only two times per second. */ + uint64_t curTS = RTTimeMilliTS(); + if ((curTS - render_spu.uiDockUpdateTS) > 500) + { + OSStatus status = noErr; + /* Send a event to the main thread, cause some function of Carbon aren't + * thread safe */ + EventRef evt; + status = CreateEvent(NULL, kEventClassVBox, kEventVBoxUpdateDock, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemSwapBuffers): CreateEvent Failed"); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN_VOID (status, "Render SPU (renderspu_SystemSwapBuffers): PostEventToQueue Failed"); + + render_spu.uiDockUpdateTS = curTS; + } +} + +GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window) +{ + return GL_FALSE; +} + +void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects) +{ + CRASSERT(window); + CRASSERT(window->window); + + /* Remember any additional clipping stuff e.g. seamless regions */ + if (window->hVisibleRegion) + { + DisposeRgn(window->hVisibleRegion); + window->hVisibleRegion = 0; + } + + if (cRects>0) + { + int i; + /* Create some temporary regions */ + RgnHandle rgn = NewRgn(); + SetEmptyRgn (rgn); + RgnHandle tmpRgn = NewRgn(); + for (i=0; i<cRects; ++i) + { + SetRectRgn (tmpRgn, + pRects[4*i] , pRects[4*i+1], + pRects[4*i+2], pRects[4*i+3]); + //DEBUG_MSG_POETZSCH (("visible rect %d %d %d %d\n", pRects[4*i] , pRects[4*i+1], + // pRects[4*i+2], pRects[4*i+3])); + UnionRgn (rgn, tmpRgn, rgn); + } + DisposeRgn (tmpRgn); + window->hVisibleRegion = rgn; + } + + renderspu_SystemWindowApplyVisibleRegion(window); +} + +static void renderspu_SystemSetRootVisibleRegion(GLint cRects, GLint *pRects) +{ + /* Remember the visible region of the root window if there is one */ + if (render_spu.hRootVisibleRegion) + { + DisposeRgn(render_spu.hRootVisibleRegion); + render_spu.hRootVisibleRegion = 0; + } + + if (cRects>0) + { + int i; + render_spu.hRootVisibleRegion = NewRgn(); + SetEmptyRgn (render_spu.hRootVisibleRegion); + RgnHandle tmpRgn = NewRgn(); + for (i=0; i<cRects; ++i) + { + SetRectRgn (tmpRgn, + pRects[4*i] , pRects[4*i+1], + pRects[4*i+2], pRects[4*i+3]); + UnionRgn (render_spu.hRootVisibleRegion, tmpRgn, render_spu.hRootVisibleRegion); + } + DisposeRgn (tmpRgn); + } +} + +/*Assumes that all regions are in the guest coordinates system*/ +static void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *window) +{ + ContextInfo *c = renderspuGetWindowContext(window); + RgnHandle rgn; + GLboolean result = true; + + DEBUG_MSG_POETZSCH (("ApplyVisibleRegion %x\n", window)); + + if (!c || !c->context) return; + + rgn = NewRgn(); + SetEmptyRgn(rgn); + + if (render_spu.hRootVisibleRegion) + { + /* The render_spu.hRootVisibleRegion has coordinates from the root + * window. We intersect it with the rect of the OpenGL window we + * currently process. */ + SetRectRgn(rgn, + window->x, window->y, + window->x + window->BltInfo.width, + window->y + window->BltInfo.height); + SectRgn(render_spu.hRootVisibleRegion, rgn, rgn); + /* Because the clipping is done in the coordinate space of the OpenGL + * window we have to remove the x/y position from the newly created + * region. */ + OffsetRgn (rgn, -window->x, -window->y); + } + else + { + /* If there is not root clipping region is available, create a base + * region with the size of the target window. This covers all + * needed/possible space. */ + SetRectRgn(rgn, 0, 0, window->BltInfo.width, window->BltInfo.height); + } + + /* Now intersect the window clipping region with a additional region e.g. + * for the seamless mode. */ + if (window->hVisibleRegion) + SectRgn(rgn, window->hVisibleRegion, rgn); + + if (rgn && !EmptyRgn(rgn)) + { + /* Set the clip region to the context */ + result = render_spu.ws.aglSetInteger(c->context, AGL_CLIP_REGION, (const GLint*)rgn); + CHECK_AGL_RC (result, "Render SPU (renderspu_SystemWindowVisibleRegion): SetInteger Failed"); + result = render_spu.ws.aglEnable(c->context, AGL_CLIP_REGION); + CHECK_AGL_RC (result, "Render SPU (renderspu_SystemWindowVisibleRegion): Enable Failed"); + } + /* Clear the region structure */ + DisposeRgn (rgn); +} + +GLboolean +renderspu_SystemVBoxCreateWindow(VisualInfo *visual, GLboolean showIt, + WindowInfo *window) +{ + CRASSERT(visual); + CRASSERT(window); + + WindowAttributes winAttr = kWindowNoShadowAttribute | kWindowCompositingAttribute | kWindowIgnoreClicksAttribute | kWindowStandardHandlerAttribute | kWindowLiveResizeAttribute; + WindowClass winClass = kOverlayWindowClass; + Rect windowRect; + OSStatus status = noErr; + + window->visual = visual; + window->nativeWindow = NULL; + + if(window->window && IsValidWindowPtr(window->window)) + { + EventRef evt; + status = CreateEvent(NULL, kEventClassVBox, kEventVBoxDisposeWindow, 0, kEventAttributeNone, &evt); + CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): CreateEvent Failed", false); + status = SetEventParameter(evt, kEventParamWindowRef, typeWindowRef, sizeof (window->window), &window->window); + CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): SetEventParameter Failed", false); + status = PostEventToQueue(GetMainEventQueue(), evt, kEventPriorityStandard); + CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): PostEventToQueue Failed", false); + } + + windowRect.left = window->x; + windowRect.top = window->y; + windowRect.right = window->x + window->BltInfo.width; + windowRect.bottom = window->y + window->BltInfo.height; + + status = CreateNewWindow(winClass, winAttr, &windowRect, &window->window); + CHECK_CARBON_RC_RETURN (status, "Render SPU (renderspu_SystemVBoxCreateWindow): CreateNewWindow Failed", GL_FALSE); + + /* We set a title for debugging purposes */ + CFStringRef title_string; + title_string = CFStringCreateWithCStringNoCopy(NULL, window->title, + kCFStringEncodingMacRoman, NULL); + SetWindowTitleWithCFString(window->BltInfo.window, title_string); + CFRelease(title_string); + + /* The parent has to be in its own group */ + WindowRef parent = NULL; + if (render_spu_parent_window_id) + { + parent = HIViewGetWindow ((HIViewRef)render_spu_parent_window_id); + SetWindowGroup (parent, render_spu.pParentGroup); + + } + + /* Add the new window to the master group */ + SetWindowGroup(window->window, render_spu.pMasterGroup); + + /* This will be initialized on the first attempt to attach the global + * context to this new window */ + window->bufferName = -1; + window->dummyContext = NULL; + window->hVisibleRegion = 0; + + if(showIt) + renderspu_SystemShowWindow(window, GL_TRUE); + + crDebug("Render SPU (renderspu_SystemVBoxCreateWindow): actual window (x, y, width, height): %d, %d, %d, %d", + window->x, window->y, window->BltInfo.width, window->BltInfo.height); + + return GL_TRUE; +} + +int renderspu_SystemInit() +{ + return VINF_SUCCESS; +} + +int renderspu_SystemTerm() +{ + return VINF_SUCCESS; +} + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c new file mode 100644 index 00000000..583398a3 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa.c @@ -0,0 +1,479 @@ +/* $Id: renderspu_cocoa.c $ */ +/** @file + * VirtualBox OpenGL Cocoa Window System implementation + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +#include <OpenGL/OpenGL.h> + +#include "renderspu.h" +#include <iprt/process.h> +#include <iprt/string.h> +#include <iprt/path.h> + +#include <cr_string.h> +#include <cr_mem.h> + +GLboolean renderspu_SystemInitVisual(VisualInfo *pVisInfo) +{ + CRASSERT(pVisInfo); + +/* cocoaGLVisualCreate(&pCtxInfo->context);*/ + + return GL_TRUE; +} + +GLboolean renderspu_SystemCreateContext(VisualInfo *pVisInfo, ContextInfo *pCtxInfo, ContextInfo *pSharedCtxInfo) +{ + CRASSERT(pVisInfo); + CRASSERT(pCtxInfo); + + pCtxInfo->currentWindow = NULL; + + cocoaGLCtxCreate(&pCtxInfo->context, pVisInfo->visAttribs, pSharedCtxInfo ? pSharedCtxInfo->context : NULL); + + return GL_TRUE; +} + +void renderspu_SystemDestroyContext(ContextInfo *pCtxInfo) +{ + if(!pCtxInfo) + return; + + if(pCtxInfo->context) + { + cocoaGLCtxDestroy(pCtxInfo->context); + pCtxInfo->context = NULL; + } +} + +void renderspuFullscreen(WindowInfo *pWinInfo, GLboolean fFullscreen) +{ + /* Real fullscreen isn't supported by VirtualBox */ +} + +GLboolean renderspu_SystemVBoxCreateWindow(VisualInfo *pVisInfo, GLboolean fShowIt, WindowInfo *pWinInfo) +{ + CRASSERT(pVisInfo); + CRASSERT(pWinInfo); + + /* VirtualBox is the only frontend which support 3D right now. */ + char pszName[256]; + if (RTProcGetExecutablePath(pszName, sizeof(pszName))) + /* Check for VirtualBox and VirtualBoxVM */ + if (RTStrNICmp(RTPathFilename(pszName), "VirtualBox", 10) != 0) + return GL_FALSE; + + pWinInfo->visual = pVisInfo; + pWinInfo->window = NULL; + pWinInfo->nativeWindow = NULL; + pWinInfo->currentCtx = NULL; + + NativeNSViewRef pParentWin = (NativeNSViewRef)(uintptr_t)render_spu_parent_window_id; + + cocoaViewCreate(&pWinInfo->window, pWinInfo, pParentWin, pVisInfo->visAttribs); + + if (fShowIt) + renderspu_SystemShowWindow(pWinInfo, fShowIt); + + return GL_TRUE; +} + +void renderspu_SystemReparentWindow(WindowInfo *pWinInfo) +{ + NativeNSViewRef pParentWin = (NativeNSViewRef)(uintptr_t)render_spu_parent_window_id; + cocoaViewReparent(pWinInfo->window, pParentWin); +} + +void renderspu_SystemDestroyWindow(WindowInfo *pWinInfo) +{ + CRASSERT(pWinInfo); + + cocoaViewDestroy(pWinInfo->window); +} + +void renderspu_SystemWindowPosition(WindowInfo *pWinInfo, GLint x, GLint y) +{ + CRASSERT(pWinInfo); + NativeNSViewRef pParentWin = (NativeNSViewRef)(uintptr_t)render_spu_parent_window_id; + + /*pParentWin is unused in the call, otherwise it might hold incorrect value if for ex. last reparent call was for + a different screen*/ + cocoaViewSetPosition(pWinInfo->window, pParentWin, x, y); +} + +void renderspu_SystemWindowSize(WindowInfo *pWinInfo, GLint w, GLint h) +{ + CRASSERT(pWinInfo); + + cocoaViewSetSize(pWinInfo->window, w, h); +} + +void renderspu_SystemGetWindowGeometry(WindowInfo *pWinInfo, GLint *pX, GLint *pY, GLint *pW, GLint *pH) +{ + CRASSERT(pWinInfo); + + cocoaViewGetGeometry(pWinInfo->window, pX, pY, pW, pH); +} + +void renderspu_SystemGetMaxWindowSize(WindowInfo *pWinInfo, GLint *pW, GLint *pH) +{ + CRASSERT(pWinInfo); + + *pW = 10000; + *pH = 10000; +} + +void renderspu_SystemShowWindow(WindowInfo *pWinInfo, GLboolean fShowIt) +{ + CRASSERT(pWinInfo); + + cocoaViewShow(pWinInfo->window, fShowIt); +} + +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + cocoaViewPresentComposition(window->window, pChangedEntry); +} + +void renderspu_SystemMakeCurrent(WindowInfo *pWinInfo, GLint nativeWindow, ContextInfo *pCtxInfo) +{ +/* if(pWinInfo->visual != pCtxInfo->visual)*/ +/* printf ("visual mismatch .....................\n");*/ + + nativeWindow = 0; + + if (pWinInfo && pCtxInfo) + cocoaViewMakeCurrentContext(pWinInfo->window, pCtxInfo->context); + else + cocoaViewMakeCurrentContext(NULL, NULL); +} + +void renderspu_SystemSwapBuffers(WindowInfo *pWinInfo, GLint flags) +{ + CRASSERT(pWinInfo); + + cocoaViewDisplay(pWinInfo->window); +} + +GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *pWinInfo) +{ + return cocoaViewNeedsEmptyPresent(pWinInfo->window); +} + +void renderspu_SystemWindowVisibleRegion(WindowInfo *pWinInfo, GLint cRects, const GLint* paRects) +{ + CRASSERT(pWinInfo); + + cocoaViewSetVisibleRegion(pWinInfo->window, cRects, paRects); +} + +void renderspu_SystemWindowApplyVisibleRegion(WindowInfo *pWinInfo) +{ +} + +int renderspu_SystemInit() +{ + return VINF_SUCCESS; +} + +int renderspu_SystemTerm() +{ + CrGlslTerm(&render_spu.GlobalShaders); + return VINF_SUCCESS; +} + +static SPUNamedFunctionTable * renderspuFindEntry(SPUNamedFunctionTable *aFunctions, const char *pcszName) +{ + SPUNamedFunctionTable *pCur; + + for (pCur = aFunctions ; pCur->name != NULL ; pCur++) + { + if (!crStrcmp( pcszName, pCur->name ) ) + { + return pCur; + } + } + + AssertFailed(); + + return NULL; +} + +typedef struct CR_RENDER_CTX_INFO +{ + ContextInfo * pContext; + WindowInfo * pWindow; +} CR_RENDER_CTX_INFO; + +void renderspuCtxInfoInitCurrent(CR_RENDER_CTX_INFO *pInfo) +{ + GET_CONTEXT(pCurCtx); + pInfo->pContext = pCurCtx; + pInfo->pWindow = pCurCtx ? pCurCtx->currentWindow : NULL; +} + +void renderspuCtxInfoRestoreCurrent(CR_RENDER_CTX_INFO *pInfo) +{ + GET_CONTEXT(pCurCtx); + if (pCurCtx == pInfo->pContext && (!pCurCtx || pCurCtx->currentWindow == pInfo->pWindow)) + return; + renderspuPerformMakeCurrent(pInfo->pWindow, 0, pInfo->pContext); +} + +GLboolean renderspuCtxSetCurrentWithAnyWindow(ContextInfo * pContext, CR_RENDER_CTX_INFO *pInfo) +{ + WindowInfo * window; + renderspuCtxInfoInitCurrent(pInfo); + + if (pInfo->pContext == pContext) + return GL_TRUE; + + window = pContext->currentWindow; + if (!window) + { + window = renderspuGetDummyWindow(pContext->BltInfo.Base.visualBits); + if (!window) + { + WARN(("renderspuGetDummyWindow failed")); + return GL_FALSE; + } + } + + Assert(window); + + renderspuPerformMakeCurrent(window, 0, pContext); + return GL_TRUE; +} + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + CRASSERT(fromContext != toContext); + + if (!CrGlslIsInited(&render_spu.GlobalShaders)) + { + CrGlslInit(&render_spu.GlobalShaders, &render_spu.blitterDispatch); + } + + if (fromContext) + { + if (CrGlslNeedsCleanup(&render_spu.GlobalShaders)) + { + CR_RENDER_CTX_INFO Info; + if (renderspuCtxSetCurrentWithAnyWindow(fromContext, &Info)) + { + CrGlslCleanup(&render_spu.GlobalShaders); + renderspuCtxInfoRestoreCurrent(&Info); + } + else + WARN(("renderspuCtxSetCurrentWithAnyWindow failed!")); + } + } + else + { + CRASSERT(!CrGlslNeedsCleanup(&render_spu.GlobalShaders)); + } + + CRASSERT(!CrGlslNeedsCleanup(&render_spu.GlobalShaders)); + + if (toContext) + { + CR_RENDER_CTX_INFO Info; + if (renderspuCtxSetCurrentWithAnyWindow(toContext, &Info)) + { + int rc = CrGlslProgGenAllNoAlpha(&render_spu.GlobalShaders); + if (!RT_SUCCESS(rc)) + WARN(("CrGlslProgGenAllNoAlpha failed, rc %d", rc)); + + renderspuCtxInfoRestoreCurrent(&Info); + } + else + crWarning("renderspuCtxSetCurrentWithAnyWindow failed!"); + } +} + +AssertCompile(sizeof (GLhandleARB) == sizeof (void*)); + +static VBoxGLhandleARB crHndlSearchVBox(GLhandleARB hNative) +{ + CRASSERT(!(((uintptr_t)hNative) >> 32)); + return (VBoxGLhandleARB)((uintptr_t)hNative); +} + +static GLhandleARB crHndlSearchNative(VBoxGLhandleARB hVBox) +{ + return (GLhandleARB)((uintptr_t)hVBox); +} + +static VBoxGLhandleARB crHndlAcquireVBox(GLhandleARB hNative) +{ + CRASSERT(!(((uintptr_t)hNative) >> 32)); + return (VBoxGLhandleARB)((uintptr_t)hNative); +} + +static GLhandleARB crHndlReleaseVBox(VBoxGLhandleARB hVBox) +{ + return (GLhandleARB)((uintptr_t)hVBox); +} + +static void SPU_APIENTRY renderspu_SystemDeleteObjectARB(VBoxGLhandleARB obj) +{ + GLhandleARB hNative = crHndlReleaseVBox(obj); + if (!hNative) + { + crWarning("no native for %d", obj); + return; + } + + render_spu.pfnDeleteObject(hNative); +} + +static void SPU_APIENTRY renderspu_SystemGetAttachedObjectsARB( VBoxGLhandleARB containerObj, GLsizei maxCount, GLsizei * pCount, VBoxGLhandleARB * obj ) +{ + GLhandleARB *paAttachments; + GLhandleARB hNative = crHndlSearchNative(containerObj); + GLsizei count, i; + + if (pCount) + *pCount = 0; + + if (!hNative) + { + crWarning("no native for %d", obj); + return; + } + + paAttachments = crCalloc(maxCount * sizeof (*paAttachments)); + if (!paAttachments) + { + crWarning("crCalloc failed"); + return; + } + + render_spu.pfnGetAttachedObjects(hNative, maxCount, &count, paAttachments); + if (pCount) + *pCount = count; + if (count > maxCount) + { + crWarning("count too big"); + count = maxCount; + } + + for (i = 0; i < count; ++i) + { + obj[i] = crHndlSearchVBox(paAttachments[i]); + CRASSERT(obj[i]); + } + + crFree(paAttachments); +} + +static VBoxGLhandleARB SPU_APIENTRY renderspu_SystemGetHandleARB(GLenum pname) +{ + GLhandleARB hNative = render_spu.pfnGetHandle(pname); + VBoxGLhandleARB hVBox; + if (!hNative) + { + crWarning("pfnGetHandle failed"); + return 0; + } + hVBox = crHndlAcquireVBox(hNative); + CRASSERT(hVBox); + return hVBox; +} + +static void SPU_APIENTRY renderspu_SystemGetInfoLogARB( VBoxGLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog ) +{ + GLhandleARB hNative = crHndlSearchNative(obj); + if (!hNative) + { + crWarning("invalid handle!"); + return; + } + + render_spu.pfnGetInfoLog(hNative, maxLength, length, infoLog); +} + +static void SPU_APIENTRY renderspu_SystemGetObjectParameterfvARB( VBoxGLhandleARB obj, GLenum pname, GLfloat * params ) +{ + GLhandleARB hNative = crHndlSearchNative(obj); + if (!hNative) + { + crWarning("invalid handle!"); + return; + } + + render_spu.pfnGetObjectParameterfv(hNative, pname, params); +} + +static void SPU_APIENTRY renderspu_SystemGetObjectParameterivARB( VBoxGLhandleARB obj, GLenum pname, GLint * params ) +{ + GLhandleARB hNative = crHndlSearchNative(obj); + if (!hNative) + { + crWarning("invalid handle!"); + return; + } + + render_spu.pfnGetObjectParameteriv(hNative, pname, params); +} + +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable) +{ + SPUNamedFunctionTable * pEntry; + + pEntry = renderspuFindEntry(aFunctions, "DeleteObjectARB"); + if (pEntry) + { + render_spu.pfnDeleteObject = (PFNDELETE_OBJECT)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemDeleteObjectARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetAttachedObjectsARB"); + if (pEntry) + { + render_spu.pfnGetAttachedObjects = (PFNGET_ATTACHED_OBJECTS)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetAttachedObjectsARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetHandleARB"); + if (pEntry) + { + render_spu.pfnGetHandle = (PFNGET_HANDLE)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetHandleARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetInfoLogARB"); + if (pEntry) + { + render_spu.pfnGetInfoLog = (PFNGET_INFO_LOG)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetInfoLogARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetObjectParameterfvARB"); + if (pEntry) + { + render_spu.pfnGetObjectParameterfv = (PFNGET_OBJECT_PARAMETERFV)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetObjectParameterfvARB; + } + + pEntry = renderspuFindEntry(aFunctions, "GetObjectParameterivARB"); + if (pEntry) + { + render_spu.pfnGetObjectParameteriv = (PFNGET_OBJECT_PARAMETERIV)pEntry->fn; + pEntry->fn = (SPUGenericFunction)renderspu_SystemGetObjectParameterivARB; + } + + return cFunctions; +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h new file mode 100644 index 00000000..11333083 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.h @@ -0,0 +1,65 @@ +/* $Id: renderspu_cocoa_helper.h $ */ +/** @file + * VirtualBox OpenGL Cocoa Window System Helper definition + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +#ifndef ___renderspu_cocoa_helper_h +#define ___renderspu_cocoa_helper_h + +#include <iprt/cdefs.h> +#include <VBox/VBoxCocoa.h> +#include <OpenGL/OpenGL.h> +#ifdef IN_VMSVGA3D +# include "../../../GuestHost/OpenGL/include/cr_vreg.h" +# include "../../../GuestHost/OpenGL/include/cr_compositor.h" +#else +# include <cr_vreg.h> +# include <cr_compositor.h> +#endif + + +RT_C_DECLS_BEGIN + +struct WindowInfo; + +ADD_COCOA_NATIVE_REF(NSView); +ADD_COCOA_NATIVE_REF(NSOpenGLContext); + +/** @name OpenGL context management + * @{ */ +void cocoaGLCtxCreate(NativeNSOpenGLContextRef *ppCtx, GLbitfield fVisParams, NativeNSOpenGLContextRef pSharedCtx); +void cocoaGLCtxDestroy(NativeNSOpenGLContextRef pCtx); +/** @} */ + +/** @name View management + * @{ */ +void cocoaViewCreate(NativeNSViewRef *ppView, struct WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams); +void cocoaViewReparent(NativeNSViewRef pView, NativeNSViewRef pParentView); +void cocoaViewDestroy(NativeNSViewRef pView); +void cocoaViewDisplay(NativeNSViewRef pView); +void cocoaViewShow(NativeNSViewRef pView, GLboolean fShowIt); +void cocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y); +void cocoaViewSetSize(NativeNSViewRef pView, int cx, int cy); +void cocoaViewGetGeometry(NativeNSViewRef pView, int *px, int *py, int *pcx, int *pcy); +void cocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx); +void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, const GLint *paRects); +GLboolean cocoaViewNeedsEmptyPresent(NativeNSViewRef pView); +void cocoaViewPresentComposition(NativeNSViewRef pView, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry); +/** @} */ + +RT_C_DECLS_END + +#endif /* !___renderspu_cocoa_helper_h */ + diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m new file mode 100644 index 00000000..6c643f9c --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m @@ -0,0 +1,3283 @@ +/* $Id: renderspu_cocoa_helper.m $ */ +/** @file + * VirtualBox OpenGL Cocoa Window System Helper Implementation. + * + * This source file is shared between the SharedOpenGL HGCM service and the + * SVGA3d emulation. + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +/** @page pg_opengl_cocoa OpenGL - Cocoa Window System Helper + * + * How this works: + * In general it is not so easy like on the other platforms, cause Cocoa + * doesn't support any clipping of already painted stuff. In Mac OS X there is + * the concept of translucent canvas's e.g. windows and there it is just + * painted what should be visible to the user. Unfortunately this isn't the + * concept of chromium. Therefor I reroute all OpenGL operation from the guest + * to a frame buffer object (FBO). This is a OpenGL extension, which is + * supported by all OS X versions we support (AFAIC tell). Of course the guest + * doesn't know that and we have to make sure that the OpenGL state always is + * in the right state to paint into the FBO and not to the front/back buffer. + * Several functions below (like cocoaBindFramebufferEXT, cocoaGetIntegerv, + * ...) doing this. When a swap or finish is triggered by the guest, the + * content (which is already bound to an texture) is painted on the screen + * within a separate OpenGL context. This allows the usage of the same + * resources (texture ids, buffers ...) but at the same time having an + * different internal OpenGL state. Another advantage is that we can paint a + * thumbnail of the current output in a much more smaller (GPU accelerated + * scale) version on a third context and use glReadPixels to get the actual + * data. glReadPixels is a very slow operation, but as we just use a much more + * smaller image, we can handle it (anyway this is only done 5 times per + * second). + * + * Other things to know: + * - If the guest request double buffering, we have to make sure there are two + * buffers. We use the same FBO with 2 color attachments. Also glDrawBuffer + * and glReadBuffer is intercepted to make sure it is painted/read to/from + * the correct buffers. On swap our buffers are swapped and not the + * front/back buffer. + * - If the guest request a depth/stencil buffer, a combined render buffer for + * this is created. + * - If the size of the guest OpenGL window changes, all FBO's, textures, ... + * need to be recreated. + * - We need to track any changes to the parent window + * (create/destroy/move/resize). The various classes like OverlayHelperView, + * OverlayWindow, ... are there for. + * - The HGCM service runs on a other thread than the Main GUI. Keeps this + * always in mind (see e.g. performSelectorOnMainThread in renderFBOToView) + * - We make heavy use of late binding. We can not be sure that the GUI (or any + * other third party GUI), overwrite our NSOpenGLContext. So we always ask if + * this is our own one, before use. Really neat concept of Objective-C/Cocoa + * ;) + * + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#ifdef IN_VMSVGA3D +# define LOG_GROUP LOG_GROUP_DEV_VMSVGA +#endif +#include "renderspu_cocoa_helper.h" + +#import <Cocoa/Cocoa.h> +#undef PVM /* sys/param.h (included via Cocoa.h) pollutes the namespace with this define. */ + +#ifndef IN_VMSVGA3D +# include "chromium.h" /* For the visual bits of chromium */ +#endif + +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/time.h> +#include <iprt/thread.h> + +#include <VBox/VBoxOGL.h> +#include <VBox/log.h> + +#ifdef IN_VMSVGA3D +# include "DevVGA-SVGA3d-cocoa.h" +# include <OpenGL/OpenGL.h> +# include <OpenGL/gl3.h> +# include <OpenGL/gl3ext.h> +# include <OpenGL/glext.h> +#else +# include <cr_vreg.h> +# include <cr_error.h> +# include <cr_blitter.h> +# ifdef VBOX_WITH_CRDUMPER_THUMBNAIL +# include <cr_pixeldata.h> +# endif +# include "renderspu.h" +#endif + + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/* Debug macros */ +/** @def FBO + * Disable this to see how the output is without the FBO in the middle of the processing chain. */ +#define FBO 1 +/** @def SHOW_WINDOW_BACKGROUND + * Define this to see the window background even if the window is clipped. */ +/** @def DEBUG_VERBOSE + * Define this to get some debug info about the messages flow. */ +#if 0 || defined(DOXYGEN_RUNNING) +# define SHOW_WINDOW_BACKGROUND 1 +# define DEBUG_VERBOSE +#endif + +#ifdef DEBUG_VERBOSE +# error "should be disabled!" +# ifdef IN_VMSVGA3D +# define DEBUG_INFO(text) do { LogRel(text); AssertFailed(); } while (0) +# define DEBUG_WARN(text) do { LogRel(text); AssertFailed(); } while (0) +# else +# define DEBUG_INFO(text) do { LogRel(text); AssertFailed(); } while (0) +# define DEBUG_WARN(text) do { LogRel(text); AssertFailed(); } while (0) +# endif + +# define DEBUG_MSG(text) do { LogRel(text); } while (0) +# define DEBUG_MSG_1(text) do { LogRel(text); } while (0) + +#else + +# ifdef IN_VMSVGA3D +# define DEBUG_INFO(text) do { LogRel(text); } while (0) +# define DEBUG_WARN(text) do { LogRel(text); } while (0) +# else +# define DEBUG_INFO(text) do { crInfo text; } while (0) +# define DEBUG_WARN(text) do { crWarning text; } while (0) +#endif +# define DEBUG_MSG(text) do {} while (0) +# define DEBUG_MSG_1(text) do {} while (0) + +#endif +#ifdef IN_VMSVGA3D +# define DEBUG_MSG_NOT_VMSVGA3D(a_TextArgs) do {} while (0) +# define COCOA_LOG_FLOW(a_TextArgs) LogFlow(a_TextArgs) +#else +# define DEBUG_MSG_NOT_VMSVGA3D(a_TextArgs) DEBUG_MSG(a_TextArgs) +# define COCOA_LOG_FLOW(a_TextArgs) DEBUG_MSG(a_TextArgs) +#endif + + +#define DEBUG_FUNC_ENTER() DEBUG_MSG(("==>%s\n", __PRETTY_FUNCTION__)) +#define DEBUG_FUNC_LEAVE() DEBUG_MSG(("<==%s\n", __PRETTY_FUNCTION__)) + +#define DEBUG_GL_SAVE_STATE() \ + do { \ + glPushAttrib(GL_ALL_ATTRIB_BITS); \ + glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); \ + glMatrixMode(GL_PROJECTION); \ + glPushMatrix(); \ + glMatrixMode(GL_TEXTURE); \ + glPushMatrix(); \ + glMatrixMode(GL_COLOR); \ + glPushMatrix(); \ + glMatrixMode(GL_MODELVIEW); \ + glPushMatrix(); \ + } while (0) + +#define DEBUG_GL_RESTORE_STATE() \ + do { \ + glMatrixMode(GL_MODELVIEW); \ + glPopMatrix(); \ + glMatrixMode(GL_COLOR); \ + glPopMatrix(); \ + glMatrixMode(GL_TEXTURE); \ + glPopMatrix(); \ + glMatrixMode(GL_PROJECTION); \ + glPopMatrix(); \ + glPopClientAttrib(); \ + glPopAttrib(); \ + } while (0) + +#ifdef VBOX_STRICT +# define DEBUG_CLEAR_GL_ERRORS() \ + do { \ + while (glGetError() != GL_NO_ERROR) \ + { /* nothing */ } \ + } while (0) +# define DEBUG_CHECK_GL_ERROR(a_szOp) \ + do { \ + GLenum iGlCheckErr = glGetError(); \ + if (RT_UNLIKELY(iGlCheckErr != GL_NO_ERROR)) \ + AssertMsgFailed((a_szOp ": iGlCheckErr=%#x\n", iGlCheckErr)); \ + } while (0) +#else +# define DEBUG_CLEAR_GL_ERRORS() do {} while (0) +# define DEBUG_CHECK_GL_ERROR(a_szOp) do {} while (0) +#endif + +/* Whether we control NSView automatic content zooming on Retina/HiDPI displays. */ +#define VBOX_WITH_CONFIGURABLE_HIDPI_SCALING 1 + + +#ifdef IN_VMSVGA3D + +/* + * VMSVGA3D compatibility glue. + */ +typedef struct WindowInfo WindowInfo; + +# define CR_RGB_BIT RT_BIT_32(0) + +# define CR_ALPHA_BIT RT_BIT_32(1) +# define CR_DEPTH_BIT RT_BIT_32(2) +# define CR_STENCIL_BIT RT_BIT_32(3) +# define CR_ACCUM_BIT RT_BIT_32(4) +# define CR_DOUBLE_BIT RT_BIT_32(5) +# define CR_STEREO_BIT RT_BIT_32(6) +# define CR_MULTISAMPLE_BIT RT_BIT_32(7) + +# define CR_OVERLAY_BIT RT_BIT_32(8) +# define CR_PBUFFER_BIT RT_BIT_32(9) +# define VMSVGA3D_NON_DEFAULT_PROFILE_BIT RT_BIT_32(31) +# define CR_ALL_BITS UINT32_C(0x800003ff) + +#endif /* IN_VMSVGA3D */ + + +/** + * Works functions that creates a NSOpenGLPixelFormat instance. + * + * @returns Instance. + * @param fVisParams Context flags. + */ +static NSOpenGLPixelFormat *vboxCreatePixelFormat(GLbitfield fVisParams) +{ + NSOpenGLPixelFormatAttribute attribs[24] = + { +#ifdef IN_VMSVGA3D + NSOpenGLPFAOpenGLProfile, (NSOpenGLPixelFormatAttribute)0, +#endif + NSOpenGLPFAAccelerated, + NSOpenGLPFAColorSize, (NSOpenGLPixelFormatAttribute)24 + }; +#ifndef IN_VMSVGA3D + int i = 3; +#else + int i = 3+2; + if (fVisParams & VMSVGA3D_NON_DEFAULT_PROFILE_BIT) + attribs[1] = VBOX_VMSVGA3D_DEFAULT_OGL_PROFILE >= 3.2 ? NSOpenGLProfileVersionLegacy : NSOpenGLProfileVersion3_2Core; + else + attribs[1] = VBOX_VMSVGA3D_DEFAULT_OGL_PROFILE >= 3.2 ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy; +#endif + + if (fVisParams & CR_ALPHA_BIT) + { + COCOA_LOG_FLOW((" CR_ALPHA_BIT requested\n")); + attribs[i++] = NSOpenGLPFAAlphaSize; + attribs[i++] = 8; + } + if (fVisParams & CR_DEPTH_BIT) + { + COCOA_LOG_FLOW((" CR_DEPTH_BIT requested\n")); + attribs[i++] = NSOpenGLPFADepthSize; + attribs[i++] = 24; + } + if (fVisParams & CR_STENCIL_BIT) + { + COCOA_LOG_FLOW((" CR_STENCIL_BIT requested\n")); + attribs[i++] = NSOpenGLPFAStencilSize; + attribs[i++] = 8; + } + if (fVisParams & CR_ACCUM_BIT) + { + COCOA_LOG_FLOW((" CR_ACCUM_BIT requested\n")); + attribs[i++] = NSOpenGLPFAAccumSize; + if (fVisParams & CR_ALPHA_BIT) + attribs[i++] = 32; + else + attribs[i++] = 24; + } + if (fVisParams & CR_MULTISAMPLE_BIT) + { + COCOA_LOG_FLOW((" CR_MULTISAMPLE_BIT requested\n")); + attribs[i++] = NSOpenGLPFASampleBuffers; + attribs[i++] = 1; + attribs[i++] = NSOpenGLPFASamples; + attribs[i++] = 4; + } + if (fVisParams & CR_DOUBLE_BIT) + { + COCOA_LOG_FLOW((" CR_DOUBLE_BIT requested\n")); + attribs[i++] = NSOpenGLPFADoubleBuffer; + } + if (fVisParams & CR_STEREO_BIT) + { + /* We don't support that. + COCOA_LOG_FLOW((" CR_STEREO_BIT requested\n")); + attribs[i++] = NSOpenGLPFAStereo; + */ + } + + if (VBoxOglIsOfflineRenderingAppropriate()) + { + COCOA_LOG_FLOW((" Offline rendering is enabled\n")); + attribs[i++] = NSOpenGLPFAAllowOfflineRenderers; + } + + /* Mark the end */ + attribs[i++] = 0; + + /* Instantiate the pixel format object. */ + return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; +} + + +static NSOpenGLContext *vboxCtxGetCurrent(void) +{ +#ifdef IN_VMSVGA3D + return [NSOpenGLContext currentContext]; +#else + GET_CONTEXT(pCtxInfo); + if (pCtxInfo) + { + Assert(pCtxInfo->context); + return pCtxInfo->context; + } + return nil; +#endif +} + +static bool vboxCtxSyncCurrentInfo(void) +{ +#ifdef IN_VMSVGA3D + return false; +#else + bool fAdjusted = false; + GET_CONTEXT(pCtxInfo); + NSOpenGLContext *pCtx = [NSOpenGLContext currentContext]; + NSView *pView = pCtx ? [pCtx view] : nil; + if (pCtxInfo) + { + WindowInfo *pWinInfo = pCtxInfo->currentWindow; + Assert(pWinInfo); + if ( pCtxInfo->context != pCtx + || pWinInfo->window != pView) + { + renderspu_SystemMakeCurrent(pWinInfo, 0, pCtxInfo); + fAdjusted = true; + } + } + else if (pCtx) + { + [NSOpenGLContext clearCurrentContext]; + fAdjusted = true; + } + + return fAdjusted; +#endif +} + + +/** + * State carrying structure for use with vboxCtxEnter and vboxCtxLeave + */ +typedef struct VBOX_CR_RENDER_CTX_INFO +{ + bool fIsValid; + NSOpenGLContext *pCtx; + NSView *pView; +} VBOX_CR_RENDER_CTX_INFO; +/** Pointer to render context info for use with vboxCtxEnter/Leave. */ +typedef VBOX_CR_RENDER_CTX_INFO *PVBOX_CR_RENDER_CTX_INFO; + +static void vboxCtxEnter(NSOpenGLContext *pNewCtx, PVBOX_CR_RENDER_CTX_INFO pCtxInfo) +{ + NSOpenGLContext *pOldCtx = vboxCtxGetCurrent(); + NSView *pOldView = pOldCtx ? [pOldCtx view] : nil; + NSView *pNewView = [pNewCtx view]; + + Assert(pNewCtx); + + if ( pOldCtx != pNewCtx + || pOldView != pNewView) + { + if (pOldCtx != nil) + glFlush(); + + DEBUG_CLEAR_GL_ERRORS(); + [pNewCtx makeCurrentContext]; + DEBUG_CHECK_GL_ERROR("makeCurrentContext"); + + pCtxInfo->fIsValid = true; + pCtxInfo->pCtx = pOldCtx; + /** @todo r=bird: Why do we save the NEW VIEW here? vboxCtxLeave calls it 'pOldView'. Bug? */ + pCtxInfo->pView = pNewView; + } + else + { + /* No context switch necessary. */ + pCtxInfo->fIsValid = false; + } +} + +static void vboxCtxLeave(PVBOX_CR_RENDER_CTX_INFO pCtxInfo) +{ + if (pCtxInfo->fIsValid) + { + NSOpenGLContext *pOldCtx = pCtxInfo->pCtx; + NSView *pOldView = pCtxInfo->pView; + + glFlush(); + if (pOldCtx != nil) + { + /* vboxCtxEnter saves the new view, not the old. So, what we actually + do here is switching the view of the old context to that of the new + one (wrt vboxCtxEnter) before making it current. */ + /** @todo r=bird: Figure out what we really want to do here, and either rename + * pOldView or fix the code. */ + if ([pOldCtx view] != pOldView) + { + DEBUG_CLEAR_GL_ERRORS(); + [pOldCtx setView: pOldView]; + DEBUG_CHECK_GL_ERROR("setView"); + } + + DEBUG_CLEAR_GL_ERRORS(); + [pOldCtx makeCurrentContext]; + DEBUG_CHECK_GL_ERROR("makeCurrentContext"); + +#ifdef VBOX_STRICT + { + NSOpenGLContext *pTstOldCtx = [NSOpenGLContext currentContext]; + NSView *pTstOldView = pTstOldCtx ? [pTstOldCtx view] : nil; + Assert(pTstOldCtx == pOldCtx); + Assert(pTstOldView == pOldView); + } +#endif + } + else + { + [NSOpenGLContext clearCurrentContext]; + } + } +} + + +/** + * Custom OpenGL context class. + * + * This implementation doesn't allow to set a view to the context, but save the + * view for later use. Also it saves a copy of the pixel format used to create + * that context for later use. + */ +@interface OverlayOpenGLContext: NSOpenGLContext +{ +@private + NSOpenGLPixelFormat *m_pPixelFormat; + NSView *m_pView; +} +- (NSOpenGLPixelFormat *)openGLPixelFormat; +@end + +/** + * Abstrack task class. + */ +@interface VBoxTask : NSObject +{ +} +- (void)run; +@end + +@implementation VBoxTask +/** Run method that the child classes must reimplement. + * This will abort the process. */ +- (void)run +{ + AssertReleaseFailed(); +} +@end + + +/** + * Generic task class for executing a given method select. + */ +@interface VBoxTaskPerformSelector : VBoxTask +{ +@private + id m_Object; + SEL m_Selector; + id m_Arg; +} +- (id)initWithObject:(id)aObject selector:(SEL)aSelector arg:(id)aArg; +- (void)run; +- (void)dealloc; +@end + +@implementation VBoxTaskPerformSelector + +/** + * Initializes a VBoxTaskPerformSelector. + * + * @param aObject The object (reference not consumed). + * @param aSelector The method selector. + * @param aArg The method argument (reference not consumed). + */ +- (id)initWithObject:(id)aObject selector:(SEL)aSelector arg:(id)aArg +{ + self = [super init]; + if (self) + { + [aObject retain]; + m_Object = aObject; + m_Selector = aSelector; + if (aArg != nil) + [aArg retain]; + m_Arg = aArg; + } + + return self; +} + +- (void)run +{ + [m_Object performSelector:m_Selector withObject:m_Arg]; +} + +- (void)dealloc +{ + [m_Object release]; + if (m_Arg != nil) + [m_Arg release]; + + [super dealloc]; +} +@end + + +/** + * + */ +@interface VBoxTaskComposite : VBoxTask +{ +@private + NSUInteger m_CurIndex; + RTCRITSECT m_Lock; + NSMutableArray *m_pArray; +} +- (id)init; +- (void)add:(VBoxTask *)pTask; +- (void)run; +- (void)dealloc; +@end + +@implementation VBoxTaskComposite +- (id)init +{ + self = [super init]; + + if (self) + { + int rc = RTCritSectInit(&m_Lock); + if (!RT_SUCCESS(rc)) + { + DEBUG_WARN(("RTCritSectInit failed %d\n", rc)); + return nil; + } + + m_CurIndex = 0; + + m_pArray = [[NSMutableArray alloc] init]; + } + + return self; +} + +/** + * Adds a task to the composite task object. + * + * @param pTask Task to add. Reference is NOT consumed. + */ +- (void)add:(VBoxTask *)pTask +{ + [pTask retain]; + int rc = RTCritSectEnter(&m_Lock); + if (RT_SUCCESS(rc)) + { + [m_pArray addObject:pTask]; + RTCritSectLeave(&m_Lock); + } + else + { + DEBUG_WARN(("RTCritSectEnter failed %d\n", rc)); + [pTask release]; + } +} + +- (void)run +{ + for (;;) + { + /* + * Dequeue a task. + */ + int rc = RTCritSectEnter(&m_Lock); + if (RT_FAILURE(rc)) + { + DEBUG_WARN(("RTCritSectEnter failed %d\n", rc)); + break; + } + + NSUInteger count = [m_pArray count]; + Assert(m_CurIndex <= count); + if (m_CurIndex == count) + { + [m_pArray removeAllObjects]; + m_CurIndex = 0; + RTCritSectLeave(&m_Lock); + break; + } + + VBoxTask *pTask = (VBoxTask *)[m_pArray objectAtIndex:m_CurIndex]; + Assert(pTask != nil); + + ++m_CurIndex; + + /* + * Remove the first 1025 empty entires. + */ + if (m_CurIndex > 1024) + { + NSRange range; + range.location = 0; + range.length = m_CurIndex; + [m_pArray removeObjectsInRange:range]; + m_CurIndex = 0; + } + RTCritSectLeave(&m_Lock); + + /* + * Run the task and release it. + */ + [pTask run]; + [pTask release]; + } +} + +- (void)dealloc +{ + NSUInteger count = [m_pArray count]; + for (;m_CurIndex < count; ++m_CurIndex) + { + VBoxTask *pTask = (VBoxTask*)[m_pArray objectAtIndex:m_CurIndex]; + DEBUG_WARN(("dealloc with non-empty tasks! %p\n", pTask)); + [pTask release]; + } + + [m_pArray release]; + RTCritSectDelete(&m_Lock); + + [super dealloc]; +} +@end + + +/** + * + * + */ +@interface VBoxMainThreadTaskRunner : NSObject +{ +@private + VBoxTaskComposite *m_pTasks; +} +- (id)init; +- (void)add:(VBoxTask *)pTask; +- (void)addObj:(id)aObject selector:(SEL)aSelector arg:(id)aArg; +- (void)runTasks; +- (bool)runTasksSyncIfPossible; +- (void)dealloc; ++ (VBoxMainThreadTaskRunner *) globalInstance; +@end + +@implementation VBoxMainThreadTaskRunner +- (id)init +{ + self = [super init]; + if (self) + { + m_pTasks = [[VBoxTaskComposite alloc] init]; + } + + return self; +} + ++ (VBoxMainThreadTaskRunner *) globalInstance +{ + static dispatch_once_t s_DispatchOnce; + static VBoxMainThreadTaskRunner *s_pRunner = nil; + dispatch_once(&s_DispatchOnce, ^{ + s_pRunner = [[VBoxMainThreadTaskRunner alloc] init]; + }); + return s_pRunner; +} + +- (void)add:(VBoxTask *)pTask +{ + DEBUG_FUNC_ENTER(); + [m_pTasks add:pTask]; + /** @todo r=bird: Unbalanced [self retain]. */ + [self retain]; + + if (![self runTasksSyncIfPossible]) + { + DEBUG_MSG(("task will be processed async\n")); + [self performSelectorOnMainThread:@selector(runTasks) withObject:nil waitUntilDone:NO]; + } + + DEBUG_FUNC_LEAVE(); +} + +/** + * Adds a task calling an object method (selector). + * + * @param aObject The object (reference not consumed).. + * @param aSelector The method selector. + * @param aArg The method argument (reference not consumed). + */ +- (void)addObj:(id)aObject selector:(SEL)aSelector arg:(id)aArg +{ + VBoxTaskPerformSelector *pSelTask = [[VBoxTaskPerformSelector alloc] initWithObject:aObject selector:aSelector arg:aArg]; + [self add:pSelTask]; + [pSelTask release]; +} + + +/** + * Internal method for running the pending tasks. + */ +- (void)runTasks +{ + if ([NSThread isMainThread]) + { + [m_pTasks run]; + /** @todo r=bird: This release and the retain in the add method aren't + * necessarily balanced if there are more than one call to add(). + * + * This could probably end up deleting the singleton prematurely and leave + * globalInstance() returning pointers to a stale object in freed memory, + * quite possibly causing crashes or/and heap corruption. */ + [self release]; + } + else + { + DEBUG_WARN(("run tasks called not on main thread!\n")); +#ifndef DEBUG_VERBOSE + AssertFailed(); +#endif + [self performSelectorOnMainThread:@selector(runTasks) withObject:nil waitUntilDone:YES]; + } +} + +/** + * Callback for calling runTasks via renderspuCalloutClient. + * @param pvUser The VBoxMainThreadTaskRunner singleton. + */ +static DECLCALLBACK(void) VBoxMainThreadTaskRunner_RcdRunCallback(void *pvUser) +{ + DEBUG_FUNC_ENTER(); + VBoxMainThreadTaskRunner *pRunner = (VBoxMainThreadTaskRunner *)pvUser; + Assert(pRunner == [VBoxMainThreadTaskRunner globalInstance]); + [pRunner runTasks]; + DEBUG_FUNC_LEAVE(); +} + +/** + * Runs pending tasks synchronously, if possible in the current context. + * + * @returns true if executed tasks, false if not possible. + */ +- (bool)runTasksSyncIfPossible +{ +#ifndef IN_VMSVGA3D + /* + * Call on main thread (?) via renderspuCalloutClient (whatever that is). + */ + if (renderspuCalloutAvailable()) + { + Assert(![NSThread isMainThread]); + renderspuCalloutClient(VBoxMainThreadTaskRunner_RcdRunCallback, self); + return true; + } +#endif + + /* + * Run directly if on main thread. + */ + if ([NSThread isMainThread]) + { + [self runTasks]; + return true; + } + + /* Not possible. */ + return false; +} + +- (void)dealloc +{ + /** @todo r=bird: WTF is the point of the deallocator. The object is a singelton + * stored in an inaccessible static variable! */ + [m_pTasks release]; + m_pTasks = nil; + + [super dealloc]; +} + +@end + +#ifndef IN_VMSVGA3D +@class DockOverlayView; +#endif + +/** + * The custom view class. + * + * This is the main class of the cocoa OpenGL implementation. It manages a + * frame buffer object for the rendering of the guest applications. The guest + * applications render in this frame buffer which is bound to an OpenGL texture. + * To display the guest content, a secondary shared OpenGL context of the main + * OpenGL context is created. The secondary context is marked as non-opaque and + * the texture is displayed on an object which is composed out of the several + * visible region rectangles. + */ +@interface OverlayView : NSView +{ +@private + NSView *m_pParentView; + NSWindow *m_pOverlayWin; + + NSOpenGLContext *m_pGLCtx; + NSOpenGLContext *m_pSharedGLCtx; + RTTHREAD m_Thread; + + GLuint m_FBOId; + +#ifndef IN_VMSVGA3D + /** The corresponding dock tile view of this OpenGL view & all helper + * members. */ + DockOverlayView *m_DockTileView; + + GLfloat m_FBOThumbScaleX; + GLfloat m_FBOThumbScaleY; + uint64_t m_msDockUpdateTS; +#endif + + /** @name For clipping + * @remarks appears to be unused and a complete waste of time + heap. + * @{ */ + GLint m_cClipRects; + GLint *m_paClipRects; + /** @} */ + + /** @name Position/Size tracking + * @{ */ + NSPoint m_Pos; + NSSize m_Size; + /** @} */ + + /** This is necessary for clipping on the root window */ + NSRect m_RootRect; + float m_yInvRootOffset; + +#ifndef IN_VMSVGA3D + CR_BLITTER *m_pBlitter; + WindowInfo *m_pWinInfo; +#endif + bool m_fNeedViewportUpdate; + bool m_fNeedCtxUpdate; + bool m_fDataVisible; + bool m_fCleanupNeeded; + bool m_fEverSized; +} +- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView *)pParentView winInfo:(WindowInfo *)pWinInfo + fVisParams:(GLbitfield) fVisParams; +- (void)setGLCtx:(NSOpenGLContext*)pCtx; +- (NSOpenGLContext *)glCtx; + +- (void)setParentView: (NSView *)view; +- (NSView *)parentView; +- (void)setOverlayWin: (NSWindow *)win; +- (NSWindow *)overlayWin; + +- (void)vboxSetPos:(NSPoint)pos; +- (void)vboxSetPosUI:(NSPoint)pos; +- (void)vboxSetPosUIObj:(NSValue *)pPos; +- (NSPoint)pos; +- (bool)isEverSized; +- (void)vboxDestroy; +- (void)vboxSetSizeUI:(NSSize)size; +- (void)vboxSetSizeUIObj:(NSValue *)pSize; +- (void)vboxSetSize:(NSSize)size; +- (NSSize)size; +- (void)updateViewportCS; +#ifdef VBOX_WITH_CONFIGURABLE_HIDPI_SCALING +- (NSRect)safeConvertRectToBacking:(NSRect *)pRect; +- (CGFloat)safeGetBackingScaleFactor; +#endif +- (NSRect)safeConvertToScreen:(NSRect *)pRect; +- (void)vboxReshapePerform; +- (void)vboxReshapeOnResizePerform; +- (void)vboxReshapeOnReparentPerform; + +#ifndef IN_VMSVGA3D +- (void)createDockTile; +- (void)deleteDockTile; +#endif + +- (void)makeCurrentFBO; +- (void)swapFBO; +- (void)vboxSetVisible:(GLboolean)fVisible; +- (void)vboxSetVisibleUIObj:(NSNumber *)pVisible; +- (void)vboxSetVisibleUI:(GLboolean)fVisible; +- (void)vboxTryDraw; +- (void)vboxTryDrawUI; +- (void)vboxReparent:(NSView *)pParentView; +- (void)vboxReparentUI:(NSView *)pParentView; +- (void)vboxPresent:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +- (void)vboxPresentCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +#ifndef IN_VMSVGA3D +- (void)vboxPresentToDockTileCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +#endif +- (void)vboxPresentToViewCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +- (void)presentComposition:(const VBOXVR_SCR_COMPOSITOR_ENTRY *)pChangedEntry; +#ifndef IN_VMSVGA3D +- (void)vboxBlitterSyncWindow; +#endif + +- (void)clearVisibleRegions; +- (void)setVisibleRegions:(GLint)cRects paRects:(const GLint *)paRects; +- (GLboolean)vboxNeedsEmptyPresent; + +#ifndef IN_VMSVGA3D +- (NSView *)dockTileScreen; +- (void)reshapeDockTile; +#endif +- (void)cleanupData; +@end + +/** + * Helper view. + * + * This view is added as a sub view of the parent view to track + * main window changes. Whenever the main window is changed + * (which happens on fullscreen/seamless entry/exit) the overlay + * window is informed & can add them self as a child window + * again. + */ +@class OverlayWindow; +@interface OverlayHelperView: NSView +{ +@private + OverlayWindow *m_pOverlayWindow; +} +-(id)initWithOverlayWindow:(NSRect)frame overlayWindow:(OverlayWindow *)pOverlayWindow; +@end + +/** + * Custom window class. + * + * This is the overlay window which contains our custom NSView. + * Its a direct child of the Qt Main window. It marks its background + * transparent & non opaque to make clipping possible. It also disable mouse + * events and handle frame change events of the parent view. + */ +@interface OverlayWindow : NSWindow +{ +@private + NSView *m_pParentView; + OverlayView *m_pOverlayView; + OverlayHelperView *m_pOverlayHelperView; + NSThread *m_Thread; +} +- (id)initWithParentView:(NSView *)pParentView overlayView:(OverlayView *)pOverlayView; +- (void)parentWindowFrameChanged:(NSNotification *)note; +- (void)parentWindowChanged:(NSWindow *)pWindow; +@end + + +#ifndef IN_VMSVGA3D +/** + * Dock overlay view class. + */ +@interface DockOverlayView: NSView +{ + NSBitmapImageRep *m_ThumbBitmap; + NSImage *m_ThumbImage; + NSLock *m_Lock; +} +- (void)dealloc; +- (void)cleanup; +- (void)lock; +- (void)unlock; +- (void)setFrame:(NSRect)frame; +- (void)drawRect:(NSRect)aRect; +- (NSBitmapImageRep *)thumbBitmap; +- (NSImage *)thumbImage; +@end + +@implementation DockOverlayView +- (id)init +{ + DEBUG_FUNC_ENTER(); + self = [super init]; + if (self) + { + /* + * We need a lock cause the thumb image could be accessed from the main + * thread when someone is calling display on the dock tile & from the + * OpenGL thread when the thumbnail is updated. + */ + m_Lock = [[NSLock alloc] init]; + } + + DEBUG_FUNC_LEAVE(); + + return self; +} + +- (void)dealloc +{ + DEBUG_FUNC_ENTER(); + + [self cleanup]; + [m_Lock release]; + + [super dealloc]; + + DEBUG_FUNC_LEAVE(); +} + +- (void)cleanup +{ + DEBUG_FUNC_ENTER(); + + if (m_ThumbImage != nil) + { + [m_ThumbImage release]; + m_ThumbImage = nil; + } + + if (m_ThumbBitmap != nil) + { + [m_ThumbBitmap release]; + m_ThumbBitmap = nil; + } + + DEBUG_FUNC_LEAVE(); +} + +- (void)lock +{ + DEBUG_FUNC_ENTER(); + [m_Lock lock]; + DEBUG_FUNC_LEAVE(); +} + +- (void)unlock +{ + DEBUG_FUNC_ENTER(); + [m_Lock unlock]; + DEBUG_FUNC_LEAVE(); +} + +- (void)setFrame:(NSRect)frame +{ + DEBUG_FUNC_ENTER(); + [super setFrame:frame]; + + [self lock]; + [self cleanup]; + + if ( frame.size.width > 0 + && frame.size.height > 0) + { + /* Create a buffer for our thumbnail image. Its in the size of this view. */ + m_ThumbBitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL + pixelsWide:frame.size.width + pixelsHigh:frame.size.height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bitmapFormat:NSAlphaFirstBitmapFormat + bytesPerRow:frame.size.width * 4 + bitsPerPixel:8 * 4 + ]; + m_ThumbImage = [[NSImage alloc] initWithSize:[m_ThumbBitmap size]]; + [m_ThumbImage addRepresentation:m_ThumbBitmap]; + } + + [self unlock]; + DEBUG_FUNC_LEAVE(); +} + +- (BOOL)isFlipped +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + return YES; +} + +- (void)drawRect:(NSRect)aRect +{ + NSRect frame; + DEBUG_FUNC_ENTER(); + [self lock]; + +#ifdef SHOW_WINDOW_BACKGROUND + [[NSColor colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:0.7] set]; + frame = [self frame]; + [NSBezierPath fillRect:NSMakeRect(0, 0, frame.size.width, frame.size.height)]; +#endif /* SHOW_WINDOW_BACKGROUND */ + if (m_ThumbImage != nil) + [m_ThumbImage drawAtPoint:NSMakePoint(0, 0) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + + [self unlock]; + DEBUG_FUNC_LEAVE(); +} + +- (NSBitmapImageRep *)thumbBitmap +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + return m_ThumbBitmap; +} + +- (NSImage *)thumbImage +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + return m_ThumbImage; +} +@end +#endif /* !IN_VMSVGA3D */ + + +/******************************************************************************** +* +* OverlayOpenGLContext class implementation +* +********************************************************************************/ +@implementation OverlayOpenGLContext + +-(id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share +{ + DEBUG_FUNC_ENTER(); + + m_pPixelFormat = NULL; + m_pView = NULL; + + self = [super initWithFormat:format shareContext:share]; + Assert(self != nil); + if (self) + m_pPixelFormat = format; + + DEBUG_MSG(("OCTX(%p): init OverlayOpenGLContext\n", (void *)self)); + DEBUG_FUNC_LEAVE(); + return self; +} + +- (void)dealloc +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OCTX(%p): dealloc OverlayOpenGLContext\n", (void *)self)); + + [m_pPixelFormat release]; + + [super dealloc]; + + DEBUG_FUNC_LEAVE(); +} + +-(bool)isDoubleBuffer +{ + DEBUG_FUNC_ENTER(); + + GLint val; + [m_pPixelFormat getValues:&val forAttribute:NSOpenGLPFADoubleBuffer forVirtualScreen:0]; + + DEBUG_FUNC_LEAVE(); + return val == GL_TRUE ? YES : NO; +} + +-(void)setView:(NSView *)view +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OCTX(%p): setView: new view: %p\n", (void *)self, (void *)view)); + +#if 1 /* def FBO */ + m_pView = view;; +#else + [super setView: view]; +#endif + + DEBUG_FUNC_LEAVE(); +} + +-(NSView *)view +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); +#if 1 /* def FBO */ + return m_pView; +#else + return [super view]; +#endif +} + +-(void)clearDrawable +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OCTX(%p): clearDrawable\n", (void *)self)); + + m_pView = NULL;; + [super clearDrawable]; + + DEBUG_FUNC_LEAVE(); +} + +-(NSOpenGLPixelFormat *)openGLPixelFormat +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + + return m_pPixelFormat; +} + +@end /* @implementation OverlayOpenGLContext */ + + +/******************************************************************************** +* +* OverlayHelperView class implementation +* +********************************************************************************/ +@implementation OverlayHelperView + +-(id)initWithOverlayWindow:(NSRect)frame overlayWindow:(OverlayWindow *)pOverlayWindow +{ + DEBUG_FUNC_ENTER(); + + self = [super initWithFrame:frame]; +#ifdef IN_VMSVGA3D + self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; +#endif + + m_pOverlayWindow = pOverlayWindow; + + DEBUG_MSG(("OHVW(%p): init OverlayHelperView\n", (void *)self)); + DEBUG_FUNC_LEAVE(); + return self; +} + +-(void)viewDidMoveToWindow +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OHVW(%p): viewDidMoveToWindow: new win: %p\n", (void *)self, (void *)[self window])); + + [m_pOverlayWindow parentWindowChanged:[self window]]; + + DEBUG_FUNC_LEAVE(); +} + +@end + + +/******************************************************************************** +* +* OverlayWindow class implementation +* +********************************************************************************/ +@implementation OverlayWindow + +- (id)initWithParentView:(NSView *)pParentView overlayView:(OverlayView *)pOverlayView +{ + DEBUG_FUNC_ENTER(); + NSWindow *pParentWin = nil; + + self = [super initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + if (self) + { + m_pParentView = pParentView; + m_pOverlayView = pOverlayView; + m_Thread = [NSThread currentThread]; + + [m_pOverlayView setOverlayWin: self]; + +#ifdef IN_VMSVGA3D + NSRect frame = [pParentView frame]; + frame.origin.x = frame.origin.x = 0; + m_pOverlayHelperView = [[OverlayHelperView alloc] initWithOverlayWindow:frame + overlayWindow:self]; +#else + m_pOverlayHelperView = [[OverlayHelperView alloc] initWithOverlayWindow:NSZeroRect + overlayWindow:self]; +#endif + + /* Add the helper view as a child of the parent view to get notifications */ + [pParentView addSubview:m_pOverlayHelperView]; + + /* Make sure this window is transparent */ +#ifdef SHOW_WINDOW_BACKGROUND + /* For debugging */ + [self setBackgroundColor:[NSColor colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:0.7]]; +#else + [self setBackgroundColor:[NSColor clearColor]]; +#endif + [self setOpaque:NO]; + [self setAlphaValue:.999]; + + /* Disable mouse events for this window */ + [self setIgnoresMouseEvents:YES]; + + pParentWin = [m_pParentView window]; + + /* Initial set the position to the parents view top/left (Compiz fix). */ + [self setFrameOrigin: + [pParentWin convertBaseToScreen: + [m_pParentView convertPoint:NSZeroPoint toView:nil]]]; + + /* Set the overlay view as our content view */ + [self setContentView:m_pOverlayView]; + + /* Add ourself as a child to the parent views window. Note: this has to + * be done last so that everything else is setup in + * parentWindowChanged. */ + [pParentWin addChildWindow:self ordered:NSWindowAbove]; + } + + DEBUG_MSG(("OWIN(%p): init OverlayWindow\n", (void *)self)); + DEBUG_FUNC_LEAVE(); + return self; +} + +- (void)dealloc +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OWIN(%p): dealloc OverlayWindow\n", (void *)self)); + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [m_pOverlayHelperView removeFromSuperview]; + [m_pOverlayHelperView release]; + + [super dealloc]; + + DEBUG_FUNC_LEAVE(); +} + +- (void)parentWindowFrameChanged:(NSNotification *)pNote +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OWIN(%p): parentWindowFrameChanged\n", (void *)self)); + + /* + * Reposition this window with the help of the OverlayView. Perform the + * call in the OpenGL thread. + */ + /* + [m_pOverlayView performSelector:@selector(vboxReshapePerform) onThread:m_Thread withObject:nil waitUntilDone:YES]; + */ + + if ([m_pOverlayView isEverSized]) + { + if ([NSThread isMainThread]) + [m_pOverlayView vboxReshapePerform]; + else + [self performSelectorOnMainThread:@selector(vboxReshapePerform) withObject:nil waitUntilDone:NO]; + } + + DEBUG_FUNC_LEAVE(); +} + +- (void)parentWindowChanged:(NSWindow *)pWindow +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OWIN(%p): parentWindowChanged\n", (void *)self)); + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + if (pWindow != nil) + { + /* Ask to get notifications when our parent window frame changes. */ + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(parentWindowFrameChanged:) + name:NSWindowDidResizeNotification + object:pWindow]; + + /* Add us self as child window */ + [pWindow addChildWindow:self ordered:NSWindowAbove]; + + /* + * Reshape the overlay view after a short waiting time to let the main + * window resize itself properly. + */ + /* + [m_pOverlayView performSelector:@selector(vboxReshapePerform) withObject:nil afterDelay:0.2]; + [NSTimer scheduledTimerWithTimeInterval:0.2 target:m_pOverlayView selector:@selector(vboxReshapePerform) userInfo:nil repeats:NO]; + */ + + if ([m_pOverlayView isEverSized]) + { + if ([NSThread isMainThread]) + [m_pOverlayView vboxReshapePerform]; + else + [self performSelectorOnMainThread:@selector(vboxReshapePerform) withObject:nil waitUntilDone:NO]; + } + } + + DEBUG_FUNC_LEAVE(); +} + +@end /* @implementation OverlayWindow */ + + + +/******************************************************************************** +* +* OverlayView class implementation +* +********************************************************************************/ +@implementation OverlayView + +- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView *)pParentView winInfo:(WindowInfo *)pWinInfo + fVisParams:(GLbitfield) fVisParams +{ + COCOA_LOG_FLOW(("%s: self=%p aThread=%p pParentView=%p pWinInfo=%p fVisParams=%#x\n", __PRETTY_FUNCTION__, (void *)self, + (void *)aThread, (void *)pParentView, (void *)pWinInfo, fVisParams)); + + m_pParentView = pParentView; + /* Make some reasonable defaults */ + m_pGLCtx = nil; + m_pSharedGLCtx = nil; + m_Thread = aThread; + m_FBOId = 0; + m_cClipRects = 0; + m_paClipRects = NULL; + m_Pos = NSZeroPoint; + m_Size = NSMakeSize(1, 1); + m_RootRect = NSMakeRect(0, 0, m_Size.width, m_Size.height); + m_yInvRootOffset = 0; +#ifndef IN_VMSVGA3D + m_pBlitter = nil; + m_pWinInfo = pWinInfo; +#endif + m_fNeedViewportUpdate = true; + m_fNeedCtxUpdate = true; + m_fDataVisible = false; + m_fCleanupNeeded = false; + m_fEverSized = false; + + self = [super initWithFrame:frame]; +#if defined(VBOX_WITH_CONFIGURABLE_HIDPI_SCALING) && !defined(IN_VMSVGA3D) + /* Always allocate HiDPI-ready backing store for NSView, so we will be able change HiDPI scaling option in runtime. */ + crDebug("HiDPI: Allocate big backing store for NSView. Up-scaling is currently %s.", render_spu.fUnscaledHiDPI ? "OFF" : "ON"); + [self performSelector:@selector(setWantsBestResolutionOpenGLSurface:) withObject: (id)YES]; +#endif + + COCOA_LOG_FLOW(("%s: returns self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + return self; +} + +- (void)cleanupData +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + +#ifndef IN_VMSVGA3D + [self deleteDockTile]; +#endif + + [self setGLCtx:nil]; + + if (m_pSharedGLCtx) + { + if ([m_pSharedGLCtx view] == self) + [m_pSharedGLCtx clearDrawable]; + + [m_pSharedGLCtx release]; + m_pSharedGLCtx = nil; + + +#ifndef IN_VMSVGA3D + CrBltTerm(m_pBlitter); + RTMemFree(m_pBlitter); + m_pBlitter = nil; +#endif + } + + [self clearVisibleRegions]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)dealloc +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + [self cleanupData]; + [super dealloc]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)drawRect:(NSRect)aRect +{ + COCOA_LOG_FLOW(("%s: self=%p aRect=%d,%d %d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)aRect.origin.x, (int)aRect.origin.y, + (int)aRect.size.width, (int)aRect.size.height)); + + [self vboxTryDrawUI]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)setGLCtx:(NSOpenGLContext *)pCtx +{ + COCOA_LOG_FLOW(("%s: self=%p pCtx=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCtx, m_pGLCtx)); + + /* + * Only do something if the context changes. + */ + if (m_pGLCtx != pCtx) + { + /* Ensure the context drawable is cleared to avoid holding a reference to inexistent view. */ + if (m_pGLCtx) + { +#ifdef IN_VMSVGA3D + Assert(!pCtx); +#endif + [m_pGLCtx clearDrawable]; + [m_pGLCtx release]; + /*[m_pGLCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/ + } + + m_pGLCtx = pCtx; + if (pCtx) + [pCtx retain]; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSOpenGLContext *)glCtx +{ + COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pGLCtx)); + return m_pGLCtx; +} + +- (NSView *)parentView +{ + COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pParentView)); + return m_pParentView; +} + +- (void)setParentView:(NSView *)pView +{ + COCOA_LOG_FLOW(("%s: self=%p pView=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pView, m_pParentView)); + + m_pParentView = pView; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)setOverlayWin:(NSWindow *)pWin +{ + COCOA_LOG_FLOW(("%s: self=%p pWin=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pWin, m_pOverlayWin)); + + m_pOverlayWin = pWin; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSWindow *)overlayWin +{ + COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pOverlayWin)); + return m_pOverlayWin; +} + +- (void)vboxSetPosUI:(NSPoint)pos +{ + COCOA_LOG_FLOW(("%s: self=%p pos=%d,%d (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (int)pos.x, (int)pos.y, + (int)m_Pos.x, (int)m_Pos.y)); + + DEBUG_MSG(("vboxSetPosUI: [%d, %d].\n", (int)pos.x, (int)pos.y)); + + m_Pos = pos; + + if (m_fEverSized) + [self vboxReshapePerform]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetPosUIObj:(NSValue *)pPos +{ + COCOA_LOG_FLOW(("%s: self=%p pPos=%p (%d,%d) (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, pPos, + (int)[pPos pointValue].x, (int)[pPos pointValue].y, (int)m_Pos.x, (int)m_Pos.y)); + + NSPoint pos = [pPos pointValue]; + [self vboxSetPosUI:pos]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetPos:(NSPoint)pos +{ + COCOA_LOG_FLOW(("%s: self=%p pos=%d,%d (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (int)pos.x, (int)pos.y, + (int)m_Pos.x, (int)m_Pos.y)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + NSValue *pPos = [NSValue valueWithPoint:pos]; + [pRunner addObj:self selector:@selector(vboxSetPosUIObj:) arg:pPos]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSPoint)pos +{ + COCOA_LOG_FLOW(("%s: self=%p returns %d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)m_Pos.x, (int)m_Pos.y)); + return m_Pos; +} + +- (bool)isEverSized +{ + COCOA_LOG_FLOW(("%s: self=%p returns %d\n", __PRETTY_FUNCTION__, (void *)self, m_fEverSized)); + return m_fEverSized; +} + +- (void)vboxDestroy +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + BOOL fIsMain = [NSThread isMainThread]; + NSWindow *pWin = nil; + + Assert(fIsMain); + + /* Hide the view early. */ + [self setHidden: YES]; + + pWin = [self window]; + [[NSNotificationCenter defaultCenter] removeObserver:pWin]; + [pWin setContentView: nil]; + [[pWin parentWindow] removeChildWindow: pWin]; + + if (fIsMain) + [pWin release]; + else + { + /* We can NOT run synchronously with the main thread since this may lead to a deadlock, + caused by main thread waiting xpcom thread, xpcom thread waiting to main hgcm thread, + and main hgcm thread waiting for us, this is why use waitUntilDone:NO, + which should cause no harm. */ + [pWin performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; + } + + [self cleanupData]; + + if (fIsMain) + [self release]; + else + { + /* We can NOT run synchronously with the main thread since this may lead to a deadlock, + caused by main thread waiting xpcom thread, xpcom thread waiting to main hgcm thread, + and main hgcm thread waiting for us, this is why use waitUntilDone:NO. + We need to avoid concurrency though, so we cleanup some data right away via a cleanupData call. */ + [self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; + } + +#ifndef IN_VMSVGA3D + renderspuWinRelease(m_pWinInfo); +#endif + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetSizeUIObj:(NSValue *)pSize +{ + COCOA_LOG_FLOW(("%s: self=%p pSize=%p (%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pSize, + (int)[pSize sizeValue].width, (int)[pSize sizeValue].height)); + + NSSize size = [pSize sizeValue]; + [self vboxSetSizeUI:size]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetSizeUI:(NSSize)size +{ + COCOA_LOG_FLOW(("%s: self=%p size=%d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)size.width, (int)size.height)); + + m_Size = size; + m_fEverSized = true; + + DEBUG_MSG(("OVIW(%p): vboxSetSize: new size: %dx%d\n", (void *)self, (int)m_Size.width, (int)m_Size.height)); + [self vboxReshapeOnResizePerform]; + + /* ensure window contents is updated after that */ + [self vboxTryDrawUI]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetSize:(NSSize)size +{ + COCOA_LOG_FLOW(("%s: self=%p size=%d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)size.width, (int)size.height)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + NSValue *pSize = [NSValue valueWithSize:size]; + [pRunner addObj:self selector:@selector(vboxSetSizeUIObj:) arg:pSize]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSSize)size +{ + COCOA_LOG_FLOW(("%s: self=%p returns %d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)m_Size.width, (int)m_Size.height)); + return m_Size; +} + +- (void)updateViewportCS +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + /* Update the viewport for our OpenGL view. */ + [m_pSharedGLCtx update]; + +#ifndef IN_VMSVGA3D + [self vboxBlitterSyncWindow]; +#endif + + /* Clear background to transparent. */ + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReshapeOnResizePerform +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + [self vboxReshapePerform]; +#ifndef IN_VMSVGA3D + [self createDockTile]; +#endif + + /* have to rebind GL_TEXTURE_RECTANGLE_ARB as m_FBOTexId could be changed in updateFBO call */ + m_fNeedViewportUpdate = true; +#if 0 + pCurCtx = [NSOpenGLContext currentContext]; + if (pCurCtx && pCurCtx == m_pGLCtx && (pCurView = [pCurCtx view]) == self) + { + [m_pGLCtx update]; + m_fNeedCtxUpdate = false; + } + else + { + /* do it in a lazy way */ + m_fNeedCtxUpdate = true; + } +#endif + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReshapeOnReparentPerform +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + [self vboxReshapePerform]; +#ifndef IN_VMSVGA3D + [self createDockTile]; +#endif + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#ifdef VBOX_WITH_CONFIGURABLE_HIDPI_SCALING +- (NSRect)safeConvertRectToBacking:(NSRect *)pRect +{ + NSRect resultingRect = NSZeroRect; + + NSWindow *pWindow = [m_pParentView window]; + if (pWindow) + { + if ([pWindow respondsToSelector:@selector(convertRectToBacking:)]) + { + NSMethodSignature *pSignature = [pWindow methodSignatureForSelector:@selector(convertRectToBacking:)]; + if (pSignature) + { + NSInvocation *pInvocation = [NSInvocation invocationWithMethodSignature:pSignature]; + if (pInvocation) + { + [pInvocation setSelector:@selector(convertRectToBacking:)]; + [pInvocation setTarget:pWindow]; + [pInvocation setArgument:pRect atIndex:2]; + [pInvocation invoke]; + [pInvocation getReturnValue:&resultingRect]; + + DEBUG_MSG(("safeConvertRectToBacking: convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; + } + } + } + } + else + /* Should never happen. */ + DEBUG_WARN(("safeConvertRectToBacking: parent widget has no window.\n")); + + resultingRect = *pRect; + + DEBUG_MSG(("safeConvertRectToBacking (reurn as is): convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; +} + + +- (CGFloat)safeGetBackingScaleFactor +{ + /* Assume its default value. */ + CGFloat backingScaleFactor = 1.; + + NSWindow *pWindow = [m_pParentView window]; + if (pWindow) + { + NSScreen *pScreen = [pWindow screen]; + if (pScreen) + { + if ([pScreen respondsToSelector:@selector(backingScaleFactor)]) + { + NSMethodSignature *pSignature = [pScreen methodSignatureForSelector:@selector(backingScaleFactor)]; + if (pSignature) + { + NSInvocation *pInvocation = [NSInvocation invocationWithMethodSignature:pSignature]; + if (pInvocation) + { + [pInvocation setSelector:@selector(backingScaleFactor)]; + [pInvocation setTarget:pScreen]; + [pInvocation invoke]; + [pInvocation getReturnValue:&backingScaleFactor]; + + DEBUG_MSG(("safeGetBackingScaleFactor: %d\n", (int)backingScaleFactor)); + + return backingScaleFactor; + } + else + DEBUG_WARN(("safeGetBackingScaleFactor: unable to create invocation for backingScaleFactor method signature.\n")); + } + else + DEBUG_WARN(("safeGetBackingScaleFactor: unable to create method signature for backingScaleFactor selector.\n")); + } + else + DEBUG_WARN(("safeGetBackingScaleFactor: NSScreen does not respond to backingScaleFactor selector.\n")); + } + else + /* Should never happen. */ + DEBUG_WARN(("safeGetBackingScaleFactor: parent window has no screen.\n")); + } + else + /* Should never happen. */ + DEBUG_WARN(("safeGetBackingScaleFactor: parent widget has no window.\n")); + + return backingScaleFactor; +} + +#endif + + +- (NSRect)safeConvertToScreen:(NSRect *)pRect +{ + NSRect resultingRect = NSZeroRect; + + NSWindow *pWindow = [m_pParentView window]; + if (pWindow) + { + if ([pWindow respondsToSelector:@selector(convertRectToScreen:)]) + { + NSMethodSignature *pSignature = [pWindow methodSignatureForSelector:@selector(convertRectToScreen:)]; + if (pSignature) + { + NSInvocation *pInvocation = [NSInvocation invocationWithMethodSignature:pSignature]; + if (pInvocation) + { + [pInvocation setSelector:@selector(convertRectToScreen:)]; + [pInvocation setTarget:pWindow]; + [pInvocation setArgument:pRect atIndex:2]; + [pInvocation invoke]; + [pInvocation getReturnValue:&resultingRect]; + + DEBUG_MSG(("safeConvertToScreen: convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; + } + } + } + + /* If we failed, let's use deprecated @selector(convertBaseToScreen:). It is a bit hacky, + * but what to do if we stick to SDK 10.6. */ + resultingRect.origin = [[m_pParentView window] convertBaseToScreen:pRect->origin]; + resultingRect.size = pRect->size; + } + else + /* Should never happen. */ + DEBUG_WARN(("safeConvertToScreen: parent widget has no window.\n")); + + DEBUG_MSG(("safeConvertToScreen (deprecated method): convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; +} + +- (void)vboxReshapePerform +{ +#ifndef IN_VMSVGA3D + COCOA_LOG_FLOW(("%s: self=%p - m_DockTileView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_DockTileView)); +#else + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); +#endif + + /* NOTE: Please consider the next naming convention for variables. + * + * Rectangle variables: + * + * <object to represent><coordinate system>: + * <object to represent>: + * parentFrame - a frame of the parent container (NSView) object + * childFrame - a frame required to display guest content + * windowFrame - resulting window frame constructed as an intersection of parentFrame and childFrame + * <coordinate system>: + * VCS - View Coordinate System + * WCS - Window Coordinate System + * SCS - Screen Coordinate System + * + * The same convention applied to offset variables naming as well which are of format: + * + * <object to represent><coordinate><coordinate system>. + * + * https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html + */ + + NSRect parentFrameVCS, parentFrameWCS, parentFrameSCS; + NSRect childFrameWCS, childFrameSCS; + NSRect windowFrameSCS; + + CGFloat childFrameXWCS, childFrameYWCS; + + /* We need to construct a new window frame (windowFrameSCS) for entire NSWindow object in + * screen coordinates. In order to make 3D overlay window to do not overlap Cocoa and Qt GUI elements (titlebar, + * Qt statusbar, scroll bars etc) let's do the next. Get parent view visible area (parentFrameSCS) in (NS)Screen + * coordinates. Then get the area required to diaplay guest content (childFrameSCS) in (NS)Screen coordinates as well. + * The intersection of these two areas in screen coordinates will be a new frame for entire NSWindow object. */ + + parentFrameVCS = [m_pParentView frame]; + parentFrameWCS = [m_pParentView convertRect:parentFrameVCS toView:nil]; + parentFrameSCS = [self safeConvertToScreen:&parentFrameWCS]; + + /* Choose childFrame origin in a bit special way. Its pop-left corner should stick to its parent top-left corner. */ + childFrameXWCS = parentFrameWCS.origin.x + m_Pos.x; + childFrameYWCS = parentFrameWCS.origin.y - m_Pos.y - (m_Size.height - parentFrameWCS.size.height); + childFrameWCS = NSMakeRect(childFrameXWCS, childFrameYWCS, m_Size.width, m_Size.height); + childFrameSCS = [self safeConvertToScreen:&childFrameWCS]; + + windowFrameSCS = NSIntersectionRect(parentFrameSCS, childFrameSCS); + + DEBUG_MSG(("vboxReshapePerform: a new overlay frame [%d, %d, %dx%d] has been constructed from intersection of window frame " + "[%d, %d, %dx%d] and guest content rectangle [%d, %d, %dx%d]; m_Pos=[%d, %d], m_Size=%dx%d.\n", + (int)windowFrameSCS.origin.x, (int)windowFrameSCS.origin.y, (int)windowFrameSCS.size.width, (int)windowFrameSCS.size.width, + (int)parentFrameSCS.origin.x, (int)parentFrameSCS.origin.y, (int)parentFrameSCS.size.width, (int)parentFrameSCS.size.width, + (int)childFrameSCS .origin.x, (int)childFrameSCS .origin.y, (int)childFrameSCS .size.width, (int)childFrameSCS .size.width, + (int)m_Pos.x, (int)m_Pos.y, (int)m_Size.width, (int)m_Size.height)); + + /** @todo galitsyn: drop this! + * Later we have to correct the texture position in the case the window is + * out of the parents window frame. So save the shift values for later use. */ + m_RootRect.origin.x = windowFrameSCS.origin.x - childFrameSCS.origin.x; + m_RootRect.origin.y = childFrameSCS.size.height + childFrameSCS.origin.y - (windowFrameSCS.size.height + windowFrameSCS.origin.y); + m_RootRect.size = windowFrameSCS.size; + m_yInvRootOffset = windowFrameSCS.origin.y - childFrameSCS.origin.y; + + DEBUG_MSG(("vboxReshapePerform: [%#p]: m_RootRect pos[%d : %d] size[%d : %d]\n", + (void *)self, (int)m_RootRect.origin.x, (int)m_RootRect.origin.y, (int)m_RootRect.size.width, (int)m_RootRect.size.height)); + + /* Set the new frame. */ + [[self window] setFrame:windowFrameSCS display:YES]; + +#ifndef IN_VMSVGA3D + /* Inform the dock tile view as well. */ + [self reshapeDockTile]; +#endif + + /* Make sure the context is updated accordingly. */ + /* [self updateViewport]; */ + if (m_pSharedGLCtx) + { + VBOX_CR_RENDER_CTX_INFO CtxInfo; + vboxCtxEnter(m_pSharedGLCtx, &CtxInfo); + + [self updateViewportCS]; + + vboxCtxLeave(&CtxInfo); + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#ifndef IN_VMSVGA3D + +- (void)createDockTile +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + NSView *pDockScreen = nil; + + [self deleteDockTile]; + + /* Is there a dock tile preview enabled in the GUI? If so setup a + * additional thumbnail view for the dock tile. */ + pDockScreen = [self dockTileScreen]; + if (pDockScreen) + { + m_DockTileView = [[DockOverlayView alloc] init]; + [self reshapeDockTile]; + [pDockScreen addSubview:m_DockTileView]; + } + + COCOA_LOG_FLOW(("%s: returns - m_DockTileView\n", __PRETTY_FUNCTION__, (void *)m_DockTileView)); +} + +- (void)deleteDockTile +{ + COCOA_LOG_FLOW(("%s: self=%p - m_DockTileView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_DockTileView)); + + if (m_DockTileView != nil) + { + [m_DockTileView removeFromSuperview]; + [m_DockTileView release]; + m_DockTileView = nil; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#endif /* !IN_VMSVGA3D */ + +- (void)makeCurrentFBO +{ + COCOA_LOG_FLOW(("%s: self=%p - m_pGLCtx=%p m_fNeedCtxUpdate=%d\n", __PRETTY_FUNCTION__, (void *)self, + (void *)m_pGLCtx, m_fNeedCtxUpdate)); + + if (m_pGLCtx) + { + NSOpenGLContext *pPrevCtx = [NSOpenGLContext currentContext]; + +#ifdef IN_VMSVGA3D + /* Always flush before flush. glXMakeCurrent and wglMakeCurrent does this + implicitly, seemingly NSOpenGLContext::makeCurrentContext doesn't. */ + if (pPrevCtx != nil) + { + DEBUG_CLEAR_GL_ERRORS(); + glFlush(); + DEBUG_CHECK_GL_ERROR("glFlush"); + } +#endif + + if ([m_pGLCtx view] != self) + { +#ifndef IN_VMSVGA3D + /* We change the active view, so flush first */ + if (pPrevCtx != nil) + glFlush(); +#endif + DEBUG_CLEAR_GL_ERRORS(); + [m_pGLCtx setView: self]; + DEBUG_CHECK_GL_ERROR("setView"); + } + +#if 0 + if (pPrevCtx != m_pGLCtx) +#endif + { + DEBUG_CLEAR_GL_ERRORS(); + [m_pGLCtx makeCurrentContext]; + DEBUG_CHECK_GL_ERROR("makeCurrentContext"); + Assert([NSOpenGLContext currentContext] == m_pGLCtx); + Assert([m_pGLCtx view] == self); + } + + if (m_fNeedCtxUpdate == true) + { + [m_pGLCtx update]; + m_fNeedCtxUpdate = false; + } + + if (!m_FBOId) + { + glGenFramebuffersEXT(1, &m_FBOId); + Assert(m_FBOId); + } + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (bool)vboxSharedCtxCreate +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + if (m_pSharedGLCtx) + { + COCOA_LOG_FLOW(("%s: returns true (m_pSharedGLCtx=%p)\n", __PRETTY_FUNCTION__, (void *)m_pSharedGLCtx)); + return true; + } + +#ifndef IN_VMSVGA3D + Assert(!m_pBlitter); + m_pBlitter = RTMemAlloc(sizeof(*m_pBlitter)); + if (RT_UNLIKELY(!m_pBlitter)) + { + DEBUG_WARN(("m_pBlitter allocation failed")); + COCOA_LOG_FLOW(("%s: returns false - m_pBlitter allocation failed\n", __PRETTY_FUNCTION__)); + return false; + } + + int rc = CrBltInit(m_pBlitter, NULL, false /*fCreateNewCtx*/, false /*fForceDrawBlt*/, + &render_spu.GlobalShaders, &render_spu.blitterDispatch); + if (RT_FAILURE(rc)) + { + DEBUG_WARN(("CrBltInit failed, rc %d", rc)); + RTMemFree(m_pBlitter); + m_pBlitter = NULL; + + COCOA_LOG_FLOW(("%s: returns false - CrBltInit failed with rc=%Rrc\n", __PRETTY_FUNCTION__, rc)); + return false; + } + + COCOA_LOG_FLOW(("%s: blitter (%p) created successfully for view 0x%p\n", (void *)m_pBlitter, (void *)self)); +#endif /* !IN_VMSVGA3D */ + + /* Create a shared context out of the main context. Use the same pixel format. */ + NSOpenGLPixelFormat *pPixelFormat = [(OverlayOpenGLContext *)m_pGLCtx openGLPixelFormat]; + NSOpenGLContext *pSharedGLCtx = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:m_pGLCtx]; + + /* Set the new context as non opaque */ + GLint opaque = 0; + [pSharedGLCtx setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; + + /* Set this view as the drawable for the new context */ + [pSharedGLCtx setView:self]; + m_fNeedViewportUpdate = true; + + m_pSharedGLCtx = pSharedGLCtx; + + COCOA_LOG_FLOW(("%s: returns true - new m_pSharedGLCtx=%p\n", __PRETTY_FUNCTION__, (void *)m_pSharedGLCtx)); + return true; +} + +- (void)vboxTryDraw +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + glFlush(); + + /* Issue to the gui thread. */ + [self performSelectorOnMainThread:@selector(vboxTryDrawUI) withObject:nil waitUntilDone:NO]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetVisible:(GLboolean)fVisible +{ + COCOA_LOG_FLOW(("%s: self=%p fVisible=%d\n", __PRETTY_FUNCTION__, (void *)self, fVisible)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + NSNumber *pVisObj = [NSNumber numberWithBool:fVisible]; + [pRunner addObj:self selector:@selector(vboxSetVisibleUIObj:) arg:pVisObj]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetVisibleUI:(GLboolean)fVisible +{ + COCOA_LOG_FLOW(("%s: self=%p fVisible=%d\n", __PRETTY_FUNCTION__, (void *)self, fVisible)); + + [self setHidden: !fVisible]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetVisibleUIObj:(NSNumber *)pVisibleObj +{ + COCOA_LOG_FLOW(("%s: self=%p pVisibleObj=%p(%d)\n", __PRETTY_FUNCTION__, + (void *)self, (void *)pVisibleObj, [pVisibleObj boolValue])); + + BOOL fVisible = [pVisibleObj boolValue]; + [self vboxSetVisibleUI:fVisible]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReparent:(NSView *)pParentView +{ + COCOA_LOG_FLOW(("%s: self=%p pParentView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pParentView)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner addObj:self selector:@selector(vboxReparentUI:) arg:pParentView]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReparentUI:(NSView *)pParentView +{ + COCOA_LOG_FLOW(("%s: self=%p pParentView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pParentView)); + + /* Make sure the window is removed from any previous parent window. */ + if ([[self overlayWin] parentWindow] != nil) + { + [[[self overlayWin] parentWindow] removeChildWindow:[self overlayWin]]; + } + + /* Set the new parent view */ + [self setParentView: pParentView]; + + /* Add the overlay window as a child to the new parent window */ + if (pParentView != nil) + { + [[pParentView window] addChildWindow:[self overlayWin] ordered:NSWindowAbove]; + if ([self isEverSized]) + [self vboxReshapeOnReparentPerform]; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxTryDrawUI +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + if ([self isHidden]) + { + COCOA_LOG_FLOW(("%s: returns - request to draw on a hidden view\n", __PRETTY_FUNCTION__)); + return; + } + + if ([[self overlayWin] parentWindow] == nil) + { + COCOA_LOG_FLOW(("%s: returns - request to draw a view w/o a parent\n", __PRETTY_FUNCTION__)); + return; + } + +#ifdef IN_VMSVGA3D + if (!m_pSharedGLCtx) + { + Assert(!m_fDataVisible); + Assert(!m_fCleanupNeeded); + if (![self vboxSharedCtxCreate]) + { + COCOA_LOG_FLOW(("%s: returns - vboxSharedCtxCreate failed\n", __PRETTY_FUNCTION__)); + return; + } + Assert(m_pSharedGLCtx); + } +#endif + + const VBOXVR_SCR_COMPOSITOR *pCompositor = NULL; +#ifndef IN_VMSVGA3D + int rc = renderspuVBoxCompositorLock(m_pWinInfo, &pCompositor); + if (RT_FAILURE(rc)) + { + COCOA_LOG_FLOW(("%s: returns - renderspuVBoxCompositorLock failed (%Rrc)\n", __PRETTY_FUNCTION__, rc)); + return; + } + + if (!pCompositor && !m_fCleanupNeeded) + { + renderspuVBoxCompositorUnlock(m_pWinInfo); + COCOA_LOG_FLOW(("%s: returns - noCompositorUI\n", __PRETTY_FUNCTION__)); + return; + } + + VBOXVR_SCR_COMPOSITOR TmpCompositor; + if (pCompositor) + { + if (!m_pSharedGLCtx) + { + Assert(!m_fDataVisible); + Assert(!m_fCleanupNeeded); + renderspuVBoxCompositorRelease(m_pWinInfo); + if (![self vboxSharedCtxCreate]) + { + COCOA_LOG_FLOW(("%s: returns - vboxSharedCtxCreate failed\n", __PRETTY_FUNCTION__)); + return; + } + + Assert(m_pSharedGLCtx); + + pCompositor = renderspuVBoxCompositorAcquire(m_pWinInfo); + Assert(!m_fDataVisible); + Assert(!m_fCleanupNeeded); + if (!pCompositor) + { + COCOA_LOG_FLOW(("%s: returns - Failed to reacquire compositor\n", __PRETTY_FUNCTION__)); + return; + } + } + } + else + { + DEBUG_MSG(("%s: NeedCleanup\n", __PRETTY_FUNCTION__)); + Assert(m_fCleanupNeeded); + CrVrScrCompositorInit(&TmpCompositor, NULL); + pCompositor = &TmpCompositor; + } +#endif /* !IN_VMSVGA3D */ + + + if ([self lockFocusIfCanDraw]) + { + COCOA_LOG_FLOW(("%s: Calling vboxPresent\n", __PRETTY_FUNCTION__)); + [self vboxPresent:pCompositor]; + [self unlockFocus]; + } +#ifndef IN_VMSVGA3D /** @todo VMSVGA3 */ + else if (!m_pWinInfo->visible) + { + COCOA_LOG_FLOW(("%s: NotVisible\n", __PRETTY_FUNCTION__)); + m_fCleanupNeeded = false; + } +#endif + else + { + COCOA_LOG_FLOW(("%s: Reschedule\n", __PRETTY_FUNCTION__)); + [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(vboxTryDrawUI) userInfo:nil repeats:NO]; + } + +#ifndef IN_VMSVGA3D + renderspuVBoxCompositorUnlock(m_pWinInfo); +#endif + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)swapFBO +{ + COCOA_LOG_FLOW(("%s: self=%p - m_pGLCtx=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pGLCtx)); + [m_pGLCtx flushBuffer]; + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxPresent:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor)); + /*DEBUG_MSG(("OVIW(%p): renderFBOToView\n", (void *)self));*/ +#ifndef IN_VMSVGA3D + AssertPtr(pCompositor); +#endif + + VBOX_CR_RENDER_CTX_INFO CtxInfo; + vboxCtxEnter(m_pSharedGLCtx, &CtxInfo); + + [self vboxPresentCS:pCompositor]; + + vboxCtxLeave(&CtxInfo); + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxPresentCS:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor)); + if ([m_pSharedGLCtx view] != self) + { + COCOA_LOG_FLOW(("%s: Not current view of shared ctx! Switching... (self=%p, view=%p, m_pSharedGLCtx)\n", + __PRETTY_FUNCTION__, (void *)self, (void *)[m_pSharedGLCtx view], (void *)m_pSharedGLCtx)); + [m_pSharedGLCtx setView: self]; + m_fNeedViewportUpdate = true; + } + + if (m_fNeedViewportUpdate) + { + [self updateViewportCS]; + m_fNeedViewportUpdate = false; + } + + m_fCleanupNeeded = false; + +#ifndef IN_VMSVGA3D + /* Render FBO content to the dock tile when necessary. */ + [self vboxPresentToDockTileCS:pCompositor]; +#endif + + /* change to #if 0 to see thumbnail image */ +#if 1 + [self vboxPresentToViewCS:pCompositor]; +#else + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + [m_pSharedGLCtx flushBuffer]; +#endif + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +DECLINLINE(void) vboxNSRectToRect(const NSRect *pR, RTRECT *pRect) +{ + pRect->xLeft = (int)pR->origin.x; + pRect->yTop = (int)pR->origin.y; + pRect->xRight = (int)(pR->origin.x + pR->size.width); + pRect->yBottom = (int)(pR->origin.y + pR->size.height); +} + +DECLINLINE(void) vboxNSRectToRectUnstretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch) +{ + pRect->xLeft = (int)(pR->origin.x / xStretch); + pRect->yTop = (int)(pR->origin.y / yStretch); + pRect->xRight = (int)((pR->origin.x + pR->size.width) / xStretch); + pRect->yBottom = (int)((pR->origin.y + pR->size.height) / yStretch); +} + +DECLINLINE(void) vboxNSRectToRectStretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch) +{ + pRect->xLeft = (int)(pR->origin.x * xStretch); + pRect->yTop = (int)(pR->origin.y * yStretch); + pRect->xRight = (int)((pR->origin.x + pR->size.width) * xStretch); + pRect->yBottom = (int)((pR->origin.y + pR->size.height) * yStretch); +} + +- (void)vboxPresentToViewCS:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + NSRect r = [self frame]; + COCOA_LOG_FLOW(("%s: self=%p - r={%d,%d %d,%d}\n", __PRETTY_FUNCTION__, (void *)self, + (int)r.origin.x, (int)r.origin.y, (int)r.size.width, (int)r.size.height)); + +#if 1 /* Set to 0 to see the docktile instead of the real output */ + float backingStretchFactor = 1.; +# if defined(VBOX_WITH_CONFIGURABLE_HIDPI_SCALING) && !defined(IN_VMSVGA3D) + /* Adjust viewport according to current NSView's backing store parameters. */ + if (render_spu.fUnscaledHiDPI) + { + /* Update stretch factor in order to satisfy current NSView's backing store parameters. */ + backingStretchFactor = [self safeGetBackingScaleFactor]; + } + + NSRect regularBounds = [self bounds]; + NSRect backingBounds = [self safeConvertRectToBacking:®ularBounds]; + glViewport(0, 0, backingBounds.size.width, backingBounds.size.height); + + //crDebug("HiDPI: vboxPresentToViewCS: up-scaling is %s (backingStretchFactor=%d).", + // render_spu.fUnscaledHiDPI ? "OFF" : "ON", (int)backingStretchFactor); +# endif + + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glDrawBuffer(GL_BACK); + + /* Clear background to transparent */ + glClear(GL_COLOR_BUFFER_BIT); + + m_fDataVisible = false; + +# ifndef IN_VMSVGA3D + float xStretch; + float yStretch; + CrVrScrCompositorGetStretching(pCompositor, &xStretch, &yStretch); + + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + rc = CrBltEnter(m_pBlitter); + if (RT_SUCCESS(rc)) + { + uint32_t i; + for (i = 0; i < cRegions; ++i) + { + const CR_TEXDATA *pTexData; + PCRTRECT pSrcRect = &paSrcRegions[i]; + PCRTRECT pDstRect = &paDstRegions[i]; + RTRECT DstRect, RestrictDstRect; + RTRECT SrcRect, RestrictSrcRect; + + vboxNSRectToRect(&m_RootRect, &RestrictDstRect); + VBoxRectIntersected(&RestrictDstRect, pDstRect, &DstRect); + + if (VBoxRectIsZero(&DstRect)) + continue; + + VBoxRectTranslate(&DstRect, -RestrictDstRect.xLeft, -RestrictDstRect.yTop); + + vboxNSRectToRectUnstretched(&m_RootRect, &RestrictSrcRect, xStretch / backingStretchFactor, yStretch / backingStretchFactor); + VBoxRectTranslate(&RestrictSrcRect, + -CrVrScrCompositorEntryRectGet(pEntry)->xLeft, + -CrVrScrCompositorEntryRectGet(pEntry)->yTop); + VBoxRectIntersected(&RestrictSrcRect, pSrcRect, &SrcRect); + + if (VBoxRectIsZero(&SrcRect)) + continue; + + pSrcRect = &SrcRect; + pDstRect = &DstRect; + + pTexData = CrVrScrCompositorEntryTexGet(pEntry); + + CrBltBlitTexMural(m_pBlitter, true, CrTdTexGet(pTexData), pSrcRect, pDstRect, 1, fFlags | CRBLT_F_NOALPHA); + + m_fDataVisible = true; + } + CrBltLeave(m_pBlitter); + } + else + { + DEBUG_WARN(("CrBltEnter failed rc %d", rc)); +# ifndef DEBUG_VERBOSE + AssertMsgFailed(("CrBltEnter failed rc %Rrc", rc)); +# endif + } + } + else + { + AssertMsgFailed(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %Rrc\n", rc)); + DEBUG_MSG_1(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d\n", rc)); + } + } +# endif /* !IN_VMSVGA3D */ +#endif + + /* + glFinish(); + */ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + [m_pSharedGLCtx flushBuffer]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)presentComposition:(PCVBOXVR_SCR_COMPOSITOR_ENTRY)pChangedEntry +{ + COCOA_LOG_FLOW(("%s: self=%p pChangedEntry=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pChangedEntry)); + [self vboxTryDraw]; +} + +#ifndef IN_VMSVGA3D +- (void)vboxBlitterSyncWindow +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + CR_BLITTER_WINDOW WinInfo; + NSRect r; + + if (!m_pBlitter) + return; + + RT_ZERO(WinInfo); + + r = [self frame]; + WinInfo.width = r.size.width; + WinInfo.height = r.size.height; + + Assert(WinInfo.width == m_RootRect.size.width); + Assert(WinInfo.height == m_RootRect.size.height); + + /*CrBltMuralSetCurrentInfo(m_pBlitter, NULL);*/ + + CrBltMuralSetCurrentInfo(m_pBlitter, &WinInfo); + CrBltCheckUpdateViewport(m_pBlitter); +} +#endif /* !IN_VMSVGA3D */ + +#ifndef IN_VMSVGA3D +# ifdef VBOX_WITH_CRDUMPER_THUMBNAIL +static int g_cVBoxTgaCtr = 0; +# endif +- (void)vboxPresentToDockTileCS:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor)); + NSRect r = [self frame]; + NSRect rr = NSZeroRect; + NSDockTile *pDT = nil; + float xStretch; + float yStretch; + + if ([m_DockTileView thumbBitmap] != nil) + { + /* + * Only update after at least 200 ms, cause glReadPixels is + * heavy performance wise. + */ + uint64_t msTS = RTTimeSystemMilliTS(); + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + + if (msTS - m_msDockUpdateTS > 200) + { + m_msDockUpdateTS = msTS; +# if 0 + /** @todo check this for optimization */ + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, myTextureName); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_STORAGE_HINT_APPLE, + GL_STORAGE_SHARED_APPLE); + glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, + sizex, sizey, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, myImagePtr); + glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, + 0, 0, 0, 0, 0, image_width, image_height); + glFlush(); + /* Do other work processing here, using a double or triple buffer */ + glGetTexImage(GL_TEXTURE_RECTANGLE_ARB, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, pixels); +# endif + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glDrawBuffer(GL_BACK); + + /* Clear background to transparent */ + glClear(GL_COLOR_BUFFER_BIT); + + rr = [m_DockTileView frame]; + + CrVrScrCompositorGetStretching(pCompositor, &xStretch, &yStretch); + + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + PCRTRECT paSrcRegions; + PCRTRECT paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + rc = CrBltEnter(m_pBlitter); + if (RT_SUCCESS(rc)) + { + uint32_t i; + for (i = 0; i < cRegions; ++i) + { + const CR_TEXDATA *pTexData; + PCRTRECT pSrcRect = &paSrcRegions[i]; + PCRTRECT pDstRect = &paDstRegions[i]; + RTRECT DstRect, RestrictDstRect; + RTRECT SrcRect, RestrictSrcRect; + + vboxNSRectToRect(&m_RootRect, &RestrictDstRect); + VBoxRectIntersected(&RestrictDstRect, pDstRect, &DstRect); + + VBoxRectTranslate(&DstRect, -RestrictDstRect.xLeft, -RestrictDstRect.yTop); + + VBoxRectScale(&DstRect, m_FBOThumbScaleX, m_FBOThumbScaleY); + + if (VBoxRectIsZero(&DstRect)) + continue; + + vboxNSRectToRectUnstretched(&m_RootRect, &RestrictSrcRect, xStretch, yStretch); + VBoxRectTranslate(&RestrictSrcRect, + -CrVrScrCompositorEntryRectGet(pEntry)->xLeft, + -CrVrScrCompositorEntryRectGet(pEntry)->yTop); + VBoxRectIntersected(&RestrictSrcRect, pSrcRect, &SrcRect); + + if (VBoxRectIsZero(&SrcRect)) + continue; + + pSrcRect = &SrcRect; + pDstRect = &DstRect; + + pTexData = CrVrScrCompositorEntryTexGet(pEntry); + + CrBltBlitTexMural(m_pBlitter, true, CrTdTexGet(pTexData), pSrcRect, pDstRect, 1, fFlags); + } + CrBltLeave(m_pBlitter); + } + else + { + DEBUG_WARN(("CrBltEnter failed rc %d", rc)); +# ifndef DEBUG_VERBOSE + AssertMsgFailed(("CrBltEnter failed rc %Rrc", rc)); +# endif + } + } + else + { + DEBUG_MSG_1(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d\n", rc)); + AssertMsgFailed(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %Rrc\n", rc)); + } + } + + glFinish(); + + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + glReadBuffer(GL_BACK); + + /* Here the magic of reading the FBO content in our own buffer + * happens. We have to lock this access, in the case the dock + * is updated currently. */ + [m_DockTileView lock]; + glReadPixels(0, m_RootRect.size.height - rr.size.height, rr.size.width, rr.size.height, + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8, + [[m_DockTileView thumbBitmap] bitmapData]); + [m_DockTileView unlock]; + +# ifdef VBOX_WITH_CRDUMPER_THUMBNAIL + ++g_cVBoxTgaCtr; + crDumpNamedTGAF((GLint)rr.size.width, (GLint)rr.size.height, + [[m_DockTileView thumbBitmap] bitmapData], "/Users/leo/vboxdumps/dump%d.tga", g_cVBoxTgaCtr); +# endif + + pDT = [[NSApplication sharedApplication] dockTile]; + + /* Send a display message to the dock tile in the main thread */ + [[[NSApplication sharedApplication] dockTile] performSelectorOnMainThread:@selector(display) withObject:nil + waitUntilDone:NO]; + } + } +} +#endif /* !IN_VMSVGA3D */ + +- (void)clearVisibleRegions +{ + if (m_paClipRects) + { + RTMemFree(m_paClipRects); + m_paClipRects = NULL; + } + m_cClipRects = 0; +} + +- (GLboolean)vboxNeedsEmptyPresent +{ + if (m_fDataVisible) + { + m_fCleanupNeeded = true; + return GL_TRUE; + } + + return GL_FALSE; +} + +- (void)setVisibleRegions:(GLint)cRects paRects:(const GLint *)paRects +{ + COCOA_LOG_FLOW(("%s: self=%p cRects=%d paRects=%p\n", __PRETTY_FUNCTION__, (void *)self, cRects, (void *)paRects)); + GLint cOldRects = m_cClipRects; + + [self clearVisibleRegions]; + + if (cRects > 0) + { +#ifdef DEBUG_poetzsch + int i = 0; + for (i = 0; i < cRects; ++i) + DEBUG_MSG_1(("OVIW(%p): setVisibleRegions: %d - %d %d %d %d\n", (void *)self, i, paRects[i * 4], paRects[i * 4 + 1], paRects[i * 4 + 2], paRects[i * 4 + 3])); +#endif + + m_paClipRects = (GLint *)RTMemDup(paRects, sizeof(GLint) * 4 * cRects); + m_cClipRects = cRects; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#ifndef IN_VMSVGA3D + +- (NSView *)dockTileScreen +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + NSView *pContentView = [[[NSApplication sharedApplication] dockTile] contentView]; + NSView *pScreenContent = nil; + + /* + * First try the new variant which checks if this window is within the + * screen which is previewed in the dock. + */ + if ([pContentView respondsToSelector:@selector(screenContentWithParentView:)]) + pScreenContent = [pContentView performSelector:@selector(screenContentWithParentView:) withObject:(id)m_pParentView]; + /* + * If it fails, fall back to the old variant (VBox...). + */ + else if ([pContentView respondsToSelector:@selector(screenContent)]) + pScreenContent = [pContentView performSelector:@selector(screenContent)]; + + COCOA_LOG_FLOW(("%s: returns %p (pContentView=%p)\n", __PRETTY_FUNCTION__, (void *)pScreenContent, (void *)pContentView)); + return pScreenContent; +} + +- (void)reshapeDockTile +{ + COCOA_LOG_FLOW(("%s:\n", __PRETTY_FUNCTION__)); + NSRect newFrame = NSZeroRect; + NSView *pView = [self dockTileScreen]; + if (pView != nil) + { + NSRect dockFrame = [pView frame]; + /** @todo This is not correct, we should use framebuffer size here, while + * parent view frame size may differ in case of scrolling. */ + NSRect parentFrame = [m_pParentView frame]; + + m_FBOThumbScaleX = (float)dockFrame.size.width / parentFrame.size.width; + m_FBOThumbScaleY = (float)dockFrame.size.height / parentFrame.size.height; + newFrame = NSMakeRect((int)(m_Pos.x * m_FBOThumbScaleX), + (int)(dockFrame.size.height - (m_Pos.y + m_Size.height - m_yInvRootOffset) * m_FBOThumbScaleY), + (int)(m_Size.width * m_FBOThumbScaleX), + (int)(m_Size.height * m_FBOThumbScaleY)); + /* + NSRect newFrame = NSMakeRect ((int)roundf(m_Pos.x * m_FBOThumbScaleX), (int)roundf(dockFrame.size.height - (m_Pos.y + m_Size.height) * m_FBOThumbScaleY), (int)roundf(m_Size.width * m_FBOThumbScaleX), (int)roundf(m_Size.height * m_FBOThumbScaleY)); + NSRect newFrame = NSMakeRect ((m_Pos.x * m_FBOThumbScaleX), (dockFrame.size.height - (m_Pos.y + m_Size.height) * m_FBOThumbScaleY), (m_Size.width * m_FBOThumbScaleX), (m_Size.height * m_FBOThumbScaleY)); + printf ("%f %f %f %f - %f %f\n", newFrame.origin.x, newFrame.origin.y, newFrame.size.width, newFrame.size.height, m_Size.height, m_FBOThumbScaleY); + */ + [m_DockTileView setFrame: newFrame]; + } + COCOA_LOG_FLOW(("%s: returns - newFrame={%d,%d %d,%d} pView=%d\n", __PRETTY_FUNCTION__, (int)newFrame.origin.x, + (int)newFrame.origin.y, (int)newFrame.size.width, (int)newFrame.size.height, (void *)pView)); +} + +#endif /* !IN_VMSVGA3D */ + +@end /* @implementation OverlayView */ + + +/******************************************************************************** +* +* OpenGL context management +* +********************************************************************************/ +void cocoaGLCtxCreate(NativeNSOpenGLContextRef *ppCtx, GLbitfield fVisParams, NativeNSOpenGLContextRef pSharedCtx) +{ + COCOA_LOG_FLOW(("cocoaGLCtxCreate: ppCtx=%p fVisParams=%#x pSharedCtx=%p\n", (void *)ppCtx, fVisParams, (void *)pSharedCtx)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + NSOpenGLPixelFormat *pFmt = vboxCreatePixelFormat(fVisParams); + if (pFmt) + { + *ppCtx = [[OverlayOpenGLContext alloc] initWithFormat:pFmt shareContext:pSharedCtx]; + Assert(*ppCtx); + + /* Enable multi threaded OpenGL engine */ + /* + CGLContextObj cglCtx = [*ppCtx CGLContextObj]; + CGLError err = CGLEnable(cglCtx, kCGLCEMPEngine); + if (err != kCGLNoError) + printf ("Couldn't enable MT OpenGL engine!\n"); + */ + } + else + { + AssertFailed(); + *ppCtx = NULL; + } + + [pPool release]; + COCOA_LOG_FLOW(("cocoaGLCtxCreate: returns *ppCtx=%p\n", (void *)*ppCtx)); +} + +void cocoaGLCtxDestroy(NativeNSOpenGLContextRef pCtx) +{ + COCOA_LOG_FLOW(("cocoaGLCtxDestroy: pCtx=%p\n", (void *)pCtx)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [pCtx release]; + /*[pCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/ + + [pPool release]; + COCOA_LOG_FLOW(("cocoaGLCtxDestroy: returns\n")); +} + +/******************************************************************************** +* +* View management +* +********************************************************************************/ +static OverlayView *vboxViewCreate(WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams) +{ + COCOA_LOG_FLOW(("vboxViewCreate: pWinInfo=%p pParentView=%p fVisParams=%#x\n", pWinInfo, (void *)pParentView, fVisParams)); + + /* Create our worker view. */ + OverlayView *pView = [[OverlayView alloc] initWithFrame:NSZeroRect + thread:RTThreadSelf() + parentView:pParentView + winInfo:pWinInfo + fVisParams:fVisParams]; + + if (pView) + { + /* We need a real window as container for the view */ + [[OverlayWindow alloc] initWithParentView:pParentView overlayView:pView]; + /* Return the freshly created overlay view */ + COCOA_LOG_FLOW(("vboxViewCreate: returns %p\n", (void *)pView)); + return pView; + } + + COCOA_LOG_FLOW(("vboxViewCreate: returns NULL\n")); + return NULL; +} + +#ifndef IN_VMSVGA3D + +typedef struct CR_RCD_CREATEVIEW +{ + WindowInfo *pWinInfo; + NSView *pParentView; + GLbitfield fVisParams; + /* out */ + OverlayView *pView; +} CR_RCD_CREATEVIEW; + +static DECLCALLBACK(void) vboxRcdCreateView(void *pvCb) +{ + CR_RCD_CREATEVIEW *pCreateView = (CR_RCD_CREATEVIEW *)pvCb; + pCreateView->pView = vboxViewCreate(pCreateView->pWinInfo, pCreateView->pParentView, pCreateView->fVisParams); + COCOA_LOG_FLOW(("vboxRcdCreateView: returns pView=%p\n", (void *)pCreateView->pView)); +} + +#endif /* !IN_VMSVGA3D */ + +void cocoaViewCreate(NativeNSViewRef *ppView, WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams) +{ + COCOA_LOG_FLOW(("cocoaViewCreate: ppView=%p pWinInfo=%p pParentView=%p fVisParams=%#x\n", + (void *)ppView, (void *)pWinInfo, (void *)pParentView, fVisParams)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + /* make sure all tasks are run, to preserve the order */ + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner runTasksSyncIfPossible]; + +#ifndef IN_VMSVGA3D + renderspuWinRetain(pWinInfo); + + if (renderspuCalloutAvailable()) + { + CR_RCD_CREATEVIEW CreateView; + CreateView.pWinInfo = pWinInfo; + CreateView.pParentView = pParentView; + CreateView.fVisParams = fVisParams; + CreateView.pView = NULL; + renderspuCalloutClient(vboxRcdCreateView, &CreateView); + *ppView = CreateView.pView; + } + else +#endif + { + DEBUG_MSG_NOT_VMSVGA3D(("no callout available on createWindow\n")); +#if 0 + dispatch_sync(dispatch_get_main_queue(), ^{ +#endif + *ppView = vboxViewCreate(pWinInfo, pParentView, fVisParams); +#if 0 + }); +#endif + } + +#ifndef IN_VMSVGA3D + if (!*ppView) + renderspuWinRelease(pWinInfo); +#endif + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewCreate: returns *ppView=%p\n", (void *)*ppView)); +} + +#ifndef IN_VMSVGA3D +void cocoaViewReparent(NativeNSViewRef pView, NativeNSViewRef pParentView) +{ + COCOA_LOG_FLOW(("cocoaViewReparent: pView=%p pParentView=%p\n", (void *)pView, (void *)pParentView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + OverlayView *pOView = (OverlayView *)pView; + if (pOView) + [pOView vboxReparent:pParentView]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewReparent: returns\n")); +} +#endif /* !IN_VMSVGA3D */ + +void cocoaViewDestroy(NativeNSViewRef pView) +{ + COCOA_LOG_FLOW(("cocoaViewDestroy: pView=%p\n", (void *)pView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner addObj:pView selector:@selector(vboxDestroy) arg:nil]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewDestroy: returns\n")); +} + +#ifndef IN_VMSVGA3D +void cocoaViewShow(NativeNSViewRef pView, GLboolean fShowIt) +{ + COCOA_LOG_FLOW(("cocoaViewShow: pView=%p fShowIt=%d\n", (void *)pView, fShowIt)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [(OverlayView *)pView vboxSetVisible:fShowIt]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewShow: returns\n")); +} +#endif /* IN_VMSVGA3D */ + +void cocoaViewDisplay(NativeNSViewRef pView) +{ + COCOA_LOG_FLOW(("cocoaViewDisplay: pView=%p\n", (void *)pView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + +#ifndef IN_VMSVGA3D + DEBUG_WARN(("cocoaViewDisplay should never happen!\n")); + DEBUG_MSG_1(("cocoaViewDisplay %p\n", (void *)pView)); +#endif + [(OverlayView *)pView swapFBO]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewDisplay: returns\n")); +} + +void cocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y) +{ + COCOA_LOG_FLOW(("cocoaViewSetPosition: pView=%p pParentView=%p x=%d y=%d\n", (void *)pView, (void *)pParentView, x, y)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [(OverlayView *)pView vboxSetPos:NSMakePoint(x, y)]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewSetPosition: returns\n")); +} + +void cocoaViewSetSize(NativeNSViewRef pView, int cx, int cy) +{ + COCOA_LOG_FLOW(("cocoaViewSetSize: pView=%p cx=%d cy=%d\n", (void *)pView, cx, cy)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + OverlayView *pOverlayView = (OverlayView *)pView; + + [pOverlayView vboxSetSize:NSMakeSize(cx, cy)]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewSetSize: returns\n")); +} + +#ifndef IN_VMSVGA3D + +typedef struct CR_RCD_GETGEOMETRY +{ + OverlayView *pView; + NSRect rect; +} CR_RCD_GETGEOMETRY; + +static DECLCALLBACK(void) vboxRcdGetGeomerty(void *pvUser) +{ + CR_RCD_GETGEOMETRY *pGetGeometry = (CR_RCD_GETGEOMETRY *)pvUser; + pGetGeometry->rect = [[pGetGeometry->pView window] frame]; + COCOA_LOG_FLOW(("vboxRcdGetGeomerty: (x,y)=(%d,%d) (cx,cy)=(%d,%d)\n", pGetGeometry->rect.origin.x, pGetGeometry->rect.origin.y, + pGetGeometry->rect.size.width, pGetGeometry->rect.size.height)); +} + +void cocoaViewGetGeometry(NativeNSViewRef pView, int *px, int *py, int *pcx, int *pcy) +{ + COCOA_LOG_FLOW(("cocoaViewGetGeometry: pView=%p px=%p py=%p pcx=%p pcy=%p\n", + (void *)pView, (void *)px, (void *)py, (void *)pcx, (void *)pcy)); + NSAutoreleasePool *pPool; + pPool = [[NSAutoreleasePool alloc] init]; + + /* make sure all tasks are run, to preserve the order */ + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner runTasksSyncIfPossible]; + + NSRect frame; +#ifndef IN_VMSVGA3D + if (renderspuCalloutAvailable()) + { + CR_RCD_GETGEOMETRY GetGeometry; + GetGeometry.pView = (OverlayView *)pView; + renderspuCalloutClient(vboxRcdGetGeomerty, &GetGeometry); + frame = GetGeometry.rect; + } + else +#endif + { + DEBUG_MSG_NOT_VMSVGA3D(("no callout available on getGeometry\n")); + frame = [[pView window] frame]; + } + + *px = frame.origin.x; + *py = frame.origin.y; + *pcx = frame.size.width; + *pcy = frame.size.height; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewGetGeometry: returns *px=%d, *py=%d, *pcx=%d, *pcy=%d\n", *px, *py, *pcx, *pcy)); +} + +void cocoaViewPresentComposition(NativeNSViewRef pView, PCVBOXVR_SCR_COMPOSITOR_ENTRY pChangedEntry) +{ + COCOA_LOG_FLOW(("cocoaViewPresentComposition: pView=%p pChangedEntry=%p\n", (void *)pView, (void *)pChangedEntry)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + NSOpenGLContext *pCtx; + +# ifdef IN_VMSVGA3D + Assert([(OverlayView *)pView glCtx]); + +# else + /* The view may not necesserily have a GL context set. */ + pCtx = [(OverlayView *)pView glCtx]; + if (!pCtx) + { + ContextInfo *pCtxInfo = renderspuDefaultSharedContextAcquire(); + if (!pCtxInfo) + { + DEBUG_WARN(("renderspuDefaultSharedContextAcquire returned NULL")); + + [pPool release]; + DEBUG_FUNC_LEAVE(); + return; + } + + pCtx = pCtxInfo->context; + + [(OverlayView *)pView setGLCtx:pCtx]; + } +# endif + + [(OverlayView *)pView presentComposition:pChangedEntry]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewPresentComposition: returns\n")); +} + +#endif /* !IN_VMSVGA3D */ + +void cocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ + COCOA_LOG_FLOW(("cocoaViewMakeCurrentContext: pView=%p pCtx=%p\n", (void *)pView, (void *)pCtx)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + if (pView) + { + [(OverlayView *)pView setGLCtx:pCtx]; + [(OverlayView *)pView makeCurrentFBO]; + } + else + { +#ifdef IN_VMSVGA3D + /* Always flush before flush. glXMakeCurrent and wglMakeCurrent does this + implicitly, seemingly NSOpenGLContext::makeCurrentContext doesn't. */ + if ([NSOpenGLContext currentContext] != nil) + { + DEBUG_CLEAR_GL_ERRORS(); + glFlush(); + DEBUG_CHECK_GL_ERROR("glFlush"); + } +#endif + [NSOpenGLContext clearCurrentContext]; + } + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewMakeCurrentContext: returns\n")); +} + +#ifndef IN_VMSVGA3D + +GLboolean cocoaViewNeedsEmptyPresent(NativeNSViewRef pView) +{ + COCOA_LOG_FLOW(("cocoaViewNeedsEmptyPresent: pView=%p\n", (void *)pView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + GLboolean fNeedsPresent = [(OverlayView *)pView vboxNeedsEmptyPresent]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewNeedsEmptyPresent: returns %d\n", fNeedsPresent)); + return fNeedsPresent; +} + +void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, const GLint *paRects) +{ + COCOA_LOG_FLOW(("cocoaViewSetVisibleRegion: pView=%p cRects=%d paRects=%p)\n", (void *)pView, cRects, (void const *)paRects)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [(OverlayView *)pView setVisibleRegions:cRects paRects:paRects]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewSetVisibleRegion: returns\n")); +} + +#endif /* IN_VMSVGA3D */ + +#ifdef IN_VMSVGA3D +/* + * VMSVGA3D interface. + */ + +VMSVGA3DCOCOA_DECL(bool) vmsvga3dCocoaCreateViewAndContext(NativeNSViewRef *ppView, NativeNSOpenGLContextRef *ppCtx, + NativeNSViewRef pParentView, uint32_t cx, uint32_t cy, + NativeNSOpenGLContextRef pSharedCtx, bool fOtherProfile) +{ + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + GLbitfield fVisParams = CR_ALPHA_BIT | CR_DEPTH_BIT | CR_DOUBLE_BIT | (fOtherProfile ? VMSVGA3D_NON_DEFAULT_PROFILE_BIT : 0); + bool fRc = false; + + cocoaGLCtxCreate(ppCtx, fVisParams, pSharedCtx); + if (*ppCtx) + { + cocoaViewCreate(ppView, NULL, pParentView, fVisParams); + if (*ppView) + { + cocoaViewSetSize(*ppView, cx, cy); + [(OverlayView *)*ppView setGLCtx: *ppCtx]; + fRc = true; + } + else + [*ppCtx release]; + } + + [pPool release]; + return fRc; +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaDestroyViewAndContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ + cocoaGLCtxDestroy(pCtx); + cocoaViewDestroy(pView); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y) +{ + cocoaViewSetPosition(pView, pParentView, x, y); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaViewSetSize(NativeNSViewRef pView, int w, int h) +{ + cocoaViewSetSize(pView, w, h); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ + Assert(!pView || [(OverlayView *)pView glCtx] == pCtx || [(OverlayView *)pView glCtx] == nil); + cocoaViewMakeCurrentContext(pView, pCtx); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaSwapBuffers(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ +# if 1 + Assert([(OverlayView *)pView glCtx] == pCtx); + Assert([pCtx view] == pView); + cocoaViewDisplay(pView); +# else + DEBUG_FUNC_ENTER(); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + Assert([(OverlayView *)pView glCtx] == pCtx); + Assert([pCtx view] == pView); + + [pCtx flushBuffer]; + + [pPool release]; + DEBUG_FUNC_LEAVE(); +# endif +} + +#endif /* IN_VMSVGA3D */ diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c new file mode 100644 index 00000000..624a051f --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_config.c @@ -0,0 +1,392 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "renderspu.h" + +#include "cr_string.h" +#include "cr_mem.h" +#include "cr_error.h" +#include "cr_environment.h" +#include "cr_url.h" + + +static void set_window_geometry( RenderSPU *render_spu, const char *response ) +{ + int x, y, w, h; + CRASSERT(response[0] == '['); + sscanf( response, "[ %d, %d, %d, %d ]", &x, &y, &w, &h ); + render_spu->defaultX = (int) x; + render_spu->defaultY = (int) y; + render_spu->defaultWidth = (unsigned int) w; + render_spu->defaultHeight = (unsigned int) h; +} + +static void set_default_visual( RenderSPU *render_spu, const char *response ) +{ + if (crStrlen(response) > 0) { + if (crStrstr(response, "rgb")) + render_spu->default_visual |= CR_RGB_BIT; + if (crStrstr(response, "alpha")) + render_spu->default_visual |= CR_ALPHA_BIT; + if (crStrstr(response, "z") || crStrstr(response, "depth")) + render_spu->default_visual |= CR_DEPTH_BIT; + if (crStrstr(response, "stencil")) + render_spu->default_visual |= CR_STENCIL_BIT; + if (crStrstr(response, "accum")) + render_spu->default_visual |= CR_ACCUM_BIT; + if (crStrstr(response, "stereo")) + render_spu->default_visual |= CR_STEREO_BIT; + if (crStrstr(response, "multisample")) + render_spu->default_visual |= CR_MULTISAMPLE_BIT; + if (crStrstr(response, "double")) + render_spu->default_visual |= CR_DOUBLE_BIT; + if (crStrstr(response, "pbuffer")) + render_spu->default_visual |= CR_PBUFFER_BIT; + } +} + +static void set_display_string( RenderSPU *render_spu, const char *response ) +{ + if (!crStrcmp(response, "DEFAULT")) { + const char *display = crGetenv("DISPLAY"); + if (display) + crStrncpy(render_spu->display_string, + display, + sizeof(render_spu->display_string)); + else + crStrcpy(render_spu->display_string, ""); /* empty string */ + } + else { + crStrncpy(render_spu->display_string, + response, + sizeof(render_spu->display_string)); + } +} + +static void set_fullscreen( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->fullscreen) ); +} + +static void set_on_top( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->ontop) ); +} + +static void set_system_gl_path( RenderSPU *render_spu, const char *response ) +{ + if (crStrlen(response) > 0) + crSetenv( "CR_SYSTEM_GL_PATH", response ); +} + +static void set_title( RenderSPU *render_spu, const char *response ) +{ + crFree( render_spu->window_title ); + render_spu->window_title = crStrdup( response ); +} + +#if defined(GLX) +static void set_try_direct( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->try_direct) ); +} + +static void set_force_direct( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->force_direct) ); +} +#endif /* GLX */ + +static void render_to_app_window( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->render_to_app_window) ); +} + +static void render_to_crut_window( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->render_to_crut_window) ); +} + +static void resizable( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->resizable) ); +} + +static void set_borderless( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->borderless) ); +} + +static void set_cursor( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->drawCursor) ); +} + +static void gather_url( RenderSPU *render_spu, const char *response ) +{ + char protocol[4096], hostname[4096]; + unsigned short port; + + if (!crParseURL(response, protocol, hostname, &port, 0)) + { + crError( "Malformed URL: \"%s\"", response ); + } + + render_spu->gather_port = port; +} + +static void gather_userbuf( RenderSPU *render_spu, const char *response ) +{ + sscanf( response, "%d", &(render_spu->gather_userbuf_size) ); +} + +static void set_lut8( RenderSPU *render_spu, const char *response ) +{ + int a; + char **lut; + + if (!response[0]) return; + + lut = crStrSplit(response, ","); + if (!lut) return; + + for (a=0; a<256; a++) + { + render_spu->lut8[0][a] = crStrToInt(lut[a]); + render_spu->lut8[1][a] = crStrToInt(lut[256+a]); + render_spu->lut8[2][a] = crStrToInt(lut[512+a]); + } + + crFreeStrings(lut); + + render_spu->use_lut8 = 1; +} + +static void set_master_url ( RenderSPU *render_spu, char *response ) +{ + if (response[0]) + render_spu->swap_master_url = crStrdup( response ); + else + render_spu->swap_master_url = NULL; +} + +static void set_is_master ( RenderSPU *render_spu, char *response ) +{ + render_spu->is_swap_master = crStrToInt( response ); +} + +static void set_num_clients ( RenderSPU *render_spu, char *response ) +{ + render_spu->num_swap_clients = crStrToInt( response ); +} + +static void set_use_osmesa ( RenderSPU *render_spu, char *response ) +{ + int val = crStrToInt( response ); +#ifdef USE_OSMESA + render_spu->use_osmesa = val; +#else + if (val != 0) + crError( "renderspu with Conf(use_osmesa, 1) but not compiled with -DUSE_OSMESA"); +#endif +} + +static void set_nv_swap_group( RenderSPU *render_spu, char *response ) +{ + render_spu->nvSwapGroup = crStrToInt( response ); + if (render_spu->nvSwapGroup < 0) + render_spu->nvSwapGroup = 0; +} + +static void set_ignore_papi( RenderSPU *render_spu, char *response ) +{ + render_spu->ignore_papi = crStrToInt( response ); +} + +static void set_ignore_window_moves( RenderSPU *render_spu, char *response ) +{ + render_spu->ignore_window_moves = crStrToInt( response ); +} + +static void set_pbuffer_size( RenderSPU *render_spu, const char *response ) +{ + CRASSERT(response[0] == '['); + sscanf( response, "[ %d, %d ]", + &render_spu->pbufferWidth, &render_spu->pbufferHeight); +} + +static void set_use_glxchoosevisual( RenderSPU *render_spu, char *response ) +{ + render_spu->use_glxchoosevisual = crStrToInt( response ); +} + +static void set_draw_bbox( RenderSPU *render_spu, char *response ) +{ + render_spu->draw_bbox = crStrToInt( response ); +} + + + +/* option, type, nr, default, min, max, title, callback + */ +SPUOptions renderSPUOptions[] = { + { "title", CR_STRING, 1, "Chromium Render SPU", NULL, NULL, + "Window Title", (SPUOptionCB)set_title }, + + { "window_geometry", CR_INT, 4, "[0, 0, 256, 256]", "[0, 0, 1, 1]", NULL, + "Default Window Geometry (x,y,w,h)", (SPUOptionCB)set_window_geometry }, + + { "fullscreen", CR_BOOL, 1, "0", NULL, NULL, + "Full-screen Window", (SPUOptionCB)set_fullscreen }, + + { "resizable", CR_BOOL, 1, "0", NULL, NULL, + "Resizable Window", (SPUOptionCB)resizable }, + + { "on_top", CR_BOOL, 1, "0", NULL, NULL, + "Display on Top", (SPUOptionCB)set_on_top }, + + { "borderless", CR_BOOL, 1, "0", NULL, NULL, + "Borderless Window", (SPUOptionCB) set_borderless }, + + { "default_visual", CR_STRING, 1, "rgb, double, depth", NULL, NULL, + "Default GL Visual", (SPUOptionCB) set_default_visual }, + +#if defined(GLX) + { "try_direct", CR_BOOL, 1, "1", NULL, NULL, + "Try Direct Rendering", (SPUOptionCB)set_try_direct }, + + { "force_direct", CR_BOOL, 1, "0", NULL, NULL, + "Force Direct Rendering", (SPUOptionCB)set_force_direct }, +#endif + + { "render_to_app_window", CR_BOOL, 1, "0", NULL, NULL, + "Render to Application window", (SPUOptionCB)render_to_app_window }, + + { "render_to_crut_window", CR_BOOL, 1, "0", NULL, NULL, + "Render to CRUT window", (SPUOptionCB)render_to_crut_window }, + + { "show_cursor", CR_BOOL, 1, "0", NULL, NULL, + "Show Software Cursor", (SPUOptionCB) set_cursor }, + + { "system_gl_path", CR_STRING, 1, "", NULL, NULL, + "System GL Path", (SPUOptionCB)set_system_gl_path }, + + { "display_string", CR_STRING, 1, "DEFAULT", NULL, NULL, + "X Display String", (SPUOptionCB)set_display_string }, + + { "gather_url", CR_STRING, 1, "", NULL, NULL, + "Gatherer URL", (SPUOptionCB)gather_url}, + + { "gather_userbuf_size", CR_INT, 1, "0", NULL, NULL, + "Size of Buffer to Allocate for Gathering", (SPUOptionCB)gather_userbuf}, + + { "lut8", CR_STRING, 1, "", NULL, NULL, + "8 bit RGB LUT", (SPUOptionCB)set_lut8}, + + { "swap_master_url", CR_STRING, 1, "", NULL, NULL, + "The URL to the master swapper", (SPUOptionCB)set_master_url }, + + { "is_swap_master", CR_BOOL, 1, "0", NULL, NULL, + "Is this the swap master", (SPUOptionCB)set_is_master }, + + { "num_swap_clients", CR_INT, 1, "1", NULL, NULL, + "How many swaps to wait on", (SPUOptionCB)set_num_clients }, + + { "use_osmesa", CR_BOOL, 1, "0", NULL, NULL, + "Use offscreen rendering with Mesa", (SPUOptionCB)set_use_osmesa }, + + { "nv_swap_group", CR_INT, 1, "0", NULL, NULL, + "NVIDIA Swap Group Number", (SPUOptionCB) set_nv_swap_group }, + + { "ignore_papi", CR_BOOL, 1, "0", NULL, NULL, + "Ignore Barrier and Semaphore calls", (SPUOptionCB) set_ignore_papi }, + + { "ignore_window_moves", CR_BOOL, 1, "0", NULL, NULL, + "Ignore crWindowPosition calls", (SPUOptionCB) set_ignore_window_moves }, + + { "pbuffer_size", CR_INT, 2, "[0, 0]", "[0, 0]", NULL, + "Maximum PBuffer Size", (SPUOptionCB) set_pbuffer_size }, + + { "use_glxchoosevisual", CR_BOOL, 1, "1", NULL, NULL, + "Use glXChooseVisual", (SPUOptionCB) set_use_glxchoosevisual }, + + { "draw_bbox", CR_BOOL, 1, "0", NULL, NULL, + "Draw Bounding Boxes", (SPUOptionCB) set_draw_bbox }, + { NULL, CR_BOOL, 0, NULL, NULL, NULL, NULL, NULL }, +}; + + +void renderspuSetVBoxConfiguration( RenderSPU *render_spu ) +{ + int a; + + for (a=0; a<256; a++) + { + render_spu->lut8[0][a] = + render_spu->lut8[1][a] = + render_spu->lut8[2][a] = a; + } + render_spu->use_lut8 = 0; + + set_title(render_spu, "Chromium Render SPU"); + set_window_geometry(render_spu, "[0, 0, 0, 0]"); + set_fullscreen(render_spu, "0"); + resizable(render_spu, "0"); + set_on_top(render_spu, "1"); + set_borderless(render_spu, "1"); + set_default_visual(render_spu, "rgb, double, depth"); +#if defined(GLX) + set_try_direct(render_spu, "1"); + set_force_direct(render_spu, "0"); +#endif + render_to_app_window(render_spu, "0"); + render_to_crut_window(render_spu, "0"); + set_cursor(render_spu, "0"); + set_system_gl_path(render_spu, ""); + set_display_string(render_spu, "DEFAULT"); + gather_url(render_spu, ""); + gather_userbuf(render_spu, "0"); + set_lut8(render_spu, ""); + set_master_url(render_spu, ""); + set_is_master(render_spu, "0"); + set_num_clients(render_spu, "1"); + set_use_osmesa(render_spu, "0"); + set_nv_swap_group(render_spu, "0"); + set_ignore_papi(render_spu, "0"); + set_ignore_window_moves(render_spu, "0"); + set_pbuffer_size(render_spu, "[0, 0]"); + set_use_glxchoosevisual(render_spu, "1"); + set_draw_bbox(render_spu, "0"); + + render_spu->swap_mtu = 1024 * 500; + + /* Some initialization that doesn't really have anything to do + * with configuration but which was done here before: + */ + render_spu->use_L2 = 0; + render_spu->cursorX = 0; + render_spu->cursorY = 0; +#if defined(GLX) + render_spu->sync = 0; +#endif + + /* Config of "render force present main thread" (currently implemented by glx and wgl). */ + { + const char *forcePresent = crGetenv("CR_RENDER_FORCE_PRESENT_MAIN_THREAD"); + if (forcePresent) + render_spu->force_present_main_thread = crStrToInt(forcePresent) ? 1 : 0; + else + { +#if defined(GLX) + /* Customer needed this for avoiding system 3D driver bugs. */ + render_spu->force_present_main_thread = 1; +#else + render_spu->force_present_main_thread = 0; +#endif + } + } +} + diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c new file mode 100644 index 00000000..8a1a61ff --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_glx.c @@ -0,0 +1,2042 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ +#if 00 /*TEMPORARY*/ +#include <unistd.h> +#include "cr_rand.h" +#endif + +#include <GL/glx.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xmu/StdCmap.h> +#include <X11/Xatom.h> +#include <X11/extensions/shape.h> +#include <sys/time.h> +#include <stdio.h> + +#include "cr_environment.h" +#include "cr_error.h" +#include "cr_string.h" +#include "cr_mem.h" +#include "cr_process.h" +#include "renderspu.h" + + +/* + * Stuff from MwmUtils.h + */ +typedef struct +{ + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; +} PropMotifWmHints; + +#define PROP_MOTIF_WM_HINTS_ELEMENTS 5 +#define MWM_HINTS_DECORATIONS (1L << 1) + + +#define WINDOW_NAME window->title + +static Bool WindowExistsFlag; + +static int +WindowExistsErrorHandler( Display *dpy, XErrorEvent *xerr ) +{ + if (xerr->error_code == BadWindow) + { + WindowExistsFlag = GL_FALSE; + } + return 0; +} + +static GLboolean +WindowExists( Display *dpy, Window w ) +{ + XWindowAttributes xwa; + int (*oldXErrorHandler)(Display *, XErrorEvent *); + + WindowExistsFlag = GL_TRUE; + oldXErrorHandler = XSetErrorHandler(WindowExistsErrorHandler); + XGetWindowAttributes(dpy, w, &xwa); /* dummy request */ + XSetErrorHandler(oldXErrorHandler); + return WindowExistsFlag; +} + +static Colormap +GetLUTColormap( Display *dpy, XVisualInfo *vi ) +{ + int a; + XColor col; + Colormap cmap; + +#if defined(__cplusplus) || defined(c_plusplus) + int localclass = vi->c_class; /* C++ */ +#else + int localclass = vi->class; /* C */ +#endif + + if ( localclass != DirectColor ) + { + crError( "No support for non-DirectColor visuals with LUTs" ); + } + + cmap = XCreateColormap( dpy, RootWindow(dpy, vi->screen), + vi->visual, AllocAll ); + + for (a=0; a<256; a++) + { + col.red = render_spu.lut8[0][a]<<8; + col.green = col.blue = 0; + col.pixel = a<<16; + col.flags = DoRed; + XStoreColor(dpy, cmap, &col); + } + + for (a=0; a<256; a++) + { + col.green = render_spu.lut8[1][a]<<8; + col.red = col.blue = 0; + col.pixel = a<<8; + col.flags = DoGreen; + XStoreColor(dpy, cmap, &col); + } + + for (a=0; a<256; a++) + { + col.blue = render_spu.lut8[2][a]<<8; + col.red = col.green= 0; + col.pixel = a; + col.flags = DoBlue; + XStoreColor(dpy, cmap, &col); + } + + return cmap; +} + +static Colormap +GetShareableColormap( Display *dpy, XVisualInfo *vi ) +{ + Status status; + XStandardColormap *standardCmaps; + Colormap cmap; + int i, numCmaps; + +#if defined(__cplusplus) || defined(c_plusplus) + int localclass = vi->c_class; /* C++ */ +#else + int localclass = vi->class; /* C */ +#endif + + if ( localclass != TrueColor ) + { + crError( "No support for non-TrueColor visuals." ); + } + + status = XmuLookupStandardColormap( dpy, vi->screen, vi->visualid, + vi->depth, XA_RGB_DEFAULT_MAP, + False, True ); + + if ( status == 1 ) + { + status = XGetRGBColormaps( dpy, RootWindow( dpy, vi->screen), + &standardCmaps, &numCmaps, + XA_RGB_DEFAULT_MAP ); + if ( status == 1 ) + { + for (i = 0 ; i < numCmaps ; i++) + { + if (standardCmaps[i].visualid == vi->visualid) + { + cmap = standardCmaps[i].colormap; + XFree( standardCmaps); + return cmap; + } + } + } + } + + cmap = XCreateColormap( dpy, RootWindow(dpy, vi->screen), + vi->visual, AllocNone ); + return cmap; +} + + +static int +WaitForMapNotify( Display *display, XEvent *event, char *arg ) +{ + (void)display; + return ( event->type == MapNotify && event->xmap.window == (Window)arg ); +} + + +/** + * Return the X Visual ID of the given window + */ +static int +GetWindowVisualID( Display *dpy, Window w ) +{ + XWindowAttributes attr; + int k = XGetWindowAttributes(dpy, w, &attr); + if (!k) + return -1; + return attr.visual->visualid; +} + + +/** + * Wrapper for glXGetConfig(). + */ +static int +Attrib( const VisualInfo *visual, int attrib ) +{ + int value = 0; + render_spu.ws.glXGetConfig( visual->dpy, visual->visual, attrib, &value ); + return value; +} + + + +/** + * Find a visual with the specified attributes. If we fail, turn off an + * attribute (like multisample or stereo) and try again. + */ +static XVisualInfo * +chooseVisualRetry( Display *dpy, int screen, GLbitfield visAttribs ) +{ + while (1) { + XVisualInfo *vis = crChooseVisual(&render_spu.ws, dpy, screen, + (GLboolean) render_spu.use_lut8, + visAttribs); + if (vis) + return vis; + + if (visAttribs & CR_MULTISAMPLE_BIT) + visAttribs &= ~CR_MULTISAMPLE_BIT; + else if (visAttribs & CR_OVERLAY_BIT) + visAttribs &= ~CR_OVERLAY_BIT; + else if (visAttribs & CR_STEREO_BIT) + visAttribs &= ~CR_STEREO_BIT; + else if (visAttribs & CR_ACCUM_BIT) + visAttribs &= ~CR_ACCUM_BIT; + else if (visAttribs & CR_ALPHA_BIT) + visAttribs &= ~CR_ALPHA_BIT; + else + return NULL; + } +} + + +/** + * Get an FBconfig for the specified attributes + */ +#ifdef GLX_VERSION_1_3 +static GLXFBConfig +chooseFBConfig( Display *dpy, int screen, GLbitfield visAttribs ) +{ + GLXFBConfig *fbconfig; + int attribs[1000], attrCount = 0, numConfigs; + int major, minor; + + CRASSERT(visAttribs & CR_PBUFFER_BIT); + + /* Make sure pbuffers are supported */ + render_spu.ws.glXQueryVersion(dpy, &major, &minor); + if (major * 100 + minor < 103) { + crWarning("Render SPU: GLX %d.%d doesn't support pbuffers", major, minor); + return 0; + } + + attribs[attrCount++] = GLX_DRAWABLE_TYPE; + attribs[attrCount++] = GLX_PBUFFER_BIT; + + if (visAttribs & CR_RGB_BIT) { + attribs[attrCount++] = GLX_RENDER_TYPE; + attribs[attrCount++] = GLX_RGBA_BIT; + attribs[attrCount++] = GLX_RED_SIZE; + attribs[attrCount++] = 1; + attribs[attrCount++] = GLX_GREEN_SIZE; + attribs[attrCount++] = 1; + attribs[attrCount++] = GLX_BLUE_SIZE; + attribs[attrCount++] = 1; + if (visAttribs & CR_ALPHA_BIT) { + attribs[attrCount++] = GLX_ALPHA_SIZE; + attribs[attrCount++] = 1; + } + } + + if (visAttribs & CR_DEPTH_BIT) { + attribs[attrCount++] = GLX_DEPTH_SIZE; + attribs[attrCount++] = 1; + } + + if (visAttribs & CR_DOUBLE_BIT) { + attribs[attrCount++] = GLX_DOUBLEBUFFER; + attribs[attrCount++] = True; + } + else { + /* don't care */ + } + + if (visAttribs & CR_STENCIL_BIT) { + attribs[attrCount++] = GLX_STENCIL_SIZE; + attribs[attrCount++] = 1; + } + + if (visAttribs & CR_ACCUM_BIT) { + attribs[attrCount++] = GLX_ACCUM_RED_SIZE; + attribs[attrCount++] = 1; + attribs[attrCount++] = GLX_ACCUM_GREEN_SIZE; + attribs[attrCount++] = 1; + attribs[attrCount++] = GLX_ACCUM_BLUE_SIZE; + attribs[attrCount++] = 1; + if (visAttribs & CR_ALPHA_BIT) { + attribs[attrCount++] = GLX_ACCUM_ALPHA_SIZE; + attribs[attrCount++] = 1; + } + } + + if (visAttribs & CR_MULTISAMPLE_BIT) { + attribs[attrCount++] = GLX_SAMPLE_BUFFERS_SGIS; + attribs[attrCount++] = 1; + attribs[attrCount++] = GLX_SAMPLES_SGIS; + attribs[attrCount++] = 4; + } + + if (visAttribs & CR_STEREO_BIT) { + attribs[attrCount++] = GLX_STEREO; + attribs[attrCount++] = 1; + } + + /* terminate */ + attribs[attrCount++] = 0; + + fbconfig = render_spu.ws.glXChooseFBConfig(dpy, screen, attribs, &numConfigs); + if (!fbconfig || numConfigs == 0) { + /* no matches! */ + return 0; + } + if (numConfigs == 1) { + /* one match */ + return fbconfig[0]; + } + else { + /* found several matches - try to find best one */ + int i; + + crDebug("Render SPU: glXChooseFBConfig found %d matches for visBits 0x%x", + numConfigs, visAttribs); + /* Skip/omit configs that have unneeded Z buffer or unneeded double + * buffering. Possible add other tests in the future. + */ + for (i = 0; i < numConfigs; i++) { + int zBits, db; + render_spu.ws.glXGetFBConfigAttrib(dpy, fbconfig[i], + GLX_DEPTH_SIZE, &zBits); + if ((visAttribs & CR_DEPTH_BIT) == 0 && zBits > 0) { + /* omit fbconfig with unneeded Z */ + continue; + } + render_spu.ws.glXGetFBConfigAttrib(dpy, fbconfig[i], + GLX_DOUBLEBUFFER, &db); + if ((visAttribs & CR_DOUBLE_BIT) == 0 && db) { + /* omit fbconfig with unneeded DB */ + continue; + } + + /* if we get here, use this config */ + return fbconfig[i]; + } + + /* if we get here, we didn't find a better fbconfig */ + return fbconfig[0]; + } +} +#endif /* GLX_VERSION_1_3 */ + +static const char * renderspuGetDisplayName() +{ + const char *dpyName; + + if (render_spu.display_string[0]) + dpyName = render_spu.display_string; + else + { + crWarning("Render SPU: no display.."); + dpyName = NULL; + } + return dpyName; +} + +static int renderspuWinCmdWinCreate(WindowInfo *pWindow) +{ + return VERR_NOT_IMPLEMENTED; +} + +static int renderspuWinCmdWinDestroy(WindowInfo *pWindow) +{ + return VERR_NOT_IMPLEMENTED; +} + +static int renderspuWinCmdInit() +{ + const char * dpyName; + int rc = VERR_GENERAL_FAILURE; + + if (!crHashtableAllocRegisterKey(render_spu.windowTable, CR_RENDER_WINCMD_ID)) + { + crError("CR_RENDER_WINCMD_ID %d is occupied already", CR_RENDER_WINCMD_ID); + return VERR_INVALID_STATE; + } + + render_spu.pWinToInfoTable = crAllocHashtable(); + if (render_spu.pWinToInfoTable) + { + dpyName = renderspuGetDisplayName(); + if (dpyName) + { + GLboolean bRc = renderspuInitVisual(&render_spu.WinCmdVisual, dpyName, render_spu.default_visual); + if (bRc) + { + bRc = renderspuWinInitWithVisual(&render_spu.WinCmdWindow, &render_spu.WinCmdVisual, GL_FALSE, CR_RENDER_WINCMD_ID); + if (bRc) + { + XSelectInput(render_spu.WinCmdVisual.dpy, render_spu.WinCmdWindow.window, StructureNotifyMask); + render_spu.WinCmdAtom = XInternAtom(render_spu.WinCmdVisual.dpy, "VBoxWinCmd", False); + CRASSERT(render_spu.WinCmdAtom != None); + return VINF_SUCCESS; + } + else + { + crError("renderspuWinInitWithVisual failed"); + } + /* there is no visual destroy impl currently + * @todo: implement */ + } + else + { + crError("renderspuInitVisual failed"); + } + } + else + { + crError("Render SPU: no display, aborting"); + } + crFreeHashtable(render_spu.pWinToInfoTable, NULL); + render_spu.pWinToInfoTable = NULL; + } + else + { + crError("crAllocHashtable failed"); + } + return rc; +} + +static void renderspuWinCmdTerm() +{ + /* the window is not in the table, this will just ensure the key is freed */ + crHashtableDelete(render_spu.windowTable, CR_RENDER_WINCMD_ID, NULL); + renderspuWinCleanup(&render_spu.WinCmdWindow); + crFreeHashtable(render_spu.pWinToInfoTable, NULL); + /* we do not have visual destroy functionality + * @todo implement */ +} + + +static bool renderspuWinCmdProcess(CR_RENDER_WINCMD* pWinCmd) +{ + bool fExit = false; + /* process commands */ + switch (pWinCmd->enmCmd) + { + case CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE: + crHashtableAdd(render_spu.pWinToInfoTable, pWinCmd->pWindow->window, pWinCmd->pWindow); + XSelectInput(render_spu.WinCmdVisual.dpy, pWinCmd->pWindow->window, ExposureMask); + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY: + crHashtableDelete(render_spu.pWinToInfoTable, pWinCmd->pWindow->window, NULL); + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_NOP: + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_EXIT: + renderspuWinCmdTerm(); + pWinCmd->rc = VINF_SUCCESS; + fExit = true; + pWinCmd->rc = VINF_SUCCESS; + break; + case CR_RENDER_WINCMD_TYPE_WIN_CREATE: + pWinCmd->rc = renderspuWinCmdWinCreate(pWinCmd->pWindow); + break; + case CR_RENDER_WINCMD_TYPE_WIN_DESTROY: + pWinCmd->rc = renderspuWinCmdWinDestroy(pWinCmd->pWindow); + break; + default: + crError("unknown WinCmd command! %d", pWinCmd->enmCmd); + pWinCmd->rc = VERR_INVALID_PARAMETER; + break; + } + + RTSemEventSignal(render_spu.hWinCmdCompleteEvent); + return fExit; +} + +static DECLCALLBACK(int) renderspuWinCmdThreadProc(RTTHREAD ThreadSelf, void *pvUser) +{ + int rc; + bool fExit = false; + crDebug("RenderSPU: Window thread started (%x)", crThreadID()); + + rc = renderspuWinCmdInit(); + + /* notify the main cmd thread that we have started */ + RTSemEventSignal(render_spu.hWinCmdCompleteEvent); + + if (!RT_SUCCESS(rc)) + { + CRASSERT(!render_spu.pWinToInfoTable); + return rc; + } + + do + { + XEvent event; + XNextEvent(render_spu.WinCmdVisual.dpy, &event); + + switch (event.type) + { + case ClientMessage: + { + CRASSERT(event.xclient.window == render_spu.WinCmdWindow.window); + if (event.xclient.window == render_spu.WinCmdWindow.window) + { + if (render_spu.WinCmdAtom == event.xclient.message_type) + { + CR_RENDER_WINCMD *pWinCmd; + memcpy(&pWinCmd, event.xclient.data.b, sizeof (pWinCmd)); + fExit = renderspuWinCmdProcess(pWinCmd); + } + } + + break; + } + case Expose: + { + if (!event.xexpose.count) + { + WindowInfo *pWindow = (WindowInfo*)crHashtableSearch(render_spu.pWinToInfoTable, event.xexpose.window); + if (pWindow) + { + const struct VBOXVR_SCR_COMPOSITOR * pCompositor; + + pCompositor = renderspuVBoxCompositorAcquire(pWindow); + if (pCompositor) + { + renderspuVBoxPresentCompositionGeneric(pWindow, pCompositor, NULL, 0, false); + renderspuVBoxCompositorRelease(pWindow); + } + } + } + break; + } + default: + break; + } + } while (!fExit); + + return 0; +} + +static int renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE enmCmd, WindowInfo *pWindow) +{ + Status status; + XEvent event; + CR_RENDER_WINCMD WinCmd, *pWinCmd; + int rc; + + pWinCmd = &WinCmd; + pWinCmd->enmCmd = enmCmd; + pWinCmd->rc = VERR_GENERAL_FAILURE; + pWinCmd->pWindow = pWindow; + + memset(&event, 0, sizeof (event)); + event.type = ClientMessage; + event.xclient.window = render_spu.WinCmdWindow.window; + event.xclient.message_type = render_spu.WinCmdAtom; + event.xclient.format = 8; + memcpy(event.xclient.data.b, &pWinCmd, sizeof (pWinCmd)); + + status = XSendEvent(render_spu.pCommunicationDisplay, render_spu.WinCmdWindow.window, False, StructureNotifyMask, &event); + if (!status) + { + Assert(0); + crWarning("XSendEvent returned null"); + return VERR_GENERAL_FAILURE; + } + + XFlush(render_spu.pCommunicationDisplay); + rc = RTSemEventWaitNoResume(render_spu.hWinCmdCompleteEvent, RT_INDEFINITE_WAIT); + if (!RT_SUCCESS(rc)) + { + crWarning("RTSemEventWaitNoResume failed rc %d", rc); + return rc; + } + return pWinCmd->rc; +} + +int renderspu_SystemInit() +{ + const char * dpyName; + int rc = VERR_GENERAL_FAILURE; + + if (!render_spu.use_glxchoosevisual) { + /* sometimes want to set this option with ATI drivers */ + render_spu.ws.glXChooseVisual = NULL; + } + + /* setup communication display connection */ + dpyName = renderspuGetDisplayName(); + if (!dpyName) + { + crWarning("no display name, aborting"); + return VERR_GENERAL_FAILURE; + } + + render_spu.pCommunicationDisplay = XOpenDisplay(dpyName); + if (!render_spu.pCommunicationDisplay) + { + crWarning( "Couldn't open X display named '%s'", dpyName ); + return VERR_GENERAL_FAILURE; + } + + if ( !render_spu.ws.glXQueryExtension( render_spu.pCommunicationDisplay, NULL, NULL ) ) + { + crWarning( "Render SPU: Display %s doesn't support GLX", dpyName ); + return VERR_GENERAL_FAILURE; + } + + rc = RTSemEventCreate(&render_spu.hWinCmdCompleteEvent); + if (RT_SUCCESS(rc)) + { + rc = RTThreadCreate(&render_spu.hWinCmdThread, renderspuWinCmdThreadProc, NULL, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "VBoxCrWinCmd"); + if (RT_SUCCESS(rc)) + { + rc = RTSemEventWait(render_spu.hWinCmdCompleteEvent, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + return VINF_SUCCESS; + } + else + { + crWarning("RTSemEventWait failed rc %d", rc); + } + + RTThreadWait(render_spu.hWinCmdThread, RT_INDEFINITE_WAIT, NULL); + } + else + { + crWarning("RTThreadCreate failed rc %d", rc); + } + RTSemEventDestroy(render_spu.hWinCmdCompleteEvent); + } + else + { + crWarning("RTSemEventCreate failed rc %d", rc); + } + + return rc; +} + +int renderspu_SystemTerm() +{ + int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_EXIT, NULL); + if (!RT_SUCCESS(rc)) + { + crWarning("renderspuWinCmdSubmit EXIT failed rc %d", rc); + return rc; + } + + RTThreadWait(render_spu.hWinCmdThread, RT_INDEFINITE_WAIT, NULL); + RTSemEventDestroy(render_spu.hWinCmdCompleteEvent); + return VINF_SUCCESS; +} + +GLboolean +renderspu_SystemInitVisual( VisualInfo *visual ) +{ + const char *dpyName; + int screen; + + CRASSERT(visual); + +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + /* A dummy visual - being non null is enough. */ + visual->visual =(XVisualInfo *) "os"; + return GL_TRUE; + } +#endif + + dpyName = renderspuGetDisplayName(); + if (!dpyName) + { + crWarning("Render SPU: no display, aborting"); + return GL_FALSE; + } + + + crInfo("Render SPU: Opening display %s", dpyName); + + if (dpyName && + (crStrncmp(dpyName, "localhost:11", 12) == 0 || + crStrncmp(dpyName, "localhost:12", 12) == 0 || + crStrncmp(dpyName, "localhost:13", 12) == 0)) { + /* Issue both debug and warning messages to make sure the + * message gets noticed! + */ + crDebug("Render SPU: display string looks like a proxy X server!"); + crDebug("Render SPU: This is usually a problem!"); + crWarning("Render SPU: display string looks like a proxy X server!"); + crWarning("Render SPU: This is usually a problem!"); + } + + visual->dpy = XOpenDisplay(dpyName); + if (!visual->dpy) + { + crWarning( "Couldn't open X display named '%s'", dpyName ); + return GL_FALSE; + } + + if ( !render_spu.ws.glXQueryExtension( visual->dpy, NULL, NULL ) ) + { + crWarning( "Render SPU: Display %s doesn't support GLX", visual->displayName ); + return GL_FALSE; + } + + screen = DefaultScreen(visual->dpy); + +#ifdef GLX_VERSION_1_3 + if (visual->visAttribs & CR_PBUFFER_BIT) + { + visual->fbconfig = chooseFBConfig(visual->dpy, screen, visual->visAttribs); + if (!visual->fbconfig) { + char s[1000]; + renderspuMakeVisString( visual->visAttribs, s ); + crWarning( "Render SPU: Display %s doesn't have the necessary fbconfig: %s", + dpyName, s ); + XCloseDisplay(visual->dpy); + return GL_FALSE; + } + } + else +#endif /* GLX_VERSION_1_3 */ + { + visual->visual = chooseVisualRetry(visual->dpy, screen, visual->visAttribs); + if (!visual->visual) { + char s[1000]; + renderspuMakeVisString( visual->visAttribs, s ); + crWarning("Render SPU: Display %s doesn't have the necessary visual: %s", + dpyName, s ); + XCloseDisplay(visual->dpy); + return GL_FALSE; + } + } + + if ( render_spu.sync ) + { + crDebug( "Render SPU: Turning on XSynchronize" ); + XSynchronize( visual->dpy, True ); + } + + if (visual->visual) { + crDebug( "Render SPU: Choose visual id=0x%x: RGBA=(%d,%d,%d,%d) Z=%d" + " stencil=%d double=%d stereo=%d accum=(%d,%d,%d,%d)", + (int) visual->visual->visualid, + Attrib( visual, GLX_RED_SIZE ), + Attrib( visual, GLX_GREEN_SIZE ), + Attrib( visual, GLX_BLUE_SIZE ), + Attrib( visual, GLX_ALPHA_SIZE ), + Attrib( visual, GLX_DEPTH_SIZE ), + Attrib( visual, GLX_STENCIL_SIZE ), + Attrib( visual, GLX_DOUBLEBUFFER ), + Attrib( visual, GLX_STEREO ), + Attrib( visual, GLX_ACCUM_RED_SIZE ), + Attrib( visual, GLX_ACCUM_GREEN_SIZE ), + Attrib( visual, GLX_ACCUM_BLUE_SIZE ), + Attrib( visual, GLX_ACCUM_ALPHA_SIZE ) + ); + } + else if (visual->fbconfig) { + int id; + render_spu.ws.glXGetFBConfigAttrib(visual->dpy, visual->fbconfig, + GLX_FBCONFIG_ID, &id); + crDebug("Render SPU: Chose FBConfig 0x%x, visBits=0x%x", + id, visual->visAttribs); + } + + return GL_TRUE; +} + + +/* + * Add a GLX window to a swap group for inter-machine SwapBuffer + * synchronization. + * Only supported on NVIDIA Quadro 3000G hardware. + */ +static void +JoinSwapGroup(Display *dpy, int screen, Window window, + GLuint group, GLuint barrier) +{ + GLuint maxGroups, maxBarriers; + const char *ext; + Bool b; + + /* + * XXX maybe query glXGetClientString() instead??? + */ + ext = render_spu.ws.glXQueryExtensionsString(dpy, screen); + + if (!crStrstr(ext, "GLX_NV_swap_group") || + !render_spu.ws.glXQueryMaxSwapGroupsNV || + !render_spu.ws.glXJoinSwapGroupNV || + !render_spu.ws.glXBindSwapBarrierNV) { + crWarning("Render SPU: nv_swap_group is set but GLX_NV_swap_group is not supported on this system!"); + return; + } + + b = render_spu.ws.glXQueryMaxSwapGroupsNV(dpy, screen, + &maxGroups, &maxBarriers); + if (!b) + crWarning("Render SPU: call to glXQueryMaxSwapGroupsNV() failed!"); + + if (group >= maxGroups) { + crWarning("Render SPU: nv_swap_group too large (%d > %d)", + group, (int) maxGroups); + return; + } + crDebug("Render SPU: max swap groups = %d, max barriers = %d", + maxGroups, maxBarriers); + + /* add this window to the swap group */ + b = render_spu.ws.glXJoinSwapGroupNV(dpy, window, group); + if (!b) { + crWarning("Render SPU: call to glXJoinSwapGroupNV() failed!"); + return; + } + else { + crDebug("Render SPU: call to glXJoinSwapGroupNV() worked!"); + } + + /* ... and bind window to barrier of same ID */ + b = render_spu.ws.glXBindSwapBarrierNV(dpy, group, barrier); + if (!b) { + crWarning("Render SPU: call to glXBindSwapBarrierNV(group=%d barrier=%d) failed!", group, barrier); + return; + } + else { + crDebug("Render SPU: call to glXBindSwapBarrierNV(group=%d barrier=%d) worked!", group, barrier); + } + + crDebug("Render SPU: window has joined swap group %d", group); +} + + + +static GLboolean +createWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) +{ + Display *dpy; + Colormap cmap; + XSetWindowAttributes swa; + XSizeHints hints = {0}; + XEvent event; + XTextProperty text_prop; + XClassHint *class_hints = NULL; + Window parent; + char *name; + unsigned long flags; + unsigned int vncWin; + + CRASSERT(visual); + window->visual = visual; + window->nativeWindow = 0; + +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + return GL_TRUE; +#endif + + dpy = visual->dpy; + + if ( render_spu.use_L2 ) + { + crWarning( "Render SPU: Going fullscreen because we think we're using Lightning-2." ); + render_spu.fullscreen = 1; + } + + /* + * Query screen size if we're going full-screen + */ + if ( render_spu.fullscreen ) + { + XWindowAttributes xwa; + Window root_window; + + /* disable the screensaver */ + XSetScreenSaver( dpy, 0, 0, PreferBlanking, AllowExposures ); + crDebug( "Render SPU: Just turned off the screensaver" ); + + /* Figure out how big the screen is, and make the window that size */ + root_window = DefaultRootWindow( dpy ); + XGetWindowAttributes( dpy, root_window, &xwa ); + + crDebug( "Render SPU: root window size: %d x %d", xwa.width, xwa.height ); + + window->x = 0; + window->y = 0; + window->BltInfo.width = xwa.width; + window->BltInfo.height = xwa.height; + } + + /* i've changed default window size to be 0,0 but X doesn't like it */ + /*CRASSERT(window->BltInfo.width >= 1); + CRASSERT(window->BltInfo.height >= 1);*/ + if (window->BltInfo.width < 1) window->BltInfo.width = 1; + if (window->BltInfo.height < 1) window->BltInfo.height = 1; + + /* + * Get a colormap. + */ + if (render_spu.use_lut8) + cmap = GetLUTColormap( dpy, visual->visual ); + else + cmap = GetShareableColormap( dpy, visual->visual ); + if ( !cmap ) { + crError( "Render SPU: Unable to get a colormap!" ); + return GL_FALSE; + } + + /* destroy existing window if there is one */ + if (window->window) { + XDestroyWindow(dpy, window->window); + } + + /* + * Create the window + * + * POSSIBLE OPTIMIZATION: + * If we're using the render_to_app_window or render_to_crut_window option + * (or DMX) we may never actually use this X window. So we could perhaps + * delay its creation until glXMakeCurrent is called. With NVIDIA's OpenGL + * driver, creating a lot of GLX windows can eat up a lot of VRAM and lead + * to slow, software-fallback rendering. Apps that use render_to_app_window, + * etc should set the default window size very small to avoid this. + * See dmx.conf for example. + */ + swa.colormap = cmap; + swa.border_pixel = 0; + swa.event_mask = ExposureMask | StructureNotifyMask; + swa.override_redirect = 1; + + flags = CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect; + + /* + * We pass the VNC's desktop windowID via an environment variable. + * If we don't find one, we're not on a 3D-capable vncviewer, or + * if we do find one, then create the renderspu subwindow as a + * child of the vncviewer's desktop window. + * + * This is purely for the replicateSPU. + * + * NOTE: This is crufty, and will do for now. FIXME. + */ + vncWin = crStrToInt( crGetenv("CRVNCWINDOW") ); + if (vncWin) + parent = (Window) vncWin; + else + parent = RootWindow(dpy, visual->visual->screen); + + if (render_spu_parent_window_id>0) + { + crDebug("Render SPU: VBox parent window_id is: %x", render_spu_parent_window_id); + window->window = XCreateWindow(dpy, render_spu_parent_window_id, + window->x, window->y, + window->BltInfo.width, window->BltInfo.height, + 0, visual->visual->depth, InputOutput, + visual->visual->visual, flags, &swa); + } + else + { + /* This should happen only at the call from crVBoxServerInit. At this point we don't + * know render_spu_parent_window_id yet, nor we need it for default window which is hidden. + */ + + crDebug("Render SPU: Creating global window, parent: %x", RootWindow(dpy, visual->visual->screen)); + window->window = XCreateWindow(dpy, RootWindow(dpy, visual->visual->screen), + window->x, window->y, + window->BltInfo.width, window->BltInfo.height, + 0, visual->visual->depth, InputOutput, + visual->visual->visual, flags, &swa); + } + + if (!window->window) { + crWarning( "Render SPU: unable to create window" ); + return GL_FALSE; + } + + crDebug( "Render SPU: Created window 0x%x on display %s, Xvisual 0x%x", + (int) window->window, + DisplayString(visual->dpy), + (int) visual->visual->visual->visualid /* yikes */ + ); + + if (render_spu.fullscreen || render_spu.borderless) + { + /* Disable border/decorations with an MWM property (observed by most + * modern window managers. + */ + PropMotifWmHints motif_hints; + Atom prop, proptype; + + /* setup the property */ + motif_hints.flags = MWM_HINTS_DECORATIONS; + motif_hints.decorations = 0; /* Turn off all decorations */ + + /* get the atom for the property */ + prop = XInternAtom( dpy, "_MOTIF_WM_HINTS", True ); + if (prop) { + /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */ + proptype = prop; + XChangeProperty( dpy, window->window, /* display, window */ + prop, proptype, /* property, type */ + 32, /* format: 32-bit datums */ + PropModeReplace, /* mode */ + (unsigned char *) &motif_hints, /* data */ + PROP_MOTIF_WM_HINTS_ELEMENTS /* nelements */ + ); + } + } + + /* Make a clear cursor to get rid of the monitor cursor */ + if ( render_spu.fullscreen ) + { + Pixmap pixmap; + Cursor cursor; + XColor colour; + char clearByte = 0; + + /* AdB - Only bother to create a 1x1 cursor (byte) */ + pixmap = XCreatePixmapFromBitmapData(dpy, window->window, &clearByte, + 1, 1, 1, 0, 1); + if(!pixmap){ + crWarning("Unable to create clear cursor pixmap"); + return GL_FALSE; + } + + cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &colour, &colour, 0, 0); + if(!cursor){ + crWarning("Unable to create clear cursor from zero byte pixmap"); + return GL_FALSE; + } + XDefineCursor(dpy, window->window, cursor); + XFreePixmap(dpy, pixmap); + } + + hints.x = window->x; + hints.y = window->y; + hints.width = window->BltInfo.width; + hints.height = window->BltInfo.height; + hints.min_width = hints.width; + hints.min_height = hints.height; + hints.max_width = hints.width; + hints.max_height = hints.height; + if (render_spu.resizable) + hints.flags = USPosition | USSize; + else + hints.flags = USPosition | USSize | PMinSize | PMaxSize; + XSetStandardProperties( dpy, window->window, + WINDOW_NAME, WINDOW_NAME, + None, NULL, 0, &hints ); + + /* New item! This is needed so that the sgimouse server can find + * the crDebug window. + */ + name = WINDOW_NAME; + XStringListToTextProperty( &name, 1, &text_prop ); + XSetWMName( dpy, window->window, &text_prop ); + + /* Set window name, resource class */ + class_hints = XAllocClassHint( ); + class_hints->res_name = crStrdup( "foo" ); + class_hints->res_class = crStrdup( "Chromium" ); + XSetClassHint( dpy, window->window, class_hints ); + crFree( class_hints->res_name ); + crFree( class_hints->res_class ); + XFree( class_hints ); + + if (showIt) { + XMapWindow( dpy, window->window ); + XIfEvent( dpy, &event, WaitForMapNotify, + (char *) window->window ); + } + + if ((window->visual->visAttribs & CR_DOUBLE_BIT) && render_spu.nvSwapGroup) { + /* NOTE: + * If this SPU creates N windows we don't want to gang the N windows + * together! + * By adding the window ID to the nvSwapGroup ID we can be sure each + * app window is in a separate swap group while all the back-end windows + * which form a mural are in the same swap group. + */ + GLuint group = 0; /*render_spu.nvSwapGroup + window->BltInfo.Base.id;*/ + GLuint barrier = 0; + JoinSwapGroup(dpy, visual->visual->screen, window->window, group, barrier); + } + + /* + * End GLX code + */ + crDebug( "Render SPU: actual window x, y, width, height: %d, %d, %d, %d", + window->x, window->y, window->BltInfo.width, window->BltInfo.height ); + + XSync(dpy, 0); + + if (window->BltInfo.Base.id != CR_RENDER_WINCMD_ID) + { + int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_WIN_ON_CREATE, window); + AssertRC(rc); + } + + return GL_TRUE; +} + + +static GLboolean +createPBuffer( VisualInfo *visual, WindowInfo *window ) +{ + window->visual = visual; + window->x = 0; + window->y = 0; + window->nativeWindow = 0; + + CRASSERT(window->BltInfo.width > 0); + CRASSERT(window->BltInfo.height > 0); + +#ifdef GLX_VERSION_1_3 + { + int attribs[100], i = 0, w, h; + CRASSERT(visual->fbconfig); + w = window->BltInfo.width; + h = window->BltInfo.height; + attribs[i++] = GLX_PRESERVED_CONTENTS; + attribs[i++] = True; + attribs[i++] = GLX_PBUFFER_WIDTH; + attribs[i++] = w; + attribs[i++] = GLX_PBUFFER_HEIGHT; + attribs[i++] = h; + attribs[i++] = 0; /* terminator */ + window->window = render_spu.ws.glXCreatePbuffer(visual->dpy, + visual->fbconfig, attribs); + if (window->window) { + crDebug("Render SPU: Allocated %d x %d pbuffer", w, h); + return GL_TRUE; + } + else { + crWarning("Render SPU: Failed to allocate %d x %d pbuffer", w, h); + return GL_FALSE; + } + } +#endif /* GLX_VERSION_1_3 */ + return GL_FALSE; +} + + +GLboolean +renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) +{ + if (visual->visAttribs & CR_PBUFFER_BIT) { + window->BltInfo.width = render_spu.defaultWidth; + window->BltInfo.height = render_spu.defaultHeight; + return createPBuffer(visual, window); + } + else { + return createWindow(visual, showIt, window); + } +} + +GLboolean +renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) +{ + return renderspu_SystemCreateWindow(visual, showIt, window); +} + +void +renderspu_SystemDestroyWindow( WindowInfo *window ) +{ + CRASSERT(window); + CRASSERT(window->visual); + +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + { + crFree(window->buffer); + window->buffer = NULL; + } + else +#endif + { + if (window->visual->visAttribs & CR_PBUFFER_BIT) { +#ifdef GLX_VERSION_1_3 + render_spu.ws.glXDestroyPbuffer(window->visual->dpy, window->window); +#endif + } + else { + /* The value window->nativeWindow will only be non-NULL if the + * render_to_app_window option is set to true. In this case, we + * don't want to do anything, since we're not responsible for this + * window. I know...personal responsibility and all... + */ + if (!window->nativeWindow) { + if (window->BltInfo.Base.id != CR_RENDER_WINCMD_ID) + { + int rc = renderspuWinCmdSubmit(CR_RENDER_WINCMD_TYPE_WIN_ON_DESTROY, window); + AssertRC(rc); + } + XDestroyWindow(window->visual->dpy, window->window); + XSync(window->visual->dpy, 0); + } + } + } + window->visual = NULL; + window->window = 0; +} + + +GLboolean +renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext ) +{ + Bool is_direct; + GLXContext sharedSystemContext = NULL; + + CRASSERT(visual); + CRASSERT(context); + + context->visual = visual; + + if (sharedContext != NULL) { + sharedSystemContext = sharedContext->context; + } + + + +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + context->context = (GLXContext) render_spu.OSMesaCreateContext(OSMESA_RGB, 0); + if (context->context) + return GL_TRUE; + else + return GL_FALSE; + } +#endif + +#ifdef GLX_VERSION_1_3 + if (visual->visAttribs & CR_PBUFFER_BIT) { + context->context = render_spu.ws.glXCreateNewContext( visual->dpy, + visual->fbconfig, + GLX_RGBA_TYPE, + sharedSystemContext, + render_spu.try_direct); + } + else +#endif + { + context->context = render_spu.ws.glXCreateContext( visual->dpy, + visual->visual, + sharedSystemContext, + render_spu.try_direct); + } + if (!context->context) { + crError( "Render SPU: Couldn't create rendering context" ); + return GL_FALSE; + } + + is_direct = render_spu.ws.glXIsDirect( visual->dpy, context->context ); + if (visual->visual) + crDebug("Render SPU: Created %s context (%d) on display %s for visAttribs 0x%x", + is_direct ? "DIRECT" : "INDIRECT", + context->BltInfo.Base.id, + DisplayString(visual->dpy), + visual->visAttribs); + + if ( render_spu.force_direct && !is_direct ) + { + crError( "Render SPU: Direct rendering not possible." ); + return GL_FALSE; + } + + return GL_TRUE; +} + + +#define USE_GLX_COPYCONTEXT 0 + +#if !USE_GLX_COPYCONTEXT + +/** + * Unfortunately, glXCopyContext() is broken sometimes (NVIDIA 76.76 driver). + * This bit of code gets and sets GL state we need to copy between contexts. + */ +struct saved_state +{ + /* XXX depending on the app, more state may be needed here */ + GLboolean Lighting; + GLboolean LightEnabled[8]; + GLfloat LightPos[8][4]; + GLfloat LightAmbient[8][4]; + GLfloat LightDiffuse[8][4]; + GLfloat LightSpecular[8][4]; + GLboolean DepthTest; +}; + +static struct saved_state SavedState; + +static void +get_state(struct saved_state *s) +{ + int i; + + s->Lighting = render_spu.self.IsEnabled(GL_LIGHTING); + for (i = 0; i < 8; i++) { + s->LightEnabled[i] = render_spu.self.IsEnabled(GL_LIGHT0 + i); + render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_POSITION, s->LightPos[i]); + render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_AMBIENT, s->LightAmbient[i]); + render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_DIFFUSE, s->LightDiffuse[i]); + render_spu.self.GetLightfv(GL_LIGHT0 + i, GL_SPECULAR, s->LightSpecular[i]); + } + + s->DepthTest = render_spu.self.IsEnabled(GL_DEPTH_TEST); +} + +static void +set_state(const struct saved_state *s) +{ + int i; + + if (s->Lighting) { + render_spu.self.Enable(GL_LIGHTING); + } + else { + render_spu.self.Disable(GL_LIGHTING); + } + + for (i = 0; i < 8; i++) { + if (s->LightEnabled[i]) { + render_spu.self.Enable(GL_LIGHT0 + i); + } + else { + render_spu.self.Disable(GL_LIGHT0 + i); + } + render_spu.self.Lightfv(GL_LIGHT0 + i, GL_POSITION, s->LightPos[i]); + render_spu.self.Lightfv(GL_LIGHT0 + i, GL_AMBIENT, s->LightAmbient[i]); + render_spu.self.Lightfv(GL_LIGHT0 + i, GL_DIFFUSE, s->LightDiffuse[i]); + render_spu.self.Lightfv(GL_LIGHT0 + i, GL_SPECULAR, s->LightSpecular[i]); + } + + if (s->DepthTest) + render_spu.self.Enable(GL_DEPTH_TEST); + else + render_spu.self.Disable(GL_DEPTH_TEST); +} + +#endif /* !USE_GLX_COPYCONTEXT */ + +/** + * Recreate the GLX context for ContextInfo. The new context will use the + * visual specified by newVisualID. + */ +static void +renderspu_RecreateContext( ContextInfo *context, int newVisualID ) +{ + XVisualInfo templateVis, *vis; + long templateFlags; + int screen = 0, count; + GLXContext oldContext = context->context; + + templateFlags = VisualScreenMask | VisualIDMask; + templateVis.screen = screen; + templateVis.visualid = newVisualID; + vis = XGetVisualInfo(context->visual->dpy, templateFlags, &templateVis, &count); + CRASSERT(vis); + if (!vis) + return; + + /* create new context */ + crDebug("Render SPU: Creating new GLX context with visual 0x%x", newVisualID); + context->context = render_spu.ws.glXCreateContext(context->visual->dpy, + vis, NULL, + render_spu.try_direct); + CRASSERT(context->context); + +#if USE_GLX_COPYCONTEXT + /* copy old context state to new context */ + render_spu.ws.glXCopyContext(context->visual->dpy, + oldContext, context->context, ~0); + crDebug("Render SPU: Done copying context state"); +#endif + + /* destroy old context */ + render_spu.ws.glXDestroyContext(context->visual->dpy, oldContext); + + context->visual->visual = vis; +} + + +void +renderspu_SystemDestroyContext( ContextInfo *context ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + { + render_spu.OSMesaDestroyContext( (OSMesaContext) context->context ); + } + else +#endif + { +#if 0 + /* XXX disable for now - causes segfaults w/ NVIDIA's driver */ + render_spu.ws.glXDestroyContext( context->visual->dpy, context->context ); +#endif + } + context->visual = NULL; + context->context = 0; +} + + +#ifdef USE_OSMESA +static void +check_buffer_size( WindowInfo *window ) +{ + if (window->BltInfo.width != window->in_buffer_width + || window->BltInfo.height != window->in_buffer_height + || ! window->buffer) { + crFree(window->buffer); + + window->buffer = crCalloc(window->BltInfo.width * window->BltInfo.height + * 4 * sizeof (GLubyte)); + + window->in_buffer_width = window->BltInfo.width; + window->in_buffer_height = window->BltInfo.height; + + crDebug("Render SPU: dimensions changed to %d x %d", window->BltInfo.width, window->BltInfo.height); + } +} +#endif + + +void +renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, + ContextInfo *context ) +{ + Bool b; + + CRASSERT(render_spu.ws.glXMakeCurrent); + + /*crDebug("%s nativeWindow=0x%x", __FUNCTION__, (int) nativeWindow);*/ + +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + check_buffer_size(window); + render_spu.OSMesaMakeCurrent( (OSMesaContext) context->context, + window->buffer, GL_UNSIGNED_BYTE, + window->BltInfo.width, window->BltInfo.height); + return; + } +#endif + + nativeWindow = 0; + + if (window && context) { + window->appWindow = nativeWindow; + + if (window->visual != context->visual) { + crDebug("Render SPU: MakeCurrent visual mismatch (win(%d) bits:0x%x != ctx(%d) bits:0x%x); remaking window.", + window->BltInfo.Base.id, window->visual->visAttribs, + context->BltInfo.Base.id, context->visual->visAttribs); + /* + * XXX have to revisit this issue!!! + * + * But for now we destroy the current window + * and re-create it with the context's visual abilities + */ +#ifndef SOLARIS_9_X_BUG + /* + I'm having some really weird issues if I destroy this window + when I'm using the version of sunX that comes with Solaris 9. + Subsiquent glX calls return GLXBadCurrentWindow error. + + This is an issue even when running Linux version and using + the Solaris 9 sunX as a display. + -- jw + + jw: we might have to call glXMakeCurrent(dpy, 0, 0) to unbind + the context from the window before destroying it. -Brian + */ + render_spu.ws.glXMakeCurrent(window->visual->dpy, 0, 0); + renderspu_SystemDestroyWindow( window ); +#endif + renderspu_SystemCreateWindow( context->visual, window->visible, window ); + /* + crError("In renderspu_SystemMakeCurrent() window and context" + " weren't created with same visual!"); + */ + } + + CRASSERT(context->context); + +#if 0 + if (render_spu.render_to_crut_window) { + if (render_spu.crut_drawable == 0) { + /* We don't know the X drawable ID yet. Ask mothership for it. */ + char response[8096]; + CRConnection *conn = crMothershipConnect(); + if (!conn) + { + crError("Couldn't connect to the mothership to get CRUT drawable-- " + "I have no idea what to do!"); + } + crMothershipGetParam( conn, "crut_drawable", response ); + render_spu.crut_drawable = crStrToInt(response); + crMothershipDisconnect(conn); + + crDebug("Render SPU: using CRUT drawable: 0x%x", + render_spu.crut_drawable); + if (!render_spu.crut_drawable) { + crDebug("Render SPU: Crut drawable 0 is invalid"); + /* Continue with nativeWindow = 0; we'll render to the window that + * we (the Render SPU) previously created. + */ + } + } + + nativeWindow = render_spu.crut_drawable; + } +#endif + + if ((render_spu.render_to_crut_window || render_spu.render_to_app_window) + && nativeWindow) + { + /* We're about to bind the rendering context to a window that we + * (the Render SPU) did not create. The window was created by the + * application or the CRUT server. + * Make sure the window ID is valid and that the window's X visual is + * the same as the rendering context's. + */ + if (WindowExists(window->visual->dpy, nativeWindow)) + { + int vid = GetWindowVisualID(window->visual->dpy, nativeWindow); + GLboolean recreated = GL_FALSE; + + /* check that the window's visual and context's visual match */ + if (vid != (int) context->visual->visual->visualid) { + crWarning("Render SPU: Can't bind context %d to CRUT/native window " + "0x%x because of different X visuals (0x%x != 0x%x)!", + context->BltInfo.Base.id, (int) nativeWindow, + vid, (int) context->visual->visual->visualid); + crWarning("Render SPU: Trying to recreate GLX context to match."); + /* Try to recreate the GLX context so that it uses the same + * GLX visual as the window. + */ +#if !USE_GLX_COPYCONTEXT + if (context->everCurrent) { + get_state(&SavedState); + } +#endif + renderspu_RecreateContext(context, vid); + recreated = GL_TRUE; + } + + /* OK, this should work */ + window->nativeWindow = (Window) nativeWindow; + b = render_spu.ws.glXMakeCurrent( window->visual->dpy, + window->nativeWindow, + context->context ); + CRASSERT(b); +#if !USE_GLX_COPYCONTEXT + if (recreated) { + set_state(&SavedState); + } +#endif + } + else + { + crWarning("Render SPU: render_to_app/crut_window option is set but " + "the window ID 0x%x is invalid on the display named %s", + (unsigned int) nativeWindow, + DisplayString(window->visual->dpy)); + CRASSERT(window->window); + b = render_spu.ws.glXMakeCurrent( window->visual->dpy, + window->window, context->context ); + CRASSERT(b); + } + } + else + { + /* This is the normal case - rendering to the render SPU's own window */ + CRASSERT(window->window); +#if 0 + crDebug("calling glXMakecurrent(%p, 0x%x, 0x%x)", + window->visual->dpy, + (int) window->window, (int) context->context ); +#endif + b = render_spu.ws.glXMakeCurrent( window->visual->dpy, + window->window, context->context ); + if (!b) { + crWarning("glXMakeCurrent(%p, 0x%x, %p) failed! (winId %d, ctxId %d)", + window->visual->dpy, + (int) window->window, (void *) context->context, + window->BltInfo.Base.id, context->BltInfo.Base.id ); + } + /*CRASSERT(b);*/ + } + + /* XXX this is a total hack to work around an NVIDIA driver bug */ +#if 0 + if (render_spu.self.GetFloatv && context->haveWindowPosARB) { + GLfloat f[4]; + render_spu.self.GetFloatv(GL_CURRENT_RASTER_POSITION, f); + if (!window->everCurrent || f[1] < 0.0) { + crDebug("Render SPU: Resetting raster pos"); + render_spu.self.WindowPos2iARB(0, 0); + } + } +#endif + } + else + { + GET_CONTEXT(pCurCtx); + if (pCurCtx) + { + b = render_spu.ws.glXMakeCurrent( pCurCtx->currentWindow->visual->dpy, None, NULL); + if (!b) { + crWarning("glXMakeCurrent(%p, None, NULL) failed!", pCurCtx->currentWindow->visual->dpy); + } + } + + } +} + + +/** + * Set window (or pbuffer) size. + */ +void +renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + window->BltInfo.width = w; + window->BltInfo.height = h; + check_buffer_size(window); + return; + } +#endif + + CRASSERT(window); + CRASSERT(window->visual); + if (window->visual->visAttribs & CR_PBUFFER_BIT) + { + /* resizing a pbuffer */ + if (render_spu.pbufferWidth != 0 || render_spu.pbufferHeight != 0) { + /* size limit check */ + if (w > render_spu.pbufferWidth || h > render_spu.pbufferHeight) { + crWarning("Render SPU: Request for %d x %d pbuffer is larger than " + "the configured size of %d x %d. ('pbuffer_size')", + w, h, render_spu.pbufferWidth, render_spu.pbufferHeight); + return; + } + /* + * If the requested new pbuffer size is greater than 1/2 the size of + * the max pbuffer, just use the max pbuffer size. This helps avoid + * problems with VRAM memory fragmentation. If we run out of VRAM + * for pbuffers, some drivers revert to software rendering. We want + * to avoid that! + */ + if (w * h >= render_spu.pbufferWidth * render_spu.pbufferHeight / 2) { + /* increase the dimensions to the max pbuffer size now */ + w = render_spu.pbufferWidth; + h = render_spu.pbufferHeight; + } + } + + if (window->BltInfo.width != w || window->BltInfo.height != h) { + /* Only resize if the new dimensions really are different */ +#ifdef CHROMIUM_THREADSAFE + ContextInfo *currentContext = (ContextInfo *) crGetTSD(&_RenderTSD); +#else + ContextInfo *currentContext = render_spu.currentContext; +#endif + /* Can't resize pbuffers, so destroy it and make a new one */ + render_spu.ws.glXDestroyPbuffer(window->visual->dpy, window->window); + window->BltInfo.width = w; + window->BltInfo.height = h; + crDebug("Render SPU: Creating new %d x %d PBuffer (id=%d)", + w, h, window->BltInfo.Base.id); + if (!createPBuffer(window->visual, window)) { + crWarning("Render SPU: Unable to create PBuffer (out of VRAM?)!"); + } + else if (currentContext && currentContext->currentWindow == window) { + /* Determine if we need to bind the current context to new pbuffer */ + render_spu.ws.glXMakeCurrent(window->visual->dpy, + window->window, + currentContext->context ); + } + } + } + else { + if (!w || !h) + { + /* X can not handle zero sizes */ + if (window->visible) + { + renderspu_SystemShowWindow( window, GL_FALSE ); + } + return; + } + /* Resize ordinary X window */ + /* + * This is ugly, but it seems to be the only thing that works. + * Basically, XResizeWindow() doesn't seem to always take effect + * immediately. + * Even after an XSync(), the GetWindowAttributes() call will sometimes + * return the old window size. So, we use a loop to repeat the window + * resize until it seems to take effect. + */ + int attempt; + crDebug("Render SPU: XResizeWindow (%x, %x, %d, %d)", window->visual->dpy, window->window, w, h); + XResizeWindow(window->visual->dpy, window->window, w, h); + XSync(window->visual->dpy, 0); + + if (!window->BltInfo.width || !window->BltInfo.height) + { + /* we have hidden the window instead of sizing it to (0;0) since X is unable to handle zero sizes */ + if (window->visible) + { + renderspu_SystemShowWindow( window, GL_TRUE ); + return; + } + } +#if 0 + for (attempt = 0; attempt < 3; attempt++) { /* try three times max */ + XWindowAttributes attribs; + /* Now, query the window size */ + XGetWindowAttributes(window->visual->dpy, window->window, &attribs); + if (attribs.width == w && attribs.height == h) + break; + /* sleep for a millisecond and try again */ + crMsleep(1); + } +#endif + } +} + + +void +renderspu_SystemGetWindowGeometry( WindowInfo *window, + GLint *x, GLint *y, GLint *w, GLint *h ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + *w = window->BltInfo.width; + *h = window->BltInfo.height; + return; + } +#endif + + CRASSERT(window); + CRASSERT(window->visual); + CRASSERT(window->window); + if (window->visual->visAttribs & CR_PBUFFER_BIT) + { + *x = 0; + *y = 0; + *w = window->BltInfo.width; + *h = window->BltInfo.height; + } + else + { + Window xw, child, root; + unsigned int width, height, bw, d; + int rx, ry; + + if ((render_spu.render_to_app_window || render_spu.render_to_crut_window) + && window->nativeWindow) { + xw = window->nativeWindow; + } + else { + xw = window->window; + } + + XGetGeometry(window->visual->dpy, xw, &root, + x, y, &width, &height, &bw, &d); + + /* translate x/y to screen coords */ + if (!XTranslateCoordinates(window->visual->dpy, xw, root, + 0, 0, &rx, &ry, &child)) { + rx = ry = 0; + } + *x = rx; + *y = ry; + *w = (int) width; + *h = (int) height; + } +} + + +void +renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h ) +{ + int scrn; +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + *w = 2048; + *h = 2048; + return; + } +#endif + + CRASSERT(window); + CRASSERT(window->visual); + CRASSERT(window->window); + + scrn = DefaultScreen(window->visual->dpy); + *w = DisplayWidth(window->visual->dpy, scrn); + *h = DisplayHeight(window->visual->dpy, scrn); +} + + +void +renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + return; +#endif + + CRASSERT(window); + CRASSERT(window->visual); + if ((window->visual->visAttribs & CR_PBUFFER_BIT) == 0) + { + crDebug("Render SPU: XMoveWindow (%x, %x, %d, %d)", window->visual->dpy, window->window, x, y); + XMoveWindow(window->visual->dpy, window->window, x, y); + XSync(window->visual->dpy, 0); + } +} + +GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window) +{ + return GL_FALSE; +} + +void +renderspu_SystemWindowVisibleRegion( WindowInfo *window, GLint cRects, const GLint *pRects ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + return; +#endif + + CRASSERT(window); + CRASSERT(window->visual); + if ((window->visual->visAttribs & CR_PBUFFER_BIT) == 0) + { + int evb, erb, i; + XRectangle *pXRects; + + if (!XShapeQueryExtension(window->visual->dpy, &evb, &erb)) + { + crWarning("Render SPU: Display %s doesn't support SHAPE extension", window->visual->displayName); + return; + } + + if (cRects>0) + { + pXRects = (XRectangle *) crAlloc(cRects * sizeof(XRectangle)); + + for (i=0; i<cRects; ++i) + { + pXRects[i].x = (short) pRects[4*i]; + pXRects[i].y = (short) pRects[4*i+1]; + pXRects[i].width = (unsigned short) (pRects[4*i+2]-pRects[4*i]); + pXRects[i].height = (unsigned short) (pRects[4*i+3]-pRects[4*i+1]); + } + } + else + { + pXRects = (XRectangle *) crAlloc(sizeof(XRectangle)); + pXRects[0].x = 0; + pXRects[0].y = 0; + pXRects[0].width = 0; + pXRects[0].height = 0; + cRects = 1; + } + + crDebug("Render SPU: XShapeCombineRectangles (%x, %x, cRects=%i)", window->visual->dpy, window->window, cRects); + + XShapeCombineRectangles(window->visual->dpy, window->window, ShapeBounding, 0, 0, + pXRects, cRects, ShapeSet, YXBanded); + XSync(window->visual->dpy, 0); + crFree(pXRects); + } +} + +/* Either show or hide the render SPU's window. */ +void +renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt ) +{ +#ifdef USE_OSMESA + if (render_spu.use_osmesa) + return; +#endif + + if (window->visual->dpy && window->window && + (window->visual->visAttribs & CR_PBUFFER_BIT) == 0) + { + if (showIt) + { + if (window->BltInfo.width && window->BltInfo.height) + { + XMapWindow( window->visual->dpy, window->window ); + XSync(window->visual->dpy, 0); + } + } + else + { + XUnmapWindow( window->visual->dpy, window->window ); + XSync(window->visual->dpy, 0); + } + } +} + +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + /* The !render_spu.force_present_main_thread code flow is actually inspired + * by cocoa backend impl, here it forces rendering in WinCmd thread rather + * than a Main thread. It defaults to 1, because otherwise there were + * 3D driver incompatibilities on some systems. Elsewhere it causes flicker + * on NVidia GPUs. In principle would need root cause investigation. */ + if (!render_spu.force_present_main_thread) + { + const struct VBOXVR_SCR_COMPOSITOR *pCompositor; + /* we do not want to be blocked with the GUI thread here, so only draw here if we are really able to do that w/o blocking */ + int rc = renderspuVBoxCompositorTryAcquire(window, &pCompositor); + if (RT_SUCCESS(rc)) + { + renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0, false); + renderspuVBoxCompositorRelease(window); + } + else if (rc != VERR_SEM_BUSY) + { + /* this is somewhat we do not expect */ + WARN(("renderspuVBoxCompositorTryAcquire failed rc %d", rc)); + return; + } + } + + { + Status status; + XEvent event; + render_spu.self.Flush(); +// renderspuVBoxPresentBlitterEnsureCreated(window, 0); + + crMemset(&event, 0, sizeof (event)); + event.type = Expose; + event.xexpose.window = window->window; + event.xexpose.width = window->BltInfo.width; + event.xexpose.height = window->BltInfo.height; + status = XSendEvent(render_spu.pCommunicationDisplay, render_spu.WinCmdWindow.window, False, 0, &event); + if (!status) + { + WARN(("XSendEvent returned null")); + } + XFlush(render_spu.pCommunicationDisplay); + } +} + +static void +MarkWindow(WindowInfo *w) +{ + static GC gc = 0; /* XXX per-window??? */ + if (!gc) { + /* Create a GC for drawing invisible lines */ + XGCValues gcValues; + gcValues.function = GXnoop; + gc = XCreateGC(w->visual->dpy, w->nativeWindow, GCFunction, &gcValues); + } + XDrawLine(w->visual->dpy, w->nativeWindow, gc, 0, 0, w->BltInfo.width, w->BltInfo.height); +} + + +void +renderspu_SystemSwapBuffers( WindowInfo *w, GLint flags ) +{ + CRASSERT(w); + +#if 00 /*TEMPORARY - FOR TESTING SWAP LOCK*/ + if (1) { + /* random delay */ + int k = crRandInt(1000 * 100, 750*1000); + static int first = 1; + if (first) { + crRandAutoSeed(); + first = 0; + } + usleep(k); + } +#endif + + /* render_to_app_window: + * w->nativeWindow will only be non-zero if the + * render_spu.render_to_app_window option is true and + * MakeCurrent() recorded the nativeWindow handle in the WindowInfo + * structure. + */ + if (w->nativeWindow) { + render_spu.ws.glXSwapBuffers( w->visual->dpy, w->nativeWindow ); +#if 0 + MarkWindow(w); +#else + (void) MarkWindow; +#endif + } + else { + render_spu.ws.glXSwapBuffers( w->visual->dpy, w->window ); + } +} + +void renderspu_SystemReparentWindow(WindowInfo *window) +{ + Window parent; + + parent = render_spu_parent_window_id>0 ? render_spu_parent_window_id : + RootWindow(window->visual->dpy, window->visual->visual->screen); + + XReparentWindow(window->visual->dpy, window->window, parent, window->x, window->y); + XSync(window->visual->dpy, False); +} + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + +} + +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable) +{ + return cFunctions; +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c new file mode 100644 index 00000000..ec494a1f --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_init.c @@ -0,0 +1,623 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_mem.h" +#include "cr_spu.h" +#include "cr_error.h" +#include "cr_string.h" +#include "cr_url.h" +#include "cr_environment.h" +#include "renderspu.h" +#include <stdio.h> + +#ifdef RT_OS_DARWIN +# include <iprt/semaphore.h> +#endif /* RT_OS_DARWIN */ + +static SPUNamedFunctionTable _cr_render_table[1000]; + +SPUFunctions render_functions = { + NULL, /* CHILD COPY */ + NULL, /* DATA */ + _cr_render_table /* THE ACTUAL FUNCTIONS */ +}; + +RenderSPU render_spu; +uint64_t render_spu_parent_window_id = 0; + +#ifdef CHROMIUM_THREADSAFE +CRtsd _RenderTSD; +#endif + +static void swapsyncConnect(void) +{ + char hostname[4096], protocol[4096]; + unsigned short port; + + crNetInit(NULL, NULL); + + if (!crParseURL( render_spu.swap_master_url, protocol, hostname, + &port, 9876)) + crError( "Bad URL: %s", render_spu.swap_master_url ); + + if (render_spu.is_swap_master) + { + int a; + + render_spu.swap_conns = (CRConnection **)crAlloc( + render_spu.num_swap_clients*sizeof(CRConnection *)); + for (a=0; a<render_spu.num_swap_clients; a++) + { + render_spu.swap_conns[a] = crNetAcceptClient( protocol, hostname, port, + render_spu.swap_mtu, 1); + } + } + else + { + render_spu.swap_conns = (CRConnection **)crAlloc(sizeof(CRConnection *)); + + render_spu.swap_conns[0] = crNetConnectToServer(render_spu.swap_master_url, + port, render_spu.swap_mtu, 1); + if (!render_spu.swap_conns[0]) + crError("Failed connection"); + } +} + +#ifdef RT_OS_WINDOWS +static DWORD WINAPI renderSPUWindowThreadProc(void* unused) +{ + MSG msg; + BOOL bRet; + + (void) unused; + + /* Force system to create the message queue. + * Else, there's a chance that render spu will issue PostThreadMessage + * before this thread calls GetMessage for first time. + */ + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + crDebug("RenderSPU: Window thread started (%x)", crThreadID()); + SetEvent(render_spu.hWinThreadReadyEvent); + + while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0) + { + if (bRet == -1) + { + crError("RenderSPU: Window thread GetMessage failed (%x)", GetLastError()); + break; + } + else + { + if (msg.message == WM_VBOX_RENDERSPU_CREATE_WINDOW) + { + LPCREATESTRUCT pCS = (LPCREATESTRUCT) msg.lParam; + HWND hWnd; + WindowInfo *pWindow = (WindowInfo *)pCS->lpCreateParams; + + CRASSERT(msg.lParam && !msg.wParam && pCS->lpCreateParams); + + hWnd = CreateWindowEx(pCS->dwExStyle, pCS->lpszName, pCS->lpszClass, pCS->style, + pCS->x, pCS->y, pCS->cx, pCS->cy, + pCS->hwndParent, pCS->hMenu, pCS->hInstance, &render_spu); + + pWindow->hWnd = hWnd; + + SetEvent(render_spu.hWinThreadReadyEvent); + } + else if (msg.message == WM_VBOX_RENDERSPU_DESTROY_WINDOW) + { + CRASSERT(msg.lParam && !msg.wParam); + + DestroyWindow(((VBOX_RENDERSPU_DESTROY_WINDOW*) msg.lParam)->hWnd); + + SetEvent(render_spu.hWinThreadReadyEvent); + } + else + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + + render_spu.dwWinThreadId = 0; + + crDebug("RenderSPU: Window thread stopped (%x)", crThreadID()); + SetEvent(render_spu.hWinThreadReadyEvent); + + return 0; +} +#endif + +int renderspuDefaultCtxInit() +{ + GLint defaultWin, defaultCtx; + WindowInfo *windowInfo; + + /* + * Create the default window and context. Their indexes are zero and + * a client can use them without calling CreateContext or WindowCreate. + */ + crDebug("Render SPU: Creating default window (visBits=0x%x, id=0)", + render_spu.default_visual); + defaultWin = renderspuWindowCreateEx( NULL, render_spu.default_visual, CR_RENDER_DEFAULT_WINDOW_ID ); + if (defaultWin != CR_RENDER_DEFAULT_WINDOW_ID) { + crError("Render SPU: Couldn't get a double-buffered, RGB visual with Z!"); + return VERR_GENERAL_FAILURE; + } + crDebug( "Render SPU: WindowCreate returned %d (0=normal)", defaultWin ); + + crDebug("Render SPU: Creating default context, visBits=0x%x", + render_spu.default_visual ); + defaultCtx = renderspuCreateContextEx( NULL, render_spu.default_visual, CR_RENDER_DEFAULT_CONTEXT_ID, 0 ); + if (defaultCtx != CR_RENDER_DEFAULT_CONTEXT_ID) { + crError("Render SPU: failed to create default context!"); + return VERR_GENERAL_FAILURE; + } + + renderspuMakeCurrent( defaultWin, 0, defaultCtx ); + + /* Get windowInfo for the default window */ + windowInfo = (WindowInfo *) crHashtableSearch(render_spu.windowTable, CR_RENDER_DEFAULT_WINDOW_ID); + CRASSERT(windowInfo); + windowInfo->mapPending = GL_TRUE; + + return VINF_SUCCESS; +} + +static SPUFunctions * +renderSPUInit( int id, SPU *child, SPU *self, + unsigned int context_id, unsigned int num_contexts ) +{ + int numFuncs, numSpecial; + + const char * pcpwSetting; + int rc; + + (void) child; + (void) context_id; + (void) num_contexts; + + self->privatePtr = (void *) &render_spu; + +#ifdef CHROMIUM_THREADSAFE + crDebug("Render SPU: thread-safe"); + crInitTSD(&_RenderTSD); +#endif + + crMemZero(&render_spu, sizeof(render_spu)); + + render_spu.id = id; + renderspuSetVBoxConfiguration(&render_spu); + + if (render_spu.swap_master_url) + swapsyncConnect(); + + + /* Get our special functions. */ + numSpecial = renderspuCreateFunctions( _cr_render_table ); + +#ifdef RT_OS_WINDOWS + /* Start thread to create windows and process window messages */ + crDebug("RenderSPU: Starting windows serving thread"); + render_spu.hWinThreadReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!render_spu.hWinThreadReadyEvent) + { + crError("RenderSPU: Failed to create WinThreadReadyEvent! (%x)", GetLastError()); + return NULL; + } + + if (!CreateThread(NULL, 0, renderSPUWindowThreadProc, 0, 0, &render_spu.dwWinThreadId)) + { + crError("RenderSPU: Failed to start windows thread! (%x)", GetLastError()); + return NULL; + } + WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE); +#endif + + /* Get the OpenGL functions. */ + numFuncs = crLoadOpenGL( &render_spu.ws, _cr_render_table + numSpecial ); + if (numFuncs == 0) { + crError("The render SPU was unable to load the native OpenGL library"); + return NULL; + } + + numFuncs += numSpecial; + + render_spu.contextTable = crAllocHashtableEx(1, INT32_MAX); + render_spu.windowTable = crAllocHashtableEx(1, INT32_MAX); + + render_spu.dummyWindowTable = crAllocHashtable(); + + pcpwSetting = crGetenv("CR_RENDER_ENABLE_SINGLE_PRESENT_CONTEXT"); + if (pcpwSetting) + { + if (pcpwSetting[0] == '0') + pcpwSetting = NULL; + } + + if (pcpwSetting) + { + /** @todo need proper blitter synchronization, do not use so far! + * the problem is that rendering can be done in multiple thread: the main command (hgcm) thread and the redraw thread + * we currently use per-window synchronization, while we'll need a per-blitter synchronization if one blitter is used for multiple windows + * this is not done currently */ + crWarning("TODO: need proper blitter synchronization, do not use so far!"); + render_spu.blitterTable = crAllocHashtable(); + CRASSERT(render_spu.blitterTable); + } + else + render_spu.blitterTable = NULL; + + CRASSERT(render_spu.default_visual & CR_RGB_BIT); + + rc = renderspu_SystemInit(); + if (!RT_SUCCESS(rc)) + { + crError("renderspu_SystemInit failed rc %d", rc); + return NULL; + } +#ifdef USE_OSMESA + if (render_spu.use_osmesa) { + if (!crLoadOSMesa(&render_spu.OSMesaCreateContext, + &render_spu.OSMesaMakeCurrent, + &render_spu.OSMesaDestroyContext)) { + crError("Unable to load OSMesa library"); + } + } +#endif + +#ifdef DARWIN +# ifdef VBOX_WITH_COCOA_QT +# else /* VBOX_WITH_COCOA_QT */ + render_spu.hRootVisibleRegion = 0; + render_spu.currentBufferName = 1; + render_spu.uiDockUpdateTS = 0; + /* Create a mutex for synchronizing events from the main Qt thread & this + thread */ + RTSemFastMutexCreate(&render_spu.syncMutex); + /* Create our window groups */ + CreateWindowGroup(kWindowGroupAttrMoveTogether | kWindowGroupAttrLayerTogether | kWindowGroupAttrSharedActivation | kWindowGroupAttrHideOnCollapse | kWindowGroupAttrFixedLevel, &render_spu.pMasterGroup); + CreateWindowGroup(kWindowGroupAttrMoveTogether | kWindowGroupAttrLayerTogether | kWindowGroupAttrSharedActivation | kWindowGroupAttrHideOnCollapse | kWindowGroupAttrFixedLevel, &render_spu.pParentGroup); + /* Make the correct z-layering */ + SendWindowGroupBehind (render_spu.pParentGroup, render_spu.pMasterGroup); + /* and set the gParentGroup as parent for gMasterGroup. */ + SetWindowGroupParent (render_spu.pMasterGroup, render_spu.pParentGroup); + /* Install the event handlers */ + EventTypeSpec eventList[] = + { + {kEventClassVBox, kEventVBoxUpdateContext}, /* Update the context after show/size/move events */ + {kEventClassVBox, kEventVBoxBoundsChanged} /* Clip/Pos the OpenGL windows when the main window is changed in pos/size */ + }; + /* We need to process events from our main window */ + render_spu.hParentEventHandler = NewEventHandlerUPP(windowEvtHndlr); + InstallApplicationEventHandler (render_spu.hParentEventHandler, + GetEventTypeCount(eventList), eventList, + NULL, NULL); + render_spu.fInit = true; +# endif /* VBOX_WITH_COCOA_QT */ +#endif /* DARWIN */ + + rc = renderspuDefaultCtxInit(); + if (!RT_SUCCESS(rc)) + { + WARN(("renderspuDefaultCtxInit failed %d", rc)); + return NULL; + } + + /* + * Get the OpenGL extension functions. + * SIGH -- we have to wait until the very bitter end to load the + * extensions, because the context has to be bound before + * wglGetProcAddress will work correctly. No such issue with GLX though. + */ + numFuncs += crLoadOpenGLExtensions( &render_spu.ws, _cr_render_table + numFuncs ); + CRASSERT(numFuncs < 1000); + +#ifdef WINDOWS + /* + * Same problem as above, these are extensions so we need to + * load them after a context has been bound. As they're WGL + * extensions too, we can't simply tag them into the spu_loader. + * So we do them here for now. + * Grrr, NVIDIA driver uses EXT for GetExtensionsStringEXT, + * but ARB for others. Need further testing here.... + */ + render_spu.ws.wglGetExtensionsStringEXT = + (wglGetExtensionsStringEXTFunc_t) + render_spu.ws.wglGetProcAddress( "wglGetExtensionsStringEXT" ); + render_spu.ws.wglChoosePixelFormatEXT = + (wglChoosePixelFormatEXTFunc_t) + render_spu.ws.wglGetProcAddress( "wglChoosePixelFormatARB" ); + render_spu.ws.wglGetPixelFormatAttribivEXT = + (wglGetPixelFormatAttribivEXTFunc_t) + render_spu.ws.wglGetProcAddress( "wglGetPixelFormatAttribivARB" ); + render_spu.ws.wglGetPixelFormatAttribfvEXT = + (wglGetPixelFormatAttribfvEXTFunc_t) + render_spu.ws.wglGetProcAddress( "wglGetPixelFormatAttribfvARB" ); + + if (render_spu.ws.wglGetProcAddress("glCopyTexSubImage3D")) + { + _cr_render_table[numFuncs].name = crStrdup("CopyTexSubImage3D"); + _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glCopyTexSubImage3D"); + ++numFuncs; + crDebug("Render SPU: Found glCopyTexSubImage3D function"); + } + + if (render_spu.ws.wglGetProcAddress("glDrawRangeElements")) + { + _cr_render_table[numFuncs].name = crStrdup("DrawRangeElements"); + _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glDrawRangeElements"); + ++numFuncs; + crDebug("Render SPU: Found glDrawRangeElements function"); + } + + if (render_spu.ws.wglGetProcAddress("glTexSubImage3D")) + { + _cr_render_table[numFuncs].name = crStrdup("TexSubImage3D"); + _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glTexSubImage3D"); + ++numFuncs; + crDebug("Render SPU: Found glTexSubImage3D function"); + } + + if (render_spu.ws.wglGetProcAddress("glTexImage3D")) + { + _cr_render_table[numFuncs].name = crStrdup("TexImage3D"); + _cr_render_table[numFuncs].fn = (SPUGenericFunction) render_spu.ws.wglGetProcAddress("glTexImage3D"); + ++numFuncs; + crDebug("Render SPU: Found glTexImage3D function"); + } + + if (render_spu.ws.wglGetExtensionsStringEXT) { + crDebug("WGL - found wglGetExtensionsStringEXT\n"); + } + if (render_spu.ws.wglChoosePixelFormatEXT) { + crDebug("WGL - found wglChoosePixelFormatEXT\n"); + } +#endif + + render_spu.barrierHash = crAllocHashtable(); + + render_spu.cursorX = 0; + render_spu.cursorY = 0; + render_spu.use_L2 = 0; + + render_spu.gather_conns = NULL; + + numFuncs = renderspu_SystemPostprocessFunctions(_cr_render_table, numFuncs, RT_ELEMENTS(_cr_render_table)); + + crDebug("Render SPU: ---------- End of Init -------------"); + + return &render_functions; +} + +static void renderSPUSelfDispatch(SPUDispatchTable *self) +{ + crSPUInitDispatchTable( &(render_spu.self) ); + crSPUCopyDispatchTable( &(render_spu.self), self ); + + crSPUInitDispatchTable( &(render_spu.blitterDispatch) ); + crSPUCopyDispatchTable( &(render_spu.blitterDispatch), self ); + + render_spu.server = (CRServer *)(self->server); + + { + GLfloat version; + version = crStrToFloat((const char *) render_spu.ws.glGetString(GL_VERSION)); + + if (version>=2.f || crStrstr((const char*)render_spu.ws.glGetString(GL_EXTENSIONS), "GL_ARB_vertex_shader")) + { + GLint mu=0; + render_spu.self.GetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &mu); + crInfo("Render SPU: GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB=%i", mu); + } + } +} + + +static void DeleteContextCallback( void *data ) +{ + ContextInfo *context = (ContextInfo *) data; + renderspuContextMarkDeletedAndRelease(context); +} + +static void DeleteWindowCallback( void *data ) +{ + WindowInfo *window = (WindowInfo *) data; + renderspuWinTermOnShutdown(window); + renderspuWinRelease(window); +} + +static void DeleteBlitterCallback( void *data ) +{ + PCR_BLITTER pBlitter = (PCR_BLITTER) data; + CrBltTerm(pBlitter); + crFree(pBlitter); +} + +static void renderspuBlitterCleanupCB(unsigned long key, void *data1, void *data2) +{ + WindowInfo *window = (WindowInfo *) data1; + CRASSERT(window); + + renderspuVBoxPresentBlitterCleanup( window ); +} + + +static void renderspuDeleteBlitterCB(unsigned long key, void *data1, void *data2) +{ + CRHashTable *pTbl = (CRHashTable*)data2; + + crHashtableDelete( pTbl, key, NULL ); + + DeleteBlitterCallback(data1); +} + + +static void renderspuDeleteWindowCB(unsigned long key, void *data1, void *data2) +{ + CRHashTable *pTbl = (CRHashTable*)data2; + + crHashtableDelete( pTbl, key, NULL ); + + DeleteWindowCallback(data1); +} + +static void renderspuDeleteBarierCB(unsigned long key, void *data1, void *data2) +{ + CRHashTable *pTbl = (CRHashTable*)data2; + + crHashtableDelete( pTbl, key, NULL ); + + crFree(data1); +} + + +static void renderspuDeleteContextCB(unsigned long key, void *data1, void *data2) +{ + CRHashTable *pTbl = (CRHashTable*)data2; + + crHashtableDelete( pTbl, key, NULL ); + + DeleteContextCallback(data1); +} + +void renderspuCleanupBase(bool fDeleteTables) +{ + renderspuVBoxCompositorClearAll(); + + if (render_spu.blitterTable) + { + if (fDeleteTables) + { + crFreeHashtable(render_spu.blitterTable, DeleteBlitterCallback); + render_spu.blitterTable = NULL; + } + else + { + crHashtableWalk(render_spu.blitterTable, renderspuDeleteBlitterCB, render_spu.contextTable); + } + } + else + { + crHashtableWalk(render_spu.windowTable, renderspuBlitterCleanupCB, NULL); + + crHashtableWalk(render_spu.dummyWindowTable, renderspuBlitterCleanupCB, NULL); + } + + renderspuSetDefaultSharedContext(NULL); + + if (fDeleteTables) + { + crFreeHashtable(render_spu.contextTable, DeleteContextCallback); + render_spu.contextTable = NULL; + crFreeHashtable(render_spu.windowTable, DeleteWindowCallback); + render_spu.windowTable = NULL; + crFreeHashtable(render_spu.dummyWindowTable, DeleteWindowCallback); + render_spu.dummyWindowTable = NULL; + crFreeHashtable(render_spu.barrierHash, crFree); + render_spu.barrierHash = NULL; + } + else + { + crHashtableWalk(render_spu.contextTable, renderspuDeleteContextCB, render_spu.contextTable); + crHashtableWalk(render_spu.windowTable, renderspuDeleteWindowCB, render_spu.windowTable); + crHashtableWalk(render_spu.dummyWindowTable, renderspuDeleteWindowCB, render_spu.dummyWindowTable); + crHashtableWalk(render_spu.barrierHash, renderspuDeleteBarierCB, render_spu.barrierHash); + } +} + +static int renderSPUCleanup(void) +{ + renderspuCleanupBase(true); + +#ifdef RT_OS_DARWIN +# ifndef VBOX_WITH_COCOA_QT + render_spu.fInit = false; + DisposeEventHandlerUPP(render_spu.hParentEventHandler); + ReleaseWindowGroup(render_spu.pMasterGroup); + ReleaseWindowGroup(render_spu.pParentGroup); + if (render_spu.hRootVisibleRegion) + { + DisposeRgn(render_spu.hRootVisibleRegion); + render_spu.hRootVisibleRegion = 0; + } + render_spu.currentBufferName = 1; + render_spu.uiDockUpdateTS = 0; + RTSemFastMutexDestroy(render_spu.syncMutex); +# else /* VBOX_WITH_COCOA_QT */ +# endif /* VBOX_WITH_COCOA_QT */ +#endif /* RT_OS_DARWIN */ + +#ifdef RT_OS_WINDOWS + if (render_spu.dwWinThreadId) + { + HANDLE hNative; + + hNative = OpenThread(SYNCHRONIZE|THREAD_QUERY_INFORMATION|THREAD_TERMINATE, + false, render_spu.dwWinThreadId); + if (!hNative) + { + crWarning("Failed to get handle for window thread(%#x)", GetLastError()); + } + + if (PostThreadMessage(render_spu.dwWinThreadId, WM_QUIT, 0, 0)) + { + WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE); + + /*wait for os thread to actually finish*/ + if (hNative && WaitForSingleObject(hNative, 3000)==WAIT_TIMEOUT) + { + crDebug("Wait failed, terminating"); + if (!TerminateThread(hNative, 1)) + { + crWarning("TerminateThread failed"); + } + } + } + + if (hNative) + { + CloseHandle(hNative); + } + } + CloseHandle(render_spu.hWinThreadReadyEvent); + render_spu.hWinThreadReadyEvent = NULL; +#endif + + crUnloadOpenGL(); + +#ifdef CHROMIUM_THREADSAFE + crFreeTSD(&_RenderTSD); +#endif + + return 1; +} + + +extern SPUOptions renderSPUOptions[]; + +int SPULoad( char **name, char **super, SPUInitFuncPtr *init, + SPUSelfDispatchFuncPtr *self, SPUCleanupFuncPtr *cleanup, + SPUOptionsPtr *options, int *flags ) +{ + *name = "render"; + *super = NULL; + *init = renderSPUInit; + *self = renderSPUSelfDispatch; + *cleanup = renderSPUCleanup; + *options = renderSPUOptions; + *flags = (SPU_NO_PACKER|SPU_IS_TERMINAL|SPU_MAX_SERVERS_ZERO); + + return 1; +} + +DECLEXPORT(void) renderspuSetWindowId(uint64_t winId) +{ + render_spu_parent_window_id = winId; + crDebug("Set new parent window %p (no actual reparent performed)", winId); +} diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c b/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c new file mode 100644 index 00000000..5df60410 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_wgl.c @@ -0,0 +1,1741 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + + +#define WIN32_LEAN_AND_MEAN +#include <iprt/win/windows.h> + +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> + +#include "cr_environment.h" +#include "cr_error.h" +#include "cr_string.h" +#include "renderspu.h" +#include "cr_mem.h" + + +/* IAT patcher stuff */ +#define RVA2PTR(_t, _base, _off) ((_t*)(((uint8_t*)(_base)) + (_off))) + +int renderspuIatPatcherGetImportAddress(HMODULE hModule, LPCSTR pszLib, LPCSTR pszName, void** ppAdr) +{ + PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hModule; + PIMAGE_NT_HEADERS pNtHdr; + PIMAGE_IMPORT_DESCRIPTOR pImportDr; + DWORD rvaImport; + + crDebug("searching entry %s from %s", pszName, pszLib); + + *ppAdr = 0; + + if (pDosHdr->e_magic != IMAGE_DOS_SIGNATURE) + { + crWarning("invalid dos signature"); + return VERR_INVALID_HANDLE; + } + pNtHdr = RVA2PTR(IMAGE_NT_HEADERS, pDosHdr, pDosHdr->e_lfanew); + if (pNtHdr->Signature != IMAGE_NT_SIGNATURE) + { + crWarning("invalid nt signature"); + return VERR_INVALID_HANDLE; + } + rvaImport = pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + if (!rvaImport) + { + crWarning("no imports found"); + return VERR_NOT_FOUND; + } + pImportDr = RVA2PTR(IMAGE_IMPORT_DESCRIPTOR, pDosHdr, rvaImport); + + for ( ;pImportDr->TimeDateStamp != 0 || pImportDr->Name != 0; ++pImportDr) + { + DWORD rvaINT, rvaIAT; + PIMAGE_THUNK_DATA pINT, pIAT; + LPCSTR pszLibCur = RVA2PTR(char, pDosHdr, pImportDr->Name); + if (stricmp(pszLibCur, pszLib)) + continue; + + /* got the necessary lib! */ + crDebug("got info for lib"); + + rvaINT = pImportDr->OriginalFirstThunk; + rvaIAT = pImportDr->FirstThunk; + + if (!rvaINT || !rvaIAT) + { + crWarning("either rvaINT(0x%x) or rvaIAT(0x%x) are NULL, nothing found!", rvaINT, rvaIAT); + return VERR_NOT_FOUND; + } + + pINT = RVA2PTR(IMAGE_THUNK_DATA, pDosHdr, rvaINT); + pIAT = RVA2PTR(IMAGE_THUNK_DATA, pDosHdr, rvaIAT); + + for ( ; pINT->u1.AddressOfData; ++pINT, ++pIAT) + { + PIMAGE_IMPORT_BY_NAME pIbn; + + if (IMAGE_SNAP_BY_ORDINAL(pINT->u1.Ordinal)) + continue; + + pIbn = RVA2PTR(IMAGE_IMPORT_BY_NAME, pDosHdr, pINT->u1.AddressOfData); + + if (stricmp(pszName, (char*)pIbn->Name)) + continue; + + *ppAdr = &pIAT->u1.Function; + + crDebug("search succeeded!"); + return VINF_SUCCESS; + } + } + + crDebug("not found"); + return VERR_NOT_FOUND; +} + +int renderspuIatPatcherPatchEntry(void *pvEntry, void *pvValue, void **ppvOldVal) +{ + void **ppfn = (void**)pvEntry; + DWORD dwOldProtect = 0; + + if (!VirtualProtect(pvEntry, sizeof (pvEntry), PAGE_READWRITE, &dwOldProtect)) + { + crWarning("VirtualProtect 1 failed, %d", GetLastError()); + return VERR_ACCESS_DENIED; + } + + if (ppvOldVal) + *ppvOldVal = *ppfn; + *ppfn = pvValue; + + if (!VirtualProtect(pvEntry, sizeof (pvEntry), dwOldProtect, &dwOldProtect)) + { + crWarning("VirtualProtect 2 failed, %d.. ignoring", GetLastError()); + } + + return VINF_SUCCESS; +} + + +int renderspuIatPatcherPatchFunction(HMODULE hModule, LPCSTR pszLib, LPCSTR pszName, void* pfn) +{ + void* pAdr; + int rc = renderspuIatPatcherGetImportAddress(hModule, pszLib, pszName, &pAdr); + if (RT_FAILURE(rc)) + { + crDebug("renderspuIatPatcherGetImportAddress failed, %d", rc); + return rc; + } + + rc = renderspuIatPatcherPatchEntry(pAdr, pfn, NULL); + if (RT_FAILURE(rc)) + { + crWarning("renderspuIatPatcherPatchEntry failed, %d", rc); + return rc; + } + + return VINF_SUCCESS; +} + +/* patch */ +static HWND __stdcall renderspuAtiQuirk_GetForegroundWindow() +{ + crDebug("renderspuAtiQuirk_GetForegroundWindow"); + return NULL; +} + + +#define CRREG_MAXKEYNAME 8 +static int renderspuAtiQuirk_GetICDDriverList(char *pBuf, DWORD cbBuf, DWORD *pcbResult) +{ + static LPCSTR aValueNames[] = {"OpenGLVendorName", "OpenGLDriverName"}; + char *pBufPos = pBuf; + DWORD cbBufRemain = cbBuf, cbTotal = 0; + HKEY hKey, hSubkey; + DWORD dwIndex = 0; + int i; + int rc = VINF_SUCCESS; + char NameBuf[CRREG_MAXKEYNAME]; + LONG lRc; + + if (pcbResult) + *pcbResult = 0; + + lRc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}", + 0, /* reserved*/ + KEY_READ, + &hKey); + if (ERROR_SUCCESS != lRc) + { + crDebug("RegOpenKeyEx 1 failed, %d", lRc); + return VERR_OPEN_FAILED; + } + + for ( ; ; ++dwIndex) + { + lRc = RegEnumKeyA(hKey, dwIndex, NameBuf, CRREG_MAXKEYNAME); + if (lRc == ERROR_NO_MORE_ITEMS) + break; + if (lRc == ERROR_MORE_DATA) + continue; + if (lRc != ERROR_SUCCESS) + { + crWarning("RegEnumKeyA failed, %d", lRc); + continue; + } + + lRc = RegOpenKeyEx(hKey, + NameBuf, + 0, /* reserved*/ + KEY_READ, + &hSubkey); + if (ERROR_SUCCESS != lRc) + { + crDebug("RegOpenKeyEx 2 failed, %d", lRc); + RegCloseKey(hKey); + return VERR_OPEN_FAILED; + } + + for (i = 0; i < RT_ELEMENTS(aValueNames); ++i) + { + DWORD cbCur = cbBufRemain; + DWORD type; + lRc = RegQueryValueExA(hSubkey, aValueNames[i], NULL, /* reserved*/ + &type, + (PBYTE)pBufPos, &cbCur); + /* exclude second null termination */ + --cbCur; + + if (ERROR_MORE_DATA == lRc) + { + if (REG_MULTI_SZ != type) + { + crWarning("unexpected data type! %d", type); + continue; + } + rc = VERR_BUFFER_OVERFLOW; + pBufPos = NULL; + cbBufRemain = 0; + CRASSERT(cbCur > 0 && cbCur < UINT32_MAX/2); + cbTotal += cbCur; + continue; + } + if (ERROR_SUCCESS != lRc) + { + crDebug("RegQueryValueExA failed, %d", lRc); + continue; + } + + if (REG_MULTI_SZ != type) + { + crWarning("unexpected data type! %d", type); + continue; + } + + /* succeeded */ + CRASSERT(cbCur > 0 && cbCur < UINT32_MAX/2); + pBufPos += cbCur; + cbBufRemain -= cbCur; + cbTotal += cbCur; + CRASSERT(cbBufRemain < UINT32_MAX/2); + } + + RegCloseKey(hSubkey); + } + + RegCloseKey(hKey); + + if (cbTotal) + { + /* include second null termination */ + CRASSERT(!pBufPos || pBufPos[0] == '\0'); + ++cbTotal; + } + + if (pcbResult) + *pcbResult = cbTotal; + + return rc; +} + +static int renderspuAtiQuirk_ApplyForModule(LPCSTR pszAtiDll) +{ + int rc; + HMODULE hAtiDll; + + crDebug("renderspuAtiQuirk_ApplyForModule (%s)", pszAtiDll); + + hAtiDll = GetModuleHandleA(pszAtiDll); + if (!hAtiDll) + { + crDebug("GetModuleHandle failed, %d", GetLastError()); + return VERR_NOT_FOUND; + } + + rc = renderspuIatPatcherPatchFunction(hAtiDll, "user32.dll", "GetForegroundWindow", (void*)renderspuAtiQuirk_GetForegroundWindow); + if (RT_FAILURE(rc)) + { + crDebug("renderspuIatPatcherPatchFunction failed, %d", rc); + return rc; + } + + crDebug("renderspuAtiQuirk_ApplyForModule SUCCEEDED!"); + crInfo("ATI Fullscreen quirk patch SUCCEEDED!"); + + return VINF_SUCCESS; +} + +static LPCSTR renderspuRegMultiSzNextVal(LPCSTR pszBuf) +{ + pszBuf += strlen(pszBuf) + sizeof (pszBuf[0]); + + if (pszBuf[0] == '\0') + return NULL; + + return pszBuf; +} + +static LPCSTR renderspuRegMultiSzCurVal(LPCSTR pszBuf) +{ + if (pszBuf[0] == '\0') + return NULL; + + return pszBuf; +} + + +static int renderspuAtiQuirk_Apply() +{ + char aBuf[4096]; + DWORD cbResult = 0; + LPCSTR pszVal; + int rc; + + crDebug("renderspuAtiQuirk_Apply.."); + + rc = renderspuAtiQuirk_GetICDDriverList(aBuf, sizeof (aBuf), &cbResult); + if (RT_FAILURE(rc)) + { + crDebug("renderspuAtiQuirk_GetICDDriverList failed, rc(%d)", rc); + return rc; + } + + for (pszVal = renderspuRegMultiSzCurVal(aBuf); + pszVal; + pszVal = renderspuRegMultiSzNextVal(pszVal)) + { + renderspuAtiQuirk_ApplyForModule(pszVal); + } + + return VINF_SUCCESS; +} + +static GLboolean renderspuAtiQuirk_Needed() +{ + const char * pszString = render_spu.ws.glGetString(GL_VENDOR); + if (pszString && strstr(pszString, "ATI")) + return GL_TRUE; + pszString = render_spu.ws.glGetString(GL_RENDERER); + if (pszString && strstr(pszString, "ATI")) + return GL_TRUE; + return GL_FALSE; +} + +static void renderspuAtiQuirk_ChkApply() +{ + static GLboolean fChecked = GL_FALSE; + if (fChecked) + return; + + fChecked = GL_TRUE; + if (!renderspuAtiQuirk_Needed()) + return; + + crInfo("This is an ATI card, taking care of fullscreen.."); + + /* + * ATI WDDM-based graphics have an issue with rendering fullscreen. + * See public tickets #9775 & #9267 . + * Namely ATI drivers check whether ogl window is foreground and fullscreen + * and if so - do D3DKMTSetDisplayMode for ogl surface, + * which prevented any other data from being displayed, no matter what. + * + * Here we check whether we're using an ATI card and if so, patch the ogl ICD driver's IAT + * to replace GetForegroundWindow reference with our renderspuAtiQuirk_GetForegroundWindow, + * which always returns NULL. + */ + renderspuAtiQuirk_Apply(); +} + +#define WINDOW_NAME window->title + +static BOOL +bSetupPixelFormat( HDC hdc, GLbitfield visAttribs ); + +GLboolean renderspu_SystemInitVisual( VisualInfo *visual ) +{ + if (visual->visAttribs & CR_PBUFFER_BIT) { + crWarning("Render SPU: PBuffers not support on Windows yet."); + } + + /* In the windows world, we need a window before a context. + * Use the device_context as a marker to do just that */ + + return TRUE; +} + +void renderspu_SystemDestroyWindow( WindowInfo *window ) +{ + VBOX_RENDERSPU_DESTROY_WINDOW vrdw; + + CRASSERT(window); + + /*DestroyWindow( window->hWnd );*/ + + vrdw.hWnd = window->hWnd; + + if (render_spu.dwWinThreadId) + { + PostThreadMessage(render_spu.dwWinThreadId, WM_VBOX_RENDERSPU_DESTROY_WINDOW, 0, (LPARAM) &vrdw); + WaitForSingleObject(render_spu.hWinThreadReadyEvent, INFINITE); + } + else + { + crError("Render SPU: window thread is not running"); + } + + window->hWnd = NULL; + window->visual = NULL; + if (window->hRgn) + { + DeleteObject(window->hRgn); + window->hRgn = NULL; + } +} + +static LONG WINAPI +MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + /* int w,h; */ + + switch ( uMsg ) { + case WM_PAINT: + { + WindowInfo *pWindow = (WindowInfo *)GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (pWindow) + { + const struct VBOXVR_SCR_COMPOSITOR * pCompositor; + + pCompositor = renderspuVBoxCompositorAcquire(pWindow); + if (pCompositor) + { + HDC hDC; + PAINTSTRUCT Paint; + + Assert(pWindow->device_context); + hDC = BeginPaint(pWindow->hWnd, &Paint); + if (hDC) + { + BOOL bRc; + pWindow->redraw_device_context = hDC; + + renderspuVBoxPresentCompositionGeneric(pWindow, pCompositor, NULL, 1, true); + + bRc = EndPaint(pWindow->hWnd, &Paint); + + pWindow->redraw_device_context = NULL; + + renderspuVBoxCompositorRelease(pWindow); + + if (!bRc) + { + DWORD winEr = GetLastError(); + crWarning("EndPaint failed, winEr %d", winEr); + } + } + else + { + DWORD winEr = GetLastError(); + crWarning("BeginPaint failed, winEr %d", winEr); + } + } + } + break; + } + case WM_SIZE: + /* w = LOWORD( lParam ); + * h = HIWORD( lParam ); */ + + /* glViewport( 0, 0, w, h ); */ +#if 0 + glViewport( -render_spu.mural_x, -render_spu.mural_y, + render_spu.mural_width, render_spu.mural_height ); + glScissor( -render_spu.mural_x, -render_spu.mural_y, + render_spu.mural_width, render_spu.mural_height ); +#endif + break; + + case WM_CLOSE: + crWarning( "Render SPU: caught WM_CLOSE -- quitting." ); + exit( 0 ); + break; + + case WM_DESTROY: + crDebug("Render SPU: caught WM_DESTROY for our window %x", hWnd); + break; + + case WM_NCHITTEST: + crDebug("WM_NCHITTEST"); + return HTNOWHERE; + } + + return DefWindowProc( hWnd, uMsg, wParam, lParam ); +} + +static BOOL +bSetupPixelFormatEXT( HDC hdc, GLbitfield visAttribs) +{ + PIXELFORMATDESCRIPTOR ppfd; + int pixelFormat; + int attribList[100]; + float fattribList[] = { 0.0, 0.0 }; + int numFormats; + int i = 0; + BOOL vis; + + CRASSERT(visAttribs & CR_RGB_BIT); /* anybody need color index */ + + crWarning("Render SPU: Using WGL_EXT_pixel_format to select visual ID."); + + attribList[i++] = WGL_DRAW_TO_WINDOW_EXT; + attribList[i++] = GL_TRUE; + attribList[i++] = WGL_ACCELERATION_EXT; + attribList[i++] = WGL_FULL_ACCELERATION_EXT; + attribList[i++] = WGL_COLOR_BITS_EXT; + attribList[i++] = 24; + attribList[i++] = WGL_RED_BITS_EXT; + attribList[i++] = 1; + attribList[i++] = WGL_GREEN_BITS_EXT; + attribList[i++] = 1; + attribList[i++] = WGL_BLUE_BITS_EXT; + attribList[i++] = 1; + + crWarning("Render SPU: Visual chosen is... RGB"); + + if (visAttribs & CR_ALPHA_BIT) + { + attribList[i++] = WGL_ALPHA_BITS_EXT; + attribList[i++] = 1; + crWarning("A"); + } + + crWarning(", "); + + if (visAttribs & CR_DOUBLE_BIT) { + attribList[i++] = WGL_DOUBLE_BUFFER_EXT; + attribList[i++] = GL_TRUE; + crWarning("DB, "); + } + + if (visAttribs & CR_STEREO_BIT) { + attribList[i++] = WGL_STEREO_EXT; + attribList[i++] = GL_TRUE; + crWarning("Stereo, "); + } + + if (visAttribs & CR_DEPTH_BIT) + { + attribList[i++] = WGL_DEPTH_BITS_EXT; + attribList[i++] = 1; + crWarning("Z, "); + } + + if (visAttribs & CR_STENCIL_BIT) + { + attribList[i++] = WGL_STENCIL_BITS_EXT; + attribList[i++] = 1; + crWarning("Stencil, "); + } + + if (visAttribs & CR_ACCUM_BIT) + { + attribList[i++] = WGL_ACCUM_RED_BITS_EXT; + attribList[i++] = 1; + attribList[i++] = WGL_ACCUM_GREEN_BITS_EXT; + attribList[i++] = 1; + attribList[i++] = WGL_ACCUM_BLUE_BITS_EXT; + attribList[i++] = 1; + crWarning("Accum, "); + if (visAttribs & CR_ALPHA_BIT) + { + attribList[i++] = WGL_ACCUM_ALPHA_BITS_EXT; + attribList[i++] = 1; + crWarning("Accum Alpha, "); + } + } + + if (visAttribs & CR_MULTISAMPLE_BIT) + { + attribList[i++] = WGL_SAMPLE_BUFFERS_EXT; + attribList[i++] = 1; + attribList[i++] = WGL_SAMPLES_EXT; + attribList[i++] = 4; + crWarning("Multisample, "); + } + + crWarning("\n"); + + /* End the list */ + attribList[i++] = 0; + attribList[i++] = 0; + + vis = render_spu.ws.wglChoosePixelFormatEXT( hdc, attribList, fattribList, 1, &pixelFormat, &numFormats); + + crDebug("Render SPU: wglChoosePixelFormatEXT (vis 0x%x, LastError 0x%x, pixelFormat 0x%x", vis, GetLastError(), pixelFormat); + +#ifdef VBOX_CR_SERVER_FORCE_WGL + render_spu.ws.wglSetPixelFormat( hdc, pixelFormat, &ppfd ); +#else + SetPixelFormat( hdc, pixelFormat, &ppfd ); +#endif + + crDebug("Render SPU: wglSetPixelFormat (Last error 0x%x)", GetLastError()); + + return vis; +} + +static BOOL +bSetupPixelFormatNormal( HDC hdc, GLbitfield visAttribs ) +{ + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), /* size of this pfd */ + 1, /* version number */ + PFD_DRAW_TO_WINDOW | /* support window */ + PFD_SUPPORT_OPENGL, /* support OpenGL */ + PFD_TYPE_RGBA, /* RGBA type */ + 24, /* 24-bit color depth */ + 0, 0, 0, 0, 0, 0, /* color bits ignored */ + 0, /* no alpha buffer */ + 0, /* shift bit ignored */ + 0, /* no accumulation buffer */ + 0, 0, 0, 0, /* accum bits ignored */ + 0, /* set depth buffer */ + 0, /* set stencil buffer */ + 0, /* no auxiliary buffer */ + PFD_MAIN_PLANE, /* main layer */ + 0, /* reserved */ + 0, 0, 0 /* layer masks ignored */ + }; + PIXELFORMATDESCRIPTOR *ppfd = &pfd; + char s[1000]; + GLbitfield b = 0; + int pixelformat; + + renderspuMakeVisString( visAttribs, s ); + + crDebug( "Render SPU: WGL wants these visual capabilities: %s", s); + + /* These really come into play with sort-last configs */ + if (visAttribs & CR_DEPTH_BIT) + ppfd->cDepthBits = 24; + if (visAttribs & CR_ACCUM_BIT) + ppfd->cAccumBits = 16; + if (visAttribs & CR_RGB_BIT) + ppfd->cColorBits = 24; + if (visAttribs & CR_STENCIL_BIT) + ppfd->cStencilBits = 8; + if (visAttribs & CR_ALPHA_BIT) + ppfd->cAlphaBits = 8; + if (visAttribs & CR_DOUBLE_BIT) + ppfd->dwFlags |= PFD_DOUBLEBUFFER; + if (visAttribs & CR_STEREO_BIT) + ppfd->dwFlags |= PFD_STEREO; + + /* + * We call the wgl functions directly if the SPU was loaded + * by our faker library, otherwise we have to call the GDI + * versions. + */ +#ifdef VBOX_CR_SERVER_FORCE_WGL + if (crGetenv( "CR_WGL_DO_NOT_USE_GDI" ) != NULL) + { + pixelformat = render_spu.ws.wglChoosePixelFormat( hdc, ppfd ); + /* doing this twice is normal Win32 magic */ + pixelformat = render_spu.ws.wglChoosePixelFormat( hdc, ppfd ); + if ( pixelformat == 0 ) + { + crError( "render_spu.ws.wglChoosePixelFormat failed" ); + } + if ( !render_spu.ws.wglSetPixelFormat( hdc, pixelformat, ppfd ) ) + { + crError( "render_spu.ws.wglSetPixelFormat failed" ); + } + + render_spu.ws.wglDescribePixelFormat( hdc, pixelformat, sizeof(*ppfd), ppfd ); + } + else +#endif + { + /* Okay, we were loaded manually. Call the GDI functions. */ + pixelformat = ChoosePixelFormat( hdc, ppfd ); + /* doing this twice is normal Win32 magic */ + pixelformat = ChoosePixelFormat( hdc, ppfd ); + if ( pixelformat == 0 ) + { + crError( "ChoosePixelFormat failed" ); + } + if ( !SetPixelFormat( hdc, pixelformat, ppfd ) ) + { + crError( "SetPixelFormat failed (Error 0x%x)", GetLastError() ); + } + + DescribePixelFormat( hdc, pixelformat, sizeof(*ppfd), ppfd ); + } + + + if (ppfd->cDepthBits > 0) + b |= CR_DEPTH_BIT; + if (ppfd->cAccumBits > 0) + b |= CR_ACCUM_BIT; + if (ppfd->cColorBits > 8) + b |= CR_RGB_BIT; + if (ppfd->cStencilBits > 0) + b |= CR_STENCIL_BIT; + if (ppfd->cAlphaBits > 0) + b |= CR_ALPHA_BIT; + if (ppfd->dwFlags & PFD_DOUBLEBUFFER) + b |= CR_DOUBLE_BIT; + if (ppfd->dwFlags & PFD_STEREO) + b |= CR_STEREO_BIT; + + renderspuMakeVisString( b, s ); + + crDebug( "Render SPU: WGL chose these visual capabilities: %s", s); + return TRUE; +} + +static BOOL +bSetupPixelFormat( HDC hdc, GLbitfield visAttribs ) +{ + /* According to http://www.opengl.org/resources/faq/technical/mswindows.htm + we shouldn't be using wgl functions to setup pixel formats unless we're loading ICD driver. + In particular, bSetupPixelFormatEXT bugs with Intel drivers. + */ + return bSetupPixelFormatNormal(hdc, visAttribs); +} + +GLboolean renderspu_SystemCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) +{ + HDESK desktop; + HINSTANCE hinstance; + WNDCLASS wc; + DWORD window_style; + int window_plus_caption_width; + int window_plus_caption_height; + + window->hRgn = NULL; + window->visual = visual; + window->nativeWindow = 0; + + if ( render_spu.use_L2 ) + { + crWarning( "Going fullscreen because we think we're using Lightning-2." ); + render_spu.fullscreen = 1; + } + + /* + * Begin Windows / WGL code + */ + + hinstance = GetModuleHandle( NULL ); + if (!hinstance) + { + crError( "Render SPU: Couldn't get a handle to my module." ); + return GL_FALSE; + } + crDebug( "Render SPU: Got the module handle: 0x%x", hinstance ); + + /* If we were launched from a service, telnet, or rsh, we need to + * get the input desktop. */ + + desktop = OpenInputDesktop( 0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE ); + + if ( !desktop ) + { + crError( "Render SPU: Couldn't acquire input desktop" ); + return GL_FALSE; + } + crDebug( "Render SPU: Got the desktop: 0x%x", desktop ); + + if ( !SetThreadDesktop( desktop ) ) + { + /* If this function fails, it's probably because + * it's already been called (i.e., the render SPU + * is bolted to an application?) */ + + /*crError( "Couldn't set thread to input desktop" ); */ + } + crDebug( "Render SPU: Set the thread desktop -- this might have failed." ); + + if ( !GetClassInfo(hinstance, WINDOW_NAME, &wc) ) + { + wc.style = CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hinstance; + wc.hIcon = LoadIcon( NULL, IDI_APPLICATION ); + wc.hCursor = LoadCursor( NULL, IDC_ARROW ); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = WINDOW_NAME; + + if ( !RegisterClass( &wc ) ) + { + crError( "Render SPU: Couldn't register window class -- you're not trying " + "to do multi-pipe stuff on windows, are you?\n\nNote --" + "This error message is from 1997 and probably doesn't make" + "any sense any more, but it's nostalgic for Humper." ); + return GL_FALSE; + } + crDebug( "Render SPU: Registered the class" ); + } + crDebug( "Render SPU: Got the class information" ); + + /* Full screen window should be a popup (undecorated) window */ +#if 1 + window_style = ( render_spu.fullscreen ? WS_POPUP : WS_CAPTION ); +#else + window_style = ( WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN ); + window_style |= WS_SYSMENU; +#endif + + crDebug( "Render SPU: Fullscreen: %s", render_spu.fullscreen ? "yes" : "no"); + + if ( render_spu.fullscreen ) + { +#if 0 + + int smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ); + int smCyFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ) + 1; + int smCyCaption = GetSystemMetrics( SM_CYCAPTION ); + + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; + + crDebug( "Render SPU: Window Dims: %d, %d", window->BltInfo.width, window->BltInfo.height ); + + window->x = render_spu->defaultX - smCxFixedFrame - 1; + window->y = render_spu->defaultY - smCyFixedFrame - smCyCaption; + + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; + +#else + /* Since it's undecorated, we don't have to do anything fancy + * with these parameters. */ + + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; + window->x = 0; + window->y = 0; + window_plus_caption_width = window->BltInfo.width; + window_plus_caption_height = window->BltInfo.height; + +#endif + } + else + { + /* CreateWindow takes the size of the entire window, so we add + * in the size necessary for the frame and the caption. */ + + int smCxFixedFrame, smCyFixedFrame, smCyCaption; + smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ); + crDebug( "Render SPU: Got the X fixed frame" ); + smCyFixedFrame = GetSystemMetrics( SM_CYFIXEDFRAME ); + crDebug( "Render SPU: Got the Y fixed frame" ); + smCyCaption = GetSystemMetrics( SM_CYCAPTION ); + crDebug( "Render SPU: Got the Caption " ); + + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; + + window->x = render_spu.defaultX - smCxFixedFrame; + window->y = render_spu.defaultY - smCyFixedFrame - smCyCaption; + } + + crDebug( "Render SPU: Creating the window: (%d,%d), (%d,%d)", render_spu.defaultX, render_spu.defaultY, window_plus_caption_width, window_plus_caption_height ); + window->hWnd = CreateWindow( WINDOW_NAME, WINDOW_NAME, + window_style, + window->x, window->y, + window_plus_caption_width, + window_plus_caption_height, + NULL, NULL, hinstance, &render_spu ); + + if ( !window->hWnd ) + { + crError( "Render SPU: Create Window failed! That's almost certainly terrible." ); + return GL_FALSE; + } + + window->visible = showIt; + + if (!showIt) + { + renderspu_SystemShowWindow( window, 0 ); + if (window->BltInfo.height <= 0 || window->BltInfo.width <= 0) + { + renderspu_SystemWindowSize(window, + window->BltInfo.width > 0 ? window->BltInfo.width : 4, + window->BltInfo.height > 0 ? window->BltInfo.height : 4); + } + } + else + { + crDebug( "Render SPU: Showing the window" ); + crDebug("renderspu_SystemCreateWindow: showwindow: %x", window->hWnd); + } + + CRASSERT(!window->visible == !showIt); + + /* Intel drivers require a window to be visible for proper 3D rendering, + * so set it visible and handle the visibility with visible regions (see below) */ + ShowWindow( window->hWnd, SW_SHOWNORMAL ); + + SetForegroundWindow( window->hWnd ); + + SetWindowPos( window->hWnd, HWND_TOP, window->x, window->y, + window_plus_caption_width, window_plus_caption_height, + ( render_spu.fullscreen ? (SWP_SHOWWINDOW | + SWP_NOSENDCHANGING | + SWP_NOREDRAW | + SWP_NOACTIVATE ) : + 0 ) ); + + if ( render_spu.fullscreen ) + ShowCursor( FALSE ); + + window->device_context = GetDC( window->hWnd ); + if (!window->device_context) + { + DWORD winEr = GetLastError(); + crWarning("GetDC failed, winEr %d", winEr); + } + + crDebug( "Render SPU: Got the DC: 0x%x", window->device_context ); + + if ( !bSetupPixelFormat( window->device_context, visual->visAttribs ) ) + { + crError( "Render SPU: Couldn't set up the device context! Yikes!" ); + return GL_FALSE; + } + + return GL_TRUE; +} + +GLboolean renderspu_SystemVBoxCreateWindow( VisualInfo *visual, GLboolean showIt, WindowInfo *window ) +{ +#if 0 + HDESK desktop; +#endif + HINSTANCE hinstance; + WNDCLASS wc; + DWORD window_style; + int window_plus_caption_width; + int window_plus_caption_height; + + window->hRgn = NULL; + window->visual = visual; + window->nativeWindow = 0; + + if ( render_spu.use_L2 ) + { + crWarning( "Going fullscreen because we think we're using Lightning-2." ); + render_spu.fullscreen = 1; + } + + /* + * Begin Windows / WGL code + */ + + hinstance = GetModuleHandle( NULL ); + if (!hinstance) + { + crError( "Render SPU: Couldn't get a handle to my module." ); + return GL_FALSE; + } + crDebug( "Render SPU: Got the module handle: 0x%x", hinstance ); + +#if 0 + /* If we were launched from a service, telnet, or rsh, we need to + * get the input desktop. */ + + desktop = OpenInputDesktop( 0, FALSE, + DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | + DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL | + DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS | + DESKTOP_SWITCHDESKTOP | GENERIC_WRITE ); + + if ( !desktop ) + { + crError( "Render SPU: Couldn't acquire input desktop" ); + return GL_FALSE; + } + crDebug( "Render SPU: Got the desktop: 0x%x", desktop ); + + if ( !SetThreadDesktop( desktop ) ) + { + /* If this function fails, it's probably because + * it's already been called (i.e., the render SPU + * is bolted to an application?) */ + + /*crError( "Couldn't set thread to input desktop" ); */ + } + crDebug( "Render SPU: Set the thread desktop -- this might have failed." ); +#endif + + if ( !GetClassInfo(hinstance, WINDOW_NAME, &wc) ) + { + wc.style = CS_OWNDC; // | CS_PARENTDC; + wc.lpfnWndProc = (WNDPROC) MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hinstance; + wc.hIcon = NULL; //LoadIcon( NULL, IDI_APPLICATION ); + wc.hCursor = NULL; //LoadCursor( NULL, IDC_ARROW ); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = WINDOW_NAME; + + if ( !RegisterClass( &wc ) ) + { + crError( "Render SPU: Couldn't register window class -- you're not trying " + "to do multi-pipe stuff on windows, are you?\n\nNote --" + "This error message is from 1997 and probably doesn't make" + "any sense any more, but it's nostalgic for Humper." ); + return GL_FALSE; + } + crDebug( "Render SPU: Registered the class" ); + } + crDebug( "Render SPU: Got the class information" ); + + /* Full screen window should be a popup (undecorated) window */ +#if 1 + window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED; + if (render_spu_parent_window_id) + { + window_style |= WS_CHILD; + } +#else + window_style = ( WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN ); + window_style |= WS_SYSMENU; +#endif + + crDebug( "Render SPU: Fullscreen: %s", render_spu.fullscreen ? "yes" : "no"); + + if ( render_spu.fullscreen ) + { +#if 0 + + int smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ); + int smCyFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ) + 1; + int smCyCaption = GetSystemMetrics( SM_CYCAPTION ); + + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; + + crDebug( "Render SPU: Window Dims: %d, %d", window->BltInfo.width, window->BltInfo.height ); + + window->x = render_spu->defaultX - smCxFixedFrame - 1; + window->y = render_spu->defaultY - smCyFixedFrame - smCyCaption; + + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; + +#else + /* Since it's undecorated, we don't have to do anything fancy + * with these parameters. */ + + window->BltInfo.width = GetSystemMetrics( SM_CXSCREEN ) ; + window->BltInfo.height = GetSystemMetrics( SM_CYSCREEN ) ; + window->x = 0; + window->y = 0; + window_plus_caption_width = window->BltInfo.width; + window_plus_caption_height = window->BltInfo.height; + +#endif + } + else + { + /* CreateWindow takes the size of the entire window, so we add + * in the size necessary for the frame and the caption. */ + int smCxFixedFrame, smCyFixedFrame, smCyCaption; + smCxFixedFrame = GetSystemMetrics( SM_CXFIXEDFRAME ); + crDebug( "Render SPU: Got the X fixed frame" ); + smCyFixedFrame = GetSystemMetrics( SM_CYFIXEDFRAME ); + crDebug( "Render SPU: Got the Y fixed frame" ); + smCyCaption = GetSystemMetrics( SM_CYCAPTION ); + crDebug( "Render SPU: Got the Caption " ); + + window_plus_caption_width = window->BltInfo.width + 2 * smCxFixedFrame; + window_plus_caption_height = window->BltInfo.height + 2 * smCyFixedFrame + smCyCaption; + + window->x = render_spu.defaultX; + window->y = render_spu.defaultY; + } + + crDebug( "Render SPU: Creating the window: (%d,%d), (%d,%d)", render_spu.defaultX, render_spu.defaultY, window_plus_caption_width, window_plus_caption_height ); + /*window->hWnd = CreateWindowEx( WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY, + WINDOW_NAME, WINDOW_NAME, + window_style, + window->x, window->y, + window->BltInfo.width, + window->BltInfo.height, + (void*) render_spu_parent_window_id, NULL, hinstance, &render_spu );*/ + { + CREATESTRUCT cs; + + cs.lpCreateParams = window; + + cs.dwExStyle = WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY; + cs.lpszName = WINDOW_NAME; + cs.lpszClass = WINDOW_NAME; + cs.style = window_style; + cs.x = window->x; + cs.y = window->y; + cs.cx = window->BltInfo.width; + cs.cy = window->BltInfo.height; + cs.hwndParent = (void*) render_spu_parent_window_id; + cs.hMenu = NULL; + cs.hInstance = hinstance; + + if (render_spu.dwWinThreadId) + { + DWORD res; + int cnt=0; + + if (!PostThreadMessage(render_spu.dwWinThreadId, WM_VBOX_RENDERSPU_CREATE_WINDOW, 0, (LPARAM) &cs)) + { + crError("Render SPU: PostThreadMessage failed with %i", GetLastError()); + return GL_FALSE; + } + + do + { + res = WaitForSingleObject(render_spu.hWinThreadReadyEvent, 1000); + cnt++; + } + while ((res!=WAIT_OBJECT_0) && (cnt<10)); + + crDebug("Render SPU: window thread waited %i secs", cnt); + + if (res!=WAIT_OBJECT_0) + { + crError("Render SPU: window thread not responded after %i tries", cnt); + return GL_FALSE; + } + } + else + { + crError("Render SPU: window thread is not running"); + return GL_FALSE; + } + } + + if ( !window->hWnd ) + { + crError( "Render SPU: Create Window failed! That's almost certainly terrible." ); + return GL_FALSE; + } + + window->visible = 1; + + if (!showIt) + { + renderspu_SystemShowWindow( window, 0 ); + if (window->BltInfo.height <= 0 || window->BltInfo.width <= 0) + { + renderspu_SystemWindowSize(window, + window->BltInfo.width > 0 ? window->BltInfo.width : 4, + window->BltInfo.height > 0 ? window->BltInfo.height : 4); + } + } + else + { +#ifdef DEBUG_misha + crWarning( "Render SPU: Showing the window" ); +#else + crDebug( "Render SPU: Showing the window" ); +#endif + crDebug("renderspu_SystemCreateWindow: showwindow: %x", window->hWnd); + } + + CRASSERT(!window->visible == !showIt); + + /* Intel drivers require a window to be visible for proper 3D rendering, + * so set it visible and handle the visibility with visible regions (see below) */ + if (window->BltInfo.Base.id != CR_RENDER_DEFAULT_WINDOW_ID) + { + ShowWindow( window->hWnd, SW_SHOWNORMAL ); + } + else + { + CRASSERT(!showIt); + /* dummy window is always hidden in any way */ + } + + //SetForegroundWindow( visual->hWnd ); + + SetWindowPos( window->hWnd, HWND_TOP, window->x, window->y, + window->BltInfo.width, window->BltInfo.height, + ( render_spu.fullscreen ? + (SWP_SHOWWINDOW | SWP_NOSENDCHANGING | SWP_NOREDRAW | SWP_NOACTIVATE ) : SWP_NOACTIVATE + ) ); + crDebug("Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, + window->x, window->y, window->BltInfo.width, window->BltInfo.height); + + if ( render_spu.fullscreen ) + ShowCursor( FALSE ); + + window->device_context = GetDC( window->hWnd ); + if (!window->device_context) + { + DWORD winEr = GetLastError(); + crWarning("GetDC failed, winEr %d", winEr); + } + + crDebug( "Render SPU: Got the DC: 0x%x", window->device_context ); + + if ( !bSetupPixelFormat( window->device_context, visual->visAttribs ) ) + { + crError( "Render SPU: Couldn't set up the device context! Yikes!" ); + return GL_FALSE; + } + + /* set the window pointer data at the last step to ensure our WM_PAINT callback does not do anything until we are fully initialized */ +#ifdef RT_STRICT + SetLastError(NO_ERROR); +#endif + { + LONG_PTR oldVal = SetWindowLongPtr(window->hWnd, GWLP_USERDATA, (LONG_PTR)window); + Assert(!oldVal && GetLastError() == NO_ERROR); RT_NOREF_PV(oldVal); + } + + return GL_TRUE; +} + +static void renderspuWindowRgnApply(WindowInfo *window) +{ + HRGN hRgn = window->hRgn; + if (hRgn) + { + /* note: according to the docs, SetWindowRgn owns the regions after it is called, + * and the regions will be freed automatically when needed, + * i.e. the caller should not do that. + * this is why we need to make a copy of the regions to be passed in */ + + int result; + hRgn = CreateRectRgn(0, 0, 0, 0); + if (!hRgn) + { + WARN(("CreateRectRgn failed")); + return; + } + + result = CombineRgn(hRgn, window->hRgn, NULL, RGN_COPY); + if (result == ERROR) + { + WARN(("CombineRgn failed")); + return; + } + } + + SetWindowRgn(window->hWnd, hRgn, true); +} + +/* Either show or hide the render SPU's window. */ +void renderspu_SystemShowWindow( WindowInfo *window, GLboolean showIt ) +{ + if (showIt) + { + crDebug("SHOW renderspu_SystemShowWindow: %x", window->hWnd); + renderspuWindowRgnApply(window); + } + else + { + HRGN hRgn; + crDebug("HIDE renderspu_SystemShowWindow: %x", window->hWnd); + hRgn = CreateRectRgn(0, 0, 0, 0); + SetWindowRgn(window->hWnd, hRgn, true); + /* note: according to the docs, SetWindowRgn owns the regions after it is called, + * and the regions will be freed automatically when needed, + * i.e. the caller should not do that */ + } + window->visible = showIt; +} + +void renderspu_SystemVBoxPresentComposition( WindowInfo *window, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry ) +{ + /* The !render_spu.force_present_main_thread code flow is actually inspired + * by cocoa backend impl, here it forces rendering in WndProc, i.e. main + * thread. It defaults to 0, because it is for debugging and working around + * bugs. In principle would need root cause investigation. */ + if (!render_spu.force_present_main_thread) + { + const struct VBOXVR_SCR_COMPOSITOR *pCompositor; + /* we do not want to be blocked with the GUI thread here, so only draw here if we are really able to do that w/o blocking */ + int rc = renderspuVBoxCompositorTryAcquire(window, &pCompositor); + if (RT_SUCCESS(rc)) + { + renderspuVBoxPresentCompositionGeneric(window, pCompositor, pChangedEntry, 0, false); + renderspuVBoxCompositorRelease(window); + } + else if (rc != VERR_SEM_BUSY) + { + /* this is somewhat we do not expect */ + crWarning("renderspuVBoxCompositorTryAcquire failed rc %d", rc); + } + } + + { + render_spu.self.Flush(); + renderspuVBoxPresentBlitterEnsureCreated(window, 0); + RedrawWindow(window->hWnd, NULL, NULL, RDW_INTERNALPAINT); + } +} + +GLboolean renderspu_SystemCreateContext( VisualInfo *visual, ContextInfo *context, ContextInfo *sharedContext ) +{ + (void) sharedContext; + context->visual = visual; + + /* Found a visual, so we're o.k. to create the context now */ + if (0/*visual->device_context*/) { + + //crDebug( "Render SPU: Using the DC: 0x%x", visual->device_context ); + + //context->hRC = render_spu.ws.wglCreateContext( visual->device_context ); + if (!context->hRC) + { + crError( "Render SPU: wglCreateContext failed (error 0x%x)", GetLastError() ); + return GL_FALSE; + } + } else { + crDebug( "Render SPU: Delaying DC creation " ); + context->hRC = NULL; /* create it later in makecurrent */ + } + + + return GL_TRUE; +} + +void renderspu_SystemDestroyContext( ContextInfo *context ) +{ + render_spu.ws.wglDeleteContext( context->hRC ); + context->hRC = NULL; +} + +static GLboolean renderspuChkActivateSharedContext(ContextInfo *sharedContext) +{ + WindowInfo *window; + + if (sharedContext->hRC) + return GL_TRUE; + + CRASSERT(sharedContext->BltInfo.Base.id); + + if (sharedContext->shared) + renderspuChkActivateSharedContext(sharedContext->shared); + + window = renderspuGetDummyWindow(sharedContext->visual->visAttribs); + if (!window) + { + crError("renderspuChkActivateSharedContext: renderspuGetDummyWindow failed!"); + return GL_FALSE; + } + + CRASSERT(window->device_context); + + crDebug( "Render SPU: renderspuChkActivateSharedContext: made the DC: 0x%x", window->device_context ); + + sharedContext->hRC = render_spu.ws.wglCreateContext(window->device_context); + if (!sharedContext->hRC) + { + crError( "Render SPU: (renderspuChkActivateSharedContext) Couldn't create the context for the window (error 0x%x)", GetLastError() ); + return GL_FALSE; + } + + return GL_TRUE; +} + +void renderspu_SystemMakeCurrent( WindowInfo *window, GLint nativeWindow, ContextInfo *context ) +{ + CRASSERT(render_spu.ws.wglMakeCurrent); + + if (context && window) { + if (window->visual != context->visual) { + /* + * XXX have to revisit this issue!!! + * + * But for now we destroy the current window + * and re-create it with the context's visual abilities + */ + + /** @todo Chromium has no correct code to remove window ids and associated info from + * various tables. This is hack which just hides the root case. + */ + crWarning("Recreating window in renderspu_SystemMakeCurrent\n"); + renderspu_SystemDestroyWindow( window ); + renderspu_SystemVBoxCreateWindow( context->visual, window->visible, window ); + } + + if (0/*render_spu.render_to_app_window && nativeWindow*/) + { + /* The render_to_app_window option + * is set and we've got a nativeWindow + * handle, save the handle for + * later calls to swapbuffers(). + * + * NOTE: This doesn't work, except + * for software driven Mesa. + * We'd need to object link the + * crappfaker and crserver to be able to share + * the HDC values between processes.. FIXME! + */ + if (context->shared) + { + /* first make sure we have shared context created */ + renderspuChkActivateSharedContext(context->shared); + } + + window->nativeWindow = (HDC) nativeWindow; + if (context->hRC == 0) { + context->hRC = render_spu.ws.wglCreateContext( window->nativeWindow ); + if (!context->hRC) + { + crError( "(MakeCurrent) Couldn't create the context for the window (error 0x%x)", GetLastError() ); + } + } + + if (context->shared + && context->shared->hRC + && context->hRC) + { + /* share lists */ + render_spu.ws.wglShareLists(context->shared->hRC, context->hRC); + } + + render_spu.ws.wglMakeCurrent( window->nativeWindow, context->hRC ); + } + else + { + if (!context->hRC) { + CRASSERT(!nativeWindow); + if (context->shared) + { + /* first make sure we have shared context created */ + renderspuChkActivateSharedContext(context->shared); + } + + context->hRC = render_spu.ws.wglCreateContext(window->device_context); + if (!context->hRC) + { + crError( "Render SPU: (MakeCurrent) Couldn't create the context for the window (error 0x%x)", GetLastError() ); + } + + if (context->shared + && context->shared->hRC + && context->hRC) + { + /* share lists */ + BOOL bRc = render_spu.ws.wglShareLists(context->shared->hRC, context->hRC); + if (!bRc) + { + DWORD winEr = GetLastError(); + crWarning("wglShareLists failed, winEr %d", winEr); + } + } + + /*Requery ext function pointers, we skip dummy ctx as it should never be used with ext functions*/ + if (0 && context->BltInfo.Base.id) + { + int numFuncs, i; + SPUNamedFunctionTable ext_table[1000]; + + + crDebug("Default server ctx created, requerying ext functions"); + /*requery ext functions*/ + numFuncs = renderspuCreateFunctions(ext_table); + numFuncs += crLoadOpenGLExtensions( &render_spu.ws, ext_table+numFuncs); + CRASSERT(numFuncs < 1000); + + /*change spu dispatch*/ + crSPUChangeDispatch(&render_spu.self, ext_table); + + + /*cleanup temp table*/ + for (i=0; i<numFuncs; ++i) + { + if (ext_table[i].name) crFree(ext_table[i].name); + } + } + } + + /*crDebug("MakeCurrent 0x%x, 0x%x", window->device_context, context->hRC);*/ + if (!render_spu.ws.wglMakeCurrent(!nativeWindow ? window->device_context : window->redraw_device_context, context->hRC)) + { + DWORD err = GetLastError(); + crError("Render SPU: (MakeCurrent) failed to make 0x%x, 0x%x current with 0x%x error.", window->device_context, context->hRC, err); + } + } + + renderspuAtiQuirk_ChkApply(); + } + else { + render_spu.ws.wglMakeCurrent( 0, 0 ); + } +} + +void renderspu_SystemWindowSize( WindowInfo *window, GLint w, GLint h ) +{ + int winprop; + CRASSERT(window); + CRASSERT(window->visual); + if ( render_spu.fullscreen ) + winprop = SWP_SHOWWINDOW | SWP_NOSENDCHANGING | + SWP_NOREDRAW | SWP_NOACTIVATE; + else + winprop = SWP_NOACTIVATE | SWP_DEFERERASE | SWP_NOSENDCHANGING | SWP_NOZORDER; //SWP_SHOWWINDOW; + + /*SetWindowRgn(window->hWnd, NULL, false);*/ + + if (!SetWindowPos( window->hWnd, HWND_TOP, + window->x, window->y, w, h, winprop )) { + crWarning("!!!FAILED!!! Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, window->x, window->y, w, h); + } else { + crDebug("Render SPU: SetWindowSize (%x, %d, %d, %d, %d)", window->hWnd, window->x, window->y, w, h); + } + /* save the new size */ + window->BltInfo.width = w; + window->BltInfo.height = h; +} + + +void renderspu_SystemGetWindowGeometry( WindowInfo *window, GLint *x, GLint *y, GLint *w, GLint *h ) +{ + RECT rect; + + CRASSERT(window); + CRASSERT(window->visual); + + GetClientRect( window->hWnd, &rect ); + *x = rect.left; + *y = rect.top; + *w = rect.right - rect.left; + *h = rect.bottom - rect.top; +} + + +void renderspu_SystemGetMaxWindowSize( WindowInfo *window, GLint *w, GLint *h ) +{ + /* XXX fix this */ + (void) window; + *w = 1600; + *h = 1200; +} + + +void renderspu_SystemWindowPosition( WindowInfo *window, GLint x, GLint y ) +{ + int winprop; + CRASSERT(window); + CRASSERT(window->visual); + if ( render_spu.fullscreen ) + winprop = SWP_SHOWWINDOW | SWP_NOSENDCHANGING | + SWP_NOREDRAW | SWP_NOACTIVATE; + else + winprop = SWP_NOACTIVATE | SWP_DEFERERASE | SWP_NOSENDCHANGING | SWP_NOZORDER; //SWP_SHOWWINDOW; + + /*SetWindowRgn(window->visual->hWnd, NULL, false);*/ + + if (!SetWindowPos( window->hWnd, HWND_TOP, + x, y, window->BltInfo.width, window->BltInfo.height, winprop )) { + crWarning("!!!FAILED!!! Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, x, y, window->BltInfo.width, window->BltInfo.height); + } else { + crDebug("Render SPU: SetWindowPos (%x, %d, %d, %d, %d)", window->hWnd, + x, y, window->BltInfo.width, window->BltInfo.height); + } + /* save the new position */ + window->x = x; + window->y = y; +} + +GLboolean renderspu_SystemWindowNeedEmptyPresent(WindowInfo *window) +{ + return GL_FALSE; +} + +void renderspu_SystemWindowVisibleRegion(WindowInfo *window, GLint cRects, const GLint* pRects) +{ + GLint i; + HRGN hRgn, hTmpRgn; + RECT rectRgnBound; + + CRASSERT(window); + CRASSERT(window->visual); + + if (window->hRgn) + { + DeleteObject(window->hRgn); + window->hRgn = NULL; + } + + hRgn = CreateRectRgn(0, 0, 0, 0); + + for (i = 0; i < cRects; i++) + { + crDebug("Render SPU: CreateRectRgn #%d: (%d, %d)-(%d, %d)", i, + pRects[4 * i], pRects[4 * i + 1], pRects[4 * i + 2], pRects[4 * i + 3]); + + hTmpRgn = CreateRectRgn(pRects[4 * i], pRects[4 * i + 1], pRects[4 * i + 2], pRects[4 * i + 3]); + CombineRgn(hRgn, hRgn, hTmpRgn, RGN_OR); + DeleteObject(hTmpRgn); + } + + if (GetRgnBox(hRgn, &rectRgnBound)) + { + crDebug("Render SPU: Rgn bounding box (%d, %d)-(%d, %d)", + rectRgnBound.left, rectRgnBound.top, rectRgnBound.right, rectRgnBound.bottom); + } + + window->hRgn = hRgn; + + if (window->visible) + renderspuWindowRgnApply(window); + + crDebug("Render SPU: SetWindowRgn (%x, cRects=%i)", window->hWnd, cRects); +} + +static void renderspuHandleWindowMessages( HWND hWnd ) +{ + MSG msg; + while ( PeekMessage( &msg, hWnd, 0, 0xffffffff, PM_REMOVE ) ) + { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + + //BringWindowToTop( hWnd ); +} + +void renderspu_SystemSwapBuffers( WindowInfo *w, GLint flags ) +{ + int return_value; + + /* peek at the windows message queue */ +// renderspuHandleWindowMessages( w->hWnd ); + + /* render_to_app_window: + * w->nativeWindow will only be non-zero if the + * render_spu.render_to_app_window option is true and + * MakeCurrent() recorded the nativeWindow handle in the WindowInfo + * structure. + */ + if (render_spu.render_to_app_window && w->nativeWindow) { +#ifdef VBOX_CR_SERVER_FORCE_WGL + return_value = render_spu.ws.wglSwapBuffers( w->nativeWindow ); +#else + return_value = SwapBuffers( w->nativeWindow ); +#endif + } else { + /* + HRGN hRgn1, hRgn2, hRgn3; + HWND hWndParent; + LONG ws; + + hRgn1 = CreateRectRgn(0, 0, w->BltInfo.width, w->BltInfo.height); + hRgn2 = CreateRectRgn(50, 50, 100, 100); + hRgn3 = CreateRectRgn(0, 0, 0, 0); + CombineRgn(hRgn3, hRgn1, hRgn2, RGN_DIFF); + SetWindowRgn(w->visual->hWnd, hRgn3, true); + DeleteObject(hRgn1); + DeleteObject(hRgn2); + + hWndParent = GetParent(w->visual->hWnd); + ws = GetWindowLong(hWndParent, GWL_STYLE); + ws &= ~WS_CLIPCHILDREN; + SetWindowLong(hWndParent, GWL_STYLE, ws); + + RECT rcClip; + + rcClip.left = 50; + rcClip.top = 50; + rcClip.right = 100; + rcClip.bottom = 100; + ValidateRect(w->visual->hWnd, &rcClip); + + return_value = GetClipBox(w->visual->device_context, &rcClip); + crDebug("GetClipBox returned %d (NR=%d,SR=%d,CR=%d,ERR=%d)", + return_value, NULLREGION, SIMPLEREGION, COMPLEXREGION, ERROR); + + crDebug("rcClip(%d, %d, %d, %d)", rcClip.left, rcClip.top, rcClip.right, rcClip.bottom); + + return_value = ExcludeClipRect(w->visual->device_context, 50, 50, 100, 100); + crDebug("ExcludeClipRect returned %d (NR=%d,SR=%d,CR=%d,ERR=%d)", + return_value, NULLREGION, SIMPLEREGION, COMPLEXREGION, ERROR); + + return_value = GetClipBox(w->visual->device_context, &rcClip); + crDebug("GetClipBox returned %d (NR=%d,SR=%d,CR=%d,ERR=%d)", + return_value, NULLREGION, SIMPLEREGION, COMPLEXREGION, ERROR); + crDebug("rcClip(%d, %d, %d, %d)", rcClip.left, rcClip.top, rcClip.right, rcClip.bottom); + */ +#ifdef VBOX_CR_SERVER_FORCE_WGL + return_value = render_spu.ws.wglSwapBuffers( w->device_context ); +#else + return_value = SwapBuffers( w->device_context ); +#endif + } + if (!return_value) + { + /* GOD DAMN IT. The latest versions of the NVIDIA drivers + * return failure from wglSwapBuffers, but it works just fine. + * WHAT THE HELL?! */ + + crWarning( "wglSwapBuffers failed: return value of %d!", return_value); + } +} + +void renderspu_SystemReparentWindow(WindowInfo *window) +{ + SetParent(window->hWnd, (HWND)render_spu_parent_window_id); +} + +int renderspu_SystemInit() +{ + return VINF_SUCCESS; +} + +int renderspu_SystemTerm() +{ + return VINF_SUCCESS; +} + +void renderspu_SystemDefaultSharedContextChanged(ContextInfo *fromContext, ContextInfo *toContext) +{ + +} + +uint32_t renderspu_SystemPostprocessFunctions(SPUNamedFunctionTable *aFunctions, uint32_t cFunctions, uint32_t cTable) +{ + return cFunctions; +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/Makefile.kup b/src/VBox/HostServices/SharedOpenGL/unpacker/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/Makefile.kup diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.def b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.def new file mode 100644 index 00000000..01a07bf2 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.def @@ -0,0 +1,11 @@ +; Copyright (c) 2001, Stanford University +; All rights reserved. +; +; See the file LICENSE.txt for information on redistributing this software. +EXPORTS +crUnpack +crUnpackPush +crUnpackPop +crUnpackSetReturnPointer +crUnpackSetWritebackPointer +cr_unpackData diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.py b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.py new file mode 100755 index 00000000..052fff6c --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack.py @@ -0,0 +1,394 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. + +from __future__ import print_function +import sys + +import apiutil + + +apiutil.CopyrightC() + +print("""/* DO NOT EDIT! THIS CODE IS AUTOGENERATED BY unpack.py */ + +#include "unpacker.h" +#include "cr_opcodes.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "cr_spu.h" +#include "unpack_extend.h" +#include <stdio.h> +#include <memory.h> + +#include <iprt/cdefs.h> + +DECLEXPORT(const unsigned char *) cr_unpackData = NULL; +DECLEXPORT(const unsigned char *) cr_unpackDataEnd = NULL; +SPUDispatchTable cr_unpackDispatch; + +static void crUnpackExtend(void); +static void crUnpackExtendDbg(void); + +#if 0 //def DEBUG_misha +//# define CR_UNPACK_DEBUG_OPCODES +# define CR_UNPACK_DEBUG_LAST_OPCODES +# define CR_UNPACK_DEBUG_PREV_OPCODES +#endif + +#ifdef CR_UNPACK_DEBUG_PREV_OPCODES +static GLenum g_VBoxDbgCrPrevOpcode = 0; +static GLenum g_VBoxDbgCrPrevExtendOpcode = 0; +#endif +""") + +nodebug_opcodes = [ + "CR_MULTITEXCOORD2FARB_OPCODE", + "CR_VERTEX3F_OPCODE", + "CR_NORMAL3F_OPCODE", + "CR_COLOR4UB_OPCODE", + "CR_LOADIDENTITY_OPCODE", + "CR_MATRIXMODE_OPCODE", + "CR_LOADMATRIXF_OPCODE", + "CR_DISABLE_OPCODE", + "CR_COLOR4F_OPCODE", + "CR_ENABLE_OPCODE", + "CR_BEGIN_OPCODE", + "CR_END_OPCODE", + "CR_SECONDARYCOLOR3FEXT_OPCODE" +] + +nodebug_extopcodes = [ + "CR_ACTIVETEXTUREARB_EXTEND_OPCODE" +] + +# +# Useful functions +# + +def ReadData( offset, arg_type ): + """Emit a READ_DOUBLE or READ_DATA call for pulling a GL function + argument out of the buffer's operand area.""" + if arg_type == "GLdouble" or arg_type == "GLclampd": + retval = "READ_DOUBLE(%d)" % offset + else: + retval = "READ_DATA(%d, %s)" % (offset, arg_type) + return retval + + +def FindReturnPointer( return_type, params ): + """For GL functions that return values (either as the return value or + through a pointer parameter) emit a SET_RETURN_PTR call.""" + arg_len = apiutil.PacketLength( params ) + if (return_type != 'void'): + print('\tSET_RETURN_PTR(%d);' % (arg_len + 8)) # extended opcode plus packet length + else: + paramList = [ ('foo', 'void *', 0) ] + print('\tSET_RETURN_PTR(%d);' % (arg_len + 8 - apiutil.PacketLength(paramList))) + + +def FindWritebackPointer( return_type, params ): + """Emit a SET_WRITEBACK_PTR call.""" + arg_len = apiutil.PacketLength( params ) + if return_type != 'void': + paramList = [ ('foo', 'void *', 0) ] + arg_len += apiutil.PacketLength( paramList ) + + print('\tSET_WRITEBACK_PTR(%d);' % (arg_len + 8)) # extended opcode plus packet length + + +def MakeNormalCall( return_type, func_name, params, counter_init = 0 ): + counter = counter_init + copy_of_params = params[:] + + for i in range( 0, len(params) ): + (name, type, vecSize) = params[i] + if apiutil.IsPointer(copy_of_params[i][1]): + params[i] = ('NULL', type, vecSize) + copy_of_params[i] = (copy_of_params[i][0], 'void', 0) + if not "get" in apiutil.Properties(func_name): + print('\tcrError( "%s needs to be special cased!" );' % func_name) + else: + print("\t%s %s = %s;" % ( copy_of_params[i][1], name, ReadData( counter, copy_of_params[i][1] ) )) + counter += apiutil.sizeof(copy_of_params[i][1]) + + if ("get" in apiutil.Properties(func_name)): + FindReturnPointer( return_type, params ) + FindWritebackPointer( return_type, params ) + + if return_type != "void": + print("\t(void)", end=" ") + else: + print("\t", end="") + print("cr_unpackDispatch.%s(%s);" % (func_name, apiutil.MakeCallString(params))) + + +def MakeVectorCall( return_type, func_name, arg_type ): + """Convert a call like glVertex3f to glVertex3fv.""" + vec_func = apiutil.VectorFunction(func_name) + params = apiutil.Parameters(vec_func) + assert len(params) == 1 + (arg_name, vecType, vecSize) = params[0] + + if arg_type == "GLdouble" or arg_type == "GLclampd": + print("#ifdef CR_UNALIGNED_ACCESS_OKAY") + print("\tcr_unpackDispatch.%s((%s) cr_unpackData);" % (vec_func, vecType)) + print("#else") + for index in range(0, vecSize): + print("\tGLdouble v" + repr(index) + " = READ_DOUBLE(" + repr(index * 8) + ");") + if return_type != "void": + print("\t(void) cr_unpackDispatch.%s(" % func_name, end="") + else: + print("\tcr_unpackDispatch.%s(" % func_name, end="") + for index in range(0, vecSize): + print("v" + repr(index), end="") + if index != vecSize - 1: + print(",", end=" ") + print(");") + print("#endif") + else: + print("\tcr_unpackDispatch.%s((%s) cr_unpackData);" % (vec_func, vecType)) + + + +keys = apiutil.GetDispatchedFunctions(sys.argv[1]+"/APIspec.txt") + + +# +# Generate unpack functions for all the simple functions. +# +for func_name in keys: + if (not "pack" in apiutil.ChromiumProps(func_name) or + apiutil.FindSpecial( "unpacker", func_name )): + continue + + params = apiutil.Parameters(func_name) + return_type = apiutil.ReturnType(func_name) + + print("static void crUnpack%s(void)" % func_name) + print("{") + + vector_func = apiutil.VectorFunction(func_name) + if (vector_func and len(apiutil.Parameters(vector_func)) == 1): + MakeVectorCall( return_type, func_name, params[0][1] ) + else: + MakeNormalCall( return_type, func_name, params ) + packet_length = apiutil.PacketLength( params ) + if packet_length == 0: + print("\tINCR_DATA_PTR_NO_ARGS( );") + else: + print("\tINCR_DATA_PTR(%d);" % packet_length) + print("}\n") + + +# +# Emit some code +# +print(""" +typedef struct __dispatchNode { + const unsigned char *unpackData; + struct __dispatchNode *next; +} DispatchNode; + +static DispatchNode *unpackStack = NULL; + +static SPUDispatchTable *cr_lastDispatch = NULL; + +void crUnpackPush(void) +{ + DispatchNode *node = (DispatchNode*)crAlloc( sizeof( *node ) ); + node->next = unpackStack; + unpackStack = node; + node->unpackData = cr_unpackData; +} + +void crUnpackPop(void) +{ + DispatchNode *node = unpackStack; + + if (!node) + { + crError( "crUnpackPop called with an empty stack!" ); + } + unpackStack = node->next; + cr_unpackData = node->unpackData; + crFree( node ); +} + +CR_UNPACK_BUFFER_TYPE crUnpackGetBufferType(const void *opcodes, unsigned int num_opcodes) +{ + const uint8_t *pu8Codes = (const uint8_t *)opcodes; + + uint8_t first; + uint8_t last; + + if (!num_opcodes) + return CR_UNPACK_BUFFER_TYPE_GENERIC; + + first = pu8Codes[0]; + last = pu8Codes[1-(int)num_opcodes]; + + switch (last) + { + case CR_CMDBLOCKFLUSH_OPCODE: + return CR_UNPACK_BUFFER_TYPE_CMDBLOCK_FLUSH; + case CR_CMDBLOCKEND_OPCODE: + return (first == CR_CMDBLOCKBEGIN_OPCODE) ? CR_UNPACK_BUFFER_TYPE_GENERIC : CR_UNPACK_BUFFER_TYPE_CMDBLOCK_END; + default: + return (first != CR_CMDBLOCKBEGIN_OPCODE) ? CR_UNPACK_BUFFER_TYPE_GENERIC : CR_UNPACK_BUFFER_TYPE_CMDBLOCK_BEGIN; + } +} + +void crUnpack( const void *data, const void *data_end, const void *opcodes, + unsigned int num_opcodes, SPUDispatchTable *table ) +{ + unsigned int i; + const unsigned char *unpack_opcodes; + if (table != cr_lastDispatch) + { + crSPUCopyDispatchTable( &cr_unpackDispatch, table ); + cr_lastDispatch = table; + } + + unpack_opcodes = (const unsigned char *)opcodes; + cr_unpackData = (const unsigned char *)data; + cr_unpackDataEnd = (const unsigned char *)data_end; + +#if defined(CR_UNPACK_DEBUG_OPCODES) || defined(CR_UNPACK_DEBUG_LAST_OPCODES) + crDebug("crUnpack: %d opcodes", num_opcodes); +#endif + + for (i = 0; i < num_opcodes; i++) + { + + CRDBGPTR_CHECKZ(writeback_ptr); + CRDBGPTR_CHECKZ(return_ptr); + + /*crDebug(\"Unpacking opcode \%d\", *unpack_opcodes);*/ +#ifdef CR_UNPACK_DEBUG_PREV_OPCODES + g_VBoxDbgCrPrevOpcode = *unpack_opcodes; +#endif + switch( *unpack_opcodes ) + {""") + +# +# Emit switch cases for all unextended opcodes +# +for func_name in keys: + if "pack" in apiutil.ChromiumProps(func_name): + print('\t\t\tcase %s:' % apiutil.OpcodeName( func_name )) + if not apiutil.OpcodeName(func_name) in nodebug_opcodes: + print(""" +#ifdef CR_UNPACK_DEBUG_LAST_OPCODES + if (i==(num_opcodes-1)) +#endif +#if defined(CR_UNPACK_DEBUG_OPCODES) || defined(CR_UNPACK_DEBUG_LAST_OPCODES) + crDebug("Unpack: %s"); +#endif """ % apiutil.OpcodeName(func_name)) + print('\t\t\t\tcrUnpack%s(); \n\t\t\t\tbreak;' % func_name) + +print(""" + case CR_EXTEND_OPCODE: + #ifdef CR_UNPACK_DEBUG_OPCODES + crUnpackExtendDbg(); + #else + # ifdef CR_UNPACK_DEBUG_LAST_OPCODES + if (i==(num_opcodes-1)) crUnpackExtendDbg(); + else + # endif + crUnpackExtend(); + #endif + break; + case CR_CMDBLOCKBEGIN_OPCODE: + case CR_CMDBLOCKEND_OPCODE: + case CR_CMDBLOCKFLUSH_OPCODE: + case CR_NOP_OPCODE: + INCR_DATA_PTR_NO_ARGS( ); + break; + default: + crError( "Unknown opcode: %d", *unpack_opcodes ); + break; + } + + CRDBGPTR_CHECKZ(writeback_ptr); + CRDBGPTR_CHECKZ(return_ptr); + + unpack_opcodes--; + } +}""") + + +# +# Emit unpack functions for extended opcodes, non-special functions only. +# +for func_name in keys: + if ("extpack" in apiutil.ChromiumProps(func_name) + and not apiutil.FindSpecial("unpacker", func_name)): + return_type = apiutil.ReturnType(func_name) + params = apiutil.Parameters(func_name) + print('static void crUnpackExtend%s(void)' % func_name) + print('{') + MakeNormalCall( return_type, func_name, params, 8 ) + print('}\n') + +print('static void crUnpackExtend(void)') +print('{') +print('\tGLenum extend_opcode = %s;' % ReadData( 4, 'GLenum' )) +print('') +print('#ifdef CR_UNPACK_DEBUG_PREV_OPCODES') +print('\tg_VBoxDbgCrPrevExtendOpcode = extend_opcode;') +print('#endif') +print('') +print('\t/*crDebug(\"Unpacking extended opcode \%d", extend_opcode);*/') +print('\tswitch( extend_opcode )') +print('\t{') + + +# +# Emit switch statement for extended opcodes +# +for func_name in keys: + if "extpack" in apiutil.ChromiumProps(func_name): + print('\t\tcase %s:' % apiutil.ExtendedOpcodeName( func_name )) +# print('\t\t\t\tcrDebug("Unpack: %s");' % apiutil.ExtendedOpcodeName( func_name ))) + print('\t\t\tcrUnpackExtend%s( );' % func_name) + print('\t\t\tbreak;') + +print(""" default: + crError( "Unknown extended opcode: %d", (int) extend_opcode ); + break; + } + INCR_VAR_PTR(); +}""") + +print('static void crUnpackExtendDbg(void)') +print('{') +print('\tGLenum extend_opcode = %s;' % ReadData( 4, 'GLenum' )) +print('') +print('#ifdef CR_UNPACK_DEBUG_PREV_OPCODES') +print('\tg_VBoxDbgCrPrevExtendOpcode = extend_opcode;') +print('#endif') +print('') +print('\t/*crDebug(\"Unpacking extended opcode \%d", extend_opcode);*/') +print('\tswitch( extend_opcode )') +print('\t{') + + +# +# Emit switch statement for extended opcodes +# +for func_name in keys: + if "extpack" in apiutil.ChromiumProps(func_name): + print('\t\tcase %s:' % apiutil.ExtendedOpcodeName( func_name )) + if not apiutil.ExtendedOpcodeName(func_name) in nodebug_extopcodes: + print('\t\t\tcrDebug("Unpack: %s");' % apiutil.ExtendedOpcodeName( func_name )) + print('\t\t\tcrUnpackExtend%s( );' % func_name) + print('\t\t\tbreak;') + +print(""" default: + crError( "Unknown extended opcode: %d", (int) extend_opcode ); + break; + } + INCR_VAR_PTR(); +}""") diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_arrays.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_arrays.c new file mode 100644 index 00000000..847bb1ed --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_arrays.c @@ -0,0 +1,312 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "cr_error.h" +#include "unpack_extend.h" +#include "unpacker.h" +#include "cr_glstate.h" +/** + * \mainpage Unpacker + * + * \section UnpackerIntroduction Introduction + * + * Chromium consists of all the top-level files in the cr + * directory. The unpacker module basically takes care of API dispatch, + * and OpenGL state management. + * + */ + +void crUnpackExtendVertexPointer(void) +{ + GLint size = READ_DATA( 8, GLint ); + GLenum type = READ_DATA( 12, GLenum ); + GLsizei stride = READ_DATA( 16, GLsizei ); + GLintptrARB pointer = (GLintptrARB) READ_DATA( 20, GLuint ); + cr_unpackDispatch.VertexPointer( size, type, stride, (void *) pointer ); +} + +void crUnpackExtendTexCoordPointer(void) +{ + GLint size = READ_DATA( 8, GLint ); + GLenum type = READ_DATA( 12, GLenum ); + GLsizei stride = READ_DATA( 16, GLsizei ); + GLintptrARB pointer = READ_DATA( 20, GLuint ); + cr_unpackDispatch.TexCoordPointer( size, type, stride, (void *) pointer ); +} + +void crUnpackExtendNormalPointer(void) +{ + GLenum type = READ_DATA( 8, GLenum ); + GLsizei stride = READ_DATA( 12, GLsizei ); + GLintptrARB pointer = READ_DATA( 16, GLuint ); + cr_unpackDispatch.NormalPointer( type, stride, (void *) pointer ); +} + +void crUnpackExtendIndexPointer(void) +{ + GLenum type = READ_DATA( 8, GLenum ); + GLsizei stride = READ_DATA( 12, GLsizei ); + GLintptrARB pointer = READ_DATA( 16, GLuint ); + cr_unpackDispatch.IndexPointer( type, stride, (void *) pointer ); +} + +void crUnpackExtendEdgeFlagPointer(void) +{ + GLsizei stride = READ_DATA( 8, GLsizei ); + GLintptrARB pointer = READ_DATA( 12, GLuint ); + cr_unpackDispatch.EdgeFlagPointer( stride, (void *) pointer ); +} + +void crUnpackExtendColorPointer(void) +{ + GLint size = READ_DATA( 8, GLint ); + GLenum type = READ_DATA( 12, GLenum ); + GLsizei stride = READ_DATA( 16, GLsizei ); + GLintptrARB pointer = READ_DATA( 20, GLuint ); + cr_unpackDispatch.ColorPointer( size, type, stride, (void *) pointer ); +} + +void crUnpackExtendFogCoordPointerEXT(void) +{ + GLenum type = READ_DATA( 8, GLenum ); + GLsizei stride = READ_DATA( 12, GLsizei ); + GLintptrARB pointer = READ_DATA( 16, GLuint ); + cr_unpackDispatch.FogCoordPointerEXT( type, stride, (void *) pointer ); +} + +void crUnpackExtendSecondaryColorPointerEXT(void) +{ + GLint size = READ_DATA( 8, GLint ); + GLenum type = READ_DATA( 12, GLenum ); + GLsizei stride = READ_DATA( 16, GLsizei ); + GLintptrARB pointer = READ_DATA( 20, GLuint ); + cr_unpackDispatch.SecondaryColorPointerEXT( size, type, stride, (void *) pointer ); +} + +void crUnpackExtendVertexAttribPointerARB(void) +{ + GLuint index = READ_DATA( 8, GLuint); + GLint size = READ_DATA( 12, GLint ); + GLenum type = READ_DATA( 16, GLenum ); + GLboolean normalized = READ_DATA( 20, GLboolean ); + GLsizei stride = READ_DATA( 24, GLsizei ); + GLintptrARB pointer = READ_DATA( 28, GLuint ); + cr_unpackDispatch.VertexAttribPointerARB( index, size, type, normalized, stride, (void *) pointer ); +} + +void crUnpackExtendVertexAttribPointerNV(void) +{ + GLuint index = READ_DATA( 8, GLuint); + GLint size = READ_DATA( 12, GLint ); + GLenum type = READ_DATA( 16, GLenum ); + GLsizei stride = READ_DATA( 20, GLsizei ); + GLintptrARB pointer = READ_DATA( 24, GLuint ); + cr_unpackDispatch.VertexAttribPointerNV( index, size, type, stride, (void *) pointer ); +} + +void crUnpackExtendInterleavedArrays(void) +{ + GLenum format = READ_DATA( 8, GLenum ); + GLsizei stride = READ_DATA( 12, GLsizei ); + GLintptrARB pointer = READ_DATA( 16, GLuint ); + cr_unpackDispatch.InterleavedArrays( format, stride, (void *) pointer ); +} + +void crUnpackExtendDrawElements(void) +{ + GLenum mode = READ_DATA( 8, GLenum ); + GLsizei count = READ_DATA( 12, GLsizei ); + GLenum type = READ_DATA( 16, GLenum ); + GLintptrARB indices = READ_DATA( 20, GLuint ); + void * indexptr; +#ifdef CR_ARB_vertex_buffer_object + GLboolean hasidxdata = READ_DATA(24, GLint); + indexptr = hasidxdata ? DATA_POINTER(28, void) : (void*)indices; +#else + indexptr = DATA_POINTER(24, void); +#endif + cr_unpackDispatch.DrawElements(mode, count, type, indexptr); +} + +void crUnpackExtendDrawRangeElements(void) +{ + GLenum mode = READ_DATA( 8, GLenum ); + GLuint start = READ_DATA( 12, GLuint ); + GLuint end = READ_DATA( 16, GLuint ); + GLsizei count = READ_DATA( 20, GLsizei ); + GLenum type = READ_DATA( 24, GLenum ); + GLintptrARB indices = READ_DATA( 28, GLuint ); + void * indexptr; +#ifdef CR_ARB_vertex_buffer_object + GLboolean hasidxdata = READ_DATA(32, GLint); + indexptr = hasidxdata ? DATA_POINTER(36, void) : (void*)indices; +#else + indexptr = DATA_POINTER(32, void); +#endif + cr_unpackDispatch.DrawRangeElements(mode, start, end, count, type, indexptr); +} + +void crUnpackMultiDrawArraysEXT(void) +{ + crError( "Can't decode MultiDrawArraysEXT" ); +} + +void crUnpackMultiDrawElementsEXT(void) +{ + crError( "Can't decode MultiDrawElementsEXT" ); +} + +static void crUnpackSetClientPointerByIndex(int index, GLint size, + GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid *pointer, CRClientState *c) +{ + /*crDebug("crUnpackSetClientPointerByIndex: %i(s=%i, t=0x%x, n=%i, str=%i) -> %p", index, size, type, normalized, stride, pointer);*/ + + if (index<7) + { + switch (index) + { + case 0: + cr_unpackDispatch.VertexPointer(size, type, stride, pointer); + break; + case 1: + cr_unpackDispatch.ColorPointer(size, type, stride, pointer); + break; + case 2: + cr_unpackDispatch.FogCoordPointerEXT(type, stride, pointer); + break; + case 3: + cr_unpackDispatch.SecondaryColorPointerEXT(size, type, stride, pointer); + break; + case 4: + cr_unpackDispatch.EdgeFlagPointer(stride, pointer); + break; + case 5: + cr_unpackDispatch.IndexPointer(type, stride, pointer); + break; + case 6: + cr_unpackDispatch.NormalPointer(type, stride, pointer); + break; + } + } + else if (index<(7+CR_MAX_TEXTURE_UNITS)) + { + int curTexUnit = c->curClientTextureUnit; + if ((index-7)!=curTexUnit) + { + cr_unpackDispatch.ClientActiveTextureARB(GL_TEXTURE0_ARB+index-7); + } + cr_unpackDispatch.TexCoordPointer(size, type, stride, pointer); + if ((index-7)!=curTexUnit) + { + cr_unpackDispatch.ClientActiveTextureARB(GL_TEXTURE0_ARB+curTexUnit); + } + } + else + { + cr_unpackDispatch.VertexAttribPointerARB(index-7-CR_MAX_TEXTURE_UNITS, + size, type, normalized, stride, pointer); + } +} + +void crUnpackExtendLockArraysEXT(void) +{ + GLint first = READ_DATA(sizeof(int) + 4, GLint); + GLint count = READ_DATA(sizeof(int) + 8, GLint); + int numenabled = READ_DATA(sizeof(int) + 12, int); + + CRContext *g = crStateGetCurrent(); + CRClientState *c = &g->client; + CRClientPointer *cp; + int i, index, offset; + unsigned char *data; + + if (first < 0 || count <= 0 || first >= INT32_MAX - count) + { + crError("crUnpackExtendLockArraysEXT: first(%i) count(%i), parameters out of range", first, count); + return; + } + + if (numenabled <= 0 || numenabled >= CRSTATECLIENT_MAX_VERTEXARRAYS) + { + crError("crUnpackExtendLockArraysEXT: numenabled(%i), parameter out of range", numenabled); + return; + } + + offset = 2 * sizeof(int) + 12; + + /*crDebug("crUnpackExtendLockArraysEXT(%i, %i) ne=%i", first, count, numenabled);*/ + + for (i = 0; i < numenabled; ++i) + { + index = READ_DATA(offset, int); + offset += sizeof(int); + + cp = crStateGetClientPointerByIndex(index, &c->array); + + CRASSERT(cp && cp->enabled && (!cp->buffer || !cp->buffer->id)); + + if (cp && cp->bytesPerIndex > 0) + { + if (first + count >= INT32_MAX / cp->bytesPerIndex) + { + crError("crUnpackExtendLockArraysEXT: first(%i) count(%i) bpi(%i), parameters out of range", first, count, cp->bytesPerIndex); + return; + } + + data = crAlloc((first + count) * cp->bytesPerIndex); + + if (data) + { + crMemcpy(data + first * cp->bytesPerIndex, DATA_POINTER(offset, GLvoid), count * cp->bytesPerIndex); + /*crDebug("crUnpackExtendLockArraysEXT: old cp(%i): en/l=%i(%i) p=%p size=%i type=0x%x n=%i str=%i pp=%p pstr=%i", + index, cp->enabled, cp->locked, cp->p, cp->size, cp->type, cp->normalized, cp->stride, cp->prevPtr, cp->prevStride);*/ + crUnpackSetClientPointerByIndex(index, cp->size, cp->type, cp->normalized, 0, data, c); + /*crDebug("crUnpackExtendLockArraysEXT: new cp(%i): en/l=%i(%i) p=%p size=%i type=0x%x n=%i str=%i pp=%p pstr=%i", + index, cp->enabled, cp->locked, cp->p, cp->size, cp->type, cp->normalized, cp->stride, cp->prevPtr, cp->prevStride);*/ + } + else + { + crError("crUnpackExtendLockArraysEXT: crAlloc failed"); + return; + } + } + else + { + crError("crUnpackExtendLockArraysEXT: wrong CRClientState %i", index); + return; + } + + offset += count * cp->bytesPerIndex; + } + + cr_unpackDispatch.LockArraysEXT(first, count); +} + +void crUnpackExtendUnlockArraysEXT(void) +{ + int i; + CRContext *g = crStateGetCurrent(); + CRClientState *c = &g->client; + CRClientPointer *cp; + + /*crDebug("crUnpackExtendUnlockArraysEXT");*/ + + cr_unpackDispatch.UnlockArraysEXT(); + + for (i=0; i<CRSTATECLIENT_MAX_VERTEXARRAYS; ++i) + { + cp = crStateGetClientPointerByIndex(i, &c->array); + if (cp->enabled) + { + /*crDebug("crUnpackExtendUnlockArraysEXT: old cp(%i): en/l=%i(%i) p=%p size=%i type=0x%x n=%i str=%i pp=%p pstr=%i", + i, cp->enabled, cp->locked, cp->p, cp->size, cp->type, cp->normalized, cp->stride, cp->prevPtr, cp->prevStride);*/ + crUnpackSetClientPointerByIndex(i, cp->size, cp->type, cp->normalized, cp->prevStride, cp->prevPtr, c); + /*crDebug("crUnpackExtendUnlockArraysEXT: new cp(%i): en/l=%i(%i) p=%p size=%i type=0x%x n=%i str=%i pp=%p pstr=%i", + i, cp->enabled, cp->locked, cp->p, cp->size, cp->type, cp->normalized, cp->stride, cp->prevPtr, cp->prevStride);*/ + } + } +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bounds.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bounds.c new file mode 100644 index 00000000..1350a3da --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bounds.c @@ -0,0 +1,27 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "state/cr_statetypes.h" + +void crUnpackBoundsInfoCR( void ) +{ + CRrecti bounds; + GLint len; + GLuint num_opcodes; + GLbyte *payload; + + len = READ_DATA( 0, GLint ); + bounds.x1 = READ_DATA( 4, GLint ); + bounds.y1 = READ_DATA( 8, GLint ); + bounds.x2 = READ_DATA( 12, GLint ); + bounds.y2 = READ_DATA( 16, GLint ); + num_opcodes = READ_DATA( 20, GLuint ); + payload = DATA_POINTER( 24, GLbyte ); + + cr_unpackDispatch.BoundsInfoCR( &bounds, payload, len, num_opcodes ); + INCR_VAR_PTR(); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bufferobject.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bufferobject.c new file mode 100644 index 00000000..2b9ae29c --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_bufferobject.c @@ -0,0 +1,54 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "cr_mem.h" +#include "cr_error.h" + + +void crUnpackExtendGetBufferSubDataARB( void ) +{ + GLenum target = READ_DATA( 8, GLenum ); + GLintptrARB offset = READ_DATA( 12, GLuint ); + GLsizeiptrARB size = READ_DATA( 16, GLuint ); + + SET_RETURN_PTR( 20 ); + SET_WRITEBACK_PTR( 28 ); + + cr_unpackDispatch.GetBufferSubDataARB( target, offset, size, NULL ); +} + + +void crUnpackExtendBufferDataARB( void ) +{ + GLenum target = READ_DATA(sizeof(int) + 4, GLenum); + GLsizeiptrARB size = READ_DATA(sizeof(int) + 8, GLuint); + GLenum usage = READ_DATA(sizeof(int) + 12, GLenum); + GLboolean hasdata = READ_DATA(sizeof(int) + 16, GLint); + GLvoid *data = DATA_POINTER(sizeof(int) + 20, GLvoid); + + cr_unpackDispatch.BufferDataARB(target, size, hasdata ? data:NULL, usage); +} + + +void crUnpackExtendBufferSubDataARB( void ) +{ + GLenum target = READ_DATA( sizeof(int) + 4, GLenum ); + GLintptrARB offset = READ_DATA( sizeof(int) + 8, GLuint ); + GLsizeiptrARB size = READ_DATA( sizeof(int) + 12, GLuint ); + GLvoid *data = DATA_POINTER( sizeof(int) + 16, GLvoid ); + + cr_unpackDispatch.BufferSubDataARB( target, offset, size, data ); +} + + +void crUnpackExtendDeleteBuffersARB(void) +{ + GLsizei n = READ_DATA( 8, GLsizei ); + const GLuint *buffers = DATA_POINTER( 12, GLuint ); + cr_unpackDispatch.DeleteBuffersARB( n, buffers ); +} + diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_calllists.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_calllists.c new file mode 100644 index 00000000..7fa87275 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_calllists.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" + +void crUnpackCallLists( void ) +{ + GLint n = READ_DATA( sizeof( int ) + 0, GLint ); + GLenum type = READ_DATA( sizeof( int ) + 4, GLenum ); + GLvoid *lists = DATA_POINTER( sizeof( int ) + 8, GLvoid ); + + cr_unpackDispatch.CallLists( n, type, lists ); + INCR_VAR_PTR(); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_clipplane.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_clipplane.c new file mode 100644 index 00000000..462f28e5 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_clipplane.c @@ -0,0 +1,19 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "cr_mem.h" +#include "unpack_extend.h" + +void crUnpackClipPlane( void ) +{ + GLenum plane = READ_DATA( 0, GLenum ); + GLdouble equation[4]; + crMemcpy( equation, DATA_POINTER( 4, GLdouble ), sizeof(equation) ); + + cr_unpackDispatch.ClipPlane( plane, equation ); + INCR_DATA_PTR( sizeof( GLenum ) + 4*sizeof( GLdouble )); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_context.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_context.c new file mode 100644 index 00000000..bc58dce3 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_context.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "cr_mem.h" + +/* XXX duplicated in pack_context.c */ +#define DISPLAY_NAME_LEN 256 + +#define READ_BYTES( dest, offset, len ) \ + crMemcpy( dest, (cr_unpackData + (offset)), len ) + +void crUnpackExtendCreateContext( void ) +{ + char dpyName[DISPLAY_NAME_LEN]; + GLint visBits = READ_DATA( DISPLAY_NAME_LEN + 8, GLint ); + GLint shareCtx = READ_DATA( DISPLAY_NAME_LEN + 12, GLint ); + GLint retVal; + + READ_BYTES( dpyName, 8, DISPLAY_NAME_LEN ); + dpyName[DISPLAY_NAME_LEN - 1] = 0; /* NULL-terminate, just in case */ + + SET_RETURN_PTR( DISPLAY_NAME_LEN + 16 ); + SET_WRITEBACK_PTR( DISPLAY_NAME_LEN + 24 ); + retVal = cr_unpackDispatch.CreateContext( dpyName, visBits, shareCtx ); + (void) retVal; +} + +void crUnpackExtendWindowCreate(void) +{ + char dpyName[DISPLAY_NAME_LEN]; + GLint visBits = READ_DATA( DISPLAY_NAME_LEN + 8, GLint ); + GLint retVal; + + READ_BYTES( dpyName, 8, DISPLAY_NAME_LEN ); + dpyName[DISPLAY_NAME_LEN - 1] = 0; /* NULL-terminate, just in case */ + + SET_RETURN_PTR( DISPLAY_NAME_LEN + 12 ); + SET_WRITEBACK_PTR( DISPLAY_NAME_LEN + 20 ); + retVal = cr_unpackDispatch.WindowCreate( dpyName, visBits ); + (void) retVal; +} + diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_drawpixels.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_drawpixels.c new file mode 100644 index 00000000..e034a28b --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_drawpixels.c @@ -0,0 +1,94 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "cr_error.h" + +#include "state/cr_bufferobject.h" + +void crUnpackDrawPixels( void ) +{ + GLsizei width = READ_DATA( sizeof( int ) + 0, GLsizei ); + GLsizei height = READ_DATA( sizeof( int ) + 4, GLsizei ); + GLenum format = READ_DATA( sizeof( int ) + 8, GLenum ); + GLenum type = READ_DATA( sizeof( int ) + 12, GLenum ); + GLint noimagedata = READ_DATA( sizeof( int ) + 16, GLint ); + GLvoid *pixels; + + if (noimagedata && !crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + return; + + if (noimagedata) + pixels = (void*) (uintptr_t) READ_DATA( sizeof( int ) + 20, GLint); + else + pixels = DATA_POINTER( sizeof( int ) + 24, GLvoid ); + + cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + + cr_unpackDispatch.DrawPixels( width, height, format, type, pixels ); + + INCR_VAR_PTR( ); +} + +void crUnpackBitmap( void ) +{ + GLsizei width = READ_DATA( sizeof( int ) + 0, GLsizei ); + GLsizei height = READ_DATA( sizeof( int ) + 4, GLsizei ); + GLfloat xorig = READ_DATA( sizeof( int ) + 8, GLfloat ); + GLfloat yorig = READ_DATA( sizeof( int ) + 12, GLfloat ); + GLfloat xmove = READ_DATA( sizeof( int ) + 16, GLfloat ); + GLfloat ymove = READ_DATA( sizeof( int ) + 20, GLfloat ); + GLuint noimagedata = READ_DATA( sizeof( int ) + 24, GLuint ); + GLubyte *bitmap; + + if (noimagedata && !crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + return; + + if (noimagedata) + bitmap = (void*) (uintptr_t) READ_DATA(sizeof(int) + 28, GLint); + else + bitmap = DATA_POINTER( sizeof(int) + 32, GLubyte ); + + cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + + cr_unpackDispatch.Bitmap( width, height, xorig, yorig, xmove, ymove, bitmap ); + + INCR_VAR_PTR( ); +} + +/* + * ZPixCR - compressed DrawPixels + */ +void crUnpackExtendZPixCR( void ) +{ + GLsizei width = READ_DATA( 8, GLsizei ); + GLsizei height = READ_DATA( 12, GLsizei ); + GLenum format = READ_DATA( 16, GLenum ); + GLenum type = READ_DATA( 20, GLenum ); + GLenum ztype = READ_DATA( 24, GLenum ); + GLint zparm = READ_DATA( 28, GLuint ); + GLint length = READ_DATA( 32, GLint ); + GLvoid *pixels = DATA_POINTER( 36, GLvoid ); + +/*XXX JAG + crDebug("UnpackZPixCR: w = %d, h = %d, len = %d", + width, height, length); +*/ + cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + + cr_unpackDispatch.ZPixCR( width, height, format, type, ztype, zparm, length, pixels ); + + /* Don't call INCR_VAR_PTR(); - it's done in crUnpackExtend() */ +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_extend.py b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_extend.py new file mode 100755 index 00000000..d81ff903 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_extend.py @@ -0,0 +1,35 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. + +from __future__ import print_function +import sys + +sys.path.append( "../glapi_parser" ) +import apiutil + + +apiutil.CopyrightC() + +print("""/* DO NOT EDIT! THIS CODE IS AUTOGENERATED BY unpack_extend.py */ + +#ifndef UNPACK_EXTEND_H +#define UNPACK_EXTEND_H 1 + +""") + + +# +# Print extern declarations for all special unpacker functions +# +for func_name in apiutil.AllSpecials( "unpacker" ): + if "extpack" in apiutil.ChromiumProps(func_name): + print('extern void crUnpackExtend%s(void);' % func_name) + else: + print('extern void crUnpack%s(void);' % func_name) + +print(""" +#endif +""") + diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fence.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fence.c new file mode 100644 index 00000000..fa616b31 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fence.c @@ -0,0 +1,10 @@ + +#include "unpacker.h" + +void crUnpackExtendDeleteFencesNV(void) +{ + GLsizei n = READ_DATA( 8, GLsizei ); + const GLuint *fences = DATA_POINTER( 12, GLuint ); + cr_unpackDispatch.DeleteFencesNV( n, fences ); +} + diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fog.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fog.c new file mode 100644 index 00000000..faf09f95 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_fog.c @@ -0,0 +1,25 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" + +void crUnpackFogfv( void ) +{ + GLenum pname = READ_DATA( sizeof( int ) + 0, GLenum ); + GLfloat *params = DATA_POINTER( sizeof( int ) + 4, GLfloat ); + + cr_unpackDispatch.Fogfv( pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackFogiv( void ) +{ + GLenum pname = READ_DATA( sizeof( int ) + 0, GLenum ); + GLint *params = DATA_POINTER( sizeof( int ) + 4, GLint ); + + cr_unpackDispatch.Fogiv( pname, params ); + INCR_VAR_PTR(); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c new file mode 100644 index 00000000..0bab6d56 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_framebuffer.c @@ -0,0 +1,37 @@ +/* $Id: unpack_framebuffer.c $ */ +/** @file + * VBox OpenGL: EXT_framebuffer_object + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +#include "unpacker.h" +#include "cr_error.h" +#include "cr_protocol.h" +#include "cr_mem.h" +#include "cr_string.h" +#include "cr_version.h" + +void crUnpackExtendDeleteFramebuffersEXT(void) +{ + GLsizei n = READ_DATA( 8, GLsizei ); + const GLuint *buffers = DATA_POINTER( 12, GLuint ); + cr_unpackDispatch.DeleteFramebuffersEXT( n, buffers ); +} + +void crUnpackExtendDeleteRenderbuffersEXT(void) +{ + GLsizei n = READ_DATA( 8, GLsizei ); + const GLuint *buffers = DATA_POINTER( 12, GLuint ); + cr_unpackDispatch.DeleteRenderbuffersEXT( n, buffers ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_header.py b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_header.py new file mode 100755 index 00000000..cb42c22a --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_header.py @@ -0,0 +1,30 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. + +from __future__ import print_function +import sys; +import pickle; +import types; +import string; +import re; + +sys.path.append( "../opengl_stub" ) + +import stub_common; + +parsed_file = open( "../glapi_parser/gl_header.parsed", "rb" ) +gl_mapping = pickle.load( parsed_file ) + +stub_common.CopyrightC() + +print("""#ifndef CR_UNPACKFUNCTIONS_H +#define CR_UNPACKFUNCTIONS_H +""") + +for func_name in sorted(gl_mapping.keys()): + ( return_type, arg_names, arg_types ) = gl_mapping[func_name] + print('void crUnpack%s();' %( func_name )) +print('void crUnpackExtend();') +print('\n#endif /* CR_UNPACKFUNCTIONS_H */') diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_lights.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_lights.c new file mode 100644 index 00000000..01bd97b3 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_lights.c @@ -0,0 +1,45 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" + +void crUnpackLightfv( void ) +{ + GLenum light = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat ); + + cr_unpackDispatch.Lightfv( light, pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackLightiv( void ) +{ + GLenum light = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint ); + + cr_unpackDispatch.Lightiv( light, pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackLightModelfv( void ) +{ + GLenum pname = READ_DATA( sizeof( int ) + 0, GLenum ); + GLfloat *params = DATA_POINTER( sizeof( int ) + 4, GLfloat ); + + cr_unpackDispatch.LightModelfv( pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackLightModeliv( void ) +{ + GLenum pname = READ_DATA( sizeof( int ) + 0, GLenum ); + GLint *params = DATA_POINTER( sizeof( int ) + 4, GLint ); + + cr_unpackDispatch.LightModeliv( pname, params ); + INCR_VAR_PTR(); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_map.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_map.c new file mode 100644 index 00000000..ac44f856 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_map.c @@ -0,0 +1,138 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "cr_error.h" +#include "cr_mem.h" +#include "state/cr_limits.h" + + +void crUnpackMap2d(void) +{ + GLenum target = READ_DATA(sizeof(int) + 0, GLenum); + GLdouble u1 = READ_DOUBLE(sizeof(int) + 4); + GLdouble u2 = READ_DOUBLE(sizeof(int) + 12); + GLint ustride = READ_DATA(sizeof(int) + 20, GLint); + GLint uorder = READ_DATA(sizeof(int) + 24, GLint); + GLdouble v1 = READ_DOUBLE(sizeof(int) + 28); + GLdouble v2 = READ_DOUBLE(sizeof(int) + 36); + GLint vstride = READ_DATA(sizeof(int) + 44, GLint); + GLint vorder = READ_DATA(sizeof(int) + 48, GLint); + GLdouble *points = DATA_POINTER(sizeof(int) + 52, GLdouble); + GLint cbMax; + + if (uorder < 1 || uorder > CR_MAX_EVAL_ORDER || + vorder < 1 || vorder > CR_MAX_EVAL_ORDER || + ustride < 1 || ustride > INT32_MAX / (ssize_t)sizeof(double) / uorder / 8 || + vstride < 1 || vstride > INT32_MAX / (ssize_t)sizeof(double) / vorder / 8 ) + { + crError("crUnpackMap2d: parameters out of range"); + return; + } + + cbMax = (ustride * uorder + vstride * vorder) * sizeof(double); + if (!DATA_POINTER_CHECK(cbMax)) + { + crError("crUnpackMap2d: parameters out of range"); + return; + } + + cr_unpackDispatch.Map2d(target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points); + + INCR_VAR_PTR(); +} + +void crUnpackMap2f(void) +{ + GLenum target = READ_DATA(sizeof(int) + 0, GLenum); + GLfloat u1 = READ_DATA(sizeof(int) + 4, GLfloat); + GLfloat u2 = READ_DATA(sizeof(int) + 8, GLfloat); + GLint ustride = READ_DATA(sizeof(int) + 12, GLint); + GLint uorder = READ_DATA(sizeof(int) + 16, GLint); + GLfloat v1 = READ_DATA(sizeof(int) + 20, GLfloat); + GLfloat v2 = READ_DATA(sizeof(int) + 24, GLfloat); + GLint vstride = READ_DATA(sizeof(int) + 28, GLint); + GLint vorder = READ_DATA(sizeof(int) + 32, GLint); + GLfloat *points = DATA_POINTER(sizeof(int) + 36, GLfloat); + GLint cbMax; + + if (uorder < 1 || uorder > CR_MAX_EVAL_ORDER || + vorder < 1 || vorder > CR_MAX_EVAL_ORDER || + ustride < 1 || ustride > INT32_MAX / (ssize_t)sizeof(float) / uorder / 8 || + vstride < 1 || vstride > INT32_MAX / (ssize_t)sizeof(float) / vorder / 8) + { + crError("crUnpackMap2d: parameters out of range"); + return; + } + + cbMax = (ustride * uorder + vstride * vorder) * sizeof(float); + if (!DATA_POINTER_CHECK(cbMax)) + { + crError("crUnpackMap2f: parameters out of range"); + return; + } + + cr_unpackDispatch.Map2f(target, u1, u2, ustride, uorder, v1, v2, vstride, vorder, points); + + INCR_VAR_PTR(); +} + +void crUnpackMap1d(void) +{ + GLenum target = READ_DATA(sizeof(int) + 0, GLenum); + GLdouble u1 = READ_DOUBLE(sizeof(int) + 4); + GLdouble u2 = READ_DOUBLE(sizeof(int) + 12); + GLint stride = READ_DATA(sizeof(int) + 20, GLint); + GLint order = READ_DATA(sizeof(int) + 24, GLint); + GLdouble *points = DATA_POINTER(sizeof(int) + 28, GLdouble); + GLint cbMax; + + if (order < 1 || order > CR_MAX_EVAL_ORDER || + stride < 1 || stride > INT32_MAX / (ssize_t)sizeof(double) / order / 8) + { + crError("crUnpackMap1d: parameters out of range"); + return; + } + + cbMax = stride * order * sizeof(double); + if (!DATA_POINTER_CHECK(cbMax)) + { + crError("crUnpackMap1d: parameters out of range"); + return; + } + + cr_unpackDispatch.Map1d(target, u1, u2, stride, order, points); + + INCR_VAR_PTR(); +} + +void crUnpackMap1f(void) +{ + GLenum target = READ_DATA(sizeof(int) + 0, GLenum); + GLfloat u1 = READ_DATA(sizeof(int) + 4, GLfloat); + GLfloat u2 = READ_DATA(sizeof(int) + 8, GLfloat); + GLint stride = READ_DATA(sizeof(int) + 12, GLint); + GLint order = READ_DATA(sizeof(int) + 16, GLint); + GLfloat *points = DATA_POINTER(sizeof(int) + 20, GLfloat); + GLint cbMax; + + if (order < 1 || order > CR_MAX_EVAL_ORDER || + stride < 1 || stride > INT32_MAX / (ssize_t)sizeof(float) / order / 8) + { + crError("crUnpackMap1f: parameters out of range"); + return; + } + + cbMax = stride * order * sizeof(float); + if (!DATA_POINTER_CHECK(cbMax)) + { + crError("crUnpackMap1f: parameters out of range"); + return; + } + + cr_unpackDispatch.Map1f(target, u1, u2, stride, order, points); + INCR_VAR_PTR(); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_materials.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_materials.c new file mode 100644 index 00000000..f5c03991 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_materials.c @@ -0,0 +1,27 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" + +void crUnpackMaterialfv( void ) +{ + GLenum face = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat ); + + cr_unpackDispatch.Materialfv( face, pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackMaterialiv( void ) +{ + GLenum face = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint ); + + cr_unpackDispatch.Materialiv( face, pname, params ); + INCR_VAR_PTR(); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_matrices.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_matrices.c new file mode 100644 index 00000000..a36db771 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_matrices.c @@ -0,0 +1,72 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "cr_mem.h" + +void crUnpackMultMatrixd( void ) +{ + GLdouble m[16]; + crMemcpy( m, DATA_POINTER( 0, GLdouble ), sizeof(m) ); + + cr_unpackDispatch.MultMatrixd( m ); + INCR_DATA_PTR( 16*sizeof( GLdouble ) ); +} + +void crUnpackMultMatrixf( void ) +{ + GLfloat *m = DATA_POINTER( 0, GLfloat ); + + cr_unpackDispatch.MultMatrixf( m ); + INCR_DATA_PTR( 16*sizeof( GLfloat ) ); +} + +void crUnpackLoadMatrixd( void ) +{ + GLdouble m[16]; + crMemcpy( m, DATA_POINTER( 0, GLdouble ), sizeof(m) ); + + cr_unpackDispatch.LoadMatrixd( m ); + INCR_DATA_PTR( 16*sizeof( GLdouble ) ); +} + +void crUnpackLoadMatrixf( void ) +{ + GLfloat *m = DATA_POINTER( 0, GLfloat ); + + cr_unpackDispatch.LoadMatrixf( m ); + INCR_DATA_PTR( 16*sizeof( GLfloat ) ); +} + +void crUnpackExtendMultTransposeMatrixdARB( void ) +{ + GLdouble m[16]; + crMemcpy( m, DATA_POINTER( 8, GLdouble ), sizeof(m) ); + + cr_unpackDispatch.MultTransposeMatrixdARB( m ); +} + +void crUnpackExtendMultTransposeMatrixfARB( void ) +{ + GLfloat *m = DATA_POINTER( 8, GLfloat ); + + cr_unpackDispatch.MultTransposeMatrixfARB( m ); +} + +void crUnpackExtendLoadTransposeMatrixdARB( void ) +{ + GLdouble m[16]; + crMemcpy( m, DATA_POINTER( 8, GLdouble ), sizeof(m) ); + + cr_unpackDispatch.LoadTransposeMatrixdARB( m ); +} + +void crUnpackExtendLoadTransposeMatrixfARB( void ) +{ + GLfloat *m = DATA_POINTER( 8, GLfloat ); + + cr_unpackDispatch.LoadTransposeMatrixfARB( m ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c new file mode 100644 index 00000000..daf2b839 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_misc.c @@ -0,0 +1,87 @@ +/* + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" + +void crUnpackExtendChromiumParametervCR( void ) +{ + GLenum target = READ_DATA( 8, GLenum ); + GLenum type = READ_DATA( 12, GLenum ); + GLsizei count = READ_DATA( 16, GLsizei ); + GLvoid *values = DATA_POINTER( 20, GLvoid ); + + cr_unpackDispatch.ChromiumParametervCR(target, type, count, values); + + + /* + INCR_VAR_PTR(); + */ +} + +void crUnpackExtendDeleteQueriesARB(void) +{ + GLsizei n = READ_DATA( 8, GLsizei ); + const GLuint *ids = DATA_POINTER(12, GLuint); + cr_unpackDispatch.DeleteQueriesARB(n, ids); +} + +void crUnpackExtendGetPolygonStipple(void) +{ + GLubyte *mask; + + SET_RETURN_PTR( 8 ); + SET_WRITEBACK_PTR( 16 ); + mask = DATA_POINTER(8, GLubyte); + + cr_unpackDispatch.GetPolygonStipple( mask ); +} + +void crUnpackExtendGetPixelMapfv(void) +{ + GLenum map = READ_DATA( 8, GLenum ); + GLfloat *values; + + SET_RETURN_PTR( 12 ); + SET_WRITEBACK_PTR( 20 ); + values = DATA_POINTER(12, GLfloat); + + cr_unpackDispatch.GetPixelMapfv( map, values ); +} + +void crUnpackExtendGetPixelMapuiv(void) +{ + GLenum map = READ_DATA( 8, GLenum ); + GLuint *values; + + SET_RETURN_PTR( 12 ); + SET_WRITEBACK_PTR( 20 ); + values = DATA_POINTER(12, GLuint); + + cr_unpackDispatch.GetPixelMapuiv( map, values ); +} + +void crUnpackExtendGetPixelMapusv(void) +{ + GLenum map = READ_DATA( 8, GLenum ); + GLushort *values; + + SET_RETURN_PTR( 12 ); + SET_WRITEBACK_PTR( 20 ); + values = DATA_POINTER(12, GLushort); + + cr_unpackDispatch.GetPixelMapusv( map, values ); +} + +void crUnpackExtendVBoxTexPresent(void) +{ + GLuint texture = READ_DATA( 8, GLuint ); + GLuint cfg = READ_DATA( 12, GLuint ); + GLint xPos = READ_DATA( 16, GLint ); + GLint yPos = READ_DATA( 20, GLint ); + GLint cRects = READ_DATA( 24, GLint ); + GLint *pRects = (GLint *)DATA_POINTER( 28, GLvoid ); + cr_unpackDispatch.VBoxTexPresent( texture, cfg, xPos, yPos, cRects, pRects ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_pixelmap.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_pixelmap.c new file mode 100644 index 00000000..9d0eb1a6 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_pixelmap.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "state/cr_bufferobject.h" + +void crUnpackPixelMapfv( void ) +{ + GLenum map = READ_DATA( sizeof( int ) + 0, GLenum ); + GLsizei mapsize = READ_DATA( sizeof( int ) + 4, GLsizei ); + int nodata = READ_DATA( sizeof(int) + 8, int); + GLfloat *values; + + if (nodata && !crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + return; + + if (nodata) + values = (GLfloat*) (uintptr_t) READ_DATA(sizeof(int) + 12, GLint); + else + values = DATA_POINTER( sizeof( int ) + 16, GLfloat ); + + cr_unpackDispatch.PixelMapfv( map, mapsize, values ); + INCR_VAR_PTR(); +} + +void crUnpackPixelMapuiv( void ) +{ + GLenum map = READ_DATA( sizeof( int ) + 0, GLenum ); + GLsizei mapsize = READ_DATA( sizeof( int ) + 4, GLsizei ); + int nodata = READ_DATA( sizeof(int) + 8, int); + GLuint *values; + + if (nodata && !crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + return; + + if (nodata) + values = (GLuint*) (uintptr_t) READ_DATA(sizeof(int) + 12, GLint); + else + values = DATA_POINTER( sizeof( int ) + 16, GLuint ); + + cr_unpackDispatch.PixelMapuiv( map, mapsize, values ); + INCR_VAR_PTR(); +} + +void crUnpackPixelMapusv( void ) +{ + GLenum map = READ_DATA( sizeof( int ) + 0, GLenum ); + GLsizei mapsize = READ_DATA( sizeof( int ) + 4, GLsizei ); + int nodata = READ_DATA( sizeof(int) + 8, int); + GLushort *values; + + if (nodata && !crStateIsBufferBound(GL_PIXEL_UNPACK_BUFFER_ARB)) + return; + + if (nodata) + values = (GLushort*) (uintptr_t) READ_DATA(sizeof(int) + 12, GLint); + else + values = DATA_POINTER( sizeof( int ) + 16, GLushort ); + + cr_unpackDispatch.PixelMapusv( map, mapsize, values ); + INCR_VAR_PTR(); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_point.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_point.c new file mode 100644 index 00000000..1faa0a53 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_point.c @@ -0,0 +1,24 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" + +void crUnpackExtendPointParameterfvARB( void ) +{ + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat ); + cr_unpackDispatch.PointParameterfvARB( pname, params ); +} + +#if 1 +void crUnpackExtendPointParameteriv( void ) +{ + GLenum pname = READ_DATA( sizeof( int ) + 0, GLenum ); + GLint *params = DATA_POINTER( sizeof( int ) + 4, GLint ); + + cr_unpackDispatch.PointParameteriv( pname, params ); +} +#endif diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_program.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_program.c new file mode 100644 index 00000000..346b623c --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_program.c @@ -0,0 +1,386 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "cr_error.h" +#include "cr_protocol.h" +#include "cr_mem.h" +#include "cr_version.h" + + +void crUnpackExtendProgramParameter4dvNV(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLuint index = READ_DATA(12, GLuint); + GLdouble params[4]; + params[0] = READ_DOUBLE(16); + params[1] = READ_DOUBLE(24); + params[2] = READ_DOUBLE(32); + params[3] = READ_DOUBLE(40); + cr_unpackDispatch.ProgramParameter4dvNV(target, index, params); +} + + +void crUnpackExtendProgramParameter4fvNV(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLuint index = READ_DATA(12, GLuint); + GLfloat params[4]; + params[0] = READ_DATA(16, GLfloat); + params[1] = READ_DATA(20, GLfloat); + params[2] = READ_DATA(24, GLfloat); + params[3] = READ_DATA(28, GLfloat); + cr_unpackDispatch.ProgramParameter4fvNV(target, index, params); +} + + +void crUnpackExtendProgramParameters4dvNV(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLuint index = READ_DATA(12, GLuint); + GLuint num = READ_DATA(16, GLuint); + GLdouble *params; + + if (num <= 0 || num >= INT32_MAX / (4 * sizeof(GLdouble))) + { + crError("crUnpackExtendProgramParameters4dvNV: parameter 'num' is out of range"); + return; + } + + params = (GLdouble *)crAlloc(num * 4 * sizeof(GLdouble)); + + if (params) { + GLuint i; + for (i = 0; i < 4 * num; i++) { + params[i] = READ_DATA(20 + i * sizeof(GLdouble), GLdouble); + } + cr_unpackDispatch.ProgramParameters4dvNV(target, index, num, params); + crFree(params); + } +} + + +void crUnpackExtendProgramParameters4fvNV(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLuint index = READ_DATA(12, GLuint); + GLuint num = READ_DATA(16, GLuint); + GLfloat *params; + + if (num <= 0 || num >= INT32_MAX / (4 * sizeof(GLfloat))) + { + crError("crUnpackExtendProgramParameters4fvNV: parameter 'num' is out of range"); + return; + } + + params = (GLfloat *)crAlloc(num * 4 * sizeof(GLfloat)); + + if (params) { + GLuint i; + for (i = 0; i < 4 * num; i++) { + params[i] = READ_DATA(20 + i * sizeof(GLfloat), GLfloat); + } + cr_unpackDispatch.ProgramParameters4fvNV(target, index, num, params); + crFree(params); + } +} + + +void crUnpackExtendAreProgramsResidentNV(void) +{ + GLsizei n = READ_DATA(8, GLsizei); + const GLuint *programs = DATA_POINTER(12, const GLuint); + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint) / 4 || !DATA_POINTER_CHECK(20 + n * sizeof(GLuint))) + { + crError("crUnpackExtendAreProgramsResidentNV: %d is out of range", n); + return; + } + + SET_RETURN_PTR(12 + n * sizeof(GLuint)); + SET_WRITEBACK_PTR(20 + n * sizeof(GLuint)); + (void) cr_unpackDispatch.AreProgramsResidentNV(n, programs, NULL); +} + + +void crUnpackExtendLoadProgramNV(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLuint id = READ_DATA(12, GLuint); + GLsizei len = READ_DATA(16, GLsizei); + GLvoid *program = DATA_POINTER(20, GLvoid); + cr_unpackDispatch.LoadProgramNV(target, id, len, program); +} + + +void crUnpackExtendExecuteProgramNV(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLuint id = READ_DATA(12, GLuint); + GLfloat params[4]; + params[0] = READ_DATA(16, GLfloat); + params[1] = READ_DATA(20, GLfloat); + params[2] = READ_DATA(24, GLfloat); + params[3] = READ_DATA(28, GLfloat); + cr_unpackDispatch.ExecuteProgramNV(target, id, params); +} + +void crUnpackExtendRequestResidentProgramsNV(void) +{ + GLsizei n = READ_DATA(8, GLsizei); + crError("RequestResidentProgramsNV needs to be special cased!"); + cr_unpackDispatch.RequestResidentProgramsNV(n, NULL); +} + + +void crUnpackExtendProgramLocalParameter4fvARB(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLuint index = READ_DATA(12, GLuint); + GLfloat params[4]; + params[0] = READ_DATA(16, GLfloat); + params[1] = READ_DATA(20, GLfloat); + params[2] = READ_DATA(24, GLfloat); + params[3] = READ_DATA(28, GLfloat); + cr_unpackDispatch.ProgramLocalParameter4fvARB(target, index, params); +} + + +void crUnpackExtendProgramLocalParameter4dvARB(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLuint index = READ_DATA(12, GLuint); + GLdouble params[4]; + params[0] = READ_DOUBLE(16); + params[1] = READ_DOUBLE(24); + params[2] = READ_DOUBLE(32); + params[3] = READ_DOUBLE(40); + cr_unpackDispatch.ProgramLocalParameter4dvARB(target, index, params); +} + + + +void crUnpackExtendProgramNamedParameter4dvNV(void) +{ + GLuint id = READ_DATA(8, GLuint); + GLsizei len = READ_DATA(12, GLsizei); + GLdouble params[4]; + GLubyte *name = crAlloc(len); + params[0] = READ_DOUBLE(16); + params[1] = READ_DOUBLE(24); + params[2] = READ_DOUBLE(32); + params[3] = READ_DOUBLE(40); + crMemcpy(name, DATA_POINTER(48, GLubyte), len); + cr_unpackDispatch.ProgramNamedParameter4dvNV(id, len, name, params); +} + +void crUnpackExtendProgramNamedParameter4dNV(void) +{ + GLuint id = READ_DATA(8, GLuint); + GLsizei len = READ_DATA(12, GLsizei); + GLdouble params[4]; + GLubyte *name = crAlloc (len); + params[0] = READ_DOUBLE(16); + params[1] = READ_DOUBLE(24); + params[2] = READ_DOUBLE(32); + params[3] = READ_DOUBLE(40); + crMemcpy(name, DATA_POINTER(48, GLubyte), len); + cr_unpackDispatch.ProgramNamedParameter4dNV(id, len, name, params[0], params[1], params[2], params[3]); +} + +void crUnpackExtendProgramNamedParameter4fNV(void) +{ + GLenum id = READ_DATA(8, GLuint); + GLsizei len = READ_DATA(12, GLsizei); + GLfloat params[4]; + GLubyte *name = crAlloc(len); + params[0] = READ_DATA(16, GLfloat); + params[1] = READ_DATA(20, GLfloat); + params[2] = READ_DATA(24, GLfloat); + params[3] = READ_DATA(28, GLfloat); + crMemcpy(name, DATA_POINTER(32, GLubyte), len); + cr_unpackDispatch.ProgramNamedParameter4fNV(id, len, name, params[0], params[1], params[2], params[3]); +} + +void crUnpackExtendProgramNamedParameter4fvNV(void) +{ + GLenum id = READ_DATA(8, GLuint); + GLsizei len = READ_DATA(12, GLsizei); + GLfloat params[4]; + GLubyte *name = crAlloc(len); + params[0] = READ_DATA(16, GLfloat); + params[1] = READ_DATA(20, GLfloat); + params[2] = READ_DATA(24, GLfloat); + params[3] = READ_DATA(28, GLfloat); + crMemcpy(name, DATA_POINTER(32, GLubyte), len); + cr_unpackDispatch.ProgramNamedParameter4fvNV(id, len, name, params); +} + +void crUnpackExtendGetProgramNamedParameterdvNV(void) +{ + GLuint id = READ_DATA(8, GLuint); + GLsizei len = READ_DATA(12, GLsizei); + const GLubyte *name = DATA_POINTER(16, GLubyte); + + if (len <= 0 || len >= INT32_MAX / 4 || !DATA_POINTER_CHECK(16 + len + 8)) + { + crError("crUnpackExtendGetProgramNamedParameterdvNV: len %d is out of range", len); + return; + } + + SET_RETURN_PTR(16+len); + SET_WRITEBACK_PTR(16+len+8); + cr_unpackDispatch.GetProgramNamedParameterdvNV(id, len, name, NULL); +} + +void crUnpackExtendGetProgramNamedParameterfvNV(void) +{ + GLuint id = READ_DATA(8, GLuint); + GLsizei len = READ_DATA(12, GLsizei); + const GLubyte *name = DATA_POINTER(16, GLubyte); + + if (len <= 0 || len >= INT32_MAX / 4 || !DATA_POINTER_CHECK(16 + len + 8)) + { + crError("crUnpackExtendGetProgramNamedParameterfvNV: len %d is out of range", len); + return; + } + + SET_RETURN_PTR(16+len); + SET_WRITEBACK_PTR(16+len+8); + cr_unpackDispatch.GetProgramNamedParameterfvNV(id, len, name, NULL); +} + +void crUnpackExtendProgramStringARB(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLenum format = READ_DATA(12, GLuint); + GLsizei len = READ_DATA(16, GLsizei); + GLvoid *program = DATA_POINTER(20, GLvoid); + cr_unpackDispatch.ProgramStringARB(target, format, len, program); +} + +void crUnpackExtendGetProgramStringARB(void) +{ +} + +void crUnpackExtendProgramEnvParameter4dvARB(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLuint index = READ_DATA(12, GLuint); + GLdouble params[4]; + params[0] = READ_DOUBLE(16); + params[1] = READ_DOUBLE(24); + params[2] = READ_DOUBLE(32); + params[3] = READ_DOUBLE(40); + cr_unpackDispatch.ProgramEnvParameter4dvARB(target, index, params); +} + +void crUnpackExtendProgramEnvParameter4fvARB(void) +{ + GLenum target = READ_DATA(8, GLenum); + GLuint index = READ_DATA(12, GLuint); + GLfloat params[4]; + params[0] = READ_DATA(16, GLfloat); + params[1] = READ_DATA(20, GLfloat); + params[2] = READ_DATA(24, GLfloat); + params[3] = READ_DATA(28, GLfloat); + cr_unpackDispatch.ProgramEnvParameter4fvARB(target, index, params); +} + +void crUnpackExtendDeleteProgramsARB(void) +{ + GLsizei n = READ_DATA(8, GLsizei); + const GLuint *programs = DATA_POINTER(12, GLuint); + cr_unpackDispatch.DeleteProgramsARB(n, programs); +} + +void crUnpackVertexAttrib4NbvARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLbyte *v = DATA_POINTER(4, const GLbyte); + cr_unpackDispatch.VertexAttrib4NbvARB(index, v); + INCR_DATA_PTR(8); +} + +void crUnpackVertexAttrib4NivARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLint *v = DATA_POINTER(4, const GLint); + cr_unpackDispatch.VertexAttrib4NivARB(index, v); + INCR_DATA_PTR(20); +} + +void crUnpackVertexAttrib4NsvARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLshort *v = DATA_POINTER(4, const GLshort); + cr_unpackDispatch.VertexAttrib4NsvARB(index, v); + INCR_DATA_PTR(12); +} + +void crUnpackVertexAttrib4NubvARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLubyte *v = DATA_POINTER(4, const GLubyte); + cr_unpackDispatch.VertexAttrib4NubvARB(index, v); + INCR_DATA_PTR(8); +} + +void crUnpackVertexAttrib4NuivARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLuint *v = DATA_POINTER(4, const GLuint); + cr_unpackDispatch.VertexAttrib4NuivARB(index, v); + INCR_DATA_PTR(20); +} + +void crUnpackVertexAttrib4NusvARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLushort *v = DATA_POINTER(4, const GLushort); + cr_unpackDispatch.VertexAttrib4NusvARB(index, v); + INCR_DATA_PTR(12); +} + +void crUnpackVertexAttrib4bvARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLbyte *v = DATA_POINTER(4, const GLbyte); + cr_unpackDispatch.VertexAttrib4bvARB(index, v); + INCR_DATA_PTR(8); +} + +void crUnpackVertexAttrib4ivARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLint *v = DATA_POINTER(4, const GLint); + cr_unpackDispatch.VertexAttrib4ivARB(index, v); + INCR_DATA_PTR(20); +} + +void crUnpackVertexAttrib4ubvARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLubyte *v = DATA_POINTER(4, const GLubyte); + cr_unpackDispatch.VertexAttrib4ubvARB(index, v); + INCR_DATA_PTR(8); +} + +void crUnpackVertexAttrib4uivARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLuint *v = DATA_POINTER(4, const GLuint); + cr_unpackDispatch.VertexAttrib4uivARB(index, v); + INCR_DATA_PTR(20); +} + +void crUnpackVertexAttrib4usvARB(void) +{ + GLuint index = READ_DATA(0, GLuint); + const GLushort *v = DATA_POINTER(4, const GLushort); + cr_unpackDispatch.VertexAttrib4usvARB(index, v); + INCR_DATA_PTR(12); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_readpixels.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_readpixels.c new file mode 100644 index 00000000..34b73616 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_readpixels.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "cr_pixeldata.h" +#include "cr_mem.h" + +void crUnpackReadPixels( void ) +{ + GLint x = READ_DATA( 0, GLint ); + GLint y = READ_DATA( 4, GLint ); + GLsizei width = READ_DATA( 8, GLsizei ); + GLsizei height = READ_DATA( 12, GLsizei ); + GLenum format = READ_DATA( 16, GLenum ); + GLenum type = READ_DATA( 20, GLenum ); + GLint stride = READ_DATA( 24, GLint ); + GLint alignment = READ_DATA( 28, GLint ); + GLint skipRows = READ_DATA( 32, GLint ); + GLint skipPixels = READ_DATA( 36, GLint ); + GLint bytes_per_row = READ_DATA( 40, GLint ); + GLint rowLength = READ_DATA( 44, GLint ); + GLvoid *pixels; + + /* point <pixels> at the 8-byte network pointer */ + pixels = DATA_POINTER( 48, GLvoid ); + + (void) stride; + (void) bytes_per_row; + (void) alignment; + (void) skipRows; + (void) skipPixels; + (void) rowLength; + + /* we always pack densely on the server side! */ + cr_unpackDispatch.PixelStorei( GL_PACK_ROW_LENGTH, 0 ); + cr_unpackDispatch.PixelStorei( GL_PACK_SKIP_PIXELS, 0 ); + cr_unpackDispatch.PixelStorei( GL_PACK_SKIP_ROWS, 0 ); + cr_unpackDispatch.PixelStorei( GL_PACK_ALIGNMENT, 1 ); + + cr_unpackDispatch.ReadPixels( x, y, width, height, format, type, pixels); + + INCR_DATA_PTR(48+sizeof(CRNetworkPointer)); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_regcombiner.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_regcombiner.c new file mode 100644 index 00000000..9ebd777f --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_regcombiner.c @@ -0,0 +1,35 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" + +void crUnpackExtendCombinerParameterfvNV( void ) +{ + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat ); + + cr_unpackDispatch.CombinerParameterfvNV( pname, params ); + /* Don't call INCR_VAR_PTR(); - it's done in crUnpackExtend() */ +} + +void crUnpackExtendCombinerParameterivNV( void ) +{ + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint ); + + cr_unpackDispatch.CombinerParameterivNV( pname, params ); + /* Don't call INCR_VAR_PTR(); - it's done in crUnpackExtend() */ +} + +void crUnpackExtendCombinerStageParameterfvNV( void ) +{ + GLenum stage = READ_DATA( sizeof( int ) + 4, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 8, GLenum ); + GLfloat *params = DATA_POINTER( sizeof( int ) + 12, GLfloat ); + + cr_unpackDispatch.CombinerStageParameterfvNV( stage, pname, params ); + /* Don't call INCR_VAR_PTR(); - it's done in crUnpackExtend() */ +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c new file mode 100644 index 00000000..8bc04e2d --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c @@ -0,0 +1,386 @@ +/* $Id: unpack_shaders.c $ */ +/** @file + * VBox OpenGL DRI driver functions + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +#include "unpacker.h" +#include "cr_error.h" +#include "cr_protocol.h" +#include "cr_mem.h" +#include "cr_string.h" +#include "cr_version.h" + +void crUnpackExtendBindAttribLocation(void) +{ + GLuint program = READ_DATA(8, GLuint); + GLuint index = READ_DATA(12, GLuint); + const char *name = DATA_POINTER(16, const char); + + cr_unpackDispatch.BindAttribLocation(program, index, name); +} + +void crUnpackExtendShaderSource(void) +{ + GLint *length = NULL; + GLuint shader = READ_DATA(8, GLuint); + GLsizei count = READ_DATA(12, GLsizei); + GLint hasNonLocalLen = READ_DATA(16, GLsizei); + GLint *pLocalLength = DATA_POINTER(20, GLint); + char **ppStrings = NULL; + GLsizei i, j, jUpTo; + int pos, pos_check; + + if (count <= 0 || count >= INT32_MAX / sizeof(char *) / 4) + { + crError("crUnpackExtendShaderSource: count %u is out of range", count); + return; + } + + pos = 20 + count * sizeof(*pLocalLength); + + if (hasNonLocalLen > 0) + { + length = DATA_POINTER(pos, GLint); + pos += count * sizeof(*length); + } + + pos_check = pos; + + if (!DATA_POINTER_CHECK(pos_check)) + { + crError("crUnpackExtendShaderSource: pos %d is out of range", pos_check); + return; + } + + for (i = 0; i < count; ++i) + { + if (pLocalLength[i] <= 0 || pos_check >= INT32_MAX - pLocalLength[i] || !DATA_POINTER_CHECK(pos_check)) + { + crError("crUnpackExtendShaderSource: pos %d is out of range", pos_check); + return; + } + + pos_check += pLocalLength[i]; + } + + ppStrings = crAlloc(count * sizeof(char*)); + if (!ppStrings) return; + + for (i = 0; i < count; ++i) + { + ppStrings[i] = DATA_POINTER(pos, char); + pos += pLocalLength[i]; + if (!length) + { + pLocalLength[i] -= 1; + } + + Assert(pLocalLength[i] > 0); + jUpTo = i == count -1 ? pLocalLength[i] - 1 : pLocalLength[i]; + for (j = 0; j < jUpTo; ++j) + { + char *pString = ppStrings[i]; + + if (pString[j] == '\0') + { + Assert(j == jUpTo - 1); + pString[j] = '\n'; + } + } + } + +// cr_unpackDispatch.ShaderSource(shader, count, ppStrings, length ? length : pLocalLength); + cr_unpackDispatch.ShaderSource(shader, 1, (const char**)ppStrings, 0); + + crFree(ppStrings); +} + +void crUnpackExtendUniform1fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + const GLfloat *value = DATA_POINTER(16, const GLfloat); + cr_unpackDispatch.Uniform1fv(location, count, value); +} + +void crUnpackExtendUniform1iv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + const GLint *value = DATA_POINTER(16, const GLint); + cr_unpackDispatch.Uniform1iv(location, count, value); +} + +void crUnpackExtendUniform2fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + const GLfloat *value = DATA_POINTER(16, const GLfloat); + cr_unpackDispatch.Uniform2fv(location, count, value); +} + +void crUnpackExtendUniform2iv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + const GLint *value = DATA_POINTER(16, const GLint); + cr_unpackDispatch.Uniform2iv(location, count, value); +} + +void crUnpackExtendUniform3fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + const GLfloat *value = DATA_POINTER(16, const GLfloat); + cr_unpackDispatch.Uniform3fv(location, count, value); +} + +void crUnpackExtendUniform3iv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + const GLint *value = DATA_POINTER(16, const GLint); + cr_unpackDispatch.Uniform3iv(location, count, value); +} + +void crUnpackExtendUniform4fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + const GLfloat *value = DATA_POINTER(16, const GLfloat); + cr_unpackDispatch.Uniform4fv(location, count, value); +} + +void crUnpackExtendUniform4iv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + const GLint *value = DATA_POINTER(16, const GLint); + cr_unpackDispatch.Uniform4iv(location, count, value); +} + +void crUnpackExtendUniformMatrix2fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + GLboolean transpose = READ_DATA(16, GLboolean); + const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat); + cr_unpackDispatch.UniformMatrix2fv(location, count, transpose, value); +} + +void crUnpackExtendUniformMatrix3fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + GLboolean transpose = READ_DATA(16, GLboolean); + const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat); + cr_unpackDispatch.UniformMatrix3fv(location, count, transpose, value); +} + +void crUnpackExtendUniformMatrix4fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + GLboolean transpose = READ_DATA(16, GLboolean); + const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat); + cr_unpackDispatch.UniformMatrix4fv(location, count, transpose, value); +} + +void crUnpackExtendUniformMatrix2x3fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + GLboolean transpose = READ_DATA(16, GLboolean); + const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat); + cr_unpackDispatch.UniformMatrix2x3fv(location, count, transpose, value); +} + +void crUnpackExtendUniformMatrix3x2fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + GLboolean transpose = READ_DATA(16, GLboolean); + const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat); + cr_unpackDispatch.UniformMatrix3x2fv(location, count, transpose, value); +} + +void crUnpackExtendUniformMatrix2x4fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + GLboolean transpose = READ_DATA(16, GLboolean); + const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat); + cr_unpackDispatch.UniformMatrix2x4fv(location, count, transpose, value); +} + +void crUnpackExtendUniformMatrix4x2fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + GLboolean transpose = READ_DATA(16, GLboolean); + const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat); + cr_unpackDispatch.UniformMatrix4x2fv(location, count, transpose, value); +} + +void crUnpackExtendUniformMatrix3x4fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + GLboolean transpose = READ_DATA(16, GLboolean); + const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat); + cr_unpackDispatch.UniformMatrix3x4fv(location, count, transpose, value); +} + +void crUnpackExtendUniformMatrix4x3fv(void) +{ + GLint location = READ_DATA(8, GLint); + GLsizei count = READ_DATA(12, GLsizei); + GLboolean transpose = READ_DATA(16, GLboolean); + const GLfloat *value = DATA_POINTER(16+sizeof(GLboolean), const GLfloat); + cr_unpackDispatch.UniformMatrix4x3fv(location, count, transpose, value); +} + +void crUnpackExtendDrawBuffers(void) +{ + GLsizei n = READ_DATA(8, GLsizei); + const GLenum *bufs = DATA_POINTER(8+sizeof(GLsizei), const GLenum); + cr_unpackDispatch.DrawBuffers(n, bufs); +} + +void crUnpackExtendGetActiveAttrib(void) +{ + GLuint program = READ_DATA(8, GLuint); + GLuint index = READ_DATA(12, GLuint); + GLsizei bufSize = READ_DATA(16, GLsizei); + SET_RETURN_PTR(20); + SET_WRITEBACK_PTR(28); + cr_unpackDispatch.GetActiveAttrib(program, index, bufSize, NULL, NULL, NULL, NULL); +} + +void crUnpackExtendGetActiveUniform(void) +{ + GLuint program = READ_DATA(8, GLuint); + GLuint index = READ_DATA(12, GLuint); + GLsizei bufSize = READ_DATA(16, GLsizei); + SET_RETURN_PTR(20); + SET_WRITEBACK_PTR(28); + cr_unpackDispatch.GetActiveUniform(program, index, bufSize, NULL, NULL, NULL, NULL); +} + +void crUnpackExtendGetAttachedShaders(void) +{ + GLuint program = READ_DATA(8, GLuint); + GLsizei maxCount = READ_DATA(12, GLsizei); + SET_RETURN_PTR(16); + SET_WRITEBACK_PTR(24); + cr_unpackDispatch.GetAttachedShaders(program, maxCount, NULL, NULL); +} + +void crUnpackExtendGetAttachedObjectsARB(void) +{ + VBoxGLhandleARB containerObj = READ_DATA(8, VBoxGLhandleARB); + GLsizei maxCount = READ_DATA(12, GLsizei); + SET_RETURN_PTR(16); + SET_WRITEBACK_PTR(24); + cr_unpackDispatch.GetAttachedObjectsARB(containerObj, maxCount, NULL, NULL); +} + +void crUnpackExtendGetInfoLogARB(void) +{ + VBoxGLhandleARB obj = READ_DATA(8, VBoxGLhandleARB); + GLsizei maxLength = READ_DATA(12, GLsizei); + SET_RETURN_PTR(16); + SET_WRITEBACK_PTR(24); + cr_unpackDispatch.GetInfoLogARB(obj, maxLength, NULL, NULL); +} + +void crUnpackExtendGetProgramInfoLog(void) +{ + GLuint program = READ_DATA(8, GLuint); + GLsizei bufSize = READ_DATA(12, GLsizei); + SET_RETURN_PTR(16); + SET_WRITEBACK_PTR(24); + cr_unpackDispatch.GetProgramInfoLog(program, bufSize, NULL, NULL); +} + +void crUnpackExtendGetShaderInfoLog(void) +{ + GLuint shader = READ_DATA(8, GLuint); + GLsizei bufSize = READ_DATA(12, GLsizei); + SET_RETURN_PTR(16); + SET_WRITEBACK_PTR(24); + cr_unpackDispatch.GetShaderInfoLog(shader, bufSize, NULL, NULL); +} + +void crUnpackExtendGetShaderSource(void) +{ + GLuint shader = READ_DATA(8, GLuint); + GLsizei bufSize = READ_DATA(12, GLsizei); + SET_RETURN_PTR(16); + SET_WRITEBACK_PTR(24); + cr_unpackDispatch.GetShaderSource(shader, bufSize, NULL, NULL); +} + +void crUnpackExtendGetAttribLocation(void) +{ + int packet_length = READ_DATA(0, int); + GLuint program = READ_DATA(8, GLuint); + const char *name = DATA_POINTER(12, const char); + + if (!DATA_POINTER_CHECK(packet_length)) + { + crError("crUnpackExtendGetAttribLocation: packet_length is out of range"); + return; + } + + SET_RETURN_PTR(packet_length-16); + SET_WRITEBACK_PTR(packet_length-8); + cr_unpackDispatch.GetAttribLocation(program, name); +} + +void crUnpackExtendGetUniformLocation(void) +{ + int packet_length = READ_DATA(0, int); + GLuint program = READ_DATA(8, GLuint); + const char *name = DATA_POINTER(12, const char); + + if (!DATA_POINTER_CHECK(packet_length)) + { + crError("crUnpackExtendGetUniformLocation: packet_length is out of range"); + return; + } + + SET_RETURN_PTR(packet_length-16); + SET_WRITEBACK_PTR(packet_length-8); + cr_unpackDispatch.GetUniformLocation(program, name); +} + +void crUnpackExtendGetUniformsLocations(void) +{ + GLuint program = READ_DATA(8, GLuint); + GLsizei maxcbData = READ_DATA(12, GLsizei); + SET_RETURN_PTR(16); + SET_WRITEBACK_PTR(24); + cr_unpackDispatch.GetUniformsLocations(program, maxcbData, NULL, NULL); +} + +void crUnpackExtendGetAttribsLocations(void) +{ + GLuint program = READ_DATA(8, GLuint); + GLsizei maxcbData = READ_DATA(12, GLsizei); + SET_RETURN_PTR(16); + SET_WRITEBACK_PTR(24); + cr_unpackDispatch.GetAttribsLocations(program, maxcbData, NULL, NULL); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_stipple.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_stipple.c new file mode 100644 index 00000000..dcc3ab58 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_stipple.c @@ -0,0 +1,27 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" + +void crUnpackPolygonStipple( void ) +{ + int nodata = READ_DATA(0, int); + + if (nodata) + { + crError("crUnpackPolygonStipple: GL_PIXEL_UNPACK_BUFFER is not supported"); + INCR_DATA_PTR(8); + } + else + { + GLubyte *mask; + + mask = DATA_POINTER(4, GLubyte); + cr_unpackDispatch.PolygonStipple(mask); + // Stipple mask consists of 32 * 32 bits + INCR_DATA_PTR(4 + 32 * 32 / 8); + } +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_texture.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_texture.c new file mode 100644 index 00000000..8c757e95 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_texture.c @@ -0,0 +1,507 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "cr_error.h" +#include "cr_protocol.h" +#include "cr_mem.h" +#include "cr_version.h" + +#if defined( GL_EXT_texture3D ) +void crUnpackTexImage3DEXT( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLint level = READ_DATA( sizeof( int ) + 4, GLint ); + GLenum internalformat = READ_DATA( sizeof( int ) + 8, GLint ); + GLsizei width = READ_DATA( sizeof( int ) + 12, GLsizei ); + GLsizei height = READ_DATA( sizeof( int ) + 16, GLsizei ); + GLsizei depth = READ_DATA( sizeof( int ) + 20, GLsizei ); + GLint border = READ_DATA( sizeof( int ) + 24, GLint ); + GLenum format = READ_DATA( sizeof( int ) + 28, GLenum ); + GLenum type = READ_DATA( sizeof( int ) + 32, GLenum ); + int noimagedata = READ_DATA( sizeof( int ) + 36, int ); + GLvoid *pixels; + + /*If there's no imagedata send, it's either that passed pointer was NULL or + there was GL_PIXEL_UNPACK_BUFFER_ARB bound, in both cases 4bytes of passed + pointer would convert to either NULL or offset in the bound buffer. + */ + if ( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+40, GLint); + else + pixels = DATA_POINTER( sizeof( int ) + 44, GLvoid ); + + cr_unpackDispatch.TexImage3DEXT(target, level, internalformat, width, + height, depth, border, format, type, + pixels); + INCR_VAR_PTR(); +} +#endif /* GL_EXT_texture3D */ + +#if defined( CR_OPENGL_VERSION_1_2 ) +void crUnpackTexImage3D( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLint level = READ_DATA( sizeof( int ) + 4, GLint ); + GLint internalformat = READ_DATA( sizeof( int ) + 8, GLint ); + GLsizei width = READ_DATA( sizeof( int ) + 12, GLsizei ); + GLsizei height = READ_DATA( sizeof( int ) + 16, GLsizei ); + GLsizei depth = READ_DATA( sizeof( int ) + 20, GLsizei ); + GLint border = READ_DATA( sizeof( int ) + 24, GLint ); + GLenum format = READ_DATA( sizeof( int ) + 28, GLenum ); + GLenum type = READ_DATA( sizeof( int ) + 32, GLenum ); + int noimagedata = READ_DATA( sizeof( int ) + 36, int ); + GLvoid *pixels; + + if ( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+40, GLint); + else + pixels = DATA_POINTER( sizeof( int ) + 44, GLvoid ); + + cr_unpackDispatch.TexImage3D( target, level, internalformat, width, height, + depth, border, format, type, pixels ); + INCR_VAR_PTR(); +} +#endif /* CR_OPENGL_VERSION_1_2 */ + +void crUnpackTexImage2D( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLint level = READ_DATA( sizeof( int ) + 4, GLint ); + GLint internalformat = READ_DATA( sizeof( int ) + 8, GLint ); + GLsizei width = READ_DATA( sizeof( int ) + 12, GLsizei ); + GLsizei height = READ_DATA( sizeof( int ) + 16, GLsizei ); + GLint border = READ_DATA( sizeof( int ) + 20, GLint ); + GLenum format = READ_DATA( sizeof( int ) + 24, GLenum ); + GLenum type = READ_DATA( sizeof( int ) + 28, GLenum ); + int noimagedata = READ_DATA( sizeof( int ) + 32, int ); + GLvoid *pixels; + + if ( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+36, GLint); + else + pixels = DATA_POINTER( sizeof( int ) + 40, GLvoid ); + + cr_unpackDispatch.TexImage2D( target, level, internalformat, width, height, + border, format, type, pixels ); + INCR_VAR_PTR(); +} + +void crUnpackTexImage1D( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLint level = READ_DATA( sizeof( int ) + 4, GLint ); + GLint internalformat = READ_DATA( sizeof( int ) + 8, GLint ); + GLsizei width = READ_DATA( sizeof( int ) + 12, GLsizei ); + GLint border = READ_DATA( sizeof( int ) + 16, GLint ); + GLenum format = READ_DATA( sizeof( int ) + 20, GLenum ); + GLenum type = READ_DATA( sizeof( int ) + 24, GLenum ); + int noimagedata = READ_DATA( sizeof( int ) + 28, int ); + GLvoid *pixels; + + if ( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+32, GLint); + else + pixels = DATA_POINTER( sizeof( int ) + 36, GLvoid ); + + cr_unpackDispatch.TexImage1D( target, level, internalformat, width, border, + format, type, pixels ); + INCR_VAR_PTR(); +} + +void crUnpackDeleteTextures( void ) +{ + GLsizei n = READ_DATA( sizeof( int ) + 0, GLsizei ); + GLuint *textures = DATA_POINTER( sizeof( int ) + 4, GLuint ); + + cr_unpackDispatch.DeleteTextures( n, textures ); + INCR_VAR_PTR(); +} + + +void crUnpackPrioritizeTextures( void ) +{ + GLsizei n = READ_DATA( sizeof( int ) + 0, GLsizei ); + GLuint *textures = DATA_POINTER( sizeof( int ) + 4, GLuint ); + GLclampf *priorities = DATA_POINTER( sizeof( int ) + 4 + n*sizeof( GLuint ), + GLclampf ); + + cr_unpackDispatch.PrioritizeTextures( n, textures, priorities ); + INCR_VAR_PTR(); +} + +void crUnpackTexParameterfv( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat ); + + cr_unpackDispatch.TexParameterfv( target, pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackTexParameteriv( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint ); + + cr_unpackDispatch.TexParameteriv( target, pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackTexParameterf( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLfloat param = READ_DATA( sizeof( int ) + 8, GLfloat ); + + cr_unpackDispatch.TexParameterf( target, pname, param ); + INCR_VAR_PTR(); +} + +void crUnpackTexParameteri( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLint param = READ_DATA( sizeof( int ) + 8, GLint ); + + cr_unpackDispatch.TexParameteri( target, pname, param ); + INCR_VAR_PTR(); +} + +#if defined(CR_OPENGL_VERSION_1_2) +void crUnpackTexSubImage3D( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLint level = READ_DATA( sizeof( int ) + 4, GLint ); + GLint xoffset = READ_DATA( sizeof( int ) + 8, GLint ); + GLint yoffset = READ_DATA( sizeof( int ) + 12, GLint ); + GLint zoffset = READ_DATA( sizeof( int ) + 16, GLint ); + GLsizei width = READ_DATA( sizeof( int ) + 20, GLsizei ); + GLsizei height = READ_DATA( sizeof( int ) + 24, GLsizei ); + GLsizei depth = READ_DATA( sizeof( int ) + 28, GLsizei ); + GLenum format = READ_DATA( sizeof( int ) + 32, GLenum ); + GLenum type = READ_DATA( sizeof( int ) + 36, GLenum ); + int noimagedata = READ_DATA( sizeof( int ) + 40, int ); + GLvoid *pixels; + + if ( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+44, GLint); + else + pixels = DATA_POINTER( sizeof( int ) + 48, GLvoid ); + + cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + + cr_unpackDispatch.TexSubImage3D(target, level, xoffset, yoffset, zoffset, + width, height, depth, format, type, pixels); + INCR_VAR_PTR(); +} +#endif /* CR_OPENGL_VERSION_1_2 */ + +void crUnpackTexSubImage2D( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLint level = READ_DATA( sizeof( int ) + 4, GLint ); + GLint xoffset = READ_DATA( sizeof( int ) + 8, GLint ); + GLint yoffset = READ_DATA( sizeof( int ) + 12, GLint ); + GLsizei width = READ_DATA( sizeof( int ) + 16, GLsizei ); + GLsizei height = READ_DATA( sizeof( int ) + 20, GLsizei ); + GLenum format = READ_DATA( sizeof( int ) + 24, GLenum ); + GLenum type = READ_DATA( sizeof( int ) + 28, GLenum ); + int noimagedata = READ_DATA( sizeof( int ) + 32, int ); + GLvoid *pixels; + + if ( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+36, GLint); + else + pixels = DATA_POINTER( sizeof( int ) + 40, GLvoid ); + + cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + + cr_unpackDispatch.TexSubImage2D( target, level, xoffset, yoffset, width, + height, format, type, pixels ); + INCR_VAR_PTR(); +} + +void crUnpackTexSubImage1D( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLint level = READ_DATA( sizeof( int ) + 4, GLint ); + GLint xoffset = READ_DATA( sizeof( int ) + 8, GLint ); + GLsizei width = READ_DATA( sizeof( int ) + 12, GLsizei ); + GLenum format = READ_DATA( sizeof( int ) + 16, GLenum ); + GLenum type = READ_DATA( sizeof( int ) + 20, GLenum ); + int noimagedata = READ_DATA( sizeof( int ) + 24, int ); + GLvoid *pixels; + + if ( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(sizeof(int)+28, GLint); + else + pixels = DATA_POINTER( sizeof( int ) + 32, GLvoid ); + + cr_unpackDispatch.PixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_PIXELS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_SKIP_ROWS, 0 ); + cr_unpackDispatch.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + + cr_unpackDispatch.TexSubImage1D( target, level, xoffset, width, format, + type, pixels ); + INCR_VAR_PTR(); +} + + +void crUnpackTexEnvfv( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat ); + + cr_unpackDispatch.TexEnvfv( target, pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackTexEnviv( void ) +{ + GLenum target = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint ); + + cr_unpackDispatch.TexEnviv( target, pname, params ); + INCR_VAR_PTR(); +} + +#define DATA_POINTER_DOUBLE( offset ) + +void crUnpackTexGendv( void ) +{ + GLenum coord = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLdouble params[4]; + unsigned int n_param = READ_DATA( 0, int ) - ( sizeof(int) + 8 ); + + if (n_param > sizeof(params)) + { + crError("crUnpackTexGendv: n_param=%d, expected <= %d\n", n_param, + (unsigned int)sizeof(params)); + return; + } + + crMemcpy( params, DATA_POINTER( sizeof( int ) + 8, GLdouble ), n_param ); + + cr_unpackDispatch.TexGendv( coord, pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackTexGenfv( void ) +{ + GLenum coord = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLfloat *params = DATA_POINTER( sizeof( int ) + 8, GLfloat ); + + cr_unpackDispatch.TexGenfv( coord, pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackTexGeniv( void ) +{ + GLenum coord = READ_DATA( sizeof( int ) + 0, GLenum ); + GLenum pname = READ_DATA( sizeof( int ) + 4, GLenum ); + GLint *params = DATA_POINTER( sizeof( int ) + 8, GLint ); + + cr_unpackDispatch.TexGeniv( coord, pname, params ); + INCR_VAR_PTR(); +} + +void crUnpackExtendAreTexturesResident( void ) +{ + GLsizei n = READ_DATA( 8, GLsizei ); + const GLuint *textures = DATA_POINTER( 12, const GLuint ); + + if (n <= 0 || n >= INT32_MAX / sizeof(GLuint) / 4 || !DATA_POINTER_CHECK(20 + n * sizeof(GLuint))) + { + crError("crUnpackExtendAreTexturesResident: %d is out of range", n); + return; + } + + SET_RETURN_PTR(12 + n * sizeof(GLuint)); + SET_WRITEBACK_PTR(20 + n * sizeof(GLuint)); + (void) cr_unpackDispatch.AreTexturesResident( n, textures, NULL ); +} + + +void crUnpackExtendCompressedTexImage3DARB( void ) +{ + GLenum target = READ_DATA( 4 + sizeof(int) + 0, GLenum ); + GLint level = READ_DATA( 4 + sizeof(int) + 4, GLint ); + GLenum internalformat = READ_DATA( 4 + sizeof(int) + 8, GLenum ); + GLsizei width = READ_DATA( 4 + sizeof(int) + 12, GLsizei ); + GLsizei height = READ_DATA( 4 + sizeof(int) + 16, GLsizei ); + GLsizei depth = READ_DATA( 4 + sizeof(int) + 20, GLsizei ); + GLint border = READ_DATA( 4 + sizeof(int) + 24, GLint ); + GLsizei imagesize = READ_DATA( 4 + sizeof(int) + 28, GLsizei ); + int noimagedata = READ_DATA( 4 + sizeof(int) + 32, int ); + GLvoid *pixels; + + if( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+36, GLint); + else + pixels = DATA_POINTER( 4 + sizeof(int) + 40, GLvoid ); + + cr_unpackDispatch.CompressedTexImage3DARB(target, level, internalformat, + width, height, depth, border, + imagesize, pixels); +} + + +void crUnpackExtendCompressedTexImage2DARB( void ) +{ + GLenum target = READ_DATA( 4 + sizeof( int ) + 0, GLenum ); + GLint level = READ_DATA( 4 + sizeof( int ) + 4, GLint ); + GLenum internalformat = READ_DATA( 4 + sizeof( int ) + 8, GLenum ); + GLsizei width = READ_DATA( 4 + sizeof( int ) + 12, GLsizei ); + GLsizei height = READ_DATA( 4 + sizeof( int ) + 16, GLsizei ); + GLint border = READ_DATA( 4 + sizeof( int ) + 20, GLint ); + GLsizei imagesize = READ_DATA( 4 + sizeof( int ) + 24, GLsizei ); + int noimagedata = READ_DATA( 4 + sizeof( int ) + 28, int ); + GLvoid *pixels; + + if ( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+32, GLint); + else + pixels = DATA_POINTER( 4 + sizeof( int ) + 36, GLvoid ); + + cr_unpackDispatch.CompressedTexImage2DARB( target, level, internalformat, + width, height, border, imagesize, + pixels ); +} + + +void crUnpackExtendCompressedTexImage1DARB( void ) +{ + GLenum target = READ_DATA( 4 + sizeof(int) + 0, GLenum ); + GLint level = READ_DATA( 4 + sizeof(int) + 4, GLint ); + GLenum internalformat = READ_DATA( 4 + sizeof(int) + 8, GLenum ); + GLsizei width = READ_DATA( 4 + sizeof(int) + 12, GLsizei ); + GLint border = READ_DATA( 4 + sizeof(int) + 16, GLint ); + GLsizei imagesize = READ_DATA( 4 + sizeof(int) + 20, GLsizei ); + int noimagedata = READ_DATA( 4 + sizeof(int) + 24, int ); + GLvoid *pixels; + + if( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+28, GLint); + else + pixels = DATA_POINTER( 4 + sizeof(int) + 32, GLvoid ); + + cr_unpackDispatch.CompressedTexImage1DARB(target, level, internalformat, + width, border, imagesize, pixels); +} + + +void crUnpackExtendCompressedTexSubImage3DARB( void ) +{ + GLenum target = READ_DATA( 4 + sizeof(int) + 0, GLenum ); + GLint level = READ_DATA( 4 + sizeof(int) + 4, GLint ); + GLint xoffset = READ_DATA( 4 + sizeof(int) + 8, GLint ); + GLint yoffset = READ_DATA( 4 + sizeof(int) + 12, GLint ); + GLint zoffset = READ_DATA( 4 + sizeof(int) + 16, GLint ); + GLsizei width = READ_DATA( 4 + sizeof(int) + 20, GLsizei ); + GLsizei height = READ_DATA( 4 + sizeof(int) + 24, GLsizei ); + GLsizei depth = READ_DATA( 4 + sizeof(int) + 28, GLsizei ); + GLenum format = READ_DATA( 4 + sizeof(int) + 32, GLenum ); + GLsizei imagesize = READ_DATA( 4 + sizeof(int) + 36, GLsizei ); + int noimagedata = READ_DATA( 4 + sizeof(int) + 40, int ); + GLvoid *pixels; + + if( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+44, GLint); + else + pixels = DATA_POINTER( 4 + sizeof(int) + 48, GLvoid ); + + cr_unpackDispatch.CompressedTexSubImage3DARB(target, level, xoffset, + yoffset, zoffset, width, + height, depth, format, + imagesize, pixels); +} + + +void crUnpackExtendCompressedTexSubImage2DARB( void ) +{ + GLenum target = READ_DATA( 4 + sizeof(int) + 0, GLenum ); + GLint level = READ_DATA( 4 + sizeof(int) + 4, GLint ); + GLint xoffset = READ_DATA( 4 + sizeof(int) + 8, GLint ); + GLint yoffset = READ_DATA( 4 + sizeof(int) + 12, GLint ); + GLsizei width = READ_DATA( 4 + sizeof(int) + 16, GLsizei ); + GLsizei height = READ_DATA( 4 + sizeof(int) + 20, GLsizei ); + GLenum format = READ_DATA( 4 + sizeof(int) + 24, GLenum ); + GLsizei imagesize = READ_DATA( 4 + sizeof(int) + 28, GLsizei ); + int noimagedata = READ_DATA( 4 + sizeof(int) + 32, int ); + GLvoid *pixels; + + if( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+36, GLint); + else + pixels = DATA_POINTER( 4 + sizeof(int) + 40, GLvoid ); + + cr_unpackDispatch.CompressedTexSubImage2DARB(target, level, xoffset, + yoffset, width, height, + format, imagesize, pixels); +} + + +void crUnpackExtendCompressedTexSubImage1DARB( void ) +{ + GLenum target = READ_DATA( 4 + sizeof(int) + 0, GLenum ); + GLint level = READ_DATA( 4 + sizeof(int) + 4, GLint ); + GLint xoffset = READ_DATA( 4 + sizeof(int) + 8, GLint ); + GLsizei width = READ_DATA( 4 + sizeof(int) + 12, GLsizei ); + GLenum format = READ_DATA( 4 + sizeof(int) + 16, GLenum ); + GLsizei imagesize = READ_DATA( 4 + sizeof(int) + 20, GLsizei ); + int noimagedata = READ_DATA( 4 + sizeof(int) + 24, int ); + GLvoid *pixels; + + if( noimagedata ) + pixels = (void*) (uintptr_t) READ_DATA(4+sizeof(int)+28, GLint); + else + pixels = DATA_POINTER( 4 + sizeof(int) + 32, GLvoid ); + + cr_unpackDispatch.CompressedTexSubImage1DARB(target, level, xoffset, width, + format, imagesize, pixels); +} + +void crUnpackExtendGetTexImage(void) +{ + GLenum target = READ_DATA( 8, GLenum ); + GLint level = READ_DATA( 12, GLint ); + GLenum format = READ_DATA( 16, GLenum ); + GLenum type = READ_DATA( 20, GLenum ); + GLvoid *pixels; + + SET_RETURN_PTR(24); + SET_WRITEBACK_PTR(32); + pixels = DATA_POINTER(24, GLvoid); + + cr_unpackDispatch.GetTexImage(target, level, format, type, pixels); +} + +void crUnpackExtendGetCompressedTexImageARB(void) +{ + GLenum target = READ_DATA( 8, GLenum ); + GLint level = READ_DATA( 12, GLint ); + GLvoid *img; + + SET_RETURN_PTR( 16 ); + SET_WRITEBACK_PTR( 24 ); + img = DATA_POINTER(16, GLvoid); + + cr_unpackDispatch.GetCompressedTexImageARB( target, level, img ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c new file mode 100644 index 00000000..ece7c710 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_visibleregion.c @@ -0,0 +1,37 @@ +/* $Id: unpack_visibleregion.c $ */ +/** @file + * VBox Packing VisibleRegion information + */ + +/* + * Copyright (C) 2008-2019 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. + */ + +#include "unpacker.h" +#include "cr_error.h" +#include "cr_protocol.h" +#include "cr_mem.h" +#include "cr_version.h" + +void crUnpackExtendWindowVisibleRegion( void ) +{ + GLint window = READ_DATA( 8, GLint ); + GLint cRects = READ_DATA( 12, GLint ); + GLvoid *pRects = DATA_POINTER( 16, GLvoid ); + + if (cRects <= 0 || cRects >= INT32_MAX / sizeof(GLint) / 8 || !DATA_POINTER_CHECK(16 + 4 * cRects * sizeof(GLint))) + { + crError("crUnpackExtendWindowVisibleRegion: parameter 'cRects' is out of range"); + return; + } + + cr_unpackDispatch.WindowVisibleRegion( window, cRects, pRects ); +} diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_writeback.c b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_writeback.c new file mode 100644 index 00000000..f55a4c37 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_writeback.c @@ -0,0 +1,35 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#include "unpacker.h" +#include "cr_error.h" +#include <stdio.h> + +CRNetworkPointer *return_ptr = NULL, *writeback_ptr = NULL; + +void crUnpackSetReturnPointer( CRNetworkPointer *ret ) +{ + return_ptr = ret; +} + +void crUnpackSetWritebackPointer( CRNetworkPointer *wri ) +{ + writeback_ptr = wri; +} + +void crUnpackExtendWriteback(void) +{ + /* This copies the unpack buffer's CRNetworkPointer to writeback_ptr */ + SET_WRITEBACK_PTR( 8 ); + cr_unpackDispatch.Writeback( NULL ); +} + +#if 0 +void crUnpackWriteback(void) +{ + crError( "crUnpackWriteback should never be called" ); +} +#endif diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker.h b/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker.h new file mode 100644 index 00000000..708a89d0 --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2001, Stanford University + * All rights reserved. + * + * See the file LICENSE.txt for information on redistributing this software. + */ + +#ifndef CR_UNPACKER_H +#define CR_UNPACKER_H + +#ifdef DLLDATA +#undef DLLDATA +#endif +#define DLLDATA(type) DECLEXPORT(type) + +#include "cr_version.h" +#include "cr_unpack.h" +#include "unpack_extend.h" + +#endif /* CR_UNPACKER_H */ diff --git a/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special b/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special new file mode 100644 index 00000000..ff1cb7ae --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special @@ -0,0 +1,184 @@ +# Copyright (c) 2001, Stanford University +# All rights reserved. +# +# See the file LICENSE.txt for information on redistributing this software. +# +# Unpack functions which can't be auto-generated +# +Bitmap +CallLists +ClipPlane +ColorPointer +DeleteTextures +DrawElements +DrawRangeElements +DrawPixels +EdgeFlagPointer +Fogfv +Fogiv +IndexPointer +InterleavedArrays +Lightfv +Lightiv +LightModelfv +LightModeliv +LoadMatrixf +LoadMatrixd +Map1d +Map1f +Map2d +Map2f +Materialfv +Materialiv +MultMatrixd +MultMatrixf +NormalPointer +PixelMapfv +PixelMapuiv +PixelMapusv +PolygonStipple +PrioritizeTextures +ReadPixels +TexCoordPointer +TexEnvfv +TexEnviv +TexGendv +TexGenfv +TexGeniv +TexImage1D +TexImage2D +TexImage3D +TexImage3DEXT +TexParameterf +TexParameteri +TexParameterfv +TexParameteriv +TexSubImage1D +TexSubImage2D +TexSubImage3D +VertexPointer +BoundsInfoCR +SecondaryColorPointerEXT +FogCoordPointerEXT +MultiDrawArraysEXT +MultiDrawElementsEXT +VertexAttrib1dvARB +VertexAttrib1fvARB +VertexAttrib1svARB +VertexAttrib2dvARB +VertexAttrib2fvARB +VertexAttrib2svARB +VertexAttrib3dvARB +VertexAttrib3fvARB +VertexAttrib3svARB +VertexAttrib4dvARB +VertexAttrib4fvARB +VertexAttrib4svARB +VertexAttribPointerARB +VertexAttrib4NbvARB +VertexAttrib4NivARB +VertexAttrib4NsvARB +VertexAttrib4NubvARB +VertexAttrib4NuivARB +VertexAttrib4NusvARB +VertexAttrib4bvARB +VertexAttrib4ivARB +VertexAttrib4ubvARB +VertexAttrib4uivARB +VertexAttrib4usvARB +VertexAttribPointerNV +Writeback +CombinerParameterfvNV +CombinerParameterivNV +CombinerStageParameterfvNV +ChromiumParametervCR +CreateContext +WindowCreate +AreTexturesResident +LoadTransposeMatrixfARB +LoadTransposeMatrixdARB +MultTransposeMatrixdARB +MultTransposeMatrixfARB +PointParameteriv +PointParameterfvARB +AreProgramsResidentNV +DeleteProgramsNV +ExecuteProgramNV +GetProgramNamedParameterdvNV +GetProgramNamedParameterfvNV +LoadProgramNV +ProgramLocalParameter4dvARB +ProgramLocalParameter4fvARB +ProgramNamedParameter4dNV +ProgramNamedParameter4dvNV +ProgramNamedParameter4fNV +ProgramNamedParameter4fvNV +ProgramParameter4dvNV +ProgramParameter4fvNV +ProgramParameters4dvNV +ProgramParameters4fvNV +RequestResidentProgramsNV +DeleteProgramsARB +GetProgramStringARB +ProgramEnvParameter4dvARB +ProgramEnvParameter4fvARB +ProgramLocalParameter4dvARB +ProgramLocalParameter4fvARB +ProgramStringARB +BufferDataARB +BufferSubDataARB +GetBufferSubDataARB +DeleteBuffersARB +ZPixCR +CompressedTexImage1DARB +CompressedTexImage2DARB +CompressedTexImage3DARB +CompressedTexSubImage1DARB +CompressedTexSubImage2DARB +CompressedTexSubImage3DARB +DeleteFencesNV +WindowVisibleRegion +BindAttribLocation +ShaderSource +Uniform1fv +Uniform1iv +Uniform2fv +Uniform2iv +Uniform3fv +Uniform3iv +Uniform4fv +Uniform4iv +UniformMatrix2fv +UniformMatrix3fv +UniformMatrix4fv +DrawBuffers +GetActiveAttrib +GetActiveUniform +GetAttachedShaders +GetShaderInfoLog +GetProgramInfoLog +GetShaderSource +GetAttribLocation +GetUniformLocation +GetAttachedObjectsARB +GetInfoLogARB +DeleteQueriesARB +DeleteFramebuffersEXT +DeleteRenderbuffersEXT +LockArraysEXT +UnlockArraysEXT +GetUniformsLocations +GetAttribsLocations +GetTexImage +GetCompressedTexImageARB +GetPolygonStipple +GetPixelMapfv +GetPixelMapuiv +GetPixelMapusv +UniformMatrix2x3fv +UniformMatrix3x2fv +UniformMatrix2x4fv +UniformMatrix4x2fv +UniformMatrix3x4fv +UniformMatrix4x3fv +VBoxTexPresent
\ No newline at end of file diff --git a/src/VBox/HostServices/auth/Makefile.kmk b/src/VBox/HostServices/auth/Makefile.kmk new file mode 100644 index 00000000..71de4316 --- /dev/null +++ b/src/VBox/HostServices/auth/Makefile.kmk @@ -0,0 +1,66 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the VBox RDP authentication plugins. +# + +# +# Copyright (C) 2006-2019 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. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# The plugin. +ifndef VBOX_ONLY_SDK + if ("$(KBUILD_TARGET)" != "linux" && "$(KBUILD_TARGET)" != "solaris") || defined(VBOX_WITH_PAM) + DLLS += VBoxAuth + endif +endif +VBoxAuth_TEMPLATE = VBOXR3 +VBoxAuth_SOURCES.linux = pam/VBoxAuthPAM.c +VBoxAuth_SOURCES.solaris = pam/VBoxAuthPAM.c +VBoxAuth_SOURCES.freebsd = pam/VBoxAuthPAM.c +VBoxAuth_SOURCES.win = winlogon/winlogon.cpp winlogon/VBoxAuth.rc +VBoxAuth_SOURCES.darwin = directoryservice/directoryservice.cpp +VBoxAuth_CXXFLAGS.darwin = -Wno-deprecated-declarations +VBoxAuth_LIBS.linux = $(LIB_RUNTIME) dl +VBoxAuth_LIBS.solaris = $(LIB_RUNTIME) dl +VBoxAuth_LIBS.freebsd = $(LIB_RUNTIME) +VBoxAuth_LIBS.darwin = $(LIB_RUNTIME) +VBoxAuth_LDFLAGS.darwin = -framework DirectoryService + +# The simple plugin. +ifndef VBOX_ONLY_SDK + if defined(VBOX_WITH_MAIN) + DLLS += VBoxAuthSimple + endif +endif +VBoxAuthSimple_TEMPLATE = VBOXMAINCLIENTDLL +VBoxAuthSimple_SOURCES = simple/VBoxAuthSimple.cpp +VBoxAuthSimple_SOURCES.win = simple/VBoxAuthSimple.rc + +# Install the SDK samples. +INSTALLS += VBoxAuth-samples +VBoxAuth-samples_INST = $(INST_SDK)bindings/auth/ +VBoxAuth-samples_MODE = a+r,u+w +VBoxAuth-samples_SOURCES = simple/VBoxAuthSimple.cpp +VBoxAuth-samples_SOURCES.linux = pam/VBoxAuthPAM.c +VBoxAuth-samples_SOURCES.win = winlogon/winlogon.cpp + +# Install the SDK header. +INSTALLS += VBoxAuth-sdkhdr +VBoxAuth-sdkhdr_INST = $(INST_SDK)bindings/auth/include/ +VBoxAuth-sdkhdr_MODE = a+r,u+w +VBoxAuth-sdkhdr_SOURCES = $(PATH_ROOT)/include/VBox/VBoxAuth.h=>VBoxAuth.h + + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostServices/auth/directoryservice/Makefile.kup b/src/VBox/HostServices/auth/directoryservice/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/auth/directoryservice/Makefile.kup diff --git a/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp b/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp new file mode 100644 index 00000000..5a65a848 --- /dev/null +++ b/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp @@ -0,0 +1,328 @@ +/** @file + * + * VirtualBox External Authentication Library: + * Mac OS X Authentication. This is based on + * http://developer.apple.com/mac/library/samplecode/CryptNoMore/ + */ + +/* + * Copyright (C) 2009-2019 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. + */ + +#include <iprt/cdefs.h> +#include <iprt/assert.h> + +#include <VBox/VBoxAuth.h> + +#include <DirectoryService/DirectoryService.h> + +/* Globals */ +static const size_t s_cBufferSize = 32 * 1024; + +tDirStatus defaultSearchNodePath(tDirReference pDirRef, tDataListPtr *pdsNodePath) +{ + tDirStatus dsErr = eDSNoErr; + /* Create a buffer for the resulting nodes */ + tDataBufferPtr pTmpBuf = NULL; + pTmpBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize); + if (pTmpBuf) + { + /* Try to find the default search node for local names */ + UInt32 cNodes; + tContextData hCtx = 0; + dsErr = dsFindDirNodes(pDirRef, pTmpBuf, NULL, eDSLocalNodeNames, &cNodes, &hCtx); + /* Any nodes found? */ + if ( dsErr == eDSNoErr + && cNodes >= 1) + /* The first path of the node list is what we looking for. */ + dsErr = dsGetDirNodeName(pDirRef, pTmpBuf, 1, pdsNodePath); + else + dsErr = eDSNodeNotFound; + + if (hCtx) /* (DSoNodeConfig.m from DSTools-162 does exactly the same free if not-zero-regardless-of-return-code.) */ + dsReleaseContinueData(pDirRef, hCtx); + dsDataBufferDeAllocate(pDirRef, pTmpBuf); + } + else + dsErr = eDSAllocationFailed; + + return dsErr; +} + +tDirStatus userAuthInfo(tDirReference pDirRef, tDirNodeReference pNodeRef, const char *pszUsername, tDataListPtr *ppAuthNodeListOut) +{ + tDirStatus dsErr = eDSNoErr; + tDirStatus dsCleanErr = eDSNoErr; + /* Create a buffer for the resulting authentication info */ + tDataBufferPtr pTmpBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize); + if (pTmpBuf) + { + /* Create the necessary lists for kDSNAttrMetaNodeLocation and kDSNAttrRecordName. */ + tDataListPtr pRecordType = dsBuildListFromStrings(pDirRef, kDSStdRecordTypeUsers, NULL); + tDataListPtr pRecordName = dsBuildListFromStrings(pDirRef, pszUsername, NULL); + tDataListPtr pRequestedAttributes = dsBuildListFromStrings(pDirRef, kDSNAttrMetaNodeLocation, kDSNAttrRecordName, NULL); + if (!( pRecordType == NULL + || pRecordName == NULL + || pRequestedAttributes == NULL)) + { + /* Now search for the first matching record */ + UInt32 cRecords = 1; + tContextData hCtx = 0; + dsErr = dsGetRecordList(pNodeRef, + pTmpBuf, + pRecordName, + eDSExact, + pRecordType, + pRequestedAttributes, + false, + &cRecords, + &hCtx); + if ( dsErr == eDSNoErr + && cRecords >= 1) + { + /* Process the first found record. Look at any attribute one by one. */ + tAttributeListRef hRecAttrListRef = 0; + tRecordEntryPtr pRecEntry = NULL; + tDataListPtr pAuthNodeList = NULL; + dsErr = dsGetRecordEntry(pNodeRef, pTmpBuf, 1, &hRecAttrListRef, &pRecEntry); + if (dsErr == eDSNoErr) + { + for (size_t i = 1; i <= pRecEntry->fRecordAttributeCount; ++i) + { + tAttributeValueListRef hAttrValueListRef = 0; + tAttributeEntryPtr pAttrEntry = NULL; + /* Get the information for this attribute. */ + dsErr = dsGetAttributeEntry(pNodeRef, pTmpBuf, hRecAttrListRef, i, + &hAttrValueListRef, &pAttrEntry); + if (dsErr == eDSNoErr) + { + tAttributeValueEntryPtr pValueEntry = NULL; + /* Has any value? */ + if (pAttrEntry->fAttributeValueCount > 0) + { + dsErr = dsGetAttributeValue(pNodeRef, pTmpBuf, 1, hAttrValueListRef, &pValueEntry); + if (dsErr == eDSNoErr) + { + /* Check for kDSNAttrMetaNodeLocation */ + if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0) + { + /* Convert the meta location attribute to a path node list */ + pAuthNodeList = dsBuildFromPath(pDirRef, + pValueEntry->fAttributeValueData.fBufferData, + "/"); + if (pAuthNodeList == NULL) + dsErr = eDSAllocationFailed; + } + } + } + + if (pValueEntry != NULL) + dsDeallocAttributeValueEntry(pDirRef, pValueEntry); + if (hAttrValueListRef) + dsCloseAttributeValueList(hAttrValueListRef); + if (pAttrEntry != NULL) + dsDeallocAttributeEntry(pDirRef, pAttrEntry); + + if (dsErr != eDSNoErr) + break; + } + } + } + /* Copy the results */ + if (dsErr == eDSNoErr) + { + if (pAuthNodeList != NULL) + { + /* Copy out results. */ + *ppAuthNodeListOut = pAuthNodeList; + pAuthNodeList = NULL; + } + else + dsErr = eDSAttributeNotFound; + } + + if (pAuthNodeList != NULL) + { + dsCleanErr = dsDataListDeallocate(pDirRef, pAuthNodeList); + if (dsCleanErr == eDSNoErr) + free(pAuthNodeList); + } + if (hRecAttrListRef) + dsCloseAttributeList(hRecAttrListRef); + if (pRecEntry != NULL) + dsDeallocRecordEntry(pDirRef, pRecEntry); + } + else + dsErr = eDSRecordNotFound; + if (hCtx) + dsReleaseContinueData(pDirRef, hCtx); + } + else + dsErr = eDSAllocationFailed; + if (pRequestedAttributes != NULL) + { + dsCleanErr = dsDataListDeallocate(pDirRef, pRequestedAttributes); + if (dsCleanErr == eDSNoErr) + free(pRequestedAttributes); + } + if (pRecordName != NULL) + { + dsCleanErr = dsDataListDeallocate(pDirRef, pRecordName); + if (dsCleanErr == eDSNoErr) + free(pRecordName); + } + if (pRecordType != NULL) + { + dsCleanErr = dsDataListDeallocate(pDirRef, pRecordType); + if (dsCleanErr == eDSNoErr) + free(pRecordType); + } + dsDataBufferDeAllocate(pDirRef, pTmpBuf); + } + else + dsErr = eDSAllocationFailed; + + return dsErr; +} + +tDirStatus authWithNode(tDirReference pDirRef, tDataListPtr pAuthNodeList, const char *pszUsername, const char *pszPassword) +{ + tDirStatus dsErr = eDSNoErr; + /* Open the authentication node. */ + tDirNodeReference hAuthNodeRef = 0; + dsErr = dsOpenDirNode(pDirRef, pAuthNodeList, &hAuthNodeRef); + if (dsErr == eDSNoErr) + { + /* How like we to authenticate! */ + tDataNodePtr pAuthMethod = dsDataNodeAllocateString(pDirRef, kDSStdAuthNodeNativeClearTextOK); + if (pAuthMethod) + { + /* Create the memory holding the authentication data. The data + * structure consists of 4 byte length of the username + zero byte, + * the username itself, a 4 byte length of the password & the + * password itself + zero byte. */ + tDataBufferPtr pAuthOutBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize); + if (pAuthOutBuf) + { + size_t cUserName = strlen(pszUsername) + 1; + size_t cPassword = strlen(pszPassword) + 1; + unsigned long cLen = 0; + tDataBufferPtr pAuthInBuf = dsDataBufferAllocate(pDirRef, sizeof(cLen) + cUserName + sizeof(cLen) + cPassword); + if (pAuthInBuf) + { + /* Move the data into the buffer. */ + pAuthInBuf->fBufferLength = 0; + /* Length of the username */ + cLen = cUserName; + memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], &cLen, sizeof(cLen)); + pAuthInBuf->fBufferLength += sizeof(cLen); + /* The username itself */ + memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], pszUsername, cUserName); + pAuthInBuf->fBufferLength += cUserName; + /* Length of the password */ + cLen = cPassword; + memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], &cLen, sizeof(cLen)); + pAuthInBuf->fBufferLength += sizeof(cLen); + /* The password itself */ + memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], pszPassword, cPassword); + pAuthInBuf->fBufferLength += cPassword; + /* Now authenticate */ + dsErr = dsDoDirNodeAuth(hAuthNodeRef, pAuthMethod, true, pAuthInBuf, pAuthOutBuf, NULL); + /* Clean up. */ + dsDataBufferDeAllocate(pDirRef, pAuthInBuf); + } + else + dsErr = eDSAllocationFailed; + dsDataBufferDeAllocate(pDirRef, pAuthOutBuf); + } + else + dsErr = eDSAllocationFailed; + dsDataNodeDeAllocate(pDirRef, pAuthMethod); + } + else + dsErr = eDSAllocationFailed; + dsCloseDirNode(hAuthNodeRef); + } + + return dsErr; +} + +RT_C_DECLS_BEGIN +DECLEXPORT(FNAUTHENTRY3) AuthEntry; +RT_C_DECLS_END + +DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *pszCaller, + PAUTHUUID pUuid, + AuthGuestJudgement guestJudgement, + const char *pszUser, + const char *pszPassword, + const char *pszDomain, + int fLogon, + unsigned clientId) +{ + RT_NOREF(pszCaller, pUuid, guestJudgement, pszDomain, clientId); + + /* Validate input */ + AssertPtrReturn(pszUser, AuthResultAccessDenied); + AssertPtrReturn(pszPassword, AuthResultAccessDenied); + + /* Result to a default value */ + AuthResult result = AuthResultAccessDenied; + + /* Only process logon requests. */ + if (!fLogon) + return result; /* Return value is ignored by the caller. */ + + tDirStatus dsErr = eDSNoErr; + tDirStatus dsCleanErr = eDSNoErr; + tDirReference hDirRef = 0; + /* Connect to the Directory Service. */ + dsErr = dsOpenDirService(&hDirRef); + if (dsErr == eDSNoErr) + { + /* Fetch the default search node */ + tDataListPtr pSearchNodeList = NULL; + dsErr = defaultSearchNodePath(hDirRef, &pSearchNodeList); + if (dsErr == eDSNoErr) + { + /* Open the default search node */ + tDirNodeReference hSearchNodeRef = 0; + dsErr = dsOpenDirNode(hDirRef, pSearchNodeList, &hSearchNodeRef); + if (dsErr == eDSNoErr) + { + /* Search for the user info, fetch the authentication node & + * the authentication user name. This allows the client to + * specify a long user name even if the name which is used to + * authenticate has the short form. */ + tDataListPtr pAuthNodeList = NULL; + dsErr = userAuthInfo(hDirRef, hSearchNodeRef, pszUser, &pAuthNodeList); + if (dsErr == eDSNoErr) + { + /* Open the authentication node and do the authentication. */ + dsErr = authWithNode(hDirRef, pAuthNodeList, pszUser, pszPassword); + if (dsErr == eDSNoErr) + result = AuthResultAccessGranted; + dsCleanErr = dsDataListDeallocate(hDirRef, pAuthNodeList); + if (dsCleanErr == eDSNoErr) + free(pAuthNodeList); + } + dsCloseDirNode(hSearchNodeRef); + } + dsCleanErr = dsDataListDeallocate(hDirRef, pSearchNodeList); + if (dsCleanErr == eDSNoErr) + free(pSearchNodeList); + } + dsCloseDirService(hDirRef); + } + + return result; +} + diff --git a/src/VBox/HostServices/auth/pam/Makefile.kup b/src/VBox/HostServices/auth/pam/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/auth/pam/Makefile.kup diff --git a/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c b/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c new file mode 100644 index 00000000..dd969f0e --- /dev/null +++ b/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c @@ -0,0 +1,402 @@ +/** @file + * + * VirtualBox External Authentication Library: + * Linux PAM Authentication. + */ + +/* + * Copyright (C) 2006-2019 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. + */ + + +/* The PAM service name. + * + * The service name is the name of a file in the /etc/pam.d which contains + * authentication rules. It is possible to use an existing service + * name, like "login" for example. But if different set of rules + * is required, one can create a new file /etc/pam.d/vrdpauth + * specially for VRDP authentication. Note that the name of the + * service must be lowercase. See PAM documentation for details. + * + * The Auth module takes the PAM service name from the + * environment variable VBOX_AUTH_PAM_SERVICE. If the variable + * is not specified, then the 'login' PAM service is used. + */ +#define VBOX_AUTH_PAM_SERVICE_NAME_ENV_OLD "VRDP_AUTH_PAM_SERVICE" +#define VBOX_AUTH_PAM_SERVICE_NAME_ENV "VBOX_AUTH_PAM_SERVICE" +#define VBOX_AUTH_PAM_DEFAULT_SERVICE_NAME "login" + + +/* The debug log file name. + * + * If defined, debug messages will be written to the file specified in the + * VBOX_AUTH_DEBUG_FILENAME (or deprecated VRDP_AUTH_DEBUG_FILENAME) environment + * variable: + * + * export VBOX_AUTH_DEBUG_FILENAME=pam.log + * + * The above will cause writing to the pam.log. + */ +#define VBOX_AUTH_DEBUG_FILENAME_ENV_OLD "VRDP_AUTH_DEBUG_FILENAME" +#define VBOX_AUTH_DEBUG_FILENAME_ENV "VBOX_AUTH_DEBUG_FILENAME" + + +/* Dynamic loading of the PAM library. + * + * If defined, the libpam.so is loaded dynamically. + * Enabled by default since it is often required, + * and does not harm. + */ +#define VBOX_AUTH_USE_PAM_DLLOAD + + +#ifdef VBOX_AUTH_USE_PAM_DLLOAD +/* The name of the PAM library */ +# ifdef RT_OS_SOLARIS +# define PAM_LIB_NAME "libpam.so.1" +# elif defined(RT_OS_FREEBSD) +# define PAM_LIB_NAME "libpam.so" +# else +# define PAM_LIB_NAME "libpam.so.0" +# endif +#endif /* VBOX_AUTH_USE_PAM_DLLOAD */ + + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#ifndef RT_OS_FREEBSD +# include <malloc.h> +#endif + +#include <security/pam_appl.h> + +#include <VBox/VBoxAuth.h> + +#ifdef VBOX_AUTH_USE_PAM_DLLOAD +#include <dlfcn.h> + +static int (*fn_pam_start)(const char *service_name, + const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh); +static int (*fn_pam_authenticate)(pam_handle_t *pamh, int flags); +static int (*fn_pam_acct_mgmt)(pam_handle_t *pamh, int flags); +static int (*fn_pam_end)(pam_handle_t *pamh, int pam_status); +static const char * (*fn_pam_strerror)(pam_handle_t *pamh, int errnum); +#else +#define fn_pam_start pam_start +#define fn_pam_authenticate pam_authenticate +#define fn_pam_acct_mgmt pam_acct_mgmt +#define fn_pam_end pam_end +#define fn_pam_strerror pam_strerror +#endif /* VBOX_AUTH_USE_PAM_DLLOAD */ + +static void debug_printf(const char *fmt, ...) +{ +#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV) || defined(VBOX_AUTH_DEBUG_FILENAME_ENV_OLD) + va_list va; + + char buffer[1024]; + + const char *filename = NULL; + + va_start(va, fmt); + +#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV) + filename = getenv (VBOX_AUTH_DEBUG_FILENAME_ENV); +#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV */ + +#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV_OLD) + if (filename == NULL) + { + filename = getenv (VBOX_AUTH_DEBUG_FILENAME_ENV_OLD); + } +#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV_OLD */ + + if (filename) + { + FILE *f; + + vsnprintf (buffer, sizeof (buffer), fmt, va); + + f = fopen (filename, "ab"); + if (f != NULL) + { + fprintf (f, "%s", buffer); + fclose (f); + } + } + + va_end (va); +#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV || VBOX_AUTH_DEBUG_FILENAME_ENV_OLD */ +} + +#ifdef VBOX_AUTH_USE_PAM_DLLOAD + +static void *gpvLibPam = NULL; + +typedef struct _SymMap +{ + void **ppfn; + const char *pszName; +} SymMap; + +static SymMap symmap[] = +{ + { (void **)&fn_pam_start, "pam_start" }, + { (void **)&fn_pam_authenticate, "pam_authenticate" }, + { (void **)&fn_pam_acct_mgmt, "pam_acct_mgmt" }, + { (void **)&fn_pam_end, "pam_end" }, + { (void **)&fn_pam_strerror, "pam_strerror" }, + { NULL, NULL } +}; + +static int auth_pam_init(void) +{ + SymMap *iter; + + gpvLibPam = dlopen(PAM_LIB_NAME, RTLD_LAZY | RTLD_GLOBAL); + + if (!gpvLibPam) + { + debug_printf("auth_pam_init: dlopen %s failed\n", PAM_LIB_NAME); + return PAM_SYSTEM_ERR; + } + + iter = &symmap[0]; + + while (iter->pszName != NULL) + { + void *pv = dlsym (gpvLibPam, iter->pszName); + + if (pv == NULL) + { + debug_printf("auth_pam_init: dlsym %s failed\n", iter->pszName); + + dlclose(gpvLibPam); + gpvLibPam = NULL; + + return PAM_SYSTEM_ERR; + } + + *iter->ppfn = pv; + + iter++; + } + + return PAM_SUCCESS; +} + +static void auth_pam_close(void) +{ + if (gpvLibPam) + { + dlclose(gpvLibPam); + gpvLibPam = NULL; + } + + return; +} +#else +static int auth_pam_init(void) +{ + return PAM_SUCCESS; +} + +static void auth_pam_close(void) +{ + return; +} +#endif /* VBOX_AUTH_USE_PAM_DLLOAD */ + +static const char *auth_get_pam_service (void) +{ + const char *service = getenv (VBOX_AUTH_PAM_SERVICE_NAME_ENV); + + if (service == NULL) + { + service = getenv (VBOX_AUTH_PAM_SERVICE_NAME_ENV_OLD); + + if (service == NULL) + { + service = VBOX_AUTH_PAM_DEFAULT_SERVICE_NAME; + } + } + + debug_printf ("Using PAM service: %s\n", service); + + return service; +} + +typedef struct _PamContext +{ + char *pszUser; + char *pszPassword; +} PamContext; + +static int conv (int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + int i; + struct pam_response *r; + + PamContext *ctx = (PamContext *)appdata_ptr; + + if (ctx == NULL) + { + debug_printf("conv: ctx is NULL\n"); + return PAM_CONV_ERR; + } + + debug_printf("conv: num %d u[%s] p[%d]\n", num_msg, ctx->pszUser, ctx->pszPassword? strlen (ctx->pszPassword): 0); + + r = (struct pam_response *) calloc (num_msg, sizeof (struct pam_response)); + + if (r == NULL) + { + return PAM_CONV_ERR; + } + + for (i = 0; i < num_msg; i++) + { + r[i].resp_retcode = 0; + + if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) + { + r[i].resp = strdup (ctx->pszPassword); + debug_printf("conv: %d returning password [%d]\n", i, r[i].resp? strlen (r[i].resp): 0); + } + else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) + { + r[i].resp = strdup (ctx->pszUser); + debug_printf("conv: %d returning name [%s]\n", i, r[i].resp); + } + else + { + debug_printf("conv: %d style %d: [%s]\n", i, msg[i]->msg_style, msg[i]->msg? msg[i]->msg: "(null)"); + r[i].resp = NULL; + } + } + + *resp = r; + return PAM_SUCCESS; +} + +/* The entry point must be visible. */ +#if defined(_MSC_VER) || defined(__OS2__) +# define DECLEXPORT(type) __declspec(dllexport) type +#else +# ifdef VBOX_HAVE_VISIBILITY_HIDDEN +# define DECLEXPORT(type) __attribute__((visibility("default"))) type +# else +# define DECLEXPORT(type) type +# endif +#endif + +/* prototype to prevent gcc warning */ +DECLEXPORT(AUTHENTRY3) AuthEntry; + +DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *pszCaller, + PAUTHUUID pUuid, + AuthGuestJudgement guestJudgement, + const char *pszUser, + const char *pszPassword, + const char *pszDomain, + int fLogon, + unsigned clientId) +{ + AuthResult result = AuthResultAccessDenied; + int rc; + PamContext ctx; + struct pam_conv pam_conversation; + pam_handle_t *pam_handle = NULL; + + (void)pszCaller; + (void)pUuid; + (void)guestJudgement; + (void)clientId; + + /* Only process logon requests. */ + if (!fLogon) + return result; /* Return value is ignored by the caller. */ + + debug_printf("u[%s], d[%s], p[%d]\n", pszUser, pszDomain, pszPassword ? strlen(pszPassword) : 0); + + ctx.pszUser = (char *)pszUser; + ctx.pszPassword = (char *)pszPassword; + + pam_conversation.conv = conv; + pam_conversation.appdata_ptr = &ctx; + + rc = auth_pam_init (); + + if (rc == PAM_SUCCESS) + { + debug_printf("init ok\n"); + + rc = fn_pam_start(auth_get_pam_service (), pszUser, &pam_conversation, &pam_handle); + + if (rc == PAM_SUCCESS) + { + debug_printf("start ok\n"); + + rc = fn_pam_authenticate(pam_handle, 0); + + if (rc == PAM_SUCCESS) + { + debug_printf("auth ok\n"); + + rc = fn_pam_acct_mgmt(pam_handle, 0); + if (rc == PAM_AUTHINFO_UNAVAIL + && + getenv("VBOX_PAM_ALLOW_INACTIVE") != NULL) + { + debug_printf("PAM_AUTHINFO_UNAVAIL\n"); + rc = PAM_SUCCESS; + } + + if (rc == PAM_SUCCESS) + { + debug_printf("access granted\n"); + + result = AuthResultAccessGranted; + } + else + { + debug_printf("pam_acct_mgmt failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc)); + } + } + else + { + debug_printf("pam_authenticate failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc)); + } + + fn_pam_end(pam_handle, rc); + } + else + { + debug_printf("pam_start failed %d\n", rc); + } + + auth_pam_close (); + + debug_printf("auth_pam_close completed\n"); + } + else + { + debug_printf("auth_pam_init failed %d\n", rc); + } + + return result; +} + diff --git a/src/VBox/HostServices/auth/simple/Makefile.kup b/src/VBox/HostServices/auth/simple/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/auth/simple/Makefile.kup diff --git a/src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp new file mode 100644 index 00000000..436d4b41 --- /dev/null +++ b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp @@ -0,0 +1,137 @@ +/* $Id: VBoxAuthSimple.cpp $ */ +/** @file + * VirtualBox External Authentication Library - Simple Authentication. + */ + +/* + * Copyright (C) 2006-2019 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <iprt/cdefs.h> +#include <iprt/uuid.h> +#include <iprt/sha.h> + +#include <VBox/VBoxAuth.h> + +#include <VBox/com/com.h> +#include <VBox/com/string.h> +#include <VBox/com/Guid.h> +#include <VBox/com/VirtualBox.h> + +using namespace com; + +/* If defined, debug messages will be written to the specified file. */ +//#define AUTH_DEBUG_FILE_NAME "/tmp/VBoxAuth.log" + + +static void dprintf(const char *pszFormat, ...) +{ +#ifdef AUTH_DEBUG_FILE_NAME + FILE *f = fopen(AUTH_DEBUG_FILE_NAME, "ab"); + if (f) + { + va_list va; + va_start(va, pszFormat); + vfprintf(f, pszFormat, va); + va_end(va); + fclose(f); + } +#else + RT_NOREF(pszFormat); +#endif +} + +RT_C_DECLS_BEGIN +DECLEXPORT(FNAUTHENTRY3) AuthEntry; +RT_C_DECLS_END + +DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *pszCaller, + PAUTHUUID pUuid, + AuthGuestJudgement guestJudgement, + const char *pszUser, + const char *pszPassword, + const char *pszDomain, + int fLogon, + unsigned clientId) +{ + RT_NOREF(pszCaller, guestJudgement, pszDomain, clientId); + + /* default is failed */ + AuthResult result = AuthResultAccessDenied; + + /* only interested in logon */ + if (!fLogon) + /* return value ignored */ + return result; + + char uuid[RTUUID_STR_LENGTH] = {0}; + if (pUuid) + RTUuidToStr((PCRTUUID)pUuid, (char*)uuid, RTUUID_STR_LENGTH); + + /* the user might contain a domain name, split it */ + const char *user = strchr(pszUser, '\\'); + if (user) + user++; + else + user = (char*)pszUser; + + dprintf("VBoxAuth: uuid: %s, user: %s, pszPassword: %s\n", uuid, user, pszPassword); + + ComPtr<IVirtualBoxClient> virtualBoxClient; + ComPtr<IVirtualBox> virtualBox; + HRESULT rc; + + rc = virtualBoxClient.createInprocObject(CLSID_VirtualBoxClient); + if (SUCCEEDED(rc)) + { + rc = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam()); + if (SUCCEEDED(rc)) + { + Bstr key = BstrFmt("VBoxAuthSimple/users/%s", user); + Bstr password; + + /* lookup in VM's extra data? */ + if (pUuid) + { + ComPtr<IMachine> machine; + virtualBox->FindMachine(Bstr(uuid).raw(), machine.asOutParam()); + if (machine) + machine->GetExtraData(key.raw(), password.asOutParam()); + } + else + /* lookup global extra data */ + virtualBox->GetExtraData(key.raw(), password.asOutParam()); + + if (!password.isEmpty()) + { + /* calculate hash */ + uint8_t abDigest[RTSHA256_HASH_SIZE]; + RTSha256(pszPassword, strlen(pszPassword), abDigest); + char pszDigest[RTSHA256_DIGEST_LEN + 1]; + RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest)); + + if (password == pszDigest) + result = AuthResultAccessGranted; + } + } + else + dprintf("VBoxAuth: failed to get VirtualBox object reference: %#x\n", rc); + } + else + dprintf("VBoxAuth: failed to get VirtualBoxClient object reference: %#x\n", rc); + + return result; +} + diff --git a/src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc new file mode 100644 index 00000000..616187ec --- /dev/null +++ b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxAuthSimple.rc $ */ +/** @file + * VBoxAuthSimple - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox Simple Authentication Host Service\0" + VALUE "InternalName", "VBoxAuthSimple\0" + VALUE "OriginalFilename", "VBoxAuthSimple.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/auth/winlogon/Makefile.kup b/src/VBox/HostServices/auth/winlogon/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/auth/winlogon/Makefile.kup diff --git a/src/VBox/HostServices/auth/winlogon/VBoxAuth.rc b/src/VBox/HostServices/auth/winlogon/VBoxAuth.rc new file mode 100644 index 00000000..b24ed0fe --- /dev/null +++ b/src/VBox/HostServices/auth/winlogon/VBoxAuth.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxAuth.rc $ */ +/** @file + * VBoxAuth - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 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. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox Authentication Host Service\0" + VALUE "InternalName", "VBoxAuth\0" + VALUE "OriginalFilename", "VBoxAuth.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/auth/winlogon/winlogon.cpp b/src/VBox/HostServices/auth/winlogon/winlogon.cpp new file mode 100644 index 00000000..4db9b7a5 --- /dev/null +++ b/src/VBox/HostServices/auth/winlogon/winlogon.cpp @@ -0,0 +1,171 @@ +/* $Id: winlogon.cpp $ */ +/** @file + * VirtualBox External Authentication Library - Windows Logon Authentication. + */ + +/* + * Copyright (C) 2006-2019 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. + */ + +/* If defined, debug messages will be written to the debugger. */ +// #define AUTH_DEBUG + +#include <iprt/win/windows.h> +#include <VBox/VBoxAuth.h> +#include <iprt/cdefs.h> + +#ifdef AUTH_DEBUG +# include <stdio.h> + +static void dprintfw(const WCHAR *fmt, ...) +{ + va_list va; + va_start(va, fmt); + + WCHAR buffer[1024]; + + _vsnwprintf(buffer, sizeof (buffer), fmt, va); + + OutputDebugStringW(buffer); + + va_end(va); +} +# define DBGAUTH(a) dprintfw a +#else +# define DBGAUTH(a) +#endif + +static WCHAR g_wszEmpty[] = { L"" }; + +static void freeWideChar(WCHAR *pwszString) +{ + if (pwszString && pwszString != &g_wszEmpty[0]) + { + size_t cb = (wcslen(pwszString) + 1) * sizeof(WCHAR); + SecureZeroMemory(pwszString, cb); + free(pwszString); + } +} + +static WCHAR *utf8ToWideChar(const char *pszString) +{ + /* + * Shortcut for empty strings. + */ + if (!pszString || *pszString == 0) + return &g_wszEmpty[0]; + + /* + * Return NULL on errors. + */ + WCHAR *pwszString = NULL; + + /* + * First calc result string length. + */ + const DWORD dwFlags = MB_ERR_INVALID_CHARS; + int cwc = MultiByteToWideChar(CP_UTF8, dwFlags, pszString, -1, NULL, 0); + if (cwc > 0) + { + /* + * Alloc space for result buffer. + */ + pwszString = (WCHAR *)malloc(cwc * sizeof(WCHAR)); + if (pwszString) + { + /* + * Do the translation. + */ + if (MultiByteToWideChar(CP_UTF8, dwFlags, pszString, -1, pwszString, cwc) <= 0) + { + /* translation error */ + free(pwszString); + pwszString = NULL; + } + } + } + + return pwszString; +} + +/* Prototype it to make sure we've got the right prototype. */ +extern "C" +#if defined(_MSC_VER) +__declspec(dllexport) +#endif +FNAUTHENTRY3 AuthEntry; + +/** + * @callback_method_impl{FNAUTHENTRY3} + */ +extern "C" +AuthResult AUTHCALL AuthEntry(const char *pszCaller, + PAUTHUUID pUuid, + AuthGuestJudgement guestJudgement, + const char *pszUser, + const char *pszPassword, + const char *pszDomain, + int fLogon, + unsigned clientId) +{ + RT_NOREF4(pszCaller, pUuid, guestJudgement, clientId); + if (!fLogon) + { + /* Nothing to cleanup. The return code does not matter. */ + return AuthResultAccessDenied; + } + + LPWSTR pwszUsername = utf8ToWideChar(pszUser); + LPWSTR pwszDomain = utf8ToWideChar(pszDomain); + LPWSTR pwszPassword = utf8ToWideChar(pszPassword); + + DBGAUTH((L"u[%ls], d[%ls], p[%ls]\n", lpwszUsername, lpwszDomain, lpwszPassword)); + + AuthResult result = AuthResultAccessDenied; + + if (pwszUsername && pwszDomain && pwszPassword) + { + /* LOGON32_LOGON_INTERACTIVE is intended for users who will be interactively using the computer, + * such as a user being logged on by a terminal server, remote shell, or similar process. + */ + DWORD dwLogonType = LOGON32_LOGON_INTERACTIVE; + DWORD dwLogonProvider = LOGON32_PROVIDER_DEFAULT; + + HANDLE hToken; + + BOOL fSuccess = LogonUserW(pwszUsername, + pwszDomain, + pwszPassword, + dwLogonType, + dwLogonProvider, + &hToken); + + if (fSuccess) + { + DBGAUTH((L"LogonUser success. hToken = %p\n", hToken)); + + result = AuthResultAccessGranted; + + CloseHandle(hToken); + } + else + { + DBGAUTH((L"LogonUser failed %08X\n", GetLastError())); + } + } + + freeWideChar(pwszUsername); + freeWideChar(pwszDomain); + freeWideChar(pwszPassword); + + return result; +} + diff --git a/src/VBox/HostServices/common/client.cpp b/src/VBox/HostServices/common/client.cpp new file mode 100644 index 00000000..7211bd4b --- /dev/null +++ b/src/VBox/HostServices/common/client.cpp @@ -0,0 +1,248 @@ +/* $Id: client.cpp $ */ +/** @file + * Base class for a host-guest service. + */ + +/* + * Copyright (C) 2011-2019 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. + */ + +#include <VBox/log.h> +#include <VBox/hgcmsvc.h> + +#include <iprt/assert.h> +#include <iprt/alloc.h> +#include <iprt/cpp/utils.h> + +#include <VBox/HostServices/Service.h> + +using namespace HGCM; + +Client::Client(uint32_t uClientID) + : m_uClientID(uClientID) + , m_uProtocolVer(0) + , m_fDeferred(false) +{ + RT_ZERO(m_Deferred); + RT_ZERO(m_SvcCtx); +} + +Client::~Client(void) +{ + +} + +/** + * Completes a guest call by returning the control back to the guest side, + * together with a status code, internal version. + * + * @returns IPRT status code. + * @param hHandle Call handle to complete guest call for. + * @param rcOp Return code to return to the guest side. + */ +int Client::completeInternal(VBOXHGCMCALLHANDLE hHandle, int rcOp) +{ + LogFlowThisFunc(("uClientID=%RU32\n", m_uClientID)); + + if ( m_SvcCtx.pHelpers + && m_SvcCtx.pHelpers->pfnCallComplete) + { + m_SvcCtx.pHelpers->pfnCallComplete(hHandle, rcOp); + + reset(); + return VINF_SUCCESS; + } + + return VERR_NOT_AVAILABLE; +} + +/** + * Resets the client's internal state. + */ +void Client::reset(void) +{ + m_fDeferred = false; + + RT_ZERO(m_Deferred); +} + +/** + * Completes a guest call by returning the control back to the guest side, + * together with a status code. + * + * @returns IPRT status code. + * @param hHandle Call handle to complete guest call for. + * @param rcOp Return code to return to the guest side. + */ +int Client::Complete(VBOXHGCMCALLHANDLE hHandle, int rcOp /* = VINF_SUCCESS */) +{ + return completeInternal(hHandle, rcOp); +} + +/** + * Completes a deferred guest call by returning the control back to the guest side, + * together with a status code. + * + * @returns IPRT status code. VERR_INVALID_STATE if the client is not in deferred mode. + * @param rcOp Return code to return to the guest side. + */ +int Client::CompleteDeferred(int rcOp) +{ + if (m_fDeferred) + { + Assert(m_Deferred.hHandle != NULL); + + int rc = completeInternal(m_Deferred.hHandle, rcOp); + if (RT_SUCCESS(rc)) + m_fDeferred = false; + + return rc; + } + + AssertMsg(m_fDeferred, ("Client %RU32 is not in deferred mode\n", m_uClientID)); + return VERR_INVALID_STATE; +} + +/** + * Returns the HGCM call handle of the client. + * + * @returns HGCM handle. + */ +VBOXHGCMCALLHANDLE Client::GetHandle(void) const +{ + return m_Deferred.hHandle; +} + +/** + * Returns the HGCM call handle of the client. + * + * @returns HGCM handle. + */ +uint32_t Client::GetMsgType(void) const +{ + return m_Deferred.uType; +} + +uint32_t Client::GetMsgParamCount(void) const +{ + return m_Deferred.cParms; +} + +/** + * Returns the client's (HGCM) ID. + * + * @returns The client's (HGCM) ID. + */ +uint32_t Client::GetClientID(void) const +{ + return m_uClientID; +} + +/** + * Returns the client's used protocol version. + * + * @returns Protocol version, or 0 if not set. + */ +uint32_t Client::GetProtocolVer(void) const +{ + return m_uProtocolVer; +} + +/** + * Returns whether the client currently is in deferred mode or not. + * + * @returns \c True if in deferred mode, \c False if not. + */ +bool Client::IsDeferred(void) const +{ + return m_fDeferred; +} + +/** + * Set the client's status to deferred, meaning that it does not return to the caller + * until CompleteDeferred() has been called. + */ +void Client::SetDeferred(VBOXHGCMCALLHANDLE hHandle, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) +{ + LogFlowThisFunc(("uClient=%RU32\n", m_uClientID)); + + AssertMsg(m_fDeferred == false, ("Client already in deferred mode\n")); + m_fDeferred = true; + + m_Deferred.hHandle = hHandle; + m_Deferred.uType = u32Function; + m_Deferred.cParms = cParms; + m_Deferred.paParms = paParms; +} + +/** + * Sets the client's protocol version. The protocol version is purely optional and bound + * to a specific HGCM service. + * + * @param uVersion Version number to set. + */ +void Client::SetProtocolVer(uint32_t uVersion) +{ + m_uProtocolVer = uVersion; +} + +/** + * Sets the HGCM service context. + * + * @param SvcCtx Service context to set. + */ +void Client::SetSvcContext(const VBOXHGCMSVCTX &SvcCtx) +{ + m_SvcCtx = SvcCtx; +} + +/** + * Sets the deferred parameters to a specific message type and + * required parameters. That way the client can re-request that message with + * the right amount of parameters from the service. + * + * @returns IPRT status code. + * @param uMsg Message type (number) to set. + * @param cParms Number of parameters the message needs. + */ +int Client::SetDeferredMsgInfo(uint32_t uMsg, uint32_t cParms) +{ + if (m_fDeferred) + { + if (m_Deferred.cParms < 2) + return VERR_INVALID_PARAMETER; + + AssertPtrReturn(m_Deferred.paParms, VERR_BUFFER_OVERFLOW); + + HGCMSvcSetU32(&m_Deferred.paParms[0], uMsg); + HGCMSvcSetU32(&m_Deferred.paParms[1], cParms); + + return VINF_SUCCESS; + } + + AssertFailed(); + return VERR_INVALID_STATE; +} + +/** + * Sets the deferred parameters to a specific message type and + * required parameters. That way the client can re-request that message with + * the right amount of parameters from the service. + * + * @returns IPRT status code. + * @param pMessage Message to get message type and required parameters from. + */ +int Client::SetDeferredMsgInfo(const Message *pMessage) +{ + AssertPtrReturn(pMessage, VERR_INVALID_POINTER); + return SetDeferredMsgInfo(pMessage->GetType(), pMessage->GetParamCount()); +} + diff --git a/src/VBox/HostServices/common/message.cpp b/src/VBox/HostServices/common/message.cpp new file mode 100644 index 00000000..0e8acc8a --- /dev/null +++ b/src/VBox/HostServices/common/message.cpp @@ -0,0 +1,296 @@ +/* $Id: message.cpp $ */ +/** @file + * Base class for wrapping HCGM messages. + */ + +/* + * Copyright (C) 2018-2019 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. + */ + +#include <VBox/HostServices/Service.h> + +using namespace HGCM; + +Message::Message(void) + : m_uMsg(0) + , m_cParms(0) + , m_paParms(NULL) { } + +Message::Message(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[]) + : m_uMsg(0) + , m_cParms(0) + , m_paParms(NULL) +{ + initData(uMsg, cParms, aParms); +} + +Message::~Message(void) +{ + reset(); +} + +/** + * Resets the message by free'ing all allocated parameters and resetting the rest. + */ +void Message::reset(void) +{ + if (m_paParms) + { + for (uint32_t i = 0; i < m_cParms; ++i) + { + switch (m_paParms[i].type) + { + case VBOX_HGCM_SVC_PARM_PTR: + if (m_paParms[i].u.pointer.size) + RTMemFree(m_paParms[i].u.pointer.addr); + break; + } + } + RTMemFree(m_paParms); + m_paParms = 0; + } + m_cParms = 0; + m_uMsg = 0; +} + +/** + * Returns the parameter count of this message. + * + * @returns Parameter count. + */ +uint32_t Message::GetParamCount(void) const +{ + return m_cParms; +} + +/** + * Retrieves the raw HGCM parameter data + * + * @returns IPRT status code. + * @param uMsg Message type to retrieve the parameter data for. Needed for sanity. + * @param cParms Size (in parameters) of @a aParms array. + * @param aParms Where to store the HGCM parameter data. + */ +int Message::GetData(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[]) const +{ + if (m_uMsg != uMsg) + { + LogFlowFunc(("Stored message type (%RU32) does not match request (%RU32)\n", m_uMsg, uMsg)); + return VERR_INVALID_PARAMETER; + } + if (m_cParms > cParms) + { + LogFlowFunc(("Stored parameter count (%RU32) exceeds request buffer (%RU32)\n", m_cParms, cParms)); + return VERR_INVALID_PARAMETER; + } + + return Message::CopyParms(&aParms[0], cParms, m_paParms, m_cParms, false /* fDeepCopy */); +} + +/** + * Retrieves a specific parameter value as uint32_t. + * + * @returns IPRT status code. + * @param uParm Index of parameter to retrieve. + * @param pu32Info Where to store the parameter value. + */ +int Message::GetParmU32(uint32_t uParm, uint32_t *pu32Info) const +{ + AssertPtrNullReturn(pu32Info, VERR_INVALID_PARAMETER); + AssertReturn(uParm < m_cParms, VERR_INVALID_PARAMETER); + AssertReturn(m_paParms[uParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_INVALID_PARAMETER); + + *pu32Info = m_paParms[uParm].u.uint32; + + return VINF_SUCCESS; +} + +/** + * Retrieves a specific parameter value as uint64_t. + * + * @returns IPRT status code. + * @param uParm Index of parameter to retrieve. + * @param pu64Info Where to store the parameter value. + */ +int Message::GetParmU64(uint32_t uParm, uint64_t *pu64Info) const +{ + AssertPtrNullReturn(pu64Info, VERR_INVALID_PARAMETER); + AssertReturn(uParm < m_cParms, VERR_INVALID_PARAMETER); + AssertReturn(m_paParms[uParm].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_INVALID_PARAMETER); + + *pu64Info = m_paParms[uParm].u.uint64; + + return VINF_SUCCESS; +} + +/** + * Retrieves a specific parameter value as a data address + size. + * + * @returns IPRT status code. + * @param uParm Index of parameter to retrieve. + * @param ppvAddr Where to store the data address. + * @param pcbSize Where to store the data size (in bytes). + * + * @remarks Does not copy (store) the actual content of the pointer (deep copy). + */ +int Message::GetParmPtr(uint32_t uParm, void **ppvAddr, uint32_t *pcbSize) const +{ + AssertPtrNullReturn(ppvAddr, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(pcbSize, VERR_INVALID_PARAMETER); + AssertReturn(uParm < m_cParms, VERR_INVALID_PARAMETER); + AssertReturn(m_paParms[uParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_INVALID_PARAMETER); + + *ppvAddr = m_paParms[uParm].u.pointer.addr; + *pcbSize = m_paParms[uParm].u.pointer.size; + + return VINF_SUCCESS; +} + +/** + * Returns the type of this message. + * + * @returns Message type. + */ +uint32_t Message::GetType(void) const +{ + return m_uMsg; +} + +/** + * Copies HGCM parameters from source to destination. + * + * @returns IPRT status code. + * @param paParmsDst Destination array to copy parameters to. + * @param cParmsDst Size (in parameters) of destination array. + * @param paParmsSrc Source array to copy parameters from. + * @param cParmsSrc Size (in parameters) of source array. + * @param fDeepCopy Whether to perform a deep copy of pointer parameters or not. + * + * @remark Static convenience function. + */ +/* static */ +int Message::CopyParms(PVBOXHGCMSVCPARM paParmsDst, uint32_t cParmsDst, + PVBOXHGCMSVCPARM paParmsSrc, uint32_t cParmsSrc, + bool fDeepCopy) +{ + AssertPtrReturn(paParmsSrc, VERR_INVALID_POINTER); + AssertPtrReturn(paParmsDst, VERR_INVALID_POINTER); + + if (cParmsSrc > cParmsDst) + return VERR_BUFFER_OVERFLOW; + + int rc = VINF_SUCCESS; + for (uint32_t i = 0; i < cParmsSrc; i++) + { + paParmsDst[i].type = paParmsSrc[i].type; + switch (paParmsSrc[i].type) + { + case VBOX_HGCM_SVC_PARM_32BIT: + { + paParmsDst[i].u.uint32 = paParmsSrc[i].u.uint32; + break; + } + case VBOX_HGCM_SVC_PARM_64BIT: + { + paParmsDst[i].u.uint64 = paParmsSrc[i].u.uint64; + break; + } + case VBOX_HGCM_SVC_PARM_PTR: + { + /* Do we have to perform a deep copy? */ + if (fDeepCopy) + { + /* Yes, do so. */ + paParmsDst[i].u.pointer.size = paParmsSrc[i].u.pointer.size; + if (paParmsDst[i].u.pointer.size > 0) + { + paParmsDst[i].u.pointer.addr = RTMemAlloc(paParmsDst[i].u.pointer.size); + if (!paParmsDst[i].u.pointer.addr) + { + rc = VERR_NO_MEMORY; + break; + } + } + } + else + { + /* No, but we have to check if there is enough room. */ + if (paParmsDst[i].u.pointer.size < paParmsSrc[i].u.pointer.size) + { + rc = VERR_BUFFER_OVERFLOW; + break; + } + } + + if (paParmsSrc[i].u.pointer.size) + { + if ( paParmsDst[i].u.pointer.addr + && paParmsDst[i].u.pointer.size) + { + memcpy(paParmsDst[i].u.pointer.addr, + paParmsSrc[i].u.pointer.addr, + RT_MIN(paParmsDst[i].u.pointer.size, paParmsSrc[i].u.pointer.size)); + } + else + rc = VERR_INVALID_POINTER; + } + break; + } + default: + { + AssertMsgFailed(("Unknown HGCM type %u\n", paParmsSrc[i].type)); + rc = VERR_INVALID_PARAMETER; + break; + } + } + if (RT_FAILURE(rc)) + break; + } + return rc; +} + +/** + * Initializes the message with a message type and parameters. + * + * @returns IPRT status code. + * @param uMsg Message type to set. + * @param cParms Number of parameters to set. + * @param aParms Array of parameters to set. + */ +int Message::initData(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[]) +{ + AssertReturn(cParms < 256, VERR_INVALID_PARAMETER); + AssertPtrNullReturn(aParms, VERR_INVALID_PARAMETER); + + /* Cleanup any eventual old stuff. */ + reset(); + + m_uMsg = uMsg; + m_cParms = cParms; + + int rc = VINF_SUCCESS; + + if (cParms) + { + m_paParms = (VBOXHGCMSVCPARM*)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * m_cParms); + if (m_paParms) + { + rc = Message::CopyParms(m_paParms, m_cParms, &aParms[0], cParms, true /* fDeepCopy */); + if (RT_FAILURE(rc)) + reset(); + } + else + rc = VERR_NO_MEMORY; + } + + return rc; +} + diff --git a/src/VBox/HostServices/testcase/Makefile.kmk b/src/VBox/HostServices/testcase/Makefile.kmk new file mode 100644 index 00000000..0b80413f --- /dev/null +++ b/src/VBox/HostServices/testcase/Makefile.kmk @@ -0,0 +1,42 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the HGCM service testcase. +# + +# +# Copyright (C) 2009-2019 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. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK) + + # + # Set this in LocalConfig.kmk if you are working on HGCM service internals + # to automatically run the unit test at build time: + # OTHERS += $(tstHGCMSvc_0_OUTDIR)/tstHGCMSvc.run + # + PROGRAMS += tstHGCMSvc + TESTING += $(tstHGCMSvc_0_OUTDIR)/tstHGCMSvc.run + tstHGCMSvc_TEMPLATE = VBOXR3TSTEXE + tstHGCMSvc_DEFS = VBOX_WITH_HGCM VBOX_TEST_HGCM_PARMS + tstHGCMSvc_SOURCES = tstHGCMSvc.cpp + tstHGCMSvc_CLEAN = $(tstHGCMSvc_0_OUTDIR)/tstHGCMSvc.run + +$$(tstHGCMSvc_0_OUTDIR)/tstHGCMSvc.run: $$(tstHGCMSvc_1_STAGE_TARGET) + export VBOX_LOG_DEST=nofile; $(tstHGCMSvc_1_STAGE_TARGET) quiet + $(QUIET)$(APPEND) -t "$@" "done" + +endif # VBOX_WITH_TESTCASES + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostServices/testcase/tstHGCMSvc.cpp b/src/VBox/HostServices/testcase/tstHGCMSvc.cpp new file mode 100644 index 00000000..07c06410 --- /dev/null +++ b/src/VBox/HostServices/testcase/tstHGCMSvc.cpp @@ -0,0 +1,109 @@ +/* $Id: tstHGCMSvc.cpp $ */ +/** @file + * HGCM Service Testcase. + */ + +/* + * Copyright (C) 2009-2019 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 * +*********************************************************************************************************************************/ +#include <VBox/hgcmsvc.h> +#include <iprt/initterm.h> +#include <iprt/test.h> + +/** Test the getString member function. Indirectly tests the getPointer + * and getBuffer APIs. + * @param hTest an running IPRT test + * @param type the type that the parameter should be set to before + * calling getString + * @param pcch the value that the parameter should be set to before + * calling getString, and also the address (!) which we + * expect getString to return. Stricter than needed of + * course, but I was feeling lazy. + * @param cb the size that the parameter should be set to before + * calling getString, and also the size which we expect + * getString to return. + * @param rcExp the expected return value of the call to getString. + */ +void doTestGetString(VBOXHGCMSVCPARM *pParm, RTTEST hTest, uint32_t type, + const char *pcch, uint32_t cb, int rcExp) +{ + /* An RTTest API like this, which would print out an additional line + * of context if a test failed, would be nice. This is because the + * line number alone doesn't help much here, given that this is a + * subroutine called many times. */ + /* + RTTestContextF(hTest, + ("doTestGetString, type=%u, pcch=%p, acp=%u, rcExp=%Rrc", + type, pcch, acp, rcExp)); + */ + HGCMSvcSetPv(pParm, (void *)pcch, cb); + pParm->type = type; /* in case we don't want VBOX_HGCM_SVC_PARM_PTR */ + const char *pcch2 = NULL; + uint32_t cb2 = 0; + int rc = HGCMSvcGetCStr(pParm, &pcch2, &cb2); + RTTEST_CHECK_RC(hTest, rc, rcExp); + if (RT_SUCCESS(rcExp)) + { + RTTEST_CHECK_MSG_RETV(hTest, (pcch2 == pcch), + (hTest, "expected %p, got %p", pcch, pcch2)); + RTTEST_CHECK_MSG_RETV(hTest, (cb2 == cb), + (hTest, "expected %u, got %u", cb, cb2)); + } +} + +/** Run some unit tests on the getString method and indirectly test + * getPointer and getBuffer as well. */ +void testGetString(VBOXHGCMSVCPARM *pParm, RTTEST hTest) +{ + RTTestSub(hTest, "HGCM string parameter handling"); + doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_32BIT, "test", 3, + VERR_INVALID_PARAMETER); + doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_PTR, "test", 5, + VINF_SUCCESS); + doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_PTR, "test", 3, + VERR_BUFFER_OVERFLOW); + doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_PTR, "test\xf0", 6, + VERR_INVALID_UTF8_ENCODING); + doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_PTR, "test", 0, + VERR_INVALID_PARAMETER); + doTestGetString(pParm, hTest, VBOX_HGCM_SVC_PARM_PTR, (const char *)0x1, 5, + VERR_INVALID_PARAMETER); + RTTestSubDone(hTest); +} + +int main() +{ + /* + * Init the runtime, test and say hello. + */ + RTTEST hTest; + int rc = RTTestInitAndCreate("tstHGCMSvc", &hTest); + if (rc) + return rc; + RTTestBanner(hTest); + + /* + * Run the test. + */ + VBOXHGCMSVCPARM parm; + testGetString(&parm, hTest); + + /* + * Summary + */ + return RTTestSummaryAndDestroy(hTest); +} + |