VirtualBox

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

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

Devices/Graphics, pdm, Main: add out-of-range to host-side pointer reporting.
bugref:9376: Complete hardware cursor implementation in VMSVGA
The front-end can now send an out-of-range pointer event to say that the
pointer is outside of all guest windows. This can be useful when the cursor
is being drawn in software, as in this case it makes sense to hide it
altogether. The event is not yet passed down to the guest. Out-of-range,
like the already existing invalid data event (-1, -1) is passed as special
pointer co-ordinate values to avoid breaking API interfaces if this is
back-ported. A future change, not to be back-ported, will change the API to
get rid of the special values and do this cleanly.

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

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