VirtualBox

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

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

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

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

© 2023 Oracle
ContactPrivacy policyTerms of Use