VirtualBox

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

Last change on this file since 27166 was 26443, checked in by vboxsync, 15 years ago

Fixed loading DisplayScreenshot from saved state (a corrected fix).

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

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