VirtualBox

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

Last change on this file since 68798 was 68798, checked in by vboxsync, 8 years ago

VideoRec: Overhauled configuration handling to also support audio configuration changes.

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

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