VirtualBox

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

Last change on this file since 78425 was 78343, checked in by vboxsync, 6 years ago

DisplayImpl.cpp: verify that the graphics controller is enabled.

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