VirtualBox

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

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

Main/DisplayImpl.cpp: Skip including vm.h as it is no longer used. bugref:9217

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

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