VirtualBox

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

Last change on this file since 31595 was 31279, checked in by vboxsync, 14 years ago

Display: replaced floating point implementation of screenshot scaling with a faster integer.

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