VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

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