/* $Id: Display.cpp 99890 2023-05-22 10:40:30Z vboxsync $ */ /** @file * VirtualBox COM class implementation */ /* * Copyright (C) 2006-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * SPDX-License-Identifier: GPL-3.0-only */ #define LOG_GROUP LOG_GROUP_MAIN_DISPLAY #include "Display.h" #include #include #include #include #include #include #include #include #include /** * Display driver instance data. * * @implements PDMIDISPLAYCONNECTOR */ typedef struct DRVMAINDISPLAY { /** Pointer to the display object. */ Display *pDisplay; /** Pointer to the driver instance structure. */ PPDMDRVINS pDrvIns; /** Pointer to the display port interface of the driver/device above us. */ PPDMIDISPLAYPORT pUpPort; /** Our display connector interface. */ PDMIDISPLAYCONNECTOR IConnector; } DRVMAINDISPLAY, *PDRVMAINDISPLAY; // constructor / destructor ///////////////////////////////////////////////////////////////////////////// Display::Display() { } Display::~Display() { if (mpDrv) mpDrv->pDisplay = NULL; mpDrv = NULL; } // public methods only for internal purposes ///////////////////////////////////////////////////////////////////////////// int Display::SetFramebuffer(unsigned iScreenID, Framebuffer *pFramebuffer) { RT_NOREF(iScreenID); m_pFramebuffer = pFramebuffer; return VINF_SUCCESS; } /** * Handles display resize event. * * @param uScreenId Screen ID * @param bpp New bits per pixel. * @param pvVRAM VRAM pointer. * @param cbLine New bytes per line. * @param w New display width. * @param h New display height. * @param flags Flags of the new video mode. * @param xOrigin New display origin X. * @param yOrigin New display origin Y. * @param fVGAResize Whether the resize is originated from the VGA device (DevVGA). */ int Display::i_handleDisplayResize(unsigned uScreenId, uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t w, uint32_t h, uint16_t flags, int32_t xOrigin, int32_t yOrigin, bool fVGAResize) { LogRel2(("Display::i_handleDisplayResize: uScreenId=%d pvVRAM=%p w=%d h=%d bpp=%d cbLine=0x%X flags=0x%X\n", uScreenId, pvVRAM, w, h, bpp, cbLine, flags)); if (uScreenId == 0 && mpDrv) { mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, false); mpDrv->IConnector.pbData = NULL; mpDrv->IConnector.cbScanline = 0; mpDrv->IConnector.cBits = 32; mpDrv->IConnector.cx = 0; mpDrv->IConnector.cy = 0; } /* Log changes. */ LogRel(("Display::i_handleDisplayResize: uScreenId=%d pvVRAM=%p w=%d h=%d bpp=%d cbLine=0x%X flags=0x%X origin=%d,%d\n", uScreenId, pvVRAM, w, h, bpp, cbLine, flags, xOrigin, yOrigin)); int vrc = m_pFramebuffer->notifyChange(uScreenId, 0, 0, w, h); LogFunc(("NotifyChange vrc=%Rrc\n", vrc)); return VINF_SUCCESS; } /** * Handle display update * * @returns COM status code * @param w New display width * @param h New display height */ void Display::i_handleDisplayUpdate (int x, int y, int w, int h) { // if there is no Framebuffer, this call is not interesting if (m_pFramebuffer == NULL) return; //m_pFramebuffer->Lock(); m_pFramebuffer->notifyUpdate(x, y, w, h); //m_pFramebuffer->Unlock(); } void Display::i_invalidateAndUpdateScreen(uint32_t aScreenId) { mpDrv->IConnector.pbData = m_pFramebuffer->getPixelData(); mpDrv->IConnector.cbScanline = m_pFramebuffer->getBytesPerLine(); mpDrv->IConnector.cBits = m_pFramebuffer->getBitsPerPixel(); mpDrv->IConnector.cx = m_pFramebuffer->getWidth(); mpDrv->IConnector.cy = m_pFramebuffer->getHeight(); mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, true); } // private methods ///////////////////////////////////////////////////////////////////////////// /** * Handle display resize event issued by the VGA device for the primary screen. * * @see PDMIDISPLAYCONNECTOR::pfnResize */ DECLCALLBACK(int) Display::i_displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy) { PDRVMAINDISPLAY pDrv = RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector); Display *pThis = pDrv->pDisplay; LogRelFlowFunc(("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n", bpp, pvVRAM, cbLine, cx, cy)); bool f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, true, false); if (!f) { /* This is a result of recursive call when the source bitmap is being updated * during a VGA resize. Tell the VGA device to ignore the call. * * @todo It is a workaround, actually pfnUpdateDisplayAll must * fail on resize. */ LogRel(("displayResizeCallback: already processing\n")); return VINF_VGA_RESIZE_IN_PROGRESS; } int vrc = pThis->i_handleDisplayResize(0, bpp, pvVRAM, cbLine, cx, cy, 0, 0, 0, true); /* Restore the flag. */ f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, false, true); AssertRelease(f); return vrc; } /** * Handle display update. * * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect */ DECLCALLBACK(void) Display::i_displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy) { PDRVMAINDISPLAY pDrv = RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector); pDrv->pDisplay->i_handleDisplayUpdate(x, y, cx, cy); } /** * Periodic display refresh callback. * * @see PDMIDISPLAYCONNECTOR::pfnRefresh * @thread EMT */ /*static*/ DECLCALLBACK(void) Display::i_displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface) { PDRVMAINDISPLAY pDrv = RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector); Display *pDisplay = pDrv->pDisplay; pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort); } /** * Reset notification * * @see PDMIDISPLAYCONNECTOR::pfnReset */ DECLCALLBACK(void) Display::i_displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface) { RT_NOREF(pInterface); LogRelFlowFunc(("\n")); } /** * LFBModeChange notification * * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange */ DECLCALLBACK(void) Display::i_displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled) { RT_NOREF(pInterface, fEnabled); } /** * Adapter information change notification. * * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData */ DECLCALLBACK(void) Display::i_displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, uint32_t u32VRAMSize) { PDRVMAINDISPLAY pDrv = RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector); //pDrv->pDisplay->processAdapterData(pvVRAM, u32VRAMSize); } /** * Display information change notification. * * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData */ DECLCALLBACK(void) Display::i_displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, unsigned uScreenId) { PDRVMAINDISPLAY pDrv = RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector); //pDrv->pDisplay->processDisplayData(pvVRAM, uScreenId); } /** * @interface_method_impl{PDMIBASE,pfnQueryInterface} */ DECLCALLBACK(void *) Display::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID) { PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYCONNECTOR, &pDrv->IConnector); return NULL; } /** * @interface_method_impl{PDMDRVREG,pfnPowerOff, * Tries to ensure no client calls gets to HGCM or the VGA device from here on.} */ DECLCALLBACK(void) Display::i_drvPowerOff(PPDMDRVINS pDrvIns) { PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY); LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance)); /* * Do much of the work that i_drvDestruct does. */ if (pThis->pUpPort) pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false); pThis->IConnector.pbData = NULL; pThis->IConnector.cbScanline = 0; pThis->IConnector.cBits = 32; pThis->IConnector.cx = 0; pThis->IConnector.cy = 0; } /** * Destruct a display driver instance. * * @returns VBox status code. * @param pDrvIns The driver instance data. */ DECLCALLBACK(void) Display::i_drvDestruct(PPDMDRVINS pDrvIns) { PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY); LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance)); /* * We repeat much of what i_drvPowerOff does in case it wasn't called. * In addition we sever the connection between us and the display. */ if (pThis->pUpPort) pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false); pThis->IConnector.pbData = NULL; pThis->IConnector.cbScanline = 0; pThis->IConnector.cBits = 32; pThis->IConnector.cx = 0; pThis->IConnector.cy = 0; if (pThis->pDisplay) { pThis->pDisplay->mpDrv = NULL; pThis->pDisplay = NULL; } } /** * Construct a display driver instance. * * @copydoc FNPDMDRVCONSTRUCT */ DECLCALLBACK(int) Display::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) { PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); RT_NOREF(fFlags, pCfg); PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY); LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance)); /* * Validate configuration. */ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "", ""); AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER, ("Configuration error: Not possible to attach anything to this driver!\n"), VERR_PDM_DRVINS_NO_ATTACH); /* * Init Interfaces. */ pDrvIns->IBase.pfnQueryInterface = Display::i_drvQueryInterface; pThis->IConnector.pfnResize = Display::i_displayResizeCallback; pThis->IConnector.pfnUpdateRect = Display::i_displayUpdateCallback; pThis->IConnector.pfnRefresh = Display::i_displayRefreshCallback; pThis->IConnector.pfnReset = Display::i_displayResetCallback; pThis->IConnector.pfnLFBModeChange = Display::i_displayLFBModeChangeCallback; pThis->IConnector.pfnProcessAdapterData = Display::i_displayProcessAdapterDataCallback; pThis->IConnector.pfnProcessDisplayData = Display::i_displayProcessDisplayDataCallback; /* * Get the IDisplayPort interface of the above driver/device. */ pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT); if (!pThis->pUpPort) { AssertMsgFailed(("Configuration error: No display port interface above!\n")); return VERR_PDM_MISSING_INTERFACE_ABOVE; } /* * Get the Display object pointer and update the mpDrv member. */ RTUUID UuidDisp; RTUuidFromStr(&UuidDisp, DISPLAY_OID); Display *pDisplay = (Display *)PDMDrvHlpQueryGenericUserObject(pDrvIns, &UuidDisp); if (!pDisplay) { AssertMsgFailed(("Configuration error: No/bad Display object!\n")); return VERR_NOT_FOUND; } pThis->pDisplay = pDisplay; pThis->pDisplay->mpDrv = pThis; /* Disable VRAM to a buffer copy initially. */ pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false); pThis->IConnector.cBits = 32; /* DevVGA does nothing otherwise. */ /* * Start periodic screen refreshes */ pThis->pUpPort->pfnSetRefreshRate(pThis->pUpPort, 20); return VINF_SUCCESS; } /** * Display driver registration record. */ const PDMDRVREG Display::DrvReg = { /* u32Version */ PDM_DRVREG_VERSION, /* szName */ "MainDisplay", /* szRCMod */ "", /* szR0Mod */ "", /* pszDescription */ "Main display driver (Main as in the API).", /* fFlags */ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, /* fClass. */ PDM_DRVREG_CLASS_DISPLAY, /* cMaxInstances */ ~0U, /* cbInstance */ sizeof(DRVMAINDISPLAY), /* pfnConstruct */ Display::i_drvConstruct, /* pfnDestruct */ Display::i_drvDestruct, /* pfnRelocate */ NULL, /* pfnIOCtl */ NULL, /* pfnPowerOn */ NULL, /* pfnReset */ NULL, /* pfnSuspend */ NULL, /* pfnResume */ NULL, /* pfnAttach */ NULL, /* pfnDetach */ NULL, /* pfnPowerOff */ Display::i_drvPowerOff, /* pfnSoftReset */ NULL, /* u32EndVersion */ PDM_DRVREG_VERSION }; /* vi: set tabstop=4 shiftwidth=4 expandtab: */