/* $Id: DisplayImplLegacy.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */ /** @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-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * SPDX-License-Identifier: GPL-3.0-only */ #define LOG_GROUP LOG_GROUP_MAIN_DISPLAY #include "LoggingNew.h" #include "DisplayImpl.h" #include "ConsoleImpl.h" #include "ConsoleVRDPServer.h" #include "VMMDev.h" #include /* 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: */