VirtualBox

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

Last change on this file since 79294 was 79002, checked in by vboxsync, 5 years ago

PDM, Devices, FE, Main: allow size hints without notification for initial hints.
bugref:9482: FE/Qt and Additions/WINNT: move away from -adjust size if...-
Attempt to fix a Windows burn from previous change.

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