summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-client/DisplayImplLegacy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-client/DisplayImplLegacy.cpp')
-rw-r--r--src/VBox/Main/src-client/DisplayImplLegacy.cpp1018
1 files changed, 1018 insertions, 0 deletions
diff --git a/src/VBox/Main/src-client/DisplayImplLegacy.cpp b/src/VBox/Main/src-client/DisplayImplLegacy.cpp
new file mode 100644
index 00000000..7f00a4d0
--- /dev/null
+++ b/src/VBox/Main/src-client/DisplayImplLegacy.cpp
@@ -0,0 +1,1018 @@
+/* $Id: DisplayImplLegacy.cpp $ */
+/** @file
+ * VirtualBox IDisplay implementation, helpers for legacy GAs.
+ *
+ * Methods and helpers to support old Guest Additions 3.x or older.
+ * This is not used by the current Guest Additions.
+ */
+
+/*
+ * Copyright (C) 2006-2022 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 "ConsoleImpl.h"
+#include "ConsoleVRDPServer.h"
+#include "VMMDev.h"
+#include <VBox/VMMDev.h>
+
+/* generated header */
+#include "VBoxEvents.h"
+
+
+int videoAccelConstruct(VIDEOACCEL *pVideoAccel)
+{
+ pVideoAccel->pVbvaMemory = NULL;
+ pVideoAccel->fVideoAccelEnabled = false;
+
+ pVideoAccel->pu8VbvaPartial = NULL;
+ pVideoAccel->cbVbvaPartial = 0;
+
+ pVideoAccel->hXRoadsVideoAccel = NIL_RTSEMXROADS;
+ int vrc = RTSemXRoadsCreate(&pVideoAccel->hXRoadsVideoAccel);
+ AssertRC(vrc);
+
+ return vrc;
+}
+
+void videoAccelDestroy(VIDEOACCEL *pVideoAccel)
+{
+ RTSemXRoadsDestroy(pVideoAccel->hXRoadsVideoAccel);
+ RT_ZERO(*pVideoAccel);
+}
+
+static unsigned mapCoordsToScreen(DISPLAYFBINFO *pInfos, unsigned cInfos, int *px, int *py, int *pw, int *ph)
+{
+ RT_NOREF(pw, ph);
+
+ DISPLAYFBINFO *pInfo = pInfos;
+ unsigned uScreenId;
+ Log9(("mapCoordsToScreen: %d,%d %dx%d\n", *px, *py, *pw, *ph));
+ for (uScreenId = 0; uScreenId < cInfos; uScreenId++, pInfo++)
+ {
+ Log9((" [%d] %d,%d %dx%d\n", uScreenId, pInfo->xOrigin, pInfo->yOrigin, pInfo->w, pInfo->h));
+ if ( (pInfo->xOrigin <= *px && *px < pInfo->xOrigin + (int)pInfo->w)
+ && (pInfo->yOrigin <= *py && *py < pInfo->yOrigin + (int)pInfo->h))
+ {
+ /* The rectangle belongs to the screen. Correct coordinates. */
+ *px -= pInfo->xOrigin;
+ *py -= pInfo->yOrigin;
+ Log9((" -> %d,%d", *px, *py));
+ break;
+ }
+ }
+ if (uScreenId == cInfos)
+ {
+ /* Map to primary screen. */
+ uScreenId = 0;
+ }
+ Log9((" scr %d\n", uScreenId));
+ return uScreenId;
+}
+
+
+typedef struct _VBVADIRTYREGION
+{
+ /* Copies of object's pointers used by vbvaRgn functions. */
+ DISPLAYFBINFO *paFramebuffers;
+ unsigned cMonitors;
+ Display *pDisplay;
+ PPDMIDISPLAYPORT pPort;
+
+ /* The rectangle that includes all dirty rectangles. */
+ RTRECT aDirtyRects[SchemaDefs::MaxGuestMonitors];
+
+} VBVADIRTYREGION;
+
+static void vbvaRgnInit(VBVADIRTYREGION *prgn, DISPLAYFBINFO *paFramebuffers, unsigned cMonitors,
+ Display *pd, PPDMIDISPLAYPORT pp)
+{
+ prgn->paFramebuffers = paFramebuffers;
+ prgn->cMonitors = cMonitors;
+ prgn->pDisplay = pd;
+ prgn->pPort = pp;
+
+ RT_ZERO(prgn->aDirtyRects);
+}
+
+static void vbvaRgnDirtyRect(VBVADIRTYREGION *prgn, unsigned uScreenId, VBVACMDHDR *phdr)
+{
+ Log9(("x = %d, y = %d, w = %d, h = %d\n", phdr->x, phdr->y, phdr->w, phdr->h));
+
+ /*
+ * Here update rectangles are accumulated to form an update area.
+ */
+ /** @todo
+ * Now the simplest method is used which builds one rectangle that
+ * includes all update areas. A bit more advanced method can be
+ * employed here. The method should be fast however.
+ */
+ if (phdr->w == 0 || phdr->h == 0)
+ {
+ /* Empty rectangle. */
+ return;
+ }
+
+ int32_t xRight = phdr->x + phdr->w;
+ int32_t yBottom = phdr->y + phdr->h;
+
+ RTRECT *pDirtyRect = &prgn->aDirtyRects[uScreenId];
+ DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
+
+ if (pDirtyRect->xRight == 0)
+ {
+ /* This is the first rectangle to be added. */
+ pDirtyRect->xLeft = phdr->x;
+ pDirtyRect->yTop = phdr->y;
+ pDirtyRect->xRight = xRight;
+ pDirtyRect->yBottom = yBottom;
+ }
+ else
+ {
+ /* Adjust region coordinates. */
+ if (pDirtyRect->xLeft > phdr->x)
+ {
+ pDirtyRect->xLeft = phdr->x;
+ }
+
+ if (pDirtyRect->yTop > phdr->y)
+ {
+ pDirtyRect->yTop = phdr->y;
+ }
+
+ if (pDirtyRect->xRight < xRight)
+ {
+ pDirtyRect->xRight = xRight;
+ }
+
+ if (pDirtyRect->yBottom < yBottom)
+ {
+ pDirtyRect->yBottom = yBottom;
+ }
+ }
+
+ if (pFBInfo->fDefaultFormat)
+ {
+ /// @todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
+ prgn->pPort->pfnUpdateDisplayRect(prgn->pPort, phdr->x, phdr->y, phdr->w, phdr->h);
+ prgn->pDisplay->i_handleDisplayUpdate(uScreenId, phdr->x, phdr->y, phdr->w, phdr->h);
+ }
+
+ return;
+}
+
+static void vbvaRgnUpdateFramebuffer(VBVADIRTYREGION *prgn, unsigned uScreenId)
+{
+ RTRECT *pDirtyRect = &prgn->aDirtyRects[uScreenId];
+ DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
+
+ uint32_t w = pDirtyRect->xRight - pDirtyRect->xLeft;
+ uint32_t h = pDirtyRect->yBottom - pDirtyRect->yTop;
+
+ if (!pFBInfo->fDefaultFormat && w != 0 && h != 0)
+ {
+ /// @todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
+ prgn->pPort->pfnUpdateDisplayRect(prgn->pPort, pDirtyRect->xLeft, pDirtyRect->yTop, w, h);
+ prgn->pDisplay->i_handleDisplayUpdate(uScreenId, pDirtyRect->xLeft, pDirtyRect->yTop, w, h);
+ }
+}
+
+void i_vbvaSetMemoryFlags(VBVAMEMORY *pVbvaMemory,
+ bool fVideoAccelEnabled,
+ bool fVideoAccelVRDP,
+ uint32_t fu32SupportedOrders,
+ DISPLAYFBINFO *paFBInfos,
+ unsigned cFBInfos)
+{
+ if (pVbvaMemory)
+ {
+ /* This called only on changes in mode. So reset VRDP always. */
+ uint32_t fu32Flags = VBVA_F_MODE_VRDP_RESET;
+
+ if (fVideoAccelEnabled)
+ {
+ fu32Flags |= VBVA_F_MODE_ENABLED;
+
+ if (fVideoAccelVRDP)
+ {
+ fu32Flags |= VBVA_F_MODE_VRDP | VBVA_F_MODE_VRDP_ORDER_MASK;
+
+ pVbvaMemory->fu32SupportedOrders = fu32SupportedOrders;
+ }
+ }
+
+ pVbvaMemory->fu32ModeFlags = fu32Flags;
+ }
+
+ unsigned uScreenId;
+ for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
+ {
+ if (paFBInfos[uScreenId].pHostEvents)
+ {
+ paFBInfos[uScreenId].pHostEvents->fu32Events |= VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
+ }
+ }
+}
+
+bool Display::i_VideoAccelAllowed(void)
+{
+ return true;
+}
+
+int videoAccelEnterVGA(VIDEOACCEL *pVideoAccel)
+{
+ return RTSemXRoadsNSEnter(pVideoAccel->hXRoadsVideoAccel);
+}
+
+void videoAccelLeaveVGA(VIDEOACCEL *pVideoAccel)
+{
+ RTSemXRoadsNSLeave(pVideoAccel->hXRoadsVideoAccel);
+}
+
+int videoAccelEnterVMMDev(VIDEOACCEL *pVideoAccel)
+{
+ return RTSemXRoadsEWEnter(pVideoAccel->hXRoadsVideoAccel);
+}
+
+void videoAccelLeaveVMMDev(VIDEOACCEL *pVideoAccel)
+{
+ RTSemXRoadsEWLeave(pVideoAccel->hXRoadsVideoAccel);
+}
+
+/**
+ * @thread EMT
+ */
+int Display::i_VideoAccelEnable(bool fEnable, VBVAMEMORY *pVbvaMemory, PPDMIDISPLAYPORT pUpPort)
+{
+ LogRelFlowFunc(("fEnable = %d\n", fEnable));
+
+ int vrc = i_videoAccelEnable(fEnable, pVbvaMemory, pUpPort);
+
+ LogRelFlowFunc(("%Rrc.\n", vrc));
+ return vrc;
+}
+
+int Display::i_videoAccelEnable(bool fEnable, VBVAMEMORY *pVbvaMemory, PPDMIDISPLAYPORT pUpPort)
+{
+ VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
+
+ /* Called each time the guest wants to use acceleration,
+ * or when the VGA device disables acceleration,
+ * or when restoring the saved state with accel enabled.
+ *
+ * VGA device disables acceleration on each video mode change
+ * and on reset.
+ *
+ * Guest enabled acceleration at will. And it has to enable
+ * acceleration after a mode change.
+ */
+ LogRelFlowFunc(("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
+ pVideoAccel->fVideoAccelEnabled, fEnable, pVbvaMemory));
+
+ /* Strictly check parameters. Callers must not pass anything in the case. */
+ Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));
+
+ if (!i_VideoAccelAllowed ())
+ return VERR_NOT_SUPPORTED;
+
+ /* Check that current status is not being changed */
+ if (pVideoAccel->fVideoAccelEnabled == fEnable)
+ return VINF_SUCCESS;
+
+ if (pVideoAccel->fVideoAccelEnabled)
+ {
+ /* Process any pending orders and empty the VBVA ring buffer. */
+ i_videoAccelFlush (pUpPort);
+ }
+
+ if (!fEnable && pVideoAccel->pVbvaMemory)
+ pVideoAccel->pVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;
+
+ if (fEnable)
+ {
+ /* Process any pending VGA device changes, resize. */
+ pUpPort->pfnUpdateDisplayAll(pUpPort, /* fFailOnResize = */ false);
+ }
+
+ /* Protect the videoaccel state transition. */
+ RTCritSectEnter(&mVideoAccelLock);
+
+ if (fEnable)
+ {
+ /* Initialize the hardware memory. */
+ i_vbvaSetMemoryFlags(pVbvaMemory, true, mfVideoAccelVRDP,
+ mfu32SupportedOrders, maFramebuffers, mcMonitors);
+ pVbvaMemory->off32Data = 0;
+ pVbvaMemory->off32Free = 0;
+
+ memset(pVbvaMemory->aRecords, 0, sizeof(pVbvaMemory->aRecords));
+ pVbvaMemory->indexRecordFirst = 0;
+ pVbvaMemory->indexRecordFree = 0;
+
+ pVideoAccel->pVbvaMemory = pVbvaMemory;
+ pVideoAccel->fVideoAccelEnabled = true;
+
+ LogRel(("VBVA: Enabled.\n"));
+ }
+ else
+ {
+ pVideoAccel->pVbvaMemory = NULL;
+ pVideoAccel->fVideoAccelEnabled = false;
+
+ LogRel(("VBVA: Disabled.\n"));
+ }
+
+ RTCritSectLeave(&mVideoAccelLock);
+
+ if (!fEnable)
+ {
+ pUpPort->pfnUpdateDisplayAll(pUpPort, /* fFailOnResize = */ false);
+ }
+
+ /* Notify the VMMDev, which saves VBVA status in the saved state,
+ * and needs to know current status.
+ */
+ VMMDev *pVMMDev = mParent->i_getVMMDev();
+ if (pVMMDev)
+ {
+ PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
+ if (pVMMDevPort)
+ pVMMDevPort->pfnVBVAChange(pVMMDevPort, fEnable);
+ }
+
+ LogRelFlowFunc(("VINF_SUCCESS.\n"));
+ return VINF_SUCCESS;
+}
+
+static bool i_vbvaVerifyRingBuffer(VBVAMEMORY *pVbvaMemory)
+{
+ RT_NOREF(pVbvaMemory);
+ return true;
+}
+
+static void i_vbvaFetchBytes(VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t cbDst)
+{
+ if (cbDst >= VBVA_RING_BUFFER_SIZE)
+ {
+ AssertMsgFailed(("cbDst = 0x%08X, ring buffer size 0x%08X\n", cbDst, VBVA_RING_BUFFER_SIZE));
+ return;
+ }
+
+ uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
+ uint8_t *src = &pVbvaMemory->au8RingBuffer[pVbvaMemory->off32Data];
+ int32_t i32Diff = cbDst - u32BytesTillBoundary;
+
+ if (i32Diff <= 0)
+ {
+ /* Chunk will not cross buffer boundary. */
+ memcpy (pu8Dst, src, cbDst);
+ }
+ else
+ {
+ /* Chunk crosses buffer boundary. */
+ memcpy(pu8Dst, src, u32BytesTillBoundary);
+ memcpy(pu8Dst + u32BytesTillBoundary, &pVbvaMemory->au8RingBuffer[0], i32Diff);
+ }
+
+ /* Advance data offset. */
+ pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbDst) % VBVA_RING_BUFFER_SIZE;
+
+ return;
+}
+
+
+static bool i_vbvaPartialRead(uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
+{
+ uint8_t *pu8New;
+
+ LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
+ *ppu8, *pcb, cbRecord));
+
+ if (*ppu8)
+ {
+ Assert (*pcb);
+ pu8New = (uint8_t *)RTMemRealloc(*ppu8, cbRecord);
+ }
+ else
+ {
+ Assert (!*pcb);
+ pu8New = (uint8_t *)RTMemAlloc(cbRecord);
+ }
+
+ if (!pu8New)
+ {
+ /* Memory allocation failed, fail the function. */
+ Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
+ cbRecord));
+
+ if (*ppu8)
+ {
+ RTMemFree(*ppu8);
+ }
+
+ *ppu8 = NULL;
+ *pcb = 0;
+
+ return false;
+ }
+
+ /* Fetch data from the ring buffer. */
+ i_vbvaFetchBytes(pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
+
+ *ppu8 = pu8New;
+ *pcb = cbRecord;
+
+ return true;
+}
+
+/* For contiguous chunks just return the address in the buffer.
+ * For crossing boundary - allocate a buffer from heap.
+ */
+static bool i_vbvaFetchCmd(VIDEOACCEL *pVideoAccel, VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
+{
+ VBVAMEMORY *pVbvaMemory = pVideoAccel->pVbvaMemory;
+
+ uint32_t indexRecordFirst = pVbvaMemory->indexRecordFirst;
+ uint32_t indexRecordFree = pVbvaMemory->indexRecordFree;
+
+#ifdef DEBUG_sunlover
+ LogFlowFunc(("first = %d, free = %d\n",
+ indexRecordFirst, indexRecordFree));
+#endif /* DEBUG_sunlover */
+
+ if (!i_vbvaVerifyRingBuffer(pVbvaMemory))
+ {
+ return false;
+ }
+
+ if (indexRecordFirst == indexRecordFree)
+ {
+ /* No records to process. Return without assigning output variables. */
+ return true;
+ }
+
+ uint32_t cbRecordCurrent = ASMAtomicReadU32(&pVbvaMemory->aRecords[indexRecordFirst].cbRecord);
+
+#ifdef DEBUG_sunlover
+ LogFlowFunc(("cbRecord = 0x%08X\n", cbRecordCurrent));
+#endif /* DEBUG_sunlover */
+
+ uint32_t cbRecord = cbRecordCurrent & ~VBVA_F_RECORD_PARTIAL;
+
+ if (pVideoAccel->cbVbvaPartial)
+ {
+ /* There is a partial read in process. Continue with it. */
+
+ Assert(pVideoAccel->pu8VbvaPartial);
+
+ LogFlowFunc(("continue partial record cbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
+ pVideoAccel->cbVbvaPartial, cbRecordCurrent, indexRecordFirst, indexRecordFree));
+
+ if (cbRecord > pVideoAccel->cbVbvaPartial)
+ {
+ /* New data has been added to the record. */
+ if (!i_vbvaPartialRead(&pVideoAccel->pu8VbvaPartial, &pVideoAccel->cbVbvaPartial, cbRecord, pVbvaMemory))
+ {
+ return false;
+ }
+ }
+
+ if (!(cbRecordCurrent & VBVA_F_RECORD_PARTIAL))
+ {
+ /* The record is completed by guest. Return it to the caller. */
+ *ppHdr = (VBVACMDHDR *)pVideoAccel->pu8VbvaPartial;
+ *pcbCmd = pVideoAccel->cbVbvaPartial;
+
+ pVideoAccel->pu8VbvaPartial = NULL;
+ pVideoAccel->cbVbvaPartial = 0;
+
+ /* Advance the record index. */
+ pVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
+
+#ifdef DEBUG_sunlover
+ LogFlowFunc(("partial done ok, data = %d, free = %d\n",
+ pVbvaMemory->off32Data, pVbvaMemory->off32Free));
+#endif /* DEBUG_sunlover */
+ }
+
+ return true;
+ }
+
+ /* A new record need to be processed. */
+ if (cbRecordCurrent & VBVA_F_RECORD_PARTIAL)
+ {
+ /* Current record is being written by guest. '=' is important here. */
+ if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
+ {
+ /* Partial read must be started. */
+ if (!i_vbvaPartialRead(&pVideoAccel->pu8VbvaPartial, &pVideoAccel->cbVbvaPartial, cbRecord, pVbvaMemory))
+ {
+ return false;
+ }
+
+ LogFlowFunc(("started partial record cbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
+ pVideoAccel->cbVbvaPartial, cbRecordCurrent, indexRecordFirst, indexRecordFree));
+ }
+
+ return true;
+ }
+
+ /* Current record is complete. If it is not empty, process it. */
+ if (cbRecord)
+ {
+ /* The size of largest contiguous chunk in the ring biffer. */
+ uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
+
+ /* The ring buffer pointer. */
+ uint8_t *au8RingBuffer = &pVbvaMemory->au8RingBuffer[0];
+
+ /* The pointer to data in the ring buffer. */
+ uint8_t *src = &au8RingBuffer[pVbvaMemory->off32Data];
+
+ /* Fetch or point the data. */
+ if (u32BytesTillBoundary >= cbRecord)
+ {
+ /* The command does not cross buffer boundary. Return address in the buffer. */
+ *ppHdr = (VBVACMDHDR *)src;
+
+ /* Advance data offset. */
+ pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
+ }
+ else
+ {
+ /* The command crosses buffer boundary. Rare case, so not optimized. */
+ uint8_t *dst = (uint8_t *)RTMemAlloc(cbRecord);
+
+ if (!dst)
+ {
+ LogRelFlowFunc(("could not allocate %d bytes from heap!!!\n", cbRecord));
+ pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
+ return false;
+ }
+
+ i_vbvaFetchBytes(pVbvaMemory, dst, cbRecord);
+
+ *ppHdr = (VBVACMDHDR *)dst;
+
+#ifdef DEBUG_sunlover
+ LogFlowFunc(("Allocated from heap %p\n", dst));
+#endif /* DEBUG_sunlover */
+ }
+ }
+
+ *pcbCmd = cbRecord;
+
+ /* Advance the record index. */
+ pVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
+
+#ifdef DEBUG_sunlover
+ LogFlowFunc(("done ok, data = %d, free = %d\n",
+ pVbvaMemory->off32Data, pVbvaMemory->off32Free));
+#endif /* DEBUG_sunlover */
+
+ return true;
+}
+
+static void i_vbvaReleaseCmd(VIDEOACCEL *pVideoAccel, VBVACMDHDR *pHdr, int32_t cbCmd)
+{
+ RT_NOREF(cbCmd);
+ uint8_t *au8RingBuffer = pVideoAccel->pVbvaMemory->au8RingBuffer;
+
+ if ( (uint8_t *)pHdr >= au8RingBuffer
+ && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
+ {
+ /* The pointer is inside ring buffer. Must be continuous chunk. */
+ Assert(VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
+
+ /* Do nothing. */
+
+ Assert(!pVideoAccel->pu8VbvaPartial && pVideoAccel->cbVbvaPartial == 0);
+ }
+ else
+ {
+ /* The pointer is outside. It is then an allocated copy. */
+
+#ifdef DEBUG_sunlover
+ LogFlowFunc(("Free heap %p\n", pHdr));
+#endif /* DEBUG_sunlover */
+
+ if ((uint8_t *)pHdr == pVideoAccel->pu8VbvaPartial)
+ {
+ pVideoAccel->pu8VbvaPartial = NULL;
+ pVideoAccel->cbVbvaPartial = 0;
+ }
+ else
+ {
+ Assert(!pVideoAccel->pu8VbvaPartial && pVideoAccel->cbVbvaPartial == 0);
+ }
+
+ RTMemFree(pHdr);
+ }
+
+ return;
+}
+
+
+/**
+ * Called regularly on the DisplayRefresh timer.
+ * Also on behalf of guest, when the ring buffer is full.
+ *
+ * @thread EMT
+ */
+void Display::i_VideoAccelFlush(PPDMIDISPLAYPORT pUpPort)
+{
+ int vrc = i_videoAccelFlush(pUpPort);
+ if (RT_FAILURE(vrc))
+ {
+ /* Disable on errors. */
+ i_videoAccelEnable(false, NULL, pUpPort);
+ }
+}
+
+int Display::i_videoAccelFlush(PPDMIDISPLAYPORT pUpPort)
+{
+ VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
+ VBVAMEMORY *pVbvaMemory = pVideoAccel->pVbvaMemory;
+
+#ifdef DEBUG_sunlover_2
+ LogFlowFunc(("fVideoAccelEnabled = %d\n", pVideoAccel->fVideoAccelEnabled));
+#endif /* DEBUG_sunlover_2 */
+
+ if (!pVideoAccel->fVideoAccelEnabled)
+ {
+ Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
+ return VINF_SUCCESS;
+ }
+
+ /* Here VBVA is enabled and we have the accelerator memory pointer. */
+ Assert(pVbvaMemory);
+
+#ifdef DEBUG_sunlover_2
+ LogFlowFunc(("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
+ pVbvaMemory->indexRecordFirst, pVbvaMemory->indexRecordFree,
+ pVbvaMemory->off32Data, pVbvaMemory->off32Free));
+#endif /* DEBUG_sunlover_2 */
+
+ /* Quick check for "nothing to update" case. */
+ if (pVbvaMemory->indexRecordFirst == pVbvaMemory->indexRecordFree)
+ {
+ return VINF_SUCCESS;
+ }
+
+ /* Process the ring buffer */
+ unsigned uScreenId;
+
+ /* Initialize dirty rectangles accumulator. */
+ VBVADIRTYREGION rgn;
+ vbvaRgnInit(&rgn, maFramebuffers, mcMonitors, this, pUpPort);
+
+ for (;;)
+ {
+ VBVACMDHDR *phdr = NULL;
+ uint32_t cbCmd = UINT32_MAX;
+
+ /* Fetch the command data. */
+ if (!i_vbvaFetchCmd(pVideoAccel, &phdr, &cbCmd))
+ {
+ Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
+ pVbvaMemory->off32Data, pVbvaMemory->off32Free));
+ return VERR_INVALID_STATE;
+ }
+
+ if (cbCmd == uint32_t(~0))
+ {
+ /* No more commands yet in the queue. */
+#ifdef DEBUG_sunlover
+ LogFlowFunc(("no command\n"));
+#endif /* DEBUG_sunlover */
+ break;
+ }
+
+ if (cbCmd != 0)
+ {
+#ifdef DEBUG_sunlover
+ LogFlowFunc(("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
+ cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
+#endif /* DEBUG_sunlover */
+
+ VBVACMDHDR hdrSaved = *phdr;
+
+ int x = phdr->x;
+ int y = phdr->y;
+ int w = phdr->w;
+ int h = phdr->h;
+
+ uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
+
+ phdr->x = (int16_t)x;
+ phdr->y = (int16_t)y;
+ phdr->w = (uint16_t)w;
+ phdr->h = (uint16_t)h;
+
+ /* Handle the command.
+ *
+ * Guest is responsible for updating the guest video memory.
+ * The Windows guest does all drawing using Eng*.
+ *
+ * For local output, only dirty rectangle information is used
+ * to update changed areas.
+ *
+ * Dirty rectangles are accumulated to exclude overlapping updates and
+ * group small updates to a larger one.
+ */
+
+ /* Accumulate the update. */
+ vbvaRgnDirtyRect(&rgn, uScreenId, phdr);
+
+ /* Forward the command to VRDP server. */
+ mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, phdr, cbCmd);
+
+ *phdr = hdrSaved;
+ }
+
+ i_vbvaReleaseCmd(pVideoAccel, phdr, cbCmd);
+ }
+
+ for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
+ {
+ /* Draw the framebuffer. */
+ vbvaRgnUpdateFramebuffer(&rgn, uScreenId);
+ }
+ return VINF_SUCCESS;
+}
+
+int Display::i_videoAccelRefreshProcess(PPDMIDISPLAYPORT pUpPort)
+{
+ int vrc = VWRN_INVALID_STATE; /* Default is to do a display update in VGA device. */
+
+ VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
+
+ videoAccelEnterVGA(pVideoAccel);
+
+ if (pVideoAccel->fVideoAccelEnabled)
+ {
+ Assert(pVideoAccel->pVbvaMemory);
+ vrc = i_videoAccelFlush(pUpPort);
+ if (RT_FAILURE(vrc))
+ {
+ /* Disable on errors. */
+ i_videoAccelEnable(false, NULL, pUpPort);
+ vrc = VWRN_INVALID_STATE; /* Do a display update in VGA device. */
+ }
+ else
+ {
+ vrc = VINF_SUCCESS;
+ }
+ }
+
+ videoAccelLeaveVGA(pVideoAccel);
+
+ return vrc;
+}
+
+void Display::processAdapterData(void *pvVRAM, uint32_t u32VRAMSize)
+{
+ RT_NOREF(u32VRAMSize);
+ if (pvVRAM == NULL)
+ {
+ unsigned i;
+ for (i = 0; i < mcMonitors; i++)
+ {
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[i];
+
+ pFBInfo->u32Offset = 0;
+ pFBInfo->u32MaxFramebufferSize = 0;
+ pFBInfo->u32InformationSize = 0;
+ }
+ }
+#ifndef VBOX_WITH_HGSMI
+ else
+ {
+ uint8_t *pu8 = (uint8_t *)pvVRAM;
+ pu8 += u32VRAMSize - VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
+
+ /// @todo
+ uint8_t *pu8End = pu8 + VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
+
+ VBOXVIDEOINFOHDR *pHdr;
+
+ for (;;)
+ {
+ pHdr = (VBOXVIDEOINFOHDR *)pu8;
+ pu8 += sizeof(VBOXVIDEOINFOHDR);
+
+ if (pu8 >= pu8End)
+ {
+ LogRel(("VBoxVideo: Guest adapter information overflow!!!\n"));
+ break;
+ }
+
+ if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_DISPLAY)
+ {
+ if (pHdr->u16Length != sizeof(VBOXVIDEOINFODISPLAY))
+ {
+ LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "DISPLAY", pHdr->u16Length));
+ break;
+ }
+
+ VBOXVIDEOINFODISPLAY *pDisplay = (VBOXVIDEOINFODISPLAY *)pu8;
+
+ if (pDisplay->u32Index >= mcMonitors)
+ {
+ LogRel(("VBoxVideo: Guest adapter information invalid display index %d!!!\n", pDisplay->u32Index));
+ break;
+ }
+
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[pDisplay->u32Index];
+
+ pFBInfo->u32Offset = pDisplay->u32Offset;
+ pFBInfo->u32MaxFramebufferSize = pDisplay->u32FramebufferSize;
+ pFBInfo->u32InformationSize = pDisplay->u32InformationSize;
+
+ LogRelFlow(("VBOX_VIDEO_INFO_TYPE_DISPLAY: %d: at 0x%08X, size 0x%08X, info 0x%08X\n", pDisplay->u32Index,
+ pDisplay->u32Offset, pDisplay->u32FramebufferSize, pDisplay->u32InformationSize));
+ }
+ else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_QUERY_CONF32)
+ {
+ if (pHdr->u16Length != sizeof(VBOXVIDEOINFOQUERYCONF32))
+ {
+ LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "CONF32", pHdr->u16Length));
+ break;
+ }
+
+ VBOXVIDEOINFOQUERYCONF32 *pConf32 = (VBOXVIDEOINFOQUERYCONF32 *)pu8;
+
+ switch (pConf32->u32Index)
+ {
+ case VBOX_VIDEO_QCI32_MONITOR_COUNT:
+ {
+ pConf32->u32Value = mcMonitors;
+ } break;
+
+ case VBOX_VIDEO_QCI32_OFFSCREEN_HEAP_SIZE:
+ {
+ /** @todo make configurable. */
+ pConf32->u32Value = _1M;
+ } break;
+
+ default:
+ LogRel(("VBoxVideo: CONF32 %d not supported!!! Skipping.\n", pConf32->u32Index));
+ }
+ }
+ else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
+ {
+ if (pHdr->u16Length != 0)
+ {
+ LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
+ break;
+ }
+
+ break;
+ }
+ else if (pHdr->u8Type != VBOX_VIDEO_INFO_TYPE_NV_HEAP)
+ {
+ /** @todo why is Additions/WINNT/Graphics/Miniport/VBoxVideo. cpp pushing this to us? */
+ LogRel(("Guest adapter information contains unsupported type %d. The block has been skipped.\n", pHdr->u8Type));
+ }
+
+ pu8 += pHdr->u16Length;
+ }
+ }
+#endif /* !VBOX_WITH_HGSMI */
+}
+
+void Display::processDisplayData(void *pvVRAM, unsigned uScreenId)
+{
+ if (uScreenId >= mcMonitors)
+ {
+ LogRel(("VBoxVideo: Guest display information invalid display index %d!!!\n", uScreenId));
+ return;
+ }
+
+ /* Get the display information structure. */
+ DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
+
+ uint8_t *pu8 = (uint8_t *)pvVRAM;
+ pu8 += pFBInfo->u32Offset + pFBInfo->u32MaxFramebufferSize;
+
+ /// @todo
+ uint8_t *pu8End = pu8 + pFBInfo->u32InformationSize;
+
+ VBOXVIDEOINFOHDR *pHdr;
+
+ for (;;)
+ {
+ pHdr = (VBOXVIDEOINFOHDR *)pu8;
+ pu8 += sizeof(VBOXVIDEOINFOHDR);
+
+ if (pu8 >= pu8End)
+ {
+ LogRel(("VBoxVideo: Guest display information overflow!!!\n"));
+ break;
+ }
+
+ if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_SCREEN)
+ {
+ if (pHdr->u16Length != sizeof(VBOXVIDEOINFOSCREEN))
+ {
+ LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "SCREEN", pHdr->u16Length));
+ break;
+ }
+
+ VBOXVIDEOINFOSCREEN *pScreen = (VBOXVIDEOINFOSCREEN *)pu8;
+
+ pFBInfo->xOrigin = pScreen->xOrigin;
+ pFBInfo->yOrigin = pScreen->yOrigin;
+
+ pFBInfo->w = pScreen->u16Width;
+ pFBInfo->h = pScreen->u16Height;
+
+ LogRelFlow(("VBOX_VIDEO_INFO_TYPE_SCREEN: (%p) %d: at %d,%d, linesize 0x%X, size %dx%d, bpp %d, flags 0x%02X\n",
+ pHdr, uScreenId, pScreen->xOrigin, pScreen->yOrigin, pScreen->u32LineSize, pScreen->u16Width,
+ pScreen->u16Height, pScreen->bitsPerPixel, pScreen->u8Flags));
+
+ if (uScreenId != VBOX_VIDEO_PRIMARY_SCREEN)
+ {
+ /* Primary screen resize is eeeeeeeee by the VGA device. */
+ if (pFBInfo->fDisabled)
+ {
+ pFBInfo->fDisabled = false;
+ ::FireGuestMonitorChangedEvent(mParent->i_getEventSource(), GuestMonitorChangedEventType_Enabled, uScreenId,
+ pFBInfo->xOrigin, pFBInfo->yOrigin, pFBInfo->w, pFBInfo->h);
+ }
+
+ i_handleDisplayResize(uScreenId, pScreen->bitsPerPixel,
+ (uint8_t *)pvVRAM + pFBInfo->u32Offset,
+ pScreen->u32LineSize,
+ pScreen->u16Width, pScreen->u16Height,
+ VBVA_SCREEN_F_ACTIVE,
+ pScreen->xOrigin, pScreen->yOrigin, false);
+ }
+ }
+ else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
+ {
+ if (pHdr->u16Length != 0)
+ {
+ LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
+ break;
+ }
+
+ break;
+ }
+ else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_HOST_EVENTS)
+ {
+ if (pHdr->u16Length != sizeof(VBOXVIDEOINFOHOSTEVENTS))
+ {
+ LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "HOST_EVENTS", pHdr->u16Length));
+ break;
+ }
+
+ VBOXVIDEOINFOHOSTEVENTS *pHostEvents = (VBOXVIDEOINFOHOSTEVENTS *)pu8;
+
+ pFBInfo->pHostEvents = pHostEvents;
+
+ LogFlow(("VBOX_VIDEO_INFO_TYPE_HOSTEVENTS: (%p)\n",
+ pHostEvents));
+ }
+ else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_LINK)
+ {
+ if (pHdr->u16Length != sizeof(VBOXVIDEOINFOLINK))
+ {
+ LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "LINK", pHdr->u16Length));
+ break;
+ }
+
+ VBOXVIDEOINFOLINK *pLink = (VBOXVIDEOINFOLINK *)pu8;
+ pu8 += pLink->i32Offset;
+ }
+ else
+ {
+ LogRel(("Guest display information contains unsupported type %d\n", pHdr->u8Type));
+ }
+
+ pu8 += pHdr->u16Length;
+ }
+}
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */