VirtualBox

source: vbox/trunk/src/VBox/Main/DisplayImpl.cpp@ 25414

Last change on this file since 25414 was 25310, checked in by vboxsync, 14 years ago

Main: lock validator, first batch: implement per-thread stack to trace locking (disabled by default, use VBOX_WITH_LOCK_VALIDATOR, but that WILL FAIL presently)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 110.0 KB
Line 
1/* $Id: DisplayImpl.cpp 25310 2009-12-10 17:06:44Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#include "DisplayImpl.h"
25#include "ConsoleImpl.h"
26#include "ConsoleVRDPServer.h"
27#include "VMMDev.h"
28
29#include "Logging.h"
30
31#include <iprt/semaphore.h>
32#include <iprt/thread.h>
33#include <iprt/asm.h>
34
35#include <VBox/pdmdrv.h>
36#ifdef DEBUG /* for VM_ASSERT_EMT(). */
37# include <VBox/vm.h>
38#endif
39
40#ifdef VBOX_WITH_VIDEOHWACCEL
41# include <VBox/VBoxVideo.h>
42#endif
43
44#include <VBox/com/array.h>
45#include <png.h>
46
47/**
48 * Display driver instance data.
49 */
50typedef struct DRVMAINDISPLAY
51{
52 /** Pointer to the display object. */
53 Display *pDisplay;
54 /** Pointer to the driver instance structure. */
55 PPDMDRVINS pDrvIns;
56 /** Pointer to the keyboard port interface of the driver/device above us. */
57 PPDMIDISPLAYPORT pUpPort;
58 /** Our display connector interface. */
59 PDMIDISPLAYCONNECTOR Connector;
60#if defined(VBOX_WITH_VIDEOHWACCEL)
61 /** VBVA callbacks */
62 PPDMDDISPLAYVBVACALLBACKS pVBVACallbacks;
63#endif
64} DRVMAINDISPLAY, *PDRVMAINDISPLAY;
65
66/** Converts PDMIDISPLAYCONNECTOR pointer to a DRVMAINDISPLAY pointer. */
67#define PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface) ( (PDRVMAINDISPLAY) ((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINDISPLAY, Connector)) )
68
69#ifdef DEBUG_sunlover
70static STAMPROFILE StatDisplayRefresh;
71static int stam = 0;
72#endif /* DEBUG_sunlover */
73
74// constructor / destructor
75/////////////////////////////////////////////////////////////////////////////
76
77DEFINE_EMPTY_CTOR_DTOR (Display)
78
79HRESULT Display::FinalConstruct()
80{
81 mpVbvaMemory = NULL;
82 mfVideoAccelEnabled = false;
83 mfVideoAccelVRDP = false;
84 mfu32SupportedOrders = 0;
85 mcVideoAccelVRDPRefs = 0;
86
87 mpPendingVbvaMemory = NULL;
88 mfPendingVideoAccelEnable = false;
89
90 mfMachineRunning = false;
91
92 mpu8VbvaPartial = NULL;
93 mcbVbvaPartial = 0;
94
95 mpDrv = NULL;
96 mpVMMDev = NULL;
97 mfVMMDevInited = false;
98
99 mLastAddress = NULL;
100 mLastBytesPerLine = 0;
101 mLastBitsPerPixel = 0,
102 mLastWidth = 0;
103 mLastHeight = 0;
104
105#ifdef VBOX_WITH_OLD_VBVA_LOCK
106 int rc = RTCritSectInit (&mVBVALock);
107 AssertRC (rc);
108 mfu32PendingVideoAccelDisable = false;
109#endif /* VBOX_WITH_OLD_VBVA_LOCK */
110
111#ifdef VBOX_WITH_HGSMI
112 mu32UpdateVBVAFlags = 0;
113#endif
114
115 return S_OK;
116}
117
118void Display::FinalRelease()
119{
120 uninit();
121
122#ifdef VBOX_WITH_OLD_VBVA_LOCK
123 if (RTCritSectIsInitialized (&mVBVALock))
124 {
125 RTCritSectDelete (&mVBVALock);
126 memset (&mVBVALock, 0, sizeof (mVBVALock));
127 }
128#endif /* VBOX_WITH_OLD_VBVA_LOCK */
129}
130
131// public initializer/uninitializer for internal purposes only
132/////////////////////////////////////////////////////////////////////////////
133
134#define sSSMDisplayScreenshotVer 0x00010001
135#define sSSMDisplayVer 0x00010001
136
137#define kMaxSizePNG 1024
138#define kMaxSizeThumbnail 64
139
140/**
141 * Save thumbnail and screenshot of the guest screen.
142 */
143static int displayMakeThumbnail(uint8_t *pu8Data, uint32_t cx, uint32_t cy,
144 uint8_t **ppu8Thumbnail, uint32_t *pcbThumbnail, uint32_t *pcxThumbnail, uint32_t *pcyThumbnail)
145{
146 int rc = VINF_SUCCESS;
147
148 uint8_t *pu8Thumbnail = NULL;
149 uint32_t cbThumbnail = 0;
150 uint32_t cxThumbnail = 0;
151 uint32_t cyThumbnail = 0;
152
153 if (cx > cy)
154 {
155 cxThumbnail = kMaxSizeThumbnail;
156 cyThumbnail = (kMaxSizeThumbnail * cy) / cx;
157 }
158 else
159 {
160 cyThumbnail = kMaxSizeThumbnail;
161 cxThumbnail = (kMaxSizeThumbnail * cx) / cy;
162 }
163
164 LogFlowFunc(("%dx%d -> %dx%d\n", cx, cy, cxThumbnail, cyThumbnail));
165
166 cbThumbnail = cxThumbnail * 4 * cyThumbnail;
167 pu8Thumbnail = (uint8_t *)RTMemAlloc(cbThumbnail);
168
169 if (pu8Thumbnail)
170 {
171 uint8_t *dst = pu8Thumbnail;
172 uint8_t *src = pu8Data;
173 int dstX = 0;
174 int dstY = 0;
175 int srcX = 0;
176 int srcY = 0;
177 int dstW = cxThumbnail;
178 int dstH = cyThumbnail;
179 int srcW = cx;
180 int srcH = cy;
181 gdImageCopyResampled (dst,
182 src,
183 dstX, dstY,
184 srcX, srcY,
185 dstW, dstH, srcW, srcH);
186
187 *ppu8Thumbnail = pu8Thumbnail;
188 *pcbThumbnail = cbThumbnail;
189 *pcxThumbnail = cxThumbnail;
190 *pcyThumbnail = cyThumbnail;
191 }
192 else
193 {
194 rc = VERR_NO_MEMORY;
195 }
196
197 return rc;
198}
199
200typedef struct PNGWriteCtx
201{
202 uint8_t *pu8PNG;
203 uint32_t cbPNG;
204 uint32_t cbAllocated;
205 int rc;
206} PNGWriteCtx;
207
208static void PNGAPI png_write_data_fn(png_structp png_ptr, png_bytep p, png_size_t cb)
209{
210 PNGWriteCtx *pCtx = (PNGWriteCtx *)png_get_io_ptr(png_ptr);
211 LogFlowFunc(("png_ptr %p, p %p, cb %d, pCtx %p\n", png_ptr, p, cb, pCtx));
212
213 if (pCtx && RT_SUCCESS(pCtx->rc))
214 {
215 if (pCtx->cbAllocated - pCtx->cbPNG < cb)
216 {
217 uint32_t cbNew = pCtx->cbPNG + (uint32_t)cb;
218 cbNew = RT_ALIGN_32(cbNew, 4096) + 4096;
219
220 void *pNew = RTMemRealloc(pCtx->pu8PNG, cbNew);
221 if (!pNew)
222 {
223 pCtx->rc = VERR_NO_MEMORY;
224 return;
225 }
226
227 pCtx->pu8PNG = (uint8_t *)pNew;
228 pCtx->cbAllocated = cbNew;
229 }
230
231 memcpy(pCtx->pu8PNG + pCtx->cbPNG, p, cb);
232 pCtx->cbPNG += cb;
233 }
234}
235
236static void PNGAPI png_output_flush_fn(png_structp png_ptr)
237{
238 NOREF(png_ptr);
239 /* Do nothing. */
240}
241
242static int displayMakePNG(uint8_t *pu8Data, uint32_t cx, uint32_t cy,
243 uint8_t **ppu8PNG, uint32_t *pcbPNG, uint32_t *pcxPNG, uint32_t *pcyPNG)
244{
245 int rc = VINF_SUCCESS;
246
247 uint8_t * volatile pu8Bitmap = NULL; /* gcc setjmp warning */
248 uint32_t volatile cbBitmap = 0; /* gcc setjmp warning */
249 uint32_t volatile cxBitmap = 0; /* gcc setjmp warning */
250 uint32_t volatile cyBitmap = 0; /* gcc setjmp warning */
251
252 if (cx < kMaxSizePNG && cy < kMaxSizePNG)
253 {
254 /* Save unscaled screenshot. */
255 pu8Bitmap = pu8Data;
256 cbBitmap = cx * 4 * cy;
257 cxBitmap = cx;
258 cyBitmap = cy;
259 }
260 else
261 {
262 /* Large screenshot, scale. */
263 if (cx > cy)
264 {
265 cxBitmap = kMaxSizePNG;
266 cyBitmap = (kMaxSizePNG * cy) / cx;
267 }
268 else
269 {
270 cyBitmap = kMaxSizePNG;
271 cxBitmap = (kMaxSizePNG * cx) / cy;
272 }
273
274 cbBitmap = cxBitmap * 4 * cyBitmap;
275
276 pu8Bitmap = (uint8_t *)RTMemAlloc(cbBitmap);
277
278 if (pu8Bitmap)
279 {
280 uint8_t *dst = pu8Bitmap;
281 uint8_t *src = pu8Data;
282 int dstX = 0;
283 int dstY = 0;
284 int srcX = 0;
285 int srcY = 0;
286 int dstW = cxBitmap;
287 int dstH = cyBitmap;
288 int srcW = cx;
289 int srcH = cy;
290 gdImageCopyResampled (dst,
291 src,
292 dstX, dstY,
293 srcX, srcY,
294 dstW, dstH, srcW, srcH);
295 }
296 else
297 {
298 rc = VERR_NO_MEMORY;
299 }
300 }
301
302 LogFlowFunc(("%dx%d -> %dx%d\n", cx, cy, cxBitmap, cyBitmap));
303
304 if (RT_SUCCESS(rc))
305 {
306 png_bytep *row_pointers = (png_bytep *)RTMemAlloc(cyBitmap * sizeof(png_bytep));
307 if (row_pointers)
308 {
309 png_infop info_ptr = NULL;
310 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
311 (png_voidp)NULL, /* error/warning context pointer */
312 (png_error_ptr)NULL, /* error function */
313 (png_error_ptr)NULL /* warning function */);
314 if (png_ptr)
315 {
316 info_ptr = png_create_info_struct(png_ptr);
317 if (info_ptr)
318 {
319 if (!setjmp(png_jmpbuf(png_ptr)))
320 {
321 PNGWriteCtx ctx;
322 ctx.pu8PNG = NULL;
323 ctx.cbPNG = 0;
324 ctx.cbAllocated = 0;
325 ctx.rc = VINF_SUCCESS;
326
327 png_set_write_fn(png_ptr,
328 (voidp)&ctx,
329 png_write_data_fn,
330 png_output_flush_fn);
331
332 png_set_IHDR(png_ptr, info_ptr,
333 cxBitmap, cyBitmap,
334 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
335 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
336
337 png_bytep row_pointer = (png_bytep)pu8Bitmap;
338 unsigned i = 0;
339 for (; i < cyBitmap; i++, row_pointer += cxBitmap * 4)
340 {
341 row_pointers[i] = row_pointer;
342 }
343 png_set_rows(png_ptr, info_ptr, &row_pointers[0]);
344
345 png_write_info(png_ptr, info_ptr);
346 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
347 png_set_bgr(png_ptr);
348
349 if (info_ptr->valid & PNG_INFO_IDAT)
350 png_write_image(png_ptr, info_ptr->row_pointers);
351
352 png_write_end(png_ptr, info_ptr);
353
354 rc = ctx.rc;
355
356 if (RT_SUCCESS(rc))
357 {
358 *ppu8PNG = ctx.pu8PNG;
359 *pcbPNG = ctx.cbPNG;
360 *pcxPNG = cxBitmap;
361 *pcyPNG = cyBitmap;
362 LogFlowFunc(("PNG %d bytes, bitmap %d bytes\n", ctx.cbPNG, cbBitmap));
363 }
364 }
365 else
366 {
367 rc = VERR_GENERAL_FAILURE; /* Something within libpng. */
368 }
369 }
370 else
371 {
372 rc = VERR_NO_MEMORY;
373 }
374
375 png_destroy_write_struct(&png_ptr, info_ptr ? &info_ptr
376 : (png_infopp)NULL);
377 }
378 else
379 {
380 rc = VERR_NO_MEMORY;
381 }
382
383 RTMemFree(row_pointers);
384 }
385 else
386 {
387 rc = VERR_NO_MEMORY;
388 }
389 }
390
391 if (pu8Bitmap && pu8Bitmap != pu8Data)
392 {
393 RTMemFree(pu8Bitmap);
394 }
395
396 return rc;
397
398}
399
400DECLCALLBACK(void)
401Display::displaySSMSaveScreenshot(PSSMHANDLE pSSM, void *pvUser)
402{
403 Display *that = static_cast<Display*>(pvUser);
404
405 /* 32bpp small RGB image. */
406 uint8_t *pu8Thumbnail = NULL;
407 uint32_t cbThumbnail = 0;
408 uint32_t cxThumbnail = 0;
409 uint32_t cyThumbnail = 0;
410
411 /* PNG screenshot. */
412 uint8_t *pu8PNG = NULL;
413 uint32_t cbPNG = 0;
414 uint32_t cxPNG = 0;
415 uint32_t cyPNG = 0;
416
417 Console::SafeVMPtr pVM (that->mParent);
418 if (SUCCEEDED(pVM.rc()))
419 {
420 /* Query RGB bitmap. */
421 uint8_t *pu8Data = NULL;
422 size_t cbData = 0;
423 uint32_t cx = 0;
424 uint32_t cy = 0;
425
426 /* SSM code is executed on EMT(0), therefore no need to use VMR3ReqCallWait. */
427#ifdef VBOX_WITH_OLD_VBVA_LOCK
428 int rc = Display::displayTakeScreenshotEMT(that, &pu8Data, &cbData, &cx, &cy);
429#else
430 int rc = that->mpDrv->pUpPort->pfnTakeScreenshot (that->mpDrv->pUpPort, &pu8Data, &cbData, &cx, &cy);
431#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
432
433 if (RT_SUCCESS(rc))
434 {
435 /* Prepare a small thumbnail and a PNG screenshot. */
436 displayMakeThumbnail(pu8Data, cx, cy, &pu8Thumbnail, &cbThumbnail, &cxThumbnail, &cyThumbnail);
437 displayMakePNG(pu8Data, cx, cy, &pu8PNG, &cbPNG, &cxPNG, &cyPNG);
438
439 /* This can be called from any thread. */
440 that->mpDrv->pUpPort->pfnFreeScreenshot (that->mpDrv->pUpPort, pu8Data);
441 }
442 }
443 else
444 {
445 LogFunc(("Failed to get VM pointer 0x%x\n", pVM.rc()));
446 }
447
448 /* Regardless of rc, save what is available:
449 * Data format:
450 * uint32_t cBlocks;
451 * [blocks]
452 *
453 * Each block is:
454 * uint32_t cbBlock; if 0 - no 'block data'.
455 * uint32_t typeOfBlock; 0 - 32bpp RGB bitmap, 1 - PNG, ignored if 'cbBlock' is 0.
456 * [block data]
457 *
458 * Block data for bitmap and PNG:
459 * uint32_t cx;
460 * uint32_t cy;
461 * [image data]
462 */
463 SSMR3PutU32(pSSM, 2); /* Write thumbnail and PNG screenshot. */
464
465 /* First block. */
466 SSMR3PutU32(pSSM, cbThumbnail + 2 * sizeof (uint32_t));
467 SSMR3PutU32(pSSM, 0); /* Block type: thumbnail. */
468
469 if (cbThumbnail)
470 {
471 SSMR3PutU32(pSSM, cxThumbnail);
472 SSMR3PutU32(pSSM, cyThumbnail);
473 SSMR3PutMem(pSSM, pu8Thumbnail, cbThumbnail);
474 }
475
476 /* Second block. */
477 SSMR3PutU32(pSSM, cbPNG + 2 * sizeof (uint32_t));
478 SSMR3PutU32(pSSM, 1); /* Block type: png. */
479
480 if (cbPNG)
481 {
482 SSMR3PutU32(pSSM, cxPNG);
483 SSMR3PutU32(pSSM, cyPNG);
484 SSMR3PutMem(pSSM, pu8PNG, cbPNG);
485 }
486
487 RTMemFree(pu8PNG);
488 RTMemFree(pu8Thumbnail);
489}
490
491DECLCALLBACK(int)
492Display::displaySSMLoadScreenshot(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
493{
494 Display *that = static_cast<Display*>(pvUser);
495
496 if (uVersion != sSSMDisplayScreenshotVer)
497 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
498 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
499
500 /* Skip data. */
501 uint32_t cBlocks;
502 int rc = SSMR3GetU32(pSSM, &cBlocks);
503 AssertRCReturn(rc, rc);
504
505 for (uint32_t i = 0; i < cBlocks; i++)
506 {
507 uint32_t cbBlock;
508 rc = SSMR3GetU32(pSSM, &cbBlock);
509 AssertRCBreak(rc);
510
511 uint32_t typeOfBlock;
512 rc = SSMR3GetU32(pSSM, &typeOfBlock);
513 AssertRCBreak(rc);
514
515 LogFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
516
517 if (cbBlock != 0)
518 {
519 rc = SSMR3Skip(pSSM, cbBlock);
520 AssertRCBreak(rc);
521 }
522 }
523
524 return rc;
525}
526
527/**
528 * Save/Load some important guest state
529 */
530DECLCALLBACK(void)
531Display::displaySSMSave(PSSMHANDLE pSSM, void *pvUser)
532{
533 Display *that = static_cast<Display*>(pvUser);
534
535 SSMR3PutU32(pSSM, that->mcMonitors);
536 for (unsigned i = 0; i < that->mcMonitors; i++)
537 {
538 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32Offset);
539 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32MaxFramebufferSize);
540 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32InformationSize);
541 }
542}
543
544DECLCALLBACK(int)
545Display::displaySSMLoad(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
546{
547 Display *that = static_cast<Display*>(pvUser);
548
549 if (uVersion != sSSMDisplayVer)
550 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
551 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
552
553 uint32_t cMonitors;
554 int rc = SSMR3GetU32(pSSM, &cMonitors);
555 if (cMonitors != that->mcMonitors)
556 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Number of monitors changed (%d->%d)!"), cMonitors, that->mcMonitors);
557
558 for (uint32_t i = 0; i < cMonitors; i++)
559 {
560 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32Offset);
561 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32MaxFramebufferSize);
562 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32InformationSize);
563 }
564
565 return VINF_SUCCESS;
566}
567
568/**
569 * Initializes the display object.
570 *
571 * @returns COM result indicator
572 * @param parent handle of our parent object
573 * @param qemuConsoleData address of common console data structure
574 */
575HRESULT Display::init (Console *aParent)
576{
577 LogFlowThisFunc(("aParent=%p\n", aParent));
578
579 ComAssertRet (aParent, E_INVALIDARG);
580
581 /* Enclose the state transition NotReady->InInit->Ready */
582 AutoInitSpan autoInitSpan(this);
583 AssertReturn(autoInitSpan.isOk(), E_FAIL);
584
585 unconst(mParent) = aParent;
586
587 // by default, we have an internal framebuffer which is
588 // NULL, i.e. a black hole for no display output
589 mFramebufferOpened = false;
590
591 ULONG ul;
592 mParent->machine()->COMGETTER(MonitorCount)(&ul);
593 mcMonitors = ul;
594
595 for (ul = 0; ul < mcMonitors; ul++)
596 {
597 maFramebuffers[ul].u32Offset = 0;
598 maFramebuffers[ul].u32MaxFramebufferSize = 0;
599 maFramebuffers[ul].u32InformationSize = 0;
600
601 maFramebuffers[ul].pFramebuffer = NULL;
602
603 maFramebuffers[ul].xOrigin = 0;
604 maFramebuffers[ul].yOrigin = 0;
605
606 maFramebuffers[ul].w = 0;
607 maFramebuffers[ul].h = 0;
608
609 maFramebuffers[ul].pHostEvents = NULL;
610
611 maFramebuffers[ul].u32ResizeStatus = ResizeStatus_Void;
612
613 maFramebuffers[ul].fDefaultFormat = false;
614
615 memset (&maFramebuffers[ul].dirtyRect, 0 , sizeof (maFramebuffers[ul].dirtyRect));
616 memset (&maFramebuffers[ul].pendingResize, 0 , sizeof (maFramebuffers[ul].pendingResize));
617#ifdef VBOX_WITH_HGSMI
618 maFramebuffers[ul].fVBVAEnabled = false;
619 maFramebuffers[ul].cVBVASkipUpdate = 0;
620 memset (&maFramebuffers[ul].vbvaSkippedRect, 0, sizeof (maFramebuffers[ul].vbvaSkippedRect));
621 maFramebuffers[ul].pVBVAHostFlags = NULL;
622#endif /* VBOX_WITH_HGSMI */
623 }
624
625 mParent->RegisterCallback (this);
626
627 /* Confirm a successful initialization */
628 autoInitSpan.setSucceeded();
629
630 return S_OK;
631}
632
633/**
634 * Uninitializes the instance and sets the ready flag to FALSE.
635 * Called either from FinalRelease() or by the parent when it gets destroyed.
636 */
637void Display::uninit()
638{
639 LogFlowThisFunc(("\n"));
640
641 /* Enclose the state transition Ready->InUninit->NotReady */
642 AutoUninitSpan autoUninitSpan(this);
643 if (autoUninitSpan.uninitDone())
644 return;
645
646 ULONG ul;
647 for (ul = 0; ul < mcMonitors; ul++)
648 maFramebuffers[ul].pFramebuffer = NULL;
649
650 if (mParent)
651 mParent->UnregisterCallback (this);
652
653 unconst(mParent).setNull();
654
655 if (mpDrv)
656 mpDrv->pDisplay = NULL;
657
658 mpDrv = NULL;
659 mpVMMDev = NULL;
660 mfVMMDevInited = true;
661}
662
663/**
664 * Register the SSM methods. Called by the power up thread to be able to
665 * pass pVM
666 */
667int Display::registerSSM(PVM pVM)
668{
669 int rc = SSMR3RegisterExternal(pVM, "DisplayData", 0, sSSMDisplayVer,
670 mcMonitors * sizeof(uint32_t) * 3 + sizeof(uint32_t),
671 NULL, NULL, NULL,
672 NULL, displaySSMSave, NULL,
673 NULL, displaySSMLoad, NULL, this);
674
675 AssertRCReturn(rc, rc);
676
677 /*
678 * Register loaders for old saved states where iInstance was 3 * sizeof(uint32_t *).
679 */
680 rc = SSMR3RegisterExternal(pVM, "DisplayData", 12 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
681 NULL, NULL, NULL,
682 NULL, NULL, NULL,
683 NULL, displaySSMLoad, NULL, this);
684 AssertRCReturn(rc, rc);
685
686 rc = SSMR3RegisterExternal(pVM, "DisplayData", 24 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
687 NULL, NULL, NULL,
688 NULL, NULL, NULL,
689 NULL, displaySSMLoad, NULL, this);
690 AssertRCReturn(rc, rc);
691
692 /* uInstance is an arbitrary value greater than 1024. Such a value will ensure a quick seek in saved state file. */
693 rc = SSMR3RegisterExternal(pVM, "DisplayScreenshot", 1100 /*uInstance*/, sSSMDisplayScreenshotVer, 0 /*cbGuess*/,
694 NULL, NULL, NULL,
695 NULL, displaySSMSaveScreenshot, NULL,
696 NULL, displaySSMLoadScreenshot, NULL, this);
697
698 AssertRCReturn(rc, rc);
699
700 return VINF_SUCCESS;
701}
702
703// IConsoleCallback method
704STDMETHODIMP Display::OnStateChange(MachineState_T machineState)
705{
706 if ( machineState == MachineState_Running
707 || machineState == MachineState_Teleporting
708 || machineState == MachineState_LiveSnapshotting
709 )
710 {
711 LogFlowFunc(("Machine is running.\n"));
712
713 mfMachineRunning = true;
714 }
715 else
716 mfMachineRunning = false;
717
718 return S_OK;
719}
720
721// public methods only for internal purposes
722/////////////////////////////////////////////////////////////////////////////
723
724/**
725 * @thread EMT
726 */
727static int callFramebufferResize (IFramebuffer *pFramebuffer, unsigned uScreenId,
728 ULONG pixelFormat, void *pvVRAM,
729 uint32_t bpp, uint32_t cbLine,
730 int w, int h)
731{
732 Assert (pFramebuffer);
733
734 /* Call the framebuffer to try and set required pixelFormat. */
735 BOOL finished = TRUE;
736
737 pFramebuffer->RequestResize (uScreenId, pixelFormat, (BYTE *) pvVRAM,
738 bpp, cbLine, w, h, &finished);
739
740 if (!finished)
741 {
742 LogFlowFunc (("External framebuffer wants us to wait!\n"));
743 return VINF_VGA_RESIZE_IN_PROGRESS;
744 }
745
746 return VINF_SUCCESS;
747}
748
749/**
750 * Handles display resize event.
751 * Disables access to VGA device;
752 * calls the framebuffer RequestResize method;
753 * if framebuffer resizes synchronously,
754 * updates the display connector data and enables access to the VGA device.
755 *
756 * @param w New display width
757 * @param h New display height
758 *
759 * @thread EMT
760 */
761int Display::handleDisplayResize (unsigned uScreenId, uint32_t bpp, void *pvVRAM,
762 uint32_t cbLine, int w, int h)
763{
764 LogRel (("Display::handleDisplayResize(): uScreenId = %d, pvVRAM=%p "
765 "w=%d h=%d bpp=%d cbLine=0x%X\n",
766 uScreenId, pvVRAM, w, h, bpp, cbLine));
767
768 /* If there is no framebuffer, this call is not interesting. */
769 if ( uScreenId >= mcMonitors
770 || maFramebuffers[uScreenId].pFramebuffer.isNull())
771 {
772 return VINF_SUCCESS;
773 }
774
775 mLastAddress = pvVRAM;
776 mLastBytesPerLine = cbLine;
777 mLastBitsPerPixel = bpp,
778 mLastWidth = w;
779 mLastHeight = h;
780
781 ULONG pixelFormat;
782
783 switch (bpp)
784 {
785 case 32:
786 case 24:
787 case 16:
788 pixelFormat = FramebufferPixelFormat_FOURCC_RGB;
789 break;
790 default:
791 pixelFormat = FramebufferPixelFormat_Opaque;
792 bpp = cbLine = 0;
793 break;
794 }
795
796 /* Atomically set the resize status before calling the framebuffer. The new InProgress status will
797 * disable access to the VGA device by the EMT thread.
798 */
799 bool f = ASMAtomicCmpXchgU32 (&maFramebuffers[uScreenId].u32ResizeStatus,
800 ResizeStatus_InProgress, ResizeStatus_Void);
801 if (!f)
802 {
803 /* This could be a result of the screenshot taking call Display::TakeScreenShot:
804 * if the framebuffer is processing the resize request and GUI calls the TakeScreenShot
805 * and the guest has reprogrammed the virtual VGA devices again so a new resize is required.
806 *
807 * Save the resize information and return the pending status code.
808 *
809 * Note: the resize information is only accessed on EMT so no serialization is required.
810 */
811 LogRel (("Display::handleDisplayResize(): Warning: resize postponed.\n"));
812
813 maFramebuffers[uScreenId].pendingResize.fPending = true;
814 maFramebuffers[uScreenId].pendingResize.pixelFormat = pixelFormat;
815 maFramebuffers[uScreenId].pendingResize.pvVRAM = pvVRAM;
816 maFramebuffers[uScreenId].pendingResize.bpp = bpp;
817 maFramebuffers[uScreenId].pendingResize.cbLine = cbLine;
818 maFramebuffers[uScreenId].pendingResize.w = w;
819 maFramebuffers[uScreenId].pendingResize.h = h;
820
821 return VINF_VGA_RESIZE_IN_PROGRESS;
822 }
823
824 int rc = callFramebufferResize (maFramebuffers[uScreenId].pFramebuffer, uScreenId,
825 pixelFormat, pvVRAM, bpp, cbLine, w, h);
826 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
827 {
828 /* Immediately return to the caller. ResizeCompleted will be called back by the
829 * GUI thread. The ResizeCompleted callback will change the resize status from
830 * InProgress to UpdateDisplayData. The latter status will be checked by the
831 * display timer callback on EMT and all required adjustments will be done there.
832 */
833 return rc;
834 }
835
836 /* Set the status so the 'handleResizeCompleted' would work. */
837 f = ASMAtomicCmpXchgU32 (&maFramebuffers[uScreenId].u32ResizeStatus,
838 ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
839 AssertRelease(f);NOREF(f);
840
841 AssertRelease(!maFramebuffers[uScreenId].pendingResize.fPending);
842
843 /* The method also unlocks the framebuffer. */
844 handleResizeCompletedEMT();
845
846 return VINF_SUCCESS;
847}
848
849/**
850 * Framebuffer has been resized.
851 * Read the new display data and unlock the framebuffer.
852 *
853 * @thread EMT
854 */
855void Display::handleResizeCompletedEMT (void)
856{
857 LogFlowFunc(("\n"));
858
859 unsigned uScreenId;
860 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
861 {
862 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
863
864 /* Try to into non resizing state. */
865 bool f = ASMAtomicCmpXchgU32 (&pFBInfo->u32ResizeStatus, ResizeStatus_Void, ResizeStatus_UpdateDisplayData);
866
867 if (f == false)
868 {
869 /* This is not the display that has completed resizing. */
870 continue;
871 }
872
873 /* Check whether a resize is pending for this framebuffer. */
874 if (pFBInfo->pendingResize.fPending)
875 {
876 /* Reset the condition, call the display resize with saved data and continue.
877 *
878 * Note: handleDisplayResize can call handleResizeCompletedEMT back,
879 * but infinite recursion is not possible, because when the handleResizeCompletedEMT
880 * is called, the pFBInfo->pendingResize.fPending is equal to false.
881 */
882 pFBInfo->pendingResize.fPending = false;
883 handleDisplayResize (uScreenId, pFBInfo->pendingResize.bpp, pFBInfo->pendingResize.pvVRAM,
884 pFBInfo->pendingResize.cbLine, pFBInfo->pendingResize.w, pFBInfo->pendingResize.h);
885 continue;
886 }
887
888 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && !pFBInfo->pFramebuffer.isNull())
889 {
890 /* Primary framebuffer has completed the resize. Update the connector data for VGA device. */
891 updateDisplayData();
892
893 /* Check the framebuffer pixel format to setup the rendering in VGA device. */
894 BOOL usesGuestVRAM = FALSE;
895 pFBInfo->pFramebuffer->COMGETTER(UsesGuestVRAM) (&usesGuestVRAM);
896
897 pFBInfo->fDefaultFormat = (usesGuestVRAM == FALSE);
898
899 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort, pFBInfo->fDefaultFormat);
900 }
901
902#ifdef DEBUG_sunlover
903 if (!stam)
904 {
905 /* protect mpVM */
906 Console::SafeVMPtr pVM (mParent);
907 AssertComRC (pVM.rc());
908
909 STAM_REG(pVM, &StatDisplayRefresh, STAMTYPE_PROFILE, "/PROF/Display/Refresh", STAMUNIT_TICKS_PER_CALL, "Time spent in EMT for display updates.");
910 stam = 1;
911 }
912#endif /* DEBUG_sunlover */
913
914 /* Inform VRDP server about the change of display parameters. */
915 LogFlowFunc (("Calling VRDP\n"));
916 mParent->consoleVRDPServer()->SendResize();
917 }
918}
919
920static void checkCoordBounds (int *px, int *py, int *pw, int *ph, int cx, int cy)
921{
922 /* Correct negative x and y coordinates. */
923 if (*px < 0)
924 {
925 *px += *pw; /* Compute xRight which is also the new width. */
926
927 *pw = (*px < 0)? 0: *px;
928
929 *px = 0;
930 }
931
932 if (*py < 0)
933 {
934 *py += *ph; /* Compute xBottom, which is also the new height. */
935
936 *ph = (*py < 0)? 0: *py;
937
938 *py = 0;
939 }
940
941 /* Also check if coords are greater than the display resolution. */
942 if (*px + *pw > cx)
943 {
944 *pw = cx > *px? cx - *px: 0;
945 }
946
947 if (*py + *ph > cy)
948 {
949 *ph = cy > *py? cy - *py: 0;
950 }
951}
952
953unsigned mapCoordsToScreen(DISPLAYFBINFO *pInfos, unsigned cInfos, int *px, int *py, int *pw, int *ph)
954{
955 DISPLAYFBINFO *pInfo = pInfos;
956 unsigned uScreenId;
957 LogSunlover (("mapCoordsToScreen: %d,%d %dx%d\n", *px, *py, *pw, *ph));
958 for (uScreenId = 0; uScreenId < cInfos; uScreenId++, pInfo++)
959 {
960 LogSunlover ((" [%d] %d,%d %dx%d\n", uScreenId, pInfo->xOrigin, pInfo->yOrigin, pInfo->w, pInfo->h));
961 if ( (pInfo->xOrigin <= *px && *px < pInfo->xOrigin + (int)pInfo->w)
962 && (pInfo->yOrigin <= *py && *py < pInfo->yOrigin + (int)pInfo->h))
963 {
964 /* The rectangle belongs to the screen. Correct coordinates. */
965 *px -= pInfo->xOrigin;
966 *py -= pInfo->yOrigin;
967 LogSunlover ((" -> %d,%d", *px, *py));
968 break;
969 }
970 }
971 if (uScreenId == cInfos)
972 {
973 /* Map to primary screen. */
974 uScreenId = 0;
975 }
976 LogSunlover ((" scr %d\n", uScreenId));
977 return uScreenId;
978}
979
980
981/**
982 * Handles display update event.
983 *
984 * @param x Update area x coordinate
985 * @param y Update area y coordinate
986 * @param w Update area width
987 * @param h Update area height
988 *
989 * @thread EMT
990 */
991void Display::handleDisplayUpdate (int x, int y, int w, int h)
992{
993#ifdef VBOX_WITH_OLD_VBVA_LOCK
994 /*
995 * Always runs under either VBVA lock or, for HGSMI, DevVGA lock.
996 * Safe to use VBVA vars and take the framebuffer lock.
997 */
998#endif /* VBOX_WITH_OLD_VBVA_LOCK */
999
1000#ifdef DEBUG_sunlover
1001 LogFlowFunc (("%d,%d %dx%d (%d,%d)\n",
1002 x, y, w, h, mpDrv->Connector.cx, mpDrv->Connector.cy));
1003#endif /* DEBUG_sunlover */
1004
1005 unsigned uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
1006
1007#ifdef DEBUG_sunlover
1008 LogFlowFunc (("%d,%d %dx%d (checked)\n", x, y, w, h));
1009#endif /* DEBUG_sunlover */
1010
1011 IFramebuffer *pFramebuffer = maFramebuffers[uScreenId].pFramebuffer;
1012
1013 // if there is no framebuffer, this call is not interesting
1014 if (pFramebuffer == NULL)
1015 return;
1016
1017 pFramebuffer->Lock();
1018
1019 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
1020 checkCoordBounds (&x, &y, &w, &h, mpDrv->Connector.cx, mpDrv->Connector.cy);
1021 else
1022 checkCoordBounds (&x, &y, &w, &h, maFramebuffers[uScreenId].w,
1023 maFramebuffers[uScreenId].h);
1024
1025 if (w != 0 && h != 0)
1026 pFramebuffer->NotifyUpdate(x, y, w, h);
1027
1028 pFramebuffer->Unlock();
1029
1030#ifndef VBOX_WITH_HGSMI
1031 if (!mfVideoAccelEnabled)
1032 {
1033#else
1034 if (!mfVideoAccelEnabled && !maFramebuffers[uScreenId].fVBVAEnabled)
1035 {
1036#endif /* VBOX_WITH_HGSMI */
1037 /* When VBVA is enabled, the VRDP server is informed in the VideoAccelFlush.
1038 * Inform the server here only if VBVA is disabled.
1039 */
1040 if (maFramebuffers[uScreenId].u32ResizeStatus == ResizeStatus_Void)
1041 mParent->consoleVRDPServer()->SendUpdateBitmap(uScreenId, x, y, w, h);
1042 }
1043}
1044
1045typedef struct _VBVADIRTYREGION
1046{
1047 /* Copies of object's pointers used by vbvaRgn functions. */
1048 DISPLAYFBINFO *paFramebuffers;
1049 unsigned cMonitors;
1050 Display *pDisplay;
1051 PPDMIDISPLAYPORT pPort;
1052
1053} VBVADIRTYREGION;
1054
1055static void vbvaRgnInit (VBVADIRTYREGION *prgn, DISPLAYFBINFO *paFramebuffers, unsigned cMonitors, Display *pd, PPDMIDISPLAYPORT pp)
1056{
1057 prgn->paFramebuffers = paFramebuffers;
1058 prgn->cMonitors = cMonitors;
1059 prgn->pDisplay = pd;
1060 prgn->pPort = pp;
1061
1062 unsigned uScreenId;
1063 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
1064 {
1065 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1066
1067 memset (&pFBInfo->dirtyRect, 0, sizeof (pFBInfo->dirtyRect));
1068 }
1069}
1070
1071static void vbvaRgnDirtyRect (VBVADIRTYREGION *prgn, unsigned uScreenId, VBVACMDHDR *phdr)
1072{
1073 LogSunlover (("x = %d, y = %d, w = %d, h = %d\n",
1074 phdr->x, phdr->y, phdr->w, phdr->h));
1075
1076 /*
1077 * Here update rectangles are accumulated to form an update area.
1078 * @todo
1079 * Now the simpliest method is used which builds one rectangle that
1080 * includes all update areas. A bit more advanced method can be
1081 * employed here. The method should be fast however.
1082 */
1083 if (phdr->w == 0 || phdr->h == 0)
1084 {
1085 /* Empty rectangle. */
1086 return;
1087 }
1088
1089 int32_t xRight = phdr->x + phdr->w;
1090 int32_t yBottom = phdr->y + phdr->h;
1091
1092 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1093
1094 if (pFBInfo->dirtyRect.xRight == 0)
1095 {
1096 /* This is the first rectangle to be added. */
1097 pFBInfo->dirtyRect.xLeft = phdr->x;
1098 pFBInfo->dirtyRect.yTop = phdr->y;
1099 pFBInfo->dirtyRect.xRight = xRight;
1100 pFBInfo->dirtyRect.yBottom = yBottom;
1101 }
1102 else
1103 {
1104 /* Adjust region coordinates. */
1105 if (pFBInfo->dirtyRect.xLeft > phdr->x)
1106 {
1107 pFBInfo->dirtyRect.xLeft = phdr->x;
1108 }
1109
1110 if (pFBInfo->dirtyRect.yTop > phdr->y)
1111 {
1112 pFBInfo->dirtyRect.yTop = phdr->y;
1113 }
1114
1115 if (pFBInfo->dirtyRect.xRight < xRight)
1116 {
1117 pFBInfo->dirtyRect.xRight = xRight;
1118 }
1119
1120 if (pFBInfo->dirtyRect.yBottom < yBottom)
1121 {
1122 pFBInfo->dirtyRect.yBottom = yBottom;
1123 }
1124 }
1125
1126 if (pFBInfo->fDefaultFormat)
1127 {
1128 //@todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
1129 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, phdr->x, phdr->y, phdr->w, phdr->h);
1130 prgn->pDisplay->handleDisplayUpdate (phdr->x + pFBInfo->xOrigin,
1131 phdr->y + pFBInfo->yOrigin, phdr->w, phdr->h);
1132 }
1133
1134 return;
1135}
1136
1137static void vbvaRgnUpdateFramebuffer (VBVADIRTYREGION *prgn, unsigned uScreenId)
1138{
1139 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1140
1141 uint32_t w = pFBInfo->dirtyRect.xRight - pFBInfo->dirtyRect.xLeft;
1142 uint32_t h = pFBInfo->dirtyRect.yBottom - pFBInfo->dirtyRect.yTop;
1143
1144 if (!pFBInfo->fDefaultFormat && pFBInfo->pFramebuffer && w != 0 && h != 0)
1145 {
1146 //@todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
1147 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, pFBInfo->dirtyRect.xLeft, pFBInfo->dirtyRect.yTop, w, h);
1148 prgn->pDisplay->handleDisplayUpdate (pFBInfo->dirtyRect.xLeft + pFBInfo->xOrigin,
1149 pFBInfo->dirtyRect.yTop + pFBInfo->yOrigin, w, h);
1150 }
1151}
1152
1153static void vbvaSetMemoryFlags (VBVAMEMORY *pVbvaMemory,
1154 bool fVideoAccelEnabled,
1155 bool fVideoAccelVRDP,
1156 uint32_t fu32SupportedOrders,
1157 DISPLAYFBINFO *paFBInfos,
1158 unsigned cFBInfos)
1159{
1160 if (pVbvaMemory)
1161 {
1162 /* This called only on changes in mode. So reset VRDP always. */
1163 uint32_t fu32Flags = VBVA_F_MODE_VRDP_RESET;
1164
1165 if (fVideoAccelEnabled)
1166 {
1167 fu32Flags |= VBVA_F_MODE_ENABLED;
1168
1169 if (fVideoAccelVRDP)
1170 {
1171 fu32Flags |= VBVA_F_MODE_VRDP | VBVA_F_MODE_VRDP_ORDER_MASK;
1172
1173 pVbvaMemory->fu32SupportedOrders = fu32SupportedOrders;
1174 }
1175 }
1176
1177 pVbvaMemory->fu32ModeFlags = fu32Flags;
1178 }
1179
1180 unsigned uScreenId;
1181 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
1182 {
1183 if (paFBInfos[uScreenId].pHostEvents)
1184 {
1185 paFBInfos[uScreenId].pHostEvents->fu32Events |= VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
1186 }
1187 }
1188}
1189
1190#ifdef VBOX_WITH_HGSMI
1191static void vbvaSetMemoryFlagsHGSMI (unsigned uScreenId,
1192 uint32_t fu32SupportedOrders,
1193 bool fVideoAccelVRDP,
1194 DISPLAYFBINFO *pFBInfo)
1195{
1196 LogFlowFunc(("HGSMI[%d]: %p\n", uScreenId, pFBInfo->pVBVAHostFlags));
1197
1198 if (pFBInfo->pVBVAHostFlags)
1199 {
1200 uint32_t fu32HostEvents = VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
1201
1202 if (pFBInfo->fVBVAEnabled)
1203 {
1204 fu32HostEvents |= VBVA_F_MODE_ENABLED;
1205
1206 if (fVideoAccelVRDP)
1207 {
1208 fu32HostEvents |= VBVA_F_MODE_VRDP;
1209 }
1210 }
1211
1212 ASMAtomicOrU32(&pFBInfo->pVBVAHostFlags->u32HostEvents, fu32HostEvents);
1213 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32SupportedOrders, fu32SupportedOrders);
1214
1215 LogFlowFunc((" fu32HostEvents = 0x%08X, fu32SupportedOrders = 0x%08X\n", fu32HostEvents, fu32SupportedOrders));
1216 }
1217}
1218
1219static void vbvaSetMemoryFlagsAllHGSMI (uint32_t fu32SupportedOrders,
1220 bool fVideoAccelVRDP,
1221 DISPLAYFBINFO *paFBInfos,
1222 unsigned cFBInfos)
1223{
1224 unsigned uScreenId;
1225
1226 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
1227 {
1228 vbvaSetMemoryFlagsHGSMI(uScreenId, fu32SupportedOrders, fVideoAccelVRDP, &paFBInfos[uScreenId]);
1229 }
1230}
1231#endif /* VBOX_WITH_HGSMI */
1232
1233bool Display::VideoAccelAllowed (void)
1234{
1235 return true;
1236}
1237
1238#ifdef VBOX_WITH_OLD_VBVA_LOCK
1239int Display::vbvaLock(void)
1240{
1241 return RTCritSectEnter(&mVBVALock);
1242}
1243
1244void Display::vbvaUnlock(void)
1245{
1246 RTCritSectLeave(&mVBVALock);
1247}
1248#endif /* VBOX_WITH_OLD_VBVA_LOCK */
1249
1250/**
1251 * @thread EMT
1252 */
1253#ifdef VBOX_WITH_OLD_VBVA_LOCK
1254int Display::VideoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
1255{
1256 int rc;
1257 vbvaLock();
1258 rc = videoAccelEnable (fEnable, pVbvaMemory);
1259 vbvaUnlock();
1260 return rc;
1261}
1262#endif /* VBOX_WITH_OLD_VBVA_LOCK */
1263
1264#ifdef VBOX_WITH_OLD_VBVA_LOCK
1265int Display::videoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
1266#else
1267int Display::VideoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
1268#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
1269{
1270 int rc = VINF_SUCCESS;
1271
1272 /* Called each time the guest wants to use acceleration,
1273 * or when the VGA device disables acceleration,
1274 * or when restoring the saved state with accel enabled.
1275 *
1276 * VGA device disables acceleration on each video mode change
1277 * and on reset.
1278 *
1279 * Guest enabled acceleration at will. And it has to enable
1280 * acceleration after a mode change.
1281 */
1282 LogFlowFunc (("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
1283 mfVideoAccelEnabled, fEnable, pVbvaMemory));
1284
1285 /* Strictly check parameters. Callers must not pass anything in the case. */
1286 Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));
1287
1288 if (!VideoAccelAllowed ())
1289 {
1290 return VERR_NOT_SUPPORTED;
1291 }
1292
1293 /*
1294 * Verify that the VM is in running state. If it is not,
1295 * then this must be postponed until it goes to running.
1296 */
1297 if (!mfMachineRunning)
1298 {
1299 Assert (!mfVideoAccelEnabled);
1300
1301 LogFlowFunc (("Machine is not yet running.\n"));
1302
1303 if (fEnable)
1304 {
1305 mfPendingVideoAccelEnable = fEnable;
1306 mpPendingVbvaMemory = pVbvaMemory;
1307 }
1308
1309 return rc;
1310 }
1311
1312 /* Check that current status is not being changed */
1313 if (mfVideoAccelEnabled == fEnable)
1314 {
1315 return rc;
1316 }
1317
1318 if (mfVideoAccelEnabled)
1319 {
1320 /* Process any pending orders and empty the VBVA ring buffer. */
1321#ifdef VBOX_WITH_OLD_VBVA_LOCK
1322 videoAccelFlush ();
1323#else
1324 VideoAccelFlush ();
1325#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
1326 }
1327
1328 if (!fEnable && mpVbvaMemory)
1329 {
1330 mpVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;
1331 }
1332
1333 /* Safety precaution. There is no more VBVA until everything is setup! */
1334 mpVbvaMemory = NULL;
1335 mfVideoAccelEnabled = false;
1336
1337 /* Update entire display. */
1338 if (maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].u32ResizeStatus == ResizeStatus_Void)
1339 {
1340 mpDrv->pUpPort->pfnUpdateDisplayAll(mpDrv->pUpPort);
1341 }
1342
1343 /* Everything OK. VBVA status can be changed. */
1344
1345 /* Notify the VMMDev, which saves VBVA status in the saved state,
1346 * and needs to know current status.
1347 */
1348 PPDMIVMMDEVPORT pVMMDevPort = mParent->getVMMDev()->getVMMDevPort ();
1349
1350 if (pVMMDevPort)
1351 {
1352 pVMMDevPort->pfnVBVAChange (pVMMDevPort, fEnable);
1353 }
1354
1355 if (fEnable)
1356 {
1357 mpVbvaMemory = pVbvaMemory;
1358 mfVideoAccelEnabled = true;
1359
1360 /* Initialize the hardware memory. */
1361 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
1362 mpVbvaMemory->off32Data = 0;
1363 mpVbvaMemory->off32Free = 0;
1364
1365 memset (mpVbvaMemory->aRecords, 0, sizeof (mpVbvaMemory->aRecords));
1366 mpVbvaMemory->indexRecordFirst = 0;
1367 mpVbvaMemory->indexRecordFree = 0;
1368
1369#ifdef VBOX_WITH_OLD_VBVA_LOCK
1370 mfu32PendingVideoAccelDisable = false;
1371#endif /* VBOX_WITH_OLD_VBVA_LOCK */
1372
1373 LogRel(("VBVA: Enabled.\n"));
1374 }
1375 else
1376 {
1377 LogRel(("VBVA: Disabled.\n"));
1378 }
1379
1380 LogFlowFunc (("VideoAccelEnable: rc = %Rrc.\n", rc));
1381
1382 return rc;
1383}
1384
1385#ifdef VBOX_WITH_VRDP
1386/* Called always by one VRDP server thread. Can be thread-unsafe.
1387 */
1388void Display::VideoAccelVRDP (bool fEnable)
1389{
1390 LogFlowFunc(("fEnable = %d\n", fEnable));
1391
1392#ifdef VBOX_WITH_OLD_VBVA_LOCK
1393 vbvaLock();
1394#endif /* VBOX_WITH_OLD_VBVA_LOCK */
1395
1396 int c = fEnable?
1397 ASMAtomicIncS32 (&mcVideoAccelVRDPRefs):
1398 ASMAtomicDecS32 (&mcVideoAccelVRDPRefs);
1399
1400 Assert (c >= 0);
1401
1402 if (c == 0)
1403 {
1404 /* The last client has disconnected, and the accel can be
1405 * disabled.
1406 */
1407 Assert (fEnable == false);
1408
1409 mfVideoAccelVRDP = false;
1410 mfu32SupportedOrders = 0;
1411
1412 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
1413#ifdef VBOX_WITH_HGSMI
1414 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1415 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1416#endif /* VBOX_WITH_HGSMI */
1417
1418 LogRel(("VBVA: VRDP acceleration has been disabled.\n"));
1419 }
1420 else if ( c == 1
1421 && !mfVideoAccelVRDP)
1422 {
1423 /* The first client has connected. Enable the accel.
1424 */
1425 Assert (fEnable == true);
1426
1427 mfVideoAccelVRDP = true;
1428 /* Supporting all orders. */
1429 mfu32SupportedOrders = ~0;
1430
1431 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
1432#ifdef VBOX_WITH_HGSMI
1433 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1434 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1435#endif /* VBOX_WITH_HGSMI */
1436
1437 LogRel(("VBVA: VRDP acceleration has been requested.\n"));
1438 }
1439 else
1440 {
1441 /* A client is connected or disconnected but there is no change in the
1442 * accel state. It remains enabled.
1443 */
1444 Assert (mfVideoAccelVRDP == true);
1445 }
1446#ifdef VBOX_WITH_OLD_VBVA_LOCK
1447 vbvaUnlock();
1448#endif /* VBOX_WITH_OLD_VBVA_LOCK */
1449}
1450#endif /* VBOX_WITH_VRDP */
1451
1452static bool vbvaVerifyRingBuffer (VBVAMEMORY *pVbvaMemory)
1453{
1454 return true;
1455}
1456
1457static void vbvaFetchBytes (VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t cbDst)
1458{
1459 if (cbDst >= VBVA_RING_BUFFER_SIZE)
1460 {
1461 AssertMsgFailed (("cbDst = 0x%08X, ring buffer size 0x%08X", cbDst, VBVA_RING_BUFFER_SIZE));
1462 return;
1463 }
1464
1465 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
1466 uint8_t *src = &pVbvaMemory->au8RingBuffer[pVbvaMemory->off32Data];
1467 int32_t i32Diff = cbDst - u32BytesTillBoundary;
1468
1469 if (i32Diff <= 0)
1470 {
1471 /* Chunk will not cross buffer boundary. */
1472 memcpy (pu8Dst, src, cbDst);
1473 }
1474 else
1475 {
1476 /* Chunk crosses buffer boundary. */
1477 memcpy (pu8Dst, src, u32BytesTillBoundary);
1478 memcpy (pu8Dst + u32BytesTillBoundary, &pVbvaMemory->au8RingBuffer[0], i32Diff);
1479 }
1480
1481 /* Advance data offset. */
1482 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbDst) % VBVA_RING_BUFFER_SIZE;
1483
1484 return;
1485}
1486
1487
1488static bool vbvaPartialRead (uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
1489{
1490 uint8_t *pu8New;
1491
1492 LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
1493 *ppu8, *pcb, cbRecord));
1494
1495 if (*ppu8)
1496 {
1497 Assert (*pcb);
1498 pu8New = (uint8_t *)RTMemRealloc (*ppu8, cbRecord);
1499 }
1500 else
1501 {
1502 Assert (!*pcb);
1503 pu8New = (uint8_t *)RTMemAlloc (cbRecord);
1504 }
1505
1506 if (!pu8New)
1507 {
1508 /* Memory allocation failed, fail the function. */
1509 Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
1510 cbRecord));
1511
1512 if (*ppu8)
1513 {
1514 RTMemFree (*ppu8);
1515 }
1516
1517 *ppu8 = NULL;
1518 *pcb = 0;
1519
1520 return false;
1521 }
1522
1523 /* Fetch data from the ring buffer. */
1524 vbvaFetchBytes (pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
1525
1526 *ppu8 = pu8New;
1527 *pcb = cbRecord;
1528
1529 return true;
1530}
1531
1532/* For contiguous chunks just return the address in the buffer.
1533 * For crossing boundary - allocate a buffer from heap.
1534 */
1535bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
1536{
1537 uint32_t indexRecordFirst = mpVbvaMemory->indexRecordFirst;
1538 uint32_t indexRecordFree = mpVbvaMemory->indexRecordFree;
1539
1540#ifdef DEBUG_sunlover
1541 LogFlowFunc (("first = %d, free = %d\n",
1542 indexRecordFirst, indexRecordFree));
1543#endif /* DEBUG_sunlover */
1544
1545 if (!vbvaVerifyRingBuffer (mpVbvaMemory))
1546 {
1547 return false;
1548 }
1549
1550 if (indexRecordFirst == indexRecordFree)
1551 {
1552 /* No records to process. Return without assigning output variables. */
1553 return true;
1554 }
1555
1556 VBVARECORD *pRecord = &mpVbvaMemory->aRecords[indexRecordFirst];
1557
1558#ifdef DEBUG_sunlover
1559 LogFlowFunc (("cbRecord = 0x%08X\n", pRecord->cbRecord));
1560#endif /* DEBUG_sunlover */
1561
1562 uint32_t cbRecord = pRecord->cbRecord & ~VBVA_F_RECORD_PARTIAL;
1563
1564 if (mcbVbvaPartial)
1565 {
1566 /* There is a partial read in process. Continue with it. */
1567
1568 Assert (mpu8VbvaPartial);
1569
1570 LogFlowFunc (("continue partial record mcbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
1571 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
1572
1573 if (cbRecord > mcbVbvaPartial)
1574 {
1575 /* New data has been added to the record. */
1576 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
1577 {
1578 return false;
1579 }
1580 }
1581
1582 if (!(pRecord->cbRecord & VBVA_F_RECORD_PARTIAL))
1583 {
1584 /* The record is completed by guest. Return it to the caller. */
1585 *ppHdr = (VBVACMDHDR *)mpu8VbvaPartial;
1586 *pcbCmd = mcbVbvaPartial;
1587
1588 mpu8VbvaPartial = NULL;
1589 mcbVbvaPartial = 0;
1590
1591 /* Advance the record index. */
1592 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
1593
1594#ifdef DEBUG_sunlover
1595 LogFlowFunc (("partial done ok, data = %d, free = %d\n",
1596 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1597#endif /* DEBUG_sunlover */
1598 }
1599
1600 return true;
1601 }
1602
1603 /* A new record need to be processed. */
1604 if (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)
1605 {
1606 /* Current record is being written by guest. '=' is important here. */
1607 if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
1608 {
1609 /* Partial read must be started. */
1610 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
1611 {
1612 return false;
1613 }
1614
1615 LogFlowFunc (("started partial record mcbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
1616 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
1617 }
1618
1619 return true;
1620 }
1621
1622 /* Current record is complete. If it is not empty, process it. */
1623 if (cbRecord)
1624 {
1625 /* The size of largest contiguos chunk in the ring biffer. */
1626 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - mpVbvaMemory->off32Data;
1627
1628 /* The ring buffer pointer. */
1629 uint8_t *au8RingBuffer = &mpVbvaMemory->au8RingBuffer[0];
1630
1631 /* The pointer to data in the ring buffer. */
1632 uint8_t *src = &au8RingBuffer[mpVbvaMemory->off32Data];
1633
1634 /* Fetch or point the data. */
1635 if (u32BytesTillBoundary >= cbRecord)
1636 {
1637 /* The command does not cross buffer boundary. Return address in the buffer. */
1638 *ppHdr = (VBVACMDHDR *)src;
1639
1640 /* Advance data offset. */
1641 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
1642 }
1643 else
1644 {
1645 /* The command crosses buffer boundary. Rare case, so not optimized. */
1646 uint8_t *dst = (uint8_t *)RTMemAlloc (cbRecord);
1647
1648 if (!dst)
1649 {
1650 LogFlowFunc (("could not allocate %d bytes from heap!!!\n", cbRecord));
1651 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
1652 return false;
1653 }
1654
1655 vbvaFetchBytes (mpVbvaMemory, dst, cbRecord);
1656
1657 *ppHdr = (VBVACMDHDR *)dst;
1658
1659#ifdef DEBUG_sunlover
1660 LogFlowFunc (("Allocated from heap %p\n", dst));
1661#endif /* DEBUG_sunlover */
1662 }
1663 }
1664
1665 *pcbCmd = cbRecord;
1666
1667 /* Advance the record index. */
1668 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
1669
1670#ifdef DEBUG_sunlover
1671 LogFlowFunc (("done ok, data = %d, free = %d\n",
1672 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1673#endif /* DEBUG_sunlover */
1674
1675 return true;
1676}
1677
1678void Display::vbvaReleaseCmd (VBVACMDHDR *pHdr, int32_t cbCmd)
1679{
1680 uint8_t *au8RingBuffer = mpVbvaMemory->au8RingBuffer;
1681
1682 if ( (uint8_t *)pHdr >= au8RingBuffer
1683 && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
1684 {
1685 /* The pointer is inside ring buffer. Must be continuous chunk. */
1686 Assert (VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
1687
1688 /* Do nothing. */
1689
1690 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1691 }
1692 else
1693 {
1694 /* The pointer is outside. It is then an allocated copy. */
1695
1696#ifdef DEBUG_sunlover
1697 LogFlowFunc (("Free heap %p\n", pHdr));
1698#endif /* DEBUG_sunlover */
1699
1700 if ((uint8_t *)pHdr == mpu8VbvaPartial)
1701 {
1702 mpu8VbvaPartial = NULL;
1703 mcbVbvaPartial = 0;
1704 }
1705 else
1706 {
1707 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1708 }
1709
1710 RTMemFree (pHdr);
1711 }
1712
1713 return;
1714}
1715
1716
1717/**
1718 * Called regularly on the DisplayRefresh timer.
1719 * Also on behalf of guest, when the ring buffer is full.
1720 *
1721 * @thread EMT
1722 */
1723#ifdef VBOX_WITH_OLD_VBVA_LOCK
1724void Display::VideoAccelFlush (void)
1725{
1726 vbvaLock();
1727 videoAccelFlush();
1728 vbvaUnlock();
1729}
1730#endif /* VBOX_WITH_OLD_VBVA_LOCK */
1731
1732#ifdef VBOX_WITH_OLD_VBVA_LOCK
1733/* Under VBVA lock. DevVGA is not taken. */
1734void Display::videoAccelFlush (void)
1735#else
1736void Display::VideoAccelFlush (void)
1737#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
1738{
1739#ifdef DEBUG_sunlover_2
1740 LogFlowFunc (("mfVideoAccelEnabled = %d\n", mfVideoAccelEnabled));
1741#endif /* DEBUG_sunlover_2 */
1742
1743 if (!mfVideoAccelEnabled)
1744 {
1745 Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
1746 return;
1747 }
1748
1749 /* Here VBVA is enabled and we have the accelerator memory pointer. */
1750 Assert(mpVbvaMemory);
1751
1752#ifdef DEBUG_sunlover_2
1753 LogFlowFunc (("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
1754 mpVbvaMemory->indexRecordFirst, mpVbvaMemory->indexRecordFree, mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1755#endif /* DEBUG_sunlover_2 */
1756
1757 /* Quick check for "nothing to update" case. */
1758 if (mpVbvaMemory->indexRecordFirst == mpVbvaMemory->indexRecordFree)
1759 {
1760 return;
1761 }
1762
1763 /* Process the ring buffer */
1764 unsigned uScreenId;
1765#ifndef VBOX_WITH_OLD_VBVA_LOCK
1766 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1767 {
1768 if (!maFramebuffers[uScreenId].pFramebuffer.isNull())
1769 {
1770 maFramebuffers[uScreenId].pFramebuffer->Lock ();
1771 }
1772 }
1773#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
1774
1775 /* Initialize dirty rectangles accumulator. */
1776 VBVADIRTYREGION rgn;
1777 vbvaRgnInit (&rgn, maFramebuffers, mcMonitors, this, mpDrv->pUpPort);
1778
1779 for (;;)
1780 {
1781 VBVACMDHDR *phdr = NULL;
1782 uint32_t cbCmd = ~0;
1783
1784 /* Fetch the command data. */
1785 if (!vbvaFetchCmd (&phdr, &cbCmd))
1786 {
1787 Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
1788 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1789
1790 /* Disable VBVA on those processing errors. */
1791#ifdef VBOX_WITH_OLD_VBVA_LOCK
1792 videoAccelEnable (false, NULL);
1793#else
1794 VideoAccelEnable (false, NULL);
1795#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
1796
1797 break;
1798 }
1799
1800 if (cbCmd == uint32_t(~0))
1801 {
1802 /* No more commands yet in the queue. */
1803 break;
1804 }
1805
1806 if (cbCmd != 0)
1807 {
1808#ifdef DEBUG_sunlover
1809 LogFlowFunc (("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
1810 cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
1811#endif /* DEBUG_sunlover */
1812
1813 VBVACMDHDR hdrSaved = *phdr;
1814
1815 int x = phdr->x;
1816 int y = phdr->y;
1817 int w = phdr->w;
1818 int h = phdr->h;
1819
1820 uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
1821
1822 phdr->x = (int16_t)x;
1823 phdr->y = (int16_t)y;
1824 phdr->w = (uint16_t)w;
1825 phdr->h = (uint16_t)h;
1826
1827 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1828
1829 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
1830 {
1831 /* Handle the command.
1832 *
1833 * Guest is responsible for updating the guest video memory.
1834 * The Windows guest does all drawing using Eng*.
1835 *
1836 * For local output, only dirty rectangle information is used
1837 * to update changed areas.
1838 *
1839 * Dirty rectangles are accumulated to exclude overlapping updates and
1840 * group small updates to a larger one.
1841 */
1842
1843 /* Accumulate the update. */
1844 vbvaRgnDirtyRect (&rgn, uScreenId, phdr);
1845
1846 /* Forward the command to VRDP server. */
1847 mParent->consoleVRDPServer()->SendUpdate (uScreenId, phdr, cbCmd);
1848
1849 *phdr = hdrSaved;
1850 }
1851 }
1852
1853 vbvaReleaseCmd (phdr, cbCmd);
1854 }
1855
1856 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1857 {
1858#ifndef VBOX_WITH_OLD_VBVA_LOCK
1859 if (!maFramebuffers[uScreenId].pFramebuffer.isNull())
1860 {
1861 maFramebuffers[uScreenId].pFramebuffer->Unlock ();
1862 }
1863#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
1864
1865 if (maFramebuffers[uScreenId].u32ResizeStatus == ResizeStatus_Void)
1866 {
1867 /* Draw the framebuffer. */
1868 vbvaRgnUpdateFramebuffer (&rgn, uScreenId);
1869 }
1870 }
1871}
1872
1873#ifdef VBOX_WITH_OLD_VBVA_LOCK
1874int Display::videoAccelRefreshProcess(void)
1875{
1876 int rc = VWRN_INVALID_STATE; /* Default is to do a display update in VGA device. */
1877
1878 vbvaLock();
1879
1880 if (ASMAtomicCmpXchgU32(&mfu32PendingVideoAccelDisable, false, true))
1881 {
1882 videoAccelEnable (false, NULL);
1883 }
1884 else if (mfPendingVideoAccelEnable)
1885 {
1886 /* Acceleration was enabled while machine was not yet running
1887 * due to restoring from saved state. Update entire display and
1888 * actually enable acceleration.
1889 */
1890 Assert(mpPendingVbvaMemory);
1891
1892 /* Acceleration can not be yet enabled.*/
1893 Assert(mpVbvaMemory == NULL);
1894 Assert(!mfVideoAccelEnabled);
1895
1896 if (mfMachineRunning)
1897 {
1898 videoAccelEnable (mfPendingVideoAccelEnable,
1899 mpPendingVbvaMemory);
1900
1901 /* Reset the pending state. */
1902 mfPendingVideoAccelEnable = false;
1903 mpPendingVbvaMemory = NULL;
1904 }
1905
1906 rc = VINF_TRY_AGAIN;
1907 }
1908 else
1909 {
1910 Assert(mpPendingVbvaMemory == NULL);
1911
1912 if (mfVideoAccelEnabled)
1913 {
1914 Assert(mpVbvaMemory);
1915 videoAccelFlush ();
1916
1917 rc = VINF_SUCCESS; /* VBVA processed, no need to a display update. */
1918 }
1919 }
1920
1921 vbvaUnlock();
1922
1923 return rc;
1924}
1925#endif /* VBOX_WITH_OLD_VBVA_LOCK */
1926
1927
1928// IDisplay properties
1929/////////////////////////////////////////////////////////////////////////////
1930
1931/**
1932 * Returns the current display width in pixel
1933 *
1934 * @returns COM status code
1935 * @param width Address of result variable.
1936 */
1937STDMETHODIMP Display::COMGETTER(Width) (ULONG *width)
1938{
1939 CheckComArgNotNull(width);
1940
1941 AutoCaller autoCaller(this);
1942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1943
1944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1945
1946 CHECK_CONSOLE_DRV (mpDrv);
1947
1948 *width = mpDrv->Connector.cx;
1949
1950 return S_OK;
1951}
1952
1953/**
1954 * Returns the current display height in pixel
1955 *
1956 * @returns COM status code
1957 * @param height Address of result variable.
1958 */
1959STDMETHODIMP Display::COMGETTER(Height) (ULONG *height)
1960{
1961 CheckComArgNotNull(height);
1962
1963 AutoCaller autoCaller(this);
1964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1965
1966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1967
1968 CHECK_CONSOLE_DRV (mpDrv);
1969
1970 *height = mpDrv->Connector.cy;
1971
1972 return S_OK;
1973}
1974
1975/**
1976 * Returns the current display color depth in bits
1977 *
1978 * @returns COM status code
1979 * @param bitsPerPixel Address of result variable.
1980 */
1981STDMETHODIMP Display::COMGETTER(BitsPerPixel) (ULONG *bitsPerPixel)
1982{
1983 if (!bitsPerPixel)
1984 return E_INVALIDARG;
1985
1986 AutoCaller autoCaller(this);
1987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1988
1989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1990
1991 CHECK_CONSOLE_DRV (mpDrv);
1992
1993 uint32_t cBits = 0;
1994 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
1995 AssertRC(rc);
1996 *bitsPerPixel = cBits;
1997
1998 return S_OK;
1999}
2000
2001
2002// IDisplay methods
2003/////////////////////////////////////////////////////////////////////////////
2004
2005STDMETHODIMP Display::SetFramebuffer (ULONG aScreenId,
2006 IFramebuffer *aFramebuffer)
2007{
2008 LogFlowFunc (("\n"));
2009
2010 if (aFramebuffer != NULL)
2011 CheckComArgOutPointerValid(aFramebuffer);
2012
2013 AutoCaller autoCaller(this);
2014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2015
2016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2017
2018 Console::SafeVMPtrQuiet pVM (mParent);
2019 if (pVM.isOk())
2020 {
2021 /* Must leave the lock here because the changeFramebuffer will
2022 * also obtain it. */
2023 alock.leave ();
2024
2025 /* send request to the EMT thread */
2026 int vrc = VMR3ReqCallWait (pVM, VMCPUID_ANY,
2027 (PFNRT) changeFramebuffer, 3, this, aFramebuffer, aScreenId);
2028
2029 alock.enter ();
2030
2031 ComAssertRCRet (vrc, E_FAIL);
2032 }
2033 else
2034 {
2035 /* No VM is created (VM is powered off), do a direct call */
2036 int vrc = changeFramebuffer (this, aFramebuffer, aScreenId);
2037 ComAssertRCRet (vrc, E_FAIL);
2038 }
2039
2040 return S_OK;
2041}
2042
2043STDMETHODIMP Display::GetFramebuffer (ULONG aScreenId,
2044 IFramebuffer **aFramebuffer, LONG *aXOrigin, LONG *aYOrigin)
2045{
2046 LogFlowFunc (("aScreenId = %d\n", aScreenId));
2047
2048 CheckComArgOutPointerValid(aFramebuffer);
2049
2050 AutoCaller autoCaller(this);
2051 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2052
2053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2054
2055 /* @todo this should be actually done on EMT. */
2056 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2057
2058 *aFramebuffer = pFBInfo->pFramebuffer;
2059 if (*aFramebuffer)
2060 (*aFramebuffer)->AddRef ();
2061 if (aXOrigin)
2062 *aXOrigin = pFBInfo->xOrigin;
2063 if (aYOrigin)
2064 *aYOrigin = pFBInfo->yOrigin;
2065
2066 return S_OK;
2067}
2068
2069STDMETHODIMP Display::SetVideoModeHint(ULONG aWidth, ULONG aHeight,
2070 ULONG aBitsPerPixel, ULONG aDisplay)
2071{
2072 AutoCaller autoCaller(this);
2073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2074
2075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2076
2077 CHECK_CONSOLE_DRV (mpDrv);
2078
2079 /*
2080 * Do some rough checks for valid input
2081 */
2082 ULONG width = aWidth;
2083 if (!width)
2084 width = mpDrv->Connector.cx;
2085 ULONG height = aHeight;
2086 if (!height)
2087 height = mpDrv->Connector.cy;
2088 ULONG bpp = aBitsPerPixel;
2089 if (!bpp)
2090 {
2091 uint32_t cBits = 0;
2092 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
2093 AssertRC(rc);
2094 bpp = cBits;
2095 }
2096 ULONG cMonitors;
2097 mParent->machine()->COMGETTER(MonitorCount)(&cMonitors);
2098 if (cMonitors == 0 && aDisplay > 0)
2099 return E_INVALIDARG;
2100 if (aDisplay >= cMonitors)
2101 return E_INVALIDARG;
2102
2103// sunlover 20070614: It is up to the guest to decide whether the hint is valid.
2104// ULONG vramSize;
2105// mParent->machine()->COMGETTER(VRAMSize)(&vramSize);
2106// /* enough VRAM? */
2107// if ((width * height * (bpp / 8)) > (vramSize * 1024 * 1024))
2108// return setError(E_FAIL, tr("Not enough VRAM for the selected video mode"));
2109
2110 /* Have to leave the lock because the pfnRequestDisplayChange
2111 * will call EMT. */
2112 alock.leave ();
2113 if (mParent->getVMMDev())
2114 mParent->getVMMDev()->getVMMDevPort()->
2115 pfnRequestDisplayChange (mParent->getVMMDev()->getVMMDevPort(),
2116 aWidth, aHeight, aBitsPerPixel, aDisplay);
2117 return S_OK;
2118}
2119
2120STDMETHODIMP Display::SetSeamlessMode (BOOL enabled)
2121{
2122 AutoCaller autoCaller(this);
2123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2124
2125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2126
2127 /* Have to leave the lock because the pfnRequestSeamlessChange will call EMT. */
2128 alock.leave ();
2129 if (mParent->getVMMDev())
2130 mParent->getVMMDev()->getVMMDevPort()->
2131 pfnRequestSeamlessChange (mParent->getVMMDev()->getVMMDevPort(),
2132 !!enabled);
2133 return S_OK;
2134}
2135
2136#ifdef VBOX_WITH_OLD_VBVA_LOCK
2137int Display::displayTakeScreenshotEMT(Display *pDisplay, uint8_t **ppu8Data, size_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height)
2138{
2139 int rc;
2140 pDisplay->vbvaLock();
2141 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppu8Data, pcbData, pu32Width, pu32Height);
2142 pDisplay->vbvaUnlock();
2143 return rc;
2144}
2145#endif /* VBOX_WITH_OLD_VBVA_LOCK */
2146
2147#ifdef VBOX_WITH_OLD_VBVA_LOCK
2148static int displayTakeScreenshot(PVM pVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, BYTE *address, ULONG width, ULONG height)
2149#else
2150static int displayTakeScreenshot(PVM pVM, struct DRVMAINDISPLAY *pDrv, BYTE *address, ULONG width, ULONG height)
2151#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
2152{
2153 uint8_t *pu8Data = NULL;
2154 size_t cbData = 0;
2155 uint32_t cx = 0;
2156 uint32_t cy = 0;
2157
2158#ifdef VBOX_WITH_OLD_VBVA_LOCK
2159 int vrc = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)Display::displayTakeScreenshotEMT, 5,
2160 pDisplay, &pu8Data, &cbData, &cx, &cy);
2161#else
2162 /* @todo pfnTakeScreenshot is probably callable from any thread, because it uses the VGA device lock. */
2163 int vrc = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)pDrv->pUpPort->pfnTakeScreenshot, 5,
2164 pDrv->pUpPort, &pu8Data, &cbData, &cx, &cy);
2165#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
2166
2167 if (RT_SUCCESS(vrc))
2168 {
2169 if (cx == width && cy == height)
2170 {
2171 /* No scaling required. */
2172 memcpy(address, pu8Data, cbData);
2173 }
2174 else
2175 {
2176 /* Scale. */
2177 LogFlowFunc(("SCALE: %dx%d -> %dx%d\n", cx, cy, width, height));
2178
2179 uint8_t *dst = address;
2180 uint8_t *src = pu8Data;
2181 int dstX = 0;
2182 int dstY = 0;
2183 int srcX = 0;
2184 int srcY = 0;
2185 int dstW = width;
2186 int dstH = height;
2187 int srcW = cx;
2188 int srcH = cy;
2189 gdImageCopyResampled (dst,
2190 src,
2191 dstX, dstY,
2192 srcX, srcY,
2193 dstW, dstH, srcW, srcH);
2194 }
2195
2196 /* This can be called from any thread. */
2197 pDrv->pUpPort->pfnFreeScreenshot (pDrv->pUpPort, pu8Data);
2198 }
2199
2200 return vrc;
2201}
2202
2203STDMETHODIMP Display::TakeScreenShot (BYTE *address, ULONG width, ULONG height)
2204{
2205 /// @todo (r=dmik) this function may take too long to complete if the VM
2206 // is doing something like saving state right now. Which, in case if it
2207 // is called on the GUI thread, will make it unresponsive. We should
2208 // check the machine state here (by enclosing the check and VMRequCall
2209 // within the Console lock to make it atomic).
2210
2211 LogFlowFuncEnter();
2212 LogFlowFunc (("address=%p, width=%d, height=%d\n",
2213 address, width, height));
2214
2215 CheckComArgNotNull(address);
2216 CheckComArgExpr(width, width != 0);
2217 CheckComArgExpr(height, height != 0);
2218
2219 AutoCaller autoCaller(this);
2220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2221
2222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2223
2224 CHECK_CONSOLE_DRV (mpDrv);
2225
2226 Console::SafeVMPtr pVM(mParent);
2227 if (FAILED(pVM.rc())) return pVM.rc();
2228
2229 HRESULT rc = S_OK;
2230
2231 LogFlowFunc (("Sending SCREENSHOT request\n"));
2232
2233 /* Leave lock because other thread (EMT) is called and it may initiate a resize
2234 * which also needs lock.
2235 *
2236 * This method does not need the lock anymore.
2237 */
2238 alock.leave();
2239
2240#ifdef VBOX_WITH_OLD_VBVA_LOCK
2241 int vrc = displayTakeScreenshot(pVM, this, mpDrv, address, width, height);
2242#else
2243 int vrc = displayTakeScreenshot(pVM, mpDrv, address, width, height);
2244#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
2245
2246 if (vrc == VERR_NOT_IMPLEMENTED)
2247 rc = setError (E_NOTIMPL,
2248 tr ("This feature is not implemented"));
2249 else if (RT_FAILURE(vrc))
2250 rc = setError (VBOX_E_IPRT_ERROR,
2251 tr ("Could not take a screenshot (%Rrc)"), vrc);
2252
2253 LogFlowFunc (("rc=%08X\n", rc));
2254 LogFlowFuncLeave();
2255 return rc;
2256}
2257
2258STDMETHODIMP Display::TakeScreenShotSlow (ULONG width, ULONG height,
2259 ComSafeArrayOut(BYTE, aScreenData))
2260{
2261 LogFlowFuncEnter();
2262 LogFlowFunc (("width=%d, height=%d\n",
2263 width, height));
2264
2265 CheckComArgSafeArrayNotNull(aScreenData);
2266 CheckComArgExpr(width, width != 0);
2267 CheckComArgExpr(height, height != 0);
2268
2269 AutoCaller autoCaller(this);
2270 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2271
2272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2273
2274 CHECK_CONSOLE_DRV (mpDrv);
2275
2276 Console::SafeVMPtr pVM(mParent);
2277 if (FAILED(pVM.rc())) return pVM.rc();
2278
2279 HRESULT rc = S_OK;
2280
2281 LogFlowFunc (("Sending SCREENSHOT request\n"));
2282
2283 /* Leave lock because other thread (EMT) is called and it may initiate a resize
2284 * which also needs lock.
2285 *
2286 * This method does not need the lock anymore.
2287 */
2288 alock.leave();
2289
2290 size_t cbData = width * 4 * height;
2291 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbData);
2292
2293 if (!pu8Data)
2294 return E_OUTOFMEMORY;
2295
2296#ifdef VBOX_WITH_OLD_VBVA_LOCK
2297 int vrc = displayTakeScreenshot(pVM, this, mpDrv, pu8Data, width, height);
2298#else
2299 int vrc = displayTakeScreenshot(pVM, mpDrv, pu8Data, width, height);
2300#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
2301
2302 if (RT_SUCCESS(vrc))
2303 {
2304 /* Convert pixels to format expected by the API caller: [0] R, [1] G, [2] B, [3] A. */
2305 uint8_t *pu8 = pu8Data;
2306 unsigned cPixels = width * height;
2307 while (cPixels)
2308 {
2309 uint8_t u8 = pu8[0];
2310 pu8[0] = pu8[2];
2311 pu8[2] = u8;
2312 pu8[3] = 0xff;
2313 cPixels--;
2314 pu8 += 4;
2315 }
2316
2317 com::SafeArray<BYTE> screenData (cbData);
2318 for (unsigned i = 0; i < cbData; i++)
2319 screenData[i] = pu8Data[i];
2320 screenData.detachTo(ComSafeArrayOutArg(aScreenData));
2321 }
2322 else if (vrc == VERR_NOT_IMPLEMENTED)
2323 rc = setError (E_NOTIMPL,
2324 tr ("This feature is not implemented"));
2325 else
2326 rc = setError (VBOX_E_IPRT_ERROR,
2327 tr ("Could not take a screenshot (%Rrc)"), vrc);
2328
2329 LogFlowFunc (("rc=%08X\n", rc));
2330 LogFlowFuncLeave();
2331 return rc;
2332}
2333
2334#ifdef VBOX_WITH_OLD_VBVA_LOCK
2335int Display::DrawToScreenEMT(Display *pDisplay, BYTE *address, ULONG x, ULONG y, ULONG width, ULONG height)
2336{
2337 int rc;
2338 pDisplay->vbvaLock();
2339 rc = pDisplay->mpDrv->pUpPort->pfnDisplayBlt(pDisplay->mpDrv->pUpPort, address, x, y, width, height);
2340 pDisplay->vbvaUnlock();
2341 return rc;
2342}
2343#endif /* VBOX_WITH_OLD_VBVA_LOCK */
2344
2345STDMETHODIMP Display::DrawToScreen (BYTE *address, ULONG x, ULONG y,
2346 ULONG width, ULONG height)
2347{
2348 /// @todo (r=dmik) this function may take too long to complete if the VM
2349 // is doing something like saving state right now. Which, in case if it
2350 // is called on the GUI thread, will make it unresponsive. We should
2351 // check the machine state here (by enclosing the check and VMRequCall
2352 // within the Console lock to make it atomic).
2353
2354 LogFlowFuncEnter();
2355 LogFlowFunc (("address=%p, x=%d, y=%d, width=%d, height=%d\n",
2356 (void *)address, x, y, width, height));
2357
2358 CheckComArgNotNull(address);
2359 CheckComArgExpr(width, width != 0);
2360 CheckComArgExpr(height, height != 0);
2361
2362 AutoCaller autoCaller(this);
2363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2364
2365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2366
2367 CHECK_CONSOLE_DRV (mpDrv);
2368
2369 Console::SafeVMPtr pVM(mParent);
2370 if (FAILED(pVM.rc())) return pVM.rc();
2371
2372 /*
2373 * Again we're lazy and make the graphics device do all the
2374 * dirty conversion work.
2375 */
2376#ifdef VBOX_WITH_OLD_VBVA_LOCK
2377 int rcVBox = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)Display::DrawToScreenEMT, 6,
2378 this, address, x, y, width, height);
2379#else
2380 int rcVBox = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)mpDrv->pUpPort->pfnDisplayBlt, 6,
2381 mpDrv->pUpPort, address, x, y, width, height);
2382#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
2383
2384 /*
2385 * If the function returns not supported, we'll have to do all the
2386 * work ourselves using the framebuffer.
2387 */
2388 HRESULT rc = S_OK;
2389 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
2390 {
2391 /** @todo implement generic fallback for screen blitting. */
2392 rc = E_NOTIMPL;
2393 }
2394 else if (RT_FAILURE(rcVBox))
2395 rc = setError (VBOX_E_IPRT_ERROR,
2396 tr ("Could not draw to the screen (%Rrc)"), rcVBox);
2397//@todo
2398// else
2399// {
2400// /* All ok. Redraw the screen. */
2401// handleDisplayUpdate (x, y, width, height);
2402// }
2403
2404 LogFlowFunc (("rc=%08X\n", rc));
2405 LogFlowFuncLeave();
2406 return rc;
2407}
2408
2409#ifdef VBOX_WITH_OLD_VBVA_LOCK
2410void Display::InvalidateAndUpdateEMT(Display *pDisplay)
2411{
2412 pDisplay->vbvaLock();
2413 pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort);
2414 pDisplay->vbvaUnlock();
2415}
2416#endif /* VBOX_WITH_OLD_VBVA_LOCK */
2417
2418/**
2419 * Does a full invalidation of the VM display and instructs the VM
2420 * to update it immediately.
2421 *
2422 * @returns COM status code
2423 */
2424STDMETHODIMP Display::InvalidateAndUpdate()
2425{
2426 LogFlowFuncEnter();
2427
2428 AutoCaller autoCaller(this);
2429 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2430
2431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2432
2433 CHECK_CONSOLE_DRV (mpDrv);
2434
2435 Console::SafeVMPtr pVM(mParent);
2436 if (FAILED(pVM.rc())) return pVM.rc();
2437
2438 HRESULT rc = S_OK;
2439
2440 LogFlowFunc (("Sending DPYUPDATE request\n"));
2441
2442 /* Have to leave the lock when calling EMT. */
2443 alock.leave ();
2444
2445 /* pdm.h says that this has to be called from the EMT thread */
2446#ifdef VBOX_WITH_OLD_VBVA_LOCK
2447 int rcVBox = VMR3ReqCallVoidWait(pVM, VMCPUID_ANY, (PFNRT)Display::InvalidateAndUpdateEMT,
2448 1, this);
2449#else
2450 int rcVBox = VMR3ReqCallVoidWait(pVM, VMCPUID_ANY,
2451 (PFNRT)mpDrv->pUpPort->pfnUpdateDisplayAll, 1, mpDrv->pUpPort);
2452#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
2453 alock.enter ();
2454
2455 if (RT_FAILURE(rcVBox))
2456 rc = setError (VBOX_E_IPRT_ERROR,
2457 tr ("Could not invalidate and update the screen (%Rrc)"), rcVBox);
2458
2459 LogFlowFunc (("rc=%08X\n", rc));
2460 LogFlowFuncLeave();
2461 return rc;
2462}
2463
2464/**
2465 * Notification that the framebuffer has completed the
2466 * asynchronous resize processing
2467 *
2468 * @returns COM status code
2469 */
2470STDMETHODIMP Display::ResizeCompleted(ULONG aScreenId)
2471{
2472 LogFlowFunc (("\n"));
2473
2474 /// @todo (dmik) can we AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); here?
2475 // do it when we switch this class to VirtualBoxBase_NEXT.
2476 // This will require general code review and may add some details.
2477 // In particular, we may want to check whether EMT is really waiting for
2478 // this notification, etc. It might be also good to obey the caller to make
2479 // sure this method is not called from more than one thread at a time
2480 // (and therefore don't use Display lock at all here to save some
2481 // milliseconds).
2482 AutoCaller autoCaller(this);
2483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2484
2485 /* this is only valid for external framebuffers */
2486 if (maFramebuffers[aScreenId].pFramebuffer == NULL)
2487 return setError (VBOX_E_NOT_SUPPORTED,
2488 tr ("Resize completed notification is valid only "
2489 "for external framebuffers"));
2490
2491 /* Set the flag indicating that the resize has completed and display
2492 * data need to be updated. */
2493 bool f = ASMAtomicCmpXchgU32 (&maFramebuffers[aScreenId].u32ResizeStatus,
2494 ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
2495 AssertRelease(f);NOREF(f);
2496
2497 return S_OK;
2498}
2499
2500/**
2501 * Notification that the framebuffer has completed the
2502 * asynchronous update processing
2503 *
2504 * @returns COM status code
2505 */
2506STDMETHODIMP Display::UpdateCompleted()
2507{
2508 LogFlowFunc (("\n"));
2509
2510 /// @todo (dmik) can we AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); here?
2511 // do it when we switch this class to VirtualBoxBase_NEXT.
2512 // Tthis will require general code review and may add some details.
2513 // In particular, we may want to check whether EMT is really waiting for
2514 // this notification, etc. It might be also good to obey the caller to make
2515 // sure this method is not called from more than one thread at a time
2516 // (and therefore don't use Display lock at all here to save some
2517 // milliseconds).
2518 AutoCaller autoCaller(this);
2519 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2520
2521 /* this is only valid for external framebuffers */
2522 if (maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].pFramebuffer == NULL)
2523 return setError (VBOX_E_NOT_SUPPORTED,
2524 tr ("Resize completed notification is valid only "
2525 "for external framebuffers"));
2526
2527 return S_OK;
2528}
2529
2530STDMETHODIMP Display::CompleteVHWACommand(BYTE *pCommand)
2531{
2532#ifdef VBOX_WITH_VIDEOHWACCEL
2533 mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsynch(mpDrv->pVBVACallbacks, (PVBOXVHWACMD)pCommand);
2534 return S_OK;
2535#else
2536 return E_NOTIMPL;
2537#endif
2538}
2539
2540// private methods
2541/////////////////////////////////////////////////////////////////////////////
2542
2543/**
2544 * Helper to update the display information from the framebuffer.
2545 *
2546 * @param aCheckParams true to compare the parameters of the current framebuffer
2547 * and the new one and issue handleDisplayResize()
2548 * if they differ.
2549 * @thread EMT
2550 */
2551void Display::updateDisplayData (bool aCheckParams /* = false */)
2552{
2553 /* the driver might not have been constructed yet */
2554 if (!mpDrv)
2555 return;
2556
2557#if DEBUG
2558 /*
2559 * Sanity check. Note that this method may be called on EMT after Console
2560 * has started the power down procedure (but before our #drvDestruct() is
2561 * called, in which case pVM will aleady be NULL but mpDrv will not). Since
2562 * we don't really need pVM to proceed, we avoid this check in the release
2563 * build to save some ms (necessary to construct SafeVMPtrQuiet) in this
2564 * time-critical method.
2565 */
2566 Console::SafeVMPtrQuiet pVM (mParent);
2567 if (pVM.isOk())
2568 VM_ASSERT_EMT (pVM.raw());
2569#endif
2570
2571 /* The method is only relevant to the primary framebuffer. */
2572 IFramebuffer *pFramebuffer = maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].pFramebuffer;
2573
2574 if (pFramebuffer)
2575 {
2576 HRESULT rc;
2577 BYTE *address = 0;
2578 rc = pFramebuffer->COMGETTER(Address) (&address);
2579 AssertComRC (rc);
2580 ULONG bytesPerLine = 0;
2581 rc = pFramebuffer->COMGETTER(BytesPerLine) (&bytesPerLine);
2582 AssertComRC (rc);
2583 ULONG bitsPerPixel = 0;
2584 rc = pFramebuffer->COMGETTER(BitsPerPixel) (&bitsPerPixel);
2585 AssertComRC (rc);
2586 ULONG width = 0;
2587 rc = pFramebuffer->COMGETTER(Width) (&width);
2588 AssertComRC (rc);
2589 ULONG height = 0;
2590 rc = pFramebuffer->COMGETTER(Height) (&height);
2591 AssertComRC (rc);
2592
2593 /*
2594 * Check current parameters with new ones and issue handleDisplayResize()
2595 * to let the new frame buffer adjust itself properly. Note that it will
2596 * result into a recursive updateDisplayData() call but with
2597 * aCheckOld = false.
2598 */
2599 if (aCheckParams &&
2600 (mLastAddress != address ||
2601 mLastBytesPerLine != bytesPerLine ||
2602 mLastBitsPerPixel != bitsPerPixel ||
2603 mLastWidth != (int) width ||
2604 mLastHeight != (int) height))
2605 {
2606 handleDisplayResize (VBOX_VIDEO_PRIMARY_SCREEN, mLastBitsPerPixel,
2607 mLastAddress,
2608 mLastBytesPerLine,
2609 mLastWidth,
2610 mLastHeight);
2611 return;
2612 }
2613
2614 mpDrv->Connector.pu8Data = (uint8_t *) address;
2615 mpDrv->Connector.cbScanline = bytesPerLine;
2616 mpDrv->Connector.cBits = bitsPerPixel;
2617 mpDrv->Connector.cx = width;
2618 mpDrv->Connector.cy = height;
2619 }
2620 else
2621 {
2622 /* black hole */
2623 mpDrv->Connector.pu8Data = NULL;
2624 mpDrv->Connector.cbScanline = 0;
2625 mpDrv->Connector.cBits = 0;
2626 mpDrv->Connector.cx = 0;
2627 mpDrv->Connector.cy = 0;
2628 }
2629}
2630
2631/**
2632 * Changes the current frame buffer. Called on EMT to avoid both
2633 * race conditions and excessive locking.
2634 *
2635 * @note locks this object for writing
2636 * @thread EMT
2637 */
2638/* static */
2639DECLCALLBACK(int) Display::changeFramebuffer (Display *that, IFramebuffer *aFB,
2640 unsigned uScreenId)
2641{
2642 LogFlowFunc (("uScreenId = %d\n", uScreenId));
2643
2644 AssertReturn(that, VERR_INVALID_PARAMETER);
2645 AssertReturn(uScreenId < that->mcMonitors, VERR_INVALID_PARAMETER);
2646
2647 AutoCaller autoCaller(that);
2648 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2649
2650 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
2651
2652 DISPLAYFBINFO *pDisplayFBInfo = &that->maFramebuffers[uScreenId];
2653 pDisplayFBInfo->pFramebuffer = aFB;
2654
2655 that->mParent->consoleVRDPServer()->SendResize ();
2656
2657 that->updateDisplayData (true /* aCheckParams */);
2658
2659 return VINF_SUCCESS;
2660}
2661
2662/**
2663 * Handle display resize event issued by the VGA device for the primary screen.
2664 *
2665 * @see PDMIDISPLAYCONNECTOR::pfnResize
2666 */
2667DECLCALLBACK(int) Display::displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
2668 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
2669{
2670 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2671
2672 LogFlowFunc (("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
2673 bpp, pvVRAM, cbLine, cx, cy));
2674
2675 return pDrv->pDisplay->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy);
2676}
2677
2678/**
2679 * Handle display update.
2680 *
2681 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
2682 */
2683DECLCALLBACK(void) Display::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
2684 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
2685{
2686 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2687
2688#ifdef DEBUG_sunlover
2689 LogFlowFunc (("mfVideoAccelEnabled = %d, %d,%d %dx%d\n",
2690 pDrv->pDisplay->mfVideoAccelEnabled, x, y, cx, cy));
2691#endif /* DEBUG_sunlover */
2692
2693 /* This call does update regardless of VBVA status.
2694 * But in VBVA mode this is called only as result of
2695 * pfnUpdateDisplayAll in the VGA device.
2696 */
2697
2698 pDrv->pDisplay->handleDisplayUpdate(x, y, cx, cy);
2699}
2700
2701/**
2702 * Periodic display refresh callback.
2703 *
2704 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
2705 */
2706DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
2707{
2708 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2709
2710#ifdef DEBUG_sunlover
2711 STAM_PROFILE_START(&StatDisplayRefresh, a);
2712#endif /* DEBUG_sunlover */
2713
2714#ifdef DEBUG_sunlover_2
2715 LogFlowFunc (("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
2716 pDrv->pDisplay->mfVideoAccelEnabled));
2717#endif /* DEBUG_sunlover_2 */
2718
2719 Display *pDisplay = pDrv->pDisplay;
2720 bool fNoUpdate = false; /* Do not update the display if any of the framebuffers is being resized. */
2721 unsigned uScreenId;
2722
2723 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
2724 {
2725 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2726
2727 /* Check the resize status. The status can be checked normally because
2728 * the status affects only the EMT.
2729 */
2730 uint32_t u32ResizeStatus = pFBInfo->u32ResizeStatus;
2731
2732 if (u32ResizeStatus == ResizeStatus_UpdateDisplayData)
2733 {
2734 LogFlowFunc (("ResizeStatus_UpdateDisplayData %d\n", uScreenId));
2735 fNoUpdate = true; /* Always set it here, because pfnUpdateDisplayAll can cause a new resize. */
2736 /* The framebuffer was resized and display data need to be updated. */
2737 pDisplay->handleResizeCompletedEMT ();
2738 if (pFBInfo->u32ResizeStatus != ResizeStatus_Void)
2739 {
2740 /* The resize status could be not Void here because a pending resize is issued. */
2741 continue;
2742 }
2743 /* Continue with normal processing because the status here is ResizeStatus_Void. */
2744 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2745 {
2746 /* Repaint the display because VM continued to run during the framebuffer resize. */
2747 if (!pFBInfo->pFramebuffer.isNull())
2748#ifdef VBOX_WITH_OLD_VBVA_LOCK
2749 {
2750 pDisplay->vbvaLock();
2751#endif /* VBOX_WITH_OLD_VBVA_LOCK */
2752 pDrv->pUpPort->pfnUpdateDisplayAll(pDrv->pUpPort);
2753#ifdef VBOX_WITH_OLD_VBVA_LOCK
2754 pDisplay->vbvaUnlock();
2755 }
2756#endif /* VBOX_WITH_OLD_VBVA_LOCK */
2757 }
2758 }
2759 else if (u32ResizeStatus == ResizeStatus_InProgress)
2760 {
2761 /* The framebuffer is being resized. Do not call the VGA device back. Immediately return. */
2762 LogFlowFunc (("ResizeStatus_InProcess\n"));
2763 fNoUpdate = true;
2764 continue;
2765 }
2766 }
2767
2768 if (!fNoUpdate)
2769 {
2770#ifdef VBOX_WITH_OLD_VBVA_LOCK
2771 int rc = pDisplay->videoAccelRefreshProcess();
2772
2773 if (rc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
2774 {
2775 if (rc == VWRN_INVALID_STATE)
2776 {
2777 /* No VBVA do a display update. */
2778 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN];
2779 if (!pFBInfo->pFramebuffer.isNull() && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
2780 {
2781 Assert(pDrv->Connector.pu8Data);
2782 pDisplay->vbvaLock();
2783 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
2784 pDisplay->vbvaUnlock();
2785 }
2786 }
2787
2788 /* Inform the VRDP server that the current display update sequence is
2789 * completed. At this moment the framebuffer memory contains a definite
2790 * image, that is synchronized with the orders already sent to VRDP client.
2791 * The server can now process redraw requests from clients or initial
2792 * fullscreen updates for new clients.
2793 */
2794 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
2795 {
2796 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2797
2798 if (!pFBInfo->pFramebuffer.isNull() && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
2799 {
2800 Assert (pDisplay->mParent && pDisplay->mParent->consoleVRDPServer());
2801 pDisplay->mParent->consoleVRDPServer()->SendUpdate (uScreenId, NULL, 0);
2802 }
2803 }
2804 }
2805#else
2806 if (pDisplay->mfPendingVideoAccelEnable)
2807 {
2808 /* Acceleration was enabled while machine was not yet running
2809 * due to restoring from saved state. Update entire display and
2810 * actually enable acceleration.
2811 */
2812 Assert(pDisplay->mpPendingVbvaMemory);
2813
2814 /* Acceleration can not be yet enabled.*/
2815 Assert(pDisplay->mpVbvaMemory == NULL);
2816 Assert(!pDisplay->mfVideoAccelEnabled);
2817
2818 if (pDisplay->mfMachineRunning)
2819 {
2820 pDisplay->VideoAccelEnable (pDisplay->mfPendingVideoAccelEnable,
2821 pDisplay->mpPendingVbvaMemory);
2822
2823 /* Reset the pending state. */
2824 pDisplay->mfPendingVideoAccelEnable = false;
2825 pDisplay->mpPendingVbvaMemory = NULL;
2826 }
2827 }
2828 else
2829 {
2830 Assert(pDisplay->mpPendingVbvaMemory == NULL);
2831
2832 if (pDisplay->mfVideoAccelEnabled)
2833 {
2834 Assert(pDisplay->mpVbvaMemory);
2835 pDisplay->VideoAccelFlush ();
2836 }
2837 else
2838 {
2839 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN];
2840 if (!pFBInfo->pFramebuffer.isNull())
2841 {
2842 Assert(pDrv->Connector.pu8Data);
2843 Assert(pFBInfo->u32ResizeStatus == ResizeStatus_Void);
2844 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
2845 }
2846 }
2847
2848 /* Inform the VRDP server that the current display update sequence is
2849 * completed. At this moment the framebuffer memory contains a definite
2850 * image, that is synchronized with the orders already sent to VRDP client.
2851 * The server can now process redraw requests from clients or initial
2852 * fullscreen updates for new clients.
2853 */
2854 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
2855 {
2856 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2857
2858 if (!pFBInfo->pFramebuffer.isNull() && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
2859 {
2860 Assert (pDisplay->mParent && pDisplay->mParent->consoleVRDPServer());
2861 pDisplay->mParent->consoleVRDPServer()->SendUpdate (uScreenId, NULL, 0);
2862 }
2863 }
2864 }
2865#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
2866 }
2867
2868#ifdef DEBUG_sunlover
2869 STAM_PROFILE_STOP(&StatDisplayRefresh, a);
2870#endif /* DEBUG_sunlover */
2871#ifdef DEBUG_sunlover_2
2872 LogFlowFunc (("leave\n"));
2873#endif /* DEBUG_sunlover_2 */
2874}
2875
2876/**
2877 * Reset notification
2878 *
2879 * @see PDMIDISPLAYCONNECTOR::pfnReset
2880 */
2881DECLCALLBACK(void) Display::displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
2882{
2883 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2884
2885 LogFlowFunc (("\n"));
2886
2887 /* Disable VBVA mode. */
2888 pDrv->pDisplay->VideoAccelEnable (false, NULL);
2889}
2890
2891/**
2892 * LFBModeChange notification
2893 *
2894 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
2895 */
2896DECLCALLBACK(void) Display::displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
2897{
2898 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2899
2900 LogFlowFunc (("fEnabled=%d\n", fEnabled));
2901
2902 NOREF(fEnabled);
2903
2904 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
2905#ifdef VBOX_WITH_OLD_VBVA_LOCK
2906 /* This is called under DevVGA lock. Postpone disabling VBVA, do it in the refresh timer. */
2907 ASMAtomicWriteU32(&pDrv->pDisplay->mfu32PendingVideoAccelDisable, true);
2908#else
2909 pDrv->pDisplay->VideoAccelEnable (false, NULL);
2910#endif /* !VBOX_WITH_OLD_VBVA_LOCK */
2911}
2912
2913/**
2914 * Adapter information change notification.
2915 *
2916 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
2917 */
2918DECLCALLBACK(void) Display::displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, uint32_t u32VRAMSize)
2919{
2920 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2921
2922 if (pvVRAM == NULL)
2923 {
2924 unsigned i;
2925 for (i = 0; i < pDrv->pDisplay->mcMonitors; i++)
2926 {
2927 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[i];
2928
2929 pFBInfo->u32Offset = 0;
2930 pFBInfo->u32MaxFramebufferSize = 0;
2931 pFBInfo->u32InformationSize = 0;
2932 }
2933 }
2934#ifndef VBOX_WITH_HGSMI
2935 else
2936 {
2937 uint8_t *pu8 = (uint8_t *)pvVRAM;
2938 pu8 += u32VRAMSize - VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
2939
2940 // @todo
2941 uint8_t *pu8End = pu8 + VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
2942
2943 VBOXVIDEOINFOHDR *pHdr;
2944
2945 for (;;)
2946 {
2947 pHdr = (VBOXVIDEOINFOHDR *)pu8;
2948 pu8 += sizeof (VBOXVIDEOINFOHDR);
2949
2950 if (pu8 >= pu8End)
2951 {
2952 LogRel(("VBoxVideo: Guest adapter information overflow!!!\n"));
2953 break;
2954 }
2955
2956 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_DISPLAY)
2957 {
2958 if (pHdr->u16Length != sizeof (VBOXVIDEOINFODISPLAY))
2959 {
2960 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "DISPLAY", pHdr->u16Length));
2961 break;
2962 }
2963
2964 VBOXVIDEOINFODISPLAY *pDisplay = (VBOXVIDEOINFODISPLAY *)pu8;
2965
2966 if (pDisplay->u32Index >= pDrv->pDisplay->mcMonitors)
2967 {
2968 LogRel(("VBoxVideo: Guest adapter information invalid display index %d!!!\n", pDisplay->u32Index));
2969 break;
2970 }
2971
2972 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[pDisplay->u32Index];
2973
2974 pFBInfo->u32Offset = pDisplay->u32Offset;
2975 pFBInfo->u32MaxFramebufferSize = pDisplay->u32FramebufferSize;
2976 pFBInfo->u32InformationSize = pDisplay->u32InformationSize;
2977
2978 LogFlow(("VBOX_VIDEO_INFO_TYPE_DISPLAY: %d: at 0x%08X, size 0x%08X, info 0x%08X\n", pDisplay->u32Index, pDisplay->u32Offset, pDisplay->u32FramebufferSize, pDisplay->u32InformationSize));
2979 }
2980 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_QUERY_CONF32)
2981 {
2982 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOQUERYCONF32))
2983 {
2984 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "CONF32", pHdr->u16Length));
2985 break;
2986 }
2987
2988 VBOXVIDEOINFOQUERYCONF32 *pConf32 = (VBOXVIDEOINFOQUERYCONF32 *)pu8;
2989
2990 switch (pConf32->u32Index)
2991 {
2992 case VBOX_VIDEO_QCI32_MONITOR_COUNT:
2993 {
2994 pConf32->u32Value = pDrv->pDisplay->mcMonitors;
2995 } break;
2996
2997 case VBOX_VIDEO_QCI32_OFFSCREEN_HEAP_SIZE:
2998 {
2999 /* @todo make configurable. */
3000 pConf32->u32Value = _1M;
3001 } break;
3002
3003 default:
3004 LogRel(("VBoxVideo: CONF32 %d not supported!!! Skipping.\n", pConf32->u32Index));
3005 }
3006 }
3007 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
3008 {
3009 if (pHdr->u16Length != 0)
3010 {
3011 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
3012 break;
3013 }
3014
3015 break;
3016 }
3017 else if (pHdr->u8Type != VBOX_VIDEO_INFO_TYPE_NV_HEAP) /** @todo why is Additions/WINNT/Graphics/Miniport/VBoxVideo.cpp pushing this to us? */
3018 {
3019 LogRel(("Guest adapter information contains unsupported type %d. The block has been skipped.\n", pHdr->u8Type));
3020 }
3021
3022 pu8 += pHdr->u16Length;
3023 }
3024 }
3025#endif /* !VBOX_WITH_HGSMI */
3026}
3027
3028/**
3029 * Display information change notification.
3030 *
3031 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
3032 */
3033DECLCALLBACK(void) Display::displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, unsigned uScreenId)
3034{
3035 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3036
3037 if (uScreenId >= pDrv->pDisplay->mcMonitors)
3038 {
3039 LogRel(("VBoxVideo: Guest display information invalid display index %d!!!\n", uScreenId));
3040 return;
3041 }
3042
3043 /* Get the display information structure. */
3044 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[uScreenId];
3045
3046 uint8_t *pu8 = (uint8_t *)pvVRAM;
3047 pu8 += pFBInfo->u32Offset + pFBInfo->u32MaxFramebufferSize;
3048
3049 // @todo
3050 uint8_t *pu8End = pu8 + pFBInfo->u32InformationSize;
3051
3052 VBOXVIDEOINFOHDR *pHdr;
3053
3054 for (;;)
3055 {
3056 pHdr = (VBOXVIDEOINFOHDR *)pu8;
3057 pu8 += sizeof (VBOXVIDEOINFOHDR);
3058
3059 if (pu8 >= pu8End)
3060 {
3061 LogRel(("VBoxVideo: Guest display information overflow!!!\n"));
3062 break;
3063 }
3064
3065 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_SCREEN)
3066 {
3067 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOSCREEN))
3068 {
3069 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "SCREEN", pHdr->u16Length));
3070 break;
3071 }
3072
3073 VBOXVIDEOINFOSCREEN *pScreen = (VBOXVIDEOINFOSCREEN *)pu8;
3074
3075 pFBInfo->xOrigin = pScreen->xOrigin;
3076 pFBInfo->yOrigin = pScreen->yOrigin;
3077
3078 pFBInfo->w = pScreen->u16Width;
3079 pFBInfo->h = pScreen->u16Height;
3080
3081 LogFlow(("VBOX_VIDEO_INFO_TYPE_SCREEN: (%p) %d: at %d,%d, linesize 0x%X, size %dx%d, bpp %d, flags 0x%02X\n",
3082 pHdr, uScreenId, pScreen->xOrigin, pScreen->yOrigin, pScreen->u32LineSize, pScreen->u16Width, pScreen->u16Height, pScreen->bitsPerPixel, pScreen->u8Flags));
3083
3084 if (uScreenId != VBOX_VIDEO_PRIMARY_SCREEN)
3085 {
3086 /* Primary screen resize is initiated by the VGA device. */
3087 pDrv->pDisplay->handleDisplayResize(uScreenId, pScreen->bitsPerPixel, (uint8_t *)pvVRAM + pFBInfo->u32Offset, pScreen->u32LineSize, pScreen->u16Width, pScreen->u16Height);
3088 }
3089 }
3090 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
3091 {
3092 if (pHdr->u16Length != 0)
3093 {
3094 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
3095 break;
3096 }
3097
3098 break;
3099 }
3100 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_HOST_EVENTS)
3101 {
3102 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOHOSTEVENTS))
3103 {
3104 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "HOST_EVENTS", pHdr->u16Length));
3105 break;
3106 }
3107
3108 VBOXVIDEOINFOHOSTEVENTS *pHostEvents = (VBOXVIDEOINFOHOSTEVENTS *)pu8;
3109
3110 pFBInfo->pHostEvents = pHostEvents;
3111
3112 LogFlow(("VBOX_VIDEO_INFO_TYPE_HOSTEVENTS: (%p)\n",
3113 pHostEvents));
3114 }
3115 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_LINK)
3116 {
3117 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOLINK))
3118 {
3119 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "LINK", pHdr->u16Length));
3120 break;
3121 }
3122
3123 VBOXVIDEOINFOLINK *pLink = (VBOXVIDEOINFOLINK *)pu8;
3124 pu8 += pLink->i32Offset;
3125 }
3126 else
3127 {
3128 LogRel(("Guest display information contains unsupported type %d\n", pHdr->u8Type));
3129 }
3130
3131 pu8 += pHdr->u16Length;
3132 }
3133}
3134
3135#ifdef VBOX_WITH_VIDEOHWACCEL
3136
3137void Display::handleVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
3138{
3139 unsigned id = (unsigned)pCommand->iDisplay;
3140 int rc = VINF_SUCCESS;
3141 if(id < mcMonitors)
3142 {
3143 IFramebuffer *pFramebuffer = maFramebuffers[id].pFramebuffer;
3144
3145 if (pFramebuffer != NULL)
3146 {
3147 pFramebuffer->Lock();
3148
3149 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
3150 if(FAILED(hr))
3151 {
3152 rc = (hr == E_NOTIMPL) ? VERR_NOT_IMPLEMENTED : VERR_GENERAL_FAILURE;
3153 }
3154
3155 pFramebuffer->Unlock();
3156 }
3157 else
3158 {
3159 rc = VERR_NOT_IMPLEMENTED;
3160 }
3161 }
3162 else
3163 {
3164 rc = VERR_INVALID_PARAMETER;
3165 }
3166
3167 if(RT_FAILURE(rc))
3168 {
3169 /* tell the guest the command is complete */
3170 pCommand->Flags &= (~VBOXVHWACMD_FLAG_HG_ASYNCH);
3171 pCommand->rc = rc;
3172 }
3173}
3174
3175DECLCALLBACK(void) Display::displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
3176{
3177 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3178
3179 pDrv->pDisplay->handleVHWACommandProcess(pInterface, pCommand);
3180}
3181#endif
3182
3183#ifdef VBOX_WITH_HGSMI
3184DECLCALLBACK(int) Display::displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, PVBVAHOSTFLAGS pHostFlags)
3185{
3186 LogFlowFunc(("uScreenId %d\n", uScreenId));
3187
3188 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3189 Display *pThis = pDrv->pDisplay;
3190
3191 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
3192 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
3193
3194 vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
3195
3196 return VINF_SUCCESS;
3197}
3198
3199DECLCALLBACK(void) Display::displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
3200{
3201 LogFlowFunc(("uScreenId %d\n", uScreenId));
3202
3203 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3204 Display *pThis = pDrv->pDisplay;
3205
3206 pThis->maFramebuffers[uScreenId].fVBVAEnabled = false;
3207
3208 vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, &pThis->maFramebuffers[uScreenId]);
3209
3210 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = NULL;
3211}
3212
3213DECLCALLBACK(void) Display::displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
3214{
3215 LogFlowFunc(("uScreenId %d\n", uScreenId));
3216
3217 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3218 Display *pThis = pDrv->pDisplay;
3219 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3220
3221 if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
3222 {
3223 vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers, pThis->mcMonitors);
3224 ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
3225 }
3226
3227 if (RT_LIKELY(pFBInfo->u32ResizeStatus == ResizeStatus_Void))
3228 {
3229 if (RT_UNLIKELY(pFBInfo->cVBVASkipUpdate != 0))
3230 {
3231 /* Some updates were skipped. Note: displayVBVAUpdate* callbacks are called
3232 * under display device lock, so thread safe.
3233 */
3234 pFBInfo->cVBVASkipUpdate = 0;
3235 pThis->handleDisplayUpdate(pFBInfo->vbvaSkippedRect.xLeft,
3236 pFBInfo->vbvaSkippedRect.yTop,
3237 pFBInfo->vbvaSkippedRect.xRight - pFBInfo->vbvaSkippedRect.xLeft,
3238 pFBInfo->vbvaSkippedRect.yBottom - pFBInfo->vbvaSkippedRect.yTop);
3239 }
3240 }
3241 else
3242 {
3243 /* The framebuffer is being resized. */
3244 pFBInfo->cVBVASkipUpdate++;
3245 }
3246}
3247
3248DECLCALLBACK(void) Display::displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, const PVBVACMDHDR pCmd, size_t cbCmd)
3249{
3250 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d\n", uScreenId, pCmd, cbCmd));
3251
3252 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3253 Display *pThis = pDrv->pDisplay;
3254 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3255
3256 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
3257 {
3258 if (pFBInfo->fDefaultFormat)
3259 {
3260 pDrv->pUpPort->pfnUpdateDisplayRect (pDrv->pUpPort, pCmd->x, pCmd->y, pCmd->w, pCmd->h);
3261 pThis->handleDisplayUpdate (pCmd->x + pFBInfo->xOrigin,
3262 pCmd->y + pFBInfo->yOrigin, pCmd->w, pCmd->h);
3263 }
3264
3265 pThis->mParent->consoleVRDPServer()->SendUpdate (uScreenId, pCmd, cbCmd);
3266 }
3267}
3268
3269DECLCALLBACK(void) Display::displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y, uint32_t cx, uint32_t cy)
3270{
3271 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
3272
3273 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3274 Display *pThis = pDrv->pDisplay;
3275 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3276
3277 /* @todo handleFramebufferUpdate (uScreenId,
3278 * x - pThis->maFramebuffers[uScreenId].xOrigin,
3279 * y - pThis->maFramebuffers[uScreenId].yOrigin,
3280 * cx, cy);
3281 */
3282 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
3283 {
3284 pThis->handleDisplayUpdate(x, y, cx, cy);
3285 }
3286 else
3287 {
3288 /* Save the updated rectangle. */
3289 int32_t xRight = x + cx;
3290 int32_t yBottom = y + cy;
3291
3292 if (pFBInfo->cVBVASkipUpdate == 1)
3293 {
3294 pFBInfo->vbvaSkippedRect.xLeft = x;
3295 pFBInfo->vbvaSkippedRect.yTop = y;
3296 pFBInfo->vbvaSkippedRect.xRight = xRight;
3297 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
3298 }
3299 else
3300 {
3301 if (pFBInfo->vbvaSkippedRect.xLeft > x)
3302 {
3303 pFBInfo->vbvaSkippedRect.xLeft = x;
3304 }
3305 if (pFBInfo->vbvaSkippedRect.yTop > y)
3306 {
3307 pFBInfo->vbvaSkippedRect.yTop = y;
3308 }
3309 if (pFBInfo->vbvaSkippedRect.xRight < xRight)
3310 {
3311 pFBInfo->vbvaSkippedRect.xRight = xRight;
3312 }
3313 if (pFBInfo->vbvaSkippedRect.yBottom < yBottom)
3314 {
3315 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
3316 }
3317 }
3318 }
3319}
3320
3321DECLCALLBACK(int) Display::displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, void *pvVRAM)
3322{
3323 LogFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
3324
3325 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3326 Display *pThis = pDrv->pDisplay;
3327
3328 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[pScreen->u32ViewIndex];
3329
3330 pFBInfo->u32Offset = pView->u32ViewOffset; /* Not used in HGSMI. */
3331 pFBInfo->u32MaxFramebufferSize = pView->u32MaxScreenSize; /* Not used in HGSMI. */
3332 pFBInfo->u32InformationSize = 0; /* Not used in HGSMI. */
3333
3334 pFBInfo->xOrigin = pScreen->i32OriginX;
3335 pFBInfo->yOrigin = pScreen->i32OriginY;
3336
3337 pFBInfo->w = pScreen->u32Width;
3338 pFBInfo->h = pScreen->u32Height;
3339
3340 return pThis->handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
3341 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
3342 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height);
3343}
3344
3345DECLCALLBACK(int) Display::displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
3346 uint32_t xHot, uint32_t yHot,
3347 uint32_t cx, uint32_t cy,
3348 const void *pvShape)
3349{
3350 LogFlowFunc(("\n"));
3351
3352 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3353 Display *pThis = pDrv->pDisplay;
3354
3355 /* Tell the console about it */
3356 pDrv->pDisplay->mParent->onMousePointerShapeChange(fVisible, fAlpha,
3357 xHot, yHot, cx, cy, (void *)pvShape);
3358
3359 return VINF_SUCCESS;
3360}
3361#endif /* VBOX_WITH_HGSMI */
3362
3363/**
3364 * Queries an interface to the driver.
3365 *
3366 * @returns Pointer to interface.
3367 * @returns NULL if the interface was not supported by the driver.
3368 * @param pInterface Pointer to this interface structure.
3369 * @param enmInterface The requested interface identification.
3370 */
3371DECLCALLBACK(void *) Display::drvQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
3372{
3373 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
3374 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3375 switch (enmInterface)
3376 {
3377 case PDMINTERFACE_BASE:
3378 return &pDrvIns->IBase;
3379 case PDMINTERFACE_DISPLAY_CONNECTOR:
3380 return &pDrv->Connector;
3381 default:
3382 return NULL;
3383 }
3384}
3385
3386
3387/**
3388 * Destruct a display driver instance.
3389 *
3390 * @returns VBox status.
3391 * @param pDrvIns The driver instance data.
3392 */
3393DECLCALLBACK(void) Display::drvDestruct(PPDMDRVINS pDrvIns)
3394{
3395 PDRVMAINDISPLAY pData = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3396 LogFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
3397 if (pData->pDisplay)
3398 {
3399 AutoWriteLock displayLock(pData->pDisplay COMMA_LOCKVAL_SRC_POS);
3400 pData->pDisplay->mpDrv = NULL;
3401 pData->pDisplay->mpVMMDev = NULL;
3402 pData->pDisplay->mLastAddress = NULL;
3403 pData->pDisplay->mLastBytesPerLine = 0;
3404 pData->pDisplay->mLastBitsPerPixel = 0,
3405 pData->pDisplay->mLastWidth = 0;
3406 pData->pDisplay->mLastHeight = 0;
3407 }
3408}
3409
3410
3411/**
3412 * Construct a display driver instance.
3413 *
3414 * @copydoc FNPDMDRVCONSTRUCT
3415 */
3416DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
3417{
3418 PDRVMAINDISPLAY pData = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3419 LogFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
3420
3421 /*
3422 * Validate configuration.
3423 */
3424 if (!CFGMR3AreValuesValid(pCfgHandle, "Object\0"))
3425 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
3426 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
3427 ("Configuration error: Not possible to attach anything to this driver!\n"),
3428 VERR_PDM_DRVINS_NO_ATTACH);
3429
3430 /*
3431 * Init Interfaces.
3432 */
3433 pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
3434
3435 pData->Connector.pfnResize = Display::displayResizeCallback;
3436 pData->Connector.pfnUpdateRect = Display::displayUpdateCallback;
3437 pData->Connector.pfnRefresh = Display::displayRefreshCallback;
3438 pData->Connector.pfnReset = Display::displayResetCallback;
3439 pData->Connector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
3440 pData->Connector.pfnProcessAdapterData = Display::displayProcessAdapterDataCallback;
3441 pData->Connector.pfnProcessDisplayData = Display::displayProcessDisplayDataCallback;
3442#ifdef VBOX_WITH_VIDEOHWACCEL
3443 pData->Connector.pfnVHWACommandProcess = Display::displayVHWACommandProcess;
3444#endif
3445#ifdef VBOX_WITH_HGSMI
3446 pData->Connector.pfnVBVAEnable = Display::displayVBVAEnable;
3447 pData->Connector.pfnVBVADisable = Display::displayVBVADisable;
3448 pData->Connector.pfnVBVAUpdateBegin = Display::displayVBVAUpdateBegin;
3449 pData->Connector.pfnVBVAUpdateProcess = Display::displayVBVAUpdateProcess;
3450 pData->Connector.pfnVBVAUpdateEnd = Display::displayVBVAUpdateEnd;
3451 pData->Connector.pfnVBVAResize = Display::displayVBVAResize;
3452 pData->Connector.pfnVBVAMousePointerShape = Display::displayVBVAMousePointerShape;
3453#endif
3454
3455
3456 /*
3457 * Get the IDisplayPort interface of the above driver/device.
3458 */
3459 pData->pUpPort = (PPDMIDISPLAYPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_DISPLAY_PORT);
3460 if (!pData->pUpPort)
3461 {
3462 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
3463 return VERR_PDM_MISSING_INTERFACE_ABOVE;
3464 }
3465#if defined(VBOX_WITH_VIDEOHWACCEL)
3466 pData->pVBVACallbacks = (PPDMDDISPLAYVBVACALLBACKS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_DISPLAY_VBVA_CALLBACKS);
3467 if (!pData->pVBVACallbacks)
3468 {
3469 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
3470 return VERR_PDM_MISSING_INTERFACE_ABOVE;
3471 }
3472#endif
3473 /*
3474 * Get the Display object pointer and update the mpDrv member.
3475 */
3476 void *pv;
3477 int rc = CFGMR3QueryPtr(pCfgHandle, "Object", &pv);
3478 if (RT_FAILURE(rc))
3479 {
3480 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
3481 return rc;
3482 }
3483 pData->pDisplay = (Display *)pv; /** @todo Check this cast! */
3484 pData->pDisplay->mpDrv = pData;
3485
3486 /*
3487 * Update our display information according to the framebuffer
3488 */
3489 pData->pDisplay->updateDisplayData();
3490
3491 /*
3492 * Start periodic screen refreshes
3493 */
3494 pData->pUpPort->pfnSetRefreshRate(pData->pUpPort, 20);
3495
3496 return VINF_SUCCESS;
3497}
3498
3499
3500/**
3501 * Display driver registration record.
3502 */
3503const PDMDRVREG Display::DrvReg =
3504{
3505 /* u32Version */
3506 PDM_DRVREG_VERSION,
3507 /* szDriverName */
3508 "MainDisplay",
3509 /* pszDescription */
3510 "Main display driver (Main as in the API).",
3511 /* fFlags */
3512 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
3513 /* fClass. */
3514 PDM_DRVREG_CLASS_DISPLAY,
3515 /* cMaxInstances */
3516 ~0,
3517 /* cbInstance */
3518 sizeof(DRVMAINDISPLAY),
3519 /* pfnConstruct */
3520 Display::drvConstruct,
3521 /* pfnDestruct */
3522 Display::drvDestruct,
3523 /* pfnIOCtl */
3524 NULL,
3525 /* pfnPowerOn */
3526 NULL,
3527 /* pfnReset */
3528 NULL,
3529 /* pfnSuspend */
3530 NULL,
3531 /* pfnResume */
3532 NULL,
3533 /* pfnAttach */
3534 NULL,
3535 /* pfnDetach */
3536 NULL,
3537 /* pfnPowerOff */
3538 NULL,
3539 /* pfnSoftReset */
3540 NULL,
3541 /* u32EndVersion */
3542 PDM_DRVREG_VERSION
3543};
3544/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use