VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/DisplayImplLegacy.cpp@ 94173

Last change on this file since 94173 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.7 KB
Line 
1/* $Id: DisplayImplLegacy.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VirtualBox IDisplay implementation, helpers for legacy GAs.
4 *
5 * Methods and helpers to support old Guest Additions 3.x or older.
6 * This is not used by the current Guest Additions.
7 */
8
9/*
10 * Copyright (C) 2006-2022 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
22#include "LoggingNew.h"
23
24#include "DisplayImpl.h"
25#include "ConsoleImpl.h"
26#include "ConsoleVRDPServer.h"
27#include "VMMDev.h"
28#include <VBox/VMMDev.h>
29
30/* generated header */
31#include "VBoxEvents.h"
32
33
34int videoAccelConstruct(VIDEOACCEL *pVideoAccel)
35{
36 pVideoAccel->pVbvaMemory = NULL;
37 pVideoAccel->fVideoAccelEnabled = false;
38
39 pVideoAccel->pu8VbvaPartial = NULL;
40 pVideoAccel->cbVbvaPartial = 0;
41
42 pVideoAccel->hXRoadsVideoAccel = NIL_RTSEMXROADS;
43 int rc = RTSemXRoadsCreate(&pVideoAccel->hXRoadsVideoAccel);
44 AssertRC(rc);
45
46 return rc;
47}
48
49void videoAccelDestroy(VIDEOACCEL *pVideoAccel)
50{
51 RTSemXRoadsDestroy(pVideoAccel->hXRoadsVideoAccel);
52 RT_ZERO(*pVideoAccel);
53}
54
55static unsigned mapCoordsToScreen(DISPLAYFBINFO *pInfos, unsigned cInfos, int *px, int *py, int *pw, int *ph)
56{
57 RT_NOREF(pw, ph);
58
59 DISPLAYFBINFO *pInfo = pInfos;
60 unsigned uScreenId;
61 Log9(("mapCoordsToScreen: %d,%d %dx%d\n", *px, *py, *pw, *ph));
62 for (uScreenId = 0; uScreenId < cInfos; uScreenId++, pInfo++)
63 {
64 Log9((" [%d] %d,%d %dx%d\n", uScreenId, pInfo->xOrigin, pInfo->yOrigin, pInfo->w, pInfo->h));
65 if ( (pInfo->xOrigin <= *px && *px < pInfo->xOrigin + (int)pInfo->w)
66 && (pInfo->yOrigin <= *py && *py < pInfo->yOrigin + (int)pInfo->h))
67 {
68 /* The rectangle belongs to the screen. Correct coordinates. */
69 *px -= pInfo->xOrigin;
70 *py -= pInfo->yOrigin;
71 Log9((" -> %d,%d", *px, *py));
72 break;
73 }
74 }
75 if (uScreenId == cInfos)
76 {
77 /* Map to primary screen. */
78 uScreenId = 0;
79 }
80 Log9((" scr %d\n", uScreenId));
81 return uScreenId;
82}
83
84
85typedef struct _VBVADIRTYREGION
86{
87 /* Copies of object's pointers used by vbvaRgn functions. */
88 DISPLAYFBINFO *paFramebuffers;
89 unsigned cMonitors;
90 Display *pDisplay;
91 PPDMIDISPLAYPORT pPort;
92
93 /* The rectangle that includes all dirty rectangles. */
94 RTRECT aDirtyRects[SchemaDefs::MaxGuestMonitors];
95
96} VBVADIRTYREGION;
97
98static void vbvaRgnInit(VBVADIRTYREGION *prgn, DISPLAYFBINFO *paFramebuffers, unsigned cMonitors,
99 Display *pd, PPDMIDISPLAYPORT pp)
100{
101 prgn->paFramebuffers = paFramebuffers;
102 prgn->cMonitors = cMonitors;
103 prgn->pDisplay = pd;
104 prgn->pPort = pp;
105
106 RT_ZERO(prgn->aDirtyRects);
107}
108
109static void vbvaRgnDirtyRect(VBVADIRTYREGION *prgn, unsigned uScreenId, VBVACMDHDR *phdr)
110{
111 Log9(("x = %d, y = %d, w = %d, h = %d\n", phdr->x, phdr->y, phdr->w, phdr->h));
112
113 /*
114 * Here update rectangles are accumulated to form an update area.
115 */
116 /** @todo
117 * Now the simplest method is used which builds one rectangle that
118 * includes all update areas. A bit more advanced method can be
119 * employed here. The method should be fast however.
120 */
121 if (phdr->w == 0 || phdr->h == 0)
122 {
123 /* Empty rectangle. */
124 return;
125 }
126
127 int32_t xRight = phdr->x + phdr->w;
128 int32_t yBottom = phdr->y + phdr->h;
129
130 RTRECT *pDirtyRect = &prgn->aDirtyRects[uScreenId];
131 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
132
133 if (pDirtyRect->xRight == 0)
134 {
135 /* This is the first rectangle to be added. */
136 pDirtyRect->xLeft = phdr->x;
137 pDirtyRect->yTop = phdr->y;
138 pDirtyRect->xRight = xRight;
139 pDirtyRect->yBottom = yBottom;
140 }
141 else
142 {
143 /* Adjust region coordinates. */
144 if (pDirtyRect->xLeft > phdr->x)
145 {
146 pDirtyRect->xLeft = phdr->x;
147 }
148
149 if (pDirtyRect->yTop > phdr->y)
150 {
151 pDirtyRect->yTop = phdr->y;
152 }
153
154 if (pDirtyRect->xRight < xRight)
155 {
156 pDirtyRect->xRight = xRight;
157 }
158
159 if (pDirtyRect->yBottom < yBottom)
160 {
161 pDirtyRect->yBottom = yBottom;
162 }
163 }
164
165 if (pFBInfo->fDefaultFormat)
166 {
167 /// @todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
168 prgn->pPort->pfnUpdateDisplayRect(prgn->pPort, phdr->x, phdr->y, phdr->w, phdr->h);
169 prgn->pDisplay->i_handleDisplayUpdate(uScreenId, phdr->x, phdr->y, phdr->w, phdr->h);
170 }
171
172 return;
173}
174
175static void vbvaRgnUpdateFramebuffer(VBVADIRTYREGION *prgn, unsigned uScreenId)
176{
177 RTRECT *pDirtyRect = &prgn->aDirtyRects[uScreenId];
178 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
179
180 uint32_t w = pDirtyRect->xRight - pDirtyRect->xLeft;
181 uint32_t h = pDirtyRect->yBottom - pDirtyRect->yTop;
182
183 if (!pFBInfo->fDefaultFormat && w != 0 && h != 0)
184 {
185 /// @todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
186 prgn->pPort->pfnUpdateDisplayRect(prgn->pPort, pDirtyRect->xLeft, pDirtyRect->yTop, w, h);
187 prgn->pDisplay->i_handleDisplayUpdate(uScreenId, pDirtyRect->xLeft, pDirtyRect->yTop, w, h);
188 }
189}
190
191void i_vbvaSetMemoryFlags(VBVAMEMORY *pVbvaMemory,
192 bool fVideoAccelEnabled,
193 bool fVideoAccelVRDP,
194 uint32_t fu32SupportedOrders,
195 DISPLAYFBINFO *paFBInfos,
196 unsigned cFBInfos)
197{
198 if (pVbvaMemory)
199 {
200 /* This called only on changes in mode. So reset VRDP always. */
201 uint32_t fu32Flags = VBVA_F_MODE_VRDP_RESET;
202
203 if (fVideoAccelEnabled)
204 {
205 fu32Flags |= VBVA_F_MODE_ENABLED;
206
207 if (fVideoAccelVRDP)
208 {
209 fu32Flags |= VBVA_F_MODE_VRDP | VBVA_F_MODE_VRDP_ORDER_MASK;
210
211 pVbvaMemory->fu32SupportedOrders = fu32SupportedOrders;
212 }
213 }
214
215 pVbvaMemory->fu32ModeFlags = fu32Flags;
216 }
217
218 unsigned uScreenId;
219 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
220 {
221 if (paFBInfos[uScreenId].pHostEvents)
222 {
223 paFBInfos[uScreenId].pHostEvents->fu32Events |= VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
224 }
225 }
226}
227
228bool Display::i_VideoAccelAllowed(void)
229{
230 return true;
231}
232
233int videoAccelEnterVGA(VIDEOACCEL *pVideoAccel)
234{
235 return RTSemXRoadsNSEnter(pVideoAccel->hXRoadsVideoAccel);
236}
237
238void videoAccelLeaveVGA(VIDEOACCEL *pVideoAccel)
239{
240 RTSemXRoadsNSLeave(pVideoAccel->hXRoadsVideoAccel);
241}
242
243int videoAccelEnterVMMDev(VIDEOACCEL *pVideoAccel)
244{
245 return RTSemXRoadsEWEnter(pVideoAccel->hXRoadsVideoAccel);
246}
247
248void videoAccelLeaveVMMDev(VIDEOACCEL *pVideoAccel)
249{
250 RTSemXRoadsEWLeave(pVideoAccel->hXRoadsVideoAccel);
251}
252
253/**
254 * @thread EMT
255 */
256int Display::i_VideoAccelEnable(bool fEnable, VBVAMEMORY *pVbvaMemory, PPDMIDISPLAYPORT pUpPort)
257{
258 int rc;
259 LogRelFlowFunc(("fEnable = %d\n", fEnable));
260
261 rc = i_videoAccelEnable(fEnable, pVbvaMemory, pUpPort);
262
263 LogRelFlowFunc(("%Rrc.\n", rc));
264 return rc;
265}
266
267int Display::i_videoAccelEnable(bool fEnable, VBVAMEMORY *pVbvaMemory, PPDMIDISPLAYPORT pUpPort)
268{
269 int rc = VINF_SUCCESS;
270
271 VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
272
273 /* Called each time the guest wants to use acceleration,
274 * or when the VGA device disables acceleration,
275 * or when restoring the saved state with accel enabled.
276 *
277 * VGA device disables acceleration on each video mode change
278 * and on reset.
279 *
280 * Guest enabled acceleration at will. And it has to enable
281 * acceleration after a mode change.
282 */
283 LogRelFlowFunc(("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
284 pVideoAccel->fVideoAccelEnabled, fEnable, pVbvaMemory));
285
286 /* Strictly check parameters. Callers must not pass anything in the case. */
287 Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));
288
289 if (!i_VideoAccelAllowed ())
290 return VERR_NOT_SUPPORTED;
291
292 /* Check that current status is not being changed */
293 if (pVideoAccel->fVideoAccelEnabled == fEnable)
294 return rc;
295
296 if (pVideoAccel->fVideoAccelEnabled)
297 {
298 /* Process any pending orders and empty the VBVA ring buffer. */
299 i_videoAccelFlush (pUpPort);
300 }
301
302 if (!fEnable && pVideoAccel->pVbvaMemory)
303 pVideoAccel->pVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;
304
305 if (fEnable)
306 {
307 /* Process any pending VGA device changes, resize. */
308 pUpPort->pfnUpdateDisplayAll(pUpPort, /* fFailOnResize = */ false);
309 }
310
311 /* Protect the videoaccel state transition. */
312 RTCritSectEnter(&mVideoAccelLock);
313
314 if (fEnable)
315 {
316 /* Initialize the hardware memory. */
317 i_vbvaSetMemoryFlags(pVbvaMemory, true, mfVideoAccelVRDP,
318 mfu32SupportedOrders, maFramebuffers, mcMonitors);
319 pVbvaMemory->off32Data = 0;
320 pVbvaMemory->off32Free = 0;
321
322 memset(pVbvaMemory->aRecords, 0, sizeof(pVbvaMemory->aRecords));
323 pVbvaMemory->indexRecordFirst = 0;
324 pVbvaMemory->indexRecordFree = 0;
325
326 pVideoAccel->pVbvaMemory = pVbvaMemory;
327 pVideoAccel->fVideoAccelEnabled = true;
328
329 LogRel(("VBVA: Enabled.\n"));
330 }
331 else
332 {
333 pVideoAccel->pVbvaMemory = NULL;
334 pVideoAccel->fVideoAccelEnabled = false;
335
336 LogRel(("VBVA: Disabled.\n"));
337 }
338
339 RTCritSectLeave(&mVideoAccelLock);
340
341 if (!fEnable)
342 {
343 pUpPort->pfnUpdateDisplayAll(pUpPort, /* fFailOnResize = */ false);
344 }
345
346 /* Notify the VMMDev, which saves VBVA status in the saved state,
347 * and needs to know current status.
348 */
349 VMMDev *pVMMDev = mParent->i_getVMMDev();
350 if (pVMMDev)
351 {
352 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
353 if (pVMMDevPort)
354 pVMMDevPort->pfnVBVAChange(pVMMDevPort, fEnable);
355 }
356
357 LogRelFlowFunc(("%Rrc.\n", rc));
358 return rc;
359}
360
361static bool i_vbvaVerifyRingBuffer(VBVAMEMORY *pVbvaMemory)
362{
363 RT_NOREF(pVbvaMemory);
364 return true;
365}
366
367static void i_vbvaFetchBytes(VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t cbDst)
368{
369 if (cbDst >= VBVA_RING_BUFFER_SIZE)
370 {
371 AssertMsgFailed(("cbDst = 0x%08X, ring buffer size 0x%08X\n", cbDst, VBVA_RING_BUFFER_SIZE));
372 return;
373 }
374
375 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
376 uint8_t *src = &pVbvaMemory->au8RingBuffer[pVbvaMemory->off32Data];
377 int32_t i32Diff = cbDst - u32BytesTillBoundary;
378
379 if (i32Diff <= 0)
380 {
381 /* Chunk will not cross buffer boundary. */
382 memcpy (pu8Dst, src, cbDst);
383 }
384 else
385 {
386 /* Chunk crosses buffer boundary. */
387 memcpy(pu8Dst, src, u32BytesTillBoundary);
388 memcpy(pu8Dst + u32BytesTillBoundary, &pVbvaMemory->au8RingBuffer[0], i32Diff);
389 }
390
391 /* Advance data offset. */
392 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbDst) % VBVA_RING_BUFFER_SIZE;
393
394 return;
395}
396
397
398static bool i_vbvaPartialRead(uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
399{
400 uint8_t *pu8New;
401
402 LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
403 *ppu8, *pcb, cbRecord));
404
405 if (*ppu8)
406 {
407 Assert (*pcb);
408 pu8New = (uint8_t *)RTMemRealloc(*ppu8, cbRecord);
409 }
410 else
411 {
412 Assert (!*pcb);
413 pu8New = (uint8_t *)RTMemAlloc(cbRecord);
414 }
415
416 if (!pu8New)
417 {
418 /* Memory allocation failed, fail the function. */
419 Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
420 cbRecord));
421
422 if (*ppu8)
423 {
424 RTMemFree(*ppu8);
425 }
426
427 *ppu8 = NULL;
428 *pcb = 0;
429
430 return false;
431 }
432
433 /* Fetch data from the ring buffer. */
434 i_vbvaFetchBytes(pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
435
436 *ppu8 = pu8New;
437 *pcb = cbRecord;
438
439 return true;
440}
441
442/* For contiguous chunks just return the address in the buffer.
443 * For crossing boundary - allocate a buffer from heap.
444 */
445static bool i_vbvaFetchCmd(VIDEOACCEL *pVideoAccel, VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
446{
447 VBVAMEMORY *pVbvaMemory = pVideoAccel->pVbvaMemory;
448
449 uint32_t indexRecordFirst = pVbvaMemory->indexRecordFirst;
450 uint32_t indexRecordFree = pVbvaMemory->indexRecordFree;
451
452#ifdef DEBUG_sunlover
453 LogFlowFunc(("first = %d, free = %d\n",
454 indexRecordFirst, indexRecordFree));
455#endif /* DEBUG_sunlover */
456
457 if (!i_vbvaVerifyRingBuffer(pVbvaMemory))
458 {
459 return false;
460 }
461
462 if (indexRecordFirst == indexRecordFree)
463 {
464 /* No records to process. Return without assigning output variables. */
465 return true;
466 }
467
468 uint32_t cbRecordCurrent = ASMAtomicReadU32(&pVbvaMemory->aRecords[indexRecordFirst].cbRecord);
469
470#ifdef DEBUG_sunlover
471 LogFlowFunc(("cbRecord = 0x%08X\n", cbRecordCurrent));
472#endif /* DEBUG_sunlover */
473
474 uint32_t cbRecord = cbRecordCurrent & ~VBVA_F_RECORD_PARTIAL;
475
476 if (pVideoAccel->cbVbvaPartial)
477 {
478 /* There is a partial read in process. Continue with it. */
479
480 Assert(pVideoAccel->pu8VbvaPartial);
481
482 LogFlowFunc(("continue partial record cbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
483 pVideoAccel->cbVbvaPartial, cbRecordCurrent, indexRecordFirst, indexRecordFree));
484
485 if (cbRecord > pVideoAccel->cbVbvaPartial)
486 {
487 /* New data has been added to the record. */
488 if (!i_vbvaPartialRead(&pVideoAccel->pu8VbvaPartial, &pVideoAccel->cbVbvaPartial, cbRecord, pVbvaMemory))
489 {
490 return false;
491 }
492 }
493
494 if (!(cbRecordCurrent & VBVA_F_RECORD_PARTIAL))
495 {
496 /* The record is completed by guest. Return it to the caller. */
497 *ppHdr = (VBVACMDHDR *)pVideoAccel->pu8VbvaPartial;
498 *pcbCmd = pVideoAccel->cbVbvaPartial;
499
500 pVideoAccel->pu8VbvaPartial = NULL;
501 pVideoAccel->cbVbvaPartial = 0;
502
503 /* Advance the record index. */
504 pVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
505
506#ifdef DEBUG_sunlover
507 LogFlowFunc(("partial done ok, data = %d, free = %d\n",
508 pVbvaMemory->off32Data, pVbvaMemory->off32Free));
509#endif /* DEBUG_sunlover */
510 }
511
512 return true;
513 }
514
515 /* A new record need to be processed. */
516 if (cbRecordCurrent & VBVA_F_RECORD_PARTIAL)
517 {
518 /* Current record is being written by guest. '=' is important here. */
519 if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
520 {
521 /* Partial read must be started. */
522 if (!i_vbvaPartialRead(&pVideoAccel->pu8VbvaPartial, &pVideoAccel->cbVbvaPartial, cbRecord, pVbvaMemory))
523 {
524 return false;
525 }
526
527 LogFlowFunc(("started partial record cbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
528 pVideoAccel->cbVbvaPartial, cbRecordCurrent, indexRecordFirst, indexRecordFree));
529 }
530
531 return true;
532 }
533
534 /* Current record is complete. If it is not empty, process it. */
535 if (cbRecord)
536 {
537 /* The size of largest contiguous chunk in the ring biffer. */
538 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
539
540 /* The ring buffer pointer. */
541 uint8_t *au8RingBuffer = &pVbvaMemory->au8RingBuffer[0];
542
543 /* The pointer to data in the ring buffer. */
544 uint8_t *src = &au8RingBuffer[pVbvaMemory->off32Data];
545
546 /* Fetch or point the data. */
547 if (u32BytesTillBoundary >= cbRecord)
548 {
549 /* The command does not cross buffer boundary. Return address in the buffer. */
550 *ppHdr = (VBVACMDHDR *)src;
551
552 /* Advance data offset. */
553 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
554 }
555 else
556 {
557 /* The command crosses buffer boundary. Rare case, so not optimized. */
558 uint8_t *dst = (uint8_t *)RTMemAlloc(cbRecord);
559
560 if (!dst)
561 {
562 LogRelFlowFunc(("could not allocate %d bytes from heap!!!\n", cbRecord));
563 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
564 return false;
565 }
566
567 i_vbvaFetchBytes(pVbvaMemory, dst, cbRecord);
568
569 *ppHdr = (VBVACMDHDR *)dst;
570
571#ifdef DEBUG_sunlover
572 LogFlowFunc(("Allocated from heap %p\n", dst));
573#endif /* DEBUG_sunlover */
574 }
575 }
576
577 *pcbCmd = cbRecord;
578
579 /* Advance the record index. */
580 pVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
581
582#ifdef DEBUG_sunlover
583 LogFlowFunc(("done ok, data = %d, free = %d\n",
584 pVbvaMemory->off32Data, pVbvaMemory->off32Free));
585#endif /* DEBUG_sunlover */
586
587 return true;
588}
589
590static void i_vbvaReleaseCmd(VIDEOACCEL *pVideoAccel, VBVACMDHDR *pHdr, int32_t cbCmd)
591{
592 RT_NOREF(cbCmd);
593 uint8_t *au8RingBuffer = pVideoAccel->pVbvaMemory->au8RingBuffer;
594
595 if ( (uint8_t *)pHdr >= au8RingBuffer
596 && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
597 {
598 /* The pointer is inside ring buffer. Must be continuous chunk. */
599 Assert(VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
600
601 /* Do nothing. */
602
603 Assert(!pVideoAccel->pu8VbvaPartial && pVideoAccel->cbVbvaPartial == 0);
604 }
605 else
606 {
607 /* The pointer is outside. It is then an allocated copy. */
608
609#ifdef DEBUG_sunlover
610 LogFlowFunc(("Free heap %p\n", pHdr));
611#endif /* DEBUG_sunlover */
612
613 if ((uint8_t *)pHdr == pVideoAccel->pu8VbvaPartial)
614 {
615 pVideoAccel->pu8VbvaPartial = NULL;
616 pVideoAccel->cbVbvaPartial = 0;
617 }
618 else
619 {
620 Assert(!pVideoAccel->pu8VbvaPartial && pVideoAccel->cbVbvaPartial == 0);
621 }
622
623 RTMemFree(pHdr);
624 }
625
626 return;
627}
628
629
630/**
631 * Called regularly on the DisplayRefresh timer.
632 * Also on behalf of guest, when the ring buffer is full.
633 *
634 * @thread EMT
635 */
636void Display::i_VideoAccelFlush(PPDMIDISPLAYPORT pUpPort)
637{
638 int rc = i_videoAccelFlush(pUpPort);
639 if (RT_FAILURE(rc))
640 {
641 /* Disable on errors. */
642 i_videoAccelEnable(false, NULL, pUpPort);
643 }
644}
645
646int Display::i_videoAccelFlush(PPDMIDISPLAYPORT pUpPort)
647{
648 VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
649 VBVAMEMORY *pVbvaMemory = pVideoAccel->pVbvaMemory;
650
651#ifdef DEBUG_sunlover_2
652 LogFlowFunc(("fVideoAccelEnabled = %d\n", pVideoAccel->fVideoAccelEnabled));
653#endif /* DEBUG_sunlover_2 */
654
655 if (!pVideoAccel->fVideoAccelEnabled)
656 {
657 Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
658 return VINF_SUCCESS;
659 }
660
661 /* Here VBVA is enabled and we have the accelerator memory pointer. */
662 Assert(pVbvaMemory);
663
664#ifdef DEBUG_sunlover_2
665 LogFlowFunc(("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
666 pVbvaMemory->indexRecordFirst, pVbvaMemory->indexRecordFree,
667 pVbvaMemory->off32Data, pVbvaMemory->off32Free));
668#endif /* DEBUG_sunlover_2 */
669
670 /* Quick check for "nothing to update" case. */
671 if (pVbvaMemory->indexRecordFirst == pVbvaMemory->indexRecordFree)
672 {
673 return VINF_SUCCESS;
674 }
675
676 /* Process the ring buffer */
677 unsigned uScreenId;
678
679 /* Initialize dirty rectangles accumulator. */
680 VBVADIRTYREGION rgn;
681 vbvaRgnInit(&rgn, maFramebuffers, mcMonitors, this, pUpPort);
682
683 for (;;)
684 {
685 VBVACMDHDR *phdr = NULL;
686 uint32_t cbCmd = UINT32_MAX;
687
688 /* Fetch the command data. */
689 if (!i_vbvaFetchCmd(pVideoAccel, &phdr, &cbCmd))
690 {
691 Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
692 pVbvaMemory->off32Data, pVbvaMemory->off32Free));
693 return VERR_INVALID_STATE;
694 }
695
696 if (cbCmd == uint32_t(~0))
697 {
698 /* No more commands yet in the queue. */
699#ifdef DEBUG_sunlover
700 LogFlowFunc(("no command\n"));
701#endif /* DEBUG_sunlover */
702 break;
703 }
704
705 if (cbCmd != 0)
706 {
707#ifdef DEBUG_sunlover
708 LogFlowFunc(("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
709 cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
710#endif /* DEBUG_sunlover */
711
712 VBVACMDHDR hdrSaved = *phdr;
713
714 int x = phdr->x;
715 int y = phdr->y;
716 int w = phdr->w;
717 int h = phdr->h;
718
719 uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
720
721 phdr->x = (int16_t)x;
722 phdr->y = (int16_t)y;
723 phdr->w = (uint16_t)w;
724 phdr->h = (uint16_t)h;
725
726 /* Handle the command.
727 *
728 * Guest is responsible for updating the guest video memory.
729 * The Windows guest does all drawing using Eng*.
730 *
731 * For local output, only dirty rectangle information is used
732 * to update changed areas.
733 *
734 * Dirty rectangles are accumulated to exclude overlapping updates and
735 * group small updates to a larger one.
736 */
737
738 /* Accumulate the update. */
739 vbvaRgnDirtyRect(&rgn, uScreenId, phdr);
740
741 /* Forward the command to VRDP server. */
742 mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, phdr, cbCmd);
743
744 *phdr = hdrSaved;
745 }
746
747 i_vbvaReleaseCmd(pVideoAccel, phdr, cbCmd);
748 }
749
750 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
751 {
752 /* Draw the framebuffer. */
753 vbvaRgnUpdateFramebuffer(&rgn, uScreenId);
754 }
755 return VINF_SUCCESS;
756}
757
758int Display::i_videoAccelRefreshProcess(PPDMIDISPLAYPORT pUpPort)
759{
760 int rc = VWRN_INVALID_STATE; /* Default is to do a display update in VGA device. */
761
762 VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
763
764 videoAccelEnterVGA(pVideoAccel);
765
766 if (pVideoAccel->fVideoAccelEnabled)
767 {
768 Assert(pVideoAccel->pVbvaMemory);
769 rc = i_videoAccelFlush(pUpPort);
770 if (RT_FAILURE(rc))
771 {
772 /* Disable on errors. */
773 i_videoAccelEnable(false, NULL, pUpPort);
774 rc = VWRN_INVALID_STATE; /* Do a display update in VGA device. */
775 }
776 else
777 {
778 rc = VINF_SUCCESS;
779 }
780 }
781
782 videoAccelLeaveVGA(pVideoAccel);
783
784 return rc;
785}
786
787void Display::processAdapterData(void *pvVRAM, uint32_t u32VRAMSize)
788{
789 RT_NOREF(u32VRAMSize);
790 if (pvVRAM == NULL)
791 {
792 unsigned i;
793 for (i = 0; i < mcMonitors; i++)
794 {
795 DISPLAYFBINFO *pFBInfo = &maFramebuffers[i];
796
797 pFBInfo->u32Offset = 0;
798 pFBInfo->u32MaxFramebufferSize = 0;
799 pFBInfo->u32InformationSize = 0;
800 }
801 }
802#ifndef VBOX_WITH_HGSMI
803 else
804 {
805 uint8_t *pu8 = (uint8_t *)pvVRAM;
806 pu8 += u32VRAMSize - VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
807
808 /// @todo
809 uint8_t *pu8End = pu8 + VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
810
811 VBOXVIDEOINFOHDR *pHdr;
812
813 for (;;)
814 {
815 pHdr = (VBOXVIDEOINFOHDR *)pu8;
816 pu8 += sizeof(VBOXVIDEOINFOHDR);
817
818 if (pu8 >= pu8End)
819 {
820 LogRel(("VBoxVideo: Guest adapter information overflow!!!\n"));
821 break;
822 }
823
824 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_DISPLAY)
825 {
826 if (pHdr->u16Length != sizeof(VBOXVIDEOINFODISPLAY))
827 {
828 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "DISPLAY", pHdr->u16Length));
829 break;
830 }
831
832 VBOXVIDEOINFODISPLAY *pDisplay = (VBOXVIDEOINFODISPLAY *)pu8;
833
834 if (pDisplay->u32Index >= mcMonitors)
835 {
836 LogRel(("VBoxVideo: Guest adapter information invalid display index %d!!!\n", pDisplay->u32Index));
837 break;
838 }
839
840 DISPLAYFBINFO *pFBInfo = &maFramebuffers[pDisplay->u32Index];
841
842 pFBInfo->u32Offset = pDisplay->u32Offset;
843 pFBInfo->u32MaxFramebufferSize = pDisplay->u32FramebufferSize;
844 pFBInfo->u32InformationSize = pDisplay->u32InformationSize;
845
846 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_DISPLAY: %d: at 0x%08X, size 0x%08X, info 0x%08X\n", pDisplay->u32Index,
847 pDisplay->u32Offset, pDisplay->u32FramebufferSize, pDisplay->u32InformationSize));
848 }
849 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_QUERY_CONF32)
850 {
851 if (pHdr->u16Length != sizeof(VBOXVIDEOINFOQUERYCONF32))
852 {
853 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "CONF32", pHdr->u16Length));
854 break;
855 }
856
857 VBOXVIDEOINFOQUERYCONF32 *pConf32 = (VBOXVIDEOINFOQUERYCONF32 *)pu8;
858
859 switch (pConf32->u32Index)
860 {
861 case VBOX_VIDEO_QCI32_MONITOR_COUNT:
862 {
863 pConf32->u32Value = mcMonitors;
864 } break;
865
866 case VBOX_VIDEO_QCI32_OFFSCREEN_HEAP_SIZE:
867 {
868 /** @todo make configurable. */
869 pConf32->u32Value = _1M;
870 } break;
871
872 default:
873 LogRel(("VBoxVideo: CONF32 %d not supported!!! Skipping.\n", pConf32->u32Index));
874 }
875 }
876 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
877 {
878 if (pHdr->u16Length != 0)
879 {
880 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
881 break;
882 }
883
884 break;
885 }
886 else if (pHdr->u8Type != VBOX_VIDEO_INFO_TYPE_NV_HEAP)
887 {
888 /** @todo why is Additions/WINNT/Graphics/Miniport/VBoxVideo. cpp pushing this to us? */
889 LogRel(("Guest adapter information contains unsupported type %d. The block has been skipped.\n", pHdr->u8Type));
890 }
891
892 pu8 += pHdr->u16Length;
893 }
894 }
895#endif /* !VBOX_WITH_HGSMI */
896}
897
898void Display::processDisplayData(void *pvVRAM, unsigned uScreenId)
899{
900 if (uScreenId >= mcMonitors)
901 {
902 LogRel(("VBoxVideo: Guest display information invalid display index %d!!!\n", uScreenId));
903 return;
904 }
905
906 /* Get the display information structure. */
907 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
908
909 uint8_t *pu8 = (uint8_t *)pvVRAM;
910 pu8 += pFBInfo->u32Offset + pFBInfo->u32MaxFramebufferSize;
911
912 /// @todo
913 uint8_t *pu8End = pu8 + pFBInfo->u32InformationSize;
914
915 VBOXVIDEOINFOHDR *pHdr;
916
917 for (;;)
918 {
919 pHdr = (VBOXVIDEOINFOHDR *)pu8;
920 pu8 += sizeof(VBOXVIDEOINFOHDR);
921
922 if (pu8 >= pu8End)
923 {
924 LogRel(("VBoxVideo: Guest display information overflow!!!\n"));
925 break;
926 }
927
928 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_SCREEN)
929 {
930 if (pHdr->u16Length != sizeof(VBOXVIDEOINFOSCREEN))
931 {
932 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "SCREEN", pHdr->u16Length));
933 break;
934 }
935
936 VBOXVIDEOINFOSCREEN *pScreen = (VBOXVIDEOINFOSCREEN *)pu8;
937
938 pFBInfo->xOrigin = pScreen->xOrigin;
939 pFBInfo->yOrigin = pScreen->yOrigin;
940
941 pFBInfo->w = pScreen->u16Width;
942 pFBInfo->h = pScreen->u16Height;
943
944 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_SCREEN: (%p) %d: at %d,%d, linesize 0x%X, size %dx%d, bpp %d, flags 0x%02X\n",
945 pHdr, uScreenId, pScreen->xOrigin, pScreen->yOrigin, pScreen->u32LineSize, pScreen->u16Width,
946 pScreen->u16Height, pScreen->bitsPerPixel, pScreen->u8Flags));
947
948 if (uScreenId != VBOX_VIDEO_PRIMARY_SCREEN)
949 {
950 /* Primary screen resize is eeeeeeeee by the VGA device. */
951 if (pFBInfo->fDisabled)
952 {
953 pFBInfo->fDisabled = false;
954 ::FireGuestMonitorChangedEvent(mParent->i_getEventSource(), GuestMonitorChangedEventType_Enabled, uScreenId,
955 pFBInfo->xOrigin, pFBInfo->yOrigin, pFBInfo->w, pFBInfo->h);
956 }
957
958 i_handleDisplayResize(uScreenId, pScreen->bitsPerPixel,
959 (uint8_t *)pvVRAM + pFBInfo->u32Offset,
960 pScreen->u32LineSize,
961 pScreen->u16Width, pScreen->u16Height,
962 VBVA_SCREEN_F_ACTIVE,
963 pScreen->xOrigin, pScreen->yOrigin, false);
964 }
965 }
966 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
967 {
968 if (pHdr->u16Length != 0)
969 {
970 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
971 break;
972 }
973
974 break;
975 }
976 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_HOST_EVENTS)
977 {
978 if (pHdr->u16Length != sizeof(VBOXVIDEOINFOHOSTEVENTS))
979 {
980 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "HOST_EVENTS", pHdr->u16Length));
981 break;
982 }
983
984 VBOXVIDEOINFOHOSTEVENTS *pHostEvents = (VBOXVIDEOINFOHOSTEVENTS *)pu8;
985
986 pFBInfo->pHostEvents = pHostEvents;
987
988 LogFlow(("VBOX_VIDEO_INFO_TYPE_HOSTEVENTS: (%p)\n",
989 pHostEvents));
990 }
991 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_LINK)
992 {
993 if (pHdr->u16Length != sizeof(VBOXVIDEOINFOLINK))
994 {
995 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "LINK", pHdr->u16Length));
996 break;
997 }
998
999 VBOXVIDEOINFOLINK *pLink = (VBOXVIDEOINFOLINK *)pu8;
1000 pu8 += pLink->i32Offset;
1001 }
1002 else
1003 {
1004 LogRel(("Guest display information contains unsupported type %d\n", pHdr->u8Type));
1005 }
1006
1007 pu8 += pHdr->u16Length;
1008 }
1009}
1010
1011/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette