VirtualBox

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

Last change on this file since 76091 was 75488, checked in by vboxsync, 6 years ago

Recording/Main: Implemented better support for recording limits (also now configurable per screen).

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

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