VirtualBox

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

Last change on this file since 68802 was 68802, checked in by vboxsync, 7 years ago

Build fix.

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