VirtualBox

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

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

Device/Graphics, pdm, Main, Front-ends: infrastructure for host software cursor.
bugref:9376: Complete hardware cursor implementation in VMSVGA.
This change provides the infrastructure for letting front-ends report to the
graphics device that they are able to render a pointer cursor at any location,
not just where the host cursor is, and for the device to ask them to do this.
It also provides mediation in Main/Display between multiple front-ends and the
device, and tells the guest that we can always render the cursor. In a
follow-up change the device is expected to add support for rendering the
cursor itself into the frame-buffer image it provides to the front-end if not
all attached front-ends are capable of rendering it.

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