summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-client/DisplayImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-client/DisplayImpl.cpp')
-rw-r--r--src/VBox/Main/src-client/DisplayImpl.cpp3871
1 files changed, 3871 insertions, 0 deletions
diff --git a/src/VBox/Main/src-client/DisplayImpl.cpp b/src/VBox/Main/src-client/DisplayImpl.cpp
new file mode 100644
index 00000000..5a4beefe
--- /dev/null
+++ b/src/VBox/Main/src-client/DisplayImpl.cpp
@@ -0,0 +1,3871 @@
+/* $Id: DisplayImpl.cpp $ */
+/** @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 <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
+#include "LoggingNew.h"
+
+#include "DisplayImpl.h"
+#include "DisplayUtils.h"
+#include "ConsoleImpl.h"
+#include "ConsoleVRDPServer.h"
+#include "GuestImpl.h"
+#include "VMMDev.h"
+
+#include "AutoCaller.h"
+
+/* generated header */
+#include "VBoxEvents.h"
+
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+#include <iprt/asm.h>
+#include <iprt/time.h>
+#include <iprt/cpp/utils.h>
+#include <iprt/alloca.h>
+
+#include <VBox/vmm/vmmr3vtable.h>
+#include <VBox/vmm/pdmdrv.h>
+
+#ifdef VBOX_WITH_VIDEOHWACCEL
+# include <VBoxVideo.h>
+#endif
+#include <VBoxVideo3D.h>
+
+#include <VBox/com/array.h>
+
+#ifdef VBOX_WITH_RECORDING
+# include <iprt/path.h>
+# include "Recording.h"
+
+# include <VBox/vmm/pdmapi.h>
+# include <VBox/vmm/pdmaudioifs.h>
+#endif
+
+/**
+ * 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;
+#if defined(VBOX_WITH_VIDEOHWACCEL)
+ /** VBVA callbacks */
+ PPDMIDISPLAYVBVACALLBACKS pVBVACallbacks;
+#endif
+} DRVMAINDISPLAY, *PDRVMAINDISPLAY;
+
+/** Converts PDMIDISPLAYCONNECTOR pointer to a DRVMAINDISPLAY pointer. */
+#define PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface) RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector)
+
+// constructor / destructor
+/////////////////////////////////////////////////////////////////////////////
+
+Display::Display()
+ : mParent(NULL)
+{
+}
+
+Display::~Display()
+{
+}
+
+
+HRESULT Display::FinalConstruct()
+{
+ int vrc = videoAccelConstruct(&mVideoAccelLegacy);
+ AssertRC(vrc);
+
+ mfVideoAccelVRDP = false;
+ mfu32SupportedOrders = 0;
+ mcVRDPRefs = 0;
+
+ mfSeamlessEnabled = false;
+ mpRectVisibleRegion = NULL;
+ mcRectVisibleRegion = 0;
+
+ mpDrv = NULL;
+
+ vrc = RTCritSectInit(&mVideoAccelLock);
+ AssertRC(vrc);
+
+#ifdef VBOX_WITH_HGSMI
+ mu32UpdateVBVAFlags = 0;
+ mfVMMDevSupportsGraphics = false;
+ mfGuestVBVACapabilities = 0;
+ mfHostCursorCapabilities = 0;
+#endif
+
+#ifdef VBOX_WITH_RECORDING
+ vrc = RTCritSectInit(&mVideoRecLock);
+ AssertRC(vrc);
+
+ for (unsigned i = 0; i < RT_ELEMENTS(maRecordingEnabled); i++)
+ maRecordingEnabled[i] = true;
+#endif
+
+ return BaseFinalConstruct();
+}
+
+void Display::FinalRelease()
+{
+ uninit();
+
+#ifdef VBOX_WITH_RECORDING
+ if (RTCritSectIsInitialized(&mVideoRecLock))
+ {
+ RTCritSectDelete(&mVideoRecLock);
+ RT_ZERO(mVideoRecLock);
+ }
+#endif
+
+ videoAccelDestroy(&mVideoAccelLegacy);
+ i_saveVisibleRegion(0, NULL);
+
+ if (RTCritSectIsInitialized(&mVideoAccelLock))
+ {
+ RTCritSectDelete(&mVideoAccelLock);
+ RT_ZERO(mVideoAccelLock);
+ }
+
+ BaseFinalRelease();
+}
+
+// public initializer/uninitializer for internal purposes only
+/////////////////////////////////////////////////////////////////////////////
+
+#define kMaxSizeThumbnail 64
+
+/**
+ * Save thumbnail and screenshot of the guest screen.
+ */
+static int displayMakeThumbnail(uint8_t *pbData, uint32_t cx, uint32_t cy,
+ uint8_t **ppu8Thumbnail, uint32_t *pcbThumbnail, uint32_t *pcxThumbnail, uint32_t *pcyThumbnail)
+{
+ int vrc = VINF_SUCCESS;
+
+ uint8_t *pu8Thumbnail = NULL;
+ uint32_t cbThumbnail = 0;
+ uint32_t cxThumbnail = 0;
+ uint32_t cyThumbnail = 0;
+
+ if (cx > cy)
+ {
+ cxThumbnail = kMaxSizeThumbnail;
+ cyThumbnail = (kMaxSizeThumbnail * cy) / cx;
+ }
+ else
+ {
+ cyThumbnail = kMaxSizeThumbnail;
+ cxThumbnail = (kMaxSizeThumbnail * cx) / cy;
+ }
+
+ LogRelFlowFunc(("%dx%d -> %dx%d\n", cx, cy, cxThumbnail, cyThumbnail));
+
+ cbThumbnail = cxThumbnail * 4 * cyThumbnail;
+ pu8Thumbnail = (uint8_t *)RTMemAlloc(cbThumbnail);
+
+ if (pu8Thumbnail)
+ {
+ uint8_t *dst = pu8Thumbnail;
+ uint8_t *src = pbData;
+ int dstW = cxThumbnail;
+ int dstH = cyThumbnail;
+ int srcW = cx;
+ int srcH = cy;
+ int iDeltaLine = cx * 4;
+
+ BitmapScale32(dst,
+ dstW, dstH,
+ src,
+ iDeltaLine,
+ srcW, srcH);
+
+ *ppu8Thumbnail = pu8Thumbnail;
+ *pcbThumbnail = cbThumbnail;
+ *pcxThumbnail = cxThumbnail;
+ *pcyThumbnail = cyThumbnail;
+ }
+ else
+ {
+ vrc = VERR_NO_MEMORY;
+ }
+
+ return vrc;
+}
+
+/**
+ * @callback_method_impl{FNSSMEXTSAVEEXEC}
+ */
+DECLCALLBACK(int) Display::i_displaySSMSaveScreenshot(PSSMHANDLE pSSM, PCVMMR3VTABLE pVMM, void *pvUser)
+{
+ Display * const pThat = static_cast<Display *>(pvUser);
+ AssertPtrReturn(pThat, VERR_INVALID_POINTER);
+
+ /* 32bpp small RGB image. */
+ uint8_t *pu8Thumbnail = NULL;
+ uint32_t cbThumbnail = 0;
+ uint32_t cxThumbnail = 0;
+ uint32_t cyThumbnail = 0;
+
+ /* PNG screenshot. */
+ uint8_t *pu8PNG = NULL;
+ uint32_t cbPNG = 0;
+ uint32_t cxPNG = 0;
+ uint32_t cyPNG = 0;
+
+ Console::SafeVMPtr ptrVM(pThat->mParent);
+ if (ptrVM.isOk())
+ {
+ /* Query RGB bitmap. */
+ /* SSM code is executed on EMT(0), therefore no need to use VMR3ReqCallWait. */
+ uint8_t *pbData = NULL;
+ size_t cbData = 0;
+ uint32_t cx = 0;
+ uint32_t cy = 0;
+ bool fFreeMem = false;
+ int vrc = Display::i_displayTakeScreenshotEMT(pThat, VBOX_VIDEO_PRIMARY_SCREEN, &pbData, &cbData, &cx, &cy, &fFreeMem);
+
+ /*
+ * It is possible that success is returned but everything is 0 or NULL.
+ * (no display attached if a VM is running with VBoxHeadless on OSE for example)
+ */
+ if (RT_SUCCESS(vrc) && pbData)
+ {
+ Assert(cx && cy);
+
+ /* Prepare a small thumbnail and a PNG screenshot. */
+ displayMakeThumbnail(pbData, cx, cy, &pu8Thumbnail, &cbThumbnail, &cxThumbnail, &cyThumbnail);
+ vrc = DisplayMakePNG(pbData, cx, cy, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 1);
+ if (RT_FAILURE(vrc))
+ {
+ if (pu8PNG)
+ {
+ RTMemFree(pu8PNG);
+ pu8PNG = NULL;
+ }
+ cbPNG = 0;
+ cxPNG = 0;
+ cyPNG = 0;
+ }
+
+ if (fFreeMem)
+ RTMemFree(pbData);
+ else
+ pThat->mpDrv->pUpPort->pfnFreeScreenshot(pThat->mpDrv->pUpPort, pbData);
+ }
+ }
+ else
+ {
+ LogFunc(("Failed to get VM pointer 0x%x\n", ptrVM.hrc()));
+ }
+
+ /* Regardless of vrc, save what is available:
+ * Data format:
+ * uint32_t cBlocks;
+ * [blocks]
+ *
+ * Each block is:
+ * uint32_t cbBlock; if 0 - no 'block data'.
+ * uint32_t typeOfBlock; 0 - 32bpp RGB bitmap, 1 - PNG, ignored if 'cbBlock' is 0.
+ * [block data]
+ *
+ * Block data for bitmap and PNG:
+ * uint32_t cx;
+ * uint32_t cy;
+ * [image data]
+ */
+ pVMM->pfnSSMR3PutU32(pSSM, 2); /* Write thumbnail and PNG screenshot. */
+
+ /* First block. */
+ pVMM->pfnSSMR3PutU32(pSSM, (uint32_t)(cbThumbnail + 2 * sizeof(uint32_t)));
+ pVMM->pfnSSMR3PutU32(pSSM, 0); /* Block type: thumbnail. */
+
+ if (cbThumbnail)
+ {
+ pVMM->pfnSSMR3PutU32(pSSM, cxThumbnail);
+ pVMM->pfnSSMR3PutU32(pSSM, cyThumbnail);
+ pVMM->pfnSSMR3PutMem(pSSM, pu8Thumbnail, cbThumbnail);
+ }
+
+ /* Second block. */
+ pVMM->pfnSSMR3PutU32(pSSM, (uint32_t)(cbPNG + 2 * sizeof(uint32_t)));
+ pVMM->pfnSSMR3PutU32(pSSM, 1); /* Block type: png. */
+
+ if (cbPNG)
+ {
+ pVMM->pfnSSMR3PutU32(pSSM, cxPNG);
+ pVMM->pfnSSMR3PutU32(pSSM, cyPNG);
+ pVMM->pfnSSMR3PutMem(pSSM, pu8PNG, cbPNG);
+ }
+
+ RTMemFree(pu8PNG);
+ RTMemFree(pu8Thumbnail);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMEXTLOADEXEC}
+ */
+DECLCALLBACK(int)
+Display::i_displaySSMLoadScreenshot(PSSMHANDLE pSSM, PCVMMR3VTABLE pVMM, void *pvUser, uint32_t uVersion, uint32_t uPass)
+{
+ Display * const pThat = static_cast<Display *>(pvUser);
+ AssertPtrReturn(pThat, VERR_INVALID_POINTER);
+ Assert(uPass == SSM_PASS_FINAL); RT_NOREF_PV(uPass);
+
+ if (uVersion != sSSMDisplayScreenshotVer)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ /* Skip data. */
+ uint32_t cBlocks;
+ int vrc = pVMM->pfnSSMR3GetU32(pSSM, &cBlocks);
+ AssertRCReturn(vrc, vrc);
+
+ for (uint32_t i = 0; i < cBlocks; i++)
+ {
+ uint32_t cbBlock;
+ vrc = pVMM->pfnSSMR3GetU32(pSSM, &cbBlock);
+ AssertRCReturn(vrc, vrc);
+
+ uint32_t typeOfBlock;
+ vrc = pVMM->pfnSSMR3GetU32(pSSM, &typeOfBlock);
+ AssertRCReturn(vrc, vrc);
+
+ LogRelFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
+
+ /* Note: displaySSMSaveScreenshot writes size of a block = 8 and
+ * do not write any data if the image size was 0.
+ */
+ /** @todo Fix and increase saved state version. */
+ if (cbBlock > 2 * sizeof(uint32_t))
+ {
+ vrc = pVMM->pfnSSMR3Skip(pSSM, cbBlock);
+ AssertRCReturn(vrc, vrc);
+ }
+ }
+
+ return vrc;
+}
+
+/**
+ * @callback_method_impl{FNSSMEXTSAVEEXEC, Save some important guest state}
+ */
+/*static*/ DECLCALLBACK(int)
+Display::i_displaySSMSave(PSSMHANDLE pSSM, PCVMMR3VTABLE pVMM, void *pvUser)
+{
+ Display * const pThat = static_cast<Display *>(pvUser);
+ AssertPtrReturn(pThat, VERR_INVALID_POINTER);
+
+ pVMM->pfnSSMR3PutU32(pSSM, pThat->mcMonitors);
+ for (unsigned i = 0; i < pThat->mcMonitors; i++)
+ {
+ pVMM->pfnSSMR3PutU32(pSSM, pThat->maFramebuffers[i].u32Offset);
+ pVMM->pfnSSMR3PutU32(pSSM, pThat->maFramebuffers[i].u32MaxFramebufferSize);
+ pVMM->pfnSSMR3PutU32(pSSM, pThat->maFramebuffers[i].u32InformationSize);
+ pVMM->pfnSSMR3PutU32(pSSM, pThat->maFramebuffers[i].w);
+ pVMM->pfnSSMR3PutU32(pSSM, pThat->maFramebuffers[i].h);
+ pVMM->pfnSSMR3PutS32(pSSM, pThat->maFramebuffers[i].xOrigin);
+ pVMM->pfnSSMR3PutS32(pSSM, pThat->maFramebuffers[i].yOrigin);
+ pVMM->pfnSSMR3PutU32(pSSM, pThat->maFramebuffers[i].flags);
+ }
+ pVMM->pfnSSMR3PutS32(pSSM, pThat->xInputMappingOrigin);
+ pVMM->pfnSSMR3PutS32(pSSM, pThat->yInputMappingOrigin);
+ pVMM->pfnSSMR3PutU32(pSSM, pThat->cxInputMapping);
+ pVMM->pfnSSMR3PutU32(pSSM, pThat->cyInputMapping);
+ pVMM->pfnSSMR3PutU32(pSSM, pThat->mfGuestVBVACapabilities);
+ return pVMM->pfnSSMR3PutU32(pSSM, pThat->mfHostCursorCapabilities);
+}
+
+/**
+ * @callback_method_impl{FNSSMEXTLOADEXEC, Load some important guest state}
+ */
+/*static*/ DECLCALLBACK(int)
+Display::i_displaySSMLoad(PSSMHANDLE pSSM, PCVMMR3VTABLE pVMM, void *pvUser, uint32_t uVersion, uint32_t uPass)
+{
+ Display * const pThat = static_cast<Display *>(pvUser);
+ AssertPtrReturn(pThat, VERR_INVALID_POINTER);
+
+ if ( uVersion != sSSMDisplayVer
+ && uVersion != sSSMDisplayVer2
+ && uVersion != sSSMDisplayVer3
+ && uVersion != sSSMDisplayVer4
+ && uVersion != sSSMDisplayVer5)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+ Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
+
+ uint32_t cMonitors;
+ int vrc = pVMM->pfnSSMR3GetU32(pSSM, &cMonitors);
+ AssertRCReturn(vrc, vrc);
+ if (cMonitors != pThat->mcMonitors)
+ return pVMM->pfnSSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Number of monitors changed (%d->%d)!"), cMonitors, pThat->mcMonitors);
+
+ for (uint32_t i = 0; i < cMonitors; i++)
+ {
+ pVMM->pfnSSMR3GetU32(pSSM, &pThat->maFramebuffers[i].u32Offset);
+ pVMM->pfnSSMR3GetU32(pSSM, &pThat->maFramebuffers[i].u32MaxFramebufferSize);
+ pVMM->pfnSSMR3GetU32(pSSM, &pThat->maFramebuffers[i].u32InformationSize);
+ if ( uVersion == sSSMDisplayVer2
+ || uVersion == sSSMDisplayVer3
+ || uVersion == sSSMDisplayVer4
+ || uVersion == sSSMDisplayVer5)
+ {
+ uint32_t w;
+ uint32_t h;
+ pVMM->pfnSSMR3GetU32(pSSM, &w);
+ vrc = pVMM->pfnSSMR3GetU32(pSSM, &h);
+ AssertRCReturn(vrc, vrc);
+ pThat->maFramebuffers[i].w = w;
+ pThat->maFramebuffers[i].h = h;
+ }
+ if ( uVersion == sSSMDisplayVer3
+ || uVersion == sSSMDisplayVer4
+ || uVersion == sSSMDisplayVer5)
+ {
+ int32_t xOrigin;
+ int32_t yOrigin;
+ uint32_t flags;
+ pVMM->pfnSSMR3GetS32(pSSM, &xOrigin);
+ pVMM->pfnSSMR3GetS32(pSSM, &yOrigin);
+ vrc = pVMM->pfnSSMR3GetU32(pSSM, &flags);
+ AssertRCReturn(vrc, vrc);
+ pThat->maFramebuffers[i].xOrigin = xOrigin;
+ pThat->maFramebuffers[i].yOrigin = yOrigin;
+ pThat->maFramebuffers[i].flags = (uint16_t)flags;
+ pThat->maFramebuffers[i].fDisabled = (pThat->maFramebuffers[i].flags & VBVA_SCREEN_F_DISABLED) != 0;
+ }
+ }
+ if ( uVersion == sSSMDisplayVer4
+ || uVersion == sSSMDisplayVer5)
+ {
+ pVMM->pfnSSMR3GetS32(pSSM, &pThat->xInputMappingOrigin);
+ pVMM->pfnSSMR3GetS32(pSSM, &pThat->yInputMappingOrigin);
+ pVMM->pfnSSMR3GetU32(pSSM, &pThat->cxInputMapping);
+ pVMM->pfnSSMR3GetU32(pSSM, &pThat->cyInputMapping);
+ }
+ if (uVersion == sSSMDisplayVer5)
+ {
+ pVMM->pfnSSMR3GetU32(pSSM, &pThat->mfGuestVBVACapabilities);
+ pVMM->pfnSSMR3GetU32(pSSM, &pThat->mfHostCursorCapabilities);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initializes the display object.
+ *
+ * @returns COM result indicator
+ * @param aParent handle of our parent object
+ */
+HRESULT Display::init(Console *aParent)
+{
+ ComAssertRet(aParent, E_INVALIDARG);
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ unconst(mParent) = aParent;
+
+ mfSourceBitmapEnabled = true;
+ fVGAResizing = false;
+
+ ComPtr<IGraphicsAdapter> pGraphicsAdapter;
+ HRESULT hrc = mParent->i_machine()->COMGETTER(GraphicsAdapter)(pGraphicsAdapter.asOutParam());
+ AssertComRCReturnRC(hrc);
+ AssertReturn(!pGraphicsAdapter.isNull(), E_FAIL);
+
+ ULONG ul;
+ pGraphicsAdapter->COMGETTER(MonitorCount)(&ul);
+ mcMonitors = ul;
+ xInputMappingOrigin = 0;
+ yInputMappingOrigin = 0;
+ cxInputMapping = 0;
+ cyInputMapping = 0;
+
+ for (ul = 0; ul < mcMonitors; ul++)
+ {
+ maFramebuffers[ul].u32Offset = 0;
+ maFramebuffers[ul].u32MaxFramebufferSize = 0;
+ maFramebuffers[ul].u32InformationSize = 0;
+
+ maFramebuffers[ul].pFramebuffer = NULL;
+ /* All secondary monitors are disabled at startup. */
+ maFramebuffers[ul].fDisabled = ul > 0;
+
+ maFramebuffers[ul].u32Caps = 0;
+
+ maFramebuffers[ul].updateImage.pu8Address = NULL;
+ maFramebuffers[ul].updateImage.cbLine = 0;
+
+ maFramebuffers[ul].xOrigin = 0;
+ maFramebuffers[ul].yOrigin = 0;
+
+ maFramebuffers[ul].w = 0;
+ maFramebuffers[ul].h = 0;
+
+ maFramebuffers[ul].flags = maFramebuffers[ul].fDisabled? VBVA_SCREEN_F_DISABLED: 0;
+
+ maFramebuffers[ul].u16BitsPerPixel = 0;
+ maFramebuffers[ul].pu8FramebufferVRAM = NULL;
+ maFramebuffers[ul].u32LineSize = 0;
+
+ maFramebuffers[ul].pHostEvents = NULL;
+
+ maFramebuffers[ul].fDefaultFormat = false;
+
+#ifdef VBOX_WITH_HGSMI
+ maFramebuffers[ul].fVBVAEnabled = false;
+ maFramebuffers[ul].fVBVAForceResize = false;
+ maFramebuffers[ul].pVBVAHostFlags = NULL;
+#endif /* VBOX_WITH_HGSMI */
+ }
+
+ {
+ // register listener for state change events
+ ComPtr<IEventSource> es;
+ mParent->COMGETTER(EventSource)(es.asOutParam());
+ com::SafeArray<VBoxEventType_T> eventTypes;
+ eventTypes.push_back(VBoxEventType_OnStateChanged);
+ es->RegisterListener(this, ComSafeArrayAsInParam(eventTypes), true);
+ }
+
+ /* Confirm a successful initialization */
+ autoInitSpan.setSucceeded();
+
+ return S_OK;
+}
+
+/**
+ * Uninitializes the instance and sets the ready flag to FALSE.
+ * Called either from FinalRelease() or by the parent when it gets destroyed.
+ */
+void Display::uninit()
+{
+ LogRelFlowFunc(("this=%p\n", this));
+
+ /* Enclose the state transition Ready->InUninit->NotReady */
+ AutoUninitSpan autoUninitSpan(this);
+ if (autoUninitSpan.uninitDone())
+ return;
+
+ unsigned uScreenId;
+ for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
+ {
+ maFramebuffers[uScreenId].pSourceBitmap.setNull();
+ maFramebuffers[uScreenId].updateImage.pSourceBitmap.setNull();
+ maFramebuffers[uScreenId].updateImage.pu8Address = NULL;
+ maFramebuffers[uScreenId].updateImage.cbLine = 0;
+ maFramebuffers[uScreenId].pFramebuffer.setNull();
+#ifdef VBOX_WITH_RECORDING
+ maFramebuffers[uScreenId].Recording.pSourceBitmap.setNull();
+#endif
+ }
+
+ if (mParent)
+ {
+ ComPtr<IEventSource> es;
+ mParent->COMGETTER(EventSource)(es.asOutParam());
+ es->UnregisterListener(this);
+ }
+
+ unconst(mParent) = NULL;
+
+ if (mpDrv)
+ mpDrv->pDisplay = NULL;
+
+ mpDrv = NULL;
+}
+
+/**
+ * Register the SSM methods. Called by the power up thread to be able to
+ * pass pVM
+ */
+int Display::i_registerSSM(PUVM pUVM)
+{
+ PCVMMR3VTABLE const pVMM = mParent->i_getVMMVTable();
+ AssertPtrReturn(pVMM, VERR_INTERNAL_ERROR_3);
+
+ /* Version 2 adds width and height of the framebuffer; version 3 adds
+ * the framebuffer offset in the virtual desktop and the framebuffer flags;
+ * version 4 adds guest to host input event mapping and version 5 adds
+ * guest VBVA and host cursor capabilities.
+ */
+ int vrc = pVMM->pfnSSMR3RegisterExternal(pUVM, "DisplayData", 0, sSSMDisplayVer5,
+ mcMonitors * sizeof(uint32_t) * 8 + sizeof(uint32_t),
+ NULL, NULL, NULL,
+ NULL, i_displaySSMSave, NULL,
+ NULL, i_displaySSMLoad, NULL, this);
+ AssertRCReturn(vrc, vrc);
+
+ /*
+ * Register loaders for old saved states where iInstance was
+ * 3 * sizeof(uint32_t *) due to a code mistake.
+ */
+ vrc = pVMM->pfnSSMR3RegisterExternal(pUVM, "DisplayData", 12 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, i_displaySSMLoad, NULL, this);
+ AssertRCReturn(vrc, vrc);
+
+ vrc = pVMM->pfnSSMR3RegisterExternal(pUVM, "DisplayData", 24 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, i_displaySSMLoad, NULL, this);
+ AssertRCReturn(vrc, vrc);
+
+ /* uInstance is an arbitrary value greater than 1024. Such a value will ensure a quick seek in saved state file. */
+ vrc = pVMM->pfnSSMR3RegisterExternal(pUVM, "DisplayScreenshot", 1100 /*uInstance*/, sSSMDisplayScreenshotVer, 0 /*cbGuess*/,
+ NULL, NULL, NULL,
+ NULL, i_displaySSMSaveScreenshot, NULL,
+ NULL, i_displaySSMLoadScreenshot, NULL, this);
+
+ AssertRCReturn(vrc, vrc);
+
+ return VINF_SUCCESS;
+}
+
+// public methods only for internal purposes
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * 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));
+
+ /* Caller must not hold the object lock. */
+ AssertReturn(!isWriteLockOnCurrentThread(), VERR_INVALID_STATE);
+
+ /* Note: the old code checked if the video mode was actually changed and
+ * did not invalidate the source bitmap if the mode did not change.
+ * The new code always invalidates the source bitmap, i.e. it will
+ * notify the frontend even if nothing actually changed.
+ *
+ * Implementing the filtering is possible but might lead to pfnSetRenderVRAM races
+ * between this method and QuerySourceBitmap. Such races can be avoided by implementing
+ * the @todo below.
+ */
+
+ /* Make sure that the VGA device does not access the source bitmap. */
+ if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && mpDrv)
+ {
+ /// @todo It is probably more convenient to implement
+ // mpDrv->pUpPort->pfnSetOutputBitmap(pvVRAM, cbScanline, cBits, cx, cy, bool fSet);
+ // and remove IConnector.pbData, cbScanline, cBits, cx, cy.
+ // fSet = false disables rendering and VGA can check
+ // if it is already rendering to a different bitmap, avoiding
+ // enable/disable rendering races.
+ mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, false);
+
+ mpDrv->IConnector.pbData = NULL;
+ mpDrv->IConnector.cbScanline = 0;
+ mpDrv->IConnector.cBits = 32; /* DevVGA does not work with cBits == 0. */
+ mpDrv->IConnector.cx = 0;
+ mpDrv->IConnector.cy = 0;
+ }
+
+ /* Update maFramebuffers[uScreenId] under lock. */
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (uScreenId >= mcMonitors)
+ {
+ LogRel(("Display::i_handleDisplayResize: mcMonitors=%u < uScreenId=%u (pvVRAM=%p w=%u h=%u bpp=%d cbLine=0x%X flags=0x%X)\n",
+ mcMonitors, uScreenId, pvVRAM, w, h, bpp, cbLine, flags));
+ return VINF_SUCCESS;
+ }
+
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
+
+ /* Whether the monitor position has changed.
+ * A resize initiated by the VGA device does not change the monitor position.
+ */
+ const bool fNewOrigin = !fVGAResize
+ && ( pFBInfo->xOrigin != xOrigin
+ || pFBInfo->yOrigin != yOrigin);
+
+ /* The event for disabled->enabled transition.
+ * VGA resizes also come when the guest uses VBVA mode. They do not affect pFBInfo->fDisabled.
+ * The primary screen is re-enabled when the guest leaves the VBVA mode in i_displayVBVADisable.
+ */
+ const bool fGuestMonitorChangedEvent = !fVGAResize
+ && (pFBInfo->fDisabled != RT_BOOL(flags & VBVA_SCREEN_F_DISABLED));
+
+ /* Reset the update mode. */
+ pFBInfo->updateImage.pSourceBitmap.setNull();
+ pFBInfo->updateImage.pu8Address = NULL;
+ pFBInfo->updateImage.cbLine = 0;
+
+ /* Release the current source bitmap. */
+ pFBInfo->pSourceBitmap.setNull();
+
+ /* VGA blanking is signaled as w=0, h=0, bpp=0 and cbLine=0, and it's
+ * best to keep the old resolution, as otherwise the window size would
+ * change before the new resolution is known. */
+ const bool fVGABlank = fVGAResize && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
+ && w == 0 && h == 0 && bpp == 0 && cbLine == 0;
+ if (fVGABlank)
+ {
+ w = pFBInfo->w;
+ h = pFBInfo->h;
+ }
+
+ /* Log changes. */
+ if ( pFBInfo->w != w
+ || pFBInfo->h != h
+ || pFBInfo->u32LineSize != cbLine
+ /*|| pFBInfo->pu8FramebufferVRAM != (uint8_t *)pvVRAM - too noisy */
+ || ( !fVGAResize
+ && ( pFBInfo->xOrigin != xOrigin
+ || pFBInfo->yOrigin != yOrigin
+ || pFBInfo->flags != flags)))
+ 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));
+
+ /* Update the video mode information. */
+ pFBInfo->w = w;
+ pFBInfo->h = h;
+ pFBInfo->u16BitsPerPixel = (uint16_t)bpp;
+ pFBInfo->pu8FramebufferVRAM = (uint8_t *)pvVRAM;
+ pFBInfo->u32LineSize = cbLine;
+ if (!fVGAResize)
+ {
+ /* Fields which are not used in not VBVA modes and not affected by a VGA resize. */
+ pFBInfo->flags = flags;
+ pFBInfo->xOrigin = xOrigin;
+ pFBInfo->yOrigin = yOrigin;
+ pFBInfo->fDisabled = RT_BOOL(flags & VBVA_SCREEN_F_DISABLED);
+ pFBInfo->fVBVAForceResize = false;
+ }
+ else
+ {
+ pFBInfo->flags = VBVA_SCREEN_F_ACTIVE;
+ if (fVGABlank)
+ pFBInfo->flags |= VBVA_SCREEN_F_BLANK;
+ pFBInfo->fDisabled = false;
+ }
+
+ /* Prepare local vars for the notification code below. */
+ ComPtr<IFramebuffer> pFramebuffer = pFBInfo->pFramebuffer;
+ const bool fDisabled = pFBInfo->fDisabled;
+
+ alock.release();
+
+ if (!pFramebuffer.isNull())
+ {
+ HRESULT hr = pFramebuffer->NotifyChange(uScreenId, 0, 0, w, h); /** @todo origin */
+ LogFunc(("NotifyChange hr %08X\n", hr));
+ NOREF(hr);
+ }
+
+ if (fGuestMonitorChangedEvent)
+ {
+ if (fDisabled)
+ ::FireGuestMonitorChangedEvent(mParent->i_getEventSource(),
+ GuestMonitorChangedEventType_Disabled, uScreenId, 0, 0, 0, 0);
+ else
+ ::FireGuestMonitorChangedEvent(mParent->i_getEventSource(),
+ GuestMonitorChangedEventType_Enabled, uScreenId, xOrigin, yOrigin, w, h);
+ }
+
+ if (fNewOrigin)
+ ::FireGuestMonitorChangedEvent(mParent->i_getEventSource(),
+ GuestMonitorChangedEventType_NewOrigin, uScreenId, xOrigin, yOrigin, 0, 0);
+
+ /* Inform the VRDP server about the change of display parameters. */
+ LogRelFlowFunc(("Calling VRDP\n"));
+ mParent->i_consoleVRDPServer()->SendResize();
+
+ /* And re-send the seamless rectangles if necessary. */
+ if (mfSeamlessEnabled)
+ i_handleSetVisibleRegion(mcRectVisibleRegion, mpRectVisibleRegion);
+
+#ifdef VBOX_WITH_RECORDING
+ i_recordingScreenChanged(uScreenId);
+#endif
+
+ LogRelFlowFunc(("[%d]: default format %d\n", uScreenId, pFBInfo->fDefaultFormat));
+
+ return VINF_SUCCESS;
+}
+
+static void i_checkCoordBounds(int *px, int *py, int *pw, int *ph, int cx, int cy)
+{
+ /* Correct negative x and y coordinates. */
+ if (*px < 0)
+ {
+ *px += *pw; /* Compute xRight which is also the new width. */
+
+ *pw = (*px < 0)? 0: *px;
+
+ *px = 0;
+ }
+
+ if (*py < 0)
+ {
+ *py += *ph; /* Compute xBottom, which is also the new height. */
+
+ *ph = (*py < 0)? 0: *py;
+
+ *py = 0;
+ }
+
+ /* Also check if coords are greater than the display resolution. */
+ if (*px + *pw > cx)
+ {
+ *pw = cx > *px? cx - *px: 0;
+ }
+
+ if (*py + *ph > cy)
+ {
+ *ph = cy > *py? cy - *py: 0;
+ }
+}
+
+void Display::i_handleDisplayUpdate(unsigned uScreenId, int x, int y, int w, int h)
+{
+ /*
+ * Always runs under either VBVA lock or, for HGSMI, DevVGA lock.
+ * Safe to use VBVA vars and take the framebuffer lock.
+ */
+
+#ifdef DEBUG_sunlover
+ LogFlowFunc(("[%d] %d,%d %dx%d\n",
+ uScreenId, x, y, w, h));
+#endif /* DEBUG_sunlover */
+
+ /* No updates for a disabled guest screen. */
+ if (maFramebuffers[uScreenId].fDisabled)
+ return;
+
+ /* No updates for a blank guest screen. */
+ /** @note Disabled for now, as the GUI does not update the picture when we
+ * first blank. */
+ /* if (maFramebuffers[uScreenId].flags & VBVA_SCREEN_F_BLANK)
+ return; */
+
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
+ AutoReadLock alockr(this COMMA_LOCKVAL_SRC_POS);
+
+ ComPtr<IFramebuffer> pFramebuffer = pFBInfo->pFramebuffer;
+ ComPtr<IDisplaySourceBitmap> pSourceBitmap = pFBInfo->updateImage.pSourceBitmap;
+
+ alockr.release();
+
+ if (RT_LIKELY(!pFramebuffer.isNull()))
+ {
+ if (RT_LIKELY(!RT_BOOL(pFBInfo->u32Caps & FramebufferCapabilities_UpdateImage)))
+ {
+ i_checkCoordBounds(&x, &y, &w, &h, pFBInfo->w, pFBInfo->h);
+
+ if (w != 0 && h != 0)
+ {
+ pFramebuffer->NotifyUpdate(x, y, w, h);
+ }
+ }
+ else
+ {
+ if (RT_LIKELY(!pSourceBitmap.isNull()))
+ { /* likely */ }
+ else
+ {
+ /* Create a source bitmap if UpdateImage mode is used. */
+ HRESULT hr = QuerySourceBitmap(uScreenId, pSourceBitmap.asOutParam());
+ if (SUCCEEDED(hr))
+ {
+ BYTE *pAddress = NULL;
+ ULONG ulWidth = 0;
+ ULONG ulHeight = 0;
+ ULONG ulBitsPerPixel = 0;
+ ULONG ulBytesPerLine = 0;
+ BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
+
+ hr = pSourceBitmap->QueryBitmapInfo(&pAddress,
+ &ulWidth,
+ &ulHeight,
+ &ulBitsPerPixel,
+ &ulBytesPerLine,
+ &bitmapFormat);
+ if (SUCCEEDED(hr))
+ {
+ AutoWriteLock alockw(this COMMA_LOCKVAL_SRC_POS);
+
+ if (pFBInfo->updateImage.pSourceBitmap.isNull())
+ {
+ pFBInfo->updateImage.pSourceBitmap = pSourceBitmap;
+ pFBInfo->updateImage.pu8Address = pAddress;
+ pFBInfo->updateImage.cbLine = ulBytesPerLine;
+ }
+
+ pSourceBitmap = pFBInfo->updateImage.pSourceBitmap;
+
+ alockw.release();
+ }
+ }
+ }
+
+ if (RT_LIKELY(!pSourceBitmap.isNull()))
+ {
+ BYTE *pbAddress = NULL;
+ ULONG ulWidth = 0;
+ ULONG ulHeight = 0;
+ ULONG ulBitsPerPixel = 0;
+ ULONG ulBytesPerLine = 0;
+ BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
+
+ HRESULT hr = pSourceBitmap->QueryBitmapInfo(&pbAddress,
+ &ulWidth,
+ &ulHeight,
+ &ulBitsPerPixel,
+ &ulBytesPerLine,
+ &bitmapFormat);
+ if (SUCCEEDED(hr))
+ {
+ /* Make sure that the requested update is within the source bitmap dimensions. */
+ i_checkCoordBounds(&x, &y, &w, &h, ulWidth, ulHeight);
+
+ if (w != 0 && h != 0)
+ {
+ const size_t cbData = w * h * 4;
+ com::SafeArray<BYTE> image(cbData);
+
+ uint8_t *pu8Dst = image.raw();
+ const uint8_t *pu8Src = pbAddress + ulBytesPerLine * y + x * 4;
+
+ int i;
+ for (i = y; i < y + h; ++i)
+ {
+ memcpy(pu8Dst, pu8Src, w * 4);
+ pu8Dst += w * 4;
+ pu8Src += ulBytesPerLine;
+ }
+
+ pFramebuffer->NotifyUpdateImage(x, y, w, h, ComSafeArrayAsInParam(image));
+ }
+ }
+ }
+ }
+ }
+
+#ifndef VBOX_WITH_HGSMI
+ if (!mVideoAccelLegacy.fVideoAccelEnabled)
+#else
+ if (!mVideoAccelLegacy.fVideoAccelEnabled && !maFramebuffers[uScreenId].fVBVAEnabled)
+#endif
+ {
+ /* When VBVA is enabled, the VRDP server is informed
+ * either in VideoAccelFlush or displayVBVAUpdateProcess.
+ * Inform the server here only if VBVA is disabled.
+ */
+ mParent->i_consoleVRDPServer()->SendUpdateBitmap(uScreenId, x, y, w, h);
+ }
+}
+
+void Display::i_updateGuestGraphicsFacility(void)
+{
+ Guest* pGuest = mParent->i_getGuest();
+ AssertPtrReturnVoid(pGuest);
+ /* The following is from GuestImpl.cpp. */
+ /** @todo A nit: The timestamp is wrong on saved state restore. Would be better
+ * to move the graphics and seamless capability -> facility translation to
+ * VMMDev so this could be saved. */
+ RTTIMESPEC TimeSpecTS;
+ RTTimeNow(&TimeSpecTS);
+
+ if ( mfVMMDevSupportsGraphics
+ || (mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS) != 0)
+ pGuest->i_setAdditionsStatus(VBoxGuestFacilityType_Graphics,
+ VBoxGuestFacilityStatus_Active,
+ 0 /*fFlags*/, &TimeSpecTS);
+ else
+ pGuest->i_setAdditionsStatus(VBoxGuestFacilityType_Graphics,
+ VBoxGuestFacilityStatus_Inactive,
+ 0 /*fFlags*/, &TimeSpecTS);
+}
+
+void Display::i_handleUpdateVMMDevSupportsGraphics(bool fSupportsGraphics)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (mfVMMDevSupportsGraphics == fSupportsGraphics)
+ return;
+ mfVMMDevSupportsGraphics = fSupportsGraphics;
+ i_updateGuestGraphicsFacility();
+ /* The VMMDev interface notifies the console. */
+}
+
+void Display::i_handleUpdateGuestVBVACapabilities(uint32_t fNewCapabilities)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ bool fNotify = (fNewCapabilities & VBVACAPS_VIDEO_MODE_HINTS) != (mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS);
+
+ mfGuestVBVACapabilities = fNewCapabilities;
+ if (!fNotify)
+ return;
+ i_updateGuestGraphicsFacility();
+ /* Tell the console about it */
+ mParent->i_onAdditionsStateChange();
+}
+
+void Display::i_handleUpdateVBVAInputMapping(int32_t xOrigin, int32_t yOrigin, uint32_t cx, uint32_t cy)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ xInputMappingOrigin = xOrigin;
+ yInputMappingOrigin = yOrigin;
+ cxInputMapping = cx;
+ cyInputMapping = cy;
+
+ /* Re-send the seamless rectangles if necessary. */
+ if (mfSeamlessEnabled)
+ i_handleSetVisibleRegion(mcRectVisibleRegion, mpRectVisibleRegion);
+}
+
+/**
+ * Returns the upper left and lower right corners of the virtual framebuffer.
+ * The lower right is "exclusive" (i.e. first pixel beyond the framebuffer),
+ * and the origin is (0, 0), not (1, 1) like the GUI returns.
+ */
+void Display::i_getFramebufferDimensions(int32_t *px1, int32_t *py1,
+ int32_t *px2, int32_t *py2)
+{
+ int32_t x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ AssertPtrReturnVoid(px1);
+ AssertPtrReturnVoid(py1);
+ AssertPtrReturnVoid(px2);
+ AssertPtrReturnVoid(py2);
+ LogRelFlowFunc(("\n"));
+
+ if (!mpDrv)
+ return;
+
+ if (maFramebuffers[0].fVBVAEnabled && cxInputMapping && cyInputMapping)
+ {
+ /* Guest uses VBVA with explicit mouse mapping dimensions. */
+ x1 = xInputMappingOrigin;
+ y1 = yInputMappingOrigin;
+ x2 = xInputMappingOrigin + cxInputMapping;
+ y2 = yInputMappingOrigin + cyInputMapping;
+ }
+ else
+ {
+ /* If VBVA is not in use then this flag will not be set and this
+ * will still work as it should. */
+ if (!maFramebuffers[0].fDisabled)
+ {
+ x1 = (int32_t)maFramebuffers[0].xOrigin;
+ y1 = (int32_t)maFramebuffers[0].yOrigin;
+ x2 = (int32_t)maFramebuffers[0].w + (int32_t)maFramebuffers[0].xOrigin;
+ y2 = (int32_t)maFramebuffers[0].h + (int32_t)maFramebuffers[0].yOrigin;
+ }
+
+ for (unsigned i = 1; i < mcMonitors; ++i)
+ {
+ if (!maFramebuffers[i].fDisabled)
+ {
+ x1 = RT_MIN(x1, maFramebuffers[i].xOrigin);
+ y1 = RT_MIN(y1, maFramebuffers[i].yOrigin);
+ x2 = RT_MAX(x2, maFramebuffers[i].xOrigin + (int32_t)maFramebuffers[i].w);
+ y2 = RT_MAX(y2, maFramebuffers[i].yOrigin + (int32_t)maFramebuffers[i].h);
+ }
+ }
+ }
+
+ *px1 = x1;
+ *py1 = y1;
+ *px2 = x2;
+ *py2 = y2;
+}
+
+/** Updates the device's view of the host cursor handling capabilities.
+ * Calls into mpDrv->pUpPort. */
+void Display::i_UpdateDeviceCursorCapabilities(void)
+{
+ bool fRenderCursor = true;
+ bool fMoveCursor = mcVRDPRefs == 0;
+#ifdef VBOX_WITH_RECORDING
+ RecordingContext *pCtx = mParent->i_recordingGetContext();
+
+ if ( pCtx
+ && pCtx->IsStarted()
+ && pCtx->IsFeatureEnabled(RecordingFeature_Video))
+ fRenderCursor = fMoveCursor = false;
+ else
+#endif /* VBOX_WITH_RECORDING */
+ {
+ for (unsigned uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
+ {
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
+ if (!(pFBInfo->u32Caps & FramebufferCapabilities_RenderCursor))
+ fRenderCursor = false;
+ if (!(pFBInfo->u32Caps & FramebufferCapabilities_MoveCursor))
+ fMoveCursor = false;
+ }
+ }
+
+ if (mpDrv)
+ mpDrv->pUpPort->pfnReportHostCursorCapabilities(mpDrv->pUpPort, fRenderCursor, fMoveCursor);
+}
+
+HRESULT Display::i_reportHostCursorCapabilities(uint32_t fCapabilitiesAdded, uint32_t fCapabilitiesRemoved)
+{
+ /* Do we need this to access mParent? I presume that the safe VM pointer
+ * ensures that mpDrv will remain valid. */
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ uint32_t fHostCursorCapabilities = (mfHostCursorCapabilities | fCapabilitiesAdded)
+ & ~fCapabilitiesRemoved;
+
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
+ return ptrVM.hrc();
+ if (mfHostCursorCapabilities == fHostCursorCapabilities)
+ return S_OK;
+ CHECK_CONSOLE_DRV(mpDrv);
+ alock.release(); /* Release before calling up for lock order reasons. */
+ mfHostCursorCapabilities = fHostCursorCapabilities;
+ i_UpdateDeviceCursorCapabilities();
+ return S_OK;
+}
+
+HRESULT Display::i_reportHostCursorPosition(int32_t x, int32_t y, bool fOutOfRange)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ uint32_t xAdj = (uint32_t)RT_MAX(x - xInputMappingOrigin, 0);
+ uint32_t yAdj = (uint32_t)RT_MAX(y - yInputMappingOrigin, 0);
+ xAdj = RT_MIN(xAdj, cxInputMapping);
+ yAdj = RT_MIN(yAdj, cyInputMapping);
+
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
+ return ptrVM.hrc();
+ CHECK_CONSOLE_DRV(mpDrv);
+ alock.release(); /* Release before calling up for lock order reasons. */
+ if (fOutOfRange)
+ mpDrv->pUpPort->pfnReportHostCursorPosition(mpDrv->pUpPort, 0, 0, true);
+ else
+ mpDrv->pUpPort->pfnReportHostCursorPosition(mpDrv->pUpPort, xAdj, yAdj, false);
+ return S_OK;
+}
+
+static bool displayIntersectRect(RTRECT *prectResult,
+ const RTRECT *prect1,
+ const RTRECT *prect2)
+{
+ /* Initialize result to an empty record. */
+ memset(prectResult, 0, sizeof(RTRECT));
+
+ int xLeftResult = RT_MAX(prect1->xLeft, prect2->xLeft);
+ int xRightResult = RT_MIN(prect1->xRight, prect2->xRight);
+
+ if (xLeftResult < xRightResult)
+ {
+ /* There is intersection by X. */
+
+ int yTopResult = RT_MAX(prect1->yTop, prect2->yTop);
+ int yBottomResult = RT_MIN(prect1->yBottom, prect2->yBottom);
+
+ if (yTopResult < yBottomResult)
+ {
+ /* There is intersection by Y. */
+
+ prectResult->xLeft = xLeftResult;
+ prectResult->yTop = yTopResult;
+ prectResult->xRight = xRightResult;
+ prectResult->yBottom = yBottomResult;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int Display::i_saveVisibleRegion(uint32_t cRect, PRTRECT pRect)
+{
+ RTRECT *pRectVisibleRegion = NULL;
+
+ if (pRect == mpRectVisibleRegion)
+ return VINF_SUCCESS;
+ if (cRect != 0)
+ {
+ pRectVisibleRegion = (RTRECT *)RTMemAlloc(cRect * sizeof(RTRECT));
+ if (!pRectVisibleRegion)
+ {
+ return VERR_NO_MEMORY;
+ }
+ memcpy(pRectVisibleRegion, pRect, cRect * sizeof(RTRECT));
+ }
+ if (mpRectVisibleRegion)
+ RTMemFree(mpRectVisibleRegion);
+ mcRectVisibleRegion = cRect;
+ mpRectVisibleRegion = pRectVisibleRegion;
+ return VINF_SUCCESS;
+}
+
+int Display::i_handleSetVisibleRegion(uint32_t cRect, PRTRECT pRect)
+{
+ RTRECT *pVisibleRegion = (RTRECT *)RTMemTmpAlloc( RT_MAX(cRect, 1)
+ * sizeof(RTRECT));
+ LogRel2(("%s: cRect=%u\n", __PRETTY_FUNCTION__, cRect));
+ if (!pVisibleRegion)
+ {
+ return VERR_NO_TMP_MEMORY;
+ }
+ int vrc = i_saveVisibleRegion(cRect, pRect);
+ if (RT_FAILURE(vrc))
+ {
+ RTMemTmpFree(pVisibleRegion);
+ return vrc;
+ }
+
+ unsigned uScreenId;
+ for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
+ {
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
+
+ if ( !pFBInfo->pFramebuffer.isNull()
+ && RT_BOOL(pFBInfo->u32Caps & FramebufferCapabilities_VisibleRegion))
+ {
+ /* Prepare a new array of rectangles which intersect with the framebuffer.
+ */
+ RTRECT rectFramebuffer;
+ rectFramebuffer.xLeft = pFBInfo->xOrigin - xInputMappingOrigin;
+ rectFramebuffer.yTop = pFBInfo->yOrigin - yInputMappingOrigin;
+ rectFramebuffer.xRight = rectFramebuffer.xLeft + pFBInfo->w;
+ rectFramebuffer.yBottom = rectFramebuffer.yTop + pFBInfo->h;
+
+ uint32_t cRectVisibleRegion = 0;
+
+ uint32_t i;
+ for (i = 0; i < cRect; i++)
+ {
+ if (displayIntersectRect(&pVisibleRegion[cRectVisibleRegion], &pRect[i], &rectFramebuffer))
+ {
+ pVisibleRegion[cRectVisibleRegion].xLeft -= rectFramebuffer.xLeft;
+ pVisibleRegion[cRectVisibleRegion].yTop -= rectFramebuffer.yTop;
+ pVisibleRegion[cRectVisibleRegion].xRight -= rectFramebuffer.xLeft;
+ pVisibleRegion[cRectVisibleRegion].yBottom -= rectFramebuffer.yTop;
+
+ cRectVisibleRegion++;
+ }
+ }
+ pFBInfo->pFramebuffer->SetVisibleRegion((BYTE *)pVisibleRegion, cRectVisibleRegion);
+ }
+ }
+
+ RTMemTmpFree(pVisibleRegion);
+
+ return VINF_SUCCESS;
+}
+
+int Display::i_handleUpdateMonitorPositions(uint32_t cPositions, PCRTPOINT paPositions)
+{
+ AssertMsgReturn(paPositions, ("Empty monitor position array\n"), E_INVALIDARG);
+ for (unsigned i = 0; i < cPositions; ++i)
+ LogRel2(("Display::i_handleUpdateMonitorPositions: uScreenId=%d xOrigin=%d yOrigin=%dX\n",
+ i, paPositions[i].x, paPositions[i].y));
+
+ if (mpDrv && mpDrv->pUpPort->pfnReportMonitorPositions)
+ mpDrv->pUpPort->pfnReportMonitorPositions(mpDrv->pUpPort, cPositions, paPositions);
+ return VINF_SUCCESS;
+}
+
+int Display::i_handleQueryVisibleRegion(uint32_t *pcRects, PRTRECT paRects)
+{
+ /// @todo Currently not used by the guest and is not implemented in
+ /// framebuffers. Remove?
+ RT_NOREF(pcRects, paRects);
+ return VERR_NOT_SUPPORTED;
+}
+
+#ifdef VBOX_WITH_HGSMI
+static void vbvaSetMemoryFlagsHGSMI(unsigned uScreenId,
+ uint32_t fu32SupportedOrders,
+ bool fVideoAccelVRDP,
+ DISPLAYFBINFO *pFBInfo)
+{
+ LogRelFlowFunc(("HGSMI[%d]: %p\n", uScreenId, pFBInfo->pVBVAHostFlags));
+
+ if (pFBInfo->pVBVAHostFlags)
+ {
+ uint32_t fu32HostEvents = VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
+
+ if (pFBInfo->fVBVAEnabled)
+ {
+ fu32HostEvents |= VBVA_F_MODE_ENABLED;
+
+ if (fVideoAccelVRDP)
+ {
+ fu32HostEvents |= VBVA_F_MODE_VRDP;
+ }
+ }
+
+ ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32HostEvents, fu32HostEvents);
+ ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32SupportedOrders, fu32SupportedOrders);
+
+ LogRelFlowFunc((" fu32HostEvents = 0x%08X, fu32SupportedOrders = 0x%08X\n", fu32HostEvents, fu32SupportedOrders));
+ }
+}
+
+static void vbvaSetMemoryFlagsAllHGSMI(uint32_t fu32SupportedOrders,
+ bool fVideoAccelVRDP,
+ DISPLAYFBINFO *paFBInfos,
+ unsigned cFBInfos)
+{
+ unsigned uScreenId;
+
+ for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
+ {
+ vbvaSetMemoryFlagsHGSMI(uScreenId, fu32SupportedOrders, fVideoAccelVRDP, &paFBInfos[uScreenId]);
+ }
+}
+#endif /* VBOX_WITH_HGSMI */
+
+int Display::VideoAccelEnableVMMDev(bool fEnable, VBVAMEMORY *pVbvaMemory)
+{
+ LogFlowFunc(("%d %p\n", fEnable, pVbvaMemory));
+ int vrc = videoAccelEnterVMMDev(&mVideoAccelLegacy);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = i_VideoAccelEnable(fEnable, pVbvaMemory, mpDrv->pUpPort);
+ videoAccelLeaveVMMDev(&mVideoAccelLegacy);
+ }
+ LogFlowFunc(("leave %Rrc\n", vrc));
+ return vrc;
+}
+
+int Display::VideoAccelEnableVGA(bool fEnable, VBVAMEMORY *pVbvaMemory)
+{
+ LogFlowFunc(("%d %p\n", fEnable, pVbvaMemory));
+ int vrc = videoAccelEnterVGA(&mVideoAccelLegacy);
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = i_VideoAccelEnable(fEnable, pVbvaMemory, mpDrv->pUpPort);
+ videoAccelLeaveVGA(&mVideoAccelLegacy);
+ }
+ LogFlowFunc(("leave %Rrc\n", vrc));
+ return vrc;
+}
+
+void Display::VideoAccelFlushVMMDev(void)
+{
+ LogFlowFunc(("enter\n"));
+ int vrc = videoAccelEnterVMMDev(&mVideoAccelLegacy);
+ if (RT_SUCCESS(vrc))
+ {
+ i_VideoAccelFlush(mpDrv->pUpPort);
+ videoAccelLeaveVMMDev(&mVideoAccelLegacy);
+ }
+ LogFlowFunc(("leave\n"));
+}
+
+/* Called always by one VRDP server thread. Can be thread-unsafe.
+ */
+void Display::i_VRDPConnectionEvent(bool fConnect)
+{
+ LogRelFlowFunc(("fConnect = %d\n", fConnect));
+
+ int c = fConnect?
+ ASMAtomicIncS32(&mcVRDPRefs):
+ ASMAtomicDecS32(&mcVRDPRefs);
+
+ i_VideoAccelVRDP(fConnect, c);
+ i_UpdateDeviceCursorCapabilities();
+}
+
+
+void Display::i_VideoAccelVRDP(bool fEnable, int c)
+{
+ VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
+
+ Assert (c >= 0);
+ RT_NOREF(fEnable);
+
+ /* This can run concurrently with Display videoaccel state change. */
+ RTCritSectEnter(&mVideoAccelLock);
+
+ if (c == 0)
+ {
+ /* The last client has disconnected, and the accel can be
+ * disabled.
+ */
+ Assert(fEnable == false);
+
+ mfVideoAccelVRDP = false;
+ mfu32SupportedOrders = 0;
+
+ i_vbvaSetMemoryFlags(pVideoAccel->pVbvaMemory, pVideoAccel->fVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders,
+ maFramebuffers, mcMonitors);
+#ifdef VBOX_WITH_HGSMI
+ /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
+ ASMAtomicIncU32(&mu32UpdateVBVAFlags);
+#endif /* VBOX_WITH_HGSMI */
+
+ LogRel(("VBVA: VRDP acceleration has been disabled.\n"));
+ }
+ else if ( c == 1
+ && !mfVideoAccelVRDP)
+ {
+ /* The first client has connected. Enable the accel.
+ */
+ Assert(fEnable == true);
+
+ mfVideoAccelVRDP = true;
+ /* Supporting all orders. */
+ mfu32SupportedOrders = UINT32_MAX;
+
+ i_vbvaSetMemoryFlags(pVideoAccel->pVbvaMemory, pVideoAccel->fVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders,
+ maFramebuffers, mcMonitors);
+#ifdef VBOX_WITH_HGSMI
+ /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
+ ASMAtomicIncU32(&mu32UpdateVBVAFlags);
+#endif /* VBOX_WITH_HGSMI */
+
+ LogRel(("VBVA: VRDP acceleration has been requested.\n"));
+ }
+ else
+ {
+ /* A client is connected or disconnected but there is no change in the
+ * accel state. It remains enabled.
+ */
+ Assert(mfVideoAccelVRDP == true);
+ }
+
+ RTCritSectLeave(&mVideoAccelLock);
+}
+
+void Display::i_notifyPowerDown(void)
+{
+ LogRelFlowFunc(("\n"));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ /* Source bitmaps are not available anymore. */
+ mfSourceBitmapEnabled = false;
+
+ alock.release();
+
+ /* Resize all displays to tell framebuffers to forget current source bitmap. */
+ unsigned uScreenId = mcMonitors;
+ while (uScreenId > 0)
+ {
+ --uScreenId;
+
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
+ if (!pFBInfo->fDisabled)
+ {
+ i_handleDisplayResize(uScreenId, 32,
+ pFBInfo->pu8FramebufferVRAM,
+ pFBInfo->u32LineSize,
+ pFBInfo->w,
+ pFBInfo->h,
+ pFBInfo->flags,
+ pFBInfo->xOrigin,
+ pFBInfo->yOrigin,
+ false);
+ }
+ }
+}
+
+// Wrapped IDisplay methods
+/////////////////////////////////////////////////////////////////////////////
+HRESULT Display::getScreenResolution(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ULONG *aBitsPerPixel,
+ LONG *aXOrigin, LONG *aYOrigin, GuestMonitorStatus_T *aGuestMonitorStatus)
+{
+ LogRelFlowFunc(("aScreenId=%RU32\n", aScreenId));
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (aScreenId >= mcMonitors)
+ return E_INVALIDARG;
+
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
+
+ GuestMonitorStatus_T guestMonitorStatus = GuestMonitorStatus_Enabled;
+
+ if (pFBInfo->flags & VBVA_SCREEN_F_DISABLED)
+ guestMonitorStatus = GuestMonitorStatus_Disabled;
+ else if (pFBInfo->flags & (VBVA_SCREEN_F_BLANK | VBVA_SCREEN_F_BLANK2))
+ guestMonitorStatus = GuestMonitorStatus_Blank;
+
+ if (aWidth)
+ *aWidth = pFBInfo->w;
+ if (aHeight)
+ *aHeight = pFBInfo->h;
+ if (aBitsPerPixel)
+ *aBitsPerPixel = pFBInfo->u16BitsPerPixel;
+ if (aXOrigin)
+ *aXOrigin = pFBInfo->xOrigin;
+ if (aYOrigin)
+ *aYOrigin = pFBInfo->yOrigin;
+ if (aGuestMonitorStatus)
+ *aGuestMonitorStatus = guestMonitorStatus;
+
+ return S_OK;
+}
+
+
+HRESULT Display::attachFramebuffer(ULONG aScreenId, const ComPtr<IFramebuffer> &aFramebuffer, com::Guid &aId)
+{
+ LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (aScreenId >= mcMonitors)
+ return setError(E_INVALIDARG, tr("AttachFramebuffer: Invalid screen %d (total %d)"),
+ aScreenId, mcMonitors);
+
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
+ if (!pFBInfo->pFramebuffer.isNull())
+ return setError(E_FAIL, tr("AttachFramebuffer: Framebuffer already attached to %d"),
+ aScreenId);
+
+ pFBInfo->pFramebuffer = aFramebuffer;
+ pFBInfo->framebufferId.create();
+ aId = pFBInfo->framebufferId;
+
+ SafeArray<FramebufferCapabilities_T> caps;
+ pFBInfo->pFramebuffer->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(caps));
+ pFBInfo->u32Caps = 0;
+ size_t i;
+ for (i = 0; i < caps.size(); ++i)
+ pFBInfo->u32Caps |= caps[i];
+
+ alock.release();
+
+ /* The driver might not have been constructed yet */
+ if (mpDrv)
+ {
+ /* Inform the framebuffer about the actual screen size. */
+ HRESULT hr = aFramebuffer->NotifyChange(aScreenId, 0, 0, pFBInfo->w, pFBInfo->h); /** @todo origin */
+ LogFunc(("NotifyChange hr %08X\n", hr)); NOREF(hr);
+
+ /* Re-send the seamless rectangles if necessary. */
+ if (mfSeamlessEnabled)
+ i_handleSetVisibleRegion(mcRectVisibleRegion, mpRectVisibleRegion);
+ }
+
+ Console::SafeVMPtrQuiet ptrVM(mParent);
+ if (ptrVM.isOk())
+ ptrVM.vtable()->pfnVMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
+ 3, this, aScreenId, false);
+
+ LogRelFlowFunc(("Attached to %d %RTuuid\n", aScreenId, aId.raw()));
+ return S_OK;
+}
+
+HRESULT Display::detachFramebuffer(ULONG aScreenId, const com::Guid &aId)
+{
+ LogRelFlowFunc(("aScreenId = %d %RTuuid\n", aScreenId, aId.raw()));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (aScreenId >= mcMonitors)
+ return setError(E_INVALIDARG, tr("DetachFramebuffer: Invalid screen %d (total %d)"),
+ aScreenId, mcMonitors);
+
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
+
+ if (pFBInfo->framebufferId != aId)
+ {
+ LogRelFlowFunc(("Invalid framebuffer aScreenId = %d, attached %p\n", aScreenId, pFBInfo->framebufferId.raw()));
+ return setError(E_FAIL, tr("DetachFramebuffer: Invalid framebuffer object"));
+ }
+
+ pFBInfo->pFramebuffer.setNull();
+ pFBInfo->framebufferId.clear();
+
+ alock.release();
+ return S_OK;
+}
+
+HRESULT Display::queryFramebuffer(ULONG aScreenId, ComPtr<IFramebuffer> &aFramebuffer)
+{
+ LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (aScreenId >= mcMonitors)
+ return setError(E_INVALIDARG, tr("QueryFramebuffer: Invalid screen %d (total %d)"),
+ aScreenId, mcMonitors);
+
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
+
+ pFBInfo->pFramebuffer.queryInterfaceTo(aFramebuffer.asOutParam());
+
+ return S_OK;
+}
+
+HRESULT Display::setVideoModeHint(ULONG aDisplay, BOOL aEnabled,
+ BOOL aChangeOrigin, LONG aOriginX, LONG aOriginY,
+ ULONG aWidth, ULONG aHeight, ULONG aBitsPerPixel,
+ BOOL aNotify)
+{
+ if (aWidth == 0 || aHeight == 0 || aBitsPerPixel == 0)
+ {
+ /* Some of parameters must not change. Query current mode. */
+ ULONG ulWidth = 0;
+ ULONG ulHeight = 0;
+ ULONG ulBitsPerPixel = 0;
+ HRESULT hr = getScreenResolution(aDisplay, &ulWidth, &ulHeight, &ulBitsPerPixel, NULL, NULL, NULL);
+ if (FAILED(hr))
+ return hr;
+
+ /* Assign current values to not changing parameters. */
+ if (aWidth == 0)
+ aWidth = ulWidth;
+ if (aHeight == 0)
+ aHeight = ulHeight;
+ if (aBitsPerPixel == 0)
+ aBitsPerPixel = ulBitsPerPixel;
+ }
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (aDisplay >= mcMonitors)
+ return E_INVALIDARG;
+
+ VMMDevDisplayDef d;
+ d.idDisplay = aDisplay;
+ d.xOrigin = aOriginX;
+ d.yOrigin = aOriginY;
+ d.cx = aWidth;
+ d.cy = aHeight;
+ d.cBitsPerPixel = aBitsPerPixel;
+ d.fDisplayFlags = VMMDEV_DISPLAY_CX | VMMDEV_DISPLAY_CY | VMMDEV_DISPLAY_BPP;
+ if (!aEnabled)
+ d.fDisplayFlags |= VMMDEV_DISPLAY_DISABLED;
+ if (aChangeOrigin)
+ d.fDisplayFlags |= VMMDEV_DISPLAY_ORIGIN;
+ if (aDisplay == 0)
+ d.fDisplayFlags |= VMMDEV_DISPLAY_PRIMARY;
+
+ /* Remember the monitor information. */
+ maFramebuffers[aDisplay].monitorDesc = d;
+
+ CHECK_CONSOLE_DRV(mpDrv);
+
+ /*
+ * It is up to the guest to decide whether the hint is
+ * valid. Therefore don't do any VRAM sanity checks here.
+ */
+
+ /* Have to release the lock because the pfnRequestDisplayChange
+ * will call EMT. */
+ alock.release();
+
+ /* We always send the hint to the graphics card in case the guest enables
+ * support later. For now we notify exactly when support is enabled. */
+ mpDrv->pUpPort->pfnSendModeHint(mpDrv->pUpPort, aWidth, aHeight,
+ aBitsPerPixel, aDisplay,
+ aChangeOrigin ? aOriginX : ~0,
+ aChangeOrigin ? aOriginY : ~0,
+ RT_BOOL(aEnabled),
+ (mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS)
+ && aNotify);
+ if ( mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS
+ && !(mfGuestVBVACapabilities & VBVACAPS_IRQ)
+ && aNotify)
+ mParent->i_sendACPIMonitorHotPlugEvent();
+
+ /* We currently never suppress the VMMDev hint if the guest has requested
+ * it. Specifically the video graphics driver may not be responsible for
+ * screen positioning in the guest virtual desktop, and the component
+ * responsible may want to get the hint from VMMDev. */
+ VMMDev *pVMMDev = mParent->i_getVMMDev();
+ if (pVMMDev)
+ {
+ PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
+ if (pVMMDevPort)
+ pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, 1, &d, false, RT_BOOL(aNotify));
+ }
+ /* Notify listeners. */
+ ::FireGuestMonitorInfoChangedEvent(mParent->i_getEventSource(), aDisplay);
+ return S_OK;
+}
+
+HRESULT Display::getVideoModeHint(ULONG cDisplay, BOOL *pfEnabled,
+ BOOL *pfChangeOrigin, LONG *pxOrigin, LONG *pyOrigin,
+ ULONG *pcx, ULONG *pcy, ULONG *pcBitsPerPixel)
+{
+ if (cDisplay >= mcMonitors)
+ return E_INVALIDARG;
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (pfEnabled)
+ *pfEnabled = !( maFramebuffers[cDisplay].monitorDesc.fDisplayFlags
+ & VMMDEV_DISPLAY_DISABLED);
+ if (pfChangeOrigin)
+ *pfChangeOrigin = RT_BOOL( maFramebuffers[cDisplay].monitorDesc.fDisplayFlags
+ & VMMDEV_DISPLAY_ORIGIN);
+ if (pxOrigin)
+ *pxOrigin = maFramebuffers[cDisplay].monitorDesc.xOrigin;
+ if (pyOrigin)
+ *pyOrigin = maFramebuffers[cDisplay].monitorDesc.yOrigin;
+ if (pcx)
+ *pcx = maFramebuffers[cDisplay].monitorDesc.cx;
+ if (pcy)
+ *pcy = maFramebuffers[cDisplay].monitorDesc.cy;
+ if (pcBitsPerPixel)
+ *pcBitsPerPixel = maFramebuffers[cDisplay].monitorDesc.cBitsPerPixel;
+ return S_OK;
+}
+
+HRESULT Display::setSeamlessMode(BOOL enabled)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ /* Have to release the lock because the pfnRequestSeamlessChange will call EMT. */
+ alock.release();
+
+ VMMDev *pVMMDev = mParent->i_getVMMDev();
+ if (pVMMDev)
+ {
+ PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
+ if (pVMMDevPort)
+ pVMMDevPort->pfnRequestSeamlessChange(pVMMDevPort, !!enabled);
+ }
+ mfSeamlessEnabled = RT_BOOL(enabled);
+ return S_OK;
+}
+
+/*static*/ DECLCALLBACK(int)
+Display::i_displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_t **ppbData, size_t *pcbData,
+ uint32_t *pcx, uint32_t *pcy, bool *pfMemFree)
+{
+ int vrc;
+ if ( aScreenId == VBOX_VIDEO_PRIMARY_SCREEN
+ && pDisplay->maFramebuffers[aScreenId].fVBVAEnabled == false) /* A non-VBVA mode. */
+ {
+ if (pDisplay->mpDrv)
+ {
+ vrc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppbData, pcbData, pcx, pcy);
+ *pfMemFree = false;
+ }
+ else
+ {
+ /* No image. */
+ *ppbData = NULL;
+ *pcbData = 0;
+ *pcx = 0;
+ *pcy = 0;
+ *pfMemFree = true;
+ vrc = VINF_SUCCESS;
+ }
+ }
+ else if (aScreenId < pDisplay->mcMonitors)
+ {
+ DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
+
+ uint32_t width = pFBInfo->w;
+ uint32_t height = pFBInfo->h;
+
+ /* Allocate 32 bit per pixel bitmap. */
+ size_t cbRequired = width * 4 * height;
+
+ if (cbRequired)
+ {
+ uint8_t *pbDst = (uint8_t *)RTMemAlloc(cbRequired);
+ if (pbDst != NULL)
+ {
+ if (pFBInfo->flags & VBVA_SCREEN_F_ACTIVE)
+ {
+ /* Copy guest VRAM to the allocated 32bpp buffer. */
+ const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
+ int32_t xSrc = 0;
+ int32_t ySrc = 0;
+ uint32_t u32SrcWidth = width;
+ uint32_t u32SrcHeight = height;
+ uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
+ uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
+
+ int32_t xDst = 0;
+ int32_t yDst = 0;
+ uint32_t u32DstWidth = u32SrcWidth;
+ uint32_t u32DstHeight = u32SrcHeight;
+ uint32_t u32DstLineSize = u32DstWidth * 4;
+ uint32_t u32DstBitsPerPixel = 32;
+
+ vrc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
+ width, height,
+ pu8Src,
+ xSrc, ySrc,
+ u32SrcWidth, u32SrcHeight,
+ u32SrcLineSize, u32SrcBitsPerPixel,
+ pbDst,
+ xDst, yDst,
+ u32DstWidth, u32DstHeight,
+ u32DstLineSize, u32DstBitsPerPixel);
+ }
+ else
+ {
+ memset(pbDst, 0, cbRequired);
+ vrc = VINF_SUCCESS;
+ }
+ if (RT_SUCCESS(vrc))
+ {
+ *ppbData = pbDst;
+ *pcbData = cbRequired;
+ *pcx = width;
+ *pcy = height;
+ *pfMemFree = true;
+ }
+ else
+ {
+ RTMemFree(pbDst);
+
+ /* CopyRect can fail if VBVA was paused in VGA device, retry using the generic method. */
+ if ( vrc == VERR_INVALID_STATE
+ && aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
+ {
+ vrc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppbData, pcbData, pcx, pcy);
+ *pfMemFree = false;
+ }
+ }
+ }
+ else
+ vrc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ /* No image. */
+ *ppbData = NULL;
+ *pcbData = 0;
+ *pcx = 0;
+ *pcy = 0;
+ *pfMemFree = true;
+ vrc = VINF_SUCCESS;
+ }
+ }
+ else
+ vrc = VERR_INVALID_PARAMETER;
+ return vrc;
+}
+
+static int i_displayTakeScreenshot(PUVM pUVM, PCVMMR3VTABLE pVMM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv,
+ ULONG aScreenId, BYTE *address, ULONG width, ULONG height)
+{
+ uint8_t *pbData = NULL;
+ size_t cbData = 0;
+ uint32_t cx = 0;
+ uint32_t cy = 0;
+ bool fFreeMem = false;
+ int vrc = VINF_SUCCESS;
+
+ int cRetries = 5;
+ while (cRetries-- > 0)
+ {
+ /* Note! Not sure if the priority call is such a good idea here, but
+ it would be nice to have an accurate screenshot for the bug
+ report if the VM deadlocks. */
+ vrc = pVMM->pfnVMR3ReqPriorityCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)Display::i_displayTakeScreenshotEMT, 7,
+ pDisplay, aScreenId, &pbData, &cbData, &cx, &cy, &fFreeMem);
+ if (vrc != VERR_TRY_AGAIN)
+ {
+ break;
+ }
+
+ RTThreadSleep(10);
+ }
+
+ if (RT_SUCCESS(vrc) && pbData)
+ {
+ if (cx == width && cy == height)
+ {
+ /* No scaling required. */
+ memcpy(address, pbData, cbData);
+ }
+ else
+ {
+ /* Scale. */
+ LogRelFlowFunc(("SCALE: %dx%d -> %dx%d\n", cx, cy, width, height));
+
+ uint8_t *dst = address;
+ uint8_t *src = pbData;
+ int dstW = width;
+ int dstH = height;
+ int srcW = cx;
+ int srcH = cy;
+ int iDeltaLine = cx * 4;
+
+ BitmapScale32(dst,
+ dstW, dstH,
+ src,
+ iDeltaLine,
+ srcW, srcH);
+ }
+
+ if (fFreeMem)
+ RTMemFree(pbData);
+ else
+ {
+ /* This can be called from any thread. */
+ pDrv->pUpPort->pfnFreeScreenshot(pDrv->pUpPort, pbData);
+ }
+ }
+
+ return vrc;
+}
+
+HRESULT Display::takeScreenShotWorker(ULONG aScreenId,
+ BYTE *aAddress,
+ ULONG aWidth,
+ ULONG aHeight,
+ BitmapFormat_T aBitmapFormat,
+ ULONG *pcbOut)
+{
+ HRESULT hrc = S_OK;
+
+ /* Do not allow too small and too large screenshots. This also filters out negative
+ * values passed as either 'aWidth' or 'aHeight'.
+ */
+ CheckComArgExpr(aWidth, aWidth != 0 && aWidth <= 32767);
+ CheckComArgExpr(aHeight, aHeight != 0 && aHeight <= 32767);
+
+ if ( aBitmapFormat != BitmapFormat_BGR0
+ && aBitmapFormat != BitmapFormat_BGRA
+ && aBitmapFormat != BitmapFormat_RGBA
+ && aBitmapFormat != BitmapFormat_PNG)
+ {
+ return setError(E_NOTIMPL,
+ tr("Unsupported screenshot format 0x%08X"), aBitmapFormat);
+ }
+
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
+ return ptrVM.hrc();
+
+ int vrc = i_displayTakeScreenshot(ptrVM.rawUVM(), ptrVM.vtable(), this, mpDrv, aScreenId, aAddress, aWidth, aHeight);
+ if (RT_SUCCESS(vrc))
+ {
+ const size_t cbData = aWidth * 4 * aHeight;
+
+ /* Most of uncompressed formats. */
+ *pcbOut = (ULONG)cbData;
+
+ if (aBitmapFormat == BitmapFormat_BGR0)
+ {
+ /* Do nothing. */
+ }
+ else if (aBitmapFormat == BitmapFormat_BGRA)
+ {
+ uint32_t *pu32 = (uint32_t *)aAddress;
+ size_t cPixels = aWidth * aHeight;
+ while (cPixels--)
+ *pu32++ |= UINT32_C(0xFF000000);
+ }
+ else if (aBitmapFormat == BitmapFormat_RGBA)
+ {
+ uint8_t *pu8 = aAddress;
+ size_t cPixels = aWidth * aHeight;
+ while (cPixels--)
+ {
+ uint8_t u8 = pu8[0];
+ pu8[0] = pu8[2];
+ pu8[2] = u8;
+ pu8[3] = 0xFF;
+
+ pu8 += 4;
+ }
+ }
+ else if (aBitmapFormat == BitmapFormat_PNG)
+ {
+ uint8_t *pu8PNG = NULL;
+ uint32_t cbPNG = 0;
+ uint32_t cxPNG = 0;
+ uint32_t cyPNG = 0;
+
+ vrc = DisplayMakePNG(aAddress, aWidth, aHeight, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
+ if (RT_SUCCESS(vrc))
+ {
+ if (cbPNG <= cbData)
+ {
+ memcpy(aAddress, pu8PNG, cbPNG);
+ *pcbOut = cbPNG;
+ }
+ else
+ hrc = setError(E_FAIL, tr("PNG is larger than 32bpp bitmap"));
+ }
+ else
+ hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("Could not convert screenshot to PNG (%Rrc)"), vrc);
+ RTMemFree(pu8PNG);
+ }
+ }
+ else if (vrc == VERR_TRY_AGAIN)
+ hrc = setErrorBoth(E_UNEXPECTED, vrc, tr("Screenshot is not available at this time"));
+ else if (RT_FAILURE(vrc))
+ hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("Could not take a screenshot (%Rrc)"), vrc);
+
+ return hrc;
+}
+
+HRESULT Display::takeScreenShot(ULONG aScreenId,
+ BYTE *aAddress,
+ ULONG aWidth,
+ ULONG aHeight,
+ BitmapFormat_T aBitmapFormat)
+{
+ LogRelFlowFunc(("[%d] address=%p, width=%d, height=%d, format 0x%08X\n",
+ aScreenId, aAddress, aWidth, aHeight, aBitmapFormat));
+
+ ULONG cbOut = 0;
+ HRESULT hrc = takeScreenShotWorker(aScreenId, aAddress, aWidth, aHeight, aBitmapFormat, &cbOut);
+ NOREF(cbOut);
+
+ LogRelFlowFunc(("%Rhrc\n", hrc));
+ return hrc;
+}
+
+HRESULT Display::takeScreenShotToArray(ULONG aScreenId,
+ ULONG aWidth,
+ ULONG aHeight,
+ BitmapFormat_T aBitmapFormat,
+ std::vector<BYTE> &aScreenData)
+{
+ LogRelFlowFunc(("[%d] width=%d, height=%d, format 0x%08X\n",
+ aScreenId, aWidth, aHeight, aBitmapFormat));
+
+ /* Do not allow too small and too large screenshots. This also filters out negative
+ * values passed as either 'aWidth' or 'aHeight'.
+ */
+ CheckComArgExpr(aWidth, aWidth != 0 && aWidth <= 32767);
+ CheckComArgExpr(aHeight, aHeight != 0 && aHeight <= 32767);
+
+ const size_t cbData = aWidth * 4 * aHeight;
+ aScreenData.resize(cbData);
+
+ ULONG cbOut = 0;
+ HRESULT hrc = takeScreenShotWorker(aScreenId, &aScreenData.front(), aWidth, aHeight, aBitmapFormat, &cbOut);
+ if (FAILED(hrc))
+ cbOut = 0;
+
+ aScreenData.resize(cbOut);
+
+ LogRelFlowFunc(("%Rhrc\n", hrc));
+ return hrc;
+}
+
+#ifdef VBOX_WITH_RECORDING
+/**
+ * Invalidates the recording configuration.
+ *
+ * @returns IPRT status code.
+ */
+int Display::i_recordingInvalidate(void)
+{
+ RecordingContext *pCtx = mParent->i_recordingGetContext();
+ if (!pCtx || !pCtx->IsStarted())
+ return VINF_SUCCESS;
+
+ /*
+ * Invalidate screens.
+ */
+ for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
+ {
+ RecordingStream *pRecordingStream = pCtx->GetStream(uScreen);
+
+ const bool fStreamEnabled = pRecordingStream->IsReady();
+ bool fChanged = maRecordingEnabled[uScreen] != fStreamEnabled;
+
+ maRecordingEnabled[uScreen] = fStreamEnabled;
+
+ if (fChanged && uScreen < mcMonitors)
+ i_recordingScreenChanged(uScreen);
+ }
+
+ return VINF_SUCCESS;
+}
+
+void Display::i_recordingScreenChanged(unsigned uScreenId)
+{
+ RecordingContext *pCtx = mParent->i_recordingGetContext();
+
+ i_UpdateDeviceCursorCapabilities();
+ if ( RT_LIKELY(!maRecordingEnabled[uScreenId])
+ || !pCtx || !pCtx->IsStarted())
+ {
+ /* Skip recording this screen. */
+ return;
+ }
+
+ /* Get a new source bitmap which will be used by video recording code. */
+ ComPtr<IDisplaySourceBitmap> pSourceBitmap;
+ QuerySourceBitmap(uScreenId, pSourceBitmap.asOutParam());
+
+ int vrc2 = RTCritSectEnter(&mVideoRecLock);
+ if (RT_SUCCESS(vrc2))
+ {
+ maFramebuffers[uScreenId].Recording.pSourceBitmap = pSourceBitmap;
+
+ vrc2 = RTCritSectLeave(&mVideoRecLock);
+ AssertRC(vrc2);
+ }
+}
+#endif /* VBOX_WITH_RECORDING */
+
+/*static*/ DECLCALLBACK(int)
+Display::i_drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address, ULONG x, ULONG y, ULONG width, ULONG height)
+{
+ int vrc = VINF_SUCCESS;
+
+ DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
+
+ if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
+ {
+ vrc = pDisplay->mpDrv->pUpPort->pfnDisplayBlt(pDisplay->mpDrv->pUpPort, address, x, y, width, height);
+ }
+ else if (aScreenId < pDisplay->mcMonitors)
+ {
+ /* Copy the bitmap to the guest VRAM. */
+ const uint8_t *pu8Src = address;
+ int32_t xSrc = 0;
+ int32_t ySrc = 0;
+ uint32_t u32SrcWidth = width;
+ uint32_t u32SrcHeight = height;
+ uint32_t u32SrcLineSize = width * 4;
+ uint32_t u32SrcBitsPerPixel = 32;
+
+ uint8_t *pu8Dst = pFBInfo->pu8FramebufferVRAM;
+ int32_t xDst = x;
+ int32_t yDst = y;
+ uint32_t u32DstWidth = pFBInfo->w;
+ uint32_t u32DstHeight = pFBInfo->h;
+ uint32_t u32DstLineSize = pFBInfo->u32LineSize;
+ uint32_t u32DstBitsPerPixel = pFBInfo->u16BitsPerPixel;
+
+ vrc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
+ width, height,
+ pu8Src,
+ xSrc, ySrc,
+ u32SrcWidth, u32SrcHeight,
+ u32SrcLineSize, u32SrcBitsPerPixel,
+ pu8Dst,
+ xDst, yDst,
+ u32DstWidth, u32DstHeight,
+ u32DstLineSize, u32DstBitsPerPixel);
+ if (RT_SUCCESS(vrc))
+ {
+ if (!pFBInfo->pSourceBitmap.isNull())
+ {
+ /* Update the changed screen area. When source bitmap uses VRAM directly, just notify
+ * frontend to update. And for default format, render the guest VRAM to the source bitmap.
+ */
+ if ( pFBInfo->fDefaultFormat
+ && !pFBInfo->fDisabled)
+ {
+ BYTE *pAddress = NULL;
+ ULONG ulWidth = 0;
+ ULONG ulHeight = 0;
+ ULONG ulBitsPerPixel = 0;
+ ULONG ulBytesPerLine = 0;
+ BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
+
+ HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
+ &ulWidth,
+ &ulHeight,
+ &ulBitsPerPixel,
+ &ulBytesPerLine,
+ &bitmapFormat);
+ if (SUCCEEDED(hrc))
+ {
+ pu8Src = pFBInfo->pu8FramebufferVRAM;
+ xSrc = x;
+ ySrc = y;
+ u32SrcWidth = pFBInfo->w;
+ u32SrcHeight = pFBInfo->h;
+ u32SrcLineSize = pFBInfo->u32LineSize;
+ u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
+
+ /* Default format is 32 bpp. */
+ pu8Dst = pAddress;
+ xDst = xSrc;
+ yDst = ySrc;
+ u32DstWidth = u32SrcWidth;
+ u32DstHeight = u32SrcHeight;
+ u32DstLineSize = u32DstWidth * 4;
+ u32DstBitsPerPixel = 32;
+
+ pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
+ width, height,
+ pu8Src,
+ xSrc, ySrc,
+ u32SrcWidth, u32SrcHeight,
+ u32SrcLineSize, u32SrcBitsPerPixel,
+ pu8Dst,
+ xDst, yDst,
+ u32DstWidth, u32DstHeight,
+ u32DstLineSize, u32DstBitsPerPixel);
+ }
+ }
+ }
+
+ pDisplay->i_handleDisplayUpdate(aScreenId, x, y, width, height);
+ }
+ }
+ else
+ {
+ vrc = VERR_INVALID_PARAMETER;
+ }
+
+ if (RT_SUCCESS(vrc))
+ pDisplay->mParent->i_consoleVRDPServer()->SendUpdateBitmap(aScreenId, x, y, width, height);
+
+ return vrc;
+}
+
+HRESULT Display::drawToScreen(ULONG aScreenId, BYTE *aAddress, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
+{
+ /// @todo (r=dmik) this function may take too long to complete if the VM
+ // is doing something like saving state right now. Which, in case if it
+ // is called on the GUI thread, will make it unresponsive. We should
+ // check the machine state here (by enclosing the check and VMRequCall
+ // within the Console lock to make it atomic).
+
+ LogRelFlowFunc(("aAddress=%p, x=%d, y=%d, width=%d, height=%d\n",
+ (void *)aAddress, aX, aY, aWidth, aHeight));
+
+ CheckComArgExpr(aWidth, aWidth != 0);
+ CheckComArgExpr(aHeight, aHeight != 0);
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ CHECK_CONSOLE_DRV(mpDrv);
+
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
+ return ptrVM.hrc();
+
+ /* Release lock because the call scheduled on EMT may also try to take it. */
+ alock.release();
+
+ /*
+ * Again we're lazy and make the graphics device do all the
+ * dirty conversion work.
+ */
+ int vrc = ptrVM.vtable()->pfnVMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_drawToScreenEMT, 7,
+ this, aScreenId, aAddress, aX, aY, aWidth, aHeight);
+
+ /*
+ * If the function returns not supported, we'll have to do all the
+ * work ourselves using the framebuffer.
+ */
+ HRESULT hrc = S_OK;
+ if (vrc == VERR_NOT_SUPPORTED || vrc == VERR_NOT_IMPLEMENTED)
+ {
+ /** @todo implement generic fallback for screen blitting. */
+ hrc = E_NOTIMPL;
+ }
+ else if (RT_FAILURE(vrc))
+ hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("Could not draw to the screen (%Rrc)"), vrc);
+/// @todo
+// else
+// {
+// /* All ok. Redraw the screen. */
+// handleDisplayUpdate(x, y, width, height);
+// }
+
+ LogRelFlowFunc(("hrc=%Rhrc\n", hrc));
+ return hrc;
+}
+
+/** @todo r=bird: cannot quite see why this would be required to run on an
+ * EMT any more. It's not an issue in the COM methods, but for the
+ * VGA device interface it is an issue, see querySourceBitmap. */
+/*static*/ DECLCALLBACK(int) Display::i_InvalidateAndUpdateEMT(Display *pDisplay, unsigned uId, bool fUpdateAll)
+{
+ LogRelFlowFunc(("uId=%d, fUpdateAll %d\n", uId, fUpdateAll));
+
+ unsigned uScreenId;
+ for (uScreenId = (fUpdateAll ? 0 : uId); uScreenId < pDisplay->mcMonitors; uScreenId++)
+ {
+ DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
+
+ if ( !pFBInfo->fVBVAEnabled
+ && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
+ pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort, /* fFailOnResize = */ true);
+ else
+ {
+ if (!pFBInfo->fDisabled)
+ {
+ /* Render complete VRAM screen to the framebuffer.
+ * When framebuffer uses VRAM directly, just notify it to update.
+ */
+ if (pFBInfo->fDefaultFormat && !pFBInfo->pSourceBitmap.isNull())
+ {
+ BYTE *pAddress = NULL;
+ ULONG ulWidth = 0;
+ ULONG ulHeight = 0;
+ ULONG ulBitsPerPixel = 0;
+ ULONG ulBytesPerLine = 0;
+ BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
+
+ HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
+ &ulWidth,
+ &ulHeight,
+ &ulBitsPerPixel,
+ &ulBytesPerLine,
+ &bitmapFormat);
+ if (SUCCEEDED(hrc))
+ {
+ uint32_t width = pFBInfo->w;
+ uint32_t height = pFBInfo->h;
+
+ const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
+ int32_t xSrc = 0;
+ int32_t ySrc = 0;
+ uint32_t u32SrcWidth = pFBInfo->w;
+ uint32_t u32SrcHeight = pFBInfo->h;
+ uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
+ uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
+
+ /* Default format is 32 bpp. */
+ uint8_t *pu8Dst = pAddress;
+ int32_t xDst = xSrc;
+ int32_t yDst = ySrc;
+ uint32_t u32DstWidth = u32SrcWidth;
+ uint32_t u32DstHeight = u32SrcHeight;
+ uint32_t u32DstLineSize = u32DstWidth * 4;
+ uint32_t u32DstBitsPerPixel = 32;
+
+ /* if uWidth != pFBInfo->w and uHeight != pFBInfo->h
+ * implies resize of Framebuffer is in progress and
+ * copyrect should not be called.
+ */
+ if (ulWidth == pFBInfo->w && ulHeight == pFBInfo->h)
+ pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
+ width, height,
+ pu8Src,
+ xSrc, ySrc,
+ u32SrcWidth, u32SrcHeight,
+ u32SrcLineSize, u32SrcBitsPerPixel,
+ pu8Dst,
+ xDst, yDst,
+ u32DstWidth, u32DstHeight,
+ u32DstLineSize, u32DstBitsPerPixel);
+ }
+ }
+
+ pDisplay->i_handleDisplayUpdate(uScreenId, 0, 0, pFBInfo->w, pFBInfo->h);
+ }
+ }
+ if (!fUpdateAll)
+ break;
+ }
+ LogRelFlowFunc(("done\n"));
+ return VINF_SUCCESS;
+}
+
+/**
+ * Does a full invalidation of the VM display and instructs the VM
+ * to update it immediately.
+ *
+ * @returns COM status code
+ */
+
+HRESULT Display::invalidateAndUpdate()
+{
+ LogRelFlowFunc(("\n"));
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ CHECK_CONSOLE_DRV(mpDrv);
+
+ Console::SafeVMPtr ptrVM(mParent);
+ HRESULT hrc = ptrVM.hrc();
+ if (SUCCEEDED(hrc))
+ {
+ LogRelFlowFunc(("Sending DPYUPDATE request\n"));
+
+ /* Have to release the lock when calling EMT. */
+ alock.release();
+
+ int vrc = ptrVM.vtable()->pfnVMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
+ 3, this, 0, true);
+ alock.acquire();
+
+ if (RT_FAILURE(vrc))
+ hrc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("Could not invalidate and update the screen (%Rrc)"), vrc);
+ }
+
+ LogRelFlowFunc(("hrc=%Rhrc\n", hrc));
+ return hrc;
+}
+
+HRESULT Display::invalidateAndUpdateScreen(ULONG aScreenId)
+{
+ LogRelFlowFunc(("\n"));
+
+ Console::SafeVMPtr ptrVM(mParent);
+ HRESULT hrc = ptrVM.hrc();
+ if (SUCCEEDED(hrc))
+ {
+ int vrc = ptrVM.vtable()->pfnVMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
+ 3, this, aScreenId, false);
+ if (RT_FAILURE(vrc))
+ hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not invalidate and update the screen %d (%Rrc)"), aScreenId, vrc);
+ }
+
+ LogRelFlowFunc(("hrc=%Rhrc\n", hrc));
+ return hrc;
+}
+
+HRESULT Display::completeVHWACommand(BYTE *aCommand)
+{
+#ifdef VBOX_WITH_VIDEOHWACCEL
+ mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsync(mpDrv->pVBVACallbacks, (VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *)aCommand);
+ return S_OK;
+#else
+ RT_NOREF(aCommand);
+ return E_NOTIMPL;
+#endif
+}
+
+HRESULT Display::viewportChanged(ULONG aScreenId, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
+{
+ AssertMsgReturn(aScreenId < mcMonitors, ("aScreendId=%d mcMonitors=%d\n", aScreenId, mcMonitors), E_INVALIDARG);
+
+ /* The driver might not have been constructed yet */
+ if (mpDrv && mpDrv->pUpPort->pfnSetViewport)
+ mpDrv->pUpPort->pfnSetViewport(mpDrv->pUpPort, aScreenId, aX, aY, aWidth, aHeight);
+
+ return S_OK;
+}
+
+HRESULT Display::querySourceBitmap(ULONG aScreenId,
+ ComPtr<IDisplaySourceBitmap> &aDisplaySourceBitmap)
+{
+ LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
+
+ Console::SafeVMPtr ptrVM(mParent);
+ if (!ptrVM.isOk())
+ return ptrVM.hrc();
+
+ CHECK_CONSOLE_DRV(mpDrv);
+
+ bool fSetRenderVRAM = false;
+ bool fInvalidate = false;
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (aScreenId >= mcMonitors)
+ return setError(E_INVALIDARG, tr("QuerySourceBitmap: Invalid screen %d (total %d)"), aScreenId, mcMonitors);
+
+ if (!mfSourceBitmapEnabled)
+ {
+ aDisplaySourceBitmap = NULL;
+ return E_FAIL;
+ }
+
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
+
+ /* No source bitmap for a blank guest screen. */
+ if (pFBInfo->flags & VBVA_SCREEN_F_BLANK)
+ {
+ aDisplaySourceBitmap = NULL;
+ return E_FAIL;
+ }
+
+ HRESULT hr = S_OK;
+
+ if (pFBInfo->pSourceBitmap.isNull())
+ {
+ /* Create a new object. */
+ ComObjPtr<DisplaySourceBitmap> obj;
+ hr = obj.createObject();
+ if (SUCCEEDED(hr))
+ hr = obj->init(this, aScreenId, pFBInfo);
+
+ if (SUCCEEDED(hr))
+ {
+ pFBInfo->pSourceBitmap = obj;
+ pFBInfo->fDefaultFormat = !obj->i_usesVRAM();
+
+ if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
+ {
+ /* Start buffer updates. */
+ BYTE *pAddress = NULL;
+ ULONG ulWidth = 0;
+ ULONG ulHeight = 0;
+ ULONG ulBitsPerPixel = 0;
+ ULONG ulBytesPerLine = 0;
+ BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
+
+ pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
+ &ulWidth,
+ &ulHeight,
+ &ulBitsPerPixel,
+ &ulBytesPerLine,
+ &bitmapFormat);
+
+ mpDrv->IConnector.pbData = pAddress;
+ mpDrv->IConnector.cbScanline = ulBytesPerLine;
+ mpDrv->IConnector.cBits = ulBitsPerPixel;
+ mpDrv->IConnector.cx = ulWidth;
+ mpDrv->IConnector.cy = ulHeight;
+
+ fSetRenderVRAM = pFBInfo->fDefaultFormat;
+ }
+
+ /* Make sure that the bitmap contains the latest image. */
+ fInvalidate = pFBInfo->fDefaultFormat;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ pFBInfo->pSourceBitmap.queryInterfaceTo(aDisplaySourceBitmap.asOutParam());
+ }
+
+ /* Leave the IDisplay lock because the VGA device must not be called under it. */
+ alock.release();
+
+ if (SUCCEEDED(hr))
+ {
+ if (fSetRenderVRAM)
+ mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, true);
+
+ if (fInvalidate)
+#if 1 /* bird: Cannot see why this needs to run on an EMT. It deadlocks now with timer callback moving to non-EMT worker threads. */
+ Display::i_InvalidateAndUpdateEMT(this, aScreenId, false /*fUpdateAll*/);
+#else
+ VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
+ 3, this, aScreenId, false);
+#endif
+ }
+
+ LogRelFlowFunc(("%Rhrc\n", hr));
+ return hr;
+}
+
+HRESULT Display::getGuestScreenLayout(std::vector<ComPtr<IGuestScreenInfo> > &aGuestScreenLayout)
+{
+ NOREF(aGuestScreenLayout);
+ return E_NOTIMPL;
+}
+
+HRESULT Display::setScreenLayout(ScreenLayoutMode_T aScreenLayoutMode,
+ const std::vector<ComPtr<IGuestScreenInfo> > &aGuestScreenInfo)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (aGuestScreenInfo.size() != mcMonitors)
+ return E_INVALIDARG;
+
+ CHECK_CONSOLE_DRV(mpDrv);
+
+ /*
+ * It is up to the guest to decide whether the hint is
+ * valid. Therefore don't do any VRAM sanity checks here.
+ */
+
+ /* Have to release the lock because the pfnRequestDisplayChange
+ * will call EMT. */
+ alock.release();
+
+ VMMDev *pVMMDev = mParent->i_getVMMDev();
+ if (pVMMDev)
+ {
+ PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
+ if (pVMMDevPort)
+ {
+ uint32_t const cDisplays = (uint32_t)aGuestScreenInfo.size();
+
+ size_t const cbAlloc = cDisplays * sizeof(VMMDevDisplayDef);
+ VMMDevDisplayDef *paDisplayDefs = (VMMDevDisplayDef *)RTMemAlloc(cbAlloc);
+ if (paDisplayDefs)
+ {
+ for (uint32_t i = 0; i < cDisplays; ++i)
+ {
+ VMMDevDisplayDef *p = &paDisplayDefs[i];
+ ComPtr<IGuestScreenInfo> pScreenInfo = aGuestScreenInfo[i];
+
+ ULONG screenId = 0;
+ GuestMonitorStatus_T guestMonitorStatus = GuestMonitorStatus_Enabled;
+ BOOL origin = FALSE;
+ BOOL primary = FALSE;
+ LONG originX = 0;
+ LONG originY = 0;
+ ULONG width = 0;
+ ULONG height = 0;
+ ULONG bitsPerPixel = 0;
+
+ pScreenInfo->COMGETTER(ScreenId) (&screenId);
+ pScreenInfo->COMGETTER(GuestMonitorStatus)(&guestMonitorStatus);
+ pScreenInfo->COMGETTER(Primary) (&primary);
+ pScreenInfo->COMGETTER(Origin) (&origin);
+ pScreenInfo->COMGETTER(OriginX) (&originX);
+ pScreenInfo->COMGETTER(OriginY) (&originY);
+ pScreenInfo->COMGETTER(Width) (&width);
+ pScreenInfo->COMGETTER(Height) (&height);
+ pScreenInfo->COMGETTER(BitsPerPixel)(&bitsPerPixel);
+
+ LogFlowFunc(("%d %d,%d %dx%d\n", screenId, originX, originY, width, height));
+
+ p->idDisplay = screenId;
+ p->xOrigin = originX;
+ p->yOrigin = originY;
+ p->cx = width;
+ p->cy = height;
+ p->cBitsPerPixel = bitsPerPixel;
+ p->fDisplayFlags = VMMDEV_DISPLAY_CX | VMMDEV_DISPLAY_CY | VMMDEV_DISPLAY_BPP;
+ if (guestMonitorStatus == GuestMonitorStatus_Disabled)
+ p->fDisplayFlags |= VMMDEV_DISPLAY_DISABLED;
+ if (origin)
+ p->fDisplayFlags |= VMMDEV_DISPLAY_ORIGIN;
+ if (primary)
+ p->fDisplayFlags |= VMMDEV_DISPLAY_PRIMARY;
+ }
+
+ bool const fForce = aScreenLayoutMode == ScreenLayoutMode_Reset
+ || aScreenLayoutMode == ScreenLayoutMode_Apply;
+ bool const fNotify = aScreenLayoutMode != ScreenLayoutMode_Silent;
+ pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, cDisplays, paDisplayDefs, fForce, fNotify);
+
+ RTMemFree(paDisplayDefs);
+ }
+ }
+ }
+ return S_OK;
+}
+
+HRESULT Display::detachScreens(const std::vector<LONG> &aScreenIds)
+{
+ NOREF(aScreenIds);
+ return E_NOTIMPL;
+}
+
+HRESULT Display::createGuestScreenInfo(ULONG aDisplay,
+ GuestMonitorStatus_T aStatus,
+ BOOL aPrimary,
+ BOOL aChangeOrigin,
+ LONG aOriginX,
+ LONG aOriginY,
+ ULONG aWidth,
+ ULONG aHeight,
+ ULONG aBitsPerPixel,
+ ComPtr<IGuestScreenInfo> &aGuestScreenInfo)
+{
+ /* Create a new object. */
+ ComObjPtr<GuestScreenInfo> obj;
+ HRESULT hr = obj.createObject();
+ if (SUCCEEDED(hr))
+ hr = obj->init(aDisplay, aStatus, aPrimary, aChangeOrigin, aOriginX, aOriginY,
+ aWidth, aHeight, aBitsPerPixel);
+ if (SUCCEEDED(hr))
+ obj.queryInterfaceTo(aGuestScreenInfo.asOutParam());
+
+ return hr;
+}
+
+
+/*
+ * GuestScreenInfo implementation.
+ */
+DEFINE_EMPTY_CTOR_DTOR(GuestScreenInfo)
+
+HRESULT GuestScreenInfo::FinalConstruct()
+{
+ return BaseFinalConstruct();
+}
+
+void GuestScreenInfo::FinalRelease()
+{
+ uninit();
+
+ BaseFinalRelease();
+}
+
+HRESULT GuestScreenInfo::init(ULONG aDisplay,
+ GuestMonitorStatus_T aGuestMonitorStatus,
+ BOOL aPrimary,
+ BOOL aChangeOrigin,
+ LONG aOriginX,
+ LONG aOriginY,
+ ULONG aWidth,
+ ULONG aHeight,
+ ULONG aBitsPerPixel)
+{
+ LogFlowThisFunc(("[%u]\n", aDisplay));
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ mScreenId = aDisplay;
+ mGuestMonitorStatus = aGuestMonitorStatus;
+ mPrimary = aPrimary;
+ mOrigin = aChangeOrigin;
+ mOriginX = aOriginX;
+ mOriginY = aOriginY;
+ mWidth = aWidth;
+ mHeight = aHeight;
+ mBitsPerPixel = aBitsPerPixel;
+
+ /* Confirm a successful initialization */
+ autoInitSpan.setSucceeded();
+
+ return S_OK;
+}
+
+void GuestScreenInfo::uninit()
+{
+ /* Enclose the state transition Ready->InUninit->NotReady */
+ AutoUninitSpan autoUninitSpan(this);
+ if (autoUninitSpan.uninitDone())
+ return;
+
+ LogFlowThisFunc(("[%u]\n", mScreenId));
+}
+
+HRESULT GuestScreenInfo::getScreenId(ULONG *aScreenId)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aScreenId = mScreenId;
+ return S_OK;
+}
+
+HRESULT GuestScreenInfo::getGuestMonitorStatus(GuestMonitorStatus_T *aGuestMonitorStatus)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aGuestMonitorStatus = mGuestMonitorStatus;
+ return S_OK;
+}
+
+HRESULT GuestScreenInfo::getPrimary(BOOL *aPrimary)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aPrimary = mPrimary;
+ return S_OK;
+}
+
+HRESULT GuestScreenInfo::getOrigin(BOOL *aOrigin)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aOrigin = mOrigin;
+ return S_OK;
+}
+
+HRESULT GuestScreenInfo::getOriginX(LONG *aOriginX)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aOriginX = mOriginX;
+ return S_OK;
+}
+
+HRESULT GuestScreenInfo::getOriginY(LONG *aOriginY)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aOriginY = mOriginY;
+ return S_OK;
+}
+
+HRESULT GuestScreenInfo::getWidth(ULONG *aWidth)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aWidth = mWidth;
+ return S_OK;
+}
+
+HRESULT GuestScreenInfo::getHeight(ULONG *aHeight)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aHeight = mHeight;
+ return S_OK;
+}
+
+HRESULT GuestScreenInfo::getBitsPerPixel(ULONG *aBitsPerPixel)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ *aBitsPerPixel = mBitsPerPixel;
+ return S_OK;
+}
+
+HRESULT GuestScreenInfo::getExtendedInfo(com::Utf8Str &aExtendedInfo)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aExtendedInfo = com::Utf8Str();
+ return S_OK;
+}
+
+// wrapped IEventListener method
+HRESULT Display::handleEvent(const ComPtr<IEvent> &aEvent)
+{
+ VBoxEventType_T aType = VBoxEventType_Invalid;
+
+ aEvent->COMGETTER(Type)(&aType);
+ switch (aType)
+ {
+ case VBoxEventType_OnStateChanged:
+ {
+ ComPtr<IStateChangedEvent> scev = aEvent;
+ Assert(scev);
+ MachineState_T machineState;
+ scev->COMGETTER(State)(&machineState);
+ if ( machineState == MachineState_Running
+ || machineState == MachineState_Teleporting
+ || machineState == MachineState_LiveSnapshotting
+ || machineState == MachineState_DeletingSnapshotOnline
+ )
+ {
+ LogRelFlowFunc(("Machine is running.\n"));
+
+ }
+ break;
+ }
+ default:
+ AssertFailed();
+ }
+
+ return S_OK;
+}
+
+
+// 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 = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ 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(VBOX_VIDEO_PRIMARY_SCREEN, 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 = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+
+#ifdef DEBUG_sunlover
+ LogFlowFunc(("fVideoAccelEnabled = %d, %d,%d %dx%d\n",
+ pDrv->pDisplay->mVideoAccelLegacy.fVideoAccelEnabled, x, y, cx, cy));
+#endif /* DEBUG_sunlover */
+
+ /* This call does update regardless of VBVA status.
+ * But in VBVA mode this is called only as result of
+ * pfnUpdateDisplayAll in the VGA device.
+ */
+
+ pDrv->pDisplay->i_handleDisplayUpdate(VBOX_VIDEO_PRIMARY_SCREEN, x, y, cx, cy);
+}
+
+/**
+ * Periodic display refresh callback.
+ *
+ * @see PDMIDISPLAYCONNECTOR::pfnRefresh
+ * @thread EMT
+ */
+/*static*/ DECLCALLBACK(void) Display::i_displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
+{
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+
+#ifdef DEBUG_sunlover_2
+ LogFlowFunc(("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
+ pDrv->pDisplay->mfVideoAccelEnabled));
+#endif /* DEBUG_sunlover_2 */
+
+ Display *pDisplay = pDrv->pDisplay;
+ unsigned uScreenId;
+
+ int vrc = pDisplay->i_videoAccelRefreshProcess(pDrv->pUpPort);
+ if (vrc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
+ {
+ if (vrc == VWRN_INVALID_STATE)
+ {
+ /* No VBVA do a display update. */
+ pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
+ }
+
+ /* Inform the VRDP server that the current display update sequence is
+ * completed. At this moment the framebuffer memory contains a definite
+ * image, that is synchronized with the orders already sent to VRDP client.
+ * The server can now process redraw requests from clients or initial
+ * fullscreen updates for new clients.
+ */
+ for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
+ {
+ Assert(pDisplay->mParent && pDisplay->mParent->i_consoleVRDPServer());
+ pDisplay->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, NULL, 0);
+ }
+ }
+
+#ifdef VBOX_WITH_RECORDING
+ AssertPtr(pDisplay->mParent);
+ RecordingContext *pCtx = pDisplay->mParent->i_recordingGetContext();
+
+ if ( pCtx
+ && pCtx->IsStarted()
+ && pCtx->IsFeatureEnabled(RecordingFeature_Video))
+ {
+ do
+ {
+ /* If the recording context has reached the configured recording
+ * limit, disable recording. */
+ if (pCtx->IsLimitReached())
+ {
+ pDisplay->mParent->i_onRecordingChange(FALSE /* Disable */);
+ break;
+ }
+
+ uint64_t tsNowMs = RTTimeProgramMilliTS();
+ for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
+ {
+ if (!pDisplay->maRecordingEnabled[uScreenId])
+ continue;
+
+ if (!pCtx->NeedsUpdate(uScreenId, tsNowMs))
+ continue;
+
+ DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
+ if (!pFBInfo->fDisabled)
+ {
+ ComPtr<IDisplaySourceBitmap> pSourceBitmap;
+ int vrc2 = RTCritSectEnter(&pDisplay->mVideoRecLock);
+ if (RT_SUCCESS(vrc2))
+ {
+ pSourceBitmap = pFBInfo->Recording.pSourceBitmap;
+ RTCritSectLeave(&pDisplay->mVideoRecLock);
+ }
+
+ if (!pSourceBitmap.isNull())
+ {
+ BYTE *pbAddress = NULL;
+ ULONG ulWidth = 0;
+ ULONG ulHeight = 0;
+ ULONG ulBitsPerPixel = 0;
+ ULONG ulBytesPerLine = 0;
+ BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
+ HRESULT hrc = pSourceBitmap->QueryBitmapInfo(&pbAddress,
+ &ulWidth,
+ &ulHeight,
+ &ulBitsPerPixel,
+ &ulBytesPerLine,
+ &bitmapFormat);
+ if (SUCCEEDED(hrc) && pbAddress)
+ vrc = pCtx->SendVideoFrame(uScreenId, 0, 0, BitmapFormat_BGR,
+ ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight,
+ pbAddress, tsNowMs);
+ else
+ vrc = VERR_NOT_SUPPORTED;
+
+ pSourceBitmap.setNull();
+ }
+ else
+ vrc = VERR_NOT_SUPPORTED;
+
+ if (vrc == VINF_TRY_AGAIN)
+ break;
+ }
+ }
+ } while (0);
+ }
+#endif /* VBOX_WITH_RECORDING */
+
+#ifdef DEBUG_sunlover_2
+ LogFlowFunc(("leave\n"));
+#endif /* DEBUG_sunlover_2 */
+}
+
+/**
+ * Reset notification
+ *
+ * @see PDMIDISPLAYCONNECTOR::pfnReset
+ */
+DECLCALLBACK(void) Display::i_displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
+{
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+
+ LogRelFlowFunc(("\n"));
+
+ /* Disable VBVA mode. */
+ pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
+}
+
+/**
+ * LFBModeChange notification
+ *
+ * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
+ */
+DECLCALLBACK(void) Display::i_displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
+{
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+
+ LogRelFlowFunc(("fEnabled=%d\n", fEnabled));
+
+ NOREF(fEnabled);
+
+ /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
+ pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
+}
+
+/**
+ * Adapter information change notification.
+ *
+ * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
+ */
+DECLCALLBACK(void) Display::i_displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM,
+ uint32_t u32VRAMSize)
+{
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ 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 = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ pDrv->pDisplay->processDisplayData(pvVRAM, uScreenId);
+}
+
+#ifdef VBOX_WITH_VIDEOHWACCEL
+
+int Display::i_handleVHWACommandProcess(int enmCmd, bool fGuestCmd, VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand)
+{
+ /* bugref:9691 Disable the legacy VHWA interface.
+ * Keep the host commands enabled because they are needed when an old saved state is loaded.
+ */
+ if (fGuestCmd)
+ return VERR_NOT_IMPLEMENTED;
+
+ unsigned id = (unsigned)pCommand->iDisplay;
+ if (id >= mcMonitors)
+ return VERR_INVALID_PARAMETER;
+
+ ComPtr<IFramebuffer> pFramebuffer;
+ AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
+ pFramebuffer = maFramebuffers[id].pFramebuffer;
+ bool fVHWASupported = RT_BOOL(maFramebuffers[id].u32Caps & FramebufferCapabilities_VHWA);
+ arlock.release();
+
+ if (pFramebuffer == NULL || !fVHWASupported)
+ return VERR_NOT_IMPLEMENTED; /* Implementation is not available. */
+
+ HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE *)pCommand, enmCmd, fGuestCmd);
+ if (hr == S_FALSE)
+ return VINF_SUCCESS;
+ if (SUCCEEDED(hr))
+ return VINF_CALLBACK_RETURN;
+ if (hr == E_ACCESSDENIED)
+ return VERR_INVALID_STATE; /* notify we can not handle request atm */
+ if (hr == E_NOTIMPL)
+ return VERR_NOT_IMPLEMENTED;
+ return VERR_GENERAL_FAILURE;
+}
+
+DECLCALLBACK(int) Display::i_displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, int enmCmd, bool fGuestCmd,
+ VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand)
+{
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+
+ return pDrv->pDisplay->i_handleVHWACommandProcess(enmCmd, fGuestCmd, pCommand);
+}
+
+#endif /* VBOX_WITH_VIDEOHWACCEL */
+
+int Display::i_handle3DNotifyProcess(VBOX3DNOTIFY *p3DNotify)
+{
+ unsigned const id = (unsigned)p3DNotify->iDisplay;
+ if (id >= mcMonitors)
+ return VERR_INVALID_PARAMETER;
+
+ ComPtr<IFramebuffer> pFramebuffer;
+ AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
+ pFramebuffer = maFramebuffers[id].pFramebuffer;
+ arlock.release();
+
+ int vrc = VINF_SUCCESS;
+
+ if (!pFramebuffer.isNull())
+ {
+ if (p3DNotify->enmNotification == VBOX3D_NOTIFY_TYPE_HW_OVERLAY_GET_ID)
+ {
+ LONG64 winId = 0;
+ HRESULT hrc = pFramebuffer->COMGETTER(WinId)(&winId);
+ if (SUCCEEDED(hrc))
+ {
+ *(uint64_t *)&p3DNotify->au8Data[0] = winId;
+ }
+ else
+ vrc = VERR_NOT_SUPPORTED;
+ }
+ else
+ {
+ com::SafeArray<BYTE> data;
+ data.initFrom((BYTE *)&p3DNotify->au8Data[0], p3DNotify->cbData);
+
+ HRESULT hrc = pFramebuffer->Notify3DEvent((ULONG)p3DNotify->enmNotification, ComSafeArrayAsInParam(data));
+ if (FAILED(hrc))
+ vrc = VERR_NOT_SUPPORTED;
+ }
+ }
+ else
+ vrc = VERR_NOT_IMPLEMENTED;
+
+ return vrc;
+}
+
+DECLCALLBACK(int) Display::i_display3DNotifyProcess(PPDMIDISPLAYCONNECTOR pInterface,
+ VBOX3DNOTIFY *p3DNotify)
+{
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ return pDrv->pDisplay->i_handle3DNotifyProcess(p3DNotify);
+}
+
+HRESULT Display::notifyScaleFactorChange(ULONG aScreenId, ULONG aScaleFactorWMultiplied, ULONG aScaleFactorHMultiplied)
+{
+ RT_NOREF(aScreenId, aScaleFactorWMultiplied, aScaleFactorHMultiplied);
+# if 0 /** @todo Thank you so very much from anyone using VMSVGA3d! */
+ AssertMsgFailed(("Attempt to specify OpenGL content scale factor while 3D acceleration is disabled in VM config. Ignored.\n"));
+# else
+ /* Need an interface like this here (and the #ifdefs needs adjusting):
+ PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
+ if (pUpPort && pUpPort->pfnSetScaleFactor)
+ pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
+# endif
+ return S_OK;
+}
+
+HRESULT Display::notifyHiDPIOutputPolicyChange(BOOL fUnscaledHiDPI)
+{
+ RT_NOREF(fUnscaledHiDPI);
+
+ /* Need an interface like this here (and the #ifdefs needs adjusting):
+ PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
+ if (pUpPort && pUpPort->pfnSetScaleFactor)
+ pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
+
+ return S_OK;
+}
+
+#ifdef VBOX_WITH_HGSMI
+/**
+ * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnVBVAEnable}
+ */
+DECLCALLBACK(int) Display::i_displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
+ VBVAHOSTFLAGS RT_UNTRUSTED_VOLATILE_GUEST *pHostFlags)
+{
+ LogRelFlowFunc(("uScreenId %d\n", uScreenId));
+
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ Display *pThis = pDrv->pDisplay;
+ AssertReturn(uScreenId < pThis->mcMonitors, VERR_INVALID_PARAMETER);
+
+ if (pThis->maFramebuffers[uScreenId].fVBVAEnabled)
+ {
+ LogRel(("Enabling different vbva mode\n"));
+#ifdef DEBUG_misha
+ AssertMsgFailed(("enabling different vbva mode\n"));
+#endif
+ return VERR_INVALID_STATE;
+ }
+
+ pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
+ pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
+ pThis->maFramebuffers[uScreenId].fVBVAForceResize = true;
+
+ vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnVBVADisable}
+ */
+DECLCALLBACK(void) Display::i_displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
+{
+ LogRelFlowFunc(("uScreenId %d\n", uScreenId));
+
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ Display *pThis = pDrv->pDisplay;
+ AssertReturnVoid(uScreenId < pThis->mcMonitors);
+
+ DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
+
+ if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
+ {
+ /* Make sure that the primary screen is visible now.
+ * The guest can't use VBVA anymore, so only only the VGA device output works.
+ */
+ pFBInfo->flags = 0;
+ if (pFBInfo->fDisabled)
+ {
+ pFBInfo->fDisabled = false;
+ ::FireGuestMonitorChangedEvent(pThis->mParent->i_getEventSource(), GuestMonitorChangedEventType_Enabled, uScreenId,
+ pFBInfo->xOrigin, pFBInfo->yOrigin, pFBInfo->w, pFBInfo->h);
+ }
+ }
+
+ pFBInfo->fVBVAEnabled = false;
+ pFBInfo->fVBVAForceResize = false;
+
+ vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, pFBInfo);
+
+ pFBInfo->pVBVAHostFlags = NULL;
+
+ if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
+ {
+ /* Force full screen update, because VGA device must take control, do resize, etc. */
+ pThis->mpDrv->pUpPort->pfnUpdateDisplayAll(pThis->mpDrv->pUpPort, /* fFailOnResize = */ false);
+ }
+}
+
+DECLCALLBACK(void) Display::i_displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
+{
+ RT_NOREF(uScreenId);
+ LogFlowFunc(("uScreenId %d\n", uScreenId));
+
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ Display *pThis = pDrv->pDisplay;
+
+ if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
+ {
+ vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers,
+ pThis->mcMonitors);
+ ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
+ }
+}
+
+/**
+ * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnVBVAUpdateProcess}
+ */
+DECLCALLBACK(void) Display::i_displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
+ struct VBVACMDHDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmd, size_t cbCmd)
+{
+ LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d, @%d,%d %dx%d\n", uScreenId, pCmd, cbCmd, pCmd->x, pCmd->y, pCmd->w, pCmd->h));
+ VBVACMDHDR hdrSaved;
+ RT_COPY_VOLATILE(hdrSaved, *pCmd);
+ RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
+
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ Display *pThis = pDrv->pDisplay;
+ DISPLAYFBINFO *pFBInfo;
+ AssertReturnVoid(uScreenId < pThis->mcMonitors);
+
+ pFBInfo = &pThis->maFramebuffers[uScreenId];
+
+ if (pFBInfo->fDefaultFormat)
+ {
+ /* Make sure that framebuffer contains the same image as the guest VRAM. */
+ if ( uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
+ && !pFBInfo->fDisabled)
+ {
+ pDrv->pUpPort->pfnUpdateDisplayRect(pDrv->pUpPort, hdrSaved.x, hdrSaved.y, hdrSaved.w, hdrSaved.h);
+ }
+ else if ( !pFBInfo->pSourceBitmap.isNull()
+ && !pFBInfo->fDisabled)
+ {
+ /* Render VRAM content to the framebuffer. */
+ BYTE *pAddress = NULL;
+ ULONG ulWidth = 0;
+ ULONG ulHeight = 0;
+ ULONG ulBitsPerPixel = 0;
+ ULONG ulBytesPerLine = 0;
+ BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
+
+ HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
+ &ulWidth,
+ &ulHeight,
+ &ulBitsPerPixel,
+ &ulBytesPerLine,
+ &bitmapFormat);
+ if (SUCCEEDED(hrc))
+ {
+ uint32_t width = hdrSaved.w;
+ uint32_t height = hdrSaved.h;
+
+ const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
+ int32_t xSrc = hdrSaved.x - pFBInfo->xOrigin;
+ int32_t ySrc = hdrSaved.y - pFBInfo->yOrigin;
+ uint32_t u32SrcWidth = pFBInfo->w;
+ uint32_t u32SrcHeight = pFBInfo->h;
+ uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
+ uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
+
+ uint8_t *pu8Dst = pAddress;
+ int32_t xDst = xSrc;
+ int32_t yDst = ySrc;
+ uint32_t u32DstWidth = u32SrcWidth;
+ uint32_t u32DstHeight = u32SrcHeight;
+ uint32_t u32DstLineSize = u32DstWidth * 4;
+ uint32_t u32DstBitsPerPixel = 32;
+
+ pDrv->pUpPort->pfnCopyRect(pDrv->pUpPort,
+ width, height,
+ pu8Src,
+ xSrc, ySrc,
+ u32SrcWidth, u32SrcHeight,
+ u32SrcLineSize, u32SrcBitsPerPixel,
+ pu8Dst,
+ xDst, yDst,
+ u32DstWidth, u32DstHeight,
+ u32DstLineSize, u32DstBitsPerPixel);
+ }
+ }
+ }
+
+ /*
+ * Here is your classic 'temporary' solution.
+ */
+ /** @todo New SendUpdate entry which can get a separate cmd header or coords. */
+ VBVACMDHDR *pHdrUnconst = (VBVACMDHDR *)pCmd;
+
+ pHdrUnconst->x -= (int16_t)pFBInfo->xOrigin;
+ pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
+
+ pThis->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, pHdrUnconst, (uint32_t)cbCmd);
+
+ *pHdrUnconst = hdrSaved;
+}
+
+DECLCALLBACK(void) Display::i_displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y,
+ uint32_t cx, uint32_t cy)
+{
+ LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
+
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ Display *pThis = pDrv->pDisplay;
+ DISPLAYFBINFO *pFBInfo;
+ AssertReturnVoid(uScreenId < pThis->mcMonitors);
+
+ pFBInfo = &pThis->maFramebuffers[uScreenId];
+
+ /** @todo handleFramebufferUpdate (uScreenId,
+ * x - pThis->maFramebuffers[uScreenId].xOrigin,
+ * y - pThis->maFramebuffers[uScreenId].yOrigin,
+ * cx, cy);
+ */
+ pThis->i_handleDisplayUpdate(uScreenId, x - pFBInfo->xOrigin, y - pFBInfo->yOrigin, cx, cy);
+}
+
+#ifdef DEBUG_sunlover
+static void logVBVAResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, const DISPLAYFBINFO *pFBInfo)
+{
+ LogRel(("displayVBVAResize: [%d] %s\n"
+ " pView->u32ViewIndex %d\n"
+ " pView->u32ViewOffset 0x%08X\n"
+ " pView->u32ViewSize 0x%08X\n"
+ " pView->u32MaxScreenSize 0x%08X\n"
+ " pScreen->i32OriginX %d\n"
+ " pScreen->i32OriginY %d\n"
+ " pScreen->u32StartOffset 0x%08X\n"
+ " pScreen->u32LineSize 0x%08X\n"
+ " pScreen->u32Width %d\n"
+ " pScreen->u32Height %d\n"
+ " pScreen->u16BitsPerPixel %d\n"
+ " pScreen->u16Flags 0x%04X\n"
+ " pFBInfo->u32Offset 0x%08X\n"
+ " pFBInfo->u32MaxFramebufferSize 0x%08X\n"
+ " pFBInfo->u32InformationSize 0x%08X\n"
+ " pFBInfo->fDisabled %d\n"
+ " xOrigin, yOrigin, w, h: %d,%d %dx%d\n"
+ " pFBInfo->u16BitsPerPixel %d\n"
+ " pFBInfo->pu8FramebufferVRAM %p\n"
+ " pFBInfo->u32LineSize 0x%08X\n"
+ " pFBInfo->flags 0x%04X\n"
+ " pFBInfo->pHostEvents %p\n"
+ " pFBInfo->fDefaultFormat %d\n"
+ " pFBInfo->fVBVAEnabled %d\n"
+ " pFBInfo->fVBVAForceResize %d\n"
+ " pFBInfo->pVBVAHostFlags %p\n"
+ "",
+ pScreen->u32ViewIndex,
+ (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)? "DISABLED": "ENABLED",
+ pView->u32ViewIndex,
+ pView->u32ViewOffset,
+ pView->u32ViewSize,
+ pView->u32MaxScreenSize,
+ pScreen->i32OriginX,
+ pScreen->i32OriginY,
+ pScreen->u32StartOffset,
+ pScreen->u32LineSize,
+ pScreen->u32Width,
+ pScreen->u32Height,
+ pScreen->u16BitsPerPixel,
+ pScreen->u16Flags,
+ pFBInfo->u32Offset,
+ pFBInfo->u32MaxFramebufferSize,
+ pFBInfo->u32InformationSize,
+ pFBInfo->fDisabled,
+ pFBInfo->xOrigin,
+ pFBInfo->yOrigin,
+ pFBInfo->w,
+ pFBInfo->h,
+ pFBInfo->u16BitsPerPixel,
+ pFBInfo->pu8FramebufferVRAM,
+ pFBInfo->u32LineSize,
+ pFBInfo->flags,
+ pFBInfo->pHostEvents,
+ pFBInfo->fDefaultFormat,
+ pFBInfo->fVBVAEnabled,
+ pFBInfo->fVBVAForceResize,
+ pFBInfo->pVBVAHostFlags
+ ));
+}
+#endif /* DEBUG_sunlover */
+
+DECLCALLBACK(int) Display::i_displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, PCVBVAINFOVIEW pView,
+ PCVBVAINFOSCREEN pScreen, void *pvVRAM, bool fResetInputMapping)
+{
+ LogRelFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
+
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ Display *pThis = pDrv->pDisplay;
+
+ return pThis->processVBVAResize(pView, pScreen, pvVRAM, fResetInputMapping);
+}
+
+int Display::processVBVAResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, void *pvVRAM, bool fResetInputMapping)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ RT_NOREF(pView);
+
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[pScreen->u32ViewIndex];
+
+#ifdef DEBUG_sunlover
+ logVBVAResize(pView, pScreen, pFBInfo);
+#endif
+
+ if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
+ {
+ /* Ask the framebuffer to resize using a default format. The framebuffer will be black.
+ * So if the frontend does not support GuestMonitorChangedEventType_Disabled event,
+ * the VM window will be black. */
+ uint32_t u32Width = pFBInfo->w ? pFBInfo->w : 640;
+ uint32_t u32Height = pFBInfo->h ? pFBInfo->h : 480;
+ int32_t xOrigin = pFBInfo->xOrigin;
+ int32_t yOrigin = pFBInfo->yOrigin;
+
+ alock.release();
+
+ i_handleDisplayResize(pScreen->u32ViewIndex, 0, (uint8_t *)NULL, 0,
+ u32Width, u32Height, pScreen->u16Flags, xOrigin, yOrigin, false);
+
+ return VINF_SUCCESS;
+ }
+
+ VBVAINFOSCREEN screenInfo;
+ RT_ZERO(screenInfo);
+
+ if (pScreen->u16Flags & VBVA_SCREEN_F_BLANK2)
+ {
+ /* Init a local VBVAINFOSCREEN structure, which will be used instead of
+ * the original pScreen. Set VBVA_SCREEN_F_BLANK, which will force
+ * the code below to choose the "blanking" branches.
+ */
+ screenInfo.u32ViewIndex = pScreen->u32ViewIndex;
+ screenInfo.i32OriginX = pFBInfo->xOrigin;
+ screenInfo.i32OriginY = pFBInfo->yOrigin;
+ screenInfo.u32StartOffset = 0; /* Irrelevant */
+ screenInfo.u32LineSize = pFBInfo->u32LineSize;
+ screenInfo.u32Width = pFBInfo->w;
+ screenInfo.u32Height = pFBInfo->h;
+ screenInfo.u16BitsPerPixel = pFBInfo->u16BitsPerPixel;
+ screenInfo.u16Flags = pScreen->u16Flags | VBVA_SCREEN_F_BLANK;
+
+ pScreen = &screenInfo;
+ }
+
+ if (fResetInputMapping)
+ {
+ /// @todo Rename to m* and verify whether some kind of lock is required.
+ xInputMappingOrigin = 0;
+ yInputMappingOrigin = 0;
+ cxInputMapping = 0;
+ cyInputMapping = 0;
+ }
+
+ alock.release();
+
+ return i_handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
+ (uint8_t *)pvVRAM + pScreen->u32StartOffset,
+ pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height, pScreen->u16Flags,
+ pScreen->i32OriginX, pScreen->i32OriginY, false);
+}
+
+DECLCALLBACK(int) Display::i_displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
+ uint32_t xHot, uint32_t yHot,
+ uint32_t cx, uint32_t cy,
+ const void *pvShape)
+{
+ LogFlowFunc(("\n"));
+ LogRel2(("%s: fVisible=%RTbool\n", __PRETTY_FUNCTION__, fVisible));
+
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+
+ uint32_t cbShape = 0;
+ if (pvShape)
+ {
+ cbShape = (cx + 7) / 8 * cy; /* size of the AND mask */
+ cbShape = ((cbShape + 3) & ~3) + cx * 4 * cy; /* + gap + size of the XOR mask */
+ }
+
+ /* Tell the console about it */
+ pDrv->pDisplay->mParent->i_onMousePointerShapeChange(fVisible, fAlpha,
+ xHot, yHot, cx, cy, (uint8_t *)pvShape, cbShape);
+
+ return VINF_SUCCESS;
+}
+
+DECLCALLBACK(void) Display::i_displayVBVAGuestCapabilityUpdate(PPDMIDISPLAYCONNECTOR pInterface, uint32_t fCapabilities)
+{
+ LogFlowFunc(("\n"));
+
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ Display *pThis = pDrv->pDisplay;
+
+ pThis->i_handleUpdateGuestVBVACapabilities(fCapabilities);
+}
+
+DECLCALLBACK(void) Display::i_displayVBVAInputMappingUpdate(PPDMIDISPLAYCONNECTOR pInterface, int32_t xOrigin, int32_t yOrigin,
+ uint32_t cx, uint32_t cy)
+{
+ LogFlowFunc(("\n"));
+
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ Display *pThis = pDrv->pDisplay;
+
+ pThis->i_handleUpdateVBVAInputMapping(xOrigin, yOrigin, cx, cy);
+}
+
+DECLCALLBACK(void) Display::i_displayVBVAReportCursorPosition(PPDMIDISPLAYCONNECTOR pInterface, uint32_t fFlags, uint32_t aScreenId, uint32_t x, uint32_t y)
+{
+ LogFlowFunc(("\n"));
+ LogRel2(("%s: fFlags=%RU32, aScreenId=%RU32, x=%RU32, y=%RU32\n",
+ __PRETTY_FUNCTION__, fFlags, aScreenId, x, y));
+
+ PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
+ Display *pThis = pDrv->pDisplay;
+
+ if (fFlags & VBVA_CURSOR_SCREEN_RELATIVE)
+ {
+ AssertReturnVoid(aScreenId < pThis->mcMonitors);
+
+ x += pThis->maFramebuffers[aScreenId].xOrigin;
+ y += pThis->maFramebuffers[aScreenId].yOrigin;
+ }
+ ::FireCursorPositionChangedEvent(pThis->mParent->i_getEventSource(), RT_BOOL(fFlags & VBVA_CURSOR_VALID_DATA), x, y);
+}
+
+#endif /* VBOX_WITH_HGSMI */
+
+/**
+ * @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;
+
+ if (pThis->pDisplay)
+ {
+ AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
+#ifdef VBOX_WITH_RECORDING
+ pThis->pDisplay->mParent->i_recordingStop();
+#endif
+#if defined(VBOX_WITH_VIDEOHWACCEL)
+ pThis->pVBVACallbacks = NULL;
+#endif
+ }
+}
+
+
+/**
+ * Destruct a display driver instance.
+ *
+ * @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)
+ {
+ AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
+#ifdef VBOX_WITH_RECORDING
+ pThis->pDisplay->mParent->i_recordingStop();
+#endif
+#if defined(VBOX_WITH_VIDEOHWACCEL)
+ pThis->pVBVACallbacks = NULL;
+#endif
+
+ pThis->pDisplay->mpDrv = NULL;
+ pThis->pDisplay = NULL;
+ }
+#if defined(VBOX_WITH_VIDEOHWACCEL)
+ pThis->pVBVACallbacks = NULL;
+#endif
+}
+
+
+/**
+ * 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;
+#ifdef VBOX_WITH_VIDEOHWACCEL
+ pThis->IConnector.pfnVHWACommandProcess = Display::i_displayVHWACommandProcess;
+#endif
+#ifdef VBOX_WITH_HGSMI
+ pThis->IConnector.pfnVBVAEnable = Display::i_displayVBVAEnable;
+ pThis->IConnector.pfnVBVADisable = Display::i_displayVBVADisable;
+ pThis->IConnector.pfnVBVAUpdateBegin = Display::i_displayVBVAUpdateBegin;
+ pThis->IConnector.pfnVBVAUpdateProcess = Display::i_displayVBVAUpdateProcess;
+ pThis->IConnector.pfnVBVAUpdateEnd = Display::i_displayVBVAUpdateEnd;
+ pThis->IConnector.pfnVBVAResize = Display::i_displayVBVAResize;
+ pThis->IConnector.pfnVBVAMousePointerShape = Display::i_displayVBVAMousePointerShape;
+ pThis->IConnector.pfnVBVAGuestCapabilityUpdate = Display::i_displayVBVAGuestCapabilityUpdate;
+ pThis->IConnector.pfnVBVAInputMappingUpdate = Display::i_displayVBVAInputMappingUpdate;
+ pThis->IConnector.pfnVBVAReportCursorPosition = Display::i_displayVBVAReportCursorPosition;
+#endif
+ pThis->IConnector.pfn3DNotifyProcess = Display::i_display3DNotifyProcess;
+
+ /*
+ * 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;
+ }
+#if defined(VBOX_WITH_VIDEOHWACCEL)
+ pThis->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
+ if (!pThis->pVBVACallbacks)
+ {
+ AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
+ return VERR_PDM_MISSING_INTERFACE_ABOVE;
+ }
+#endif
+ /*
+ * Get the Display object pointer and update the mpDrv member.
+ */
+ com::Guid uuid(COM_IIDOF(IDisplay));
+ IDisplay *pIDisplay = (IDisplay *)PDMDrvHlpQueryGenericUserObject(pDrvIns, uuid.raw());
+ if (!pIDisplay)
+ {
+ AssertMsgFailed(("Configuration error: No/bad Keyboard object!\n"));
+ return VERR_NOT_FOUND;
+ }
+ pThis->pDisplay = static_cast<Display *>(pIDisplay);
+ 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: */