VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/DisplayImpl.cpp@ 51552

Last change on this file since 51552 was 51552, checked in by vboxsync, 11 years ago

Main: removed obsolete GetFramebuffer, updateDisplayData methods.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 181.6 KB
Line 
1/* $Id: DisplayImpl.cpp 51552 2014-06-05 12:33:13Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2013 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 "DisplayUtils.h"
20#include "ConsoleImpl.h"
21#include "ConsoleVRDPServer.h"
22#include "VMMDev.h"
23
24#include "AutoCaller.h"
25#include "Logging.h"
26
27/* generated header */
28#include "VBoxEvents.h"
29
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32#include <iprt/asm.h>
33#include <iprt/time.h>
34#include <iprt/cpp/utils.h>
35#include <iprt/alloca.h>
36
37#include <VBox/vmm/pdmdrv.h>
38#if defined(DEBUG) || defined(VBOX_STRICT) /* for VM_ASSERT_EMT(). */
39# include <VBox/vmm/vm.h>
40#endif
41
42#ifdef VBOX_WITH_VIDEOHWACCEL
43# include <VBox/VBoxVideo.h>
44#endif
45
46#if defined(VBOX_WITH_CROGL) || defined(VBOX_WITH_CRHGSMI)
47# include <VBox/HostServices/VBoxCrOpenGLSvc.h>
48#endif
49
50#include <VBox/com/array.h>
51
52#ifdef VBOX_WITH_VPX
53# include <iprt/path.h>
54# include "VideoRec.h"
55#endif
56
57#ifdef VBOX_WITH_CROGL
58typedef enum
59{
60 CRVREC_STATE_IDLE,
61 CRVREC_STATE_SUBMITTED
62} CRVREC_STATE;
63#endif
64
65/**
66 * Display driver instance data.
67 *
68 * @implements PDMIDISPLAYCONNECTOR
69 */
70typedef struct DRVMAINDISPLAY
71{
72 /** Pointer to the display object. */
73 Display *pDisplay;
74 /** Pointer to the driver instance structure. */
75 PPDMDRVINS pDrvIns;
76 /** Pointer to the keyboard port interface of the driver/device above us. */
77 PPDMIDISPLAYPORT pUpPort;
78 /** Our display connector interface. */
79 PDMIDISPLAYCONNECTOR IConnector;
80#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
81 /** VBVA callbacks */
82 PPDMIDISPLAYVBVACALLBACKS pVBVACallbacks;
83#endif
84} DRVMAINDISPLAY, *PDRVMAINDISPLAY;
85
86/** Converts PDMIDISPLAYCONNECTOR pointer to a DRVMAINDISPLAY pointer. */
87#define PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface) RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector)
88
89#ifdef DEBUG_sunlover
90static STAMPROFILE g_StatDisplayRefresh;
91static int g_stam = 0;
92#endif /* DEBUG_sunlover */
93
94// constructor / destructor
95/////////////////////////////////////////////////////////////////////////////
96
97Display::Display()
98 : mParent(NULL)
99{
100}
101
102Display::~Display()
103{
104}
105
106
107HRESULT Display::FinalConstruct()
108{
109 mpVbvaMemory = NULL;
110 mfVideoAccelEnabled = false;
111 mfVideoAccelVRDP = false;
112 mfu32SupportedOrders = 0;
113 mcVideoAccelVRDPRefs = 0;
114
115 mpPendingVbvaMemory = NULL;
116 mfPendingVideoAccelEnable = false;
117
118 mfMachineRunning = false;
119#ifdef VBOX_WITH_CROGL
120 mfCrOglDataHidden = false;
121#endif
122
123 mpu8VbvaPartial = NULL;
124 mcbVbvaPartial = 0;
125
126 mpDrv = NULL;
127 mpVMMDev = NULL;
128 mfVMMDevInited = false;
129
130 mLastAddress = NULL;
131 mLastBytesPerLine = 0;
132 mLastBitsPerPixel = 0,
133 mLastWidth = 0;
134 mLastHeight = 0;
135
136 int rc = RTCritSectInit(&mVBVALock);
137 AssertRC(rc);
138
139 rc = RTCritSectInit(&mSaveSeamlessRectLock);
140 AssertRC(rc);
141
142 mfu32PendingVideoAccelDisable = false;
143
144#ifdef VBOX_WITH_HGSMI
145 mu32UpdateVBVAFlags = 0;
146#endif
147#ifdef VBOX_WITH_VPX
148 mpVideoRecCtx = NULL;
149 for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
150 maVideoRecEnabled[i] = true;
151#endif
152
153#ifdef VBOX_WITH_CRHGSMI
154 mhCrOglSvc = NULL;
155 rc = RTCritSectRwInit(&mCrOglLock);
156 AssertRC(rc);
157#endif
158#ifdef VBOX_WITH_CROGL
159 RT_ZERO(mCrOglCallbacks);
160 RT_ZERO(mCrOglScreenshotData);
161 mfCrOglVideoRecState = CRVREC_STATE_IDLE;
162 mCrOglScreenshotData.u32Screen = CRSCREEN_ALL;
163 mCrOglScreenshotData.pvContext = this;
164 mCrOglScreenshotData.pfnScreenshotBegin = displayCrVRecScreenshotBegin;
165 mCrOglScreenshotData.pfnScreenshotPerform = displayCrVRecScreenshotPerform;
166 mCrOglScreenshotData.pfnScreenshotEnd = displayCrVRecScreenshotEnd;
167#endif
168
169 return BaseFinalConstruct();
170}
171
172void Display::FinalRelease()
173{
174 uninit();
175
176 if (RTCritSectIsInitialized (&mVBVALock))
177 {
178 RTCritSectDelete (&mVBVALock);
179 RT_ZERO(mVBVALock);
180 }
181
182 if (RTCritSectIsInitialized(&mSaveSeamlessRectLock))
183 {
184 RTCritSectDelete(&mSaveSeamlessRectLock);
185 RT_ZERO(mSaveSeamlessRectLock);
186 }
187
188#ifdef VBOX_WITH_CRHGSMI
189 if (RTCritSectRwIsInitialized (&mCrOglLock))
190 {
191 RTCritSectRwDelete (&mCrOglLock);
192 RT_ZERO(mCrOglLock);
193 }
194#endif
195 BaseFinalRelease();
196}
197
198// public initializer/uninitializer for internal purposes only
199/////////////////////////////////////////////////////////////////////////////
200
201#define kMaxSizeThumbnail 64
202
203/**
204 * Save thumbnail and screenshot of the guest screen.
205 */
206static int displayMakeThumbnail(uint8_t *pu8Data, uint32_t cx, uint32_t cy,
207 uint8_t **ppu8Thumbnail, uint32_t *pcbThumbnail, uint32_t *pcxThumbnail, uint32_t *pcyThumbnail)
208{
209 int rc = VINF_SUCCESS;
210
211 uint8_t *pu8Thumbnail = NULL;
212 uint32_t cbThumbnail = 0;
213 uint32_t cxThumbnail = 0;
214 uint32_t cyThumbnail = 0;
215
216 if (cx > cy)
217 {
218 cxThumbnail = kMaxSizeThumbnail;
219 cyThumbnail = (kMaxSizeThumbnail * cy) / cx;
220 }
221 else
222 {
223 cyThumbnail = kMaxSizeThumbnail;
224 cxThumbnail = (kMaxSizeThumbnail * cx) / cy;
225 }
226
227 LogRelFlowFunc(("%dx%d -> %dx%d\n", cx, cy, cxThumbnail, cyThumbnail));
228
229 cbThumbnail = cxThumbnail * 4 * cyThumbnail;
230 pu8Thumbnail = (uint8_t *)RTMemAlloc(cbThumbnail);
231
232 if (pu8Thumbnail)
233 {
234 uint8_t *dst = pu8Thumbnail;
235 uint8_t *src = pu8Data;
236 int dstW = cxThumbnail;
237 int dstH = cyThumbnail;
238 int srcW = cx;
239 int srcH = cy;
240 int iDeltaLine = cx * 4;
241
242 BitmapScale32 (dst,
243 dstW, dstH,
244 src,
245 iDeltaLine,
246 srcW, srcH);
247
248 *ppu8Thumbnail = pu8Thumbnail;
249 *pcbThumbnail = cbThumbnail;
250 *pcxThumbnail = cxThumbnail;
251 *pcyThumbnail = cyThumbnail;
252 }
253 else
254 {
255 rc = VERR_NO_MEMORY;
256 }
257
258 return rc;
259}
260
261#ifdef VBOX_WITH_CROGL
262typedef struct
263{
264 CRVBOXHGCMTAKESCREENSHOT Base;
265
266 /* 32bpp small RGB image. */
267 uint8_t *pu8Thumbnail;
268 uint32_t cbThumbnail;
269 uint32_t cxThumbnail;
270 uint32_t cyThumbnail;
271
272 /* PNG screenshot. */
273 uint8_t *pu8PNG;
274 uint32_t cbPNG;
275 uint32_t cxPNG;
276 uint32_t cyPNG;
277} VBOX_DISPLAY_SAVESCREENSHOT_DATA;
278
279static DECLCALLBACK(void) displaySaveScreenshotReport(void *pvCtx, uint32_t uScreen,
280 uint32_t x, uint32_t y, uint32_t uBitsPerPixel,
281 uint32_t uBytesPerLine, uint32_t uGuestWidth, uint32_t uGuestHeight,
282 uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
283{
284 VBOX_DISPLAY_SAVESCREENSHOT_DATA *pData = (VBOX_DISPLAY_SAVESCREENSHOT_DATA*)pvCtx;
285 displayMakeThumbnail(pu8BufferAddress, uGuestWidth, uGuestHeight, &pData->pu8Thumbnail,
286 &pData->cbThumbnail, &pData->cxThumbnail, &pData->cyThumbnail);
287 int rc = DisplayMakePNG(pu8BufferAddress, uGuestWidth, uGuestHeight, &pData->pu8PNG,
288 &pData->cbPNG, &pData->cxPNG, &pData->cyPNG, 1);
289 if (RT_FAILURE(rc))
290 {
291 AssertMsgFailed(("DisplayMakePNG failed %d\n", rc));
292 if (pData->pu8PNG)
293 {
294 RTMemFree(pData->pu8PNG);
295 pData->pu8PNG = NULL;
296 }
297 pData->cbPNG = 0;
298 pData->cxPNG = 0;
299 pData->cyPNG = 0;
300 }
301}
302#endif
303
304DECLCALLBACK(void)
305Display::displaySSMSaveScreenshot(PSSMHANDLE pSSM, void *pvUser)
306{
307 Display *that = static_cast<Display*>(pvUser);
308
309 /* 32bpp small RGB image. */
310 uint8_t *pu8Thumbnail = NULL;
311 uint32_t cbThumbnail = 0;
312 uint32_t cxThumbnail = 0;
313 uint32_t cyThumbnail = 0;
314
315 /* PNG screenshot. */
316 uint8_t *pu8PNG = NULL;
317 uint32_t cbPNG = 0;
318 uint32_t cxPNG = 0;
319 uint32_t cyPNG = 0;
320
321 Console::SafeVMPtr ptrVM(that->mParent);
322 if (ptrVM.isOk())
323 {
324 /* Query RGB bitmap. */
325 uint8_t *pu8Data = NULL;
326 size_t cbData = 0;
327 uint32_t cx = 0;
328 uint32_t cy = 0;
329
330#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
331 BOOL f3DSnapshot = FALSE;
332 BOOL is3denabled;
333 that->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
334 if (is3denabled && that->mCrOglCallbacks.pfnHasData())
335 {
336 VMMDev *pVMMDev = that->mParent->getVMMDev();
337 if (pVMMDev)
338 {
339 VBOX_DISPLAY_SAVESCREENSHOT_DATA *pScreenshot =
340 (VBOX_DISPLAY_SAVESCREENSHOT_DATA*)RTMemAllocZ(sizeof (*pScreenshot));
341 if (pScreenshot)
342 {
343 /* screen id or CRSCREEN_ALL to specify all enabled */
344 pScreenshot->Base.u32Screen = 0;
345 pScreenshot->Base.u32Width = 0;
346 pScreenshot->Base.u32Height = 0;
347 pScreenshot->Base.u32Pitch = 0;
348 pScreenshot->Base.pvBuffer = NULL;
349 pScreenshot->Base.pvContext = pScreenshot;
350 pScreenshot->Base.pfnScreenshotBegin = NULL;
351 pScreenshot->Base.pfnScreenshotPerform = displaySaveScreenshotReport;
352 pScreenshot->Base.pfnScreenshotEnd = NULL;
353
354 VBOXCRCMDCTL_HGCM data;
355 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
356 data.Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
357
358 data.aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
359 data.aParms[0].u.pointer.addr = &pScreenshot->Base;
360 data.aParms[0].u.pointer.size = sizeof (pScreenshot->Base);
361
362 int rc = that->crCtlSubmitSync(&data.Hdr, sizeof (data));
363 if (RT_SUCCESS(rc))
364 {
365 if (pScreenshot->pu8PNG)
366 {
367 pu8Thumbnail = pScreenshot->pu8Thumbnail;
368 cbThumbnail = pScreenshot->cbThumbnail;
369 cxThumbnail = pScreenshot->cxThumbnail;
370 cyThumbnail = pScreenshot->cyThumbnail;
371
372 /* PNG screenshot. */
373 pu8PNG = pScreenshot->pu8PNG;
374 cbPNG = pScreenshot->cbPNG;
375 cxPNG = pScreenshot->cxPNG;
376 cyPNG = pScreenshot->cyPNG;
377 f3DSnapshot = TRUE;
378 }
379 else
380 AssertMsgFailed(("no png\n"));
381 }
382 else
383 AssertMsgFailed(("SHCRGL_HOST_FN_TAKE_SCREENSHOT failed %d\n", rc));
384
385
386 RTMemFree(pScreenshot);
387 }
388 }
389 }
390
391 if (!f3DSnapshot)
392#endif
393 {
394 /* SSM code is executed on EMT(0), therefore no need to use VMR3ReqCallWait. */
395 int rc = Display::displayTakeScreenshotEMT(that, VBOX_VIDEO_PRIMARY_SCREEN, &pu8Data, &cbData, &cx, &cy);
396
397 /*
398 * It is possible that success is returned but everything is 0 or NULL.
399 * (no display attached if a VM is running with VBoxHeadless on OSE for example)
400 */
401 if (RT_SUCCESS(rc) && pu8Data)
402 {
403 Assert(cx && cy);
404
405 /* Prepare a small thumbnail and a PNG screenshot. */
406 displayMakeThumbnail(pu8Data, cx, cy, &pu8Thumbnail, &cbThumbnail, &cxThumbnail, &cyThumbnail);
407 rc = DisplayMakePNG(pu8Data, cx, cy, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 1);
408 if (RT_FAILURE(rc))
409 {
410 if (pu8PNG)
411 {
412 RTMemFree(pu8PNG);
413 pu8PNG = NULL;
414 }
415 cbPNG = 0;
416 cxPNG = 0;
417 cyPNG = 0;
418 }
419
420 /* This can be called from any thread. */
421 that->mpDrv->pUpPort->pfnFreeScreenshot(that->mpDrv->pUpPort, pu8Data);
422 }
423 }
424 }
425 else
426 {
427 LogFunc(("Failed to get VM pointer 0x%x\n", ptrVM.rc()));
428 }
429
430 /* Regardless of rc, save what is available:
431 * Data format:
432 * uint32_t cBlocks;
433 * [blocks]
434 *
435 * Each block is:
436 * uint32_t cbBlock; if 0 - no 'block data'.
437 * uint32_t typeOfBlock; 0 - 32bpp RGB bitmap, 1 - PNG, ignored if 'cbBlock' is 0.
438 * [block data]
439 *
440 * Block data for bitmap and PNG:
441 * uint32_t cx;
442 * uint32_t cy;
443 * [image data]
444 */
445 SSMR3PutU32(pSSM, 2); /* Write thumbnail and PNG screenshot. */
446
447 /* First block. */
448 SSMR3PutU32(pSSM, cbThumbnail + 2 * sizeof (uint32_t));
449 SSMR3PutU32(pSSM, 0); /* Block type: thumbnail. */
450
451 if (cbThumbnail)
452 {
453 SSMR3PutU32(pSSM, cxThumbnail);
454 SSMR3PutU32(pSSM, cyThumbnail);
455 SSMR3PutMem(pSSM, pu8Thumbnail, cbThumbnail);
456 }
457
458 /* Second block. */
459 SSMR3PutU32(pSSM, cbPNG + 2 * sizeof (uint32_t));
460 SSMR3PutU32(pSSM, 1); /* Block type: png. */
461
462 if (cbPNG)
463 {
464 SSMR3PutU32(pSSM, cxPNG);
465 SSMR3PutU32(pSSM, cyPNG);
466 SSMR3PutMem(pSSM, pu8PNG, cbPNG);
467 }
468
469 RTMemFree(pu8PNG);
470 RTMemFree(pu8Thumbnail);
471}
472
473DECLCALLBACK(int)
474Display::displaySSMLoadScreenshot(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
475{
476 Display *that = static_cast<Display*>(pvUser);
477
478 if (uVersion != sSSMDisplayScreenshotVer)
479 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
480 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
481
482 /* Skip data. */
483 uint32_t cBlocks;
484 int rc = SSMR3GetU32(pSSM, &cBlocks);
485 AssertRCReturn(rc, rc);
486
487 for (uint32_t i = 0; i < cBlocks; i++)
488 {
489 uint32_t cbBlock;
490 rc = SSMR3GetU32(pSSM, &cbBlock);
491 AssertRCBreak(rc);
492
493 uint32_t typeOfBlock;
494 rc = SSMR3GetU32(pSSM, &typeOfBlock);
495 AssertRCBreak(rc);
496
497 LogRelFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
498
499 /* Note: displaySSMSaveScreenshot writes size of a block = 8 and
500 * do not write any data if the image size was 0.
501 * @todo Fix and increase saved state version.
502 */
503 if (cbBlock > 2 * sizeof (uint32_t))
504 {
505 rc = SSMR3Skip(pSSM, cbBlock);
506 AssertRCBreak(rc);
507 }
508 }
509
510 return rc;
511}
512
513/**
514 * Save/Load some important guest state
515 */
516DECLCALLBACK(void)
517Display::displaySSMSave(PSSMHANDLE pSSM, void *pvUser)
518{
519 Display *that = static_cast<Display*>(pvUser);
520
521 SSMR3PutU32(pSSM, that->mcMonitors);
522 for (unsigned i = 0; i < that->mcMonitors; i++)
523 {
524 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32Offset);
525 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32MaxFramebufferSize);
526 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32InformationSize);
527 SSMR3PutU32(pSSM, that->maFramebuffers[i].w);
528 SSMR3PutU32(pSSM, that->maFramebuffers[i].h);
529 SSMR3PutS32(pSSM, that->maFramebuffers[i].xOrigin);
530 SSMR3PutS32(pSSM, that->maFramebuffers[i].yOrigin);
531 SSMR3PutU32(pSSM, that->maFramebuffers[i].flags);
532 }
533}
534
535DECLCALLBACK(int)
536Display::displaySSMLoad(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
537{
538 Display *that = static_cast<Display*>(pvUser);
539
540 if (!( uVersion == sSSMDisplayVer
541 || uVersion == sSSMDisplayVer2
542 || uVersion == sSSMDisplayVer3))
543 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
544 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
545
546 uint32_t cMonitors;
547 int rc = SSMR3GetU32(pSSM, &cMonitors);
548 if (cMonitors != that->mcMonitors)
549 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Number of monitors changed (%d->%d)!"), cMonitors, that->mcMonitors);
550
551 for (uint32_t i = 0; i < cMonitors; i++)
552 {
553 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32Offset);
554 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32MaxFramebufferSize);
555 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32InformationSize);
556 if ( uVersion == sSSMDisplayVer2
557 || uVersion == sSSMDisplayVer3)
558 {
559 uint32_t w;
560 uint32_t h;
561 SSMR3GetU32(pSSM, &w);
562 SSMR3GetU32(pSSM, &h);
563 that->maFramebuffers[i].w = w;
564 that->maFramebuffers[i].h = h;
565 }
566 if (uVersion == sSSMDisplayVer3)
567 {
568 int32_t xOrigin;
569 int32_t yOrigin;
570 uint32_t flags;
571 SSMR3GetS32(pSSM, &xOrigin);
572 SSMR3GetS32(pSSM, &yOrigin);
573 SSMR3GetU32(pSSM, &flags);
574 that->maFramebuffers[i].xOrigin = xOrigin;
575 that->maFramebuffers[i].yOrigin = yOrigin;
576 that->maFramebuffers[i].flags = (uint16_t)flags;
577 that->maFramebuffers[i].fDisabled = (that->maFramebuffers[i].flags & VBVA_SCREEN_F_DISABLED) != 0;
578 }
579 }
580
581 return VINF_SUCCESS;
582}
583
584/**
585 * Initializes the display object.
586 *
587 * @returns COM result indicator
588 * @param parent handle of our parent object
589 * @param qemuConsoleData address of common console data structure
590 */
591HRESULT Display::init(Console *aParent)
592{
593 ComAssertRet(aParent, E_INVALIDARG);
594 /* Enclose the state transition NotReady->InInit->Ready */
595 AutoInitSpan autoInitSpan(this);
596 AssertReturn(autoInitSpan.isOk(), E_FAIL);
597
598 unconst(mParent) = aParent;
599
600 mfSourceBitmapEnabled = true;
601
602 ULONG ul;
603 mParent->machine()->COMGETTER(MonitorCount)(&ul);
604 mcMonitors = ul;
605
606 for (ul = 0; ul < mcMonitors; ul++)
607 {
608 maFramebuffers[ul].u32Offset = 0;
609 maFramebuffers[ul].u32MaxFramebufferSize = 0;
610 maFramebuffers[ul].u32InformationSize = 0;
611
612 maFramebuffers[ul].pFramebuffer = NULL;
613 /* All secondary monitors are disabled at startup. */
614 maFramebuffers[ul].fDisabled = ul > 0;
615
616 maFramebuffers[ul].xOrigin = 0;
617 maFramebuffers[ul].yOrigin = 0;
618
619 maFramebuffers[ul].w = 0;
620 maFramebuffers[ul].h = 0;
621
622 maFramebuffers[ul].flags = maFramebuffers[ul].fDisabled? VBVA_SCREEN_F_DISABLED: 0;
623
624 maFramebuffers[ul].u16BitsPerPixel = 0;
625 maFramebuffers[ul].pu8FramebufferVRAM = NULL;
626 maFramebuffers[ul].u32LineSize = 0;
627
628 maFramebuffers[ul].pHostEvents = NULL;
629
630 maFramebuffers[ul].u32ResizeStatus = ResizeStatus_Void;
631
632 maFramebuffers[ul].fDefaultFormat = false;
633
634 maFramebuffers[ul].mcSavedVisibleRegion = 0;
635 maFramebuffers[ul].mpSavedVisibleRegion = NULL;
636
637 RT_ZERO(maFramebuffers[ul].dirtyRect);
638 RT_ZERO(maFramebuffers[ul].pendingResize);
639#ifdef VBOX_WITH_HGSMI
640 maFramebuffers[ul].fVBVAEnabled = false;
641 maFramebuffers[ul].fVBVAForceResize = false;
642 maFramebuffers[ul].fRenderThreadMode = false;
643 maFramebuffers[ul].cVBVASkipUpdate = 0;
644 RT_ZERO(maFramebuffers[ul].vbvaSkippedRect);
645 maFramebuffers[ul].pVBVAHostFlags = NULL;
646#endif /* VBOX_WITH_HGSMI */
647#ifdef VBOX_WITH_CROGL
648 RT_ZERO(maFramebuffers[ul].pendingViewportInfo);
649#endif
650 }
651
652 {
653 // register listener for state change events
654 ComPtr<IEventSource> es;
655 mParent->COMGETTER(EventSource)(es.asOutParam());
656 com::SafeArray <VBoxEventType_T> eventTypes;
657 eventTypes.push_back(VBoxEventType_OnStateChanged);
658 es->RegisterListener(this, ComSafeArrayAsInParam(eventTypes), true);
659 }
660
661 /* Confirm a successful initialization */
662 autoInitSpan.setSucceeded();
663
664 return S_OK;
665}
666
667/**
668 * Uninitializes the instance and sets the ready flag to FALSE.
669 * Called either from FinalRelease() or by the parent when it gets destroyed.
670 */
671void Display::uninit()
672{
673 LogRelFlowFunc(("this=%p\n", this));
674
675 /* Enclose the state transition Ready->InUninit->NotReady */
676 AutoUninitSpan autoUninitSpan(this);
677 if (autoUninitSpan.uninitDone())
678 return;
679
680 unsigned uScreenId;
681 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
682 {
683 maFramebuffers[uScreenId].pSourceBitmap.setNull();
684 maFramebuffers[uScreenId].pFramebuffer.setNull();
685 }
686
687 if (mParent)
688 {
689 ComPtr<IEventSource> es;
690 mParent->COMGETTER(EventSource)(es.asOutParam());
691 es->UnregisterListener(this);
692 }
693
694 unconst(mParent) = NULL;
695
696 if (mpDrv)
697 mpDrv->pDisplay = NULL;
698
699 mpDrv = NULL;
700 mpVMMDev = NULL;
701 mfVMMDevInited = true;
702}
703
704/**
705 * Register the SSM methods. Called by the power up thread to be able to
706 * pass pVM
707 */
708int Display::registerSSM(PUVM pUVM)
709{
710 /* Version 2 adds width and height of the framebuffer; version 3 adds
711 * the framebuffer offset in the virtual desktop and the framebuffer flags.
712 */
713 int rc = SSMR3RegisterExternal(pUVM, "DisplayData", 0, sSSMDisplayVer3,
714 mcMonitors * sizeof(uint32_t) * 8 + sizeof(uint32_t),
715 NULL, NULL, NULL,
716 NULL, displaySSMSave, NULL,
717 NULL, displaySSMLoad, NULL, this);
718 AssertRCReturn(rc, rc);
719
720 /*
721 * Register loaders for old saved states where iInstance was
722 * 3 * sizeof(uint32_t *) due to a code mistake.
723 */
724 rc = SSMR3RegisterExternal(pUVM, "DisplayData", 12 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
725 NULL, NULL, NULL,
726 NULL, NULL, NULL,
727 NULL, displaySSMLoad, NULL, this);
728 AssertRCReturn(rc, rc);
729
730 rc = SSMR3RegisterExternal(pUVM, "DisplayData", 24 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
731 NULL, NULL, NULL,
732 NULL, NULL, NULL,
733 NULL, displaySSMLoad, NULL, this);
734 AssertRCReturn(rc, rc);
735
736 /* uInstance is an arbitrary value greater than 1024. Such a value will ensure a quick seek in saved state file. */
737 rc = SSMR3RegisterExternal(pUVM, "DisplayScreenshot", 1100 /*uInstance*/, sSSMDisplayScreenshotVer, 0 /*cbGuess*/,
738 NULL, NULL, NULL,
739 NULL, displaySSMSaveScreenshot, NULL,
740 NULL, displaySSMLoadScreenshot, NULL, this);
741
742 AssertRCReturn(rc, rc);
743
744 return VINF_SUCCESS;
745}
746
747DECLCALLBACK(void) Display::displayCrCmdFree(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
748{
749 Assert(pvCompletion);
750 RTMemFree(pvCompletion);
751}
752
753#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
754int Display::crOglWindowsShow(bool fShow)
755{
756 if (!mfCrOglDataHidden == !!fShow)
757 return VINF_SUCCESS;
758
759 if (!mhCrOglSvc)
760 {
761 /* no 3D */
762#ifdef DEBUG
763 BOOL is3denabled;
764 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
765 Assert(!is3denabled);
766#endif
767 return VERR_INVALID_STATE;
768 }
769
770 VMMDev *pVMMDev = mParent->getVMMDev();
771 if (!pVMMDev)
772 {
773 AssertMsgFailed(("no vmmdev\n"));
774 return VERR_INVALID_STATE;
775 }
776
777 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM*)RTMemAlloc(sizeof (VBOXCRCMDCTL_HGCM));
778 if (!pData)
779 {
780 AssertMsgFailed(("RTMemAlloc failed\n"));
781 return VERR_NO_MEMORY;
782 }
783
784 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
785 pData->Hdr.u32Function = SHCRGL_HOST_FN_WINDOWS_SHOW;
786
787 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
788 pData->aParms[0].u.uint32 = (uint32_t)fShow;
789
790 int rc = crCtlSubmit(&pData->Hdr, sizeof (*pData), displayCrCmdFree, pData);
791 if (RT_SUCCESS(rc))
792 mfCrOglDataHidden = !fShow;
793 else
794 {
795 AssertMsgFailed(("crCtlSubmit failed rc %d\n", rc));
796 RTMemFree(pData);
797 }
798
799 return rc;
800}
801#endif
802
803
804// IEventListener method
805STDMETHODIMP Display::HandleEvent(IEvent * aEvent)
806{
807 VBoxEventType_T aType = VBoxEventType_Invalid;
808
809 aEvent->COMGETTER(Type)(&aType);
810 switch (aType)
811 {
812 case VBoxEventType_OnStateChanged:
813 {
814 ComPtr<IStateChangedEvent> scev = aEvent;
815 Assert(scev);
816 MachineState_T machineState;
817 scev->COMGETTER(State)(&machineState);
818 if ( machineState == MachineState_Running
819 || machineState == MachineState_Teleporting
820 || machineState == MachineState_LiveSnapshotting
821 )
822 {
823 LogRelFlowFunc(("Machine is running.\n"));
824
825 mfMachineRunning = true;
826
827#ifdef VBOX_WITH_CROGL
828 crOglWindowsShow(true);
829#endif
830 }
831 else
832 {
833 mfMachineRunning = false;
834
835#ifdef VBOX_WITH_CROGL
836 if (machineState == MachineState_Paused)
837 crOglWindowsShow(false);
838#endif
839 }
840 break;
841 }
842 default:
843 AssertFailed();
844 }
845
846 return S_OK;
847}
848
849// public methods only for internal purposes
850/////////////////////////////////////////////////////////////////////////////
851
852/**
853 * @thread EMT
854 */
855static int callFramebufferResize (IFramebuffer *pFramebuffer, unsigned uScreenId,
856 ULONG pixelFormat, void *pvVRAM,
857 uint32_t bpp, uint32_t cbLine,
858 uint32_t w, uint32_t h)
859{
860 Assert (pFramebuffer);
861
862 NOREF(pixelFormat);
863 NOREF(pvVRAM);
864 NOREF(bpp);
865 NOREF(cbLine);
866
867 /* Call the framebuffer to try and set required pixelFormat. */
868 HRESULT hr = pFramebuffer->NotifyChange(uScreenId, 0, 0, w, h); /* @todo origin */
869
870 Log(("pFramebuffer->NotifyChange hr %08x\n", hr));
871
872 return VINF_SUCCESS;
873}
874
875int Display::notifyCroglResize(const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, void *pvVRAM)
876{
877#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
878 if (maFramebuffers[pScreen->u32ViewIndex].fRenderThreadMode)
879 return VINF_SUCCESS; /* nop it */
880
881 BOOL is3denabled;
882 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
883
884 if (is3denabled)
885 {
886 int rc = VERR_INVALID_STATE;
887 if (mhCrOglSvc)
888 {
889 VMMDev *pVMMDev = mParent->getVMMDev();
890 if (pVMMDev)
891 {
892 VBOXCRCMDCTL_HGCM *pCtl =
893 (VBOXCRCMDCTL_HGCM*)RTMemAlloc(sizeof (CRVBOXHGCMDEVRESIZE) + sizeof (VBOXCRCMDCTL_HGCM));
894 if (pCtl)
895 {
896 CRVBOXHGCMDEVRESIZE *pData = (CRVBOXHGCMDEVRESIZE*)(pCtl+1);
897 pData->Screen = *pScreen;
898 pData->pvVRAM = pvVRAM;
899
900 pCtl->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
901 pCtl->Hdr.u32Function = SHCRGL_HOST_FN_DEV_RESIZE;
902 pCtl->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
903 pCtl->aParms[0].u.pointer.addr = pData;
904 pCtl->aParms[0].u.pointer.size = sizeof (*pData);
905
906 rc = crCtlSubmit(&pCtl->Hdr, sizeof (*pCtl), displayCrCmdFree, pCtl);
907 if (!RT_SUCCESS(rc))
908 {
909 AssertMsgFailed(("crCtlSubmit failed rc %d\n", rc));
910 RTMemFree(pCtl);
911 }
912 }
913 else
914 rc = VERR_NO_MEMORY;
915 }
916 }
917
918 return rc;
919 }
920#endif /* #if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL) */
921 return VINF_SUCCESS;
922}
923
924/**
925 * Handles display resize event.
926 *
927 * @param w New display width
928 * @param h New display height
929 *
930 * @thread EMT
931 */
932int Display::handleDisplayResize (unsigned uScreenId, uint32_t bpp, void *pvVRAM,
933 uint32_t cbLine, uint32_t w, uint32_t h, uint16_t flags)
934{
935 LogRel(("Display::handleDisplayResize(): uScreenId = %d, pvVRAM=%p "
936 "w=%d h=%d bpp=%d cbLine=0x%X, flags=0x%X\n",
937 uScreenId, pvVRAM, w, h, bpp, cbLine, flags));
938
939 /* If there is no framebuffer, this call is not interesting. */
940 if (uScreenId >= mcMonitors)
941 {
942 return VINF_SUCCESS;
943 }
944
945 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
946 {
947 mLastAddress = pvVRAM;
948 mLastBytesPerLine = cbLine;
949 mLastBitsPerPixel = bpp;
950 mLastWidth = w;
951 mLastHeight = h;
952 mLastFlags = flags;
953
954 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
955 pFBInfo->w = w;
956 pFBInfo->h = h;
957
958 pFBInfo->u16BitsPerPixel = (uint16_t)bpp;
959 pFBInfo->pu8FramebufferVRAM = (uint8_t *)pvVRAM;
960 pFBInfo->u32LineSize = cbLine;
961 }
962
963 /* Guest screen image will be invalid during resize, make sure that it is not updated. */
964 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
965 {
966 mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, false);
967
968 mpDrv->IConnector.pu8Data = NULL;
969 mpDrv->IConnector.cbScanline = 0;
970 mpDrv->IConnector.cBits = 32; /* DevVGA does not work with cBits == 0. */
971 mpDrv->IConnector.cx = 0;
972 mpDrv->IConnector.cy = 0;
973 }
974
975 maFramebuffers[uScreenId].pSourceBitmap.setNull();
976
977 ULONG pixelFormat;
978
979 switch (bpp)
980 {
981 case 32:
982 case 24:
983 case 16:
984 pixelFormat = FramebufferPixelFormat_FOURCC_RGB;
985 break;
986 default:
987 pixelFormat = FramebufferPixelFormat_Opaque;
988 bpp = cbLine = 0;
989 break;
990 }
991
992 /* Atomically set the resize status before calling the framebuffer. The new InProgress status will
993 * disable access to the VGA device by the EMT thread.
994 */
995 bool f = ASMAtomicCmpXchgU32 (&maFramebuffers[uScreenId].u32ResizeStatus,
996 ResizeStatus_InProgress, ResizeStatus_Void);
997 if (!f)
998 {
999 /* This could be a result of the screenshot taking call Display::TakeScreenShot:
1000 * if the framebuffer is processing the resize request and GUI calls the TakeScreenShot
1001 * and the guest has reprogrammed the virtual VGA devices again so a new resize is required.
1002 *
1003 * Save the resize information and return the pending status code.
1004 *
1005 * Note: the resize information is only accessed on EMT so no serialization is required.
1006 */
1007 LogRel(("Display::handleDisplayResize(): Warning: resize postponed.\n"));
1008
1009 maFramebuffers[uScreenId].pendingResize.fPending = true;
1010 maFramebuffers[uScreenId].pendingResize.pixelFormat = pixelFormat;
1011 maFramebuffers[uScreenId].pendingResize.pvVRAM = pvVRAM;
1012 maFramebuffers[uScreenId].pendingResize.bpp = bpp;
1013 maFramebuffers[uScreenId].pendingResize.cbLine = cbLine;
1014 maFramebuffers[uScreenId].pendingResize.w = w;
1015 maFramebuffers[uScreenId].pendingResize.h = h;
1016 maFramebuffers[uScreenId].pendingResize.flags = flags;
1017
1018 return VINF_VGA_RESIZE_IN_PROGRESS;
1019 }
1020
1021 if (!maFramebuffers[uScreenId].pFramebuffer.isNull())
1022 {
1023 callFramebufferResize(maFramebuffers[uScreenId].pFramebuffer, uScreenId,
1024 pixelFormat, pvVRAM, bpp, cbLine, w, h);
1025 }
1026
1027 /* Set the status so the 'handleResizeCompleted' would work. */
1028 f = ASMAtomicCmpXchgU32 (&maFramebuffers[uScreenId].u32ResizeStatus,
1029 ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
1030 AssertRelease(f);NOREF(f);
1031
1032 AssertRelease(!maFramebuffers[uScreenId].pendingResize.fPending);
1033
1034 /* The method also unlocks the framebuffer. */
1035 handleResizeCompletedEMT(uScreenId, TRUE);
1036
1037 return VINF_SUCCESS;
1038}
1039
1040/**
1041 * Framebuffer has been resized.
1042 * Read the new display data and unlock the framebuffer.
1043 *
1044 * @thread EMT
1045 */
1046void Display::handleResizeCompletedEMT(unsigned uScreenId, BOOL fResizeContext)
1047{
1048 LogRelFlowFunc(("\n"));
1049
1050 do /* to use 'break' */
1051 {
1052 if (uScreenId >= mcMonitors)
1053 {
1054 break;
1055 }
1056
1057 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1058
1059 /* Try to into non resizing state. */
1060 bool f = ASMAtomicCmpXchgU32 (&pFBInfo->u32ResizeStatus, ResizeStatus_Void, ResizeStatus_UpdateDisplayData);
1061
1062 if (f == false)
1063 {
1064 /* This is not the display that has completed resizing. */
1065 AssertFailed();
1066 break;
1067 }
1068
1069 /* Check whether a resize is pending for this framebuffer. */
1070 if (pFBInfo->pendingResize.fPending)
1071 {
1072 /* Reset the condition, call the display resize with saved data and continue.
1073 *
1074 * Note: handleDisplayResize can call handleResizeCompletedEMT back,
1075 * but infinite recursion is not possible, because when the handleResizeCompletedEMT
1076 * is called, the pFBInfo->pendingResize.fPending is equal to false.
1077 */
1078 pFBInfo->pendingResize.fPending = false;
1079 handleDisplayResize (uScreenId, pFBInfo->pendingResize.bpp, pFBInfo->pendingResize.pvVRAM,
1080 pFBInfo->pendingResize.cbLine, pFBInfo->pendingResize.w, pFBInfo->pendingResize.h,
1081 pFBInfo->pendingResize.flags);
1082 break;
1083 }
1084
1085 /* Inform VRDP server about the change of display parameters.
1086 * Must be done before calling NotifyUpdate below.
1087 */
1088 LogRelFlowFunc(("Calling VRDP\n"));
1089 mParent->consoleVRDPServer()->SendResize();
1090
1091 /* @todo Merge these two 'if's within one 'if (!pFBInfo->pFramebuffer.isNull())' */
1092 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && !pFBInfo->pFramebuffer.isNull())
1093 {
1094 /* If the screen resize was because of disabling, tell framebuffer to repaint.
1095 * The framebuffer if now in default format so it will not use guest VRAM
1096 * and will show usually black image which is there after framebuffer resize.
1097 */
1098 if (pFBInfo->fDisabled)
1099 pFBInfo->pFramebuffer->NotifyUpdate(0, 0, mpDrv->IConnector.cx, mpDrv->IConnector.cy);
1100 }
1101 else if (!pFBInfo->pFramebuffer.isNull())
1102 {
1103 /* If the screen resize was because of disabling, tell framebuffer to repaint.
1104 * The framebuffer if now in default format so it will not use guest VRAM
1105 * and will show usually black image which is there after framebuffer resize.
1106 */
1107 if (pFBInfo->fDisabled)
1108 pFBInfo->pFramebuffer->NotifyUpdate(0, 0, pFBInfo->w, pFBInfo->h);
1109 }
1110 LogRelFlow(("[%d]: default format %d\n", uScreenId, pFBInfo->fDefaultFormat));
1111
1112 /* Handle the case if there are some saved visible region that needs to be
1113 * applied after the resize of the framebuffer is completed
1114 */
1115 SaveSeamlessRectLock();
1116 PRTRECT pSavedVisibleRegion = pFBInfo->mpSavedVisibleRegion;
1117 uint32_t cSavedVisibleRegion = pFBInfo->mcSavedVisibleRegion;
1118 pFBInfo->mpSavedVisibleRegion = NULL;
1119 pFBInfo->mcSavedVisibleRegion = 0;
1120 SaveSeamlessRectUnLock();
1121
1122 if (pSavedVisibleRegion)
1123 {
1124 handleSetVisibleRegion(cSavedVisibleRegion, pSavedVisibleRegion);
1125 RTMemFree(pSavedVisibleRegion);
1126 }
1127
1128#ifdef DEBUG_sunlover
1129 if (!g_stam)
1130 {
1131 Console::SafeVMPtr ptrVM(mParent);
1132 AssertComRC(ptrVM.rc());
1133 STAMR3RegisterU(ptrVM.rawUVM(), &g_StatDisplayRefresh, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
1134 "/PROF/Display/Refresh", STAMUNIT_TICKS_PER_CALL, "Time spent in EMT for display updates.");
1135 g_stam = 1;
1136 }
1137#endif /* DEBUG_sunlover */
1138
1139#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1140 {
1141 BOOL is3denabled;
1142 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
1143
1144 if (is3denabled)
1145 {
1146 VBOXCRCMDCTL_HGCM data;
1147 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1148 data.Hdr.u32Function = SHCRGL_HOST_FN_SCREEN_CHANGED;
1149
1150 data.aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
1151 data.aParms[0].u.uint32 = uScreenId;
1152
1153 if (fResizeContext)
1154 crCtlSubmitAsyncCmdCopy(&data.Hdr, sizeof (data));
1155 else
1156 crCtlSubmitSync(&data.Hdr, sizeof (data));
1157 }
1158 }
1159#endif /* VBOX_WITH_CROGL */
1160 } while(0);
1161}
1162
1163static void checkCoordBounds (int *px, int *py, int *pw, int *ph, int cx, int cy)
1164{
1165 /* Correct negative x and y coordinates. */
1166 if (*px < 0)
1167 {
1168 *px += *pw; /* Compute xRight which is also the new width. */
1169
1170 *pw = (*px < 0)? 0: *px;
1171
1172 *px = 0;
1173 }
1174
1175 if (*py < 0)
1176 {
1177 *py += *ph; /* Compute xBottom, which is also the new height. */
1178
1179 *ph = (*py < 0)? 0: *py;
1180
1181 *py = 0;
1182 }
1183
1184 /* Also check if coords are greater than the display resolution. */
1185 if (*px + *pw > cx)
1186 {
1187 *pw = cx > *px? cx - *px: 0;
1188 }
1189
1190 if (*py + *ph > cy)
1191 {
1192 *ph = cy > *py? cy - *py: 0;
1193 }
1194}
1195
1196unsigned mapCoordsToScreen(DISPLAYFBINFO *pInfos, unsigned cInfos, int *px, int *py, int *pw, int *ph)
1197{
1198 DISPLAYFBINFO *pInfo = pInfos;
1199 unsigned uScreenId;
1200 LogSunlover(("mapCoordsToScreen: %d,%d %dx%d\n", *px, *py, *pw, *ph));
1201 for (uScreenId = 0; uScreenId < cInfos; uScreenId++, pInfo++)
1202 {
1203 LogSunlover((" [%d] %d,%d %dx%d\n", uScreenId, pInfo->xOrigin, pInfo->yOrigin, pInfo->w, pInfo->h));
1204 if ( (pInfo->xOrigin <= *px && *px < pInfo->xOrigin + (int)pInfo->w)
1205 && (pInfo->yOrigin <= *py && *py < pInfo->yOrigin + (int)pInfo->h))
1206 {
1207 /* The rectangle belongs to the screen. Correct coordinates. */
1208 *px -= pInfo->xOrigin;
1209 *py -= pInfo->yOrigin;
1210 LogSunlover((" -> %d,%d", *px, *py));
1211 break;
1212 }
1213 }
1214 if (uScreenId == cInfos)
1215 {
1216 /* Map to primary screen. */
1217 uScreenId = 0;
1218 }
1219 LogSunlover((" scr %d\n", uScreenId));
1220 return uScreenId;
1221}
1222
1223
1224/**
1225 * Handles display update event.
1226 *
1227 * @param x Update area x coordinate
1228 * @param y Update area y coordinate
1229 * @param w Update area width
1230 * @param h Update area height
1231 *
1232 * @thread EMT
1233 */
1234void Display::handleDisplayUpdateLegacy (int x, int y, int w, int h)
1235{
1236 unsigned uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
1237
1238#ifdef DEBUG_sunlover
1239 LogFlowFunc(("%d,%d %dx%d (checked)\n", x, y, w, h));
1240#endif /* DEBUG_sunlover */
1241
1242 handleDisplayUpdate (uScreenId, x, y, w, h);
1243}
1244
1245void Display::handleDisplayUpdate (unsigned uScreenId, int x, int y, int w, int h)
1246{
1247 /*
1248 * Always runs under either VBVA lock or, for HGSMI, DevVGA lock.
1249 * Safe to use VBVA vars and take the framebuffer lock.
1250 */
1251
1252#ifdef DEBUG_sunlover
1253 LogFlowFunc(("[%d] %d,%d %dx%d (%d,%d)\n",
1254 uScreenId, x, y, w, h, mpDrv->IConnector.cx, mpDrv->IConnector.cy));
1255#endif /* DEBUG_sunlover */
1256
1257 /* No updates for a disabled guest screen. */
1258 if (maFramebuffers[uScreenId].fDisabled)
1259 return;
1260
1261 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
1262 checkCoordBounds (&x, &y, &w, &h, mpDrv->IConnector.cx, mpDrv->IConnector.cy);
1263 else
1264 checkCoordBounds (&x, &y, &w, &h, maFramebuffers[uScreenId].w,
1265 maFramebuffers[uScreenId].h);
1266
1267 IFramebuffer *pFramebuffer = maFramebuffers[uScreenId].pFramebuffer;
1268 if (pFramebuffer != NULL)
1269 {
1270 pFramebuffer->Lock();
1271
1272 if (w != 0 && h != 0)
1273 pFramebuffer->NotifyUpdate(x, y, w, h);
1274
1275 pFramebuffer->Unlock();
1276 }
1277
1278#ifndef VBOX_WITH_HGSMI
1279 if (!mfVideoAccelEnabled)
1280 {
1281#else
1282 if (!mfVideoAccelEnabled && !maFramebuffers[uScreenId].fVBVAEnabled)
1283 {
1284#endif /* VBOX_WITH_HGSMI */
1285 /* When VBVA is enabled, the VRDP server is informed in the VideoAccelFlush.
1286 * Inform the server here only if VBVA is disabled.
1287 */
1288 if (maFramebuffers[uScreenId].u32ResizeStatus == ResizeStatus_Void)
1289 mParent->consoleVRDPServer()->SendUpdateBitmap(uScreenId, x, y, w, h);
1290 }
1291}
1292
1293/**
1294 * Returns the upper left and lower right corners of the virtual framebuffer.
1295 * The lower right is "exclusive" (i.e. first pixel beyond the framebuffer),
1296 * and the origin is (0, 0), not (1, 1) like the GUI returns.
1297 */
1298void Display::getFramebufferDimensions(int32_t *px1, int32_t *py1,
1299 int32_t *px2, int32_t *py2)
1300{
1301 int32_t x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1303
1304 AssertPtrReturnVoid(px1);
1305 AssertPtrReturnVoid(py1);
1306 AssertPtrReturnVoid(px2);
1307 AssertPtrReturnVoid(py2);
1308 LogRelFlowFunc(("\n"));
1309
1310 if (!mpDrv)
1311 return;
1312 /* If VBVA is not in use then this flag will not be set and this
1313 * will still work as it should. */
1314 if (!maFramebuffers[0].fDisabled)
1315 {
1316 x1 = (int32_t)maFramebuffers[0].xOrigin;
1317 y1 = (int32_t)maFramebuffers[0].yOrigin;
1318 x2 = mpDrv->IConnector.cx + (int32_t)maFramebuffers[0].xOrigin;
1319 y2 = mpDrv->IConnector.cy + (int32_t)maFramebuffers[0].yOrigin;
1320 }
1321 for (unsigned i = 1; i < mcMonitors; ++i)
1322 {
1323 if (!maFramebuffers[i].fDisabled)
1324 {
1325 x1 = RT_MIN(x1, maFramebuffers[i].xOrigin);
1326 y1 = RT_MIN(y1, maFramebuffers[i].yOrigin);
1327 x2 = RT_MAX(x2, maFramebuffers[i].xOrigin
1328 + (int32_t)maFramebuffers[i].w);
1329 y2 = RT_MAX(y2, maFramebuffers[i].yOrigin
1330 + (int32_t)maFramebuffers[i].h);
1331 }
1332 }
1333 *px1 = x1;
1334 *py1 = y1;
1335 *px2 = x2;
1336 *py2 = y2;
1337}
1338
1339static bool displayIntersectRect(RTRECT *prectResult,
1340 const RTRECT *prect1,
1341 const RTRECT *prect2)
1342{
1343 /* Initialize result to an empty record. */
1344 memset (prectResult, 0, sizeof (RTRECT));
1345
1346 int xLeftResult = RT_MAX(prect1->xLeft, prect2->xLeft);
1347 int xRightResult = RT_MIN(prect1->xRight, prect2->xRight);
1348
1349 if (xLeftResult < xRightResult)
1350 {
1351 /* There is intersection by X. */
1352
1353 int yTopResult = RT_MAX(prect1->yTop, prect2->yTop);
1354 int yBottomResult = RT_MIN(prect1->yBottom, prect2->yBottom);
1355
1356 if (yTopResult < yBottomResult)
1357 {
1358 /* There is intersection by Y. */
1359
1360 prectResult->xLeft = xLeftResult;
1361 prectResult->yTop = yTopResult;
1362 prectResult->xRight = xRightResult;
1363 prectResult->yBottom = yBottomResult;
1364
1365 return true;
1366 }
1367 }
1368
1369 return false;
1370}
1371
1372int Display::handleSetVisibleRegion(uint32_t cRect, PRTRECT pRect)
1373{
1374 RTRECT *pVisibleRegion = (RTRECT *)RTMemTmpAlloc( RT_MAX(cRect, 1)
1375 * sizeof (RTRECT));
1376 if (!pVisibleRegion)
1377 {
1378 return VERR_NO_TMP_MEMORY;
1379 }
1380
1381 unsigned uScreenId;
1382 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1383 {
1384 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1385
1386 if (!pFBInfo->pFramebuffer.isNull())
1387 {
1388 if (pFBInfo->u32ResizeStatus != ResizeStatus_Void)
1389 {
1390 /* handle the case where new rectangles are received from the GA
1391 * when framebuffer resizing is in progress.
1392 * Just save the rectangles to be applied for later time when FB resizing is complete
1393 * (from handleResizeCompletedEMT).
1394 * This is done to prevent a race condition where a new rectangles are received
1395 * from the GA after a resize event and framebuffer resizing is still in progress
1396 * As a result the coordinates of the framebuffer are still
1397 * not updated and hence there is no intersection with the new rectangles passed
1398 * for the new region (THis is checked in the above if condition ). With 0 intersection,
1399 * cRectVisibleRegions = 0 is returned to the GUI and if GUI has invalidated its
1400 * earlier region then it draws nothihing and seamless mode doesn't display the
1401 * guest desktop.
1402 */
1403 SaveSeamlessRectLock();
1404 RTMemFree(pFBInfo->mpSavedVisibleRegion);
1405
1406 pFBInfo->mpSavedVisibleRegion = (RTRECT *)RTMemAlloc( RT_MAX(cRect, 1)
1407 * sizeof (RTRECT));
1408 if (pFBInfo->mpSavedVisibleRegion)
1409 {
1410 memcpy(pFBInfo->mpSavedVisibleRegion, pRect, cRect * sizeof(RTRECT));
1411 pFBInfo->mcSavedVisibleRegion = cRect;
1412 }
1413 else
1414 {
1415 pFBInfo->mcSavedVisibleRegion = 0;
1416 }
1417 SaveSeamlessRectUnLock();
1418 continue;
1419 }
1420 /* Prepare a new array of rectangles which intersect with the framebuffer.
1421 */
1422 RTRECT rectFramebuffer;
1423 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
1424 {
1425 rectFramebuffer.xLeft = 0;
1426 rectFramebuffer.yTop = 0;
1427 if (mpDrv)
1428 {
1429 rectFramebuffer.xRight = mpDrv->IConnector.cx;
1430 rectFramebuffer.yBottom = mpDrv->IConnector.cy;
1431 }
1432 else
1433 {
1434 rectFramebuffer.xRight = 0;
1435 rectFramebuffer.yBottom = 0;
1436 }
1437 }
1438 else
1439 {
1440 rectFramebuffer.xLeft = pFBInfo->xOrigin;
1441 rectFramebuffer.yTop = pFBInfo->yOrigin;
1442 rectFramebuffer.xRight = pFBInfo->xOrigin + pFBInfo->w;
1443 rectFramebuffer.yBottom = pFBInfo->yOrigin + pFBInfo->h;
1444 }
1445
1446 uint32_t cRectVisibleRegion = 0;
1447
1448 uint32_t i;
1449 for (i = 0; i < cRect; i++)
1450 {
1451 if (displayIntersectRect(&pVisibleRegion[cRectVisibleRegion], &pRect[i], &rectFramebuffer))
1452 {
1453 pVisibleRegion[cRectVisibleRegion].xLeft -= pFBInfo->xOrigin;
1454 pVisibleRegion[cRectVisibleRegion].yTop -= pFBInfo->yOrigin;
1455 pVisibleRegion[cRectVisibleRegion].xRight -= pFBInfo->xOrigin;
1456 pVisibleRegion[cRectVisibleRegion].yBottom -= pFBInfo->yOrigin;
1457
1458 cRectVisibleRegion++;
1459 }
1460 }
1461 pFBInfo->pFramebuffer->SetVisibleRegion((BYTE *)pVisibleRegion, cRectVisibleRegion);
1462 }
1463 }
1464
1465#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1466 BOOL is3denabled = FALSE;
1467
1468 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
1469
1470 VMMDev *vmmDev = mParent->getVMMDev();
1471 if (is3denabled && vmmDev)
1472 {
1473 if (mhCrOglSvc)
1474 {
1475 VBOXCRCMDCTL_HGCM *pCtl = (VBOXCRCMDCTL_HGCM*)RTMemAlloc(RT_MAX(cRect, 1) * sizeof (RTRECT)
1476 + sizeof (VBOXCRCMDCTL_HGCM));
1477 if (pCtl)
1478 {
1479 RTRECT *pRectsCopy = (RTRECT*)(pCtl+1);
1480 memcpy(pRectsCopy, pRect, cRect * sizeof (RTRECT));
1481
1482 pCtl->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1483 pCtl->Hdr.u32Function = SHCRGL_HOST_FN_SET_VISIBLE_REGION;
1484
1485 pCtl->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
1486 pCtl->aParms[0].u.pointer.addr = pRectsCopy;
1487 pCtl->aParms[0].u.pointer.size = cRect * sizeof (RTRECT);
1488
1489 int rc = crCtlSubmit(&pCtl->Hdr, sizeof (*pCtl), displayCrCmdFree, pCtl);
1490 if (!RT_SUCCESS(rc))
1491 {
1492 AssertMsgFailed(("crCtlSubmit failed rc %d\n", rc));
1493 RTMemFree(pCtl);
1494 }
1495 }
1496 else
1497 AssertMsgFailed(("failed to allocate rects memory\n"));
1498 }
1499 else
1500 AssertMsgFailed(("mhCrOglSvc is NULL\n"));
1501 }
1502#endif
1503
1504 RTMemTmpFree(pVisibleRegion);
1505
1506 return VINF_SUCCESS;
1507}
1508
1509int Display::handleQueryVisibleRegion(uint32_t *pcRect, PRTRECT pRect)
1510{
1511 // @todo Currently not used by the guest and is not implemented in framebuffers. Remove?
1512 return VERR_NOT_SUPPORTED;
1513}
1514
1515typedef struct _VBVADIRTYREGION
1516{
1517 /* Copies of object's pointers used by vbvaRgn functions. */
1518 DISPLAYFBINFO *paFramebuffers;
1519 unsigned cMonitors;
1520 Display *pDisplay;
1521 PPDMIDISPLAYPORT pPort;
1522
1523} VBVADIRTYREGION;
1524
1525static void vbvaRgnInit (VBVADIRTYREGION *prgn, DISPLAYFBINFO *paFramebuffers, unsigned cMonitors,
1526 Display *pd, PPDMIDISPLAYPORT pp)
1527{
1528 prgn->paFramebuffers = paFramebuffers;
1529 prgn->cMonitors = cMonitors;
1530 prgn->pDisplay = pd;
1531 prgn->pPort = pp;
1532
1533 unsigned uScreenId;
1534 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
1535 {
1536 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1537
1538 RT_ZERO(pFBInfo->dirtyRect);
1539 }
1540}
1541
1542static void vbvaRgnDirtyRect (VBVADIRTYREGION *prgn, unsigned uScreenId, VBVACMDHDR *phdr)
1543{
1544 LogSunlover(("x = %d, y = %d, w = %d, h = %d\n",
1545 phdr->x, phdr->y, phdr->w, phdr->h));
1546
1547 /*
1548 * Here update rectangles are accumulated to form an update area.
1549 * @todo
1550 * Now the simplest method is used which builds one rectangle that
1551 * includes all update areas. A bit more advanced method can be
1552 * employed here. The method should be fast however.
1553 */
1554 if (phdr->w == 0 || phdr->h == 0)
1555 {
1556 /* Empty rectangle. */
1557 return;
1558 }
1559
1560 int32_t xRight = phdr->x + phdr->w;
1561 int32_t yBottom = phdr->y + phdr->h;
1562
1563 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1564
1565 if (pFBInfo->dirtyRect.xRight == 0)
1566 {
1567 /* This is the first rectangle to be added. */
1568 pFBInfo->dirtyRect.xLeft = phdr->x;
1569 pFBInfo->dirtyRect.yTop = phdr->y;
1570 pFBInfo->dirtyRect.xRight = xRight;
1571 pFBInfo->dirtyRect.yBottom = yBottom;
1572 }
1573 else
1574 {
1575 /* Adjust region coordinates. */
1576 if (pFBInfo->dirtyRect.xLeft > phdr->x)
1577 {
1578 pFBInfo->dirtyRect.xLeft = phdr->x;
1579 }
1580
1581 if (pFBInfo->dirtyRect.yTop > phdr->y)
1582 {
1583 pFBInfo->dirtyRect.yTop = phdr->y;
1584 }
1585
1586 if (pFBInfo->dirtyRect.xRight < xRight)
1587 {
1588 pFBInfo->dirtyRect.xRight = xRight;
1589 }
1590
1591 if (pFBInfo->dirtyRect.yBottom < yBottom)
1592 {
1593 pFBInfo->dirtyRect.yBottom = yBottom;
1594 }
1595 }
1596
1597 if (pFBInfo->fDefaultFormat)
1598 {
1599 //@todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
1600 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, phdr->x, phdr->y, phdr->w, phdr->h);
1601 prgn->pDisplay->handleDisplayUpdateLegacy (phdr->x + pFBInfo->xOrigin,
1602 phdr->y + pFBInfo->yOrigin, phdr->w, phdr->h);
1603 }
1604
1605 return;
1606}
1607
1608static void vbvaRgnUpdateFramebuffer (VBVADIRTYREGION *prgn, unsigned uScreenId)
1609{
1610 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1611
1612 uint32_t w = pFBInfo->dirtyRect.xRight - pFBInfo->dirtyRect.xLeft;
1613 uint32_t h = pFBInfo->dirtyRect.yBottom - pFBInfo->dirtyRect.yTop;
1614
1615 if (!pFBInfo->fDefaultFormat && w != 0 && h != 0)
1616 {
1617 //@todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
1618 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, pFBInfo->dirtyRect.xLeft, pFBInfo->dirtyRect.yTop, w, h);
1619 prgn->pDisplay->handleDisplayUpdateLegacy (pFBInfo->dirtyRect.xLeft + pFBInfo->xOrigin,
1620 pFBInfo->dirtyRect.yTop + pFBInfo->yOrigin, w, h);
1621 }
1622}
1623
1624static void vbvaSetMemoryFlags (VBVAMEMORY *pVbvaMemory,
1625 bool fVideoAccelEnabled,
1626 bool fVideoAccelVRDP,
1627 uint32_t fu32SupportedOrders,
1628 DISPLAYFBINFO *paFBInfos,
1629 unsigned cFBInfos)
1630{
1631 if (pVbvaMemory)
1632 {
1633 /* This called only on changes in mode. So reset VRDP always. */
1634 uint32_t fu32Flags = VBVA_F_MODE_VRDP_RESET;
1635
1636 if (fVideoAccelEnabled)
1637 {
1638 fu32Flags |= VBVA_F_MODE_ENABLED;
1639
1640 if (fVideoAccelVRDP)
1641 {
1642 fu32Flags |= VBVA_F_MODE_VRDP | VBVA_F_MODE_VRDP_ORDER_MASK;
1643
1644 pVbvaMemory->fu32SupportedOrders = fu32SupportedOrders;
1645 }
1646 }
1647
1648 pVbvaMemory->fu32ModeFlags = fu32Flags;
1649 }
1650
1651 unsigned uScreenId;
1652 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
1653 {
1654 if (paFBInfos[uScreenId].pHostEvents)
1655 {
1656 paFBInfos[uScreenId].pHostEvents->fu32Events |= VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
1657 }
1658 }
1659}
1660
1661#ifdef VBOX_WITH_HGSMI
1662static void vbvaSetMemoryFlagsHGSMI (unsigned uScreenId,
1663 uint32_t fu32SupportedOrders,
1664 bool fVideoAccelVRDP,
1665 DISPLAYFBINFO *pFBInfo)
1666{
1667 LogRelFlowFunc(("HGSMI[%d]: %p\n", uScreenId, pFBInfo->pVBVAHostFlags));
1668
1669 if (pFBInfo->pVBVAHostFlags)
1670 {
1671 uint32_t fu32HostEvents = VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
1672
1673 if (pFBInfo->fVBVAEnabled)
1674 {
1675 fu32HostEvents |= VBVA_F_MODE_ENABLED;
1676
1677 if (fVideoAccelVRDP)
1678 {
1679 fu32HostEvents |= VBVA_F_MODE_VRDP;
1680 }
1681 }
1682
1683 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32HostEvents, fu32HostEvents);
1684 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32SupportedOrders, fu32SupportedOrders);
1685
1686 LogRelFlowFunc((" fu32HostEvents = 0x%08X, fu32SupportedOrders = 0x%08X\n", fu32HostEvents, fu32SupportedOrders));
1687 }
1688}
1689
1690static void vbvaSetMemoryFlagsAllHGSMI (uint32_t fu32SupportedOrders,
1691 bool fVideoAccelVRDP,
1692 DISPLAYFBINFO *paFBInfos,
1693 unsigned cFBInfos)
1694{
1695 unsigned uScreenId;
1696
1697 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
1698 {
1699 vbvaSetMemoryFlagsHGSMI(uScreenId, fu32SupportedOrders, fVideoAccelVRDP, &paFBInfos[uScreenId]);
1700 }
1701}
1702#endif /* VBOX_WITH_HGSMI */
1703
1704bool Display::VideoAccelAllowed (void)
1705{
1706 return true;
1707}
1708
1709int Display::vbvaLock(void)
1710{
1711 return RTCritSectEnter(&mVBVALock);
1712}
1713
1714void Display::vbvaUnlock(void)
1715{
1716 RTCritSectLeave(&mVBVALock);
1717}
1718
1719int Display::SaveSeamlessRectLock(void)
1720{
1721 return RTCritSectEnter(&mSaveSeamlessRectLock);
1722}
1723
1724void Display::SaveSeamlessRectUnLock(void)
1725{
1726 RTCritSectLeave(&mSaveSeamlessRectLock);
1727}
1728
1729
1730/**
1731 * @thread EMT
1732 */
1733int Display::VideoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
1734{
1735 int rc;
1736 vbvaLock();
1737 rc = videoAccelEnable (fEnable, pVbvaMemory);
1738 vbvaUnlock();
1739 return rc;
1740}
1741
1742int Display::videoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
1743{
1744 int rc = VINF_SUCCESS;
1745
1746 /* Called each time the guest wants to use acceleration,
1747 * or when the VGA device disables acceleration,
1748 * or when restoring the saved state with accel enabled.
1749 *
1750 * VGA device disables acceleration on each video mode change
1751 * and on reset.
1752 *
1753 * Guest enabled acceleration at will. And it has to enable
1754 * acceleration after a mode change.
1755 */
1756 LogRelFlowFunc(("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
1757 mfVideoAccelEnabled, fEnable, pVbvaMemory));
1758
1759 /* Strictly check parameters. Callers must not pass anything in the case. */
1760 Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));
1761
1762 if (!VideoAccelAllowed ())
1763 return VERR_NOT_SUPPORTED;
1764
1765 /*
1766 * Verify that the VM is in running state. If it is not,
1767 * then this must be postponed until it goes to running.
1768 */
1769 if (!mfMachineRunning)
1770 {
1771 Assert (!mfVideoAccelEnabled);
1772
1773 LogRelFlowFunc(("Machine is not yet running.\n"));
1774
1775 if (fEnable)
1776 {
1777 mfPendingVideoAccelEnable = fEnable;
1778 mpPendingVbvaMemory = pVbvaMemory;
1779 }
1780
1781 return rc;
1782 }
1783
1784 /* Check that current status is not being changed */
1785 if (mfVideoAccelEnabled == fEnable)
1786 return rc;
1787
1788 if (mfVideoAccelEnabled)
1789 {
1790 /* Process any pending orders and empty the VBVA ring buffer. */
1791 videoAccelFlush ();
1792 }
1793
1794 if (!fEnable && mpVbvaMemory)
1795 mpVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;
1796
1797 /* Safety precaution. There is no more VBVA until everything is setup! */
1798 mpVbvaMemory = NULL;
1799 mfVideoAccelEnabled = false;
1800
1801 /* Update entire display. */
1802 if (maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].u32ResizeStatus == ResizeStatus_Void)
1803 mpDrv->pUpPort->pfnUpdateDisplayAll(mpDrv->pUpPort);
1804
1805 /* Everything OK. VBVA status can be changed. */
1806
1807 /* Notify the VMMDev, which saves VBVA status in the saved state,
1808 * and needs to know current status.
1809 */
1810 VMMDev *pVMMDev = mParent->getVMMDev();
1811 if (pVMMDev)
1812 {
1813 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
1814 if (pVMMDevPort)
1815 pVMMDevPort->pfnVBVAChange(pVMMDevPort, fEnable);
1816 }
1817
1818 if (fEnable)
1819 {
1820 mpVbvaMemory = pVbvaMemory;
1821 mfVideoAccelEnabled = true;
1822
1823 /* Initialize the hardware memory. */
1824 vbvaSetMemoryFlags(mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
1825 mpVbvaMemory->off32Data = 0;
1826 mpVbvaMemory->off32Free = 0;
1827
1828 memset(mpVbvaMemory->aRecords, 0, sizeof (mpVbvaMemory->aRecords));
1829 mpVbvaMemory->indexRecordFirst = 0;
1830 mpVbvaMemory->indexRecordFree = 0;
1831
1832 mfu32PendingVideoAccelDisable = false;
1833
1834 LogRel(("VBVA: Enabled.\n"));
1835 }
1836 else
1837 {
1838 LogRel(("VBVA: Disabled.\n"));
1839 }
1840
1841 LogRelFlowFunc(("VideoAccelEnable: rc = %Rrc.\n", rc));
1842
1843 return rc;
1844}
1845
1846/* Called always by one VRDP server thread. Can be thread-unsafe.
1847 */
1848void Display::VideoAccelVRDP (bool fEnable)
1849{
1850 LogRelFlowFunc(("fEnable = %d\n", fEnable));
1851
1852 vbvaLock();
1853
1854 int c = fEnable?
1855 ASMAtomicIncS32 (&mcVideoAccelVRDPRefs):
1856 ASMAtomicDecS32 (&mcVideoAccelVRDPRefs);
1857
1858 Assert (c >= 0);
1859
1860 if (c == 0)
1861 {
1862 /* The last client has disconnected, and the accel can be
1863 * disabled.
1864 */
1865 Assert (fEnable == false);
1866
1867 mfVideoAccelVRDP = false;
1868 mfu32SupportedOrders = 0;
1869
1870 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders,
1871 maFramebuffers, mcMonitors);
1872#ifdef VBOX_WITH_HGSMI
1873 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1874 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1875#endif /* VBOX_WITH_HGSMI */
1876
1877 LogRel(("VBVA: VRDP acceleration has been disabled.\n"));
1878 }
1879 else if ( c == 1
1880 && !mfVideoAccelVRDP)
1881 {
1882 /* The first client has connected. Enable the accel.
1883 */
1884 Assert (fEnable == true);
1885
1886 mfVideoAccelVRDP = true;
1887 /* Supporting all orders. */
1888 mfu32SupportedOrders = ~0;
1889
1890 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders,
1891 maFramebuffers, mcMonitors);
1892#ifdef VBOX_WITH_HGSMI
1893 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1894 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1895#endif /* VBOX_WITH_HGSMI */
1896
1897 LogRel(("VBVA: VRDP acceleration has been requested.\n"));
1898 }
1899 else
1900 {
1901 /* A client is connected or disconnected but there is no change in the
1902 * accel state. It remains enabled.
1903 */
1904 Assert (mfVideoAccelVRDP == true);
1905 }
1906 vbvaUnlock();
1907}
1908
1909static bool vbvaVerifyRingBuffer (VBVAMEMORY *pVbvaMemory)
1910{
1911 return true;
1912}
1913
1914static void vbvaFetchBytes (VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t cbDst)
1915{
1916 if (cbDst >= VBVA_RING_BUFFER_SIZE)
1917 {
1918 AssertMsgFailed (("cbDst = 0x%08X, ring buffer size 0x%08X\n", cbDst, VBVA_RING_BUFFER_SIZE));
1919 return;
1920 }
1921
1922 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
1923 uint8_t *src = &pVbvaMemory->au8RingBuffer[pVbvaMemory->off32Data];
1924 int32_t i32Diff = cbDst - u32BytesTillBoundary;
1925
1926 if (i32Diff <= 0)
1927 {
1928 /* Chunk will not cross buffer boundary. */
1929 memcpy (pu8Dst, src, cbDst);
1930 }
1931 else
1932 {
1933 /* Chunk crosses buffer boundary. */
1934 memcpy (pu8Dst, src, u32BytesTillBoundary);
1935 memcpy (pu8Dst + u32BytesTillBoundary, &pVbvaMemory->au8RingBuffer[0], i32Diff);
1936 }
1937
1938 /* Advance data offset. */
1939 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbDst) % VBVA_RING_BUFFER_SIZE;
1940
1941 return;
1942}
1943
1944
1945static bool vbvaPartialRead (uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
1946{
1947 uint8_t *pu8New;
1948
1949 LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
1950 *ppu8, *pcb, cbRecord));
1951
1952 if (*ppu8)
1953 {
1954 Assert (*pcb);
1955 pu8New = (uint8_t *)RTMemRealloc (*ppu8, cbRecord);
1956 }
1957 else
1958 {
1959 Assert (!*pcb);
1960 pu8New = (uint8_t *)RTMemAlloc (cbRecord);
1961 }
1962
1963 if (!pu8New)
1964 {
1965 /* Memory allocation failed, fail the function. */
1966 Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
1967 cbRecord));
1968
1969 if (*ppu8)
1970 {
1971 RTMemFree (*ppu8);
1972 }
1973
1974 *ppu8 = NULL;
1975 *pcb = 0;
1976
1977 return false;
1978 }
1979
1980 /* Fetch data from the ring buffer. */
1981 vbvaFetchBytes (pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
1982
1983 *ppu8 = pu8New;
1984 *pcb = cbRecord;
1985
1986 return true;
1987}
1988
1989/* For contiguous chunks just return the address in the buffer.
1990 * For crossing boundary - allocate a buffer from heap.
1991 */
1992bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
1993{
1994 uint32_t indexRecordFirst = mpVbvaMemory->indexRecordFirst;
1995 uint32_t indexRecordFree = mpVbvaMemory->indexRecordFree;
1996
1997#ifdef DEBUG_sunlover
1998 LogFlowFunc(("first = %d, free = %d\n",
1999 indexRecordFirst, indexRecordFree));
2000#endif /* DEBUG_sunlover */
2001
2002 if (!vbvaVerifyRingBuffer (mpVbvaMemory))
2003 {
2004 return false;
2005 }
2006
2007 if (indexRecordFirst == indexRecordFree)
2008 {
2009 /* No records to process. Return without assigning output variables. */
2010 return true;
2011 }
2012
2013 VBVARECORD *pRecord = &mpVbvaMemory->aRecords[indexRecordFirst];
2014
2015#ifdef DEBUG_sunlover
2016 LogFlowFunc(("cbRecord = 0x%08X\n", pRecord->cbRecord));
2017#endif /* DEBUG_sunlover */
2018
2019 uint32_t cbRecord = pRecord->cbRecord & ~VBVA_F_RECORD_PARTIAL;
2020
2021 if (mcbVbvaPartial)
2022 {
2023 /* There is a partial read in process. Continue with it. */
2024
2025 Assert (mpu8VbvaPartial);
2026
2027 LogFlowFunc(("continue partial record mcbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
2028 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
2029
2030 if (cbRecord > mcbVbvaPartial)
2031 {
2032 /* New data has been added to the record. */
2033 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
2034 {
2035 return false;
2036 }
2037 }
2038
2039 if (!(pRecord->cbRecord & VBVA_F_RECORD_PARTIAL))
2040 {
2041 /* The record is completed by guest. Return it to the caller. */
2042 *ppHdr = (VBVACMDHDR *)mpu8VbvaPartial;
2043 *pcbCmd = mcbVbvaPartial;
2044
2045 mpu8VbvaPartial = NULL;
2046 mcbVbvaPartial = 0;
2047
2048 /* Advance the record index. */
2049 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
2050
2051#ifdef DEBUG_sunlover
2052 LogFlowFunc(("partial done ok, data = %d, free = %d\n",
2053 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
2054#endif /* DEBUG_sunlover */
2055 }
2056
2057 return true;
2058 }
2059
2060 /* A new record need to be processed. */
2061 if (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)
2062 {
2063 /* Current record is being written by guest. '=' is important here. */
2064 if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
2065 {
2066 /* Partial read must be started. */
2067 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
2068 {
2069 return false;
2070 }
2071
2072 LogFlowFunc(("started partial record mcbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
2073 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
2074 }
2075
2076 return true;
2077 }
2078
2079 /* Current record is complete. If it is not empty, process it. */
2080 if (cbRecord)
2081 {
2082 /* The size of largest contiguous chunk in the ring biffer. */
2083 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - mpVbvaMemory->off32Data;
2084
2085 /* The ring buffer pointer. */
2086 uint8_t *au8RingBuffer = &mpVbvaMemory->au8RingBuffer[0];
2087
2088 /* The pointer to data in the ring buffer. */
2089 uint8_t *src = &au8RingBuffer[mpVbvaMemory->off32Data];
2090
2091 /* Fetch or point the data. */
2092 if (u32BytesTillBoundary >= cbRecord)
2093 {
2094 /* The command does not cross buffer boundary. Return address in the buffer. */
2095 *ppHdr = (VBVACMDHDR *)src;
2096
2097 /* Advance data offset. */
2098 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
2099 }
2100 else
2101 {
2102 /* The command crosses buffer boundary. Rare case, so not optimized. */
2103 uint8_t *dst = (uint8_t *)RTMemAlloc (cbRecord);
2104
2105 if (!dst)
2106 {
2107 LogRelFlowFunc(("could not allocate %d bytes from heap!!!\n", cbRecord));
2108 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
2109 return false;
2110 }
2111
2112 vbvaFetchBytes (mpVbvaMemory, dst, cbRecord);
2113
2114 *ppHdr = (VBVACMDHDR *)dst;
2115
2116#ifdef DEBUG_sunlover
2117 LogFlowFunc(("Allocated from heap %p\n", dst));
2118#endif /* DEBUG_sunlover */
2119 }
2120 }
2121
2122 *pcbCmd = cbRecord;
2123
2124 /* Advance the record index. */
2125 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
2126
2127#ifdef DEBUG_sunlover
2128 LogFlowFunc(("done ok, data = %d, free = %d\n",
2129 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
2130#endif /* DEBUG_sunlover */
2131
2132 return true;
2133}
2134
2135void Display::vbvaReleaseCmd (VBVACMDHDR *pHdr, int32_t cbCmd)
2136{
2137 uint8_t *au8RingBuffer = mpVbvaMemory->au8RingBuffer;
2138
2139 if ( (uint8_t *)pHdr >= au8RingBuffer
2140 && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
2141 {
2142 /* The pointer is inside ring buffer. Must be continuous chunk. */
2143 Assert (VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
2144
2145 /* Do nothing. */
2146
2147 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
2148 }
2149 else
2150 {
2151 /* The pointer is outside. It is then an allocated copy. */
2152
2153#ifdef DEBUG_sunlover
2154 LogFlowFunc(("Free heap %p\n", pHdr));
2155#endif /* DEBUG_sunlover */
2156
2157 if ((uint8_t *)pHdr == mpu8VbvaPartial)
2158 {
2159 mpu8VbvaPartial = NULL;
2160 mcbVbvaPartial = 0;
2161 }
2162 else
2163 {
2164 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
2165 }
2166
2167 RTMemFree (pHdr);
2168 }
2169
2170 return;
2171}
2172
2173
2174/**
2175 * Called regularly on the DisplayRefresh timer.
2176 * Also on behalf of guest, when the ring buffer is full.
2177 *
2178 * @thread EMT
2179 */
2180void Display::VideoAccelFlush (void)
2181{
2182 vbvaLock();
2183 videoAccelFlush();
2184 vbvaUnlock();
2185}
2186
2187/* Under VBVA lock. DevVGA is not taken. */
2188void Display::videoAccelFlush (void)
2189{
2190#ifdef DEBUG_sunlover_2
2191 LogFlowFunc(("mfVideoAccelEnabled = %d\n", mfVideoAccelEnabled));
2192#endif /* DEBUG_sunlover_2 */
2193
2194 if (!mfVideoAccelEnabled)
2195 {
2196 Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
2197 return;
2198 }
2199
2200 /* Here VBVA is enabled and we have the accelerator memory pointer. */
2201 Assert(mpVbvaMemory);
2202
2203#ifdef DEBUG_sunlover_2
2204 LogFlowFunc(("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
2205 mpVbvaMemory->indexRecordFirst, mpVbvaMemory->indexRecordFree,
2206 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
2207#endif /* DEBUG_sunlover_2 */
2208
2209 /* Quick check for "nothing to update" case. */
2210 if (mpVbvaMemory->indexRecordFirst == mpVbvaMemory->indexRecordFree)
2211 {
2212 return;
2213 }
2214
2215 /* Process the ring buffer */
2216 unsigned uScreenId;
2217
2218 /* Initialize dirty rectangles accumulator. */
2219 VBVADIRTYREGION rgn;
2220 vbvaRgnInit (&rgn, maFramebuffers, mcMonitors, this, mpDrv->pUpPort);
2221
2222 for (;;)
2223 {
2224 VBVACMDHDR *phdr = NULL;
2225 uint32_t cbCmd = ~0;
2226
2227 /* Fetch the command data. */
2228 if (!vbvaFetchCmd (&phdr, &cbCmd))
2229 {
2230 Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
2231 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
2232
2233 /* Disable VBVA on those processing errors. */
2234 videoAccelEnable (false, NULL);
2235
2236 break;
2237 }
2238
2239 if (cbCmd == uint32_t(~0))
2240 {
2241 /* No more commands yet in the queue. */
2242 break;
2243 }
2244
2245 if (cbCmd != 0)
2246 {
2247#ifdef DEBUG_sunlover
2248 LogFlowFunc(("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
2249 cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
2250#endif /* DEBUG_sunlover */
2251
2252 VBVACMDHDR hdrSaved = *phdr;
2253
2254 int x = phdr->x;
2255 int y = phdr->y;
2256 int w = phdr->w;
2257 int h = phdr->h;
2258
2259 uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
2260
2261 phdr->x = (int16_t)x;
2262 phdr->y = (int16_t)y;
2263 phdr->w = (uint16_t)w;
2264 phdr->h = (uint16_t)h;
2265
2266 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
2267
2268 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
2269 {
2270 /* Handle the command.
2271 *
2272 * Guest is responsible for updating the guest video memory.
2273 * The Windows guest does all drawing using Eng*.
2274 *
2275 * For local output, only dirty rectangle information is used
2276 * to update changed areas.
2277 *
2278 * Dirty rectangles are accumulated to exclude overlapping updates and
2279 * group small updates to a larger one.
2280 */
2281
2282 /* Accumulate the update. */
2283 vbvaRgnDirtyRect (&rgn, uScreenId, phdr);
2284
2285 /* Forward the command to VRDP server. */
2286 mParent->consoleVRDPServer()->SendUpdate (uScreenId, phdr, cbCmd);
2287
2288 *phdr = hdrSaved;
2289 }
2290 }
2291
2292 vbvaReleaseCmd (phdr, cbCmd);
2293 }
2294
2295 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
2296 {
2297 if (maFramebuffers[uScreenId].u32ResizeStatus == ResizeStatus_Void)
2298 {
2299 /* Draw the framebuffer. */
2300 vbvaRgnUpdateFramebuffer (&rgn, uScreenId);
2301 }
2302 }
2303}
2304
2305int Display::videoAccelRefreshProcess(void)
2306{
2307 int rc = VWRN_INVALID_STATE; /* Default is to do a display update in VGA device. */
2308
2309 vbvaLock();
2310
2311 if (ASMAtomicCmpXchgU32(&mfu32PendingVideoAccelDisable, false, true))
2312 {
2313 videoAccelEnable (false, NULL);
2314 }
2315 else if (mfPendingVideoAccelEnable)
2316 {
2317 /* Acceleration was enabled while machine was not yet running
2318 * due to restoring from saved state. Update entire display and
2319 * actually enable acceleration.
2320 */
2321 Assert(mpPendingVbvaMemory);
2322
2323 /* Acceleration can not be yet enabled.*/
2324 Assert(mpVbvaMemory == NULL);
2325 Assert(!mfVideoAccelEnabled);
2326
2327 if (mfMachineRunning)
2328 {
2329 videoAccelEnable (mfPendingVideoAccelEnable,
2330 mpPendingVbvaMemory);
2331
2332 /* Reset the pending state. */
2333 mfPendingVideoAccelEnable = false;
2334 mpPendingVbvaMemory = NULL;
2335 }
2336
2337 rc = VINF_TRY_AGAIN;
2338 }
2339 else
2340 {
2341 Assert(mpPendingVbvaMemory == NULL);
2342
2343 if (mfVideoAccelEnabled)
2344 {
2345 Assert(mpVbvaMemory);
2346 videoAccelFlush ();
2347
2348 rc = VINF_SUCCESS; /* VBVA processed, no need to a display update. */
2349 }
2350 }
2351
2352 vbvaUnlock();
2353
2354 return rc;
2355}
2356
2357void Display::notifyPowerDown(void)
2358{
2359 LogRelFlowFunc(("\n"));
2360
2361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2362
2363 /* Source bitmaps are not available anymore. */
2364 mfSourceBitmapEnabled = false;
2365
2366 /* Resize all displays to tell framebuffers to forget current source bitmap. */
2367 unsigned uScreenId = mcMonitors;
2368 while (uScreenId > 0)
2369 {
2370 --uScreenId;
2371
2372 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
2373 if (!pFBInfo->fDisabled)
2374 {
2375 handleDisplayResize(uScreenId, 32,
2376 pFBInfo->pu8FramebufferVRAM,
2377 pFBInfo->u32LineSize,
2378 pFBInfo->w,
2379 pFBInfo->h,
2380 0);
2381 }
2382 }
2383}
2384
2385// IDisplay methods
2386/////////////////////////////////////////////////////////////////////////////
2387STDMETHODIMP Display::GetScreenResolution(ULONG aScreenId,
2388 ULONG *aWidth, ULONG *aHeight, ULONG *aBitsPerPixel,
2389 LONG *aXOrigin, LONG *aYOrigin)
2390{
2391 LogRelFlowFunc(("aScreenId=%RU32\n", aScreenId));
2392
2393 AutoCaller autoCaller(this);
2394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2395
2396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2397
2398 uint32_t u32Width = 0;
2399 uint32_t u32Height = 0;
2400 uint32_t u32BitsPerPixel = 0;
2401 int32_t xOrigin = 0;
2402 int32_t yOrigin = 0;
2403
2404 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2405 {
2406 if (mpDrv)
2407 {
2408 u32Width = mpDrv->IConnector.cx;
2409 u32Height = mpDrv->IConnector.cy;
2410 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &u32BitsPerPixel);
2411 AssertRC(rc);
2412 }
2413 }
2414 else if (aScreenId < mcMonitors)
2415 {
2416 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2417 u32Width = pFBInfo->w;
2418 u32Height = pFBInfo->h;
2419 u32BitsPerPixel = pFBInfo->u16BitsPerPixel;
2420 xOrigin = pFBInfo->xOrigin;
2421 yOrigin = pFBInfo->yOrigin;
2422 }
2423 else
2424 {
2425 return E_INVALIDARG;
2426 }
2427
2428 if (aWidth)
2429 *aWidth = u32Width;
2430 if (aHeight)
2431 *aHeight = u32Height;
2432 if (aBitsPerPixel)
2433 *aBitsPerPixel = u32BitsPerPixel;
2434 if (aXOrigin)
2435 *aXOrigin = xOrigin;
2436 if (aYOrigin)
2437 *aYOrigin = yOrigin;
2438
2439 return S_OK;
2440}
2441
2442STDMETHODIMP Display::SetFramebuffer(ULONG aScreenId, IFramebuffer *aFramebuffer)
2443{
2444 LogRelFlowFunc(("\n"));
2445
2446 if (aFramebuffer != NULL)
2447 CheckComArgOutPointerValid(aFramebuffer);
2448
2449 AutoCaller autoCaller(this);
2450 if (FAILED(autoCaller.rc()))
2451 return autoCaller.rc();
2452
2453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2454
2455 Console::SafeVMPtrQuiet ptrVM(mParent);
2456 if (ptrVM.isOk())
2457 {
2458 /* Must release the lock here because the changeFramebuffer will
2459 * also obtain it. */
2460 alock.release();
2461
2462 /* send request to the EMT thread */
2463 int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY,
2464 (PFNRT)changeFramebuffer, 3, this, aFramebuffer, aScreenId);
2465
2466 ComAssertRCRet(vrc, E_FAIL);
2467 alock.acquire();
2468
2469#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2470 BOOL fIs3DEnabled;
2471 HRESULT hr = mParent->machine()->COMGETTER(Accelerate3DEnabled)(&fIs3DEnabled);
2472 ComAssertComRC(hr);
2473 if (fIs3DEnabled)
2474 {
2475 VBOXCRCMDCTL_HGCM data;
2476 RT_ZERO(data);
2477 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
2478 data.Hdr.u32Function = SHCRGL_HOST_FN_SCREEN_CHANGED;
2479
2480 data.aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
2481 data.aParms[0].u.uint32 = aScreenId;
2482
2483 alock.release();
2484
2485 vrc = crCtlSubmitSync(&data.Hdr, sizeof(data));
2486 AssertRC(vrc);
2487
2488 alock.acquire();
2489 }
2490#endif /* #if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL) */
2491 }
2492 else
2493 {
2494 /* No VM is created (VM is powered off), do a direct call */
2495 int vrc = changeFramebuffer(this, aFramebuffer, aScreenId);
2496 ComAssertRCRet(vrc, E_FAIL);
2497 }
2498
2499 return S_OK;
2500}
2501
2502STDMETHODIMP Display::AttachFramebuffer(ULONG aScreenId,
2503 IFramebuffer *aFramebuffer)
2504{
2505 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2506
2507 CheckComArgPointerValid(aFramebuffer);
2508
2509 AutoCaller autoCaller(this);
2510 if (FAILED(autoCaller.rc()))
2511 return autoCaller.rc();
2512
2513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2514
2515 if (aScreenId >= mcMonitors)
2516 return setError(E_INVALIDARG, tr("AttachFramebuffer: Invalid screen %d (total %d)"),
2517 aScreenId, mcMonitors);
2518
2519 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2520 if (!pFBInfo->pFramebuffer.isNull())
2521 return setError(E_FAIL, tr("AttachFramebuffer: Framebuffer already attached to %d"),
2522 aScreenId);
2523
2524 pFBInfo->pFramebuffer = aFramebuffer;
2525
2526 /* The driver might not have been constructed yet */
2527 if (mpDrv)
2528 {
2529 /* Setup the new framebuffer. */
2530
2531#if defined(VBOX_WITH_CROGL)
2532 /* Release the lock, because SHCRGL_HOST_FN_SCREEN_CHANGED will read current framebuffer */
2533 /* @todo investigate */
2534 {
2535 BOOL is3denabled;
2536 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2537
2538 if (is3denabled)
2539 {
2540 alock.release();
2541 }
2542 }
2543#endif
2544
2545 /* @todo generic code for all monitors. */
2546 if (pFBInfo->fVBVAEnabled && pFBInfo->pu8FramebufferVRAM)
2547 {
2548 /* This display in VBVA mode. Resize it to the last guest resolution,
2549 * if it has been reported.
2550 */
2551 handleDisplayResize(aScreenId, pFBInfo->u16BitsPerPixel,
2552 pFBInfo->pu8FramebufferVRAM,
2553 pFBInfo->u32LineSize,
2554 pFBInfo->w,
2555 pFBInfo->h,
2556 pFBInfo->flags);
2557 }
2558 else if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2559 {
2560 /* VGA device mode, only for the primary screen. */
2561 handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, mLastBitsPerPixel,
2562 mLastAddress,
2563 mLastBytesPerLine,
2564 mLastWidth,
2565 mLastHeight,
2566 mLastFlags);
2567 }
2568
2569 alock.release();
2570
2571 Console::SafeVMPtrQuiet ptrVM(mParent);
2572 if (ptrVM.isOk())
2573 {
2574 VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::InvalidateAndUpdateEMT,
2575 3, this, aScreenId, false);
2576 }
2577 }
2578
2579 LogRelFlowFunc(("Attached to %d\n", aScreenId));
2580 return S_OK;
2581}
2582
2583STDMETHODIMP Display::DetachFramebuffer(ULONG aScreenId)
2584{
2585 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2586
2587 AutoCaller autoCaller(this);
2588 if (FAILED(autoCaller.rc()))
2589 return autoCaller.rc();
2590
2591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 if (aScreenId >= mcMonitors)
2594 return setError(E_INVALIDARG, tr("DetachFramebuffer: Invalid screen %d (total %d)"),
2595 aScreenId, mcMonitors);
2596
2597 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2598
2599 pFBInfo->pFramebuffer.setNull();
2600
2601 return S_OK;
2602}
2603
2604STDMETHODIMP Display::QueryFramebuffer(ULONG aScreenId,
2605 IFramebuffer **aFramebuffer)
2606{
2607 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2608
2609 CheckComArgOutPointerValid(aFramebuffer);
2610
2611 AutoCaller autoCaller(this);
2612 if (FAILED(autoCaller.rc()))
2613 return autoCaller.rc();
2614
2615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2616
2617 if (aScreenId >= mcMonitors)
2618 return setError(E_INVALIDARG, tr("QueryFramebuffer: Invalid screen %d (total %d)"),
2619 aScreenId, mcMonitors);
2620
2621 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2622
2623 *aFramebuffer = pFBInfo->pFramebuffer;
2624 if (!pFBInfo->pFramebuffer.isNull())
2625 pFBInfo->pFramebuffer->AddRef();
2626
2627 return S_OK;
2628}
2629
2630STDMETHODIMP Display::SetVideoModeHint(ULONG aDisplay, BOOL aEnabled,
2631 BOOL aChangeOrigin, LONG aOriginX, LONG aOriginY,
2632 ULONG aWidth, ULONG aHeight, ULONG aBitsPerPixel)
2633{
2634 AutoCaller autoCaller(this);
2635 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2636
2637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2638
2639 CHECK_CONSOLE_DRV(mpDrv);
2640
2641 /*
2642 * Do some rough checks for valid input.
2643 */
2644 ULONG width = aWidth;
2645 if (!width)
2646 width = mpDrv->IConnector.cx;
2647 ULONG height = aHeight;
2648 if (!height)
2649 height = mpDrv->IConnector.cy;
2650 ULONG bpp = aBitsPerPixel;
2651 if (!bpp)
2652 {
2653 uint32_t cBits = 0;
2654 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
2655 AssertRC(rc);
2656 bpp = cBits;
2657 }
2658 ULONG cMonitors;
2659 mParent->machine()->COMGETTER(MonitorCount)(&cMonitors);
2660 if (cMonitors == 0 && aDisplay > 0)
2661 return E_INVALIDARG;
2662 if (aDisplay >= cMonitors)
2663 return E_INVALIDARG;
2664
2665 /*
2666 * sunlover 20070614: It is up to the guest to decide whether the hint is
2667 * valid. Therefore don't do any VRAM sanity checks here!
2668 */
2669
2670 /* Have to release the lock because the pfnRequestDisplayChange
2671 * will call EMT. */
2672 alock.release();
2673
2674 VMMDev *pVMMDev = mParent->getVMMDev();
2675 if (pVMMDev)
2676 {
2677 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
2678 if (pVMMDevPort)
2679 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aBitsPerPixel,
2680 aDisplay, aOriginX, aOriginY,
2681 RT_BOOL(aEnabled), RT_BOOL(aChangeOrigin));
2682 }
2683 return S_OK;
2684}
2685
2686STDMETHODIMP Display::SetSeamlessMode (BOOL enabled)
2687{
2688 AutoCaller autoCaller(this);
2689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2690
2691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2692
2693 /* Have to release the lock because the pfnRequestSeamlessChange will call EMT. */
2694 alock.release();
2695
2696 VMMDev *pVMMDev = mParent->getVMMDev();
2697 if (pVMMDev)
2698 {
2699 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
2700 if (pVMMDevPort)
2701 pVMMDevPort->pfnRequestSeamlessChange(pVMMDevPort, !!enabled);
2702 }
2703
2704#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2705 if (!enabled)
2706 {
2707 BOOL is3denabled = FALSE;
2708
2709 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2710
2711 VMMDev *vmmDev = mParent->getVMMDev();
2712 if (is3denabled && vmmDev)
2713 {
2714 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM*)RTMemAlloc(sizeof (VBOXCRCMDCTL_HGCM));
2715 if (!pData)
2716 {
2717 AssertMsgFailed(("RTMemAlloc failed\n"));
2718 return VERR_NO_MEMORY;
2719 }
2720
2721 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
2722 pData->Hdr.u32Function = SHCRGL_HOST_FN_SET_VISIBLE_REGION;
2723
2724 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
2725 pData->aParms[0].u.pointer.addr = NULL;
2726 pData->aParms[0].u.pointer.size = 0; /* <- means null rects, NULL pRects address and 0 rects means "disable" */
2727
2728 int rc = crCtlSubmit(&pData->Hdr, sizeof (*pData), displayCrCmdFree, pData);
2729 if (!RT_SUCCESS(rc))
2730 {
2731 AssertMsgFailed(("crCtlSubmit failed rc %d\n", rc));
2732 RTMemFree(pData);
2733 }
2734 }
2735 }
2736#endif
2737 return S_OK;
2738}
2739
2740#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2741BOOL Display::displayCheckTakeScreenshotCrOgl(Display *pDisplay, ULONG aScreenId, uint8_t *pu8Data,
2742 uint32_t u32Width, uint32_t u32Height)
2743{
2744 BOOL is3denabled;
2745 pDisplay->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2746 if (is3denabled && pDisplay->mCrOglCallbacks.pfnHasData())
2747 {
2748 VMMDev *pVMMDev = pDisplay->mParent->getVMMDev();
2749 if (pVMMDev)
2750 {
2751 CRVBOXHGCMTAKESCREENSHOT *pScreenshot = (CRVBOXHGCMTAKESCREENSHOT*)RTMemAlloc(sizeof (*pScreenshot));
2752 if (pScreenshot)
2753 {
2754 /* screen id or CRSCREEN_ALL to specify all enabled */
2755 pScreenshot->u32Screen = aScreenId;
2756 pScreenshot->u32Width = u32Width;
2757 pScreenshot->u32Height = u32Height;
2758 pScreenshot->u32Pitch = u32Width * 4;
2759 pScreenshot->pvBuffer = pu8Data;
2760 pScreenshot->pvContext = NULL;
2761 pScreenshot->pfnScreenshotBegin = NULL;
2762 pScreenshot->pfnScreenshotPerform = NULL;
2763 pScreenshot->pfnScreenshotEnd = NULL;
2764
2765 VBOXCRCMDCTL_HGCM data;
2766 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
2767 data.Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
2768
2769 data.aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
2770 data.aParms[0].u.pointer.addr = pScreenshot;
2771 data.aParms[0].u.pointer.size = sizeof (*pScreenshot);
2772
2773 int rc = pDisplay->crCtlSubmitSync(&data.Hdr, sizeof (data));
2774
2775 RTMemFree(pScreenshot);
2776
2777 if (RT_SUCCESS(rc))
2778 return TRUE;
2779 else
2780 {
2781 AssertMsgFailed(("failed to get screenshot data from crOgl %d\n", rc));
2782 /* fall back to the non-3d mechanism */
2783 }
2784 }
2785 }
2786 }
2787 return FALSE;
2788}
2789#endif
2790
2791int Display::displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_t **ppu8Data, size_t *pcbData,
2792 uint32_t *pu32Width, uint32_t *pu32Height)
2793{
2794 int rc;
2795 pDisplay->vbvaLock();
2796 if ( aScreenId == VBOX_VIDEO_PRIMARY_SCREEN
2797 && pDisplay->maFramebuffers[aScreenId].fVBVAEnabled == false) /* A non-VBVA mode. */
2798 {
2799 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppu8Data, pcbData, pu32Width, pu32Height);
2800 }
2801 else if (aScreenId < pDisplay->mcMonitors)
2802 {
2803 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2804
2805 uint32_t width = pFBInfo->w;
2806 uint32_t height = pFBInfo->h;
2807
2808 /* Allocate 32 bit per pixel bitmap. */
2809 size_t cbRequired = width * 4 * height;
2810
2811 if (cbRequired)
2812 {
2813 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbRequired);
2814
2815 if (pu8Data == NULL)
2816 {
2817 rc = VERR_NO_MEMORY;
2818 }
2819 else
2820 {
2821 /* Copy guest VRAM to the allocated 32bpp buffer. */
2822 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
2823 int32_t xSrc = 0;
2824 int32_t ySrc = 0;
2825 uint32_t u32SrcWidth = width;
2826 uint32_t u32SrcHeight = height;
2827 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
2828 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2829
2830 uint8_t *pu8Dst = pu8Data;
2831 int32_t xDst = 0;
2832 int32_t yDst = 0;
2833 uint32_t u32DstWidth = u32SrcWidth;
2834 uint32_t u32DstHeight = u32SrcHeight;
2835 uint32_t u32DstLineSize = u32DstWidth * 4;
2836 uint32_t u32DstBitsPerPixel = 32;
2837
2838 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2839 width, height,
2840 pu8Src,
2841 xSrc, ySrc,
2842 u32SrcWidth, u32SrcHeight,
2843 u32SrcLineSize, u32SrcBitsPerPixel,
2844 pu8Dst,
2845 xDst, yDst,
2846 u32DstWidth, u32DstHeight,
2847 u32DstLineSize, u32DstBitsPerPixel);
2848 if (RT_SUCCESS(rc))
2849 {
2850 *ppu8Data = pu8Data;
2851 *pcbData = cbRequired;
2852 *pu32Width = width;
2853 *pu32Height = height;
2854 }
2855 else
2856 {
2857 RTMemFree(pu8Data);
2858
2859 /* CopyRect can fail if VBVA was paused in VGA device, retry using the generic method. */
2860 if ( rc == VERR_INVALID_STATE
2861 && aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2862 {
2863 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort,
2864 ppu8Data, pcbData, pu32Width, pu32Height);
2865 }
2866 }
2867 }
2868 }
2869 else
2870 {
2871 /* No image. */
2872 *ppu8Data = NULL;
2873 *pcbData = 0;
2874 *pu32Width = 0;
2875 *pu32Height = 0;
2876 rc = VINF_SUCCESS;
2877 }
2878 }
2879 else
2880 {
2881 rc = VERR_INVALID_PARAMETER;
2882 }
2883 pDisplay->vbvaUnlock();
2884 return rc;
2885}
2886
2887static int displayTakeScreenshot(PUVM pUVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, ULONG aScreenId,
2888 BYTE *address, ULONG width, ULONG height)
2889{
2890 uint8_t *pu8Data = NULL;
2891 size_t cbData = 0;
2892 uint32_t cx = 0;
2893 uint32_t cy = 0;
2894 int vrc = VINF_SUCCESS;
2895
2896# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2897 if (Display::displayCheckTakeScreenshotCrOgl(pDisplay, aScreenId, (uint8_t*)address, width, height))
2898 return VINF_SUCCESS;
2899#endif
2900
2901 int cRetries = 5;
2902
2903 while (cRetries-- > 0)
2904 {
2905 /* Note! Not sure if the priority call is such a good idea here, but
2906 it would be nice to have an accurate screenshot for the bug
2907 report if the VM deadlocks. */
2908 vrc = VMR3ReqPriorityCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)Display::displayTakeScreenshotEMT, 6,
2909 pDisplay, aScreenId, &pu8Data, &cbData, &cx, &cy);
2910 if (vrc != VERR_TRY_AGAIN)
2911 {
2912 break;
2913 }
2914
2915 RTThreadSleep(10);
2916 }
2917
2918 if (RT_SUCCESS(vrc) && pu8Data)
2919 {
2920 if (cx == width && cy == height)
2921 {
2922 /* No scaling required. */
2923 memcpy(address, pu8Data, cbData);
2924 }
2925 else
2926 {
2927 /* Scale. */
2928 LogRelFlowFunc(("SCALE: %dx%d -> %dx%d\n", cx, cy, width, height));
2929
2930 uint8_t *dst = address;
2931 uint8_t *src = pu8Data;
2932 int dstW = width;
2933 int dstH = height;
2934 int srcW = cx;
2935 int srcH = cy;
2936 int iDeltaLine = cx * 4;
2937
2938 BitmapScale32(dst,
2939 dstW, dstH,
2940 src,
2941 iDeltaLine,
2942 srcW, srcH);
2943 }
2944
2945 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2946 {
2947 /* This can be called from any thread. */
2948 pDrv->pUpPort->pfnFreeScreenshot(pDrv->pUpPort, pu8Data);
2949 }
2950 else
2951 {
2952 RTMemFree(pu8Data);
2953 }
2954 }
2955
2956 return vrc;
2957}
2958
2959STDMETHODIMP Display::TakeScreenShot(ULONG aScreenId, BYTE *address, ULONG width, ULONG height)
2960{
2961 /// @todo (r=dmik) this function may take too long to complete if the VM
2962 // is doing something like saving state right now. Which, in case if it
2963 // is called on the GUI thread, will make it unresponsive. We should
2964 // check the machine state here (by enclosing the check and VMRequCall
2965 // within the Console lock to make it atomic).
2966
2967 LogRelFlowFunc(("address=%p, width=%d, height=%d\n",
2968 address, width, height));
2969
2970 CheckComArgNotNull(address);
2971 CheckComArgExpr(width, width != 0);
2972 CheckComArgExpr(height, height != 0);
2973
2974 /* Do not allow too large screenshots. This also filters out negative
2975 * values passed as either 'width' or 'height'.
2976 */
2977 CheckComArgExpr(width, width <= 32767);
2978 CheckComArgExpr(height, height <= 32767);
2979
2980 AutoCaller autoCaller(this);
2981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2982
2983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2984
2985 if (!mpDrv)
2986 return E_FAIL;
2987
2988 Console::SafeVMPtr ptrVM(mParent);
2989 if (!ptrVM.isOk())
2990 return ptrVM.rc();
2991
2992 HRESULT rc = S_OK;
2993
2994 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
2995
2996 /* Release lock because other thread (EMT) is called and it may initiate a resize
2997 * which also needs lock.
2998 *
2999 * This method does not need the lock anymore.
3000 */
3001 alock.release();
3002
3003 int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, address, width, height);
3004
3005 if (vrc == VERR_NOT_IMPLEMENTED)
3006 rc = setError(E_NOTIMPL,
3007 tr("This feature is not implemented"));
3008 else if (vrc == VERR_TRY_AGAIN)
3009 rc = setError(E_UNEXPECTED,
3010 tr("This feature is not available at this time"));
3011 else if (RT_FAILURE(vrc))
3012 rc = setError(VBOX_E_IPRT_ERROR,
3013 tr("Could not take a screenshot (%Rrc)"), vrc);
3014
3015 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3016 return rc;
3017}
3018
3019STDMETHODIMP Display::TakeScreenShotToArray(ULONG aScreenId, ULONG width, ULONG height,
3020 ComSafeArrayOut(BYTE, aScreenData))
3021{
3022 LogRelFlowFunc(("width=%d, height=%d\n", width, height));
3023
3024 CheckComArgOutSafeArrayPointerValid(aScreenData);
3025 CheckComArgExpr(width, width != 0);
3026 CheckComArgExpr(height, height != 0);
3027
3028 /* Do not allow too large screenshots. This also filters out negative
3029 * values passed as either 'width' or 'height'.
3030 */
3031 CheckComArgExpr(width, width <= 32767);
3032 CheckComArgExpr(height, height <= 32767);
3033
3034 AutoCaller autoCaller(this);
3035 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3036
3037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3038
3039 if (!mpDrv)
3040 return E_FAIL;
3041
3042 Console::SafeVMPtr ptrVM(mParent);
3043 if (!ptrVM.isOk())
3044 return ptrVM.rc();
3045
3046 HRESULT rc = S_OK;
3047
3048 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
3049
3050 /* Release lock because other thread (EMT) is called and it may initiate a resize
3051 * which also needs lock.
3052 *
3053 * This method does not need the lock anymore.
3054 */
3055 alock.release();
3056
3057 size_t cbData = width * 4 * height;
3058 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbData);
3059
3060 if (!pu8Data)
3061 return E_OUTOFMEMORY;
3062
3063 int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, pu8Data, width, height);
3064
3065 if (RT_SUCCESS(vrc))
3066 {
3067 /* Convert pixels to format expected by the API caller: [0] R, [1] G, [2] B, [3] A. */
3068 uint8_t *pu8 = pu8Data;
3069 unsigned cPixels = width * height;
3070 while (cPixels)
3071 {
3072 uint8_t u8 = pu8[0];
3073 pu8[0] = pu8[2];
3074 pu8[2] = u8;
3075 pu8[3] = 0xff;
3076 cPixels--;
3077 pu8 += 4;
3078 }
3079
3080 com::SafeArray<BYTE> screenData(cbData);
3081 screenData.initFrom(pu8Data, cbData);
3082 screenData.detachTo(ComSafeArrayOutArg(aScreenData));
3083 }
3084 else if (vrc == VERR_NOT_IMPLEMENTED)
3085 rc = setError(E_NOTIMPL,
3086 tr("This feature is not implemented"));
3087 else
3088 rc = setError(VBOX_E_IPRT_ERROR,
3089 tr("Could not take a screenshot (%Rrc)"), vrc);
3090
3091 RTMemFree(pu8Data);
3092
3093 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3094 return rc;
3095}
3096
3097STDMETHODIMP Display::TakeScreenShotPNGToArray(ULONG aScreenId, ULONG width, ULONG height,
3098 ComSafeArrayOut(BYTE, aScreenData))
3099{
3100 LogRelFlowFunc(("width=%d, height=%d\n", width, height));
3101
3102 CheckComArgOutSafeArrayPointerValid(aScreenData);
3103 CheckComArgExpr(width, width != 0);
3104 CheckComArgExpr(height, height != 0);
3105
3106 /* Do not allow too large screenshots. This also filters out negative
3107 * values passed as either 'width' or 'height'.
3108 */
3109 CheckComArgExpr(width, width <= 32767);
3110 CheckComArgExpr(height, height <= 32767);
3111
3112 AutoCaller autoCaller(this);
3113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3114
3115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 CHECK_CONSOLE_DRV(mpDrv);
3118
3119 Console::SafeVMPtr ptrVM(mParent);
3120 if (!ptrVM.isOk())
3121 return ptrVM.rc();
3122
3123 HRESULT rc = S_OK;
3124
3125 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
3126
3127 /* Release lock because other thread (EMT) is called and it may initiate a resize
3128 * which also needs lock.
3129 *
3130 * This method does not need the lock anymore.
3131 */
3132 alock.release();
3133
3134 size_t cbData = width * 4 * height;
3135 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbData);
3136
3137 if (!pu8Data)
3138 return E_OUTOFMEMORY;
3139
3140 int vrc = displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, pu8Data, width, height);
3141
3142 if (RT_SUCCESS(vrc))
3143 {
3144 uint8_t *pu8PNG = NULL;
3145 uint32_t cbPNG = 0;
3146 uint32_t cxPNG = 0;
3147 uint32_t cyPNG = 0;
3148
3149 vrc = DisplayMakePNG(pu8Data, width, height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
3150 if (RT_SUCCESS(vrc))
3151 {
3152 com::SafeArray<BYTE> screenData(cbPNG);
3153 screenData.initFrom(pu8PNG, cbPNG);
3154 if (pu8PNG)
3155 RTMemFree(pu8PNG);
3156
3157 screenData.detachTo(ComSafeArrayOutArg(aScreenData));
3158 }
3159 else
3160 {
3161 if (pu8PNG)
3162 RTMemFree(pu8PNG);
3163 rc = setError(VBOX_E_IPRT_ERROR,
3164 tr("Could not convert screenshot to PNG (%Rrc)"), vrc);
3165 }
3166 }
3167 else if (vrc == VERR_NOT_IMPLEMENTED)
3168 rc = setError(E_NOTIMPL,
3169 tr("This feature is not implemented"));
3170 else
3171 rc = setError(VBOX_E_IPRT_ERROR,
3172 tr("Could not take a screenshot (%Rrc)"), vrc);
3173
3174 RTMemFree(pu8Data);
3175
3176 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3177 return rc;
3178}
3179
3180int Display::VideoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens))
3181{
3182#ifdef VBOX_WITH_VPX
3183 com::SafeArray<BOOL> Screens(ComSafeArrayInArg(aScreens));
3184 for (unsigned i = 0; i < Screens.size(); i++)
3185 maVideoRecEnabled[i] = RT_BOOL(Screens[i]);
3186 return VINF_SUCCESS;
3187#else
3188 return VERR_NOT_IMPLEMENTED;
3189#endif
3190}
3191
3192/**
3193 * Start video capturing. Does nothing if capturing is already active.
3194 */
3195int Display::VideoCaptureStart()
3196{
3197#ifdef VBOX_WITH_VPX
3198 if (VideoRecIsEnabled(mpVideoRecCtx))
3199 return VINF_SUCCESS;
3200
3201 int rc = VideoRecContextCreate(&mpVideoRecCtx, mcMonitors);
3202 if (RT_FAILURE(rc))
3203 {
3204 LogFlow(("Failed to create video recording context (%Rrc)!\n", rc));
3205 return rc;
3206 }
3207 ComPtr<IMachine> pMachine = mParent->machine();
3208 com::SafeArray<BOOL> screens;
3209 HRESULT hrc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens));
3210 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3211 for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
3212 maVideoRecEnabled[i] = i < screens.size() && screens[i];
3213 ULONG ulWidth;
3214 hrc = pMachine->COMGETTER(VideoCaptureWidth)(&ulWidth);
3215 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3216 ULONG ulHeight;
3217 hrc = pMachine->COMGETTER(VideoCaptureHeight)(&ulHeight);
3218 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3219 ULONG ulRate;
3220 hrc = pMachine->COMGETTER(VideoCaptureRate)(&ulRate);
3221 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3222 ULONG ulFPS;
3223 hrc = pMachine->COMGETTER(VideoCaptureFPS)(&ulFPS);
3224 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3225 BSTR strFile;
3226 hrc = pMachine->COMGETTER(VideoCaptureFile)(&strFile);
3227 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
3228 RTTIMESPEC ts;
3229 RTTimeNow(&ts);
3230 RTTIME time;
3231 RTTimeExplode(&time, &ts);
3232 for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
3233 {
3234 char *pszAbsPath = RTPathAbsDup(com::Utf8Str(strFile).c_str());
3235 char *pszSuff = RTPathSuffix(pszAbsPath);
3236 if (pszSuff)
3237 pszSuff = RTStrDup(pszSuff);
3238 RTPathStripSuffix(pszAbsPath);
3239 if (!pszAbsPath)
3240 rc = VERR_INVALID_PARAMETER;
3241 if (!pszSuff)
3242 pszSuff = RTStrDup(".webm");
3243 char *pszName = NULL;
3244 if (RT_SUCCESS(rc))
3245 {
3246 if (mcMonitors > 1)
3247 rc = RTStrAPrintf(&pszName, "%s-%u%s", pszAbsPath, uScreen+1, pszSuff);
3248 else
3249 rc = RTStrAPrintf(&pszName, "%s%s", pszAbsPath, pszSuff);
3250 }
3251 if (RT_SUCCESS(rc))
3252 {
3253 rc = VideoRecStrmInit(mpVideoRecCtx, uScreen,
3254 pszName, ulWidth, ulHeight, ulRate, ulFPS);
3255 if (rc == VERR_ALREADY_EXISTS)
3256 {
3257 RTStrFree(pszName);
3258 pszName = NULL;
3259
3260 if (mcMonitors > 1)
3261 rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s",
3262 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
3263 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
3264 uScreen+1, pszSuff);
3265 else
3266 rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s",
3267 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
3268 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
3269 pszSuff);
3270 if (RT_SUCCESS(rc))
3271 rc = VideoRecStrmInit(mpVideoRecCtx, uScreen,
3272 pszName, ulWidth, ulHeight, ulRate, ulFPS);
3273 }
3274 }
3275
3276 if (RT_SUCCESS(rc))
3277 LogRel(("WebM/VP8 video recording screen #%u with %ux%u @ %u kbps, %u fps to '%s' enabled.\n",
3278 uScreen, ulWidth, ulHeight, ulRate, ulFPS, pszName));
3279 else
3280 LogRel(("Failed to initialize video recording context #%u (%Rrc)!\n", uScreen, rc));
3281 RTStrFree(pszName);
3282 RTStrFree(pszSuff);
3283 RTStrFree(pszAbsPath);
3284 }
3285 return rc;
3286#else
3287 return VERR_NOT_IMPLEMENTED;
3288#endif
3289}
3290
3291/**
3292 * Stop video capturing. Does nothing if video capturing is not active.
3293 */
3294void Display::VideoCaptureStop()
3295{
3296#ifdef VBOX_WITH_VPX
3297 if (VideoRecIsEnabled(mpVideoRecCtx))
3298 LogRel(("WebM/VP8 video recording stopped.\n"));
3299 VideoRecContextClose(mpVideoRecCtx);
3300 mpVideoRecCtx = NULL;
3301#endif
3302}
3303
3304int Display::drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address,
3305 ULONG x, ULONG y, ULONG width, ULONG height)
3306{
3307 int rc = VINF_SUCCESS;
3308 pDisplay->vbvaLock();
3309
3310 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
3311
3312 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3313 {
3314 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3315 {
3316 rc = pDisplay->mpDrv->pUpPort->pfnDisplayBlt(pDisplay->mpDrv->pUpPort, address, x, y, width, height);
3317 }
3318 }
3319 else if (aScreenId < pDisplay->mcMonitors)
3320 {
3321 /* Copy the bitmap to the guest VRAM. */
3322 const uint8_t *pu8Src = address;
3323 int32_t xSrc = 0;
3324 int32_t ySrc = 0;
3325 uint32_t u32SrcWidth = width;
3326 uint32_t u32SrcHeight = height;
3327 uint32_t u32SrcLineSize = width * 4;
3328 uint32_t u32SrcBitsPerPixel = 32;
3329
3330 uint8_t *pu8Dst = pFBInfo->pu8FramebufferVRAM;
3331 int32_t xDst = x;
3332 int32_t yDst = y;
3333 uint32_t u32DstWidth = pFBInfo->w;
3334 uint32_t u32DstHeight = pFBInfo->h;
3335 uint32_t u32DstLineSize = pFBInfo->u32LineSize;
3336 uint32_t u32DstBitsPerPixel = pFBInfo->u16BitsPerPixel;
3337
3338 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
3339 width, height,
3340 pu8Src,
3341 xSrc, ySrc,
3342 u32SrcWidth, u32SrcHeight,
3343 u32SrcLineSize, u32SrcBitsPerPixel,
3344 pu8Dst,
3345 xDst, yDst,
3346 u32DstWidth, u32DstHeight,
3347 u32DstLineSize, u32DstBitsPerPixel);
3348 if (RT_SUCCESS(rc))
3349 {
3350 if (!pFBInfo->pSourceBitmap.isNull())
3351 {
3352 /* Update the changed screen area. When source bitmap uses VRAM directly, just notify
3353 * frontend to update. And for default format, render the guest VRAM to the source bitmap.
3354 */
3355 if ( pFBInfo->fDefaultFormat
3356 && !pFBInfo->fDisabled)
3357 {
3358 BYTE *pAddress = NULL;
3359 ULONG ulWidth = 0;
3360 ULONG ulHeight = 0;
3361 ULONG ulBitsPerPixel = 0;
3362 ULONG ulBytesPerLine = 0;
3363 ULONG ulPixelFormat = 0;
3364
3365 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
3366 &ulWidth,
3367 &ulHeight,
3368 &ulBitsPerPixel,
3369 &ulBytesPerLine,
3370 &ulPixelFormat);
3371 if (SUCCEEDED(hrc))
3372 {
3373 pu8Src = pFBInfo->pu8FramebufferVRAM;
3374 xSrc = x;
3375 ySrc = y;
3376 u32SrcWidth = pFBInfo->w;
3377 u32SrcHeight = pFBInfo->h;
3378 u32SrcLineSize = pFBInfo->u32LineSize;
3379 u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3380
3381 /* Default format is 32 bpp. */
3382 pu8Dst = pAddress;
3383 xDst = xSrc;
3384 yDst = ySrc;
3385 u32DstWidth = u32SrcWidth;
3386 u32DstHeight = u32SrcHeight;
3387 u32DstLineSize = u32DstWidth * 4;
3388 u32DstBitsPerPixel = 32;
3389
3390 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
3391 width, height,
3392 pu8Src,
3393 xSrc, ySrc,
3394 u32SrcWidth, u32SrcHeight,
3395 u32SrcLineSize, u32SrcBitsPerPixel,
3396 pu8Dst,
3397 xDst, yDst,
3398 u32DstWidth, u32DstHeight,
3399 u32DstLineSize, u32DstBitsPerPixel);
3400 }
3401 }
3402 }
3403
3404 pDisplay->handleDisplayUpdate(aScreenId, x, y, width, height);
3405 }
3406 }
3407 else
3408 {
3409 rc = VERR_INVALID_PARAMETER;
3410 }
3411
3412 if ( RT_SUCCESS(rc)
3413 && pDisplay->maFramebuffers[aScreenId].u32ResizeStatus == ResizeStatus_Void)
3414 pDisplay->mParent->consoleVRDPServer()->SendUpdateBitmap(aScreenId, x, y, width, height);
3415
3416 pDisplay->vbvaUnlock();
3417 return rc;
3418}
3419
3420STDMETHODIMP Display::DrawToScreen(ULONG aScreenId, BYTE *address,
3421 ULONG x, ULONG y, ULONG width, ULONG height)
3422{
3423 /// @todo (r=dmik) this function may take too long to complete if the VM
3424 // is doing something like saving state right now. Which, in case if it
3425 // is called on the GUI thread, will make it unresponsive. We should
3426 // check the machine state here (by enclosing the check and VMRequCall
3427 // within the Console lock to make it atomic).
3428
3429 LogRelFlowFunc(("address=%p, x=%d, y=%d, width=%d, height=%d\n",
3430 (void *)address, x, y, width, height));
3431
3432 CheckComArgNotNull(address);
3433 CheckComArgExpr(width, width != 0);
3434 CheckComArgExpr(height, height != 0);
3435
3436 AutoCaller autoCaller(this);
3437 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3438
3439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3440
3441 CHECK_CONSOLE_DRV(mpDrv);
3442
3443 Console::SafeVMPtr ptrVM(mParent);
3444 if (!ptrVM.isOk())
3445 return ptrVM.rc();
3446
3447 /* Release lock because the call scheduled on EMT may also try to take it. */
3448 alock.release();
3449
3450 /*
3451 * Again we're lazy and make the graphics device do all the
3452 * dirty conversion work.
3453 */
3454 int rcVBox = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::drawToScreenEMT, 7,
3455 this, aScreenId, address, x, y, width, height);
3456
3457 /*
3458 * If the function returns not supported, we'll have to do all the
3459 * work ourselves using the framebuffer.
3460 */
3461 HRESULT rc = S_OK;
3462 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
3463 {
3464 /** @todo implement generic fallback for screen blitting. */
3465 rc = E_NOTIMPL;
3466 }
3467 else if (RT_FAILURE(rcVBox))
3468 rc = setError(VBOX_E_IPRT_ERROR,
3469 tr("Could not draw to the screen (%Rrc)"), rcVBox);
3470//@todo
3471// else
3472// {
3473// /* All ok. Redraw the screen. */
3474// handleDisplayUpdate (x, y, width, height);
3475// }
3476
3477 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3478 return rc;
3479}
3480
3481void Display::InvalidateAndUpdateEMT(Display *pDisplay, unsigned uId, bool fUpdateAll)
3482{
3483 pDisplay->vbvaLock();
3484 unsigned uScreenId;
3485 for (uScreenId = (fUpdateAll ? 0 : uId); uScreenId < pDisplay->mcMonitors; uScreenId++)
3486 {
3487 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3488
3489 if ( !pFBInfo->fVBVAEnabled
3490 && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3491 {
3492 pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort);
3493 }
3494 else
3495 {
3496 if ( !pFBInfo->fDisabled
3497 && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3498 {
3499 /* Render complete VRAM screen to the framebuffer.
3500 * When framebuffer uses VRAM directly, just notify it to update.
3501 */
3502 if (pFBInfo->fDefaultFormat && !pFBInfo->pSourceBitmap.isNull())
3503 {
3504 BYTE *pAddress = NULL;
3505 ULONG ulWidth = 0;
3506 ULONG ulHeight = 0;
3507 ULONG ulBitsPerPixel = 0;
3508 ULONG ulBytesPerLine = 0;
3509 ULONG ulPixelFormat = 0;
3510
3511 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
3512 &ulWidth,
3513 &ulHeight,
3514 &ulBitsPerPixel,
3515 &ulBytesPerLine,
3516 &ulPixelFormat);
3517 if (SUCCEEDED(hrc))
3518 {
3519 uint32_t width = pFBInfo->w;
3520 uint32_t height = pFBInfo->h;
3521
3522 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
3523 int32_t xSrc = 0;
3524 int32_t ySrc = 0;
3525 uint32_t u32SrcWidth = pFBInfo->w;
3526 uint32_t u32SrcHeight = pFBInfo->h;
3527 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
3528 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3529
3530 /* Default format is 32 bpp. */
3531 uint8_t *pu8Dst = pAddress;
3532 int32_t xDst = xSrc;
3533 int32_t yDst = ySrc;
3534 uint32_t u32DstWidth = u32SrcWidth;
3535 uint32_t u32DstHeight = u32SrcHeight;
3536 uint32_t u32DstLineSize = u32DstWidth * 4;
3537 uint32_t u32DstBitsPerPixel = 32;
3538
3539 /* if uWidth != pFBInfo->w and uHeight != pFBInfo->h
3540 * implies resize of Framebuffer is in progress and
3541 * copyrect should not be called.
3542 */
3543 if (ulWidth == pFBInfo->w && ulHeight == pFBInfo->h)
3544 {
3545
3546 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
3547 width, height,
3548 pu8Src,
3549 xSrc, ySrc,
3550 u32SrcWidth, u32SrcHeight,
3551 u32SrcLineSize, u32SrcBitsPerPixel,
3552 pu8Dst,
3553 xDst, yDst,
3554 u32DstWidth, u32DstHeight,
3555 u32DstLineSize, u32DstBitsPerPixel);
3556 }
3557 }
3558 }
3559
3560 pDisplay->handleDisplayUpdate (uScreenId, 0, 0, pFBInfo->w, pFBInfo->h);
3561 }
3562 }
3563 if (!fUpdateAll)
3564 break;
3565 }
3566 pDisplay->vbvaUnlock();
3567}
3568
3569/**
3570 * Does a full invalidation of the VM display and instructs the VM
3571 * to update it immediately.
3572 *
3573 * @returns COM status code
3574 */
3575STDMETHODIMP Display::InvalidateAndUpdate()
3576{
3577 LogRelFlowFunc(("\n"));
3578
3579 AutoCaller autoCaller(this);
3580 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3581
3582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3583
3584 CHECK_CONSOLE_DRV(mpDrv);
3585
3586 Console::SafeVMPtr ptrVM(mParent);
3587 if (!ptrVM.isOk())
3588 return ptrVM.rc();
3589
3590 HRESULT rc = S_OK;
3591
3592 LogRelFlowFunc(("Sending DPYUPDATE request\n"));
3593
3594 /* Have to release the lock when calling EMT. */
3595 alock.release();
3596
3597 /* pdm.h says that this has to be called from the EMT thread */
3598 int rcVBox = VMR3ReqCallVoidWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::InvalidateAndUpdateEMT,
3599 3, this, 0, true);
3600 alock.acquire();
3601
3602 if (RT_FAILURE(rcVBox))
3603 rc = setError(VBOX_E_IPRT_ERROR,
3604 tr("Could not invalidate and update the screen (%Rrc)"), rcVBox);
3605
3606 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3607 return rc;
3608}
3609
3610STDMETHODIMP Display::CompleteVHWACommand(BYTE *pCommand)
3611{
3612#ifdef VBOX_WITH_VIDEOHWACCEL
3613 mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsync(mpDrv->pVBVACallbacks, (PVBOXVHWACMD)pCommand);
3614 return S_OK;
3615#else
3616 return E_NOTIMPL;
3617#endif
3618}
3619
3620STDMETHODIMP Display::ViewportChanged(ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
3621{
3622#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3623
3624 if (mcMonitors <= aScreenId)
3625 {
3626 AssertMsgFailed(("invalid screen id\n"));
3627 return E_INVALIDARG;
3628 }
3629
3630 BOOL is3denabled;
3631 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
3632
3633 if (is3denabled)
3634 {
3635 int rc = crViewportNotify(aScreenId, x, y, width, height);
3636 if (RT_FAILURE(rc))
3637 {
3638 DISPLAYFBINFO *pFb = &maFramebuffers[aScreenId];
3639 pFb->pendingViewportInfo.fPending = true;
3640 pFb->pendingViewportInfo.x = x;
3641 pFb->pendingViewportInfo.y = y;
3642 pFb->pendingViewportInfo.width = width;
3643 pFb->pendingViewportInfo.height = height;
3644 }
3645 }
3646#endif /* VBOX_WITH_CROGL && VBOX_WITH_HGCM */
3647 return S_OK;
3648}
3649
3650STDMETHODIMP Display::QuerySourceBitmap(ULONG aScreenId,
3651 IDisplaySourceBitmap **aDisplaySourceBitmap)
3652{
3653 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
3654
3655 AutoCaller autoCaller(this);
3656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3657
3658 Console::SafeVMPtr ptrVM(mParent);
3659 if (!ptrVM.isOk())
3660 return ptrVM.rc();
3661
3662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3663
3664 if (aScreenId >= mcMonitors)
3665 return setError(E_INVALIDARG, tr("QuerySourceBitmap: Invalid screen %d (total %d)"),
3666 aScreenId, mcMonitors);
3667
3668 HRESULT hr = querySourceBitmap(aScreenId, aDisplaySourceBitmap);
3669
3670 alock.release();
3671
3672 LogRelFlowFunc(("%Rhrc\n", hr));
3673 return hr;
3674}
3675
3676// private methods
3677/////////////////////////////////////////////////////////////////////////////
3678
3679HRESULT Display::querySourceBitmap(ULONG aScreenId,
3680 IDisplaySourceBitmap **ppDisplaySourceBitmap)
3681{
3682 HRESULT hr = S_OK;
3683
3684 if (!mfSourceBitmapEnabled)
3685 {
3686 *ppDisplaySourceBitmap = NULL;
3687 return E_FAIL;
3688 }
3689
3690 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
3691 if (pFBInfo->pSourceBitmap.isNull())
3692 {
3693 /* Create a new object. */
3694 ComObjPtr<DisplaySourceBitmap> obj;
3695 hr = obj.createObject();
3696 if (SUCCEEDED(hr))
3697 {
3698 hr = obj->init(this, aScreenId, pFBInfo);
3699 }
3700
3701 if (SUCCEEDED(hr))
3702 {
3703 pFBInfo->pSourceBitmap = obj;
3704
3705 /* Whether VRAM must be copied to the internal buffer. */
3706 pFBInfo->fDefaultFormat = !obj->usesVRAM();
3707
3708 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3709 {
3710 /* Start buffer updates. */
3711 BYTE *pAddress = NULL;
3712 ULONG ulWidth = 0;
3713 ULONG ulHeight = 0;
3714 ULONG ulBitsPerPixel = 0;
3715 ULONG ulBytesPerLine = 0;
3716 ULONG ulPixelFormat = 0;
3717
3718 obj->QueryBitmapInfo(&pAddress,
3719 &ulWidth,
3720 &ulHeight,
3721 &ulBitsPerPixel,
3722 &ulBytesPerLine,
3723 &ulPixelFormat);
3724
3725 mpDrv->IConnector.pu8Data = pAddress;
3726 mpDrv->IConnector.cbScanline = ulBytesPerLine;
3727 mpDrv->IConnector.cBits = ulBitsPerPixel;
3728 mpDrv->IConnector.cx = ulWidth;
3729 mpDrv->IConnector.cy = ulHeight;
3730
3731 if (pFBInfo->fDefaultFormat)
3732 mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, true);
3733 }
3734
3735 if (pFBInfo->fDefaultFormat)
3736 {
3737 /* @todo make sure that the bitmap contains the latest image? */
3738 }
3739 }
3740 }
3741
3742 if (SUCCEEDED(hr))
3743 {
3744 pFBInfo->pSourceBitmap->AddRef();
3745 *ppDisplaySourceBitmap = pFBInfo->pSourceBitmap;
3746 }
3747
3748 return hr;
3749}
3750
3751#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3752int Display::crViewportNotify(ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
3753{
3754 VMMDev *pVMMDev = mParent->getVMMDev();
3755 if (!pVMMDev)
3756 return VERR_INVALID_STATE;
3757
3758 size_t cbData = RT_UOFFSETOF(VBOXCRCMDCTL_HGCM, aParms[5]);
3759 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM*)alloca(cbData);
3760
3761 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3762 pData->Hdr.u32Function = SHCRGL_HOST_FN_VIEWPORT_CHANGED;
3763
3764 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
3765 pData->aParms[0].u.uint32 = aScreenId;
3766
3767 pData->aParms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
3768 pData->aParms[1].u.uint32 = x;
3769
3770 pData->aParms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
3771 pData->aParms[2].u.uint32 = y;
3772
3773 pData->aParms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
3774 pData->aParms[3].u.uint32 = width;
3775
3776 pData->aParms[4].type = VBOX_HGCM_SVC_PARM_32BIT;
3777 pData->aParms[4].u.uint32 = height;
3778
3779 return crCtlSubmitSyncIfHasDataForScreen(aScreenId, &pData->Hdr, cbData);
3780}
3781#endif
3782
3783#ifdef VBOX_WITH_CRHGSMI
3784void Display::setupCrHgsmiData(void)
3785{
3786 VMMDev *pVMMDev = mParent->getVMMDev();
3787 Assert(pVMMDev);
3788 int rc = RTCritSectRwEnterExcl(&mCrOglLock);
3789 AssertRC(rc);
3790
3791 if (pVMMDev)
3792 rc = pVMMDev->hgcmHostSvcHandleCreate("VBoxSharedCrOpenGL", &mhCrOglSvc);
3793 else
3794 rc = VERR_GENERAL_FAILURE;
3795
3796 if (RT_SUCCESS(rc))
3797 {
3798 Assert(mhCrOglSvc);
3799 /* setup command completion callback */
3800 VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB Completion;
3801 Completion.Hdr.enmType = VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB;
3802 Completion.Hdr.cbCmd = sizeof (Completion);
3803 Completion.hCompletion = mpDrv->pVBVACallbacks;
3804 Completion.pfnCompletion = mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync;
3805
3806 VBOXHGCMSVCPARM parm;
3807 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3808 parm.u.pointer.addr = &Completion;
3809 parm.u.pointer.size = 0;
3810
3811 rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_CRHGSMI_CTL, 1, &parm);
3812 if (RT_SUCCESS(rc))
3813 mCrOglCallbacks = Completion.MainInterface;
3814 else
3815 AssertMsgFailed(("VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION failed rc %d", rc));
3816 }
3817
3818 if (RT_FAILURE(rc))
3819 mhCrOglSvc = NULL;
3820
3821 RTCritSectRwLeaveExcl(&mCrOglLock);
3822}
3823
3824void Display::destructCrHgsmiData(void)
3825{
3826 int rc = RTCritSectRwEnterExcl(&mCrOglLock);
3827 AssertRC(rc);
3828 mhCrOglSvc = NULL;
3829 RTCritSectRwLeaveExcl(&mCrOglLock);
3830}
3831#endif
3832
3833/**
3834 * Changes the current frame buffer. Called on EMT to avoid both
3835 * race conditions and excessive locking.
3836 *
3837 * @note locks this object for writing
3838 * @thread EMT
3839 */
3840/* static */
3841DECLCALLBACK(int) Display::changeFramebuffer (Display *that, IFramebuffer *aFB,
3842 unsigned uScreenId)
3843{
3844 LogRelFlowFunc(("uScreenId = %d\n", uScreenId));
3845
3846 AssertReturn(that, VERR_INVALID_PARAMETER);
3847 AssertReturn(uScreenId < that->mcMonitors, VERR_INVALID_PARAMETER);
3848
3849 AutoCaller autoCaller(that);
3850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3851
3852 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
3853
3854 DISPLAYFBINFO *pDisplayFBInfo = &that->maFramebuffers[uScreenId];
3855 pDisplayFBInfo->pFramebuffer = aFB;
3856
3857 that->mParent->consoleVRDPServer()->SendResize ();
3858
3859 /* The driver might not have been constructed yet */
3860 if (that->mpDrv)
3861 {
3862 /* Setup the new framebuffer. */
3863 DISPLAYFBINFO *pFBInfo = &that->maFramebuffers[uScreenId];
3864
3865#if defined(VBOX_WITH_CROGL)
3866 /* Release the lock, because SHCRGL_HOST_FN_SCREEN_CHANGED will read current framebuffer */
3867 {
3868 BOOL is3denabled;
3869 that->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
3870
3871 if (is3denabled)
3872 {
3873 alock.release();
3874 }
3875 }
3876#endif
3877
3878 if (pFBInfo->fVBVAEnabled && pFBInfo->pu8FramebufferVRAM)
3879 {
3880 /* This display in VBVA mode. Resize it to the last guest resolution,
3881 * if it has been reported.
3882 */
3883 that->handleDisplayResize(uScreenId, pFBInfo->u16BitsPerPixel,
3884 pFBInfo->pu8FramebufferVRAM,
3885 pFBInfo->u32LineSize,
3886 pFBInfo->w,
3887 pFBInfo->h,
3888 pFBInfo->flags);
3889 }
3890 else if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3891 {
3892 /* VGA device mode, only for the primary screen. */
3893 that->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, that->mLastBitsPerPixel,
3894 that->mLastAddress,
3895 that->mLastBytesPerLine,
3896 that->mLastWidth,
3897 that->mLastHeight,
3898 that->mLastFlags);
3899 }
3900 }
3901
3902 LogRelFlowFunc(("leave\n"));
3903 return VINF_SUCCESS;
3904}
3905
3906/**
3907 * Handle display resize event issued by the VGA device for the primary screen.
3908 *
3909 * @see PDMIDISPLAYCONNECTOR::pfnResize
3910 */
3911DECLCALLBACK(int) Display::displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
3912 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
3913{
3914 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3915
3916 LogRelFlowFunc(("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
3917 bpp, pvVRAM, cbLine, cx, cy));
3918
3919 return pDrv->pDisplay->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy, VBVA_SCREEN_F_ACTIVE);
3920}
3921
3922/**
3923 * Handle display update.
3924 *
3925 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
3926 */
3927DECLCALLBACK(void) Display::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
3928 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3929{
3930 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3931
3932#ifdef DEBUG_sunlover
3933 LogFlowFunc(("mfVideoAccelEnabled = %d, %d,%d %dx%d\n",
3934 pDrv->pDisplay->mfVideoAccelEnabled, x, y, cx, cy));
3935#endif /* DEBUG_sunlover */
3936
3937 /* This call does update regardless of VBVA status.
3938 * But in VBVA mode this is called only as result of
3939 * pfnUpdateDisplayAll in the VGA device.
3940 */
3941
3942 pDrv->pDisplay->handleDisplayUpdate(VBOX_VIDEO_PRIMARY_SCREEN, x, y, cx, cy);
3943}
3944
3945/**
3946 * Periodic display refresh callback.
3947 *
3948 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
3949 * @thread EMT
3950 */
3951DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
3952{
3953 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3954
3955#ifdef DEBUG_sunlover
3956 STAM_PROFILE_START(&g_StatDisplayRefresh, a);
3957#endif /* DEBUG_sunlover */
3958
3959#ifdef DEBUG_sunlover_2
3960 LogFlowFunc(("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
3961 pDrv->pDisplay->mfVideoAccelEnabled));
3962#endif /* DEBUG_sunlover_2 */
3963
3964 Display *pDisplay = pDrv->pDisplay;
3965 bool fNoUpdate = false; /* Do not update the display if any of the framebuffers is being resized. */
3966 unsigned uScreenId;
3967
3968 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3969 {
3970 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3971
3972 /* Check the resize status. The status can be checked normally because
3973 * the status affects only the EMT.
3974 */
3975 uint32_t u32ResizeStatus = pFBInfo->u32ResizeStatus;
3976
3977 if (u32ResizeStatus == ResizeStatus_UpdateDisplayData)
3978 {
3979 LogRelFlowFunc(("ResizeStatus_UpdateDisplayData %d\n", uScreenId));
3980 fNoUpdate = true; /* Always set it here, because pfnUpdateDisplayAll can cause a new resize. */
3981 /* The framebuffer was resized and display data need to be updated. */
3982 pDisplay->handleResizeCompletedEMT(uScreenId, FALSE);
3983 if (pFBInfo->u32ResizeStatus != ResizeStatus_Void)
3984 {
3985 /* The resize status could be not Void here because a pending resize is issued. */
3986 continue;
3987 }
3988
3989 /* Repaint the display because VM continued to run during the framebuffer resize. */
3990 pDisplay->InvalidateAndUpdateEMT(pDisplay, uScreenId, false);
3991
3992 /* Continue with normal processing because the status here is ResizeStatus_Void. */
3993 }
3994 else if (u32ResizeStatus == ResizeStatus_InProgress)
3995 {
3996 /* The framebuffer is being resized. Do not call the VGA device back. Immediately return. */
3997 LogRelFlowFunc(("ResizeStatus_InProcess\n"));
3998 fNoUpdate = true;
3999 continue;
4000 }
4001 }
4002
4003 if (!fNoUpdate)
4004 {
4005 int rc = pDisplay->videoAccelRefreshProcess();
4006 if (rc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
4007 {
4008 if (rc == VWRN_INVALID_STATE)
4009 {
4010 /* No VBVA do a display update. */
4011 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN];
4012 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
4013 {
4014 pDisplay->vbvaLock();
4015 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
4016 pDisplay->vbvaUnlock();
4017 }
4018 }
4019
4020 /* Inform the VRDP server that the current display update sequence is
4021 * completed. At this moment the framebuffer memory contains a definite
4022 * image, that is synchronized with the orders already sent to VRDP client.
4023 * The server can now process redraw requests from clients or initial
4024 * fullscreen updates for new clients.
4025 */
4026 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
4027 {
4028 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
4029
4030 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
4031 {
4032 Assert (pDisplay->mParent && pDisplay->mParent->consoleVRDPServer());
4033 pDisplay->mParent->consoleVRDPServer()->SendUpdate (uScreenId, NULL, 0);
4034 }
4035 }
4036 }
4037 }
4038
4039#ifdef VBOX_WITH_VPX
4040 if (VideoRecIsEnabled(pDisplay->mpVideoRecCtx))
4041 {
4042 do {
4043# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4044 BOOL is3denabled;
4045 pDisplay->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
4046 if (is3denabled)
4047 {
4048 if (ASMAtomicCmpXchgU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_SUBMITTED, CRVREC_STATE_IDLE))
4049 {
4050 if (pDisplay->mCrOglCallbacks.pfnHasData())
4051 {
4052 /* submit */
4053 VBOXCRCMDCTL_HGCM *pData = &pDisplay->mCrOglScreenshotCtl;
4054
4055 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
4056 pData->Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
4057
4058 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
4059 pData->aParms[0].u.pointer.addr = &pDisplay->mCrOglScreenshotData;
4060 pData->aParms[0].u.pointer.size = sizeof (pDisplay->mCrOglScreenshotData);
4061 int rc = pDisplay->crCtlSubmit(&pData->Hdr, sizeof (*pData), displayCrCmdFree, pData);
4062 if (!RT_SUCCESS(rc))
4063 AssertMsgFailed(("crCtlSubmit failed rc %d\n", rc));
4064 }
4065
4066 /* no 3D data available, or error has occured,
4067 * go the straight way */
4068 ASMAtomicWriteU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_IDLE);
4069 }
4070 else
4071 {
4072 /* record request is still in progress, don't do anything */
4073 break;
4074 }
4075 }
4076# endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
4077
4078 uint64_t u64Now = RTTimeProgramMilliTS();
4079 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
4080 {
4081 if (!pDisplay->maVideoRecEnabled[uScreenId])
4082 continue;
4083
4084 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
4085
4086 if ( !pFBInfo->pFramebuffer.isNull()
4087 && !pFBInfo->fDisabled
4088 && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
4089 {
4090 int rc = VERR_NOT_SUPPORTED;
4091 if ( pFBInfo->fVBVAEnabled
4092 && pFBInfo->pu8FramebufferVRAM)
4093 {
4094 rc = VideoRecCopyToIntBuf(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
4095 FramebufferPixelFormat_FOURCC_RGB,
4096 pFBInfo->u16BitsPerPixel,
4097 pFBInfo->u32LineSize, pFBInfo->w, pFBInfo->h,
4098 pFBInfo->pu8FramebufferVRAM, u64Now);
4099 }
4100 else if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && pDrv->IConnector.pu8Data)
4101 {
4102 rc = VideoRecCopyToIntBuf(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
4103 FramebufferPixelFormat_FOURCC_RGB,
4104 pDrv->IConnector.cBits,
4105 pDrv->IConnector.cbScanline, pDrv->IConnector.cx,
4106 pDrv->IConnector.cy, pDrv->IConnector.pu8Data, u64Now);
4107 }
4108 if (rc == VINF_TRY_AGAIN)
4109 break;
4110 }
4111 }
4112 } while (0);
4113 }
4114#endif /* VBOX_WITH_VPX */
4115
4116#ifdef DEBUG_sunlover
4117 STAM_PROFILE_STOP(&g_StatDisplayRefresh, a);
4118#endif /* DEBUG_sunlover */
4119#ifdef DEBUG_sunlover_2
4120 LogFlowFunc(("leave\n"));
4121#endif /* DEBUG_sunlover_2 */
4122}
4123
4124/**
4125 * Reset notification
4126 *
4127 * @see PDMIDISPLAYCONNECTOR::pfnReset
4128 */
4129DECLCALLBACK(void) Display::displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
4130{
4131 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4132
4133 LogRelFlowFunc(("\n"));
4134
4135 /* Disable VBVA mode. */
4136 pDrv->pDisplay->VideoAccelEnable (false, NULL);
4137}
4138
4139/**
4140 * LFBModeChange notification
4141 *
4142 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
4143 */
4144DECLCALLBACK(void) Display::displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
4145{
4146 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4147
4148 LogRelFlowFunc(("fEnabled=%d\n", fEnabled));
4149
4150 NOREF(fEnabled);
4151
4152 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
4153 /* The LFBModeChange function is called under DevVGA lock. Postpone disabling VBVA, do it in the refresh timer. */
4154 ASMAtomicWriteU32(&pDrv->pDisplay->mfu32PendingVideoAccelDisable, true);
4155}
4156
4157/**
4158 * Adapter information change notification.
4159 *
4160 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
4161 */
4162DECLCALLBACK(void) Display::displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM,
4163 uint32_t u32VRAMSize)
4164{
4165 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4166
4167 if (pvVRAM == NULL)
4168 {
4169 unsigned i;
4170 for (i = 0; i < pDrv->pDisplay->mcMonitors; i++)
4171 {
4172 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[i];
4173
4174 pFBInfo->u32Offset = 0;
4175 pFBInfo->u32MaxFramebufferSize = 0;
4176 pFBInfo->u32InformationSize = 0;
4177 }
4178 }
4179#ifndef VBOX_WITH_HGSMI
4180 else
4181 {
4182 uint8_t *pu8 = (uint8_t *)pvVRAM;
4183 pu8 += u32VRAMSize - VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
4184
4185 // @todo
4186 uint8_t *pu8End = pu8 + VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
4187
4188 VBOXVIDEOINFOHDR *pHdr;
4189
4190 for (;;)
4191 {
4192 pHdr = (VBOXVIDEOINFOHDR *)pu8;
4193 pu8 += sizeof (VBOXVIDEOINFOHDR);
4194
4195 if (pu8 >= pu8End)
4196 {
4197 LogRel(("VBoxVideo: Guest adapter information overflow!!!\n"));
4198 break;
4199 }
4200
4201 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_DISPLAY)
4202 {
4203 if (pHdr->u16Length != sizeof (VBOXVIDEOINFODISPLAY))
4204 {
4205 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "DISPLAY", pHdr->u16Length));
4206 break;
4207 }
4208
4209 VBOXVIDEOINFODISPLAY *pDisplay = (VBOXVIDEOINFODISPLAY *)pu8;
4210
4211 if (pDisplay->u32Index >= pDrv->pDisplay->mcMonitors)
4212 {
4213 LogRel(("VBoxVideo: Guest adapter information invalid display index %d!!!\n", pDisplay->u32Index));
4214 break;
4215 }
4216
4217 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[pDisplay->u32Index];
4218
4219 pFBInfo->u32Offset = pDisplay->u32Offset;
4220 pFBInfo->u32MaxFramebufferSize = pDisplay->u32FramebufferSize;
4221 pFBInfo->u32InformationSize = pDisplay->u32InformationSize;
4222
4223 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_DISPLAY: %d: at 0x%08X, size 0x%08X, info 0x%08X\n", pDisplay->u32Index,
4224 pDisplay->u32Offset, pDisplay->u32FramebufferSize, pDisplay->u32InformationSize));
4225 }
4226 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_QUERY_CONF32)
4227 {
4228 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOQUERYCONF32))
4229 {
4230 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "CONF32", pHdr->u16Length));
4231 break;
4232 }
4233
4234 VBOXVIDEOINFOQUERYCONF32 *pConf32 = (VBOXVIDEOINFOQUERYCONF32 *)pu8;
4235
4236 switch (pConf32->u32Index)
4237 {
4238 case VBOX_VIDEO_QCI32_MONITOR_COUNT:
4239 {
4240 pConf32->u32Value = pDrv->pDisplay->mcMonitors;
4241 } break;
4242
4243 case VBOX_VIDEO_QCI32_OFFSCREEN_HEAP_SIZE:
4244 {
4245 /* @todo make configurable. */
4246 pConf32->u32Value = _1M;
4247 } break;
4248
4249 default:
4250 LogRel(("VBoxVideo: CONF32 %d not supported!!! Skipping.\n", pConf32->u32Index));
4251 }
4252 }
4253 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
4254 {
4255 if (pHdr->u16Length != 0)
4256 {
4257 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
4258 break;
4259 }
4260
4261 break;
4262 }
4263 else if (pHdr->u8Type != VBOX_VIDEO_INFO_TYPE_NV_HEAP)
4264 {
4265 /** @todo why is Additions/WINNT/Graphics/Miniport/VBoxVideo. cpp pushing this to us? */
4266 LogRel(("Guest adapter information contains unsupported type %d. The block has been skipped.\n", pHdr->u8Type));
4267 }
4268
4269 pu8 += pHdr->u16Length;
4270 }
4271 }
4272#endif /* !VBOX_WITH_HGSMI */
4273}
4274
4275/**
4276 * Display information change notification.
4277 *
4278 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
4279 */
4280DECLCALLBACK(void) Display::displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, unsigned uScreenId)
4281{
4282 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4283
4284 if (uScreenId >= pDrv->pDisplay->mcMonitors)
4285 {
4286 LogRel(("VBoxVideo: Guest display information invalid display index %d!!!\n", uScreenId));
4287 return;
4288 }
4289
4290 /* Get the display information structure. */
4291 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[uScreenId];
4292
4293 uint8_t *pu8 = (uint8_t *)pvVRAM;
4294 pu8 += pFBInfo->u32Offset + pFBInfo->u32MaxFramebufferSize;
4295
4296 // @todo
4297 uint8_t *pu8End = pu8 + pFBInfo->u32InformationSize;
4298
4299 VBOXVIDEOINFOHDR *pHdr;
4300
4301 for (;;)
4302 {
4303 pHdr = (VBOXVIDEOINFOHDR *)pu8;
4304 pu8 += sizeof (VBOXVIDEOINFOHDR);
4305
4306 if (pu8 >= pu8End)
4307 {
4308 LogRel(("VBoxVideo: Guest display information overflow!!!\n"));
4309 break;
4310 }
4311
4312 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_SCREEN)
4313 {
4314 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOSCREEN))
4315 {
4316 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "SCREEN", pHdr->u16Length));
4317 break;
4318 }
4319
4320 VBOXVIDEOINFOSCREEN *pScreen = (VBOXVIDEOINFOSCREEN *)pu8;
4321
4322 pFBInfo->xOrigin = pScreen->xOrigin;
4323 pFBInfo->yOrigin = pScreen->yOrigin;
4324
4325 pFBInfo->w = pScreen->u16Width;
4326 pFBInfo->h = pScreen->u16Height;
4327
4328 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_SCREEN: (%p) %d: at %d,%d, linesize 0x%X, size %dx%d, bpp %d, flags 0x%02X\n",
4329 pHdr, uScreenId, pScreen->xOrigin, pScreen->yOrigin, pScreen->u32LineSize, pScreen->u16Width,
4330 pScreen->u16Height, pScreen->bitsPerPixel, pScreen->u8Flags));
4331
4332 if (uScreenId != VBOX_VIDEO_PRIMARY_SCREEN)
4333 {
4334 /* Primary screen resize is eeeeeeeee by the VGA device. */
4335 if (pFBInfo->fDisabled)
4336 {
4337 pFBInfo->fDisabled = false;
4338 fireGuestMonitorChangedEvent(pDrv->pDisplay->mParent->getEventSource(),
4339 GuestMonitorChangedEventType_Enabled,
4340 uScreenId,
4341 pFBInfo->xOrigin, pFBInfo->yOrigin,
4342 pFBInfo->w, pFBInfo->h);
4343 }
4344
4345 pDrv->pDisplay->handleDisplayResize(uScreenId, pScreen->bitsPerPixel,
4346 (uint8_t *)pvVRAM + pFBInfo->u32Offset,
4347 pScreen->u32LineSize,
4348 pScreen->u16Width, pScreen->u16Height,
4349 VBVA_SCREEN_F_ACTIVE);
4350 }
4351 }
4352 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
4353 {
4354 if (pHdr->u16Length != 0)
4355 {
4356 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
4357 break;
4358 }
4359
4360 break;
4361 }
4362 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_HOST_EVENTS)
4363 {
4364 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOHOSTEVENTS))
4365 {
4366 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "HOST_EVENTS", pHdr->u16Length));
4367 break;
4368 }
4369
4370 VBOXVIDEOINFOHOSTEVENTS *pHostEvents = (VBOXVIDEOINFOHOSTEVENTS *)pu8;
4371
4372 pFBInfo->pHostEvents = pHostEvents;
4373
4374 LogFlow(("VBOX_VIDEO_INFO_TYPE_HOSTEVENTS: (%p)\n",
4375 pHostEvents));
4376 }
4377 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_LINK)
4378 {
4379 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOLINK))
4380 {
4381 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "LINK", pHdr->u16Length));
4382 break;
4383 }
4384
4385 VBOXVIDEOINFOLINK *pLink = (VBOXVIDEOINFOLINK *)pu8;
4386 pu8 += pLink->i32Offset;
4387 }
4388 else
4389 {
4390 LogRel(("Guest display information contains unsupported type %d\n", pHdr->u8Type));
4391 }
4392
4393 pu8 += pHdr->u16Length;
4394 }
4395}
4396
4397#ifdef VBOX_WITH_VIDEOHWACCEL
4398
4399#ifndef S_FALSE
4400# define S_FALSE ((HRESULT)1L)
4401#endif
4402
4403int Display::handleVHWACommandProcess(PVBOXVHWACMD pCommand)
4404{
4405 unsigned id = (unsigned)pCommand->iDisplay;
4406 int rc = VINF_SUCCESS;
4407 if (id >= mcMonitors)
4408 return VERR_INVALID_PARAMETER;
4409
4410 ComPtr<IFramebuffer> pFramebuffer;
4411 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
4412 pFramebuffer = maFramebuffers[id].pFramebuffer;
4413 arlock.release();
4414
4415 if (pFramebuffer == NULL)
4416 return VERR_NOT_IMPLEMENTED; /* Implementation is not available. */
4417
4418 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
4419 if (hr == S_FALSE)
4420 return VINF_SUCCESS;
4421 else if (SUCCEEDED(hr))
4422 return VINF_CALLBACK_RETURN;
4423 else if (hr == E_ACCESSDENIED)
4424 return VERR_INVALID_STATE; /* notify we can not handle request atm */
4425 else if (hr == E_NOTIMPL)
4426 return VERR_NOT_IMPLEMENTED;
4427 return VERR_GENERAL_FAILURE;
4428}
4429
4430DECLCALLBACK(int) Display::displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
4431{
4432 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4433
4434 return pDrv->pDisplay->handleVHWACommandProcess(pCommand);
4435}
4436#endif
4437
4438#ifdef VBOX_WITH_CRHGSMI
4439void Display::handleCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
4440{
4441 mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync(mpDrv->pVBVACallbacks,
4442 (PVBOXVDMACMD_CHROMIUM_CMD)pParam->u.pointer.addr, result);
4443}
4444
4445void Display::handleCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
4446{
4447 PVBOXVDMACMD_CHROMIUM_CTL pCtl = (PVBOXVDMACMD_CHROMIUM_CTL)pParam->u.pointer.addr;
4448 mpDrv->pVBVACallbacks->pfnCrHgsmiControlCompleteAsync(mpDrv->pVBVACallbacks, pCtl, result);
4449}
4450
4451void Display::handleCrHgsmiCommandProcess(PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
4452{
4453 int rc = VERR_NOT_SUPPORTED;
4454 VBOXHGCMSVCPARM parm;
4455 parm.type = VBOX_HGCM_SVC_PARM_PTR;
4456 parm.u.pointer.addr = pCmd;
4457 parm.u.pointer.size = cbCmd;
4458
4459 if (mhCrOglSvc)
4460 {
4461 VMMDev *pVMMDev = mParent->getVMMDev();
4462 if (pVMMDev)
4463 {
4464 /* no completion callback is specified with this call,
4465 * the CrOgl code will complete the CrHgsmi command once it processes it */
4466 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm, NULL, NULL);
4467 AssertRC(rc);
4468 if (RT_SUCCESS(rc))
4469 return;
4470 }
4471 else
4472 rc = VERR_INVALID_STATE;
4473 }
4474
4475 /* we are here because something went wrong with command processing, complete it */
4476 handleCrHgsmiCommandCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm);
4477}
4478
4479void Display::handleCrHgsmiControlProcess(PVBOXVDMACMD_CHROMIUM_CTL pCtl, uint32_t cbCtl)
4480{
4481 int rc = VERR_NOT_SUPPORTED;
4482 VBOXHGCMSVCPARM parm;
4483 parm.type = VBOX_HGCM_SVC_PARM_PTR;
4484 parm.u.pointer.addr = pCtl;
4485 parm.u.pointer.size = cbCtl;
4486
4487 if (mhCrOglSvc)
4488 {
4489 VMMDev *pVMMDev = mParent->getVMMDev();
4490 if (pVMMDev)
4491 {
4492 bool fCheckPendingViewport = (pCtl->enmType == VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP);
4493 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm,
4494 Display::displayCrHgsmiControlCompletion, this);
4495 AssertRC(rc);
4496 if (RT_SUCCESS(rc))
4497 {
4498 if (fCheckPendingViewport)
4499 {
4500 ULONG ul;
4501 for (ul = 0; ul < mcMonitors; ul++)
4502 {
4503 DISPLAYFBINFO *pFb = &maFramebuffers[ul];
4504 if (!pFb->pendingViewportInfo.fPending)
4505 continue;
4506
4507 rc = crViewportNotify(ul, pFb->pendingViewportInfo.x, pFb->pendingViewportInfo.y,
4508 pFb->pendingViewportInfo.width, pFb->pendingViewportInfo.height);
4509 if (RT_SUCCESS(rc))
4510 pFb->pendingViewportInfo.fPending = false;
4511 else
4512 {
4513 AssertMsgFailed(("crViewportNotify failed %d\n", rc));
4514 rc = VINF_SUCCESS;
4515 }
4516 }
4517 }
4518 return;
4519 }
4520 }
4521 else
4522 rc = VERR_INVALID_STATE;
4523 }
4524
4525 /* we are here because something went wrong with command processing, complete it */
4526 handleCrHgsmiControlCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm);
4527}
4528
4529DECLCALLBACK(void) Display::displayCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd,
4530 uint32_t cbCmd)
4531{
4532 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4533
4534 pDrv->pDisplay->handleCrHgsmiCommandProcess(pCmd, cbCmd);
4535}
4536
4537DECLCALLBACK(void) Display::displayCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd,
4538 uint32_t cbCmd)
4539{
4540 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4541
4542 pDrv->pDisplay->handleCrHgsmiControlProcess(pCmd, cbCmd);
4543}
4544
4545DECLCALLBACK(void) Display::displayCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
4546 void *pvContext)
4547{
4548 AssertMsgFailed(("not expected!"));
4549 Display *pDisplay = (Display *)pvContext;
4550 pDisplay->handleCrHgsmiCommandCompletion(result, u32Function, pParam);
4551}
4552
4553DECLCALLBACK(void) Display::displayCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
4554 void *pvContext)
4555{
4556 Display *pDisplay = (Display *)pvContext;
4557 pDisplay->handleCrHgsmiControlCompletion(result, u32Function, pParam);
4558
4559}
4560#endif
4561
4562#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4563DECLCALLBACK(void) Display::displayCrHgcmCtlSubmitCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
4564 void *pvContext)
4565{
4566 VBOXCRCMDCTL *pCmd = (VBOXCRCMDCTL*)pParam->u.pointer.addr;
4567 if (pCmd->u.pfnInternal)
4568 ((PFNCRCTLCOMPLETION)pCmd->u.pfnInternal)(pCmd, pParam->u.pointer.size, result, pvContext);
4569}
4570
4571int Display::handleCrHgcmCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
4572 PFNCRCTLCOMPLETION pfnCompletion,
4573 void *pvCompletion)
4574{
4575 VMMDev *pVMMDev = mParent ? mParent->getVMMDev() : NULL;
4576 if (!pVMMDev)
4577 {
4578 AssertMsgFailed(("no vmmdev\n"));
4579 return VERR_INVALID_STATE;
4580 }
4581
4582 Assert(mhCrOglSvc);
4583 VBOXHGCMSVCPARM parm;
4584 parm.type = VBOX_HGCM_SVC_PARM_PTR;
4585 parm.u.pointer.addr = pCmd;
4586 parm.u.pointer.size = cbCmd;
4587
4588 pCmd->u.pfnInternal = (void(*)())pfnCompletion;
4589 int rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CTL, &parm, displayCrHgcmCtlSubmitCompletion,
4590 pvCompletion);
4591 if (!RT_SUCCESS(rc))
4592 AssertMsgFailed(("hgcmHostFastCallAsync failed rc %d\n", rc));
4593
4594 return rc;
4595}
4596
4597DECLCALLBACK(int) Display::displayCrHgcmCtlSubmit(PPDMIDISPLAYCONNECTOR pInterface,
4598 struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
4599 PFNCRCTLCOMPLETION pfnCompletion,
4600 void *pvCompletion)
4601{
4602 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4603 Display *pThis = pDrv->pDisplay;
4604 return pThis->handleCrHgcmCtlSubmit(pCmd, cbCmd, pfnCompletion, pvCompletion);
4605}
4606
4607int Display::crCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, PFNCRCTLCOMPLETION pfnCompletion, void *pvCompletion)
4608{
4609 int rc = RTCritSectRwEnterShared(&mCrOglLock);
4610 if (RT_SUCCESS(rc))
4611 {
4612 if (mhCrOglSvc)
4613 rc = mpDrv->pVBVACallbacks->pfnCrCtlSubmit(mpDrv->pVBVACallbacks, pCmd, cbCmd, pfnCompletion, pvCompletion);
4614 else
4615 rc = VERR_NOT_SUPPORTED;
4616
4617 RTCritSectRwLeaveShared(&mCrOglLock);
4618 }
4619 return rc;
4620}
4621
4622int Display::crCtlSubmitSync(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
4623{
4624 int rc = RTCritSectRwEnterShared(&mCrOglLock);
4625 if (RT_SUCCESS(rc))
4626 {
4627 if (mhCrOglSvc)
4628 rc = mpDrv->pVBVACallbacks->pfnCrCtlSubmitSync(mpDrv->pVBVACallbacks, pCmd, cbCmd);
4629 else
4630 rc = VERR_NOT_SUPPORTED;
4631
4632 RTCritSectRwLeaveShared(&mCrOglLock);
4633 }
4634 return rc;
4635}
4636
4637int Display::crCtlSubmitAsyncCmdCopy(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
4638{
4639 VBOXCRCMDCTL* pCmdCopy = (VBOXCRCMDCTL*)RTMemAlloc(cbCmd);
4640 if (!pCmdCopy)
4641 {
4642 LogRel(("RTMemAlloc failed\n"));
4643 return VERR_NO_MEMORY;
4644 }
4645
4646 memcpy(pCmdCopy, pCmd, cbCmd);
4647
4648 int rc = crCtlSubmit(pCmdCopy, cbCmd, displayCrCmdFree, pCmdCopy);
4649 if (RT_FAILURE(rc))
4650 {
4651 LogRel(("crCtlSubmit failed %d\n", rc));
4652 RTMemFree(pCmdCopy);
4653 return rc;
4654 }
4655
4656 return VINF_SUCCESS;
4657}
4658
4659int Display::crCtlSubmitSyncIfHasDataForScreen(uint32_t u32ScreenID, struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
4660{
4661 int rc = RTCritSectRwEnterShared(&mCrOglLock);
4662 AssertRCReturn(rc, rc);
4663
4664 if (mCrOglCallbacks.pfnHasDataForScreen && mCrOglCallbacks.pfnHasDataForScreen(u32ScreenID))
4665 rc = crCtlSubmitSync(pCmd, cbCmd);
4666 else
4667 rc = crCtlSubmitAsyncCmdCopy(pCmd, cbCmd);
4668
4669 RTCritSectRwLeaveShared(&mCrOglLock);
4670
4671 return rc;
4672}
4673
4674bool Display::handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t u64TimeStamp)
4675{
4676# if VBOX_WITH_VPX
4677 return VideoRecIsReady(mpVideoRecCtx, uScreen, u64TimeStamp);
4678# else
4679 return false;
4680# endif
4681}
4682
4683void Display::handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t u64TimeStamp)
4684{
4685}
4686
4687void Display::handleCrVRecScreenshotPerform(uint32_t uScreen,
4688 uint32_t x, uint32_t y, uint32_t uPixelFormat,
4689 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
4690 uint32_t uGuestWidth, uint32_t uGuestHeight,
4691 uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
4692{
4693 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
4694# if VBOX_WITH_VPX
4695 int rc = VideoRecCopyToIntBuf(mpVideoRecCtx, uScreen, x, y,
4696 uPixelFormat,
4697 uBitsPerPixel, uBytesPerLine,
4698 uGuestWidth, uGuestHeight,
4699 pu8BufferAddress, u64TimeStamp);
4700 Assert(rc == VINF_SUCCESS /* || rc == VERR_TRY_AGAIN || rc == VINF_TRY_AGAIN*/);
4701# endif
4702}
4703
4704void Display::handleVRecCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
4705{
4706 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
4707 ASMAtomicWriteU32(&mfCrOglVideoRecState, CRVREC_STATE_IDLE);
4708}
4709
4710DECLCALLBACK(void) Display::displayCrVRecScreenshotPerform(void *pvCtx, uint32_t uScreen,
4711 uint32_t x, uint32_t y,
4712 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
4713 uint32_t uGuestWidth, uint32_t uGuestHeight,
4714 uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
4715{
4716 Display *pDisplay = (Display *)pvCtx;
4717 pDisplay->handleCrVRecScreenshotPerform(uScreen,
4718 x, y, FramebufferPixelFormat_FOURCC_RGB, uBitsPerPixel,
4719 uBytesPerLine, uGuestWidth, uGuestHeight,
4720 pu8BufferAddress, u64TimeStamp);
4721}
4722
4723DECLCALLBACK(bool) Display::displayCrVRecScreenshotBegin(void *pvCtx, uint32_t uScreen, uint64_t u64TimeStamp)
4724{
4725 Display *pDisplay = (Display *)pvCtx;
4726 return pDisplay->handleCrVRecScreenshotBegin(uScreen, u64TimeStamp);
4727}
4728
4729DECLCALLBACK(void) Display::displayCrVRecScreenshotEnd(void *pvCtx, uint32_t uScreen, uint64_t u64TimeStamp)
4730{
4731 Display *pDisplay = (Display *)pvCtx;
4732 pDisplay->handleCrVRecScreenshotEnd(uScreen, u64TimeStamp);
4733}
4734
4735DECLCALLBACK(void) Display::displayVRecCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
4736{
4737 Display *pDisplay = (Display *)pvContext;
4738 pDisplay->handleVRecCompletion(result, u32Function, pParam, pvContext);
4739}
4740
4741#endif
4742
4743
4744#ifdef VBOX_WITH_HGSMI
4745DECLCALLBACK(int) Display::displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, PVBVAHOSTFLAGS pHostFlags, bool fRenderThreadMode)
4746{
4747 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
4748
4749 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4750 Display *pThis = pDrv->pDisplay;
4751
4752 if (pThis->maFramebuffers[uScreenId].fVBVAEnabled && pThis->maFramebuffers[uScreenId].fRenderThreadMode != fRenderThreadMode)
4753 {
4754 LogRel(("enabling different vbva mode"));
4755#ifdef DEBUG_misha
4756 AssertMsgFailed(("enabling different vbva mode"));
4757#endif
4758 return VERR_INVALID_STATE;
4759 }
4760
4761 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
4762 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
4763 pThis->maFramebuffers[uScreenId].fRenderThreadMode = fRenderThreadMode;
4764 pThis->maFramebuffers[uScreenId].fVBVAForceResize = true;
4765
4766 vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
4767
4768 return VINF_SUCCESS;
4769}
4770
4771DECLCALLBACK(void) Display::displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
4772{
4773 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
4774
4775 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4776 Display *pThis = pDrv->pDisplay;
4777
4778 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4779
4780 bool fRenderThreadMode = pFBInfo->fRenderThreadMode;
4781
4782 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
4783 {
4784 /* Make sure that the primary screen is visible now.
4785 * The guest can't use VBVA anymore, so only only the VGA device output works.
4786 */
4787 if (pFBInfo->fDisabled)
4788 {
4789 pFBInfo->fDisabled = false;
4790 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
4791 GuestMonitorChangedEventType_Enabled,
4792 uScreenId,
4793 pFBInfo->xOrigin, pFBInfo->yOrigin,
4794 pFBInfo->w, pFBInfo->h);
4795 }
4796 }
4797
4798 pFBInfo->fVBVAEnabled = false;
4799 pFBInfo->fVBVAForceResize = false;
4800 pFBInfo->fRenderThreadMode = false;
4801
4802 vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, pFBInfo);
4803
4804 pFBInfo->pVBVAHostFlags = NULL;
4805
4806 if (!fRenderThreadMode && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
4807 {
4808 /* Force full screen update, because VGA device must take control, do resize, etc. */
4809 pThis->mpDrv->pUpPort->pfnUpdateDisplayAll(pThis->mpDrv->pUpPort);
4810 }
4811}
4812
4813DECLCALLBACK(void) Display::displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
4814{
4815 LogFlowFunc(("uScreenId %d\n", uScreenId));
4816
4817 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4818 Display *pThis = pDrv->pDisplay;
4819 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4820
4821 if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
4822 {
4823 vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers,
4824 pThis->mcMonitors);
4825 ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
4826 }
4827
4828 if (RT_LIKELY(pFBInfo->u32ResizeStatus == ResizeStatus_Void))
4829 {
4830 if (RT_UNLIKELY(pFBInfo->cVBVASkipUpdate != 0))
4831 {
4832 /* Some updates were skipped. Note: displayVBVAUpdate* callbacks are called
4833 * under display device lock, so thread safe.
4834 */
4835 pFBInfo->cVBVASkipUpdate = 0;
4836 pThis->handleDisplayUpdate(uScreenId, pFBInfo->vbvaSkippedRect.xLeft - pFBInfo->xOrigin,
4837 pFBInfo->vbvaSkippedRect.yTop - pFBInfo->yOrigin,
4838 pFBInfo->vbvaSkippedRect.xRight - pFBInfo->vbvaSkippedRect.xLeft,
4839 pFBInfo->vbvaSkippedRect.yBottom - pFBInfo->vbvaSkippedRect.yTop);
4840 }
4841 }
4842 else
4843 {
4844 /* The framebuffer is being resized. */
4845 pFBInfo->cVBVASkipUpdate++;
4846 }
4847}
4848
4849DECLCALLBACK(void) Display::displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
4850 const PVBVACMDHDR pCmd, size_t cbCmd)
4851{
4852 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d, @%d,%d %dx%d\n", uScreenId, pCmd, cbCmd, pCmd->x, pCmd->y, pCmd->w, pCmd->h));
4853
4854 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4855 Display *pThis = pDrv->pDisplay;
4856 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4857
4858 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
4859 {
4860 if (pFBInfo->fDefaultFormat)
4861 {
4862 /* Make sure that framebuffer contains the same image as the guest VRAM. */
4863 if ( uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
4864 && !pFBInfo->fDisabled)
4865 {
4866 pDrv->pUpPort->pfnUpdateDisplayRect (pDrv->pUpPort, pCmd->x, pCmd->y, pCmd->w, pCmd->h);
4867 }
4868 else if ( !pFBInfo->pSourceBitmap.isNull()
4869 && !pFBInfo->fDisabled)
4870 {
4871 /* Render VRAM content to the framebuffer. */
4872 BYTE *pAddress = NULL;
4873 ULONG ulWidth = 0;
4874 ULONG ulHeight = 0;
4875 ULONG ulBitsPerPixel = 0;
4876 ULONG ulBytesPerLine = 0;
4877 ULONG ulPixelFormat = 0;
4878
4879 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
4880 &ulWidth,
4881 &ulHeight,
4882 &ulBitsPerPixel,
4883 &ulBytesPerLine,
4884 &ulPixelFormat);
4885 if (SUCCEEDED(hrc))
4886 {
4887 uint32_t width = pCmd->w;
4888 uint32_t height = pCmd->h;
4889
4890 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
4891 int32_t xSrc = pCmd->x - pFBInfo->xOrigin;
4892 int32_t ySrc = pCmd->y - pFBInfo->yOrigin;
4893 uint32_t u32SrcWidth = pFBInfo->w;
4894 uint32_t u32SrcHeight = pFBInfo->h;
4895 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
4896 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
4897
4898 uint8_t *pu8Dst = pAddress;
4899 int32_t xDst = xSrc;
4900 int32_t yDst = ySrc;
4901 uint32_t u32DstWidth = u32SrcWidth;
4902 uint32_t u32DstHeight = u32SrcHeight;
4903 uint32_t u32DstLineSize = u32DstWidth * 4;
4904 uint32_t u32DstBitsPerPixel = 32;
4905
4906 pDrv->pUpPort->pfnCopyRect(pDrv->pUpPort,
4907 width, height,
4908 pu8Src,
4909 xSrc, ySrc,
4910 u32SrcWidth, u32SrcHeight,
4911 u32SrcLineSize, u32SrcBitsPerPixel,
4912 pu8Dst,
4913 xDst, yDst,
4914 u32DstWidth, u32DstHeight,
4915 u32DstLineSize, u32DstBitsPerPixel);
4916 }
4917 }
4918 }
4919
4920 VBVACMDHDR hdrSaved = *pCmd;
4921
4922 VBVACMDHDR *pHdrUnconst = (VBVACMDHDR *)pCmd;
4923
4924 pHdrUnconst->x -= (int16_t)pFBInfo->xOrigin;
4925 pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
4926
4927 /* @todo new SendUpdate entry which can get a separate cmd header or coords. */
4928 pThis->mParent->consoleVRDPServer()->SendUpdate (uScreenId, pCmd, (uint32_t)cbCmd);
4929
4930 *pHdrUnconst = hdrSaved;
4931 }
4932}
4933
4934DECLCALLBACK(void) Display::displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y,
4935 uint32_t cx, uint32_t cy)
4936{
4937 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
4938
4939 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4940 Display *pThis = pDrv->pDisplay;
4941 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4942
4943 /* @todo handleFramebufferUpdate (uScreenId,
4944 * x - pThis->maFramebuffers[uScreenId].xOrigin,
4945 * y - pThis->maFramebuffers[uScreenId].yOrigin,
4946 * cx, cy);
4947 */
4948 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
4949 {
4950 pThis->handleDisplayUpdate(uScreenId, x - pFBInfo->xOrigin, y - pFBInfo->yOrigin, cx, cy);
4951 }
4952 else
4953 {
4954 /* Save the updated rectangle. */
4955 int32_t xRight = x + cx;
4956 int32_t yBottom = y + cy;
4957
4958 if (pFBInfo->cVBVASkipUpdate == 1)
4959 {
4960 pFBInfo->vbvaSkippedRect.xLeft = x;
4961 pFBInfo->vbvaSkippedRect.yTop = y;
4962 pFBInfo->vbvaSkippedRect.xRight = xRight;
4963 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
4964 }
4965 else
4966 {
4967 if (pFBInfo->vbvaSkippedRect.xLeft > x)
4968 {
4969 pFBInfo->vbvaSkippedRect.xLeft = x;
4970 }
4971 if (pFBInfo->vbvaSkippedRect.yTop > y)
4972 {
4973 pFBInfo->vbvaSkippedRect.yTop = y;
4974 }
4975 if (pFBInfo->vbvaSkippedRect.xRight < xRight)
4976 {
4977 pFBInfo->vbvaSkippedRect.xRight = xRight;
4978 }
4979 if (pFBInfo->vbvaSkippedRect.yBottom < yBottom)
4980 {
4981 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
4982 }
4983 }
4984 }
4985}
4986
4987#ifdef DEBUG_sunlover
4988static void logVBVAResize(const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, const DISPLAYFBINFO *pFBInfo)
4989{
4990 LogRel(("displayVBVAResize: [%d] %s\n"
4991 " pView->u32ViewIndex %d\n"
4992 " pView->u32ViewOffset 0x%08X\n"
4993 " pView->u32ViewSize 0x%08X\n"
4994 " pView->u32MaxScreenSize 0x%08X\n"
4995 " pScreen->i32OriginX %d\n"
4996 " pScreen->i32OriginY %d\n"
4997 " pScreen->u32StartOffset 0x%08X\n"
4998 " pScreen->u32LineSize 0x%08X\n"
4999 " pScreen->u32Width %d\n"
5000 " pScreen->u32Height %d\n"
5001 " pScreen->u16BitsPerPixel %d\n"
5002 " pScreen->u16Flags 0x%04X\n"
5003 " pFBInfo->u32Offset 0x%08X\n"
5004 " pFBInfo->u32MaxFramebufferSize 0x%08X\n"
5005 " pFBInfo->u32InformationSize 0x%08X\n"
5006 " pFBInfo->fDisabled %d\n"
5007 " xOrigin, yOrigin, w, h: %d,%d %dx%d\n"
5008 " pFBInfo->u16BitsPerPixel %d\n"
5009 " pFBInfo->pu8FramebufferVRAM %p\n"
5010 " pFBInfo->u32LineSize 0x%08X\n"
5011 " pFBInfo->flags 0x%04X\n"
5012 " pFBInfo->pHostEvents %p\n"
5013 " pFBInfo->u32ResizeStatus %d\n"
5014 " pFBInfo->fDefaultFormat %d\n"
5015 " dirtyRect %d-%d %d-%d\n"
5016 " pFBInfo->pendingResize.fPending %d\n"
5017 " pFBInfo->pendingResize.pixelFormat %d\n"
5018 " pFBInfo->pendingResize.pvVRAM %p\n"
5019 " pFBInfo->pendingResize.bpp %d\n"
5020 " pFBInfo->pendingResize.cbLine 0x%08X\n"
5021 " pFBInfo->pendingResize.w,h %dx%d\n"
5022 " pFBInfo->pendingResize.flags 0x%04X\n"
5023 " pFBInfo->fVBVAEnabled %d\n"
5024 " pFBInfo->fVBVAForceResize %d\n"
5025 " pFBInfo->cVBVASkipUpdate %d\n"
5026 " pFBInfo->vbvaSkippedRect %d-%d %d-%d\n"
5027 " pFBInfo->pVBVAHostFlags %p\n"
5028 "",
5029 pScreen->u32ViewIndex,
5030 (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)? "DISABLED": "ENABLED",
5031 pView->u32ViewIndex,
5032 pView->u32ViewOffset,
5033 pView->u32ViewSize,
5034 pView->u32MaxScreenSize,
5035 pScreen->i32OriginX,
5036 pScreen->i32OriginY,
5037 pScreen->u32StartOffset,
5038 pScreen->u32LineSize,
5039 pScreen->u32Width,
5040 pScreen->u32Height,
5041 pScreen->u16BitsPerPixel,
5042 pScreen->u16Flags,
5043 pFBInfo->u32Offset,
5044 pFBInfo->u32MaxFramebufferSize,
5045 pFBInfo->u32InformationSize,
5046 pFBInfo->fDisabled,
5047 pFBInfo->xOrigin,
5048 pFBInfo->yOrigin,
5049 pFBInfo->w,
5050 pFBInfo->h,
5051 pFBInfo->u16BitsPerPixel,
5052 pFBInfo->pu8FramebufferVRAM,
5053 pFBInfo->u32LineSize,
5054 pFBInfo->flags,
5055 pFBInfo->pHostEvents,
5056 pFBInfo->u32ResizeStatus,
5057 pFBInfo->fDefaultFormat,
5058 pFBInfo->dirtyRect.xLeft,
5059 pFBInfo->dirtyRect.xRight,
5060 pFBInfo->dirtyRect.yTop,
5061 pFBInfo->dirtyRect.yBottom,
5062 pFBInfo->pendingResize.fPending,
5063 pFBInfo->pendingResize.pixelFormat,
5064 pFBInfo->pendingResize.pvVRAM,
5065 pFBInfo->pendingResize.bpp,
5066 pFBInfo->pendingResize.cbLine,
5067 pFBInfo->pendingResize.w,
5068 pFBInfo->pendingResize.h,
5069 pFBInfo->pendingResize.flags,
5070 pFBInfo->fVBVAEnabled,
5071 pFBInfo->fVBVAForceResize,
5072 pFBInfo->cVBVASkipUpdate,
5073 pFBInfo->vbvaSkippedRect.xLeft,
5074 pFBInfo->vbvaSkippedRect.yTop,
5075 pFBInfo->vbvaSkippedRect.xRight,
5076 pFBInfo->vbvaSkippedRect.yBottom,
5077 pFBInfo->pVBVAHostFlags
5078 ));
5079}
5080#endif /* DEBUG_sunlover */
5081
5082DECLCALLBACK(int) Display::displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, const PVBVAINFOVIEW pView,
5083 const PVBVAINFOSCREEN pScreen, void *pvVRAM)
5084{
5085 LogRelFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
5086
5087 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
5088 Display *pThis = pDrv->pDisplay;
5089
5090 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[pScreen->u32ViewIndex];
5091
5092 if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
5093 {
5094 pThis->notifyCroglResize(pView, pScreen, pvVRAM);
5095
5096 pFBInfo->fDisabled = true;
5097 pFBInfo->flags = pScreen->u16Flags;
5098
5099 /* Ask the framebuffer to resize using a default format. The framebuffer will be black.
5100 * So if the frontend does not support GuestMonitorChangedEventType_Disabled event,
5101 * the VM window will be black. */
5102 uint32_t u32Width = pFBInfo->w ? pFBInfo->w : 640;
5103 uint32_t u32Height = pFBInfo->h ? pFBInfo->h : 480;
5104 pThis->handleDisplayResize(pScreen->u32ViewIndex, 0, (uint8_t *)NULL, 0,
5105 u32Width, u32Height, pScreen->u16Flags);
5106
5107 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
5108 GuestMonitorChangedEventType_Disabled,
5109 pScreen->u32ViewIndex,
5110 0, 0, 0, 0);
5111 return VINF_SUCCESS;
5112 }
5113
5114 /* If display was disabled or there is no framebuffer, a resize will be required,
5115 * because the framebuffer was/will be changed.
5116 */
5117 bool fResize = pFBInfo->fDisabled || pFBInfo->pFramebuffer.isNull();
5118
5119 if (pFBInfo->fVBVAForceResize)
5120 {
5121 /* VBVA was just enabled. Do the resize. */
5122 fResize = true;
5123 pFBInfo->fVBVAForceResize = false;
5124 }
5125
5126 /* Check if this is a real resize or a notification about the screen origin.
5127 * The guest uses this VBVAResize call for both.
5128 */
5129 fResize = fResize
5130 || pFBInfo->u16BitsPerPixel != pScreen->u16BitsPerPixel
5131 || pFBInfo->pu8FramebufferVRAM != (uint8_t *)pvVRAM + pScreen->u32StartOffset
5132 || pFBInfo->u32LineSize != pScreen->u32LineSize
5133 || pFBInfo->w != pScreen->u32Width
5134 || pFBInfo->h != pScreen->u32Height;
5135
5136 bool fNewOrigin = pFBInfo->xOrigin != pScreen->i32OriginX
5137 || pFBInfo->yOrigin != pScreen->i32OriginY;
5138
5139 if (fNewOrigin || fResize)
5140 pThis->notifyCroglResize(pView, pScreen, pvVRAM);
5141
5142 if (pFBInfo->fDisabled)
5143 {
5144 pFBInfo->fDisabled = false;
5145 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
5146 GuestMonitorChangedEventType_Enabled,
5147 pScreen->u32ViewIndex,
5148 pScreen->i32OriginX, pScreen->i32OriginY,
5149 pScreen->u32Width, pScreen->u32Height);
5150 /* Continue to update pFBInfo. */
5151 }
5152
5153 pFBInfo->u32Offset = pView->u32ViewOffset; /* Not used in HGSMI. */
5154 pFBInfo->u32MaxFramebufferSize = pView->u32MaxScreenSize; /* Not used in HGSMI. */
5155 pFBInfo->u32InformationSize = 0; /* Not used in HGSMI. */
5156
5157 pFBInfo->xOrigin = pScreen->i32OriginX;
5158 pFBInfo->yOrigin = pScreen->i32OriginY;
5159
5160 pFBInfo->w = pScreen->u32Width;
5161 pFBInfo->h = pScreen->u32Height;
5162
5163 pFBInfo->u16BitsPerPixel = pScreen->u16BitsPerPixel;
5164 pFBInfo->pu8FramebufferVRAM = (uint8_t *)pvVRAM + pScreen->u32StartOffset;
5165 pFBInfo->u32LineSize = pScreen->u32LineSize;
5166
5167 pFBInfo->flags = pScreen->u16Flags;
5168
5169 if (fNewOrigin)
5170 {
5171 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
5172 GuestMonitorChangedEventType_NewOrigin,
5173 pScreen->u32ViewIndex,
5174 pScreen->i32OriginX, pScreen->i32OriginY,
5175 0, 0);
5176 }
5177
5178 if (!fResize)
5179 {
5180 /* No parameters of the framebuffer have actually changed. */
5181 if (fNewOrigin)
5182 {
5183 /* VRDP server still need this notification. */
5184 LogRelFlowFunc(("Calling VRDP\n"));
5185 pThis->mParent->consoleVRDPServer()->SendResize();
5186 }
5187 return VINF_SUCCESS;
5188 }
5189
5190 /* Do a regular resize. */
5191 return pThis->handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
5192 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
5193 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height, pScreen->u16Flags);
5194}
5195
5196DECLCALLBACK(int) Display::displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
5197 uint32_t xHot, uint32_t yHot,
5198 uint32_t cx, uint32_t cy,
5199 const void *pvShape)
5200{
5201 LogFlowFunc(("\n"));
5202
5203 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
5204 Display *pThis = pDrv->pDisplay;
5205
5206 size_t cbShapeSize = 0;
5207
5208 if (pvShape)
5209 {
5210 cbShapeSize = (cx + 7) / 8 * cy; /* size of the AND mask */
5211 cbShapeSize = ((cbShapeSize + 3) & ~3) + cx * 4 * cy; /* + gap + size of the XOR mask */
5212 }
5213 com::SafeArray<BYTE> shapeData(cbShapeSize);
5214
5215 if (pvShape)
5216 ::memcpy(shapeData.raw(), pvShape, cbShapeSize);
5217
5218 /* Tell the console about it */
5219 pDrv->pDisplay->mParent->onMousePointerShapeChange(fVisible, fAlpha,
5220 xHot, yHot, cx, cy, ComSafeArrayAsInParam(shapeData));
5221
5222 return VINF_SUCCESS;
5223}
5224#endif /* VBOX_WITH_HGSMI */
5225
5226/**
5227 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
5228 */
5229DECLCALLBACK(void *) Display::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
5230{
5231 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
5232 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
5233 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
5234 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYCONNECTOR, &pDrv->IConnector);
5235 return NULL;
5236}
5237
5238
5239/**
5240 * Destruct a display driver instance.
5241 *
5242 * @returns VBox status.
5243 * @param pDrvIns The driver instance data.
5244 */
5245DECLCALLBACK(void) Display::drvDestruct(PPDMDRVINS pDrvIns)
5246{
5247 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
5248 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
5249 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
5250
5251 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
5252
5253 pThis->IConnector.pu8Data = NULL;
5254 pThis->IConnector.cbScanline = 0;
5255 pThis->IConnector.cBits = 32;
5256 pThis->IConnector.cx = 0;
5257 pThis->IConnector.cy = 0;
5258
5259 if (pThis->pDisplay)
5260 {
5261 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
5262#ifdef VBOX_WITH_VPX
5263 pThis->pDisplay->VideoCaptureStop();
5264#endif
5265#ifdef VBOX_WITH_CRHGSMI
5266 pThis->pDisplay->destructCrHgsmiData();
5267#endif
5268 pThis->pDisplay->mpDrv = NULL;
5269 pThis->pDisplay->mpVMMDev = NULL;
5270 pThis->pDisplay->mLastAddress = NULL;
5271 pThis->pDisplay->mLastBytesPerLine = 0;
5272 pThis->pDisplay->mLastBitsPerPixel = 0,
5273 pThis->pDisplay->mLastWidth = 0;
5274 pThis->pDisplay->mLastHeight = 0;
5275 }
5276}
5277
5278
5279/**
5280 * Construct a display driver instance.
5281 *
5282 * @copydoc FNPDMDRVCONSTRUCT
5283 */
5284DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
5285{
5286 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
5287 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
5288 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
5289
5290 /*
5291 * Validate configuration.
5292 */
5293 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
5294 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
5295 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
5296 ("Configuration error: Not possible to attach anything to this driver!\n"),
5297 VERR_PDM_DRVINS_NO_ATTACH);
5298
5299 /*
5300 * Init Interfaces.
5301 */
5302 pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
5303
5304 pThis->IConnector.pfnResize = Display::displayResizeCallback;
5305 pThis->IConnector.pfnUpdateRect = Display::displayUpdateCallback;
5306 pThis->IConnector.pfnRefresh = Display::displayRefreshCallback;
5307 pThis->IConnector.pfnReset = Display::displayResetCallback;
5308 pThis->IConnector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
5309 pThis->IConnector.pfnProcessAdapterData = Display::displayProcessAdapterDataCallback;
5310 pThis->IConnector.pfnProcessDisplayData = Display::displayProcessDisplayDataCallback;
5311#ifdef VBOX_WITH_VIDEOHWACCEL
5312 pThis->IConnector.pfnVHWACommandProcess = Display::displayVHWACommandProcess;
5313#endif
5314#ifdef VBOX_WITH_CRHGSMI
5315 pThis->IConnector.pfnCrHgsmiCommandProcess = Display::displayCrHgsmiCommandProcess;
5316 pThis->IConnector.pfnCrHgsmiControlProcess = Display::displayCrHgsmiControlProcess;
5317#endif
5318#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
5319 pThis->IConnector.pfnCrHgcmCtlSubmit = Display::displayCrHgcmCtlSubmit;
5320#endif
5321#ifdef VBOX_WITH_HGSMI
5322 pThis->IConnector.pfnVBVAEnable = Display::displayVBVAEnable;
5323 pThis->IConnector.pfnVBVADisable = Display::displayVBVADisable;
5324 pThis->IConnector.pfnVBVAUpdateBegin = Display::displayVBVAUpdateBegin;
5325 pThis->IConnector.pfnVBVAUpdateProcess = Display::displayVBVAUpdateProcess;
5326 pThis->IConnector.pfnVBVAUpdateEnd = Display::displayVBVAUpdateEnd;
5327 pThis->IConnector.pfnVBVAResize = Display::displayVBVAResize;
5328 pThis->IConnector.pfnVBVAMousePointerShape = Display::displayVBVAMousePointerShape;
5329#endif
5330
5331 /*
5332 * Get the IDisplayPort interface of the above driver/device.
5333 */
5334 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
5335 if (!pThis->pUpPort)
5336 {
5337 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
5338 return VERR_PDM_MISSING_INTERFACE_ABOVE;
5339 }
5340#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
5341 pThis->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
5342 if (!pThis->pVBVACallbacks)
5343 {
5344 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
5345 return VERR_PDM_MISSING_INTERFACE_ABOVE;
5346 }
5347#endif
5348 /*
5349 * Get the Display object pointer and update the mpDrv member.
5350 */
5351 void *pv;
5352 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
5353 if (RT_FAILURE(rc))
5354 {
5355 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
5356 return rc;
5357 }
5358 Display *pDisplay = (Display *)pv; /** @todo Check this cast! */
5359 pThis->pDisplay = pDisplay;
5360 pThis->pDisplay->mpDrv = pThis;
5361
5362 /* Disable VRAM to a buffer copy initially. */
5363 pThis->pUpPort->pfnSetRenderVRAM (pThis->pUpPort, false);
5364 pThis->IConnector.cBits = 32; /* DevVGA does nothing otherwise. */
5365
5366 /*
5367 * Start periodic screen refreshes
5368 */
5369 pThis->pUpPort->pfnSetRefreshRate(pThis->pUpPort, 20);
5370
5371#ifdef VBOX_WITH_CRHGSMI
5372 pDisplay->setupCrHgsmiData();
5373#endif
5374
5375#ifdef VBOX_WITH_VPX
5376 ComPtr<IMachine> pMachine = pDisplay->mParent->machine();
5377 BOOL fEnabled = false;
5378 HRESULT hrc = pMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
5379 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
5380 if (fEnabled)
5381 {
5382 rc = pDisplay->VideoCaptureStart();
5383 fireVideoCaptureChangedEvent(pDisplay->mParent->getEventSource());
5384 }
5385#endif
5386
5387 return rc;
5388}
5389
5390
5391/**
5392 * Display driver registration record.
5393 */
5394const PDMDRVREG Display::DrvReg =
5395{
5396 /* u32Version */
5397 PDM_DRVREG_VERSION,
5398 /* szName */
5399 "MainDisplay",
5400 /* szRCMod */
5401 "",
5402 /* szR0Mod */
5403 "",
5404 /* pszDescription */
5405 "Main display driver (Main as in the API).",
5406 /* fFlags */
5407 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
5408 /* fClass. */
5409 PDM_DRVREG_CLASS_DISPLAY,
5410 /* cMaxInstances */
5411 ~0U,
5412 /* cbInstance */
5413 sizeof(DRVMAINDISPLAY),
5414 /* pfnConstruct */
5415 Display::drvConstruct,
5416 /* pfnDestruct */
5417 Display::drvDestruct,
5418 /* pfnRelocate */
5419 NULL,
5420 /* pfnIOCtl */
5421 NULL,
5422 /* pfnPowerOn */
5423 NULL,
5424 /* pfnReset */
5425 NULL,
5426 /* pfnSuspend */
5427 NULL,
5428 /* pfnResume */
5429 NULL,
5430 /* pfnAttach */
5431 NULL,
5432 /* pfnDetach */
5433 NULL,
5434 /* pfnPowerOff */
5435 NULL,
5436 /* pfnSoftReset */
5437 NULL,
5438 /* u32EndVersion */
5439 PDM_DRVREG_VERSION
5440};
5441/* 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