VirtualBox

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

Last change on this file since 68803 was 68803, checked in by vboxsync, 8 years ago

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 159.7 KB
Line 
1/* $Id: DisplayImpl.cpp 68803 2017-09-20 13:06:54Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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#define LOG_GROUP LOG_GROUP_MAIN_DISPLAY
19#include "LoggingNew.h"
20
21#include "DisplayImpl.h"
22#include "DisplayUtils.h"
23#include "ConsoleImpl.h"
24#include "ConsoleVRDPServer.h"
25#include "GuestImpl.h"
26#include "VMMDev.h"
27
28#include "AutoCaller.h"
29
30/* generated header */
31#include "VBoxEvents.h"
32
33#include <iprt/semaphore.h>
34#include <iprt/thread.h>
35#include <iprt/asm.h>
36#include <iprt/time.h>
37#include <iprt/cpp/utils.h>
38#include <iprt/alloca.h>
39
40#include <VBox/vmm/pdmdrv.h>
41#if defined(DEBUG) || defined(VBOX_STRICT) /* for VM_ASSERT_EMT(). */
42# include <VBox/vmm/vm.h>
43#endif
44
45#ifdef VBOX_WITH_VIDEOHWACCEL
46# include <VBoxVideo.h>
47#endif
48
49#if defined(VBOX_WITH_CROGL) || defined(VBOX_WITH_CRHGSMI)
50# include <VBox/HostServices/VBoxCrOpenGLSvc.h>
51#endif
52
53#include <VBox/com/array.h>
54
55#ifdef VBOX_WITH_VIDEOREC
56# include <iprt/path.h>
57# include "VideoRec.h"
58
59# ifdef VBOX_WITH_LIBVPX
60# ifdef _MSC_VER
61# pragma warning(push)
62# pragma warning(disable: 4668) /* vpx_codec.h(64) : warning C4668: '__GNUC__' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */
63# include <vpx/vpx_encoder.h>
64# pragma warning(pop)
65# else
66# include <vpx/vpx_encoder.h>
67# endif
68# endif
69#endif
70
71#ifdef VBOX_WITH_CROGL
72typedef enum
73{
74 CRVREC_STATE_IDLE,
75 CRVREC_STATE_SUBMITTED
76} CRVREC_STATE;
77#endif
78
79/**
80 * Display driver instance data.
81 *
82 * @implements PDMIDISPLAYCONNECTOR
83 */
84typedef struct DRVMAINDISPLAY
85{
86 /** Pointer to the display object. */
87 Display *pDisplay;
88 /** Pointer to the driver instance structure. */
89 PPDMDRVINS pDrvIns;
90 /** Pointer to the keyboard port interface of the driver/device above us. */
91 PPDMIDISPLAYPORT pUpPort;
92 /** Our display connector interface. */
93 PDMIDISPLAYCONNECTOR IConnector;
94#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
95 /** VBVA callbacks */
96 PPDMIDISPLAYVBVACALLBACKS pVBVACallbacks;
97#endif
98} DRVMAINDISPLAY, *PDRVMAINDISPLAY;
99
100/** Converts PDMIDISPLAYCONNECTOR pointer to a DRVMAINDISPLAY pointer. */
101#define PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface) RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector)
102
103// constructor / destructor
104/////////////////////////////////////////////////////////////////////////////
105
106Display::Display()
107 : mParent(NULL), mfIsCr3DEnabled(false)
108{
109}
110
111Display::~Display()
112{
113}
114
115
116HRESULT Display::FinalConstruct()
117{
118 int rc = videoAccelConstruct(&mVideoAccelLegacy);
119 AssertRC(rc);
120
121 mfVideoAccelVRDP = false;
122 mfu32SupportedOrders = 0;
123 mcVideoAccelVRDPRefs = 0;
124
125 mfSeamlessEnabled = false;
126 mpRectVisibleRegion = NULL;
127 mcRectVisibleRegion = 0;
128
129#ifdef VBOX_WITH_CROGL
130 mfCrOglDataHidden = false;
131#endif
132
133 mpDrv = NULL;
134 mpVMMDev = NULL;
135 mfVMMDevInited = false;
136
137 rc = RTCritSectInit(&mVideoAccelLock);
138 AssertRC(rc);
139
140#ifdef VBOX_WITH_HGSMI
141 mu32UpdateVBVAFlags = 0;
142 mfVMMDevSupportsGraphics = false;
143 mfGuestVBVACapabilities = 0;
144 mfHostCursorCapabilities = 0;
145#endif
146
147#ifdef VBOX_WITH_VIDEOREC
148 rc = RTCritSectInit(&mVideoCaptureLock);
149 AssertRC(rc);
150
151 mpVideoRecCtx = NULL;
152 for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
153 maVideoRecEnabled[i] = true;
154#endif
155
156#ifdef VBOX_WITH_CRHGSMI
157 mhCrOglSvc = NULL;
158 rc = RTCritSectRwInit(&mCrOglLock);
159 AssertRC(rc);
160#endif
161
162#ifdef VBOX_WITH_CROGL
163 RT_ZERO(mCrOglCallbacks);
164 RT_ZERO(mCrOglScreenshotData);
165 mfCrOglVideoRecState = CRVREC_STATE_IDLE;
166 mCrOglScreenshotData.u32Screen = CRSCREEN_ALL;
167 mCrOglScreenshotData.pvContext = this;
168 mCrOglScreenshotData.pfnScreenshotBegin = i_displayCrVRecScreenshotBegin;
169 mCrOglScreenshotData.pfnScreenshotPerform = i_displayCrVRecScreenshotPerform;
170 mCrOglScreenshotData.pfnScreenshotEnd = i_displayCrVRecScreenshotEnd;
171#endif
172
173 return BaseFinalConstruct();
174}
175
176void Display::FinalRelease()
177{
178 uninit();
179
180#ifdef VBOX_WITH_VIDEOREC
181 if (RTCritSectIsInitialized(&mVideoCaptureLock))
182 {
183 RTCritSectDelete(&mVideoCaptureLock);
184 RT_ZERO(mVideoCaptureLock);
185 }
186#endif
187
188 videoAccelDestroy(&mVideoAccelLegacy);
189 i_saveVisibleRegion(0, NULL);
190
191 if (RTCritSectIsInitialized(&mVideoAccelLock))
192 {
193 RTCritSectDelete(&mVideoAccelLock);
194 RT_ZERO(mVideoAccelLock);
195 }
196
197#ifdef VBOX_WITH_CRHGSMI
198 if (RTCritSectRwIsInitialized(&mCrOglLock))
199 {
200 RTCritSectRwDelete(&mCrOglLock);
201 RT_ZERO(mCrOglLock);
202 }
203#endif
204 BaseFinalRelease();
205}
206
207// public initializer/uninitializer for internal purposes only
208/////////////////////////////////////////////////////////////////////////////
209
210#define kMaxSizeThumbnail 64
211
212/**
213 * Save thumbnail and screenshot of the guest screen.
214 */
215static int displayMakeThumbnail(uint8_t *pbData, uint32_t cx, uint32_t cy,
216 uint8_t **ppu8Thumbnail, uint32_t *pcbThumbnail, uint32_t *pcxThumbnail, uint32_t *pcyThumbnail)
217{
218 int rc = VINF_SUCCESS;
219
220 uint8_t *pu8Thumbnail = NULL;
221 uint32_t cbThumbnail = 0;
222 uint32_t cxThumbnail = 0;
223 uint32_t cyThumbnail = 0;
224
225 if (cx > cy)
226 {
227 cxThumbnail = kMaxSizeThumbnail;
228 cyThumbnail = (kMaxSizeThumbnail * cy) / cx;
229 }
230 else
231 {
232 cyThumbnail = kMaxSizeThumbnail;
233 cxThumbnail = (kMaxSizeThumbnail * cx) / cy;
234 }
235
236 LogRelFlowFunc(("%dx%d -> %dx%d\n", cx, cy, cxThumbnail, cyThumbnail));
237
238 cbThumbnail = cxThumbnail * 4 * cyThumbnail;
239 pu8Thumbnail = (uint8_t *)RTMemAlloc(cbThumbnail);
240
241 if (pu8Thumbnail)
242 {
243 uint8_t *dst = pu8Thumbnail;
244 uint8_t *src = pbData;
245 int dstW = cxThumbnail;
246 int dstH = cyThumbnail;
247 int srcW = cx;
248 int srcH = cy;
249 int iDeltaLine = cx * 4;
250
251 BitmapScale32(dst,
252 dstW, dstH,
253 src,
254 iDeltaLine,
255 srcW, srcH);
256
257 *ppu8Thumbnail = pu8Thumbnail;
258 *pcbThumbnail = cbThumbnail;
259 *pcxThumbnail = cxThumbnail;
260 *pcyThumbnail = cyThumbnail;
261 }
262 else
263 {
264 rc = VERR_NO_MEMORY;
265 }
266
267 return rc;
268}
269
270#ifdef VBOX_WITH_CROGL
271typedef struct
272{
273 CRVBOXHGCMTAKESCREENSHOT Base;
274
275 /* 32bpp small RGB image. */
276 uint8_t *pu8Thumbnail;
277 uint32_t cbThumbnail;
278 uint32_t cxThumbnail;
279 uint32_t cyThumbnail;
280
281 /* PNG screenshot. */
282 uint8_t *pu8PNG;
283 uint32_t cbPNG;
284 uint32_t cxPNG;
285 uint32_t cyPNG;
286} VBOX_DISPLAY_SAVESCREENSHOT_DATA;
287
288static DECLCALLBACK(void) displaySaveScreenshotReport(void *pvCtx, uint32_t uScreen,
289 uint32_t x, uint32_t y, uint32_t uBitsPerPixel,
290 uint32_t uBytesPerLine, uint32_t uGuestWidth, uint32_t uGuestHeight,
291 uint8_t *pu8BufferAddress, uint64_t u64Timestamp)
292{
293 RT_NOREF(uScreen, x, y, uBitsPerPixel,uBytesPerLine, u64Timestamp);
294 VBOX_DISPLAY_SAVESCREENSHOT_DATA *pData = (VBOX_DISPLAY_SAVESCREENSHOT_DATA*)pvCtx;
295 displayMakeThumbnail(pu8BufferAddress, uGuestWidth, uGuestHeight, &pData->pu8Thumbnail,
296 &pData->cbThumbnail, &pData->cxThumbnail, &pData->cyThumbnail);
297 int rc = DisplayMakePNG(pu8BufferAddress, uGuestWidth, uGuestHeight, &pData->pu8PNG,
298 &pData->cbPNG, &pData->cxPNG, &pData->cyPNG, 1);
299 if (RT_FAILURE(rc))
300 {
301 AssertMsgFailed(("DisplayMakePNG failed (rc=%Rrc)\n", rc));
302 if (pData->pu8PNG)
303 {
304 RTMemFree(pData->pu8PNG);
305 pData->pu8PNG = NULL;
306 }
307 pData->cbPNG = 0;
308 pData->cxPNG = 0;
309 pData->cyPNG = 0;
310 }
311}
312#endif
313
314DECLCALLBACK(void) Display::i_displaySSMSaveScreenshot(PSSMHANDLE pSSM, void *pvUser)
315{
316 Display *that = static_cast<Display*>(pvUser);
317
318 /* 32bpp small RGB image. */
319 uint8_t *pu8Thumbnail = NULL;
320 uint32_t cbThumbnail = 0;
321 uint32_t cxThumbnail = 0;
322 uint32_t cyThumbnail = 0;
323
324 /* PNG screenshot. */
325 uint8_t *pu8PNG = NULL;
326 uint32_t cbPNG = 0;
327 uint32_t cxPNG = 0;
328 uint32_t cyPNG = 0;
329
330 Console::SafeVMPtr ptrVM(that->mParent);
331 if (ptrVM.isOk())
332 {
333#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
334 BOOL f3DSnapshot = FALSE;
335 if ( that->mfIsCr3DEnabled
336 && that->mCrOglCallbacks.pfnHasData
337 && that->mCrOglCallbacks.pfnHasData())
338 {
339 VMMDev *pVMMDev = that->mParent->i_getVMMDev();
340 if (pVMMDev)
341 {
342 VBOX_DISPLAY_SAVESCREENSHOT_DATA *pScreenshot;
343 pScreenshot = (VBOX_DISPLAY_SAVESCREENSHOT_DATA*)RTMemAllocZ(sizeof(*pScreenshot));
344 if (pScreenshot)
345 {
346 /* screen id or CRSCREEN_ALL to specify all enabled */
347 pScreenshot->Base.u32Screen = 0;
348 pScreenshot->Base.u32Width = 0;
349 pScreenshot->Base.u32Height = 0;
350 pScreenshot->Base.u32Pitch = 0;
351 pScreenshot->Base.pvBuffer = NULL;
352 pScreenshot->Base.pvContext = pScreenshot;
353 pScreenshot->Base.pfnScreenshotBegin = NULL;
354 pScreenshot->Base.pfnScreenshotPerform = displaySaveScreenshotReport;
355 pScreenshot->Base.pfnScreenshotEnd = NULL;
356
357 VBOXCRCMDCTL_HGCM data;
358 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
359 data.Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
360
361 data.aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
362 data.aParms[0].u.pointer.addr = &pScreenshot->Base;
363 data.aParms[0].u.pointer.size = sizeof(pScreenshot->Base);
364
365 int rc = that->i_crCtlSubmitSync(&data.Hdr, sizeof(data));
366 if (RT_SUCCESS(rc))
367 {
368 if (pScreenshot->pu8PNG)
369 {
370 pu8Thumbnail = pScreenshot->pu8Thumbnail;
371 cbThumbnail = pScreenshot->cbThumbnail;
372 cxThumbnail = pScreenshot->cxThumbnail;
373 cyThumbnail = pScreenshot->cyThumbnail;
374
375 /* PNG screenshot. */
376 pu8PNG = pScreenshot->pu8PNG;
377 cbPNG = pScreenshot->cbPNG;
378 cxPNG = pScreenshot->cxPNG;
379 cyPNG = pScreenshot->cyPNG;
380 f3DSnapshot = TRUE;
381 }
382 else
383 AssertMsgFailed(("no png\n"));
384 }
385 else
386 AssertMsgFailed(("SHCRGL_HOST_FN_TAKE_SCREENSHOT failed (rc=%Rrc)\n", rc));
387
388
389 RTMemFree(pScreenshot);
390 }
391 }
392 }
393
394 if (!f3DSnapshot)
395#endif
396 {
397 /* Query RGB bitmap. */
398 /* SSM code is executed on EMT(0), therefore no need to use VMR3ReqCallWait. */
399 uint8_t *pbData = NULL;
400 size_t cbData = 0;
401 uint32_t cx = 0;
402 uint32_t cy = 0;
403 bool fFreeMem = false;
404 int rc = Display::i_displayTakeScreenshotEMT(that, VBOX_VIDEO_PRIMARY_SCREEN, &pbData, &cbData, &cx, &cy, &fFreeMem);
405
406 /*
407 * It is possible that success is returned but everything is 0 or NULL.
408 * (no display attached if a VM is running with VBoxHeadless on OSE for example)
409 */
410 if (RT_SUCCESS(rc) && pbData)
411 {
412 Assert(cx && cy);
413
414 /* Prepare a small thumbnail and a PNG screenshot. */
415 displayMakeThumbnail(pbData, cx, cy, &pu8Thumbnail, &cbThumbnail, &cxThumbnail, &cyThumbnail);
416 rc = DisplayMakePNG(pbData, cx, cy, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 1);
417 if (RT_FAILURE(rc))
418 {
419 if (pu8PNG)
420 {
421 RTMemFree(pu8PNG);
422 pu8PNG = NULL;
423 }
424 cbPNG = 0;
425 cxPNG = 0;
426 cyPNG = 0;
427 }
428
429 if (fFreeMem)
430 RTMemFree(pbData);
431 else
432 that->mpDrv->pUpPort->pfnFreeScreenshot(that->mpDrv->pUpPort, pbData);
433 }
434 }
435 }
436 else
437 {
438 LogFunc(("Failed to get VM pointer 0x%x\n", ptrVM.rc()));
439 }
440
441 /* Regardless of rc, save what is available:
442 * Data format:
443 * uint32_t cBlocks;
444 * [blocks]
445 *
446 * Each block is:
447 * uint32_t cbBlock; if 0 - no 'block data'.
448 * uint32_t typeOfBlock; 0 - 32bpp RGB bitmap, 1 - PNG, ignored if 'cbBlock' is 0.
449 * [block data]
450 *
451 * Block data for bitmap and PNG:
452 * uint32_t cx;
453 * uint32_t cy;
454 * [image data]
455 */
456 SSMR3PutU32(pSSM, 2); /* Write thumbnail and PNG screenshot. */
457
458 /* First block. */
459 SSMR3PutU32(pSSM, (uint32_t)(cbThumbnail + 2 * sizeof(uint32_t)));
460 SSMR3PutU32(pSSM, 0); /* Block type: thumbnail. */
461
462 if (cbThumbnail)
463 {
464 SSMR3PutU32(pSSM, cxThumbnail);
465 SSMR3PutU32(pSSM, cyThumbnail);
466 SSMR3PutMem(pSSM, pu8Thumbnail, cbThumbnail);
467 }
468
469 /* Second block. */
470 SSMR3PutU32(pSSM, (uint32_t)(cbPNG + 2 * sizeof(uint32_t)));
471 SSMR3PutU32(pSSM, 1); /* Block type: png. */
472
473 if (cbPNG)
474 {
475 SSMR3PutU32(pSSM, cxPNG);
476 SSMR3PutU32(pSSM, cyPNG);
477 SSMR3PutMem(pSSM, pu8PNG, cbPNG);
478 }
479
480 RTMemFree(pu8PNG);
481 RTMemFree(pu8Thumbnail);
482}
483
484DECLCALLBACK(int)
485Display::i_displaySSMLoadScreenshot(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
486{
487 RT_NOREF(pvUser);
488 if (uVersion != sSSMDisplayScreenshotVer)
489 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
490 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
491
492 /* Skip data. */
493 uint32_t cBlocks;
494 int rc = SSMR3GetU32(pSSM, &cBlocks);
495 AssertRCReturn(rc, rc);
496
497 for (uint32_t i = 0; i < cBlocks; i++)
498 {
499 uint32_t cbBlock;
500 rc = SSMR3GetU32(pSSM, &cbBlock);
501 AssertRCBreak(rc);
502
503 uint32_t typeOfBlock;
504 rc = SSMR3GetU32(pSSM, &typeOfBlock);
505 AssertRCBreak(rc);
506
507 LogRelFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
508
509 /* Note: displaySSMSaveScreenshot writes size of a block = 8 and
510 * do not write any data if the image size was 0.
511 * @todo Fix and increase saved state version.
512 */
513 if (cbBlock > 2 * sizeof(uint32_t))
514 {
515 rc = SSMR3Skip(pSSM, cbBlock);
516 AssertRCBreak(rc);
517 }
518 }
519
520 return rc;
521}
522
523/**
524 * Save/Load some important guest state
525 */
526DECLCALLBACK(void)
527Display::i_displaySSMSave(PSSMHANDLE pSSM, void *pvUser)
528{
529 Display *that = static_cast<Display*>(pvUser);
530
531 SSMR3PutU32(pSSM, that->mcMonitors);
532 for (unsigned i = 0; i < that->mcMonitors; i++)
533 {
534 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32Offset);
535 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32MaxFramebufferSize);
536 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32InformationSize);
537 SSMR3PutU32(pSSM, that->maFramebuffers[i].w);
538 SSMR3PutU32(pSSM, that->maFramebuffers[i].h);
539 SSMR3PutS32(pSSM, that->maFramebuffers[i].xOrigin);
540 SSMR3PutS32(pSSM, that->maFramebuffers[i].yOrigin);
541 SSMR3PutU32(pSSM, that->maFramebuffers[i].flags);
542 }
543 SSMR3PutS32(pSSM, that->xInputMappingOrigin);
544 SSMR3PutS32(pSSM, that->yInputMappingOrigin);
545 SSMR3PutU32(pSSM, that->cxInputMapping);
546 SSMR3PutU32(pSSM, that->cyInputMapping);
547 SSMR3PutU32(pSSM, that->mfGuestVBVACapabilities);
548 SSMR3PutU32(pSSM, that->mfHostCursorCapabilities);
549}
550
551DECLCALLBACK(int)
552Display::i_displaySSMLoad(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
553{
554 Display *that = static_cast<Display*>(pvUser);
555
556 if ( uVersion != sSSMDisplayVer
557 && uVersion != sSSMDisplayVer2
558 && uVersion != sSSMDisplayVer3
559 && uVersion != sSSMDisplayVer4
560 && uVersion != sSSMDisplayVer5)
561 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
562 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
563
564 uint32_t cMonitors;
565 int rc = SSMR3GetU32(pSSM, &cMonitors);
566 AssertRCReturn(rc, rc);
567 if (cMonitors != that->mcMonitors)
568 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Number of monitors changed (%d->%d)!"), cMonitors, that->mcMonitors);
569
570 for (uint32_t i = 0; i < cMonitors; i++)
571 {
572 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32Offset);
573 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32MaxFramebufferSize);
574 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32InformationSize);
575 if ( uVersion == sSSMDisplayVer2
576 || uVersion == sSSMDisplayVer3
577 || uVersion == sSSMDisplayVer4
578 || uVersion == sSSMDisplayVer5)
579 {
580 uint32_t w;
581 uint32_t h;
582 SSMR3GetU32(pSSM, &w);
583 SSMR3GetU32(pSSM, &h);
584 that->maFramebuffers[i].w = w;
585 that->maFramebuffers[i].h = h;
586 }
587 if ( uVersion == sSSMDisplayVer3
588 || uVersion == sSSMDisplayVer4
589 || uVersion == sSSMDisplayVer5)
590 {
591 int32_t xOrigin;
592 int32_t yOrigin;
593 uint32_t flags;
594 SSMR3GetS32(pSSM, &xOrigin);
595 SSMR3GetS32(pSSM, &yOrigin);
596 SSMR3GetU32(pSSM, &flags);
597 that->maFramebuffers[i].xOrigin = xOrigin;
598 that->maFramebuffers[i].yOrigin = yOrigin;
599 that->maFramebuffers[i].flags = (uint16_t)flags;
600 that->maFramebuffers[i].fDisabled = (that->maFramebuffers[i].flags & VBVA_SCREEN_F_DISABLED) != 0;
601 }
602 }
603 if ( uVersion == sSSMDisplayVer4
604 || uVersion == sSSMDisplayVer5)
605 {
606 SSMR3GetS32(pSSM, &that->xInputMappingOrigin);
607 SSMR3GetS32(pSSM, &that->yInputMappingOrigin);
608 SSMR3GetU32(pSSM, &that->cxInputMapping);
609 SSMR3GetU32(pSSM, &that->cyInputMapping);
610 }
611 if (uVersion == sSSMDisplayVer5)
612 {
613 SSMR3GetU32(pSSM, &that->mfGuestVBVACapabilities);
614 SSMR3GetU32(pSSM, &that->mfHostCursorCapabilities);
615 }
616
617 return VINF_SUCCESS;
618}
619
620/**
621 * Initializes the display object.
622 *
623 * @returns COM result indicator
624 * @param aParent handle of our parent object
625 */
626HRESULT Display::init(Console *aParent)
627{
628 ComAssertRet(aParent, E_INVALIDARG);
629 /* Enclose the state transition NotReady->InInit->Ready */
630 AutoInitSpan autoInitSpan(this);
631 AssertReturn(autoInitSpan.isOk(), E_FAIL);
632
633 unconst(mParent) = aParent;
634
635 mfSourceBitmapEnabled = true;
636 fVGAResizing = false;
637
638 ULONG ul;
639 mParent->i_machine()->COMGETTER(MonitorCount)(&ul);
640 mcMonitors = ul;
641 xInputMappingOrigin = 0;
642 yInputMappingOrigin = 0;
643 cxInputMapping = 0;
644 cyInputMapping = 0;
645
646 for (ul = 0; ul < mcMonitors; ul++)
647 {
648 maFramebuffers[ul].u32Offset = 0;
649 maFramebuffers[ul].u32MaxFramebufferSize = 0;
650 maFramebuffers[ul].u32InformationSize = 0;
651
652 maFramebuffers[ul].pFramebuffer = NULL;
653 /* All secondary monitors are disabled at startup. */
654 maFramebuffers[ul].fDisabled = ul > 0;
655
656 maFramebuffers[ul].u32Caps = 0;
657
658 maFramebuffers[ul].updateImage.pu8Address = NULL;
659 maFramebuffers[ul].updateImage.cbLine = 0;
660
661 maFramebuffers[ul].xOrigin = 0;
662 maFramebuffers[ul].yOrigin = 0;
663
664 maFramebuffers[ul].w = 0;
665 maFramebuffers[ul].h = 0;
666
667 maFramebuffers[ul].flags = maFramebuffers[ul].fDisabled? VBVA_SCREEN_F_DISABLED: 0;
668
669 maFramebuffers[ul].u16BitsPerPixel = 0;
670 maFramebuffers[ul].pu8FramebufferVRAM = NULL;
671 maFramebuffers[ul].u32LineSize = 0;
672
673 maFramebuffers[ul].pHostEvents = NULL;
674
675 maFramebuffers[ul].fDefaultFormat = false;
676
677#ifdef VBOX_WITH_HGSMI
678 maFramebuffers[ul].fVBVAEnabled = false;
679 maFramebuffers[ul].fVBVAForceResize = false;
680 maFramebuffers[ul].fRenderThreadMode = false;
681 maFramebuffers[ul].pVBVAHostFlags = NULL;
682#endif /* VBOX_WITH_HGSMI */
683#ifdef VBOX_WITH_CROGL
684 RT_ZERO(maFramebuffers[ul].pendingViewportInfo);
685#endif
686 }
687
688 {
689 // register listener for state change events
690 ComPtr<IEventSource> es;
691 mParent->COMGETTER(EventSource)(es.asOutParam());
692 com::SafeArray<VBoxEventType_T> eventTypes;
693 eventTypes.push_back(VBoxEventType_OnStateChanged);
694 es->RegisterListener(this, ComSafeArrayAsInParam(eventTypes), true);
695 }
696
697 /* Cache the 3D settings. */
698 BOOL fIs3DEnabled = FALSE;
699 mParent->i_machine()->COMGETTER(Accelerate3DEnabled)(&fIs3DEnabled);
700 GraphicsControllerType_T enmGpuType = (GraphicsControllerType_T)GraphicsControllerType_VBoxVGA;
701 mParent->i_machine()->COMGETTER(GraphicsControllerType)(&enmGpuType);
702 mfIsCr3DEnabled = fIs3DEnabled && enmGpuType == GraphicsControllerType_VBoxVGA;
703
704 /* Confirm a successful initialization */
705 autoInitSpan.setSucceeded();
706
707 return S_OK;
708}
709
710/**
711 * Uninitializes the instance and sets the ready flag to FALSE.
712 * Called either from FinalRelease() or by the parent when it gets destroyed.
713 */
714void Display::uninit()
715{
716 LogRelFlowFunc(("this=%p\n", this));
717
718 /* Enclose the state transition Ready->InUninit->NotReady */
719 AutoUninitSpan autoUninitSpan(this);
720 if (autoUninitSpan.uninitDone())
721 return;
722
723 unsigned uScreenId;
724 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
725 {
726 maFramebuffers[uScreenId].pSourceBitmap.setNull();
727 maFramebuffers[uScreenId].updateImage.pSourceBitmap.setNull();
728 maFramebuffers[uScreenId].updateImage.pu8Address = NULL;
729 maFramebuffers[uScreenId].updateImage.cbLine = 0;
730 maFramebuffers[uScreenId].pFramebuffer.setNull();
731#ifdef VBOX_WITH_VIDEOREC
732 maFramebuffers[uScreenId].videoCapture.pSourceBitmap.setNull();
733#endif
734 }
735
736 if (mParent)
737 {
738 ComPtr<IEventSource> es;
739 mParent->COMGETTER(EventSource)(es.asOutParam());
740 es->UnregisterListener(this);
741 }
742
743 unconst(mParent) = NULL;
744
745 if (mpDrv)
746 mpDrv->pDisplay = NULL;
747
748 mpDrv = NULL;
749 mpVMMDev = NULL;
750 mfVMMDevInited = true;
751}
752
753/**
754 * Register the SSM methods. Called by the power up thread to be able to
755 * pass pVM
756 */
757int Display::i_registerSSM(PUVM pUVM)
758{
759 /* Version 2 adds width and height of the framebuffer; version 3 adds
760 * the framebuffer offset in the virtual desktop and the framebuffer flags;
761 * version 4 adds guest to host input event mapping and version 5 adds
762 * guest VBVA and host cursor capabilities.
763 */
764 int rc = SSMR3RegisterExternal(pUVM, "DisplayData", 0, sSSMDisplayVer5,
765 mcMonitors * sizeof(uint32_t) * 8 + sizeof(uint32_t),
766 NULL, NULL, NULL,
767 NULL, i_displaySSMSave, NULL,
768 NULL, i_displaySSMLoad, NULL, this);
769 AssertRCReturn(rc, rc);
770
771 /*
772 * Register loaders for old saved states where iInstance was
773 * 3 * sizeof(uint32_t *) due to a code mistake.
774 */
775 rc = SSMR3RegisterExternal(pUVM, "DisplayData", 12 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
776 NULL, NULL, NULL,
777 NULL, NULL, NULL,
778 NULL, i_displaySSMLoad, NULL, this);
779 AssertRCReturn(rc, rc);
780
781 rc = SSMR3RegisterExternal(pUVM, "DisplayData", 24 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
782 NULL, NULL, NULL,
783 NULL, NULL, NULL,
784 NULL, i_displaySSMLoad, NULL, this);
785 AssertRCReturn(rc, rc);
786
787 /* uInstance is an arbitrary value greater than 1024. Such a value will ensure a quick seek in saved state file. */
788 rc = SSMR3RegisterExternal(pUVM, "DisplayScreenshot", 1100 /*uInstance*/, sSSMDisplayScreenshotVer, 0 /*cbGuess*/,
789 NULL, NULL, NULL,
790 NULL, i_displaySSMSaveScreenshot, NULL,
791 NULL, i_displaySSMLoadScreenshot, NULL, this);
792
793 AssertRCReturn(rc, rc);
794
795 return VINF_SUCCESS;
796}
797
798DECLCALLBACK(void) Display::i_displayCrCmdFree(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
799{
800 RT_NOREF(pCmd, cbCmd, rc);
801 Assert(pvCompletion);
802 RTMemFree(pvCompletion);
803}
804
805#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
806int Display::i_crOglWindowsShow(bool fShow)
807{
808 if (!mfCrOglDataHidden == !!fShow)
809 return VINF_SUCCESS;
810
811 if (!mhCrOglSvc)
812 {
813 /* No 3D or the VMSVGA3d kind. */
814 Assert(!mfIsCr3DEnabled);
815 return VERR_INVALID_STATE;
816 }
817
818 VMMDev *pVMMDev = mParent->i_getVMMDev();
819 if (!pVMMDev)
820 {
821 AssertMsgFailed(("no vmmdev\n"));
822 return VERR_INVALID_STATE;
823 }
824
825 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM*)RTMemAlloc(sizeof(VBOXCRCMDCTL_HGCM));
826 if (!pData)
827 {
828 AssertMsgFailed(("RTMemAlloc failed\n"));
829 return VERR_NO_MEMORY;
830 }
831
832 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
833 pData->Hdr.u32Function = SHCRGL_HOST_FN_WINDOWS_SHOW;
834
835 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
836 pData->aParms[0].u.uint32 = (uint32_t)fShow;
837
838 int rc = i_crCtlSubmit(&pData->Hdr, sizeof(*pData), i_displayCrCmdFree, pData);
839 if (RT_SUCCESS(rc))
840 mfCrOglDataHidden = !fShow;
841 else
842 {
843 AssertMsgFailed(("crCtlSubmit failed (rc=%Rrc)\n", rc));
844 RTMemFree(pData);
845 }
846
847 return rc;
848}
849#endif
850
851
852// public methods only for internal purposes
853/////////////////////////////////////////////////////////////////////////////
854
855int Display::i_notifyCroglResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, void *pvVRAM)
856{
857 RT_NOREF(pView);
858#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
859 if (maFramebuffers[pScreen->u32ViewIndex].fRenderThreadMode)
860 return VINF_SUCCESS; /* nop it */
861
862 if (mfIsCr3DEnabled)
863 {
864 int rc = VERR_INVALID_STATE;
865 if (mhCrOglSvc)
866 {
867 VMMDev *pVMMDev = mParent->i_getVMMDev();
868 if (pVMMDev)
869 {
870 VBOXCRCMDCTL_HGCM *pCtl;
871 pCtl = (VBOXCRCMDCTL_HGCM*)RTMemAlloc(sizeof(CRVBOXHGCMDEVRESIZE) + sizeof(VBOXCRCMDCTL_HGCM));
872 if (pCtl)
873 {
874 CRVBOXHGCMDEVRESIZE *pData = (CRVBOXHGCMDEVRESIZE*)(pCtl+1);
875 pData->Screen = *pScreen;
876 pData->pvVRAM = pvVRAM;
877
878 pCtl->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
879 pCtl->Hdr.u32Function = SHCRGL_HOST_FN_DEV_RESIZE;
880 pCtl->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
881 pCtl->aParms[0].u.pointer.addr = pData;
882 pCtl->aParms[0].u.pointer.size = sizeof(*pData);
883
884 rc = i_crCtlSubmit(&pCtl->Hdr, sizeof(*pCtl), i_displayCrCmdFree, pCtl);
885 if (RT_FAILURE(rc))
886 {
887 AssertMsgFailed(("crCtlSubmit failed (rc=%Rrc)\n", rc));
888 RTMemFree(pCtl);
889 }
890 }
891 else
892 rc = VERR_NO_MEMORY;
893 }
894 }
895
896 return rc;
897 }
898#else /* !VBOX_WITH_HGCM || !VBOX_WITH_CROGL */
899 RT_NOREF(pScreen, pvVRAM);
900#endif
901 return VINF_SUCCESS;
902}
903
904/**
905 * Handles display resize event.
906 *
907 * @param uScreenId Screen ID
908 * @param bpp New bits per pixel.
909 * @param pvVRAM VRAM pointer.
910 * @param cbLine New bytes per line.
911 * @param w New display width.
912 * @param h New display height.
913 * @param flags Flags of the new video mode.
914 * @param xOrigin New display origin X.
915 * @param yOrigin New display origin Y.
916 * @param fVGAResize Whether the resize is originated from the VGA device (DevVGA).
917 */
918int Display::i_handleDisplayResize(unsigned uScreenId, uint32_t bpp, void *pvVRAM,
919 uint32_t cbLine, uint32_t w, uint32_t h, uint16_t flags,
920 int32_t xOrigin, int32_t yOrigin, bool fVGAResize)
921{
922 LogRel(("Display::handleDisplayResize: uScreenId=%d pvVRAM=%p w=%d h=%d bpp=%d cbLine=0x%X flags=0x%X\n", uScreenId,
923 pvVRAM, w, h, bpp, cbLine, flags));
924
925 /* Caller must not hold the object lock. */
926 AssertReturn(!isWriteLockOnCurrentThread(), VERR_INVALID_STATE);
927
928 /* Note: the old code checked if the video mode was actially chnaged and
929 * did not invalidate the source bitmap if the mode did not change.
930 * The new code always invalidates the source bitmap, i.e. it will
931 * notify the frontend even if nothing actually changed.
932 *
933 * Implementing the filtering is possible but might lead to pfnSetRenderVRAM races
934 * between this method and QuerySourceBitmap. Such races can be avoided by implementing
935 * the @todo below.
936 */
937
938 /* Make sure that the VGA device does not access the source bitmap. */
939 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && mpDrv)
940 {
941 /// @todo It is probably more convenient to implement
942 // mpDrv->pUpPort->pfnSetOutputBitmap(pvVRAM, cbScanline, cBits, cx, cy, bool fSet);
943 // and remove IConnector.pbData, cbScanline, cBits, cx, cy.
944 // fSet = false disables rendering and VGA can check
945 // if it is already rendering to a different bitmap, avoiding
946 // enable/disable rendering races.
947 mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, false);
948
949 mpDrv->IConnector.pbData = NULL;
950 mpDrv->IConnector.cbScanline = 0;
951 mpDrv->IConnector.cBits = 32; /* DevVGA does not work with cBits == 0. */
952 mpDrv->IConnector.cx = 0;
953 mpDrv->IConnector.cy = 0;
954 }
955
956 /* Update maFramebuffers[uScreenId] under lock. */
957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
958
959 if (uScreenId >= mcMonitors)
960 return VINF_SUCCESS;
961
962 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
963
964 /* Whether the monitor position has changed.
965 * A resize initiated by the VGA device does not change the monitor position.
966 */
967 const bool fNewOrigin = !fVGAResize
968 && ( pFBInfo->xOrigin != xOrigin
969 || pFBInfo->yOrigin != yOrigin);
970
971 /* The event for disabled->enabled transition.
972 * VGA resizes also come when the guest uses VBVA mode. They do not affect pFBInfo->fDisabled.
973 * The primary screen is re-enabled when the guest leaves the VBVA mode in i_displayVBVADisable.
974 */
975 const bool fGuestMonitorChangedEvent = !fVGAResize
976 && (pFBInfo->fDisabled != RT_BOOL(flags & VBVA_SCREEN_F_DISABLED));
977
978 /* Reset the update mode. */
979 pFBInfo->updateImage.pSourceBitmap.setNull();
980 pFBInfo->updateImage.pu8Address = NULL;
981 pFBInfo->updateImage.cbLine = 0;
982
983 /* Release the current source bitmap. */
984 pFBInfo->pSourceBitmap.setNull();
985
986 /* VGA blanking is signaled as w=0, h=0, bpp=0 and cbLine=0, and it's
987 * best to keep the old resolution, as otherwise the window size would
988 * change before the new resolution is known. */
989 const bool fVGABlank = fVGAResize && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
990 && w == 0 && h == 0 && bpp == 0 && cbLine == 0;
991 if (fVGABlank)
992 {
993 w = pFBInfo->w;
994 h = pFBInfo->h;
995 }
996
997 /* Update the video mode information. */
998 pFBInfo->w = w;
999 pFBInfo->h = h;
1000 pFBInfo->u16BitsPerPixel = (uint16_t)bpp;
1001 pFBInfo->pu8FramebufferVRAM = (uint8_t *)pvVRAM;
1002 pFBInfo->u32LineSize = cbLine;
1003 if (!fVGAResize)
1004 {
1005 /* Fields which are not used in not VBVA modes and not affected by a VGA resize. */
1006 pFBInfo->flags = flags;
1007 pFBInfo->xOrigin = xOrigin;
1008 pFBInfo->yOrigin = yOrigin;
1009 pFBInfo->fDisabled = RT_BOOL(flags & VBVA_SCREEN_F_DISABLED);
1010 pFBInfo->fVBVAForceResize = false;
1011 }
1012 else
1013 {
1014 pFBInfo->flags = 0;
1015 if (fVGABlank)
1016 pFBInfo->flags |= VBVA_SCREEN_F_BLANK;
1017 pFBInfo->fDisabled = false;
1018 }
1019
1020 /* Prepare local vars for the notification code below. */
1021 ComPtr<IFramebuffer> pFramebuffer = pFBInfo->pFramebuffer;
1022 const bool fDisabled = pFBInfo->fDisabled;
1023
1024 alock.release();
1025
1026 if (!pFramebuffer.isNull())
1027 {
1028 HRESULT hr = pFramebuffer->NotifyChange(uScreenId, 0, 0, w, h); /** @todo origin */
1029 LogFunc(("NotifyChange hr %08X\n", hr));
1030 NOREF(hr);
1031 }
1032
1033 if (fGuestMonitorChangedEvent)
1034 {
1035 if (fDisabled)
1036 fireGuestMonitorChangedEvent(mParent->i_getEventSource(),
1037 GuestMonitorChangedEventType_Disabled,
1038 uScreenId,
1039 0, 0, 0, 0);
1040 else
1041 fireGuestMonitorChangedEvent(mParent->i_getEventSource(),
1042 GuestMonitorChangedEventType_Enabled,
1043 uScreenId,
1044 xOrigin, yOrigin, w, h);
1045 }
1046
1047 if (fNewOrigin)
1048 fireGuestMonitorChangedEvent(mParent->i_getEventSource(),
1049 GuestMonitorChangedEventType_NewOrigin,
1050 uScreenId,
1051 xOrigin, yOrigin, 0, 0);
1052
1053 /* Inform the VRDP server about the change of display parameters. */
1054 LogRelFlowFunc(("Calling VRDP\n"));
1055 mParent->i_consoleVRDPServer()->SendResize();
1056
1057 /* And re-send the seamless rectangles if necessary. */
1058 if (mfSeamlessEnabled)
1059 i_handleSetVisibleRegion(mcRectVisibleRegion, mpRectVisibleRegion);
1060
1061#ifdef VBOX_WITH_VIDEOREC
1062 i_videoCaptureScreenChanged(uScreenId);
1063#endif
1064
1065 LogRelFlowFunc(("[%d]: default format %d\n", uScreenId, pFBInfo->fDefaultFormat));
1066
1067 return VINF_SUCCESS;
1068}
1069
1070static void i_checkCoordBounds(int *px, int *py, int *pw, int *ph, int cx, int cy)
1071{
1072 /* Correct negative x and y coordinates. */
1073 if (*px < 0)
1074 {
1075 *px += *pw; /* Compute xRight which is also the new width. */
1076
1077 *pw = (*px < 0)? 0: *px;
1078
1079 *px = 0;
1080 }
1081
1082 if (*py < 0)
1083 {
1084 *py += *ph; /* Compute xBottom, which is also the new height. */
1085
1086 *ph = (*py < 0)? 0: *py;
1087
1088 *py = 0;
1089 }
1090
1091 /* Also check if coords are greater than the display resolution. */
1092 if (*px + *pw > cx)
1093 {
1094 *pw = cx > *px? cx - *px: 0;
1095 }
1096
1097 if (*py + *ph > cy)
1098 {
1099 *ph = cy > *py? cy - *py: 0;
1100 }
1101}
1102
1103void Display::i_handleDisplayUpdate(unsigned uScreenId, int x, int y, int w, int h)
1104{
1105 /*
1106 * Always runs under either VBVA lock or, for HGSMI, DevVGA lock.
1107 * Safe to use VBVA vars and take the framebuffer lock.
1108 */
1109
1110#ifdef DEBUG_sunlover
1111 LogFlowFunc(("[%d] %d,%d %dx%d\n",
1112 uScreenId, x, y, w, h));
1113#endif /* DEBUG_sunlover */
1114
1115 /* No updates for a disabled guest screen. */
1116 if (maFramebuffers[uScreenId].fDisabled)
1117 return;
1118
1119 /* No updates for a blank guest screen. */
1120 /** @note Disabled for now, as the GUI does not update the picture when we
1121 * first blank. */
1122 /* if (maFramebuffers[uScreenId].flags & VBVA_SCREEN_F_BLANK)
1123 return; */
1124
1125 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1126 AutoReadLock alockr(this COMMA_LOCKVAL_SRC_POS);
1127
1128 ComPtr<IFramebuffer> pFramebuffer = pFBInfo->pFramebuffer;
1129 ComPtr<IDisplaySourceBitmap> pSourceBitmap = pFBInfo->updateImage.pSourceBitmap;
1130
1131 alockr.release();
1132
1133 if (RT_LIKELY(!pFramebuffer.isNull()))
1134 {
1135 if (RT_LIKELY(!RT_BOOL(pFBInfo->u32Caps & FramebufferCapabilities_UpdateImage)))
1136 {
1137 i_checkCoordBounds(&x, &y, &w, &h, pFBInfo->w, pFBInfo->h);
1138
1139 if (w != 0 && h != 0)
1140 {
1141 pFramebuffer->NotifyUpdate(x, y, w, h);
1142 }
1143 }
1144 else
1145 {
1146 if (RT_LIKELY(!pSourceBitmap.isNull()))
1147 { /* likely */ }
1148 else
1149 {
1150 /* Create a source bitmap if UpdateImage mode is used. */
1151 HRESULT hr = QuerySourceBitmap(uScreenId, pSourceBitmap.asOutParam());
1152 if (SUCCEEDED(hr))
1153 {
1154 BYTE *pAddress = NULL;
1155 ULONG ulWidth = 0;
1156 ULONG ulHeight = 0;
1157 ULONG ulBitsPerPixel = 0;
1158 ULONG ulBytesPerLine = 0;
1159 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
1160
1161 hr = pSourceBitmap->QueryBitmapInfo(&pAddress,
1162 &ulWidth,
1163 &ulHeight,
1164 &ulBitsPerPixel,
1165 &ulBytesPerLine,
1166 &bitmapFormat);
1167 if (SUCCEEDED(hr))
1168 {
1169 AutoWriteLock alockw(this COMMA_LOCKVAL_SRC_POS);
1170
1171 if (pFBInfo->updateImage.pSourceBitmap.isNull())
1172 {
1173 pFBInfo->updateImage.pSourceBitmap = pSourceBitmap;
1174 pFBInfo->updateImage.pu8Address = pAddress;
1175 pFBInfo->updateImage.cbLine = ulBytesPerLine;
1176 }
1177
1178 pSourceBitmap = pFBInfo->updateImage.pSourceBitmap;
1179
1180 alockw.release();
1181 }
1182 }
1183 }
1184
1185 if (RT_LIKELY(!pSourceBitmap.isNull()))
1186 {
1187 BYTE *pbAddress = NULL;
1188 ULONG ulWidth = 0;
1189 ULONG ulHeight = 0;
1190 ULONG ulBitsPerPixel = 0;
1191 ULONG ulBytesPerLine = 0;
1192 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
1193
1194 HRESULT hr = pSourceBitmap->QueryBitmapInfo(&pbAddress,
1195 &ulWidth,
1196 &ulHeight,
1197 &ulBitsPerPixel,
1198 &ulBytesPerLine,
1199 &bitmapFormat);
1200 if (SUCCEEDED(hr))
1201 {
1202 /* Make sure that the requested update is within the source bitmap dimensions. */
1203 i_checkCoordBounds(&x, &y, &w, &h, ulWidth, ulHeight);
1204
1205 if (w != 0 && h != 0)
1206 {
1207 const size_t cbData = w * h * 4;
1208 com::SafeArray<BYTE> image(cbData);
1209
1210 uint8_t *pu8Dst = image.raw();
1211 const uint8_t *pu8Src = pbAddress + ulBytesPerLine * y + x * 4;
1212
1213 int i;
1214 for (i = y; i < y + h; ++i)
1215 {
1216 memcpy(pu8Dst, pu8Src, w * 4);
1217 pu8Dst += w * 4;
1218 pu8Src += ulBytesPerLine;
1219 }
1220
1221 pFramebuffer->NotifyUpdateImage(x, y, w, h, ComSafeArrayAsInParam(image));
1222 }
1223 }
1224 }
1225 }
1226 }
1227
1228#ifndef VBOX_WITH_HGSMI
1229 if (!mVideoAccelLegacy.fVideoAccelEnabled)
1230 {
1231#else
1232 if (!mVideoAccelLegacy.fVideoAccelEnabled && !maFramebuffers[uScreenId].fVBVAEnabled)
1233 {
1234#endif /* VBOX_WITH_HGSMI */
1235 /* When VBVA is enabled, the VRDP server is informed
1236 * either in VideoAccelFlush or displayVBVAUpdateProcess.
1237 * Inform the server here only if VBVA is disabled.
1238 */
1239 mParent->i_consoleVRDPServer()->SendUpdateBitmap(uScreenId, x, y, w, h);
1240 }
1241}
1242
1243void Display::i_updateGuestGraphicsFacility(void)
1244{
1245 Guest* pGuest = mParent->i_getGuest();
1246 AssertPtrReturnVoid(pGuest);
1247 /* The following is from GuestImpl.cpp. */
1248 /** @todo A nit: The timestamp is wrong on saved state restore. Would be better
1249 * to move the graphics and seamless capability -> facility translation to
1250 * VMMDev so this could be saved. */
1251 RTTIMESPEC TimeSpecTS;
1252 RTTimeNow(&TimeSpecTS);
1253
1254 if ( mfVMMDevSupportsGraphics
1255 || (mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS) != 0)
1256 pGuest->i_setAdditionsStatus(VBoxGuestFacilityType_Graphics,
1257 VBoxGuestFacilityStatus_Active,
1258 0 /*fFlags*/, &TimeSpecTS);
1259 else
1260 pGuest->i_setAdditionsStatus(VBoxGuestFacilityType_Graphics,
1261 VBoxGuestFacilityStatus_Inactive,
1262 0 /*fFlags*/, &TimeSpecTS);
1263}
1264
1265void Display::i_handleUpdateVMMDevSupportsGraphics(bool fSupportsGraphics)
1266{
1267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1268 if (mfVMMDevSupportsGraphics == fSupportsGraphics)
1269 return;
1270 mfVMMDevSupportsGraphics = fSupportsGraphics;
1271 i_updateGuestGraphicsFacility();
1272 /* The VMMDev interface notifies the console. */
1273}
1274
1275void Display::i_handleUpdateGuestVBVACapabilities(uint32_t fNewCapabilities)
1276{
1277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1278 bool fNotify = (fNewCapabilities & VBVACAPS_VIDEO_MODE_HINTS) != (mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS);
1279
1280 mfGuestVBVACapabilities = fNewCapabilities;
1281 if (!fNotify)
1282 return;
1283 i_updateGuestGraphicsFacility();
1284 /* Tell the console about it */
1285 mParent->i_onAdditionsStateChange();
1286}
1287
1288void Display::i_handleUpdateVBVAInputMapping(int32_t xOrigin, int32_t yOrigin, uint32_t cx, uint32_t cy)
1289{
1290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1291
1292 xInputMappingOrigin = xOrigin;
1293 yInputMappingOrigin = yOrigin;
1294 cxInputMapping = cx;
1295 cyInputMapping = cy;
1296
1297 /* Re-send the seamless rectangles if necessary. */
1298 if (mfSeamlessEnabled)
1299 i_handleSetVisibleRegion(mcRectVisibleRegion, mpRectVisibleRegion);
1300}
1301
1302/**
1303 * Returns the upper left and lower right corners of the virtual framebuffer.
1304 * The lower right is "exclusive" (i.e. first pixel beyond the framebuffer),
1305 * and the origin is (0, 0), not (1, 1) like the GUI returns.
1306 */
1307void Display::i_getFramebufferDimensions(int32_t *px1, int32_t *py1,
1308 int32_t *px2, int32_t *py2)
1309{
1310 int32_t x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1312
1313 AssertPtrReturnVoid(px1);
1314 AssertPtrReturnVoid(py1);
1315 AssertPtrReturnVoid(px2);
1316 AssertPtrReturnVoid(py2);
1317 LogRelFlowFunc(("\n"));
1318
1319 if (!mpDrv)
1320 return;
1321 /* If VBVA is not in use then this flag will not be set and this
1322 * will still work as it should. */
1323 if (!maFramebuffers[0].fDisabled)
1324 {
1325 x1 = (int32_t)maFramebuffers[0].xOrigin;
1326 y1 = (int32_t)maFramebuffers[0].yOrigin;
1327 x2 = (int32_t)maFramebuffers[0].w + (int32_t)maFramebuffers[0].xOrigin;
1328 y2 = (int32_t)maFramebuffers[0].h + (int32_t)maFramebuffers[0].yOrigin;
1329 }
1330 if (cxInputMapping && cyInputMapping)
1331 {
1332 x1 = xInputMappingOrigin;
1333 y1 = yInputMappingOrigin;
1334 x2 = xInputMappingOrigin + cxInputMapping;
1335 y2 = yInputMappingOrigin + cyInputMapping;
1336 }
1337 else
1338 for (unsigned i = 1; i < mcMonitors; ++i)
1339 {
1340 if (!maFramebuffers[i].fDisabled)
1341 {
1342 x1 = RT_MIN(x1, maFramebuffers[i].xOrigin);
1343 y1 = RT_MIN(y1, maFramebuffers[i].yOrigin);
1344 x2 = RT_MAX(x2, maFramebuffers[i].xOrigin + (int32_t)maFramebuffers[i].w);
1345 y2 = RT_MAX(y2, maFramebuffers[i].yOrigin + (int32_t)maFramebuffers[i].h);
1346 }
1347 }
1348 *px1 = x1;
1349 *py1 = y1;
1350 *px2 = x2;
1351 *py2 = y2;
1352}
1353
1354HRESULT Display::i_reportHostCursorCapabilities(uint32_t fCapabilitiesAdded, uint32_t fCapabilitiesRemoved)
1355{
1356 /* Do we need this to access mParent? I presume that the safe VM pointer
1357 * ensures that mpDrv will remain valid. */
1358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1359 uint32_t fHostCursorCapabilities = (mfHostCursorCapabilities | fCapabilitiesAdded)
1360 & ~fCapabilitiesRemoved;
1361
1362 Console::SafeVMPtr ptrVM(mParent);
1363 if (!ptrVM.isOk())
1364 return ptrVM.rc();
1365 if (mfHostCursorCapabilities == fHostCursorCapabilities)
1366 return S_OK;
1367 CHECK_CONSOLE_DRV(mpDrv);
1368 alock.release(); /* Release before calling up for lock order reasons. */
1369 mpDrv->pUpPort->pfnReportHostCursorCapabilities(mpDrv->pUpPort, fCapabilitiesAdded, fCapabilitiesRemoved);
1370 mfHostCursorCapabilities = fHostCursorCapabilities;
1371 return S_OK;
1372}
1373
1374HRESULT Display::i_reportHostCursorPosition(int32_t x, int32_t y)
1375{
1376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1377 uint32_t xAdj = (uint32_t)RT_MAX(x - xInputMappingOrigin, 0);
1378 uint32_t yAdj = (uint32_t)RT_MAX(y - yInputMappingOrigin, 0);
1379 xAdj = RT_MIN(xAdj, cxInputMapping);
1380 yAdj = RT_MIN(yAdj, cyInputMapping);
1381
1382 Console::SafeVMPtr ptrVM(mParent);
1383 if (!ptrVM.isOk())
1384 return ptrVM.rc();
1385 CHECK_CONSOLE_DRV(mpDrv);
1386 alock.release(); /* Release before calling up for lock order reasons. */
1387 mpDrv->pUpPort->pfnReportHostCursorPosition(mpDrv->pUpPort, xAdj, yAdj);
1388 return S_OK;
1389}
1390
1391static bool displayIntersectRect(RTRECT *prectResult,
1392 const RTRECT *prect1,
1393 const RTRECT *prect2)
1394{
1395 /* Initialize result to an empty record. */
1396 memset(prectResult, 0, sizeof(RTRECT));
1397
1398 int xLeftResult = RT_MAX(prect1->xLeft, prect2->xLeft);
1399 int xRightResult = RT_MIN(prect1->xRight, prect2->xRight);
1400
1401 if (xLeftResult < xRightResult)
1402 {
1403 /* There is intersection by X. */
1404
1405 int yTopResult = RT_MAX(prect1->yTop, prect2->yTop);
1406 int yBottomResult = RT_MIN(prect1->yBottom, prect2->yBottom);
1407
1408 if (yTopResult < yBottomResult)
1409 {
1410 /* There is intersection by Y. */
1411
1412 prectResult->xLeft = xLeftResult;
1413 prectResult->yTop = yTopResult;
1414 prectResult->xRight = xRightResult;
1415 prectResult->yBottom = yBottomResult;
1416
1417 return true;
1418 }
1419 }
1420
1421 return false;
1422}
1423
1424int Display::i_saveVisibleRegion(uint32_t cRect, PRTRECT pRect)
1425{
1426 RTRECT *pRectVisibleRegion = NULL;
1427
1428 if (pRect == mpRectVisibleRegion)
1429 return VINF_SUCCESS;
1430 if (cRect != 0)
1431 {
1432 pRectVisibleRegion = (RTRECT *)RTMemAlloc(cRect * sizeof(RTRECT));
1433 if (!pRectVisibleRegion)
1434 {
1435 return VERR_NO_MEMORY;
1436 }
1437 memcpy(pRectVisibleRegion, pRect, cRect * sizeof(RTRECT));
1438 }
1439 if (mpRectVisibleRegion)
1440 RTMemFree(mpRectVisibleRegion);
1441 mcRectVisibleRegion = cRect;
1442 mpRectVisibleRegion = pRectVisibleRegion;
1443 return VINF_SUCCESS;
1444}
1445
1446int Display::i_handleSetVisibleRegion(uint32_t cRect, PRTRECT pRect)
1447{
1448 RTRECT *pVisibleRegion = (RTRECT *)RTMemTmpAlloc( RT_MAX(cRect, 1)
1449 * sizeof(RTRECT));
1450 LogRel2(("%s: cRect=%u\n", __PRETTY_FUNCTION__, cRect));
1451 if (!pVisibleRegion)
1452 {
1453 return VERR_NO_TMP_MEMORY;
1454 }
1455 int rc = i_saveVisibleRegion(cRect, pRect);
1456 if (RT_FAILURE(rc))
1457 {
1458 RTMemTmpFree(pVisibleRegion);
1459 return rc;
1460 }
1461
1462 unsigned uScreenId;
1463 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1464 {
1465 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1466
1467 if ( !pFBInfo->pFramebuffer.isNull()
1468 && RT_BOOL(pFBInfo->u32Caps & FramebufferCapabilities_VisibleRegion))
1469 {
1470 /* Prepare a new array of rectangles which intersect with the framebuffer.
1471 */
1472 RTRECT rectFramebuffer;
1473 rectFramebuffer.xLeft = pFBInfo->xOrigin - xInputMappingOrigin;
1474 rectFramebuffer.yTop = pFBInfo->yOrigin - yInputMappingOrigin;
1475 rectFramebuffer.xRight = rectFramebuffer.xLeft + pFBInfo->w;
1476 rectFramebuffer.yBottom = rectFramebuffer.yTop + pFBInfo->h;
1477
1478 uint32_t cRectVisibleRegion = 0;
1479
1480 uint32_t i;
1481 for (i = 0; i < cRect; i++)
1482 {
1483 if (displayIntersectRect(&pVisibleRegion[cRectVisibleRegion], &pRect[i], &rectFramebuffer))
1484 {
1485 pVisibleRegion[cRectVisibleRegion].xLeft -= rectFramebuffer.xLeft;
1486 pVisibleRegion[cRectVisibleRegion].yTop -= rectFramebuffer.yTop;
1487 pVisibleRegion[cRectVisibleRegion].xRight -= rectFramebuffer.xLeft;
1488 pVisibleRegion[cRectVisibleRegion].yBottom -= rectFramebuffer.yTop;
1489
1490 cRectVisibleRegion++;
1491 }
1492 }
1493 pFBInfo->pFramebuffer->SetVisibleRegion((BYTE *)pVisibleRegion, cRectVisibleRegion);
1494 }
1495 }
1496
1497#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1498 VMMDev *vmmDev = mParent->i_getVMMDev();
1499 if (mfIsCr3DEnabled && vmmDev)
1500 {
1501 if (mhCrOglSvc)
1502 {
1503 VBOXCRCMDCTL_HGCM *pCtl;
1504 pCtl = (VBOXCRCMDCTL_HGCM*)RTMemAlloc(RT_MAX(cRect, 1) * sizeof(RTRECT) + sizeof(VBOXCRCMDCTL_HGCM));
1505 if (pCtl)
1506 {
1507 RTRECT *pRectsCopy = (RTRECT*)(pCtl+1);
1508 memcpy(pRectsCopy, pRect, cRect * sizeof(RTRECT));
1509
1510 pCtl->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1511 pCtl->Hdr.u32Function = SHCRGL_HOST_FN_SET_VISIBLE_REGION;
1512
1513 pCtl->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
1514 pCtl->aParms[0].u.pointer.addr = pRectsCopy;
1515 pCtl->aParms[0].u.pointer.size = (uint32_t)(cRect * sizeof(RTRECT));
1516
1517 rc = i_crCtlSubmit(&pCtl->Hdr, sizeof(*pCtl), i_displayCrCmdFree, pCtl);
1518 if (!RT_SUCCESS(rc))
1519 {
1520 AssertMsgFailed(("crCtlSubmit failed (rc=%Rrc)\n", rc));
1521 RTMemFree(pCtl);
1522 }
1523 }
1524 else
1525 AssertMsgFailed(("failed to allocate rects memory\n"));
1526 }
1527 else
1528 AssertMsgFailed(("mhCrOglSvc is NULL\n"));
1529 }
1530#endif
1531
1532 RTMemTmpFree(pVisibleRegion);
1533
1534 return VINF_SUCCESS;
1535}
1536
1537int Display::i_handleQueryVisibleRegion(uint32_t *pcRects, PRTRECT paRects)
1538{
1539 /// @todo Currently not used by the guest and is not implemented in
1540 /// framebuffers. Remove?
1541 RT_NOREF(pcRects, paRects);
1542 return VERR_NOT_SUPPORTED;
1543}
1544
1545#ifdef VBOX_WITH_HGSMI
1546static void vbvaSetMemoryFlagsHGSMI(unsigned uScreenId,
1547 uint32_t fu32SupportedOrders,
1548 bool fVideoAccelVRDP,
1549 DISPLAYFBINFO *pFBInfo)
1550{
1551 LogRelFlowFunc(("HGSMI[%d]: %p\n", uScreenId, pFBInfo->pVBVAHostFlags));
1552
1553 if (pFBInfo->pVBVAHostFlags)
1554 {
1555 uint32_t fu32HostEvents = VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
1556
1557 if (pFBInfo->fVBVAEnabled)
1558 {
1559 fu32HostEvents |= VBVA_F_MODE_ENABLED;
1560
1561 if (fVideoAccelVRDP)
1562 {
1563 fu32HostEvents |= VBVA_F_MODE_VRDP;
1564 }
1565 }
1566
1567 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32HostEvents, fu32HostEvents);
1568 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32SupportedOrders, fu32SupportedOrders);
1569
1570 LogRelFlowFunc((" fu32HostEvents = 0x%08X, fu32SupportedOrders = 0x%08X\n", fu32HostEvents, fu32SupportedOrders));
1571 }
1572}
1573
1574static void vbvaSetMemoryFlagsAllHGSMI(uint32_t fu32SupportedOrders,
1575 bool fVideoAccelVRDP,
1576 DISPLAYFBINFO *paFBInfos,
1577 unsigned cFBInfos)
1578{
1579 unsigned uScreenId;
1580
1581 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
1582 {
1583 vbvaSetMemoryFlagsHGSMI(uScreenId, fu32SupportedOrders, fVideoAccelVRDP, &paFBInfos[uScreenId]);
1584 }
1585}
1586#endif /* VBOX_WITH_HGSMI */
1587
1588int Display::VideoAccelEnableVMMDev(bool fEnable, VBVAMEMORY *pVbvaMemory)
1589{
1590 LogFlowFunc(("%d %p\n", fEnable, pVbvaMemory));
1591 int rc = videoAccelEnterVMMDev(&mVideoAccelLegacy);
1592 if (RT_SUCCESS(rc))
1593 {
1594 rc = i_VideoAccelEnable(fEnable, pVbvaMemory, mpDrv->pUpPort);
1595 videoAccelLeaveVMMDev(&mVideoAccelLegacy);
1596 }
1597 LogFlowFunc(("leave %Rrc\n", rc));
1598 return rc;
1599}
1600
1601int Display::VideoAccelEnableVGA(bool fEnable, VBVAMEMORY *pVbvaMemory)
1602{
1603 LogFlowFunc(("%d %p\n", fEnable, pVbvaMemory));
1604 int rc = videoAccelEnterVGA(&mVideoAccelLegacy);
1605 if (RT_SUCCESS(rc))
1606 {
1607 rc = i_VideoAccelEnable(fEnable, pVbvaMemory, mpDrv->pUpPort);
1608 videoAccelLeaveVGA(&mVideoAccelLegacy);
1609 }
1610 LogFlowFunc(("leave %Rrc\n", rc));
1611 return rc;
1612}
1613
1614void Display::VideoAccelFlushVMMDev(void)
1615{
1616 LogFlowFunc(("enter\n"));
1617 int rc = videoAccelEnterVMMDev(&mVideoAccelLegacy);
1618 if (RT_SUCCESS(rc))
1619 {
1620 i_VideoAccelFlush(mpDrv->pUpPort);
1621 videoAccelLeaveVMMDev(&mVideoAccelLegacy);
1622 }
1623 LogFlowFunc(("leave\n"));
1624}
1625
1626/* Called always by one VRDP server thread. Can be thread-unsafe.
1627 */
1628void Display::i_VideoAccelVRDP(bool fEnable)
1629{
1630 LogRelFlowFunc(("fEnable = %d\n", fEnable));
1631
1632 VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
1633
1634 int c = fEnable?
1635 ASMAtomicIncS32(&mcVideoAccelVRDPRefs):
1636 ASMAtomicDecS32(&mcVideoAccelVRDPRefs);
1637
1638 Assert (c >= 0);
1639
1640 /* This can run concurrently with Display videoaccel state change. */
1641 RTCritSectEnter(&mVideoAccelLock);
1642
1643 if (c == 0)
1644 {
1645 /* The last client has disconnected, and the accel can be
1646 * disabled.
1647 */
1648 Assert(fEnable == false);
1649
1650 mfVideoAccelVRDP = false;
1651 mfu32SupportedOrders = 0;
1652
1653 i_vbvaSetMemoryFlags(pVideoAccel->pVbvaMemory, pVideoAccel->fVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders,
1654 maFramebuffers, mcMonitors);
1655#ifdef VBOX_WITH_HGSMI
1656 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1657 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1658#endif /* VBOX_WITH_HGSMI */
1659
1660 LogRel(("VBVA: VRDP acceleration has been disabled.\n"));
1661 }
1662 else if ( c == 1
1663 && !mfVideoAccelVRDP)
1664 {
1665 /* The first client has connected. Enable the accel.
1666 */
1667 Assert(fEnable == true);
1668
1669 mfVideoAccelVRDP = true;
1670 /* Supporting all orders. */
1671 mfu32SupportedOrders = UINT32_MAX;
1672
1673 i_vbvaSetMemoryFlags(pVideoAccel->pVbvaMemory, pVideoAccel->fVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders,
1674 maFramebuffers, mcMonitors);
1675#ifdef VBOX_WITH_HGSMI
1676 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1677 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1678#endif /* VBOX_WITH_HGSMI */
1679
1680 LogRel(("VBVA: VRDP acceleration has been requested.\n"));
1681 }
1682 else
1683 {
1684 /* A client is connected or disconnected but there is no change in the
1685 * accel state. It remains enabled.
1686 */
1687 Assert(mfVideoAccelVRDP == true);
1688 }
1689
1690 RTCritSectLeave(&mVideoAccelLock);
1691}
1692
1693void Display::i_notifyPowerDown(void)
1694{
1695 LogRelFlowFunc(("\n"));
1696
1697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1698
1699 /* Source bitmaps are not available anymore. */
1700 mfSourceBitmapEnabled = false;
1701
1702 alock.release();
1703
1704 /* Resize all displays to tell framebuffers to forget current source bitmap. */
1705 unsigned uScreenId = mcMonitors;
1706 while (uScreenId > 0)
1707 {
1708 --uScreenId;
1709
1710 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1711 if (!pFBInfo->fDisabled)
1712 {
1713 i_handleDisplayResize(uScreenId, 32,
1714 pFBInfo->pu8FramebufferVRAM,
1715 pFBInfo->u32LineSize,
1716 pFBInfo->w,
1717 pFBInfo->h,
1718 pFBInfo->flags,
1719 pFBInfo->xOrigin,
1720 pFBInfo->yOrigin,
1721 false);
1722 }
1723 }
1724}
1725
1726// Wrapped IDisplay methods
1727/////////////////////////////////////////////////////////////////////////////
1728HRESULT Display::getScreenResolution(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ULONG *aBitsPerPixel,
1729 LONG *aXOrigin, LONG *aYOrigin, GuestMonitorStatus_T *aGuestMonitorStatus)
1730{
1731 LogRelFlowFunc(("aScreenId=%RU32\n", aScreenId));
1732
1733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1734
1735 if (aScreenId >= mcMonitors)
1736 return E_INVALIDARG;
1737
1738 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1739
1740 GuestMonitorStatus_T guestMonitorStatus = GuestMonitorStatus_Enabled;
1741
1742 if (pFBInfo->flags & VBVA_SCREEN_F_DISABLED)
1743 guestMonitorStatus = GuestMonitorStatus_Disabled;
1744 else if (pFBInfo->flags & (VBVA_SCREEN_F_BLANK | VBVA_SCREEN_F_BLANK2))
1745 guestMonitorStatus = GuestMonitorStatus_Blank;
1746
1747 if (aWidth)
1748 *aWidth = pFBInfo->w;
1749 if (aHeight)
1750 *aHeight = pFBInfo->h;
1751 if (aBitsPerPixel)
1752 *aBitsPerPixel = pFBInfo->u16BitsPerPixel;
1753 if (aXOrigin)
1754 *aXOrigin = pFBInfo->xOrigin;
1755 if (aYOrigin)
1756 *aYOrigin = pFBInfo->yOrigin;
1757 if (aGuestMonitorStatus)
1758 *aGuestMonitorStatus = guestMonitorStatus;
1759
1760 return S_OK;
1761}
1762
1763
1764HRESULT Display::attachFramebuffer(ULONG aScreenId, const ComPtr<IFramebuffer> &aFramebuffer, com::Guid &aId)
1765{
1766 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
1767
1768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1769
1770 if (aScreenId >= mcMonitors)
1771 return setError(E_INVALIDARG, tr("AttachFramebuffer: Invalid screen %d (total %d)"),
1772 aScreenId, mcMonitors);
1773
1774 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1775 if (!pFBInfo->pFramebuffer.isNull())
1776 return setError(E_FAIL, tr("AttachFramebuffer: Framebuffer already attached to %d"),
1777 aScreenId);
1778
1779 pFBInfo->pFramebuffer = aFramebuffer;
1780 pFBInfo->framebufferId.create();
1781 aId = pFBInfo->framebufferId;
1782
1783 SafeArray<FramebufferCapabilities_T> caps;
1784 pFBInfo->pFramebuffer->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(caps));
1785 pFBInfo->u32Caps = 0;
1786 size_t i;
1787 for (i = 0; i < caps.size(); ++i)
1788 pFBInfo->u32Caps |= caps[i];
1789
1790 alock.release();
1791
1792 /* The driver might not have been constructed yet */
1793 if (mpDrv)
1794 {
1795 /* Inform the framebuffer about the actual screen size. */
1796 HRESULT hr = aFramebuffer->NotifyChange(aScreenId, 0, 0, pFBInfo->w, pFBInfo->h); /** @todo origin */
1797 LogFunc(("NotifyChange hr %08X\n", hr)); NOREF(hr);
1798
1799 /* Re-send the seamless rectangles if necessary. */
1800 if (mfSeamlessEnabled)
1801 i_handleSetVisibleRegion(mcRectVisibleRegion, mpRectVisibleRegion);
1802 }
1803
1804 Console::SafeVMPtrQuiet ptrVM(mParent);
1805 if (ptrVM.isOk())
1806 {
1807#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1808 if (mfIsCr3DEnabled)
1809 {
1810 VBOXCRCMDCTL_HGCM data;
1811 RT_ZERO(data);
1812 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1813 data.Hdr.u32Function = SHCRGL_HOST_FN_SCREEN_CHANGED;
1814
1815 data.aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
1816 data.aParms[0].u.uint32 = aScreenId;
1817
1818 int vrc = i_crCtlSubmitSync(&data.Hdr, sizeof(data));
1819 AssertRC(vrc);
1820 }
1821#endif /* defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL) */
1822
1823 VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
1824 3, this, aScreenId, false);
1825 }
1826
1827 LogRelFlowFunc(("Attached to %d %RTuuid\n", aScreenId, aId.raw()));
1828 return S_OK;
1829}
1830
1831HRESULT Display::detachFramebuffer(ULONG aScreenId, const com::Guid &aId)
1832{
1833 LogRelFlowFunc(("aScreenId = %d %RTuuid\n", aScreenId, aId.raw()));
1834
1835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1836
1837 if (aScreenId >= mcMonitors)
1838 return setError(E_INVALIDARG, tr("DetachFramebuffer: Invalid screen %d (total %d)"),
1839 aScreenId, mcMonitors);
1840
1841 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1842
1843 if (pFBInfo->framebufferId != aId)
1844 {
1845 LogRelFlowFunc(("Invalid framebuffer aScreenId = %d, attached %p\n", aScreenId, pFBInfo->framebufferId.raw()));
1846 return setError(E_FAIL, tr("DetachFramebuffer: Invalid framebuffer object"));
1847 }
1848
1849 pFBInfo->pFramebuffer.setNull();
1850 pFBInfo->framebufferId.clear();
1851
1852 alock.release();
1853
1854#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1855 Console::SafeVMPtrQuiet ptrVM(mParent);
1856 if (ptrVM.isOk())
1857 {
1858 if (mfIsCr3DEnabled)
1859 {
1860 VBOXCRCMDCTL_HGCM data;
1861 RT_ZERO(data);
1862 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1863 data.Hdr.u32Function = SHCRGL_HOST_FN_SCREEN_CHANGED;
1864
1865 data.aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
1866 data.aParms[0].u.uint32 = aScreenId;
1867
1868 int vrc = i_crCtlSubmitSync(&data.Hdr, sizeof(data));
1869 AssertRC(vrc);
1870 }
1871 }
1872#endif /* defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL) */
1873
1874 return S_OK;
1875}
1876
1877HRESULT Display::queryFramebuffer(ULONG aScreenId, ComPtr<IFramebuffer> &aFramebuffer)
1878{
1879 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
1880
1881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1882
1883 if (aScreenId >= mcMonitors)
1884 return setError(E_INVALIDARG, tr("QueryFramebuffer: Invalid screen %d (total %d)"),
1885 aScreenId, mcMonitors);
1886
1887 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1888
1889 pFBInfo->pFramebuffer.queryInterfaceTo(aFramebuffer.asOutParam());
1890
1891 return S_OK;
1892}
1893
1894HRESULT Display::setVideoModeHint(ULONG aDisplay, BOOL aEnabled,
1895 BOOL aChangeOrigin, LONG aOriginX, LONG aOriginY,
1896 ULONG aWidth, ULONG aHeight, ULONG aBitsPerPixel)
1897{
1898 if (aWidth == 0 || aHeight == 0 || aBitsPerPixel == 0)
1899 {
1900 /* Some of parameters must not change. Query current mode. */
1901 ULONG ulWidth = 0;
1902 ULONG ulHeight = 0;
1903 ULONG ulBitsPerPixel = 0;
1904 HRESULT hr = getScreenResolution(aDisplay, &ulWidth, &ulHeight, &ulBitsPerPixel, NULL, NULL, NULL);
1905 if (FAILED(hr))
1906 return hr;
1907
1908 /* Assign current values to not changing parameters. */
1909 if (aWidth == 0)
1910 aWidth = ulWidth;
1911 if (aHeight == 0)
1912 aHeight = ulHeight;
1913 if (aBitsPerPixel == 0)
1914 aBitsPerPixel = ulBitsPerPixel;
1915 }
1916
1917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1918
1919 if (aDisplay >= mcMonitors)
1920 return E_INVALIDARG;
1921
1922 CHECK_CONSOLE_DRV(mpDrv);
1923
1924 /*
1925 * It is up to the guest to decide whether the hint is
1926 * valid. Therefore don't do any VRAM sanity checks here.
1927 */
1928
1929 /* Have to release the lock because the pfnRequestDisplayChange
1930 * will call EMT. */
1931 alock.release();
1932
1933 /* We always send the hint to the graphics card in case the guest enables
1934 * support later. For now we notify exactly when support is enabled. */
1935 mpDrv->pUpPort->pfnSendModeHint(mpDrv->pUpPort, aWidth, aHeight,
1936 aBitsPerPixel, aDisplay,
1937 aChangeOrigin ? aOriginX : ~0,
1938 aChangeOrigin ? aOriginY : ~0,
1939 RT_BOOL(aEnabled),
1940 mfGuestVBVACapabilities
1941 & VBVACAPS_VIDEO_MODE_HINTS);
1942 if ( mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS
1943 && !(mfGuestVBVACapabilities & VBVACAPS_IRQ))
1944 {
1945 mParent->i_sendACPIMonitorHotPlugEvent();
1946 }
1947
1948 /* We currently never suppress the VMMDev hint if the guest has requested
1949 * it. Specifically the video graphics driver may not be responsible for
1950 * screen positioning in the guest virtual desktop, and the component
1951 * responsible may want to get the hint from VMMDev. */
1952 VMMDev *pVMMDev = mParent->i_getVMMDev();
1953 if (pVMMDev)
1954 {
1955 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
1956 if (pVMMDevPort)
1957 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aBitsPerPixel,
1958 aDisplay, aOriginX, aOriginY,
1959 RT_BOOL(aEnabled), RT_BOOL(aChangeOrigin));
1960 }
1961 return S_OK;
1962}
1963
1964HRESULT Display::setSeamlessMode(BOOL enabled)
1965{
1966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1967
1968 /* Have to release the lock because the pfnRequestSeamlessChange will call EMT. */
1969 alock.release();
1970
1971 VMMDev *pVMMDev = mParent->i_getVMMDev();
1972 if (pVMMDev)
1973 {
1974 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
1975 if (pVMMDevPort)
1976 pVMMDevPort->pfnRequestSeamlessChange(pVMMDevPort, !!enabled);
1977 }
1978 mfSeamlessEnabled = RT_BOOL(enabled);
1979
1980#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1981 if (!enabled)
1982 {
1983 VMMDev *vmmDev = mParent->i_getVMMDev();
1984 if (mfIsCr3DEnabled && vmmDev)
1985 {
1986 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM*)RTMemAlloc(sizeof(VBOXCRCMDCTL_HGCM));
1987 if (!pData)
1988 {
1989 AssertMsgFailed(("RTMemAlloc failed\n"));
1990 return VERR_NO_MEMORY;
1991 }
1992
1993 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1994 pData->Hdr.u32Function = SHCRGL_HOST_FN_SET_VISIBLE_REGION;
1995
1996 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
1997 pData->aParms[0].u.pointer.addr = NULL;
1998 pData->aParms[0].u.pointer.size = 0; /* <- means null rects, NULL pRects address and 0 rects means "disable" */
1999
2000 int rc = i_crCtlSubmit(&pData->Hdr, sizeof(*pData), i_displayCrCmdFree, pData);
2001 if (!RT_SUCCESS(rc))
2002 {
2003 AssertMsgFailed(("crCtlSubmit failed (rc=%Rrc)\n", rc));
2004 RTMemFree(pData);
2005 }
2006 }
2007 }
2008#endif
2009 return S_OK;
2010}
2011
2012#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2013BOOL Display::i_displayCheckTakeScreenshotCrOgl(Display *pDisplay, ULONG aScreenId, uint8_t *pbData,
2014 uint32_t u32Width, uint32_t u32Height)
2015{
2016 if ( pDisplay->mfIsCr3DEnabled
2017 && pDisplay->mCrOglCallbacks.pfnHasData
2018 && pDisplay->mCrOglCallbacks.pfnHasData())
2019 {
2020 VMMDev *pVMMDev = pDisplay->mParent->i_getVMMDev();
2021 if (pVMMDev)
2022 {
2023 CRVBOXHGCMTAKESCREENSHOT *pScreenshot = (CRVBOXHGCMTAKESCREENSHOT *)RTMemAlloc(sizeof(*pScreenshot));
2024 if (pScreenshot)
2025 {
2026 /* screen id or CRSCREEN_ALL to specify all enabled */
2027 pScreenshot->u32Screen = aScreenId;
2028 pScreenshot->u32Width = u32Width;
2029 pScreenshot->u32Height = u32Height;
2030 pScreenshot->u32Pitch = u32Width * 4;
2031 pScreenshot->pvBuffer = pbData;
2032 pScreenshot->pvContext = NULL;
2033 pScreenshot->pfnScreenshotBegin = NULL;
2034 pScreenshot->pfnScreenshotPerform = NULL;
2035 pScreenshot->pfnScreenshotEnd = NULL;
2036
2037 VBOXCRCMDCTL_HGCM data;
2038 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
2039 data.Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
2040
2041 data.aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
2042 data.aParms[0].u.pointer.addr = pScreenshot;
2043 data.aParms[0].u.pointer.size = sizeof(*pScreenshot);
2044
2045 int rc = pDisplay->i_crCtlSubmitSync(&data.Hdr, sizeof(data));
2046
2047 RTMemFree(pScreenshot);
2048
2049 if (RT_SUCCESS(rc))
2050 return TRUE;
2051 AssertMsgFailed(("failed to get screenshot data from crOgl (rc=%Rrc)\n", rc));
2052 /* fall back to the non-3d mechanism */
2053 }
2054 }
2055 }
2056 return FALSE;
2057}
2058#endif
2059
2060/* static */
2061int Display::i_displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_t **ppbData, size_t *pcbData,
2062 uint32_t *pcx, uint32_t *pcy, bool *pfMemFree)
2063{
2064 int rc;
2065 if ( aScreenId == VBOX_VIDEO_PRIMARY_SCREEN
2066 && pDisplay->maFramebuffers[aScreenId].fVBVAEnabled == false) /* A non-VBVA mode. */
2067 {
2068 if (pDisplay->mpDrv)
2069 {
2070 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppbData, pcbData, pcx, pcy);
2071 *pfMemFree = false;
2072 }
2073 else
2074 {
2075 /* No image. */
2076 *ppbData = NULL;
2077 *pcbData = 0;
2078 *pcx = 0;
2079 *pcy = 0;
2080 *pfMemFree = true;
2081 rc = VINF_SUCCESS;
2082 }
2083 }
2084 else if (aScreenId < pDisplay->mcMonitors)
2085 {
2086 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2087
2088 uint32_t width = pFBInfo->w;
2089 uint32_t height = pFBInfo->h;
2090
2091 /* Allocate 32 bit per pixel bitmap. */
2092 size_t cbRequired = width * 4 * height;
2093
2094 if (cbRequired)
2095 {
2096 uint8_t *pbDst = (uint8_t *)RTMemAlloc(cbRequired);
2097 if (pbDst != NULL)
2098 {
2099 if (pFBInfo->flags & VBVA_SCREEN_F_ACTIVE)
2100 {
2101 /* Copy guest VRAM to the allocated 32bpp buffer. */
2102 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
2103 int32_t xSrc = 0;
2104 int32_t ySrc = 0;
2105 uint32_t u32SrcWidth = width;
2106 uint32_t u32SrcHeight = height;
2107 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
2108 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2109
2110 int32_t xDst = 0;
2111 int32_t yDst = 0;
2112 uint32_t u32DstWidth = u32SrcWidth;
2113 uint32_t u32DstHeight = u32SrcHeight;
2114 uint32_t u32DstLineSize = u32DstWidth * 4;
2115 uint32_t u32DstBitsPerPixel = 32;
2116
2117 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2118 width, height,
2119 pu8Src,
2120 xSrc, ySrc,
2121 u32SrcWidth, u32SrcHeight,
2122 u32SrcLineSize, u32SrcBitsPerPixel,
2123 pbDst,
2124 xDst, yDst,
2125 u32DstWidth, u32DstHeight,
2126 u32DstLineSize, u32DstBitsPerPixel);
2127 }
2128 else
2129 {
2130 memset(pbDst, 0, cbRequired);
2131 rc = VINF_SUCCESS;
2132 }
2133 if (RT_SUCCESS(rc))
2134 {
2135 *ppbData = pbDst;
2136 *pcbData = cbRequired;
2137 *pcx = width;
2138 *pcy = height;
2139 *pfMemFree = true;
2140 }
2141 else
2142 {
2143 RTMemFree(pbDst);
2144
2145 /* CopyRect can fail if VBVA was paused in VGA device, retry using the generic method. */
2146 if ( rc == VERR_INVALID_STATE
2147 && aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2148 {
2149 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppbData, pcbData, pcx, pcy);
2150 *pfMemFree = false;
2151 }
2152 }
2153 }
2154 else
2155 rc = VERR_NO_MEMORY;
2156 }
2157 else
2158 {
2159 /* No image. */
2160 *ppbData = NULL;
2161 *pcbData = 0;
2162 *pcx = 0;
2163 *pcy = 0;
2164 *pfMemFree = true;
2165 rc = VINF_SUCCESS;
2166 }
2167 }
2168 else
2169 rc = VERR_INVALID_PARAMETER;
2170 return rc;
2171}
2172
2173static int i_displayTakeScreenshot(PUVM pUVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, ULONG aScreenId,
2174 BYTE *address, ULONG width, ULONG height)
2175{
2176#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2177 /*
2178 * CrOgl screenshot hook/hack.
2179 */
2180 if (Display::i_displayCheckTakeScreenshotCrOgl(pDisplay, aScreenId, (uint8_t *)address, width, height))
2181 return VINF_SUCCESS;
2182#endif
2183
2184 uint8_t *pbData = NULL;
2185 size_t cbData = 0;
2186 uint32_t cx = 0;
2187 uint32_t cy = 0;
2188 bool fFreeMem = false;
2189 int vrc = VINF_SUCCESS;
2190
2191 int cRetries = 5;
2192 while (cRetries-- > 0)
2193 {
2194 /* Note! Not sure if the priority call is such a good idea here, but
2195 it would be nice to have an accurate screenshot for the bug
2196 report if the VM deadlocks. */
2197 vrc = VMR3ReqPriorityCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)Display::i_displayTakeScreenshotEMT, 7,
2198 pDisplay, aScreenId, &pbData, &cbData, &cx, &cy, &fFreeMem);
2199 if (vrc != VERR_TRY_AGAIN)
2200 {
2201 break;
2202 }
2203
2204 RTThreadSleep(10);
2205 }
2206
2207 if (RT_SUCCESS(vrc) && pbData)
2208 {
2209 if (cx == width && cy == height)
2210 {
2211 /* No scaling required. */
2212 memcpy(address, pbData, cbData);
2213 }
2214 else
2215 {
2216 /* Scale. */
2217 LogRelFlowFunc(("SCALE: %dx%d -> %dx%d\n", cx, cy, width, height));
2218
2219 uint8_t *dst = address;
2220 uint8_t *src = pbData;
2221 int dstW = width;
2222 int dstH = height;
2223 int srcW = cx;
2224 int srcH = cy;
2225 int iDeltaLine = cx * 4;
2226
2227 BitmapScale32(dst,
2228 dstW, dstH,
2229 src,
2230 iDeltaLine,
2231 srcW, srcH);
2232 }
2233
2234 if (fFreeMem)
2235 RTMemFree(pbData);
2236 else
2237 {
2238 /* This can be called from any thread. */
2239 pDrv->pUpPort->pfnFreeScreenshot(pDrv->pUpPort, pbData);
2240 }
2241 }
2242
2243 return vrc;
2244}
2245
2246HRESULT Display::takeScreenShotWorker(ULONG aScreenId,
2247 BYTE *aAddress,
2248 ULONG aWidth,
2249 ULONG aHeight,
2250 BitmapFormat_T aBitmapFormat,
2251 ULONG *pcbOut)
2252{
2253 HRESULT rc = S_OK;
2254
2255 /* Do not allow too small and too large screenshots. This also filters out negative
2256 * values passed as either 'aWidth' or 'aHeight'.
2257 */
2258 CheckComArgExpr(aWidth, aWidth != 0 && aWidth <= 32767);
2259 CheckComArgExpr(aHeight, aHeight != 0 && aHeight <= 32767);
2260
2261 if ( aBitmapFormat != BitmapFormat_BGR0
2262 && aBitmapFormat != BitmapFormat_BGRA
2263 && aBitmapFormat != BitmapFormat_RGBA
2264 && aBitmapFormat != BitmapFormat_PNG)
2265 {
2266 return setError(E_NOTIMPL,
2267 tr("Unsupported screenshot format 0x%08X"), aBitmapFormat);
2268 }
2269
2270 Console::SafeVMPtr ptrVM(mParent);
2271 if (!ptrVM.isOk())
2272 return ptrVM.rc();
2273
2274 int vrc = i_displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, aAddress, aWidth, aHeight);
2275
2276 if (RT_SUCCESS(vrc))
2277 {
2278 const size_t cbData = aWidth * 4 * aHeight;
2279
2280 /* Most of uncompressed formats. */
2281 *pcbOut = (ULONG)cbData;
2282
2283 if (aBitmapFormat == BitmapFormat_BGR0)
2284 {
2285 /* Do nothing. */
2286 }
2287 else if (aBitmapFormat == BitmapFormat_BGRA)
2288 {
2289 uint32_t *pu32 = (uint32_t *)aAddress;
2290 size_t cPixels = aWidth * aHeight;
2291 while (cPixels--)
2292 {
2293 *pu32++ |= UINT32_C(0xFF000000);
2294 }
2295 }
2296 else if (aBitmapFormat == BitmapFormat_RGBA)
2297 {
2298 uint8_t *pu8 = aAddress;
2299 size_t cPixels = aWidth * aHeight;
2300 while (cPixels--)
2301 {
2302 uint8_t u8 = pu8[0];
2303 pu8[0] = pu8[2];
2304 pu8[2] = u8;
2305 pu8[3] = 0xFF;
2306
2307 pu8 += 4;
2308 }
2309 }
2310 else if (aBitmapFormat == BitmapFormat_PNG)
2311 {
2312 uint8_t *pu8PNG = NULL;
2313 uint32_t cbPNG = 0;
2314 uint32_t cxPNG = 0;
2315 uint32_t cyPNG = 0;
2316
2317 vrc = DisplayMakePNG(aAddress, aWidth, aHeight, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
2318 if (RT_SUCCESS(vrc))
2319 {
2320 if (cbPNG <= cbData)
2321 {
2322 memcpy(aAddress, pu8PNG, cbPNG);
2323 *pcbOut = cbPNG;
2324 }
2325 else
2326 {
2327 rc = setError(E_FAIL,
2328 tr("PNG is larger than 32bpp bitmap"));
2329 }
2330 }
2331 else
2332 {
2333 rc = setError(VBOX_E_IPRT_ERROR,
2334 tr("Could not convert screenshot to PNG (%Rrc)"), vrc);
2335 }
2336 RTMemFree(pu8PNG);
2337 }
2338 }
2339 else if (vrc == VERR_TRY_AGAIN)
2340 rc = setError(E_UNEXPECTED,
2341 tr("Screenshot is not available at this time"));
2342 else if (RT_FAILURE(vrc))
2343 rc = setError(VBOX_E_IPRT_ERROR,
2344 tr("Could not take a screenshot (%Rrc)"), vrc);
2345
2346 return rc;
2347}
2348
2349HRESULT Display::takeScreenShot(ULONG aScreenId,
2350 BYTE *aAddress,
2351 ULONG aWidth,
2352 ULONG aHeight,
2353 BitmapFormat_T aBitmapFormat)
2354{
2355 HRESULT rc = S_OK;
2356
2357 LogRelFlowFunc(("[%d] address=%p, width=%d, height=%d, format 0x%08X\n",
2358 aScreenId, aAddress, aWidth, aHeight, aBitmapFormat));
2359
2360 ULONG cbOut = 0;
2361 rc = takeScreenShotWorker(aScreenId, aAddress, aWidth, aHeight, aBitmapFormat, &cbOut);
2362 NOREF(cbOut);
2363
2364 LogRelFlowFunc(("%Rhrc\n", rc));
2365 return rc;
2366}
2367
2368HRESULT Display::takeScreenShotToArray(ULONG aScreenId,
2369 ULONG aWidth,
2370 ULONG aHeight,
2371 BitmapFormat_T aBitmapFormat,
2372 std::vector<BYTE> &aScreenData)
2373{
2374 HRESULT rc = S_OK;
2375
2376 LogRelFlowFunc(("[%d] width=%d, height=%d, format 0x%08X\n",
2377 aScreenId, aWidth, aHeight, aBitmapFormat));
2378
2379 /* Do not allow too small and too large screenshots. This also filters out negative
2380 * values passed as either 'aWidth' or 'aHeight'.
2381 */
2382 CheckComArgExpr(aWidth, aWidth != 0 && aWidth <= 32767);
2383 CheckComArgExpr(aHeight, aHeight != 0 && aHeight <= 32767);
2384
2385 const size_t cbData = aWidth * 4 * aHeight;
2386 aScreenData.resize(cbData);
2387
2388 ULONG cbOut = 0;
2389 rc = takeScreenShotWorker(aScreenId, &aScreenData.front(), aWidth, aHeight, aBitmapFormat, &cbOut);
2390 if (FAILED(rc))
2391 cbOut = 0;
2392
2393 aScreenData.resize(cbOut);
2394
2395 LogRelFlowFunc(("%Rhrc\n", rc));
2396 return rc;
2397}
2398
2399#ifdef VBOX_WITH_VIDEOREC
2400VIDEORECFEATURES Display::i_videoCaptureGetEnabled(void)
2401{
2402 return VideoRecGetEnabled(&mVideoRecCfg);
2403}
2404
2405bool Display::i_videoCaptureStarted(void)
2406{
2407 return VideoRecIsActive(mpVideoRecCtx);
2408}
2409
2410int Display::i_videoCaptureInvalidate(void)
2411{
2412 AssertPtr(mParent);
2413 ComPtr<IMachine> pMachine = mParent->i_machine();
2414 Assert(pMachine.isNotNull());
2415
2416 mVideoRecCfg.fEnabled = true;
2417 mVideoRecCfg.enmDst = VIDEORECDEST_FILE; /** @todo Make this configurable once we have more variations. */
2418
2419 /*
2420 * Get parameters from API.
2421 */
2422 com::SafeArray<BOOL> aScreens;
2423 HRESULT rc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(aScreens));
2424 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2425
2426 mVideoRecCfg.aScreens.resize(aScreens.size());
2427 for (size_t i = 0; i < aScreens.size(); ++i)
2428 mVideoRecCfg.aScreens[i] = aScreens[i];
2429
2430 rc = pMachine->COMGETTER(VideoCaptureWidth)((ULONG *)&mVideoRecCfg.Video.uWidth);
2431 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2432 rc = pMachine->COMGETTER(VideoCaptureHeight)((ULONG *)&mVideoRecCfg.Video.uHeight);
2433 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2434 rc = pMachine->COMGETTER(VideoCaptureRate)((ULONG *)&mVideoRecCfg.Video.uRate);
2435 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2436 rc = pMachine->COMGETTER(VideoCaptureFPS)((ULONG *)&mVideoRecCfg.Video.uFPS);
2437 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2438 rc = pMachine->COMGETTER(VideoCaptureFile)(&mVideoRecCfg.File.strFile);
2439 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2440 rc = pMachine->COMGETTER(VideoCaptureMaxFileSize)((ULONG *)&mVideoRecCfg.File.uMaxSizeMB);
2441 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2442 rc = pMachine->COMGETTER(VideoCaptureMaxTime)((ULONG *)&mVideoRecCfg.uMaxTimeS);
2443 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2444 BSTR bstrOptions;
2445 rc = pMachine->COMGETTER(VideoCaptureOptions)(&bstrOptions);
2446 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2447
2448 /*
2449 * Parse options string (from API).
2450 */
2451 com::Utf8Str strOptions(bstrOptions);
2452 size_t pos = 0;
2453
2454 /* By default we only enable video recording for now.
2455 * Audio support is considered as being experimental. There be dragons! */
2456 mVideoRecCfg.Video.fEnabled = true;
2457#ifdef VBOX_WITH_AUDIO_VIDEOREC
2458 mVideoRecCfg.Audio.fEnabled = false;
2459 /* By default we use 48kHz, 16-bit, stereo for the audio track. */
2460 mVideoRecCfg.Audio.uHz = 48000;
2461 mVideoRecCfg.Audio.cBits = 16;
2462 mVideoRecCfg.Audio.cChannels = 2;
2463#endif
2464
2465 if (!mVideoRecCfg.Video.uFPS) /* Prevent division by zero. */
2466 mVideoRecCfg.Video.uFPS = 30;
2467
2468 /*
2469 * Parse options string.
2470 */
2471 com::Utf8Str key, value;
2472 while ((pos = strOptions.parseKeyValue(key, value, pos)) != com::Utf8Str::npos)
2473 {
2474 if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0)
2475 {
2476#ifdef VBOX_WITH_LIBVPX
2477 if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0)
2478 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = VPX_DL_REALTIME;
2479 else if (value.compare("good", Utf8Str::CaseInsensitive) == 0)
2480 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = 1000000 / mVideoRecCfg.Video.uFPS;
2481 else if (value.compare("best", Utf8Str::CaseInsensitive) == 0)
2482 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = VPX_DL_BEST_QUALITY;
2483 else
2484 {
2485 LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str()));
2486 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = value.toUInt32();
2487#endif
2488 }
2489 }
2490 else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
2491 {
2492 if (value.compare("false", Utf8Str::CaseInsensitive) == 0)
2493 {
2494 mVideoRecCfg.Video.fEnabled = false;
2495 LogRel(("VideoRec: Only audio will be recorded\n"));
2496 }
2497 }
2498 else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
2499 {
2500#ifdef VBOX_WITH_AUDIO_VIDEOREC
2501 if (value.compare("false", Utf8Str::CaseInsensitive))
2502 {
2503 mVideoRecCfg.Audio.fEnabled = false;
2504 LogRel(("VideoRec: Only video will be recorded\n"));
2505 }
2506#endif
2507 }
2508 else if (key.compare("ac_profile", Utf8Str::CaseInsensitive) == 0)
2509 {
2510#ifdef VBOX_WITH_AUDIO_VIDEOREC
2511 if (value.compare("low", Utf8Str::CaseInsensitive) == 0)
2512 {
2513 mVideoRecCfg.Audio.uHz = 8000;
2514 mVideoRecCfg.Audio.cBits = 16;
2515 mVideoRecCfg.Audio.cChannels = 1;
2516 }
2517 else if (value.startsWith("med" /* "med[ium]" */, Utf8Str::CaseInsensitive) == 0)
2518 {
2519 mVideoRecCfg.Audio.uHz = 22050;
2520 mVideoRecCfg.Audio.cBits = 16;
2521 mVideoRecCfg.Audio.cChannels = 2;
2522 }
2523 else if (value.compare("high", Utf8Str::CaseInsensitive) == 0)
2524 {
2525 /* Stay with the default set above. */
2526 }
2527#endif
2528 }
2529 else
2530 LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str()));
2531
2532 } /* while */
2533
2534 /*
2535 * Invalidate screens.
2536 */
2537 for (unsigned i = 0; i < mVideoRecCfg.aScreens.size(); i++)
2538 {
2539 bool fChanged = maVideoRecEnabled[i] != RT_BOOL(mVideoRecCfg.aScreens[i]);
2540
2541 maVideoRecEnabled[i] = RT_BOOL(mVideoRecCfg.aScreens[i]);
2542
2543 if (fChanged && i < mcMonitors)
2544 i_videoCaptureScreenChanged(i);
2545
2546 }
2547
2548 return S_OK;
2549}
2550
2551/**
2552 * Sends belonging audio samples to the video capturing code.
2553 * Does nothing if capturing is disabled or if audio support for video capturing is disabled.
2554 *
2555 * @returns IPRT status code.
2556 * @param pvData Audio data.
2557 * @param cbData Size (in bytes) of audio data.
2558 * @param uTimestampMs Timestamp (in ms) of the audio data.
2559 */
2560int Display::i_videoCaptureSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs)
2561{
2562 if ( VideoRecIsActive(mpVideoRecCtx)
2563 && VideoRecGetEnabled(&mVideoRecCfg) & VIDEORECFEATURE_AUDIO)
2564 {
2565 return VideoRecSendAudioFrame(mpVideoRecCtx, pvData, cbData, uTimestampMs);
2566 }
2567
2568 return VINF_SUCCESS;
2569}
2570
2571/**
2572 * Start video capturing. Does nothing if capturing is already active.
2573 *
2574 * @param pVideoRecCfg Video recording configuration to use.
2575 * @returns IPRT status code.
2576 */
2577int Display::i_videoCaptureStart(void)
2578{
2579 if (VideoRecIsActive(mpVideoRecCtx))
2580 return VINF_SUCCESS;
2581
2582 int rc = VideoRecContextCreate(mcMonitors, &mVideoRecCfg, &mpVideoRecCtx);
2583 if (RT_FAILURE(rc))
2584 {
2585 LogFlow(("Failed to create video recording context (%Rrc)!\n", rc));
2586 return rc;
2587 }
2588
2589 for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
2590 {
2591 int rc2 = VideoRecStreamInit(mpVideoRecCtx, uScreen);
2592 if (RT_SUCCESS(rc2))
2593 {
2594 i_videoCaptureScreenChanged(uScreen);
2595 }
2596 else
2597 LogRel(("Display::VideoCaptureStart: Failed to initialize video recording context #%u (%Rrc)\n", uScreen, rc2));
2598
2599 if (RT_SUCCESS(rc))
2600 rc = rc2;
2601 }
2602 return rc;
2603}
2604
2605/**
2606 * Stop video capturing. Does nothing if video capturing is not active.
2607 */
2608void Display::i_videoCaptureStop(void)
2609{
2610 if (!VideoRecIsActive(mpVideoRecCtx))
2611 return;
2612
2613 VideoRecContextDestroy(mpVideoRecCtx);
2614 mpVideoRecCtx = NULL;
2615
2616 unsigned uScreenId;
2617 for (uScreenId = 0; uScreenId < mcMonitors; ++uScreenId)
2618 i_videoCaptureScreenChanged(uScreenId);
2619}
2620
2621void Display::i_videoCaptureScreenChanged(unsigned uScreenId)
2622{
2623 if ( !VideoRecIsActive(mpVideoRecCtx)
2624 || !maVideoRecEnabled[uScreenId])
2625 {
2626 /* Skip recording this screen. */
2627 return;
2628 }
2629
2630 /* Get a new source bitmap which will be used by video capture code. */
2631 ComPtr<IDisplaySourceBitmap> pSourceBitmap;
2632 QuerySourceBitmap(uScreenId, pSourceBitmap.asOutParam());
2633
2634 int rc2 = RTCritSectEnter(&mVideoCaptureLock);
2635 if (RT_SUCCESS(rc2))
2636 {
2637 maFramebuffers[uScreenId].videoCapture.pSourceBitmap = pSourceBitmap;
2638
2639 rc2 = RTCritSectLeave(&mVideoCaptureLock);
2640 AssertRC(rc2);
2641 }
2642}
2643#endif /* VBOX_WITH_VIDEOREC */
2644
2645int Display::i_drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address,
2646 ULONG x, ULONG y, ULONG width, ULONG height)
2647{
2648 int rc = VINF_SUCCESS;
2649
2650 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2651
2652 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2653 {
2654 rc = pDisplay->mpDrv->pUpPort->pfnDisplayBlt(pDisplay->mpDrv->pUpPort, address, x, y, width, height);
2655 }
2656 else if (aScreenId < pDisplay->mcMonitors)
2657 {
2658 /* Copy the bitmap to the guest VRAM. */
2659 const uint8_t *pu8Src = address;
2660 int32_t xSrc = 0;
2661 int32_t ySrc = 0;
2662 uint32_t u32SrcWidth = width;
2663 uint32_t u32SrcHeight = height;
2664 uint32_t u32SrcLineSize = width * 4;
2665 uint32_t u32SrcBitsPerPixel = 32;
2666
2667 uint8_t *pu8Dst = pFBInfo->pu8FramebufferVRAM;
2668 int32_t xDst = x;
2669 int32_t yDst = y;
2670 uint32_t u32DstWidth = pFBInfo->w;
2671 uint32_t u32DstHeight = pFBInfo->h;
2672 uint32_t u32DstLineSize = pFBInfo->u32LineSize;
2673 uint32_t u32DstBitsPerPixel = pFBInfo->u16BitsPerPixel;
2674
2675 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2676 width, height,
2677 pu8Src,
2678 xSrc, ySrc,
2679 u32SrcWidth, u32SrcHeight,
2680 u32SrcLineSize, u32SrcBitsPerPixel,
2681 pu8Dst,
2682 xDst, yDst,
2683 u32DstWidth, u32DstHeight,
2684 u32DstLineSize, u32DstBitsPerPixel);
2685 if (RT_SUCCESS(rc))
2686 {
2687 if (!pFBInfo->pSourceBitmap.isNull())
2688 {
2689 /* Update the changed screen area. When source bitmap uses VRAM directly, just notify
2690 * frontend to update. And for default format, render the guest VRAM to the source bitmap.
2691 */
2692 if ( pFBInfo->fDefaultFormat
2693 && !pFBInfo->fDisabled)
2694 {
2695 BYTE *pAddress = NULL;
2696 ULONG ulWidth = 0;
2697 ULONG ulHeight = 0;
2698 ULONG ulBitsPerPixel = 0;
2699 ULONG ulBytesPerLine = 0;
2700 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2701
2702 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
2703 &ulWidth,
2704 &ulHeight,
2705 &ulBitsPerPixel,
2706 &ulBytesPerLine,
2707 &bitmapFormat);
2708 if (SUCCEEDED(hrc))
2709 {
2710 pu8Src = pFBInfo->pu8FramebufferVRAM;
2711 xSrc = x;
2712 ySrc = y;
2713 u32SrcWidth = pFBInfo->w;
2714 u32SrcHeight = pFBInfo->h;
2715 u32SrcLineSize = pFBInfo->u32LineSize;
2716 u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2717
2718 /* Default format is 32 bpp. */
2719 pu8Dst = pAddress;
2720 xDst = xSrc;
2721 yDst = ySrc;
2722 u32DstWidth = u32SrcWidth;
2723 u32DstHeight = u32SrcHeight;
2724 u32DstLineSize = u32DstWidth * 4;
2725 u32DstBitsPerPixel = 32;
2726
2727 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2728 width, height,
2729 pu8Src,
2730 xSrc, ySrc,
2731 u32SrcWidth, u32SrcHeight,
2732 u32SrcLineSize, u32SrcBitsPerPixel,
2733 pu8Dst,
2734 xDst, yDst,
2735 u32DstWidth, u32DstHeight,
2736 u32DstLineSize, u32DstBitsPerPixel);
2737 }
2738 }
2739 }
2740
2741 pDisplay->i_handleDisplayUpdate(aScreenId, x, y, width, height);
2742 }
2743 }
2744 else
2745 {
2746 rc = VERR_INVALID_PARAMETER;
2747 }
2748
2749 if (RT_SUCCESS(rc))
2750 pDisplay->mParent->i_consoleVRDPServer()->SendUpdateBitmap(aScreenId, x, y, width, height);
2751
2752 return rc;
2753}
2754
2755HRESULT Display::drawToScreen(ULONG aScreenId, BYTE *aAddress, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
2756{
2757 /// @todo (r=dmik) this function may take too long to complete if the VM
2758 // is doing something like saving state right now. Which, in case if it
2759 // is called on the GUI thread, will make it unresponsive. We should
2760 // check the machine state here (by enclosing the check and VMRequCall
2761 // within the Console lock to make it atomic).
2762
2763 LogRelFlowFunc(("aAddress=%p, x=%d, y=%d, width=%d, height=%d\n",
2764 (void *)aAddress, aX, aY, aWidth, aHeight));
2765
2766 CheckComArgExpr(aWidth, aWidth != 0);
2767 CheckComArgExpr(aHeight, aHeight != 0);
2768
2769 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2770
2771 CHECK_CONSOLE_DRV(mpDrv);
2772
2773 Console::SafeVMPtr ptrVM(mParent);
2774 if (!ptrVM.isOk())
2775 return ptrVM.rc();
2776
2777 /* Release lock because the call scheduled on EMT may also try to take it. */
2778 alock.release();
2779
2780 /*
2781 * Again we're lazy and make the graphics device do all the
2782 * dirty conversion work.
2783 */
2784 int rcVBox = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_drawToScreenEMT, 7,
2785 this, aScreenId, aAddress, aX, aY, aWidth, aHeight);
2786
2787 /*
2788 * If the function returns not supported, we'll have to do all the
2789 * work ourselves using the framebuffer.
2790 */
2791 HRESULT rc = S_OK;
2792 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
2793 {
2794 /** @todo implement generic fallback for screen blitting. */
2795 rc = E_NOTIMPL;
2796 }
2797 else if (RT_FAILURE(rcVBox))
2798 rc = setError(VBOX_E_IPRT_ERROR,
2799 tr("Could not draw to the screen (%Rrc)"), rcVBox);
2800/// @todo
2801// else
2802// {
2803// /* All ok. Redraw the screen. */
2804// handleDisplayUpdate(x, y, width, height);
2805// }
2806
2807 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2808 return rc;
2809}
2810
2811int Display::i_InvalidateAndUpdateEMT(Display *pDisplay, unsigned uId, bool fUpdateAll)
2812{
2813 LogRelFlowFunc(("uId=%d, fUpdateAll %d\n", uId, fUpdateAll));
2814
2815 unsigned uScreenId;
2816 for (uScreenId = (fUpdateAll ? 0 : uId); uScreenId < pDisplay->mcMonitors; uScreenId++)
2817 {
2818 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2819
2820 if ( !pFBInfo->fVBVAEnabled
2821 && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2822 {
2823 pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort, /* fFailOnResize = */ true);
2824 }
2825 else
2826 {
2827 if (!pFBInfo->fDisabled)
2828 {
2829 /* Render complete VRAM screen to the framebuffer.
2830 * When framebuffer uses VRAM directly, just notify it to update.
2831 */
2832 if (pFBInfo->fDefaultFormat && !pFBInfo->pSourceBitmap.isNull())
2833 {
2834 BYTE *pAddress = NULL;
2835 ULONG ulWidth = 0;
2836 ULONG ulHeight = 0;
2837 ULONG ulBitsPerPixel = 0;
2838 ULONG ulBytesPerLine = 0;
2839 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2840
2841 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
2842 &ulWidth,
2843 &ulHeight,
2844 &ulBitsPerPixel,
2845 &ulBytesPerLine,
2846 &bitmapFormat);
2847 if (SUCCEEDED(hrc))
2848 {
2849 uint32_t width = pFBInfo->w;
2850 uint32_t height = pFBInfo->h;
2851
2852 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
2853 int32_t xSrc = 0;
2854 int32_t ySrc = 0;
2855 uint32_t u32SrcWidth = pFBInfo->w;
2856 uint32_t u32SrcHeight = pFBInfo->h;
2857 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
2858 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2859
2860 /* Default format is 32 bpp. */
2861 uint8_t *pu8Dst = pAddress;
2862 int32_t xDst = xSrc;
2863 int32_t yDst = ySrc;
2864 uint32_t u32DstWidth = u32SrcWidth;
2865 uint32_t u32DstHeight = u32SrcHeight;
2866 uint32_t u32DstLineSize = u32DstWidth * 4;
2867 uint32_t u32DstBitsPerPixel = 32;
2868
2869 /* if uWidth != pFBInfo->w and uHeight != pFBInfo->h
2870 * implies resize of Framebuffer is in progress and
2871 * copyrect should not be called.
2872 */
2873 if (ulWidth == pFBInfo->w && ulHeight == pFBInfo->h)
2874 {
2875 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2876 width, height,
2877 pu8Src,
2878 xSrc, ySrc,
2879 u32SrcWidth, u32SrcHeight,
2880 u32SrcLineSize, u32SrcBitsPerPixel,
2881 pu8Dst,
2882 xDst, yDst,
2883 u32DstWidth, u32DstHeight,
2884 u32DstLineSize, u32DstBitsPerPixel);
2885 }
2886 }
2887 }
2888
2889 pDisplay->i_handleDisplayUpdate(uScreenId, 0, 0, pFBInfo->w, pFBInfo->h);
2890 }
2891 }
2892 if (!fUpdateAll)
2893 break;
2894 }
2895 LogRelFlowFunc(("done\n"));
2896 return VINF_SUCCESS;
2897}
2898
2899/**
2900 * Does a full invalidation of the VM display and instructs the VM
2901 * to update it immediately.
2902 *
2903 * @returns COM status code
2904 */
2905
2906HRESULT Display::invalidateAndUpdate()
2907{
2908 LogRelFlowFunc(("\n"));
2909
2910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2911
2912 CHECK_CONSOLE_DRV(mpDrv);
2913
2914 Console::SafeVMPtr ptrVM(mParent);
2915 if (!ptrVM.isOk())
2916 return ptrVM.rc();
2917
2918 HRESULT rc = S_OK;
2919
2920 LogRelFlowFunc(("Sending DPYUPDATE request\n"));
2921
2922 /* Have to release the lock when calling EMT. */
2923 alock.release();
2924
2925 int rcVBox = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
2926 3, this, 0, true);
2927 alock.acquire();
2928
2929 if (RT_FAILURE(rcVBox))
2930 rc = setError(VBOX_E_IPRT_ERROR,
2931 tr("Could not invalidate and update the screen (%Rrc)"), rcVBox);
2932
2933 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2934 return rc;
2935}
2936
2937HRESULT Display::invalidateAndUpdateScreen(ULONG aScreenId)
2938{
2939 LogRelFlowFunc(("\n"));
2940
2941 HRESULT rc = S_OK;
2942
2943 Console::SafeVMPtr ptrVM(mParent);
2944 if (!ptrVM.isOk())
2945 return ptrVM.rc();
2946
2947 int rcVBox = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
2948 3, this, aScreenId, false);
2949 if (RT_FAILURE(rcVBox))
2950 rc = setError(VBOX_E_IPRT_ERROR,
2951 tr("Could not invalidate and update the screen %d (%Rrc)"), aScreenId, rcVBox);
2952
2953 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2954 return rc;
2955}
2956
2957HRESULT Display::completeVHWACommand(BYTE *aCommand)
2958{
2959#ifdef VBOX_WITH_VIDEOHWACCEL
2960 mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsync(mpDrv->pVBVACallbacks, (PVBOXVHWACMD)aCommand);
2961 return S_OK;
2962#else
2963 return E_NOTIMPL;
2964#endif
2965}
2966
2967HRESULT Display::viewportChanged(ULONG aScreenId, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
2968{
2969 AssertMsgReturn(aScreenId < mcMonitors, ("aScreendId=%d mcMonitors=%d\n", aScreenId, mcMonitors), E_INVALIDARG);
2970
2971#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2972 if (mfIsCr3DEnabled)
2973 {
2974 int rc = i_crViewportNotify(aScreenId, aX, aY, aWidth, aHeight);
2975 if (RT_FAILURE(rc))
2976 {
2977 DISPLAYFBINFO *pFb = &maFramebuffers[aScreenId];
2978 pFb->pendingViewportInfo.fPending = true;
2979 pFb->pendingViewportInfo.x = aX;
2980 pFb->pendingViewportInfo.y = aY;
2981 pFb->pendingViewportInfo.width = aWidth;
2982 pFb->pendingViewportInfo.height = aHeight;
2983 }
2984 }
2985#endif /* VBOX_WITH_CROGL && VBOX_WITH_HGCM */
2986
2987 /* The driver might not have been constructed yet */
2988 if (mpDrv && mpDrv->pUpPort->pfnSetViewport)
2989 mpDrv->pUpPort->pfnSetViewport(mpDrv->pUpPort, aScreenId, aX, aY, aWidth, aHeight);
2990
2991 return S_OK;
2992}
2993
2994HRESULT Display::querySourceBitmap(ULONG aScreenId,
2995 ComPtr<IDisplaySourceBitmap> &aDisplaySourceBitmap)
2996{
2997 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2998
2999 Console::SafeVMPtr ptrVM(mParent);
3000 if (!ptrVM.isOk())
3001 return ptrVM.rc();
3002
3003 bool fSetRenderVRAM = false;
3004 bool fInvalidate = false;
3005
3006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3007
3008 if (aScreenId >= mcMonitors)
3009 return setError(E_INVALIDARG, tr("QuerySourceBitmap: Invalid screen %d (total %d)"),
3010 aScreenId, mcMonitors);
3011
3012 if (!mfSourceBitmapEnabled)
3013 {
3014 aDisplaySourceBitmap = NULL;
3015 return E_FAIL;
3016 }
3017
3018 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
3019
3020 /* No source bitmap for a blank guest screen. */
3021 if (pFBInfo->flags & VBVA_SCREEN_F_BLANK)
3022 {
3023 aDisplaySourceBitmap = NULL;
3024 return E_FAIL;
3025 }
3026
3027 HRESULT hr = S_OK;
3028
3029 if (pFBInfo->pSourceBitmap.isNull())
3030 {
3031 /* Create a new object. */
3032 ComObjPtr<DisplaySourceBitmap> obj;
3033 hr = obj.createObject();
3034 if (SUCCEEDED(hr))
3035 hr = obj->init(this, aScreenId, pFBInfo);
3036
3037 if (SUCCEEDED(hr))
3038 {
3039 pFBInfo->pSourceBitmap = obj;
3040 pFBInfo->fDefaultFormat = !obj->i_usesVRAM();
3041
3042 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3043 {
3044 /* Start buffer updates. */
3045 BYTE *pAddress = NULL;
3046 ULONG ulWidth = 0;
3047 ULONG ulHeight = 0;
3048 ULONG ulBitsPerPixel = 0;
3049 ULONG ulBytesPerLine = 0;
3050 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
3051
3052 pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
3053 &ulWidth,
3054 &ulHeight,
3055 &ulBitsPerPixel,
3056 &ulBytesPerLine,
3057 &bitmapFormat);
3058
3059 mpDrv->IConnector.pbData = pAddress;
3060 mpDrv->IConnector.cbScanline = ulBytesPerLine;
3061 mpDrv->IConnector.cBits = ulBitsPerPixel;
3062 mpDrv->IConnector.cx = ulWidth;
3063 mpDrv->IConnector.cy = ulHeight;
3064
3065 fSetRenderVRAM = pFBInfo->fDefaultFormat;
3066 }
3067
3068 /* Make sure that the bitmap contains the latest image. */
3069 fInvalidate = pFBInfo->fDefaultFormat;
3070 }
3071 }
3072
3073 if (SUCCEEDED(hr))
3074 {
3075 pFBInfo->pSourceBitmap.queryInterfaceTo(aDisplaySourceBitmap.asOutParam());
3076 }
3077
3078 /* Leave the IDisplay lock because the VGA device must not be called under it. */
3079 alock.release();
3080
3081 if (SUCCEEDED(hr))
3082 {
3083 if (fSetRenderVRAM)
3084 {
3085 mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, true);
3086 }
3087
3088 if (fInvalidate)
3089 VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
3090 3, this, aScreenId, false);
3091 }
3092
3093 LogRelFlowFunc(("%Rhrc\n", hr));
3094 return hr;
3095}
3096
3097HRESULT Display::getGuestScreenLayout(std::vector<ComPtr<IGuestScreenInfo> > &aGuestScreenLayout)
3098{
3099 NOREF(aGuestScreenLayout);
3100 return E_NOTIMPL;
3101}
3102
3103HRESULT Display::setScreenLayout(ScreenLayoutMode_T aScreenLayoutMode,
3104 const std::vector<ComPtr<IGuestScreenInfo> > &aGuestScreenInfo)
3105{
3106 NOREF(aScreenLayoutMode);
3107 NOREF(aGuestScreenInfo);
3108 return E_NOTIMPL;
3109}
3110
3111HRESULT Display::detachScreens(const std::vector<LONG> &aScreenIds)
3112{
3113 NOREF(aScreenIds);
3114 return E_NOTIMPL;
3115}
3116
3117// wrapped IEventListener method
3118HRESULT Display::handleEvent(const ComPtr<IEvent> &aEvent)
3119{
3120 VBoxEventType_T aType = VBoxEventType_Invalid;
3121
3122 aEvent->COMGETTER(Type)(&aType);
3123 switch (aType)
3124 {
3125 case VBoxEventType_OnStateChanged:
3126 {
3127 ComPtr<IStateChangedEvent> scev = aEvent;
3128 Assert(scev);
3129 MachineState_T machineState;
3130 scev->COMGETTER(State)(&machineState);
3131 if ( machineState == MachineState_Running
3132 || machineState == MachineState_Teleporting
3133 || machineState == MachineState_LiveSnapshotting
3134 || machineState == MachineState_DeletingSnapshotOnline
3135 )
3136 {
3137 LogRelFlowFunc(("Machine is running.\n"));
3138
3139#ifdef VBOX_WITH_CROGL
3140 i_crOglWindowsShow(true);
3141#endif
3142 }
3143 else
3144 {
3145#ifdef VBOX_WITH_CROGL
3146 if (machineState == MachineState_Paused)
3147 i_crOglWindowsShow(false);
3148#endif
3149 }
3150 break;
3151 }
3152 default:
3153 AssertFailed();
3154 }
3155
3156 return S_OK;
3157}
3158
3159
3160// private methods
3161/////////////////////////////////////////////////////////////////////////////
3162
3163#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3164int Display::i_crViewportNotify(ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
3165{
3166 VMMDev *pVMMDev = mParent->i_getVMMDev();
3167 if (!pVMMDev)
3168 return VERR_INVALID_STATE;
3169
3170 size_t cbData = RT_UOFFSETOF(VBOXCRCMDCTL_HGCM, aParms[5]);
3171 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM *)alloca(cbData);
3172
3173 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3174 pData->Hdr.u32Function = SHCRGL_HOST_FN_VIEWPORT_CHANGED;
3175
3176 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
3177 pData->aParms[0].u.uint32 = aScreenId;
3178
3179 pData->aParms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
3180 pData->aParms[1].u.uint32 = x;
3181
3182 pData->aParms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
3183 pData->aParms[2].u.uint32 = y;
3184
3185 pData->aParms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
3186 pData->aParms[3].u.uint32 = width;
3187
3188 pData->aParms[4].type = VBOX_HGCM_SVC_PARM_32BIT;
3189 pData->aParms[4].u.uint32 = height;
3190
3191 return i_crCtlSubmitSyncIfHasDataForScreen(aScreenId, &pData->Hdr, (uint32_t)cbData);
3192}
3193#endif
3194
3195#ifdef VBOX_WITH_CRHGSMI
3196void Display::i_setupCrHgsmiData(void)
3197{
3198 VMMDev *pVMMDev = mParent->i_getVMMDev();
3199 Assert(pVMMDev);
3200 int rc = RTCritSectRwEnterExcl(&mCrOglLock);
3201 AssertRC(rc);
3202
3203 if (pVMMDev)
3204 rc = pVMMDev->hgcmHostSvcHandleCreate("VBoxSharedCrOpenGL", &mhCrOglSvc);
3205 else
3206 rc = VERR_GENERAL_FAILURE;
3207
3208 if (RT_SUCCESS(rc))
3209 {
3210 Assert(mhCrOglSvc);
3211 /* setup command completion callback */
3212 VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB Completion;
3213 Completion.Hdr.enmType = VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB;
3214 Completion.Hdr.cbCmd = sizeof(Completion);
3215 Completion.hCompletion = mpDrv->pVBVACallbacks;
3216 Completion.pfnCompletion = mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync;
3217
3218 VBOXHGCMSVCPARM parm;
3219 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3220 parm.u.pointer.addr = &Completion;
3221 parm.u.pointer.size = 0;
3222
3223 rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_CRHGSMI_CTL, 1, &parm);
3224 if (RT_SUCCESS(rc))
3225 mCrOglCallbacks = Completion.MainInterface;
3226 else
3227 AssertMsgFailed(("VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION failed (rc=%Rrc)\n", rc));
3228 }
3229
3230 if (RT_FAILURE(rc))
3231 mhCrOglSvc = NULL;
3232
3233 RTCritSectRwLeaveExcl(&mCrOglLock);
3234}
3235
3236void Display::i_destructCrHgsmiData(void)
3237{
3238 int rc = RTCritSectRwEnterExcl(&mCrOglLock);
3239 AssertRC(rc);
3240 mhCrOglSvc = NULL;
3241 RTCritSectRwLeaveExcl(&mCrOglLock);
3242}
3243#endif /* VBOX_WITH_CRHGSMI */
3244
3245/**
3246 * Handle display resize event issued by the VGA device for the primary screen.
3247 *
3248 * @see PDMIDISPLAYCONNECTOR::pfnResize
3249 */
3250DECLCALLBACK(int) Display::i_displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
3251 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
3252{
3253 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3254 Display *pThis = pDrv->pDisplay;
3255
3256 LogRelFlowFunc(("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
3257 bpp, pvVRAM, cbLine, cx, cy));
3258
3259 bool f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, true, false);
3260 if (!f)
3261 {
3262 /* This is a result of recursive call when the source bitmap is being updated
3263 * during a VGA resize. Tell the VGA device to ignore the call.
3264 *
3265 * @todo It is a workaround, actually pfnUpdateDisplayAll must
3266 * fail on resize.
3267 */
3268 LogRel(("displayResizeCallback: already processing\n"));
3269 return VINF_VGA_RESIZE_IN_PROGRESS;
3270 }
3271
3272 int rc = pThis->i_handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy, 0, 0, 0, true);
3273
3274 /* Restore the flag. */
3275 f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, false, true);
3276 AssertRelease(f);
3277
3278 return rc;
3279}
3280
3281/**
3282 * Handle display update.
3283 *
3284 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
3285 */
3286DECLCALLBACK(void) Display::i_displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
3287 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3288{
3289 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3290
3291#ifdef DEBUG_sunlover
3292 LogFlowFunc(("fVideoAccelEnabled = %d, %d,%d %dx%d\n",
3293 pDrv->pDisplay->mVideoAccelLegacy.fVideoAccelEnabled, x, y, cx, cy));
3294#endif /* DEBUG_sunlover */
3295
3296 /* This call does update regardless of VBVA status.
3297 * But in VBVA mode this is called only as result of
3298 * pfnUpdateDisplayAll in the VGA device.
3299 */
3300
3301 pDrv->pDisplay->i_handleDisplayUpdate(VBOX_VIDEO_PRIMARY_SCREEN, x, y, cx, cy);
3302}
3303
3304/**
3305 * Periodic display refresh callback.
3306 *
3307 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
3308 * @thread EMT
3309 */
3310/*static*/ DECLCALLBACK(void) Display::i_displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
3311{
3312 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3313
3314#ifdef DEBUG_sunlover_2
3315 LogFlowFunc(("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
3316 pDrv->pDisplay->mfVideoAccelEnabled));
3317#endif /* DEBUG_sunlover_2 */
3318
3319 Display *pDisplay = pDrv->pDisplay;
3320 unsigned uScreenId;
3321
3322 int rc = pDisplay->i_videoAccelRefreshProcess(pDrv->pUpPort);
3323 if (rc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
3324 {
3325 if (rc == VWRN_INVALID_STATE)
3326 {
3327 /* No VBVA do a display update. */
3328 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
3329 }
3330
3331 /* Inform the VRDP server that the current display update sequence is
3332 * completed. At this moment the framebuffer memory contains a definite
3333 * image, that is synchronized with the orders already sent to VRDP client.
3334 * The server can now process redraw requests from clients or initial
3335 * fullscreen updates for new clients.
3336 */
3337 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3338 {
3339 Assert(pDisplay->mParent && pDisplay->mParent->i_consoleVRDPServer());
3340 pDisplay->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, NULL, 0);
3341 }
3342 }
3343
3344#ifdef VBOX_WITH_VIDEOREC
3345 if ( VideoRecIsActive(pDisplay->mpVideoRecCtx)
3346 && VideoRecGetEnabled(&pDisplay->mVideoRecCfg) & VIDEORECFEATURE_VIDEO)
3347 {
3348 do {
3349# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3350 if (pDisplay->mfIsCr3DEnabled)
3351 {
3352 if (ASMAtomicCmpXchgU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_SUBMITTED, CRVREC_STATE_IDLE))
3353 {
3354 if ( pDisplay->mCrOglCallbacks.pfnHasData
3355 && pDisplay->mCrOglCallbacks.pfnHasData())
3356 {
3357 /* submit */
3358 VBOXCRCMDCTL_HGCM *pData = &pDisplay->mCrOglScreenshotCtl;
3359
3360 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3361 pData->Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
3362
3363 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
3364 pData->aParms[0].u.pointer.addr = &pDisplay->mCrOglScreenshotData;
3365 pData->aParms[0].u.pointer.size = sizeof(pDisplay->mCrOglScreenshotData);
3366 rc = pDisplay->i_crCtlSubmit(&pData->Hdr, sizeof(*pData), Display::i_displayVRecCompletion, pDisplay);
3367 if (RT_SUCCESS(rc))
3368 break;
3369 AssertMsgFailed(("crCtlSubmit failed (rc=%Rrc)\n", rc));
3370 }
3371
3372 /* no 3D data available, or error has occured,
3373 * go the straight way */
3374 ASMAtomicWriteU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_IDLE);
3375 }
3376 else
3377 {
3378 /* record request is still in progress, don't do anything */
3379 break;
3380 }
3381 }
3382# endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
3383
3384 uint64_t u64Now = RTTimeProgramMilliTS();
3385 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3386 {
3387 if (!pDisplay->maVideoRecEnabled[uScreenId])
3388 continue;
3389
3390 if (VideoRecIsLimitReached(pDisplay->mpVideoRecCtx, uScreenId, u64Now))
3391 {
3392 pDisplay->i_videoCaptureStop();
3393 pDisplay->mParent->i_machine()->COMSETTER(VideoCaptureEnabled)(false);
3394 break;
3395 }
3396
3397 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3398 if (!pFBInfo->fDisabled)
3399 {
3400 ComPtr<IDisplaySourceBitmap> pSourceBitmap;
3401 int rc2 = RTCritSectEnter(&pDisplay->mVideoCaptureLock);
3402 if (RT_SUCCESS(rc2))
3403 {
3404 pSourceBitmap = pFBInfo->videoCapture.pSourceBitmap;
3405 RTCritSectLeave(&pDisplay->mVideoCaptureLock);
3406 }
3407
3408 if (!pSourceBitmap.isNull())
3409 {
3410 BYTE *pbAddress = NULL;
3411 ULONG ulWidth = 0;
3412 ULONG ulHeight = 0;
3413 ULONG ulBitsPerPixel = 0;
3414 ULONG ulBytesPerLine = 0;
3415 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
3416 HRESULT hr = pSourceBitmap->QueryBitmapInfo(&pbAddress,
3417 &ulWidth,
3418 &ulHeight,
3419 &ulBitsPerPixel,
3420 &ulBytesPerLine,
3421 &bitmapFormat);
3422 if (SUCCEEDED(hr) && pbAddress)
3423 rc = VideoRecSendVideoFrame(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
3424 BitmapFormat_BGR,
3425 ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight,
3426 pbAddress, u64Now);
3427 else
3428 rc = VERR_NOT_SUPPORTED;
3429
3430 pSourceBitmap.setNull();
3431 }
3432 else
3433 rc = VERR_NOT_SUPPORTED;
3434
3435 if (rc == VINF_TRY_AGAIN)
3436 break;
3437 }
3438 }
3439 } while (0);
3440 }
3441#endif /* VBOX_WITH_VIDEOREC */
3442
3443#ifdef DEBUG_sunlover_2
3444 LogFlowFunc(("leave\n"));
3445#endif /* DEBUG_sunlover_2 */
3446}
3447
3448/**
3449 * Reset notification
3450 *
3451 * @see PDMIDISPLAYCONNECTOR::pfnReset
3452 */
3453DECLCALLBACK(void) Display::i_displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
3454{
3455 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3456
3457 LogRelFlowFunc(("\n"));
3458
3459 /* Disable VBVA mode. */
3460 pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
3461}
3462
3463/**
3464 * LFBModeChange notification
3465 *
3466 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
3467 */
3468DECLCALLBACK(void) Display::i_displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
3469{
3470 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3471
3472 LogRelFlowFunc(("fEnabled=%d\n", fEnabled));
3473
3474 NOREF(fEnabled);
3475
3476 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
3477 pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
3478}
3479
3480/**
3481 * Adapter information change notification.
3482 *
3483 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
3484 */
3485DECLCALLBACK(void) Display::i_displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM,
3486 uint32_t u32VRAMSize)
3487{
3488 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3489 pDrv->pDisplay->processAdapterData(pvVRAM, u32VRAMSize);
3490}
3491
3492/**
3493 * Display information change notification.
3494 *
3495 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
3496 */
3497DECLCALLBACK(void) Display::i_displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface,
3498 void *pvVRAM, unsigned uScreenId)
3499{
3500 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3501 pDrv->pDisplay->processDisplayData(pvVRAM, uScreenId);
3502}
3503
3504#ifdef VBOX_WITH_VIDEOHWACCEL
3505
3506#ifndef S_FALSE
3507# define S_FALSE ((HRESULT)1L)
3508#endif
3509
3510int Display::i_handleVHWACommandProcess(PVBOXVHWACMD pCommand)
3511{
3512 unsigned id = (unsigned)pCommand->iDisplay;
3513 if (id >= mcMonitors)
3514 return VERR_INVALID_PARAMETER;
3515
3516 ComPtr<IFramebuffer> pFramebuffer;
3517 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
3518 pFramebuffer = maFramebuffers[id].pFramebuffer;
3519 bool fVHWASupported = RT_BOOL(maFramebuffers[id].u32Caps & FramebufferCapabilities_VHWA);
3520 arlock.release();
3521
3522 if (pFramebuffer == NULL || !fVHWASupported)
3523 return VERR_NOT_IMPLEMENTED; /* Implementation is not available. */
3524
3525 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
3526 if (hr == S_FALSE)
3527 return VINF_SUCCESS;
3528 if (SUCCEEDED(hr))
3529 return VINF_CALLBACK_RETURN;
3530 if (hr == E_ACCESSDENIED)
3531 return VERR_INVALID_STATE; /* notify we can not handle request atm */
3532 if (hr == E_NOTIMPL)
3533 return VERR_NOT_IMPLEMENTED;
3534 return VERR_GENERAL_FAILURE;
3535}
3536
3537DECLCALLBACK(int) Display::i_displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
3538{
3539 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3540
3541 return pDrv->pDisplay->i_handleVHWACommandProcess(pCommand);
3542}
3543#endif
3544
3545#ifdef VBOX_WITH_CRHGSMI
3546void Display::i_handleCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
3547{
3548 RT_NOREF(u32Function);
3549 mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync(mpDrv->pVBVACallbacks,
3550 (PVBOXVDMACMD_CHROMIUM_CMD)pParam->u.pointer.addr, result);
3551}
3552
3553void Display::i_handleCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
3554{
3555 RT_NOREF(u32Function);
3556 PVBOXVDMACMD_CHROMIUM_CTL pCtl = (PVBOXVDMACMD_CHROMIUM_CTL)pParam->u.pointer.addr;
3557 mpDrv->pVBVACallbacks->pfnCrHgsmiControlCompleteAsync(mpDrv->pVBVACallbacks, pCtl, result);
3558}
3559
3560void Display::i_handleCrHgsmiCommandProcess(PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
3561{
3562 int rc = VERR_NOT_SUPPORTED;
3563 VBOXHGCMSVCPARM parm;
3564 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3565 parm.u.pointer.addr = pCmd;
3566 parm.u.pointer.size = cbCmd;
3567
3568 if (mhCrOglSvc)
3569 {
3570 VMMDev *pVMMDev = mParent->i_getVMMDev();
3571 if (pVMMDev)
3572 {
3573 /* no completion callback is specified with this call,
3574 * the CrOgl code will complete the CrHgsmi command once it processes it */
3575 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm, NULL, NULL);
3576 AssertRC(rc);
3577 if (RT_SUCCESS(rc))
3578 return;
3579 }
3580 else
3581 rc = VERR_INVALID_STATE;
3582 }
3583
3584 /* we are here because something went wrong with command processing, complete it */
3585 i_handleCrHgsmiCommandCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm);
3586}
3587
3588void Display::i_handleCrHgsmiControlProcess(PVBOXVDMACMD_CHROMIUM_CTL pCtl, uint32_t cbCtl)
3589{
3590 int rc = VERR_NOT_SUPPORTED;
3591 VBOXHGCMSVCPARM parm;
3592 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3593 parm.u.pointer.addr = pCtl;
3594 parm.u.pointer.size = cbCtl;
3595
3596 if (mhCrOglSvc)
3597 {
3598 VMMDev *pVMMDev = mParent->i_getVMMDev();
3599 if (pVMMDev)
3600 {
3601 bool fCheckPendingViewport = (pCtl->enmType == VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP);
3602 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm,
3603 Display::i_displayCrHgsmiControlCompletion, this);
3604 AssertRC(rc);
3605 if (RT_SUCCESS(rc))
3606 {
3607 if (fCheckPendingViewport)
3608 {
3609 ULONG ul;
3610 for (ul = 0; ul < mcMonitors; ul++)
3611 {
3612 DISPLAYFBINFO *pFb = &maFramebuffers[ul];
3613 if (!pFb->pendingViewportInfo.fPending)
3614 continue;
3615
3616 rc = i_crViewportNotify(ul, pFb->pendingViewportInfo.x, pFb->pendingViewportInfo.y,
3617 pFb->pendingViewportInfo.width, pFb->pendingViewportInfo.height);
3618 if (RT_SUCCESS(rc))
3619 pFb->pendingViewportInfo.fPending = false;
3620 else
3621 {
3622 AssertMsgFailed(("crViewportNotify failed (rc=%Rrc)\n", rc));
3623 rc = VINF_SUCCESS;
3624 }
3625 }
3626 }
3627 return;
3628 }
3629 }
3630 else
3631 rc = VERR_INVALID_STATE;
3632 }
3633
3634 /* we are here because something went wrong with command processing, complete it */
3635 i_handleCrHgsmiControlCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm);
3636}
3637
3638DECLCALLBACK(void) Display::i_displayCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd,
3639 uint32_t cbCmd)
3640{
3641 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3642
3643 pDrv->pDisplay->i_handleCrHgsmiCommandProcess(pCmd, cbCmd);
3644}
3645
3646DECLCALLBACK(void) Display::i_displayCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd,
3647 uint32_t cbCmd)
3648{
3649 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3650
3651 pDrv->pDisplay->i_handleCrHgsmiControlProcess(pCmd, cbCmd);
3652}
3653
3654DECLCALLBACK(void) Display::i_displayCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
3655 void *pvContext)
3656{
3657 AssertMsgFailed(("not expected!\n"));
3658 Display *pDisplay = (Display *)pvContext;
3659 pDisplay->i_handleCrHgsmiCommandCompletion(result, u32Function, pParam);
3660}
3661
3662DECLCALLBACK(void) Display::i_displayCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
3663 void *pvContext)
3664{
3665 Display *pDisplay = (Display *)pvContext;
3666 pDisplay->i_handleCrHgsmiControlCompletion(result, u32Function, pParam);
3667
3668}
3669#endif
3670
3671#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3672DECLCALLBACK(void) Display::i_displayCrHgcmCtlSubmitCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
3673 void *pvContext)
3674{
3675 RT_NOREF(u32Function);
3676 VBOXCRCMDCTL *pCmd = (VBOXCRCMDCTL*)pParam->u.pointer.addr;
3677 if (pCmd->u.pfnInternal)
3678 ((PFNCRCTLCOMPLETION)pCmd->u.pfnInternal)(pCmd, pParam->u.pointer.size, result, pvContext);
3679}
3680
3681int Display::i_handleCrHgcmCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
3682 PFNCRCTLCOMPLETION pfnCompletion,
3683 void *pvCompletion)
3684{
3685 VMMDev *pVMMDev = mParent ? mParent->i_getVMMDev() : NULL;
3686 if (!pVMMDev)
3687 {
3688 AssertMsgFailed(("no vmmdev\n"));
3689 return VERR_INVALID_STATE;
3690 }
3691
3692 Assert(mhCrOglSvc);
3693 VBOXHGCMSVCPARM parm;
3694 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3695 parm.u.pointer.addr = pCmd;
3696 parm.u.pointer.size = cbCmd;
3697
3698 pCmd->u.pfnInternal = (void(*)())pfnCompletion;
3699 int rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CTL, &parm, i_displayCrHgcmCtlSubmitCompletion,
3700 pvCompletion);
3701 if (!RT_SUCCESS(rc))
3702 AssertMsgFailed(("hgcmHostFastCallAsync failed (rc=%Rrc)\n", rc));
3703
3704 return rc;
3705}
3706
3707DECLCALLBACK(int) Display::i_displayCrHgcmCtlSubmit(PPDMIDISPLAYCONNECTOR pInterface,
3708 struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
3709 PFNCRCTLCOMPLETION pfnCompletion,
3710 void *pvCompletion)
3711{
3712 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3713 Display *pThis = pDrv->pDisplay;
3714 return pThis->i_handleCrHgcmCtlSubmit(pCmd, cbCmd, pfnCompletion, pvCompletion);
3715}
3716
3717int Display::i_crCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, PFNCRCTLCOMPLETION pfnCompletion, void *pvCompletion)
3718{
3719 int rc = RTCritSectRwEnterShared(&mCrOglLock);
3720 if (RT_SUCCESS(rc))
3721 {
3722 if (mhCrOglSvc)
3723 rc = mpDrv->pVBVACallbacks->pfnCrCtlSubmit(mpDrv->pVBVACallbacks, pCmd, cbCmd, pfnCompletion, pvCompletion);
3724 else
3725 rc = VERR_NOT_SUPPORTED;
3726
3727 RTCritSectRwLeaveShared(&mCrOglLock);
3728 }
3729 return rc;
3730}
3731
3732int Display::i_crCtlSubmitSync(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
3733{
3734 int rc = RTCritSectRwEnterShared(&mCrOglLock);
3735 if (RT_SUCCESS(rc))
3736 {
3737 if (mhCrOglSvc)
3738 rc = mpDrv->pVBVACallbacks->pfnCrCtlSubmitSync(mpDrv->pVBVACallbacks, pCmd, cbCmd);
3739 else
3740 rc = VERR_NOT_SUPPORTED;
3741
3742 RTCritSectRwLeaveShared(&mCrOglLock);
3743 }
3744 return rc;
3745}
3746
3747int Display::i_crCtlSubmitAsyncCmdCopy(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
3748{
3749 VBOXCRCMDCTL* pCmdCopy = (VBOXCRCMDCTL*)RTMemAlloc(cbCmd);
3750 if (!pCmdCopy)
3751 {
3752 LogRel(("RTMemAlloc failed\n"));
3753 return VERR_NO_MEMORY;
3754 }
3755
3756 memcpy(pCmdCopy, pCmd, cbCmd);
3757
3758 int rc = i_crCtlSubmit(pCmdCopy, cbCmd, i_displayCrCmdFree, pCmdCopy);
3759 if (RT_FAILURE(rc))
3760 {
3761 LogRel(("crCtlSubmit failed (rc=%Rrc)\n", rc));
3762 RTMemFree(pCmdCopy);
3763 return rc;
3764 }
3765
3766 return VINF_SUCCESS;
3767}
3768
3769int Display::i_crCtlSubmitSyncIfHasDataForScreen(uint32_t u32ScreenID, struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
3770{
3771 int rc = RTCritSectRwEnterShared(&mCrOglLock);
3772 AssertRCReturn(rc, rc);
3773
3774 if ( mCrOglCallbacks.pfnHasDataForScreen
3775 && mCrOglCallbacks.pfnHasDataForScreen(u32ScreenID))
3776 rc = i_crCtlSubmitSync(pCmd, cbCmd);
3777 else
3778 rc = i_crCtlSubmitAsyncCmdCopy(pCmd, cbCmd);
3779
3780 RTCritSectRwLeaveShared(&mCrOglLock);
3781
3782 return rc;
3783}
3784
3785bool Display::i_handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t u64Timestamp)
3786{
3787 /** @todo r=bird: u64Timestamp - using the 'u64' prefix add nothing.
3788 * However, using one of the prefixes indicating the timestamp unit
3789 * would be very valuable! */
3790# ifdef VBOX_WITH_VIDEOREC
3791 return VideoRecIsReady(mpVideoRecCtx, uScreen, u64Timestamp);
3792# else
3793 RT_NOREF(uScreen, u64Timestamp);
3794 return false;
3795# endif
3796}
3797
3798void Display::i_handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t u64Timestamp)
3799{
3800 RT_NOREF(uScreen, u64Timestamp);
3801}
3802
3803void Display::i_handleCrVRecScreenshotPerform(uint32_t uScreen,
3804 uint32_t x, uint32_t y, uint32_t uPixelFormat,
3805 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
3806 uint32_t uGuestWidth, uint32_t uGuestHeight,
3807 uint8_t *pu8BufferAddress, uint64_t u64Timestamp)
3808{
3809 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
3810# ifdef VBOX_WITH_VIDEOREC
3811 int rc = VideoRecSendVideoFrame(mpVideoRecCtx, uScreen, x, y,
3812 uPixelFormat,
3813 uBitsPerPixel, uBytesPerLine,
3814 uGuestWidth, uGuestHeight,
3815 pu8BufferAddress, u64Timestamp);
3816 NOREF(rc);
3817 Assert(rc == VINF_SUCCESS /* || rc == VERR_TRY_AGAIN || rc == VINF_TRY_AGAIN*/);
3818# else
3819 RT_NOREF(uScreen, x, y, uPixelFormat, \
3820 uBitsPerPixel, uBytesPerLine, uGuestWidth, uGuestHeight, pu8BufferAddress, u64Timestamp);
3821# endif /* VBOX_WITH_VIDEOREC */
3822}
3823
3824void Display::i_handleVRecCompletion()
3825{
3826 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
3827 ASMAtomicWriteU32(&mfCrOglVideoRecState, CRVREC_STATE_IDLE);
3828}
3829
3830#endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
3831
3832HRESULT Display::notifyScaleFactorChange(ULONG aScreenId, ULONG aScaleFactorWMultiplied, ULONG aScaleFactorHMultiplied)
3833{
3834#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3835 HRESULT hr = E_UNEXPECTED;
3836
3837 if (aScreenId >= mcMonitors)
3838 return E_INVALIDARG;
3839
3840 /* 3D acceleration enabled in VM config. */
3841 if (mfIsCr3DEnabled)
3842 {
3843 /* VBoxSharedCrOpenGL HGCM host service is running. */
3844 if (mhCrOglSvc)
3845 {
3846 VMMDev *pVMMDev = mParent->i_getVMMDev();
3847 if (pVMMDev)
3848 {
3849 VBOXCRCMDCTL_HGCM *pCtl;
3850 pCtl = (VBOXCRCMDCTL_HGCM *)RTMemAlloc(sizeof(CRVBOXHGCMSETSCALEFACTOR) + sizeof(VBOXCRCMDCTL_HGCM));
3851 if (pCtl)
3852 {
3853 CRVBOXHGCMSETSCALEFACTOR *pData = (CRVBOXHGCMSETSCALEFACTOR *)(pCtl + 1);
3854 int rc;
3855
3856 pData->u32Screen = aScreenId;
3857 pData->u32ScaleFactorWMultiplied = aScaleFactorWMultiplied;
3858 pData->u32ScaleFactorHMultiplied = aScaleFactorHMultiplied;
3859
3860 pCtl->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3861 pCtl->Hdr.u32Function = SHCRGL_HOST_FN_SET_SCALE_FACTOR;
3862 pCtl->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
3863 pCtl->aParms[0].u.pointer.addr = pData;
3864 pCtl->aParms[0].u.pointer.size = sizeof(*pData);
3865
3866 rc = i_crCtlSubmitSync(&pCtl->Hdr, sizeof(*pCtl));
3867 if (RT_FAILURE(rc))
3868 AssertMsgFailed(("crCtlSubmitSync failed (rc=%Rrc)\n", rc));
3869 else
3870 hr = S_OK;
3871
3872 RTMemFree(pCtl);
3873 }
3874 else
3875 {
3876 LogRel(("Running out of memory on attempt to set OpenGL content scale factor. Ignored.\n"));
3877 hr = E_OUTOFMEMORY;
3878 }
3879 }
3880 else
3881 LogRel(("Internal error occurred on attempt to set OpenGL content scale factor. Ignored.\n"));
3882 }
3883 else
3884 LogRel(("Attempt to specify OpenGL content scale factor while corresponding HGCM host service not yet runing. Ignored.\n"));
3885 }
3886 else
3887# if 0 /** @todo Thank you so very much from anyone using VMSVGA3d! */
3888 AssertMsgFailed(("Attempt to specify OpenGL content scale factor while 3D acceleration is disabled in VM config. Ignored.\n"));
3889# else
3890 {
3891 hr = S_OK;
3892 /* Need an interface like this here (and the #ifdefs needs adjusting):
3893 PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
3894 if (pUpPort && pUpPort->pfnSetScaleFactor)
3895 pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
3896 }
3897# endif
3898
3899 return hr;
3900
3901#else /* !VBOX_WITH_HGCM || !VBOX_WITH_CROGL */
3902 RT_NOREF(aScreenId, aScaleFactorWMultiplied, aScaleFactorHMultiplied);
3903 AssertMsgFailed(("Attempt to specify OpenGL content scale factor while corresponding functionality is disabled."));
3904 return E_UNEXPECTED;
3905#endif /* !VBOX_WITH_HGCM || !VBOX_WITH_CROGL */
3906}
3907
3908HRESULT Display::notifyHiDPIOutputPolicyChange(BOOL fUnscaledHiDPI)
3909{
3910#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3911 HRESULT hr = E_UNEXPECTED;
3912
3913 /* 3D acceleration enabled in VM config. */
3914 if (mfIsCr3DEnabled)
3915 {
3916 /* VBoxSharedCrOpenGL HGCM host service is running. */
3917 if (mhCrOglSvc)
3918 {
3919 VMMDev *pVMMDev = mParent->i_getVMMDev();
3920 if (pVMMDev)
3921 {
3922 VBOXCRCMDCTL_HGCM *pCtl;
3923 pCtl = (VBOXCRCMDCTL_HGCM *)RTMemAlloc(sizeof(CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT) + sizeof(VBOXCRCMDCTL_HGCM));
3924 if (pCtl)
3925 {
3926 CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT *pData = (CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT *)(pCtl + 1);
3927 int rc;
3928
3929 pData->fUnscaledHiDPI = RT_BOOL(fUnscaledHiDPI);
3930
3931 pCtl->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3932 pCtl->Hdr.u32Function = SHCRGL_HOST_FN_SET_UNSCALED_HIDPI;
3933 pCtl->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
3934 pCtl->aParms[0].u.pointer.addr = pData;
3935 pCtl->aParms[0].u.pointer.size = sizeof(*pData);
3936
3937 rc = i_crCtlSubmitSync(&pCtl->Hdr, sizeof(*pCtl));
3938 if (RT_FAILURE(rc))
3939 AssertMsgFailed(("crCtlSubmitSync failed (rc=%Rrc)\n", rc));
3940 else
3941 hr = S_OK;
3942
3943 RTMemFree(pCtl);
3944 }
3945 else
3946 {
3947 LogRel(("Running out of memory on attempt to notify OpenGL about HiDPI output scaling policy change. Ignored.\n"));
3948 hr = E_OUTOFMEMORY;
3949 }
3950 }
3951 else
3952 LogRel(("Internal error occurred on attempt to notify OpenGL about HiDPI output scaling policy change. Ignored.\n"));
3953 }
3954 else
3955 LogRel(("Attempt to notify OpenGL about HiDPI output scaling policy change while corresponding HGCM host service not yet runing. Ignored.\n"));
3956 }
3957 else
3958 {
3959 hr = S_OK;
3960 /* Need an interface like this here (and the #ifdefs needs adjusting):
3961 PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
3962 if (pUpPort && pUpPort->pfnSetScaleFactor)
3963 pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
3964 }
3965
3966 return hr;
3967
3968#else /* !VBOX_WITH_HGCM || !VBOX_WITH_CROGL */
3969 RT_NOREF(fUnscaledHiDPI);
3970 AssertMsgFailed(("Attempt to notify OpenGL about HiDPI output scaling policy change while corresponding functionality is disabled."));
3971 return E_UNEXPECTED;
3972#endif /* !VBOX_WITH_HGCM || !VBOX_WITH_CROGL */
3973}
3974
3975#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3976DECLCALLBACK(void) Display::i_displayCrVRecScreenshotPerform(void *pvCtx, uint32_t uScreen,
3977 uint32_t x, uint32_t y,
3978 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
3979 uint32_t uGuestWidth, uint32_t uGuestHeight,
3980 uint8_t *pu8BufferAddress, uint64_t u64Timestamp)
3981{
3982 Display *pDisplay = (Display *)pvCtx;
3983 pDisplay->i_handleCrVRecScreenshotPerform(uScreen,
3984 x, y, BitmapFormat_BGR, uBitsPerPixel,
3985 uBytesPerLine, uGuestWidth, uGuestHeight,
3986 pu8BufferAddress, u64Timestamp);
3987}
3988
3989DECLCALLBACK(bool) Display::i_displayCrVRecScreenshotBegin(void *pvCtx, uint32_t uScreen, uint64_t u64Timestamp)
3990{
3991 Display *pDisplay = (Display *)pvCtx;
3992 return pDisplay->i_handleCrVRecScreenshotBegin(uScreen, u64Timestamp);
3993}
3994
3995DECLCALLBACK(void) Display::i_displayCrVRecScreenshotEnd(void *pvCtx, uint32_t uScreen, uint64_t u64Timestamp)
3996{
3997 Display *pDisplay = (Display *)pvCtx;
3998 pDisplay->i_handleCrVRecScreenshotEnd(uScreen, u64Timestamp);
3999}
4000
4001DECLCALLBACK(void) Display::i_displayVRecCompletion(struct VBOXCRCMDCTL *pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
4002{
4003 RT_NOREF(pCmd, cbCmd, rc);
4004 Display *pDisplay = (Display *)pvCompletion;
4005 pDisplay->i_handleVRecCompletion();
4006}
4007
4008#endif
4009
4010
4011#ifdef VBOX_WITH_HGSMI
4012DECLCALLBACK(int) Display::i_displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, PVBVAHOSTFLAGS pHostFlags,
4013 bool fRenderThreadMode)
4014{
4015 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
4016
4017 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4018 Display *pThis = pDrv->pDisplay;
4019
4020 if (pThis->maFramebuffers[uScreenId].fVBVAEnabled && pThis->maFramebuffers[uScreenId].fRenderThreadMode != fRenderThreadMode)
4021 {
4022 LogRel(("Enabling different vbva mode\n"));
4023#ifdef DEBUG_misha
4024 AssertMsgFailed(("enabling different vbva mode\n"));
4025#endif
4026 return VERR_INVALID_STATE;
4027 }
4028
4029 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
4030 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
4031 pThis->maFramebuffers[uScreenId].fRenderThreadMode = fRenderThreadMode;
4032 pThis->maFramebuffers[uScreenId].fVBVAForceResize = true;
4033
4034 vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
4035
4036 return VINF_SUCCESS;
4037}
4038
4039DECLCALLBACK(void) Display::i_displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
4040{
4041 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
4042
4043 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4044 Display *pThis = pDrv->pDisplay;
4045
4046 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4047
4048 bool fRenderThreadMode = pFBInfo->fRenderThreadMode;
4049
4050 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
4051 {
4052 /* Make sure that the primary screen is visible now.
4053 * The guest can't use VBVA anymore, so only only the VGA device output works.
4054 */
4055 pFBInfo->flags = 0;
4056 if (pFBInfo->fDisabled)
4057 {
4058 pFBInfo->fDisabled = false;
4059 fireGuestMonitorChangedEvent(pThis->mParent->i_getEventSource(),
4060 GuestMonitorChangedEventType_Enabled,
4061 uScreenId,
4062 pFBInfo->xOrigin, pFBInfo->yOrigin,
4063 pFBInfo->w, pFBInfo->h);
4064 }
4065 }
4066
4067 pFBInfo->fVBVAEnabled = false;
4068 pFBInfo->fVBVAForceResize = false;
4069 pFBInfo->fRenderThreadMode = false;
4070
4071 vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, pFBInfo);
4072
4073 pFBInfo->pVBVAHostFlags = NULL;
4074
4075 if (!fRenderThreadMode && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
4076 {
4077 /* Force full screen update, because VGA device must take control, do resize, etc. */
4078 pThis->mpDrv->pUpPort->pfnUpdateDisplayAll(pThis->mpDrv->pUpPort, /* fFailOnResize = */ false);
4079 }
4080}
4081
4082DECLCALLBACK(void) Display::i_displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
4083{
4084 RT_NOREF(uScreenId);
4085 LogFlowFunc(("uScreenId %d\n", uScreenId));
4086
4087 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4088 Display *pThis = pDrv->pDisplay;
4089
4090 if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
4091 {
4092 vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers,
4093 pThis->mcMonitors);
4094 ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
4095 }
4096}
4097
4098DECLCALLBACK(void) Display::i_displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
4099 PCVBVACMDHDR pCmd, size_t cbCmd)
4100{
4101 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d, @%d,%d %dx%d\n", uScreenId, pCmd, cbCmd, pCmd->x, pCmd->y, pCmd->w, pCmd->h));
4102
4103 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4104 Display *pThis = pDrv->pDisplay;
4105 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4106
4107 if (pFBInfo->fDefaultFormat)
4108 {
4109 /* Make sure that framebuffer contains the same image as the guest VRAM. */
4110 if ( uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
4111 && !pFBInfo->fDisabled)
4112 {
4113 pDrv->pUpPort->pfnUpdateDisplayRect(pDrv->pUpPort, pCmd->x, pCmd->y, pCmd->w, pCmd->h);
4114 }
4115 else if ( !pFBInfo->pSourceBitmap.isNull()
4116 && !pFBInfo->fDisabled)
4117 {
4118 /* Render VRAM content to the framebuffer. */
4119 BYTE *pAddress = NULL;
4120 ULONG ulWidth = 0;
4121 ULONG ulHeight = 0;
4122 ULONG ulBitsPerPixel = 0;
4123 ULONG ulBytesPerLine = 0;
4124 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
4125
4126 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
4127 &ulWidth,
4128 &ulHeight,
4129 &ulBitsPerPixel,
4130 &ulBytesPerLine,
4131 &bitmapFormat);
4132 if (SUCCEEDED(hrc))
4133 {
4134 uint32_t width = pCmd->w;
4135 uint32_t height = pCmd->h;
4136
4137 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
4138 int32_t xSrc = pCmd->x - pFBInfo->xOrigin;
4139 int32_t ySrc = pCmd->y - pFBInfo->yOrigin;
4140 uint32_t u32SrcWidth = pFBInfo->w;
4141 uint32_t u32SrcHeight = pFBInfo->h;
4142 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
4143 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
4144
4145 uint8_t *pu8Dst = pAddress;
4146 int32_t xDst = xSrc;
4147 int32_t yDst = ySrc;
4148 uint32_t u32DstWidth = u32SrcWidth;
4149 uint32_t u32DstHeight = u32SrcHeight;
4150 uint32_t u32DstLineSize = u32DstWidth * 4;
4151 uint32_t u32DstBitsPerPixel = 32;
4152
4153 pDrv->pUpPort->pfnCopyRect(pDrv->pUpPort,
4154 width, height,
4155 pu8Src,
4156 xSrc, ySrc,
4157 u32SrcWidth, u32SrcHeight,
4158 u32SrcLineSize, u32SrcBitsPerPixel,
4159 pu8Dst,
4160 xDst, yDst,
4161 u32DstWidth, u32DstHeight,
4162 u32DstLineSize, u32DstBitsPerPixel);
4163 }
4164 }
4165 }
4166
4167 VBVACMDHDR hdrSaved = *pCmd;
4168
4169 VBVACMDHDR *pHdrUnconst = (VBVACMDHDR *)pCmd;
4170
4171 pHdrUnconst->x -= (int16_t)pFBInfo->xOrigin;
4172 pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
4173
4174 /** @todo new SendUpdate entry which can get a separate cmd header or coords. */
4175 pThis->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, pHdrUnconst, (uint32_t)cbCmd);
4176
4177 *pHdrUnconst = hdrSaved;
4178}
4179
4180DECLCALLBACK(void) Display::i_displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y,
4181 uint32_t cx, uint32_t cy)
4182{
4183 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
4184
4185 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4186 Display *pThis = pDrv->pDisplay;
4187 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4188
4189 /** @todo handleFramebufferUpdate (uScreenId,
4190 * x - pThis->maFramebuffers[uScreenId].xOrigin,
4191 * y - pThis->maFramebuffers[uScreenId].yOrigin,
4192 * cx, cy);
4193 */
4194 pThis->i_handleDisplayUpdate(uScreenId, x - pFBInfo->xOrigin, y - pFBInfo->yOrigin, cx, cy);
4195}
4196
4197#ifdef DEBUG_sunlover
4198static void logVBVAResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, const DISPLAYFBINFO *pFBInfo)
4199{
4200 LogRel(("displayVBVAResize: [%d] %s\n"
4201 " pView->u32ViewIndex %d\n"
4202 " pView->u32ViewOffset 0x%08X\n"
4203 " pView->u32ViewSize 0x%08X\n"
4204 " pView->u32MaxScreenSize 0x%08X\n"
4205 " pScreen->i32OriginX %d\n"
4206 " pScreen->i32OriginY %d\n"
4207 " pScreen->u32StartOffset 0x%08X\n"
4208 " pScreen->u32LineSize 0x%08X\n"
4209 " pScreen->u32Width %d\n"
4210 " pScreen->u32Height %d\n"
4211 " pScreen->u16BitsPerPixel %d\n"
4212 " pScreen->u16Flags 0x%04X\n"
4213 " pFBInfo->u32Offset 0x%08X\n"
4214 " pFBInfo->u32MaxFramebufferSize 0x%08X\n"
4215 " pFBInfo->u32InformationSize 0x%08X\n"
4216 " pFBInfo->fDisabled %d\n"
4217 " xOrigin, yOrigin, w, h: %d,%d %dx%d\n"
4218 " pFBInfo->u16BitsPerPixel %d\n"
4219 " pFBInfo->pu8FramebufferVRAM %p\n"
4220 " pFBInfo->u32LineSize 0x%08X\n"
4221 " pFBInfo->flags 0x%04X\n"
4222 " pFBInfo->pHostEvents %p\n"
4223 " pFBInfo->fDefaultFormat %d\n"
4224 " pFBInfo->fVBVAEnabled %d\n"
4225 " pFBInfo->fVBVAForceResize %d\n"
4226 " pFBInfo->pVBVAHostFlags %p\n"
4227 "",
4228 pScreen->u32ViewIndex,
4229 (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)? "DISABLED": "ENABLED",
4230 pView->u32ViewIndex,
4231 pView->u32ViewOffset,
4232 pView->u32ViewSize,
4233 pView->u32MaxScreenSize,
4234 pScreen->i32OriginX,
4235 pScreen->i32OriginY,
4236 pScreen->u32StartOffset,
4237 pScreen->u32LineSize,
4238 pScreen->u32Width,
4239 pScreen->u32Height,
4240 pScreen->u16BitsPerPixel,
4241 pScreen->u16Flags,
4242 pFBInfo->u32Offset,
4243 pFBInfo->u32MaxFramebufferSize,
4244 pFBInfo->u32InformationSize,
4245 pFBInfo->fDisabled,
4246 pFBInfo->xOrigin,
4247 pFBInfo->yOrigin,
4248 pFBInfo->w,
4249 pFBInfo->h,
4250 pFBInfo->u16BitsPerPixel,
4251 pFBInfo->pu8FramebufferVRAM,
4252 pFBInfo->u32LineSize,
4253 pFBInfo->flags,
4254 pFBInfo->pHostEvents,
4255 pFBInfo->fDefaultFormat,
4256 pFBInfo->fVBVAEnabled,
4257 pFBInfo->fVBVAForceResize,
4258 pFBInfo->pVBVAHostFlags
4259 ));
4260}
4261#endif /* DEBUG_sunlover */
4262
4263DECLCALLBACK(int) Display::i_displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, PCVBVAINFOVIEW pView,
4264 PCVBVAINFOSCREEN pScreen, void *pvVRAM, bool fResetInputMapping)
4265{
4266 LogRelFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
4267
4268 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4269 Display *pThis = pDrv->pDisplay;
4270
4271 return pThis->processVBVAResize(pView, pScreen, pvVRAM, fResetInputMapping);
4272}
4273
4274int Display::processVBVAResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, void *pvVRAM, bool fResetInputMapping)
4275{
4276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4277
4278 DISPLAYFBINFO *pFBInfo = &maFramebuffers[pScreen->u32ViewIndex];
4279
4280 if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
4281 {
4282 /* Ask the framebuffer to resize using a default format. The framebuffer will be black.
4283 * So if the frontend does not support GuestMonitorChangedEventType_Disabled event,
4284 * the VM window will be black. */
4285 uint32_t u32Width = pFBInfo->w ? pFBInfo->w : 640;
4286 uint32_t u32Height = pFBInfo->h ? pFBInfo->h : 480;
4287 int32_t xOrigin = pFBInfo->xOrigin;
4288 int32_t yOrigin = pFBInfo->yOrigin;
4289
4290 alock.release();
4291
4292 i_notifyCroglResize(pView, pScreen, pvVRAM);
4293
4294 i_handleDisplayResize(pScreen->u32ViewIndex, 0, (uint8_t *)NULL, 0,
4295 u32Width, u32Height, pScreen->u16Flags, xOrigin, yOrigin, false);
4296
4297 return VINF_SUCCESS;
4298 }
4299
4300 VBVAINFOSCREEN screenInfo;
4301 RT_ZERO(screenInfo);
4302
4303 if (pScreen->u16Flags & VBVA_SCREEN_F_BLANK2)
4304 {
4305 /* Init a local VBVAINFOSCREEN structure, which will be used instead of
4306 * the original pScreen. Set VBVA_SCREEN_F_BLANK, which will force
4307 * the code below to choose the "blanking" branches.
4308 */
4309 screenInfo.u32ViewIndex = pScreen->u32ViewIndex;
4310 screenInfo.i32OriginX = pFBInfo->xOrigin;
4311 screenInfo.i32OriginY = pFBInfo->yOrigin;
4312 screenInfo.u32StartOffset = 0; /* Irrelevant */
4313 screenInfo.u32LineSize = pFBInfo->u32LineSize;
4314 screenInfo.u32Width = pFBInfo->w;
4315 screenInfo.u32Height = pFBInfo->h;
4316 screenInfo.u16BitsPerPixel = pFBInfo->u16BitsPerPixel;
4317 screenInfo.u16Flags = pScreen->u16Flags | VBVA_SCREEN_F_BLANK;
4318
4319 pScreen = &screenInfo;
4320 }
4321
4322 if (fResetInputMapping)
4323 {
4324 /// @todo Rename to m* and verify whether some kind of lock is required.
4325 xInputMappingOrigin = 0;
4326 yInputMappingOrigin = 0;
4327 cxInputMapping = 0;
4328 cyInputMapping = 0;
4329 }
4330
4331 alock.release();
4332
4333 i_notifyCroglResize(pView, pScreen, pvVRAM);
4334
4335 return i_handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
4336 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
4337 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height, pScreen->u16Flags,
4338 pScreen->i32OriginX, pScreen->i32OriginY, false);
4339}
4340
4341DECLCALLBACK(int) Display::i_displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
4342 uint32_t xHot, uint32_t yHot,
4343 uint32_t cx, uint32_t cy,
4344 const void *pvShape)
4345{
4346 LogFlowFunc(("\n"));
4347
4348 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4349
4350 uint32_t cbShape = 0;
4351 if (pvShape)
4352 {
4353 cbShape = (cx + 7) / 8 * cy; /* size of the AND mask */
4354 cbShape = ((cbShape + 3) & ~3) + cx * 4 * cy; /* + gap + size of the XOR mask */
4355 }
4356
4357 /* Tell the console about it */
4358 pDrv->pDisplay->mParent->i_onMousePointerShapeChange(fVisible, fAlpha,
4359 xHot, yHot, cx, cy, (uint8_t *)pvShape, cbShape);
4360
4361 return VINF_SUCCESS;
4362}
4363
4364DECLCALLBACK(void) Display::i_displayVBVAGuestCapabilityUpdate(PPDMIDISPLAYCONNECTOR pInterface, uint32_t fCapabilities)
4365{
4366 LogFlowFunc(("\n"));
4367
4368 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4369 Display *pThis = pDrv->pDisplay;
4370
4371 pThis->i_handleUpdateGuestVBVACapabilities(fCapabilities);
4372}
4373
4374DECLCALLBACK(void) Display::i_displayVBVAInputMappingUpdate(PPDMIDISPLAYCONNECTOR pInterface, int32_t xOrigin, int32_t yOrigin,
4375 uint32_t cx, uint32_t cy)
4376{
4377 LogFlowFunc(("\n"));
4378
4379 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4380 Display *pThis = pDrv->pDisplay;
4381
4382 pThis->i_handleUpdateVBVAInputMapping(xOrigin, yOrigin, cx, cy);
4383}
4384
4385#endif /* VBOX_WITH_HGSMI */
4386
4387/**
4388 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4389 */
4390DECLCALLBACK(void *) Display::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4391{
4392 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
4393 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4394 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
4395 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYCONNECTOR, &pDrv->IConnector);
4396 return NULL;
4397}
4398
4399
4400/**
4401 * Destruct a display driver instance.
4402 *
4403 * @returns VBox status code.
4404 * @param pDrvIns The driver instance data.
4405 */
4406DECLCALLBACK(void) Display::i_drvDestruct(PPDMDRVINS pDrvIns)
4407{
4408 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4409 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4410 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
4411
4412 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
4413
4414 pThis->IConnector.pbData = NULL;
4415 pThis->IConnector.cbScanline = 0;
4416 pThis->IConnector.cBits = 32;
4417 pThis->IConnector.cx = 0;
4418 pThis->IConnector.cy = 0;
4419
4420 if (pThis->pDisplay)
4421 {
4422 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
4423#ifdef VBOX_WITH_VIDEOREC
4424 pThis->pDisplay->i_videoCaptureStop();
4425#endif
4426#ifdef VBOX_WITH_CRHGSMI
4427 pThis->pDisplay->i_destructCrHgsmiData();
4428#endif
4429 pThis->pDisplay->mpDrv = NULL;
4430 pThis->pDisplay->mpVMMDev = NULL;
4431 }
4432}
4433
4434
4435/**
4436 * Construct a display driver instance.
4437 *
4438 * @copydoc FNPDMDRVCONSTRUCT
4439 */
4440DECLCALLBACK(int) Display::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
4441{
4442 RT_NOREF(fFlags);
4443 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
4444 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4445 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
4446
4447 /*
4448 * Validate configuration.
4449 */
4450 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
4451 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
4452 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
4453 ("Configuration error: Not possible to attach anything to this driver!\n"),
4454 VERR_PDM_DRVINS_NO_ATTACH);
4455
4456 /*
4457 * Init Interfaces.
4458 */
4459 pDrvIns->IBase.pfnQueryInterface = Display::i_drvQueryInterface;
4460
4461 pThis->IConnector.pfnResize = Display::i_displayResizeCallback;
4462 pThis->IConnector.pfnUpdateRect = Display::i_displayUpdateCallback;
4463 pThis->IConnector.pfnRefresh = Display::i_displayRefreshCallback;
4464 pThis->IConnector.pfnReset = Display::i_displayResetCallback;
4465 pThis->IConnector.pfnLFBModeChange = Display::i_displayLFBModeChangeCallback;
4466 pThis->IConnector.pfnProcessAdapterData = Display::i_displayProcessAdapterDataCallback;
4467 pThis->IConnector.pfnProcessDisplayData = Display::i_displayProcessDisplayDataCallback;
4468#ifdef VBOX_WITH_VIDEOHWACCEL
4469 pThis->IConnector.pfnVHWACommandProcess = Display::i_displayVHWACommandProcess;
4470#endif
4471#ifdef VBOX_WITH_CRHGSMI
4472 pThis->IConnector.pfnCrHgsmiCommandProcess = Display::i_displayCrHgsmiCommandProcess;
4473 pThis->IConnector.pfnCrHgsmiControlProcess = Display::i_displayCrHgsmiControlProcess;
4474#endif
4475#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4476 pThis->IConnector.pfnCrHgcmCtlSubmit = Display::i_displayCrHgcmCtlSubmit;
4477#endif
4478#ifdef VBOX_WITH_HGSMI
4479 pThis->IConnector.pfnVBVAEnable = Display::i_displayVBVAEnable;
4480 pThis->IConnector.pfnVBVADisable = Display::i_displayVBVADisable;
4481 pThis->IConnector.pfnVBVAUpdateBegin = Display::i_displayVBVAUpdateBegin;
4482 pThis->IConnector.pfnVBVAUpdateProcess = Display::i_displayVBVAUpdateProcess;
4483 pThis->IConnector.pfnVBVAUpdateEnd = Display::i_displayVBVAUpdateEnd;
4484 pThis->IConnector.pfnVBVAResize = Display::i_displayVBVAResize;
4485 pThis->IConnector.pfnVBVAMousePointerShape = Display::i_displayVBVAMousePointerShape;
4486 pThis->IConnector.pfnVBVAGuestCapabilityUpdate = Display::i_displayVBVAGuestCapabilityUpdate;
4487 pThis->IConnector.pfnVBVAInputMappingUpdate = Display::i_displayVBVAInputMappingUpdate;
4488#endif
4489
4490 /*
4491 * Get the IDisplayPort interface of the above driver/device.
4492 */
4493 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
4494 if (!pThis->pUpPort)
4495 {
4496 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
4497 return VERR_PDM_MISSING_INTERFACE_ABOVE;
4498 }
4499#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
4500 pThis->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
4501 if (!pThis->pVBVACallbacks)
4502 {
4503 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
4504 return VERR_PDM_MISSING_INTERFACE_ABOVE;
4505 }
4506#endif
4507 /*
4508 * Get the Display object pointer and update the mpDrv member.
4509 */
4510 void *pv;
4511 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
4512 if (RT_FAILURE(rc))
4513 {
4514 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
4515 return rc;
4516 }
4517 Display *pDisplay = (Display *)pv; /** @todo Check this cast! */
4518 pThis->pDisplay = pDisplay;
4519 pThis->pDisplay->mpDrv = pThis;
4520
4521 /* Disable VRAM to a buffer copy initially. */
4522 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
4523 pThis->IConnector.cBits = 32; /* DevVGA does nothing otherwise. */
4524
4525 /*
4526 * Start periodic screen refreshes
4527 */
4528 pThis->pUpPort->pfnSetRefreshRate(pThis->pUpPort, 20);
4529
4530#ifdef VBOX_WITH_CRHGSMI
4531 pDisplay->i_setupCrHgsmiData();
4532#endif
4533
4534#ifdef VBOX_WITH_VIDEOREC
4535 ComPtr<IMachine> pMachine = pDisplay->mParent->i_machine();
4536 BOOL fEnabled = false;
4537 HRESULT hrc = pMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
4538 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
4539
4540 if (fEnabled)
4541 {
4542 rc = pDisplay->i_videoCaptureInvalidate();
4543 if (RT_SUCCESS(rc))
4544 {
4545 rc = pDisplay->i_videoCaptureStart();
4546 fireVideoCaptureChangedEvent(pDisplay->mParent->i_getEventSource());
4547 }
4548 }
4549#endif
4550
4551 return rc;
4552}
4553
4554
4555/**
4556 * Display driver registration record.
4557 */
4558const PDMDRVREG Display::DrvReg =
4559{
4560 /* u32Version */
4561 PDM_DRVREG_VERSION,
4562 /* szName */
4563 "MainDisplay",
4564 /* szRCMod */
4565 "",
4566 /* szR0Mod */
4567 "",
4568 /* pszDescription */
4569 "Main display driver (Main as in the API).",
4570 /* fFlags */
4571 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
4572 /* fClass. */
4573 PDM_DRVREG_CLASS_DISPLAY,
4574 /* cMaxInstances */
4575 ~0U,
4576 /* cbInstance */
4577 sizeof(DRVMAINDISPLAY),
4578 /* pfnConstruct */
4579 Display::i_drvConstruct,
4580 /* pfnDestruct */
4581 Display::i_drvDestruct,
4582 /* pfnRelocate */
4583 NULL,
4584 /* pfnIOCtl */
4585 NULL,
4586 /* pfnPowerOn */
4587 NULL,
4588 /* pfnReset */
4589 NULL,
4590 /* pfnSuspend */
4591 NULL,
4592 /* pfnResume */
4593 NULL,
4594 /* pfnAttach */
4595 NULL,
4596 /* pfnDetach */
4597 NULL,
4598 /* pfnPowerOff */
4599 NULL,
4600 /* pfnSoftReset */
4601 NULL,
4602 /* u32EndVersion */
4603 PDM_DRVREG_VERSION
4604};
4605
4606/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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