VirtualBox

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

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

Fix #4631 (Crash when taking a snapshot of a VM running with VBoxHeadless on OSE)

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