VirtualBox

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

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

Devices/Graphics, Main, FE/Qt: add plumbing for sending cursor position events.
bugref:9038: FE/Qt: better handle mouse cursor when toggling integration
The Linux and X11 graphics driver sends notification when the guest cursor
position changes. This could be used by the front-end to switch back to
pointer capturing without requiring that the guest draw the cursor, which
often results in artifacts. This change adds the necessary plumbing between
the graphics device and the user interface.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 165.7 KB
Line 
1/* $Id: DisplayImpl.cpp 70075 2017-12-12 09:54:21Z 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_T)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(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 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aBitsPerPixel,
1957 aDisplay, aOriginX, aOriginY,
1958 RT_BOOL(aEnabled), RT_BOOL(aChangeOrigin));
1959 }
1960 return S_OK;
1961}
1962
1963HRESULT Display::setSeamlessMode(BOOL enabled)
1964{
1965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1966
1967 /* Have to release the lock because the pfnRequestSeamlessChange will call EMT. */
1968 alock.release();
1969
1970 VMMDev *pVMMDev = mParent->i_getVMMDev();
1971 if (pVMMDev)
1972 {
1973 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
1974 if (pVMMDevPort)
1975 pVMMDevPort->pfnRequestSeamlessChange(pVMMDevPort, !!enabled);
1976 }
1977 mfSeamlessEnabled = RT_BOOL(enabled);
1978
1979#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1980 if (!enabled)
1981 {
1982 VMMDev *vmmDev = mParent->i_getVMMDev();
1983 if (mfIsCr3DEnabled && vmmDev)
1984 {
1985 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM*)RTMemAlloc(sizeof(VBOXCRCMDCTL_HGCM));
1986 if (!pData)
1987 {
1988 AssertMsgFailed(("RTMemAlloc failed\n"));
1989 return VERR_NO_MEMORY;
1990 }
1991
1992 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1993 pData->Hdr.u32Function = SHCRGL_HOST_FN_SET_VISIBLE_REGION;
1994
1995 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
1996 pData->aParms[0].u.pointer.addr = NULL;
1997 pData->aParms[0].u.pointer.size = 0; /* <- means null rects, NULL pRects address and 0 rects means "disable" */
1998
1999 int rc = i_crCtlSubmit(&pData->Hdr, sizeof(*pData), i_displayCrCmdFree, pData);
2000 if (!RT_SUCCESS(rc))
2001 {
2002 AssertMsgFailed(("crCtlSubmit failed (rc=%Rrc)\n", rc));
2003 RTMemFree(pData);
2004 }
2005 }
2006 }
2007#endif
2008 return S_OK;
2009}
2010
2011#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2012BOOL Display::i_displayCheckTakeScreenshotCrOgl(Display *pDisplay, ULONG aScreenId, uint8_t *pbData,
2013 uint32_t u32Width, uint32_t u32Height)
2014{
2015 if ( pDisplay->mfIsCr3DEnabled
2016 && pDisplay->mCrOglCallbacks.pfnHasData
2017 && pDisplay->mCrOglCallbacks.pfnHasData())
2018 {
2019 VMMDev *pVMMDev = pDisplay->mParent->i_getVMMDev();
2020 if (pVMMDev)
2021 {
2022 CRVBOXHGCMTAKESCREENSHOT *pScreenshot = (CRVBOXHGCMTAKESCREENSHOT *)RTMemAlloc(sizeof(*pScreenshot));
2023 if (pScreenshot)
2024 {
2025 /* screen id or CRSCREEN_ALL to specify all enabled */
2026 pScreenshot->u32Screen = aScreenId;
2027 pScreenshot->u32Width = u32Width;
2028 pScreenshot->u32Height = u32Height;
2029 pScreenshot->u32Pitch = u32Width * 4;
2030 pScreenshot->pvBuffer = pbData;
2031 pScreenshot->pvContext = NULL;
2032 pScreenshot->pfnScreenshotBegin = NULL;
2033 pScreenshot->pfnScreenshotPerform = NULL;
2034 pScreenshot->pfnScreenshotEnd = NULL;
2035
2036 VBOXCRCMDCTL_HGCM data;
2037 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
2038 data.Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
2039
2040 data.aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
2041 data.aParms[0].u.pointer.addr = pScreenshot;
2042 data.aParms[0].u.pointer.size = sizeof(*pScreenshot);
2043
2044 int rc = pDisplay->i_crCtlSubmitSync(&data.Hdr, sizeof(data));
2045
2046 RTMemFree(pScreenshot);
2047
2048 if (RT_SUCCESS(rc))
2049 return TRUE;
2050 AssertMsgFailed(("failed to get screenshot data from crOgl (rc=%Rrc)\n", rc));
2051 /* fall back to the non-3d mechanism */
2052 }
2053 }
2054 }
2055 return FALSE;
2056}
2057#endif
2058
2059/* static */
2060int Display::i_displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_t **ppbData, size_t *pcbData,
2061 uint32_t *pcx, uint32_t *pcy, bool *pfMemFree)
2062{
2063 int rc;
2064 if ( aScreenId == VBOX_VIDEO_PRIMARY_SCREEN
2065 && pDisplay->maFramebuffers[aScreenId].fVBVAEnabled == false) /* A non-VBVA mode. */
2066 {
2067 if (pDisplay->mpDrv)
2068 {
2069 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppbData, pcbData, pcx, pcy);
2070 *pfMemFree = false;
2071 }
2072 else
2073 {
2074 /* No image. */
2075 *ppbData = NULL;
2076 *pcbData = 0;
2077 *pcx = 0;
2078 *pcy = 0;
2079 *pfMemFree = true;
2080 rc = VINF_SUCCESS;
2081 }
2082 }
2083 else if (aScreenId < pDisplay->mcMonitors)
2084 {
2085 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2086
2087 uint32_t width = pFBInfo->w;
2088 uint32_t height = pFBInfo->h;
2089
2090 /* Allocate 32 bit per pixel bitmap. */
2091 size_t cbRequired = width * 4 * height;
2092
2093 if (cbRequired)
2094 {
2095 uint8_t *pbDst = (uint8_t *)RTMemAlloc(cbRequired);
2096 if (pbDst != NULL)
2097 {
2098 if (pFBInfo->flags & VBVA_SCREEN_F_ACTIVE)
2099 {
2100 /* Copy guest VRAM to the allocated 32bpp buffer. */
2101 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
2102 int32_t xSrc = 0;
2103 int32_t ySrc = 0;
2104 uint32_t u32SrcWidth = width;
2105 uint32_t u32SrcHeight = height;
2106 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
2107 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2108
2109 int32_t xDst = 0;
2110 int32_t yDst = 0;
2111 uint32_t u32DstWidth = u32SrcWidth;
2112 uint32_t u32DstHeight = u32SrcHeight;
2113 uint32_t u32DstLineSize = u32DstWidth * 4;
2114 uint32_t u32DstBitsPerPixel = 32;
2115
2116 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2117 width, height,
2118 pu8Src,
2119 xSrc, ySrc,
2120 u32SrcWidth, u32SrcHeight,
2121 u32SrcLineSize, u32SrcBitsPerPixel,
2122 pbDst,
2123 xDst, yDst,
2124 u32DstWidth, u32DstHeight,
2125 u32DstLineSize, u32DstBitsPerPixel);
2126 }
2127 else
2128 {
2129 memset(pbDst, 0, cbRequired);
2130 rc = VINF_SUCCESS;
2131 }
2132 if (RT_SUCCESS(rc))
2133 {
2134 *ppbData = pbDst;
2135 *pcbData = cbRequired;
2136 *pcx = width;
2137 *pcy = height;
2138 *pfMemFree = true;
2139 }
2140 else
2141 {
2142 RTMemFree(pbDst);
2143
2144 /* CopyRect can fail if VBVA was paused in VGA device, retry using the generic method. */
2145 if ( rc == VERR_INVALID_STATE
2146 && aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2147 {
2148 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppbData, pcbData, pcx, pcy);
2149 *pfMemFree = false;
2150 }
2151 }
2152 }
2153 else
2154 rc = VERR_NO_MEMORY;
2155 }
2156 else
2157 {
2158 /* No image. */
2159 *ppbData = NULL;
2160 *pcbData = 0;
2161 *pcx = 0;
2162 *pcy = 0;
2163 *pfMemFree = true;
2164 rc = VINF_SUCCESS;
2165 }
2166 }
2167 else
2168 rc = VERR_INVALID_PARAMETER;
2169 return rc;
2170}
2171
2172static int i_displayTakeScreenshot(PUVM pUVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, ULONG aScreenId,
2173 BYTE *address, ULONG width, ULONG height)
2174{
2175#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2176 /*
2177 * CrOgl screenshot hook/hack.
2178 */
2179 if (Display::i_displayCheckTakeScreenshotCrOgl(pDisplay, aScreenId, (uint8_t *)address, width, height))
2180 return VINF_SUCCESS;
2181#endif
2182
2183 uint8_t *pbData = NULL;
2184 size_t cbData = 0;
2185 uint32_t cx = 0;
2186 uint32_t cy = 0;
2187 bool fFreeMem = false;
2188 int vrc = VINF_SUCCESS;
2189
2190 int cRetries = 5;
2191 while (cRetries-- > 0)
2192 {
2193 /* Note! Not sure if the priority call is such a good idea here, but
2194 it would be nice to have an accurate screenshot for the bug
2195 report if the VM deadlocks. */
2196 vrc = VMR3ReqPriorityCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)Display::i_displayTakeScreenshotEMT, 7,
2197 pDisplay, aScreenId, &pbData, &cbData, &cx, &cy, &fFreeMem);
2198 if (vrc != VERR_TRY_AGAIN)
2199 {
2200 break;
2201 }
2202
2203 RTThreadSleep(10);
2204 }
2205
2206 if (RT_SUCCESS(vrc) && pbData)
2207 {
2208 if (cx == width && cy == height)
2209 {
2210 /* No scaling required. */
2211 memcpy(address, pbData, cbData);
2212 }
2213 else
2214 {
2215 /* Scale. */
2216 LogRelFlowFunc(("SCALE: %dx%d -> %dx%d\n", cx, cy, width, height));
2217
2218 uint8_t *dst = address;
2219 uint8_t *src = pbData;
2220 int dstW = width;
2221 int dstH = height;
2222 int srcW = cx;
2223 int srcH = cy;
2224 int iDeltaLine = cx * 4;
2225
2226 BitmapScale32(dst,
2227 dstW, dstH,
2228 src,
2229 iDeltaLine,
2230 srcW, srcH);
2231 }
2232
2233 if (fFreeMem)
2234 RTMemFree(pbData);
2235 else
2236 {
2237 /* This can be called from any thread. */
2238 pDrv->pUpPort->pfnFreeScreenshot(pDrv->pUpPort, pbData);
2239 }
2240 }
2241
2242 return vrc;
2243}
2244
2245HRESULT Display::takeScreenShotWorker(ULONG aScreenId,
2246 BYTE *aAddress,
2247 ULONG aWidth,
2248 ULONG aHeight,
2249 BitmapFormat_T aBitmapFormat,
2250 ULONG *pcbOut)
2251{
2252 HRESULT rc = S_OK;
2253
2254 /* Do not allow too small and too large screenshots. This also filters out negative
2255 * values passed as either 'aWidth' or 'aHeight'.
2256 */
2257 CheckComArgExpr(aWidth, aWidth != 0 && aWidth <= 32767);
2258 CheckComArgExpr(aHeight, aHeight != 0 && aHeight <= 32767);
2259
2260 if ( aBitmapFormat != BitmapFormat_BGR0
2261 && aBitmapFormat != BitmapFormat_BGRA
2262 && aBitmapFormat != BitmapFormat_RGBA
2263 && aBitmapFormat != BitmapFormat_PNG)
2264 {
2265 return setError(E_NOTIMPL,
2266 tr("Unsupported screenshot format 0x%08X"), aBitmapFormat);
2267 }
2268
2269 Console::SafeVMPtr ptrVM(mParent);
2270 if (!ptrVM.isOk())
2271 return ptrVM.rc();
2272
2273 int vrc = i_displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, aAddress, aWidth, aHeight);
2274
2275 if (RT_SUCCESS(vrc))
2276 {
2277 const size_t cbData = aWidth * 4 * aHeight;
2278
2279 /* Most of uncompressed formats. */
2280 *pcbOut = (ULONG)cbData;
2281
2282 if (aBitmapFormat == BitmapFormat_BGR0)
2283 {
2284 /* Do nothing. */
2285 }
2286 else if (aBitmapFormat == BitmapFormat_BGRA)
2287 {
2288 uint32_t *pu32 = (uint32_t *)aAddress;
2289 size_t cPixels = aWidth * aHeight;
2290 while (cPixels--)
2291 {
2292 *pu32++ |= UINT32_C(0xFF000000);
2293 }
2294 }
2295 else if (aBitmapFormat == BitmapFormat_RGBA)
2296 {
2297 uint8_t *pu8 = aAddress;
2298 size_t cPixels = aWidth * aHeight;
2299 while (cPixels--)
2300 {
2301 uint8_t u8 = pu8[0];
2302 pu8[0] = pu8[2];
2303 pu8[2] = u8;
2304 pu8[3] = 0xFF;
2305
2306 pu8 += 4;
2307 }
2308 }
2309 else if (aBitmapFormat == BitmapFormat_PNG)
2310 {
2311 uint8_t *pu8PNG = NULL;
2312 uint32_t cbPNG = 0;
2313 uint32_t cxPNG = 0;
2314 uint32_t cyPNG = 0;
2315
2316 vrc = DisplayMakePNG(aAddress, aWidth, aHeight, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
2317 if (RT_SUCCESS(vrc))
2318 {
2319 if (cbPNG <= cbData)
2320 {
2321 memcpy(aAddress, pu8PNG, cbPNG);
2322 *pcbOut = cbPNG;
2323 }
2324 else
2325 {
2326 rc = setError(E_FAIL,
2327 tr("PNG is larger than 32bpp bitmap"));
2328 }
2329 }
2330 else
2331 {
2332 rc = setError(VBOX_E_IPRT_ERROR,
2333 tr("Could not convert screenshot to PNG (%Rrc)"), vrc);
2334 }
2335 RTMemFree(pu8PNG);
2336 }
2337 }
2338 else if (vrc == VERR_TRY_AGAIN)
2339 rc = setError(E_UNEXPECTED,
2340 tr("Screenshot is not available at this time"));
2341 else if (RT_FAILURE(vrc))
2342 rc = setError(VBOX_E_IPRT_ERROR,
2343 tr("Could not take a screenshot (%Rrc)"), vrc);
2344
2345 return rc;
2346}
2347
2348HRESULT Display::takeScreenShot(ULONG aScreenId,
2349 BYTE *aAddress,
2350 ULONG aWidth,
2351 ULONG aHeight,
2352 BitmapFormat_T aBitmapFormat)
2353{
2354 HRESULT rc = S_OK;
2355
2356 LogRelFlowFunc(("[%d] address=%p, width=%d, height=%d, format 0x%08X\n",
2357 aScreenId, aAddress, aWidth, aHeight, aBitmapFormat));
2358
2359 ULONG cbOut = 0;
2360 rc = takeScreenShotWorker(aScreenId, aAddress, aWidth, aHeight, aBitmapFormat, &cbOut);
2361 NOREF(cbOut);
2362
2363 LogRelFlowFunc(("%Rhrc\n", rc));
2364 return rc;
2365}
2366
2367HRESULT Display::takeScreenShotToArray(ULONG aScreenId,
2368 ULONG aWidth,
2369 ULONG aHeight,
2370 BitmapFormat_T aBitmapFormat,
2371 std::vector<BYTE> &aScreenData)
2372{
2373 HRESULT rc = S_OK;
2374
2375 LogRelFlowFunc(("[%d] width=%d, height=%d, format 0x%08X\n",
2376 aScreenId, aWidth, aHeight, aBitmapFormat));
2377
2378 /* Do not allow too small and too large screenshots. This also filters out negative
2379 * values passed as either 'aWidth' or 'aHeight'.
2380 */
2381 CheckComArgExpr(aWidth, aWidth != 0 && aWidth <= 32767);
2382 CheckComArgExpr(aHeight, aHeight != 0 && aHeight <= 32767);
2383
2384 const size_t cbData = aWidth * 4 * aHeight;
2385 aScreenData.resize(cbData);
2386
2387 ULONG cbOut = 0;
2388 rc = takeScreenShotWorker(aScreenId, &aScreenData.front(), aWidth, aHeight, aBitmapFormat, &cbOut);
2389 if (FAILED(rc))
2390 cbOut = 0;
2391
2392 aScreenData.resize(cbOut);
2393
2394 LogRelFlowFunc(("%Rhrc\n", rc));
2395 return rc;
2396}
2397
2398#ifdef VBOX_WITH_VIDEOREC
2399/**
2400 * Returns the currently enabled video capturing features.
2401 *
2402 * @returns Enables video capturing features.
2403 */
2404VIDEORECFEATURES Display::i_videoRecGetEnabled(void)
2405{
2406 return VideoRecGetEnabled(&mVideoRecCfg);
2407}
2408
2409/**
2410 * Returns whether video capturing is currently is active or not.
2411 *
2412 * @returns True if video capturing is active, false if not.
2413 */
2414bool Display::i_videoRecStarted(void)
2415{
2416 return VideoRecIsActive(mpVideoRecCtx);
2417}
2418
2419#ifdef VBOX_WITH_AUDIO_VIDEOREC
2420/**
2421 * Configures the video recording audio driver in CFGM.
2422 *
2423 * @returns VBox status code.
2424 * @param strDevice The PDM device name.
2425 * @param uInstance The PDM device instance.
2426 * @param uLUN The PDM LUN number of the driver.
2427 * @param fAttach Whether to attach or detach the driver configuration to CFGM.
2428 *
2429 * @thread EMT
2430 */
2431int Display::i_videoRecConfigureAudioDriver(const Utf8Str& strDevice,
2432 unsigned uInstance,
2433 unsigned uLUN,
2434 bool fAttach)
2435{
2436 if (strDevice.isEmpty()) /* No audio device configured. Bail out. */
2437 return VINF_SUCCESS;
2438
2439 AssertPtr(mParent);
2440
2441 ComPtr<IMachine> pMachine = mParent->i_machine();
2442 Assert(pMachine.isNotNull());
2443
2444 Console::SafeVMPtr ptrVM(mParent);
2445 Assert(ptrVM.isOk());
2446
2447 PUVM pUVM = ptrVM.rawUVM();
2448 AssertPtr(pUVM);
2449
2450 PCFGMNODE pRoot = CFGMR3GetRootU(pUVM);
2451 AssertPtr(pRoot);
2452 PCFGMNODE pDev0 = CFGMR3GetChildF(pRoot, "Devices/%s/%u/", strDevice.c_str(), uInstance);
2453 AssertPtr(pDev0);
2454
2455 PCFGMNODE pDevLun = CFGMR3GetChildF(pDev0, "LUN#%u/", uLUN);
2456
2457 if (fAttach)
2458 {
2459 if (!pDevLun)
2460 {
2461 LogRel2(("VideoRec: Attaching audio driver\n"));
2462
2463 PCFGMNODE pLunL0;
2464 CFGMR3InsertNodeF(pDev0, &pLunL0, "LUN#%RU8", uLUN);
2465 CFGMR3InsertString(pLunL0, "Driver", "AUDIO");
2466
2467 PCFGMNODE pCfg;
2468 CFGMR3InsertNode(pLunL0, "Config", &pCfg);
2469 CFGMR3InsertString (pCfg, "DriverName", "AudioVideoRec");
2470 CFGMR3InsertInteger(pCfg, "InputEnabled", 0);
2471 CFGMR3InsertInteger(pCfg, "OutputEnabled", 1);
2472
2473 PCFGMNODE pLunL1;
2474 CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1);
2475 CFGMR3InsertString(pLunL1, "Driver", "AudioVideoRec");
2476
2477 CFGMR3InsertNode(pLunL1, "Config", &pCfg);
2478 CFGMR3InsertInteger(pCfg, "Object", (uintptr_t)mParent->i_getAudioVideoRec());
2479 CFGMR3InsertInteger(pCfg, "ObjectConsole", (uintptr_t)mParent /* Console */);
2480 }
2481 }
2482 else /* Detach */
2483 {
2484 if (pDevLun)
2485 {
2486 LogRel2(("VideoRec: Detaching audio driver\n"));
2487 CFGMR3RemoveNode(pDevLun);
2488 }
2489 }
2490
2491 return VINF_SUCCESS;
2492}
2493#endif
2494
2495/**
2496 * Configures video capturing (via CFGM) and attaches / detaches the associated driver.
2497 * Currently this only is the audio driver (if available).
2498 *
2499 * @returns IPRT status code.
2500 * @param pThis Display instance to configure video capturing for.
2501 * @param pCfg Where to store the configuration into.
2502 * @param fAttachDetach Whether to attach/detach associated drivers or not.
2503 * @param puLUN On input, this defines the LUN to which the audio driver shall be assigned to.
2504 * On output, this returns the LUN the audio driver was assigned to.
2505 */
2506/* static */
2507DECLCALLBACK(int) Display::i_videoRecConfigure(Display *pThis, PVIDEORECCFG pCfg, bool fAttachDetach, unsigned *puLUN)
2508{
2509 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2510 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2511 AssertPtrReturn(puLUN, VERR_INVALID_POINTER);
2512
2513 AssertPtr(pThis->mParent);
2514 ComPtr<IMachine> pMachine = pThis->mParent->i_machine();
2515 Assert(pMachine.isNotNull());
2516
2517 pCfg->enmDst = VIDEORECDEST_FILE; /** @todo Make this configurable once we have more variations. */
2518
2519 /*
2520 * Cache parameters from API.
2521 */
2522 BOOL fEnabled;
2523 HRESULT rc = pMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
2524 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2525 pCfg->fEnabled = RT_BOOL(fEnabled);
2526
2527 com::SafeArray<BOOL> aScreens;
2528 rc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(aScreens));
2529 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2530
2531 pCfg->aScreens.resize(aScreens.size());
2532 for (size_t i = 0; i < aScreens.size(); ++i)
2533 pCfg->aScreens[i] = aScreens[i];
2534
2535 rc = pMachine->COMGETTER(VideoCaptureWidth)((ULONG *)&pCfg->Video.uWidth);
2536 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2537 rc = pMachine->COMGETTER(VideoCaptureHeight)((ULONG *)&pCfg->Video.uHeight);
2538 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2539 rc = pMachine->COMGETTER(VideoCaptureRate)((ULONG *)&pCfg->Video.uRate);
2540 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2541 rc = pMachine->COMGETTER(VideoCaptureFPS)((ULONG *)&pCfg->Video.uFPS);
2542 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2543 rc = pMachine->COMGETTER(VideoCaptureFile)(pCfg->File.strName.asOutParam());
2544 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2545 rc = pMachine->COMGETTER(VideoCaptureMaxFileSize)((ULONG *)&pCfg->File.uMaxSizeMB);
2546 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2547 rc = pMachine->COMGETTER(VideoCaptureMaxTime)((ULONG *)&pCfg->uMaxTimeS);
2548 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2549 BSTR bstrOptions;
2550 rc = pMachine->COMGETTER(VideoCaptureOptions)(&bstrOptions);
2551 AssertComRCReturn(rc, VERR_COM_UNEXPECTED);
2552
2553 /*
2554 * Set sensible defaults.
2555 */
2556 pCfg->Video.fEnabled = pCfg->fEnabled;
2557
2558 if (!pCfg->Video.uFPS) /* Prevent division by zero. */
2559 pCfg->Video.uFPS = 15;
2560
2561#ifdef VBOX_WITH_LIBVPX
2562 pCfg->Video.Codec.VPX.uEncoderDeadline = 1000000 / pCfg->Video.uFPS;
2563#endif
2564
2565 /* Note: Audio support is considered as being experimental, thus it's disabled by default.
2566 * There be dragons! */
2567#ifdef VBOX_WITH_AUDIO_VIDEOREC
2568 pCfg->Audio.fEnabled = false;
2569 /* By default we use 48kHz, 16-bit, stereo for the audio track. */
2570 pCfg->Audio.uHz = 48000;
2571 pCfg->Audio.cBits = 16;
2572 pCfg->Audio.cChannels = 2;
2573#endif
2574
2575 /*
2576 * Parse options string.
2577 */
2578 com::Utf8Str strOptions(bstrOptions);
2579 size_t pos = 0;
2580 com::Utf8Str key, value;
2581 while ((pos = strOptions.parseKeyValue(key, value, pos)) != com::Utf8Str::npos)
2582 {
2583 if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0)
2584 {
2585#ifdef VBOX_WITH_LIBVPX
2586 if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0)
2587 pCfg->Video.Codec.VPX.uEncoderDeadline = VPX_DL_REALTIME;
2588 else if (value.compare("good", Utf8Str::CaseInsensitive) == 0)
2589 pCfg->Video.Codec.VPX.uEncoderDeadline = 1000000 / pCfg->Video.uFPS;
2590 else if (value.compare("best", Utf8Str::CaseInsensitive) == 0)
2591 pCfg->Video.Codec.VPX.uEncoderDeadline = VPX_DL_BEST_QUALITY;
2592 else
2593 {
2594 LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str()));
2595 pCfg->Video.Codec.VPX.uEncoderDeadline = value.toUInt32();
2596#endif
2597 }
2598 }
2599 else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
2600 {
2601 if (value.compare("false", Utf8Str::CaseInsensitive) == 0)
2602 {
2603 pCfg->Video.fEnabled = false;
2604#ifdef VBOX_WITH_AUDIO_VIDEOREC
2605 LogRel(("VideoRec: Only audio will be recorded\n"));
2606#endif
2607 }
2608 }
2609 else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
2610 {
2611#ifdef VBOX_WITH_AUDIO_VIDEOREC
2612 if (value.compare("true", Utf8Str::CaseInsensitive) == 0)
2613 {
2614 pCfg->Audio.fEnabled = true;
2615
2616 }
2617 else
2618 LogRel(("VideoRec: Only video will be recorded\n"));
2619#endif
2620 }
2621 else if (key.compare("ac_profile", Utf8Str::CaseInsensitive) == 0)
2622 {
2623#ifdef VBOX_WITH_AUDIO_VIDEOREC
2624 if (value.compare("low", Utf8Str::CaseInsensitive) == 0)
2625 {
2626 pCfg->Audio.uHz = 8000;
2627 pCfg->Audio.cBits = 16;
2628 pCfg->Audio.cChannels = 1;
2629 }
2630 else if (value.startsWith("med" /* "med[ium]" */, Utf8Str::CaseInsensitive) == 0)
2631 {
2632 pCfg->Audio.uHz = 22050;
2633 pCfg->Audio.cBits = 16;
2634 pCfg->Audio.cChannels = 2;
2635 }
2636 else if (value.compare("high", Utf8Str::CaseInsensitive) == 0)
2637 {
2638 /* Stay with the default set above. */
2639 }
2640#endif
2641 }
2642 else
2643 LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str()));
2644
2645 } /* while */
2646
2647 unsigned uLUN = *puLUN;
2648
2649#ifdef VBOX_WITH_AUDIO_VIDEOREC
2650 ComPtr<IAudioAdapter> audioAdapter;
2651 rc = pMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
2652 AssertComRC(rc);
2653
2654 unsigned uInstance = 0;
2655
2656 Utf8Str strAudioDev = pThis->mParent->i_getAudioAdapterDeviceName(audioAdapter);
2657 if (!strAudioDev.isEmpty())
2658 {
2659 Console::SafeVMPtr ptrVM(pThis->mParent);
2660 Assert(ptrVM.isOk());
2661
2662 /*
2663 * Configure + attach audio driver.
2664 */
2665 int vrc2 = VINF_SUCCESS;
2666
2667 LogFunc(("Audio fAttachDetach=%RTbool, fEnabled=%RTbool\n", fAttachDetach, pCfg->Audio.fEnabled));
2668
2669 if (pCfg->Audio.fEnabled) /* Enable */
2670 {
2671 vrc2 = pThis->i_videoRecConfigureAudioDriver(strAudioDev, uInstance, uLUN, true /* fAttach */);
2672 if ( RT_SUCCESS(vrc2)
2673 && fAttachDetach)
2674 {
2675 vrc2 = PDMR3DriverAttach(ptrVM.rawUVM(), strAudioDev.c_str(), uInstance, uLUN, 0 /* fFlags */, NULL /* ppBase */);
2676 }
2677
2678 if (RT_FAILURE(vrc2))
2679 LogRel(("VideoRec: Failed to attach audio driver, rc=%Rrc\n", vrc2));
2680 }
2681 else /* Disable */
2682 {
2683 if (fAttachDetach)
2684 vrc2 = PDMR3DriverDetach(ptrVM.rawUVM(), strAudioDev.c_str(), uInstance, uLUN, "AUDIO",
2685 0 /* iOccurrence */, 0 /* fFlags */);
2686
2687 if (RT_SUCCESS(vrc2))
2688 {
2689 vrc2 = pThis->i_videoRecConfigureAudioDriver(strAudioDev, uInstance, uLUN, false /* fAttach */);
2690 }
2691
2692 if (RT_FAILURE(vrc2))
2693 LogRel(("VideoRec: Failed to detach audio driver, rc=%Rrc\n", vrc2));
2694 }
2695
2696 AssertRC(vrc2);
2697 }
2698 else
2699 LogRel2(("VideoRec: No audio hardware configured, skipping to record audio\n"));
2700#else
2701 RT_NOREF(fAttachDetach);
2702#endif
2703
2704 /*
2705 * Invalidate screens.
2706 */
2707 for (unsigned i = 0; i < pCfg->aScreens.size(); i++)
2708 {
2709 bool fChanged = pThis->maVideoRecEnabled[i] != RT_BOOL(pCfg->aScreens[i]);
2710
2711 pThis->maVideoRecEnabled[i] = RT_BOOL(pCfg->aScreens[i]);
2712
2713 if (fChanged && i < pThis->mcMonitors)
2714 pThis->i_videoRecScreenChanged(i);
2715
2716 }
2717
2718 if (puLUN)
2719 *puLUN = uLUN;
2720
2721 return S_OK;
2722}
2723
2724/**
2725 * Sends belonging audio samples to the video capturing code.
2726 * Does nothing if capturing is disabled or if audio support for video capturing is disabled.
2727 *
2728 * @returns IPRT status code.
2729 * @param pvData Audio data.
2730 * @param cbData Size (in bytes) of audio data.
2731 * @param uTimestampMs Timestamp (in ms) of the audio data.
2732 */
2733int Display::i_videoRecSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs)
2734{
2735 if ( VideoRecIsActive(mpVideoRecCtx)
2736 && VideoRecGetEnabled(&mVideoRecCfg) & VIDEORECFEATURE_AUDIO)
2737 {
2738 return VideoRecSendAudioFrame(mpVideoRecCtx, pvData, cbData, uTimestampMs);
2739 }
2740
2741 return VINF_SUCCESS;
2742}
2743
2744/**
2745 * Start video capturing. Does nothing if capturing is already active.
2746 *
2747 * @param pVideoRecCfg Video recording configuration to use.
2748 * @returns IPRT status code.
2749 */
2750int Display::i_videoRecStart(void)
2751{
2752 if (VideoRecIsActive(mpVideoRecCtx))
2753 return VINF_SUCCESS;
2754
2755 int rc = VideoRecContextCreate(mcMonitors, &mVideoRecCfg, &mpVideoRecCtx);
2756 if (RT_SUCCESS(rc))
2757 {
2758 for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
2759 {
2760 int rc2 = VideoRecStreamInit(mpVideoRecCtx, uScreen);
2761 if (RT_SUCCESS(rc2))
2762 {
2763 i_videoRecScreenChanged(uScreen);
2764 }
2765 else
2766 LogRel(("VideoRec: Failed to initialize video recording context #%u (%Rrc)\n", uScreen, rc2));
2767
2768 if (RT_SUCCESS(rc))
2769 rc = rc2;
2770 }
2771 }
2772
2773 if (RT_FAILURE(rc))
2774 LogRel(("VideoRec: Failed to start video recording (%Rrc)\n", rc));
2775
2776 return rc;
2777}
2778
2779/**
2780 * Stops video capturing. Does nothing if video capturing is not active.
2781 */
2782void Display::i_videoRecStop(void)
2783{
2784 if (!VideoRecIsActive(mpVideoRecCtx))
2785 return;
2786
2787 VideoRecContextDestroy(mpVideoRecCtx);
2788 mpVideoRecCtx = NULL;
2789
2790 unsigned uScreenId;
2791 for (uScreenId = 0; uScreenId < mcMonitors; ++uScreenId)
2792 i_videoRecScreenChanged(uScreenId);
2793}
2794
2795void Display::i_videoRecScreenChanged(unsigned uScreenId)
2796{
2797 if ( !VideoRecIsActive(mpVideoRecCtx)
2798 || !maVideoRecEnabled[uScreenId])
2799 {
2800 /* Skip recording this screen. */
2801 return;
2802 }
2803
2804 /* Get a new source bitmap which will be used by video recording code. */
2805 ComPtr<IDisplaySourceBitmap> pSourceBitmap;
2806 QuerySourceBitmap(uScreenId, pSourceBitmap.asOutParam());
2807
2808 int rc2 = RTCritSectEnter(&mVideoRecLock);
2809 if (RT_SUCCESS(rc2))
2810 {
2811 maFramebuffers[uScreenId].videoRec.pSourceBitmap = pSourceBitmap;
2812
2813 rc2 = RTCritSectLeave(&mVideoRecLock);
2814 AssertRC(rc2);
2815 }
2816}
2817#endif /* VBOX_WITH_VIDEOREC */
2818
2819int Display::i_drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address,
2820 ULONG x, ULONG y, ULONG width, ULONG height)
2821{
2822 int rc = VINF_SUCCESS;
2823
2824 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2825
2826 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2827 {
2828 rc = pDisplay->mpDrv->pUpPort->pfnDisplayBlt(pDisplay->mpDrv->pUpPort, address, x, y, width, height);
2829 }
2830 else if (aScreenId < pDisplay->mcMonitors)
2831 {
2832 /* Copy the bitmap to the guest VRAM. */
2833 const uint8_t *pu8Src = address;
2834 int32_t xSrc = 0;
2835 int32_t ySrc = 0;
2836 uint32_t u32SrcWidth = width;
2837 uint32_t u32SrcHeight = height;
2838 uint32_t u32SrcLineSize = width * 4;
2839 uint32_t u32SrcBitsPerPixel = 32;
2840
2841 uint8_t *pu8Dst = pFBInfo->pu8FramebufferVRAM;
2842 int32_t xDst = x;
2843 int32_t yDst = y;
2844 uint32_t u32DstWidth = pFBInfo->w;
2845 uint32_t u32DstHeight = pFBInfo->h;
2846 uint32_t u32DstLineSize = pFBInfo->u32LineSize;
2847 uint32_t u32DstBitsPerPixel = pFBInfo->u16BitsPerPixel;
2848
2849 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2850 width, height,
2851 pu8Src,
2852 xSrc, ySrc,
2853 u32SrcWidth, u32SrcHeight,
2854 u32SrcLineSize, u32SrcBitsPerPixel,
2855 pu8Dst,
2856 xDst, yDst,
2857 u32DstWidth, u32DstHeight,
2858 u32DstLineSize, u32DstBitsPerPixel);
2859 if (RT_SUCCESS(rc))
2860 {
2861 if (!pFBInfo->pSourceBitmap.isNull())
2862 {
2863 /* Update the changed screen area. When source bitmap uses VRAM directly, just notify
2864 * frontend to update. And for default format, render the guest VRAM to the source bitmap.
2865 */
2866 if ( pFBInfo->fDefaultFormat
2867 && !pFBInfo->fDisabled)
2868 {
2869 BYTE *pAddress = NULL;
2870 ULONG ulWidth = 0;
2871 ULONG ulHeight = 0;
2872 ULONG ulBitsPerPixel = 0;
2873 ULONG ulBytesPerLine = 0;
2874 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2875
2876 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
2877 &ulWidth,
2878 &ulHeight,
2879 &ulBitsPerPixel,
2880 &ulBytesPerLine,
2881 &bitmapFormat);
2882 if (SUCCEEDED(hrc))
2883 {
2884 pu8Src = pFBInfo->pu8FramebufferVRAM;
2885 xSrc = x;
2886 ySrc = y;
2887 u32SrcWidth = pFBInfo->w;
2888 u32SrcHeight = pFBInfo->h;
2889 u32SrcLineSize = pFBInfo->u32LineSize;
2890 u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2891
2892 /* Default format is 32 bpp. */
2893 pu8Dst = pAddress;
2894 xDst = xSrc;
2895 yDst = ySrc;
2896 u32DstWidth = u32SrcWidth;
2897 u32DstHeight = u32SrcHeight;
2898 u32DstLineSize = u32DstWidth * 4;
2899 u32DstBitsPerPixel = 32;
2900
2901 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2902 width, height,
2903 pu8Src,
2904 xSrc, ySrc,
2905 u32SrcWidth, u32SrcHeight,
2906 u32SrcLineSize, u32SrcBitsPerPixel,
2907 pu8Dst,
2908 xDst, yDst,
2909 u32DstWidth, u32DstHeight,
2910 u32DstLineSize, u32DstBitsPerPixel);
2911 }
2912 }
2913 }
2914
2915 pDisplay->i_handleDisplayUpdate(aScreenId, x, y, width, height);
2916 }
2917 }
2918 else
2919 {
2920 rc = VERR_INVALID_PARAMETER;
2921 }
2922
2923 if (RT_SUCCESS(rc))
2924 pDisplay->mParent->i_consoleVRDPServer()->SendUpdateBitmap(aScreenId, x, y, width, height);
2925
2926 return rc;
2927}
2928
2929HRESULT Display::drawToScreen(ULONG aScreenId, BYTE *aAddress, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
2930{
2931 /// @todo (r=dmik) this function may take too long to complete if the VM
2932 // is doing something like saving state right now. Which, in case if it
2933 // is called on the GUI thread, will make it unresponsive. We should
2934 // check the machine state here (by enclosing the check and VMRequCall
2935 // within the Console lock to make it atomic).
2936
2937 LogRelFlowFunc(("aAddress=%p, x=%d, y=%d, width=%d, height=%d\n",
2938 (void *)aAddress, aX, aY, aWidth, aHeight));
2939
2940 CheckComArgExpr(aWidth, aWidth != 0);
2941 CheckComArgExpr(aHeight, aHeight != 0);
2942
2943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2944
2945 CHECK_CONSOLE_DRV(mpDrv);
2946
2947 Console::SafeVMPtr ptrVM(mParent);
2948 if (!ptrVM.isOk())
2949 return ptrVM.rc();
2950
2951 /* Release lock because the call scheduled on EMT may also try to take it. */
2952 alock.release();
2953
2954 /*
2955 * Again we're lazy and make the graphics device do all the
2956 * dirty conversion work.
2957 */
2958 int rcVBox = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_drawToScreenEMT, 7,
2959 this, aScreenId, aAddress, aX, aY, aWidth, aHeight);
2960
2961 /*
2962 * If the function returns not supported, we'll have to do all the
2963 * work ourselves using the framebuffer.
2964 */
2965 HRESULT rc = S_OK;
2966 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
2967 {
2968 /** @todo implement generic fallback for screen blitting. */
2969 rc = E_NOTIMPL;
2970 }
2971 else if (RT_FAILURE(rcVBox))
2972 rc = setError(VBOX_E_IPRT_ERROR,
2973 tr("Could not draw to the screen (%Rrc)"), rcVBox);
2974/// @todo
2975// else
2976// {
2977// /* All ok. Redraw the screen. */
2978// handleDisplayUpdate(x, y, width, height);
2979// }
2980
2981 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2982 return rc;
2983}
2984
2985int Display::i_InvalidateAndUpdateEMT(Display *pDisplay, unsigned uId, bool fUpdateAll)
2986{
2987 LogRelFlowFunc(("uId=%d, fUpdateAll %d\n", uId, fUpdateAll));
2988
2989 unsigned uScreenId;
2990 for (uScreenId = (fUpdateAll ? 0 : uId); uScreenId < pDisplay->mcMonitors; uScreenId++)
2991 {
2992 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2993
2994 if ( !pFBInfo->fVBVAEnabled
2995 && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2996 {
2997 pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort, /* fFailOnResize = */ true);
2998 }
2999 else
3000 {
3001 if (!pFBInfo->fDisabled)
3002 {
3003 /* Render complete VRAM screen to the framebuffer.
3004 * When framebuffer uses VRAM directly, just notify it to update.
3005 */
3006 if (pFBInfo->fDefaultFormat && !pFBInfo->pSourceBitmap.isNull())
3007 {
3008 BYTE *pAddress = NULL;
3009 ULONG ulWidth = 0;
3010 ULONG ulHeight = 0;
3011 ULONG ulBitsPerPixel = 0;
3012 ULONG ulBytesPerLine = 0;
3013 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
3014
3015 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
3016 &ulWidth,
3017 &ulHeight,
3018 &ulBitsPerPixel,
3019 &ulBytesPerLine,
3020 &bitmapFormat);
3021 if (SUCCEEDED(hrc))
3022 {
3023 uint32_t width = pFBInfo->w;
3024 uint32_t height = pFBInfo->h;
3025
3026 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
3027 int32_t xSrc = 0;
3028 int32_t ySrc = 0;
3029 uint32_t u32SrcWidth = pFBInfo->w;
3030 uint32_t u32SrcHeight = pFBInfo->h;
3031 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
3032 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3033
3034 /* Default format is 32 bpp. */
3035 uint8_t *pu8Dst = pAddress;
3036 int32_t xDst = xSrc;
3037 int32_t yDst = ySrc;
3038 uint32_t u32DstWidth = u32SrcWidth;
3039 uint32_t u32DstHeight = u32SrcHeight;
3040 uint32_t u32DstLineSize = u32DstWidth * 4;
3041 uint32_t u32DstBitsPerPixel = 32;
3042
3043 /* if uWidth != pFBInfo->w and uHeight != pFBInfo->h
3044 * implies resize of Framebuffer is in progress and
3045 * copyrect should not be called.
3046 */
3047 if (ulWidth == pFBInfo->w && ulHeight == pFBInfo->h)
3048 {
3049 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
3050 width, height,
3051 pu8Src,
3052 xSrc, ySrc,
3053 u32SrcWidth, u32SrcHeight,
3054 u32SrcLineSize, u32SrcBitsPerPixel,
3055 pu8Dst,
3056 xDst, yDst,
3057 u32DstWidth, u32DstHeight,
3058 u32DstLineSize, u32DstBitsPerPixel);
3059 }
3060 }
3061 }
3062
3063 pDisplay->i_handleDisplayUpdate(uScreenId, 0, 0, pFBInfo->w, pFBInfo->h);
3064 }
3065 }
3066 if (!fUpdateAll)
3067 break;
3068 }
3069 LogRelFlowFunc(("done\n"));
3070 return VINF_SUCCESS;
3071}
3072
3073/**
3074 * Does a full invalidation of the VM display and instructs the VM
3075 * to update it immediately.
3076 *
3077 * @returns COM status code
3078 */
3079
3080HRESULT Display::invalidateAndUpdate()
3081{
3082 LogRelFlowFunc(("\n"));
3083
3084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3085
3086 CHECK_CONSOLE_DRV(mpDrv);
3087
3088 Console::SafeVMPtr ptrVM(mParent);
3089 if (!ptrVM.isOk())
3090 return ptrVM.rc();
3091
3092 HRESULT rc = S_OK;
3093
3094 LogRelFlowFunc(("Sending DPYUPDATE request\n"));
3095
3096 /* Have to release the lock when calling EMT. */
3097 alock.release();
3098
3099 int rcVBox = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
3100 3, this, 0, true);
3101 alock.acquire();
3102
3103 if (RT_FAILURE(rcVBox))
3104 rc = setError(VBOX_E_IPRT_ERROR,
3105 tr("Could not invalidate and update the screen (%Rrc)"), rcVBox);
3106
3107 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3108 return rc;
3109}
3110
3111HRESULT Display::invalidateAndUpdateScreen(ULONG aScreenId)
3112{
3113 LogRelFlowFunc(("\n"));
3114
3115 HRESULT rc = S_OK;
3116
3117 Console::SafeVMPtr ptrVM(mParent);
3118 if (!ptrVM.isOk())
3119 return ptrVM.rc();
3120
3121 int rcVBox = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
3122 3, this, aScreenId, false);
3123 if (RT_FAILURE(rcVBox))
3124 rc = setError(VBOX_E_IPRT_ERROR,
3125 tr("Could not invalidate and update the screen %d (%Rrc)"), aScreenId, rcVBox);
3126
3127 LogRelFlowFunc(("rc=%Rhrc\n", rc));
3128 return rc;
3129}
3130
3131HRESULT Display::completeVHWACommand(BYTE *aCommand)
3132{
3133#ifdef VBOX_WITH_VIDEOHWACCEL
3134 mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsync(mpDrv->pVBVACallbacks, (PVBOXVHWACMD)aCommand);
3135 return S_OK;
3136#else
3137 return E_NOTIMPL;
3138#endif
3139}
3140
3141HRESULT Display::viewportChanged(ULONG aScreenId, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
3142{
3143 AssertMsgReturn(aScreenId < mcMonitors, ("aScreendId=%d mcMonitors=%d\n", aScreenId, mcMonitors), E_INVALIDARG);
3144
3145#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3146 if (mfIsCr3DEnabled)
3147 {
3148 int rc = i_crViewportNotify(aScreenId, aX, aY, aWidth, aHeight);
3149 if (RT_FAILURE(rc))
3150 {
3151 DISPLAYFBINFO *pFb = &maFramebuffers[aScreenId];
3152 pFb->pendingViewportInfo.fPending = true;
3153 pFb->pendingViewportInfo.x = aX;
3154 pFb->pendingViewportInfo.y = aY;
3155 pFb->pendingViewportInfo.width = aWidth;
3156 pFb->pendingViewportInfo.height = aHeight;
3157 }
3158 }
3159#endif /* VBOX_WITH_CROGL && VBOX_WITH_HGCM */
3160
3161 /* The driver might not have been constructed yet */
3162 if (mpDrv && mpDrv->pUpPort->pfnSetViewport)
3163 mpDrv->pUpPort->pfnSetViewport(mpDrv->pUpPort, aScreenId, aX, aY, aWidth, aHeight);
3164
3165 return S_OK;
3166}
3167
3168HRESULT Display::querySourceBitmap(ULONG aScreenId,
3169 ComPtr<IDisplaySourceBitmap> &aDisplaySourceBitmap)
3170{
3171 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
3172
3173 Console::SafeVMPtr ptrVM(mParent);
3174 if (!ptrVM.isOk())
3175 return ptrVM.rc();
3176
3177 bool fSetRenderVRAM = false;
3178 bool fInvalidate = false;
3179
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 if (aScreenId >= mcMonitors)
3183 return setError(E_INVALIDARG, tr("QuerySourceBitmap: Invalid screen %d (total %d)"),
3184 aScreenId, mcMonitors);
3185
3186 if (!mfSourceBitmapEnabled)
3187 {
3188 aDisplaySourceBitmap = NULL;
3189 return E_FAIL;
3190 }
3191
3192 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
3193
3194 /* No source bitmap for a blank guest screen. */
3195 if (pFBInfo->flags & VBVA_SCREEN_F_BLANK)
3196 {
3197 aDisplaySourceBitmap = NULL;
3198 return E_FAIL;
3199 }
3200
3201 HRESULT hr = S_OK;
3202
3203 if (pFBInfo->pSourceBitmap.isNull())
3204 {
3205 /* Create a new object. */
3206 ComObjPtr<DisplaySourceBitmap> obj;
3207 hr = obj.createObject();
3208 if (SUCCEEDED(hr))
3209 hr = obj->init(this, aScreenId, pFBInfo);
3210
3211 if (SUCCEEDED(hr))
3212 {
3213 pFBInfo->pSourceBitmap = obj;
3214 pFBInfo->fDefaultFormat = !obj->i_usesVRAM();
3215
3216 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3217 {
3218 /* Start buffer updates. */
3219 BYTE *pAddress = NULL;
3220 ULONG ulWidth = 0;
3221 ULONG ulHeight = 0;
3222 ULONG ulBitsPerPixel = 0;
3223 ULONG ulBytesPerLine = 0;
3224 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
3225
3226 pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
3227 &ulWidth,
3228 &ulHeight,
3229 &ulBitsPerPixel,
3230 &ulBytesPerLine,
3231 &bitmapFormat);
3232
3233 mpDrv->IConnector.pbData = pAddress;
3234 mpDrv->IConnector.cbScanline = ulBytesPerLine;
3235 mpDrv->IConnector.cBits = ulBitsPerPixel;
3236 mpDrv->IConnector.cx = ulWidth;
3237 mpDrv->IConnector.cy = ulHeight;
3238
3239 fSetRenderVRAM = pFBInfo->fDefaultFormat;
3240 }
3241
3242 /* Make sure that the bitmap contains the latest image. */
3243 fInvalidate = pFBInfo->fDefaultFormat;
3244 }
3245 }
3246
3247 if (SUCCEEDED(hr))
3248 {
3249 pFBInfo->pSourceBitmap.queryInterfaceTo(aDisplaySourceBitmap.asOutParam());
3250 }
3251
3252 /* Leave the IDisplay lock because the VGA device must not be called under it. */
3253 alock.release();
3254
3255 if (SUCCEEDED(hr))
3256 {
3257 if (fSetRenderVRAM)
3258 {
3259 mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, true);
3260 }
3261
3262 if (fInvalidate)
3263 VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
3264 3, this, aScreenId, false);
3265 }
3266
3267 LogRelFlowFunc(("%Rhrc\n", hr));
3268 return hr;
3269}
3270
3271HRESULT Display::getGuestScreenLayout(std::vector<ComPtr<IGuestScreenInfo> > &aGuestScreenLayout)
3272{
3273 NOREF(aGuestScreenLayout);
3274 return E_NOTIMPL;
3275}
3276
3277HRESULT Display::setScreenLayout(ScreenLayoutMode_T aScreenLayoutMode,
3278 const std::vector<ComPtr<IGuestScreenInfo> > &aGuestScreenInfo)
3279{
3280 NOREF(aScreenLayoutMode);
3281 NOREF(aGuestScreenInfo);
3282 return E_NOTIMPL;
3283}
3284
3285HRESULT Display::detachScreens(const std::vector<LONG> &aScreenIds)
3286{
3287 NOREF(aScreenIds);
3288 return E_NOTIMPL;
3289}
3290
3291// wrapped IEventListener method
3292HRESULT Display::handleEvent(const ComPtr<IEvent> &aEvent)
3293{
3294 VBoxEventType_T aType = VBoxEventType_Invalid;
3295
3296 aEvent->COMGETTER(Type)(&aType);
3297 switch (aType)
3298 {
3299 case VBoxEventType_OnStateChanged:
3300 {
3301 ComPtr<IStateChangedEvent> scev = aEvent;
3302 Assert(scev);
3303 MachineState_T machineState;
3304 scev->COMGETTER(State)(&machineState);
3305 if ( machineState == MachineState_Running
3306 || machineState == MachineState_Teleporting
3307 || machineState == MachineState_LiveSnapshotting
3308 || machineState == MachineState_DeletingSnapshotOnline
3309 )
3310 {
3311 LogRelFlowFunc(("Machine is running.\n"));
3312
3313#ifdef VBOX_WITH_CROGL
3314 i_crOglWindowsShow(true);
3315#endif
3316 }
3317 else
3318 {
3319#ifdef VBOX_WITH_CROGL
3320 if (machineState == MachineState_Paused)
3321 i_crOglWindowsShow(false);
3322#endif
3323 }
3324 break;
3325 }
3326 default:
3327 AssertFailed();
3328 }
3329
3330 return S_OK;
3331}
3332
3333
3334// private methods
3335/////////////////////////////////////////////////////////////////////////////
3336
3337#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3338int Display::i_crViewportNotify(ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
3339{
3340 VMMDev *pVMMDev = mParent->i_getVMMDev();
3341 if (!pVMMDev)
3342 return VERR_INVALID_STATE;
3343
3344 size_t cbData = RT_UOFFSETOF(VBOXCRCMDCTL_HGCM, aParms[5]);
3345 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM *)alloca(cbData);
3346
3347 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3348 pData->Hdr.u32Function = SHCRGL_HOST_FN_VIEWPORT_CHANGED;
3349
3350 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
3351 pData->aParms[0].u.uint32 = aScreenId;
3352
3353 pData->aParms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
3354 pData->aParms[1].u.uint32 = x;
3355
3356 pData->aParms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
3357 pData->aParms[2].u.uint32 = y;
3358
3359 pData->aParms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
3360 pData->aParms[3].u.uint32 = width;
3361
3362 pData->aParms[4].type = VBOX_HGCM_SVC_PARM_32BIT;
3363 pData->aParms[4].u.uint32 = height;
3364
3365 return i_crCtlSubmitSyncIfHasDataForScreen(aScreenId, &pData->Hdr, (uint32_t)cbData);
3366}
3367#endif
3368
3369#ifdef VBOX_WITH_CRHGSMI
3370void Display::i_setupCrHgsmiData(void)
3371{
3372 VMMDev *pVMMDev = mParent->i_getVMMDev();
3373 Assert(pVMMDev);
3374 int rc = RTCritSectRwEnterExcl(&mCrOglLock);
3375 AssertRC(rc);
3376
3377 if (pVMMDev)
3378 rc = pVMMDev->hgcmHostSvcHandleCreate("VBoxSharedCrOpenGL", &mhCrOglSvc);
3379 else
3380 rc = VERR_GENERAL_FAILURE;
3381
3382 if (RT_SUCCESS(rc))
3383 {
3384 Assert(mhCrOglSvc);
3385 /* setup command completion callback */
3386 VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB Completion;
3387 Completion.Hdr.enmType = VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB;
3388 Completion.Hdr.cbCmd = sizeof(Completion);
3389 Completion.hCompletion = mpDrv->pVBVACallbacks;
3390 Completion.pfnCompletion = mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync;
3391
3392 VBOXHGCMSVCPARM parm;
3393 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3394 parm.u.pointer.addr = &Completion;
3395 parm.u.pointer.size = 0;
3396
3397 rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_CRHGSMI_CTL, 1, &parm);
3398 if (RT_SUCCESS(rc))
3399 mCrOglCallbacks = Completion.MainInterface;
3400 else
3401 AssertMsgFailed(("VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION failed (rc=%Rrc)\n", rc));
3402 }
3403
3404 if (RT_FAILURE(rc))
3405 mhCrOglSvc = NULL;
3406
3407 RTCritSectRwLeaveExcl(&mCrOglLock);
3408}
3409
3410void Display::i_destructCrHgsmiData(void)
3411{
3412 int rc = RTCritSectRwEnterExcl(&mCrOglLock);
3413 AssertRC(rc);
3414 mhCrOglSvc = NULL;
3415 RTCritSectRwLeaveExcl(&mCrOglLock);
3416}
3417#endif /* VBOX_WITH_CRHGSMI */
3418
3419/**
3420 * Handle display resize event issued by the VGA device for the primary screen.
3421 *
3422 * @see PDMIDISPLAYCONNECTOR::pfnResize
3423 */
3424DECLCALLBACK(int) Display::i_displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
3425 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
3426{
3427 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3428 Display *pThis = pDrv->pDisplay;
3429
3430 LogRelFlowFunc(("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
3431 bpp, pvVRAM, cbLine, cx, cy));
3432
3433 bool f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, true, false);
3434 if (!f)
3435 {
3436 /* This is a result of recursive call when the source bitmap is being updated
3437 * during a VGA resize. Tell the VGA device to ignore the call.
3438 *
3439 * @todo It is a workaround, actually pfnUpdateDisplayAll must
3440 * fail on resize.
3441 */
3442 LogRel(("displayResizeCallback: already processing\n"));
3443 return VINF_VGA_RESIZE_IN_PROGRESS;
3444 }
3445
3446 int rc = pThis->i_handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy, 0, 0, 0, true);
3447
3448 /* Restore the flag. */
3449 f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, false, true);
3450 AssertRelease(f);
3451
3452 return rc;
3453}
3454
3455/**
3456 * Handle display update.
3457 *
3458 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
3459 */
3460DECLCALLBACK(void) Display::i_displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
3461 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3462{
3463 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3464
3465#ifdef DEBUG_sunlover
3466 LogFlowFunc(("fVideoAccelEnabled = %d, %d,%d %dx%d\n",
3467 pDrv->pDisplay->mVideoAccelLegacy.fVideoAccelEnabled, x, y, cx, cy));
3468#endif /* DEBUG_sunlover */
3469
3470 /* This call does update regardless of VBVA status.
3471 * But in VBVA mode this is called only as result of
3472 * pfnUpdateDisplayAll in the VGA device.
3473 */
3474
3475 pDrv->pDisplay->i_handleDisplayUpdate(VBOX_VIDEO_PRIMARY_SCREEN, x, y, cx, cy);
3476}
3477
3478/**
3479 * Periodic display refresh callback.
3480 *
3481 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
3482 * @thread EMT
3483 */
3484/*static*/ DECLCALLBACK(void) Display::i_displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
3485{
3486 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3487
3488#ifdef DEBUG_sunlover_2
3489 LogFlowFunc(("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
3490 pDrv->pDisplay->mfVideoAccelEnabled));
3491#endif /* DEBUG_sunlover_2 */
3492
3493 Display *pDisplay = pDrv->pDisplay;
3494 unsigned uScreenId;
3495
3496 int rc = pDisplay->i_videoAccelRefreshProcess(pDrv->pUpPort);
3497 if (rc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
3498 {
3499 if (rc == VWRN_INVALID_STATE)
3500 {
3501 /* No VBVA do a display update. */
3502 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
3503 }
3504
3505 /* Inform the VRDP server that the current display update sequence is
3506 * completed. At this moment the framebuffer memory contains a definite
3507 * image, that is synchronized with the orders already sent to VRDP client.
3508 * The server can now process redraw requests from clients or initial
3509 * fullscreen updates for new clients.
3510 */
3511 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3512 {
3513 Assert(pDisplay->mParent && pDisplay->mParent->i_consoleVRDPServer());
3514 pDisplay->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, NULL, 0);
3515 }
3516 }
3517
3518#ifdef VBOX_WITH_VIDEOREC
3519 if ( VideoRecIsActive(pDisplay->mpVideoRecCtx)
3520 && VideoRecGetEnabled(&pDisplay->mVideoRecCfg) & VIDEORECFEATURE_VIDEO)
3521 {
3522 do {
3523# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3524 if (pDisplay->mfIsCr3DEnabled)
3525 {
3526 if (ASMAtomicCmpXchgU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_SUBMITTED, CRVREC_STATE_IDLE))
3527 {
3528 if ( pDisplay->mCrOglCallbacks.pfnHasData
3529 && pDisplay->mCrOglCallbacks.pfnHasData())
3530 {
3531 /* submit */
3532 VBOXCRCMDCTL_HGCM *pData = &pDisplay->mCrOglScreenshotCtl;
3533
3534 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3535 pData->Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
3536
3537 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
3538 pData->aParms[0].u.pointer.addr = &pDisplay->mCrOglScreenshotData;
3539 pData->aParms[0].u.pointer.size = sizeof(pDisplay->mCrOglScreenshotData);
3540 rc = pDisplay->i_crCtlSubmit(&pData->Hdr, sizeof(*pData), Display::i_displayVRecCompletion, pDisplay);
3541 if (RT_SUCCESS(rc))
3542 break;
3543 AssertMsgFailed(("crCtlSubmit failed (rc=%Rrc)\n", rc));
3544 }
3545
3546 /* no 3D data available, or error has occured,
3547 * go the straight way */
3548 ASMAtomicWriteU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_IDLE);
3549 }
3550 else
3551 {
3552 /* record request is still in progress, don't do anything */
3553 break;
3554 }
3555 }
3556# endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
3557
3558 uint64_t u64Now = RTTimeProgramMilliTS();
3559 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3560 {
3561 if (!pDisplay->maVideoRecEnabled[uScreenId])
3562 continue;
3563
3564 if (VideoRecIsLimitReached(pDisplay->mpVideoRecCtx, uScreenId, u64Now))
3565 {
3566 pDisplay->i_videoRecStop();
3567 pDisplay->mParent->i_machine()->COMSETTER(VideoCaptureEnabled)(false);
3568 break;
3569 }
3570
3571 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3572 if (!pFBInfo->fDisabled)
3573 {
3574 ComPtr<IDisplaySourceBitmap> pSourceBitmap;
3575 int rc2 = RTCritSectEnter(&pDisplay->mVideoRecLock);
3576 if (RT_SUCCESS(rc2))
3577 {
3578 pSourceBitmap = pFBInfo->videoRec.pSourceBitmap;
3579 RTCritSectLeave(&pDisplay->mVideoRecLock);
3580 }
3581
3582 if (!pSourceBitmap.isNull())
3583 {
3584 BYTE *pbAddress = NULL;
3585 ULONG ulWidth = 0;
3586 ULONG ulHeight = 0;
3587 ULONG ulBitsPerPixel = 0;
3588 ULONG ulBytesPerLine = 0;
3589 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
3590 HRESULT hr = pSourceBitmap->QueryBitmapInfo(&pbAddress,
3591 &ulWidth,
3592 &ulHeight,
3593 &ulBitsPerPixel,
3594 &ulBytesPerLine,
3595 &bitmapFormat);
3596 if (SUCCEEDED(hr) && pbAddress)
3597 rc = VideoRecSendVideoFrame(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
3598 BitmapFormat_BGR,
3599 ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight,
3600 pbAddress, u64Now);
3601 else
3602 rc = VERR_NOT_SUPPORTED;
3603
3604 pSourceBitmap.setNull();
3605 }
3606 else
3607 rc = VERR_NOT_SUPPORTED;
3608
3609 if (rc == VINF_TRY_AGAIN)
3610 break;
3611 }
3612 }
3613 } while (0);
3614 }
3615#endif /* VBOX_WITH_VIDEOREC */
3616
3617#ifdef DEBUG_sunlover_2
3618 LogFlowFunc(("leave\n"));
3619#endif /* DEBUG_sunlover_2 */
3620}
3621
3622/**
3623 * Reset notification
3624 *
3625 * @see PDMIDISPLAYCONNECTOR::pfnReset
3626 */
3627DECLCALLBACK(void) Display::i_displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
3628{
3629 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3630
3631 LogRelFlowFunc(("\n"));
3632
3633 /* Disable VBVA mode. */
3634 pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
3635}
3636
3637/**
3638 * LFBModeChange notification
3639 *
3640 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
3641 */
3642DECLCALLBACK(void) Display::i_displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
3643{
3644 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3645
3646 LogRelFlowFunc(("fEnabled=%d\n", fEnabled));
3647
3648 NOREF(fEnabled);
3649
3650 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
3651 pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
3652}
3653
3654/**
3655 * Adapter information change notification.
3656 *
3657 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
3658 */
3659DECLCALLBACK(void) Display::i_displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM,
3660 uint32_t u32VRAMSize)
3661{
3662 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3663 pDrv->pDisplay->processAdapterData(pvVRAM, u32VRAMSize);
3664}
3665
3666/**
3667 * Display information change notification.
3668 *
3669 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
3670 */
3671DECLCALLBACK(void) Display::i_displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface,
3672 void *pvVRAM, unsigned uScreenId)
3673{
3674 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3675 pDrv->pDisplay->processDisplayData(pvVRAM, uScreenId);
3676}
3677
3678#ifdef VBOX_WITH_VIDEOHWACCEL
3679
3680#ifndef S_FALSE
3681# define S_FALSE ((HRESULT)1L)
3682#endif
3683
3684int Display::i_handleVHWACommandProcess(PVBOXVHWACMD pCommand)
3685{
3686 unsigned id = (unsigned)pCommand->iDisplay;
3687 if (id >= mcMonitors)
3688 return VERR_INVALID_PARAMETER;
3689
3690 ComPtr<IFramebuffer> pFramebuffer;
3691 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
3692 pFramebuffer = maFramebuffers[id].pFramebuffer;
3693 bool fVHWASupported = RT_BOOL(maFramebuffers[id].u32Caps & FramebufferCapabilities_VHWA);
3694 arlock.release();
3695
3696 if (pFramebuffer == NULL || !fVHWASupported)
3697 return VERR_NOT_IMPLEMENTED; /* Implementation is not available. */
3698
3699 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
3700 if (hr == S_FALSE)
3701 return VINF_SUCCESS;
3702 if (SUCCEEDED(hr))
3703 return VINF_CALLBACK_RETURN;
3704 if (hr == E_ACCESSDENIED)
3705 return VERR_INVALID_STATE; /* notify we can not handle request atm */
3706 if (hr == E_NOTIMPL)
3707 return VERR_NOT_IMPLEMENTED;
3708 return VERR_GENERAL_FAILURE;
3709}
3710
3711DECLCALLBACK(int) Display::i_displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
3712{
3713 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3714
3715 return pDrv->pDisplay->i_handleVHWACommandProcess(pCommand);
3716}
3717#endif
3718
3719#ifdef VBOX_WITH_CRHGSMI
3720void Display::i_handleCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
3721{
3722 RT_NOREF(u32Function);
3723 mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync(mpDrv->pVBVACallbacks,
3724 (PVBOXVDMACMD_CHROMIUM_CMD)pParam->u.pointer.addr, result);
3725}
3726
3727void Display::i_handleCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
3728{
3729 RT_NOREF(u32Function);
3730 PVBOXVDMACMD_CHROMIUM_CTL pCtl = (PVBOXVDMACMD_CHROMIUM_CTL)pParam->u.pointer.addr;
3731 mpDrv->pVBVACallbacks->pfnCrHgsmiControlCompleteAsync(mpDrv->pVBVACallbacks, pCtl, result);
3732}
3733
3734void Display::i_handleCrHgsmiCommandProcess(PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
3735{
3736 int rc = VERR_NOT_SUPPORTED;
3737 VBOXHGCMSVCPARM parm;
3738 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3739 parm.u.pointer.addr = pCmd;
3740 parm.u.pointer.size = cbCmd;
3741
3742 if (mhCrOglSvc)
3743 {
3744 VMMDev *pVMMDev = mParent->i_getVMMDev();
3745 if (pVMMDev)
3746 {
3747 /* no completion callback is specified with this call,
3748 * the CrOgl code will complete the CrHgsmi command once it processes it */
3749 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm, NULL, NULL);
3750 AssertRC(rc);
3751 if (RT_SUCCESS(rc))
3752 return;
3753 }
3754 else
3755 rc = VERR_INVALID_STATE;
3756 }
3757
3758 /* we are here because something went wrong with command processing, complete it */
3759 i_handleCrHgsmiCommandCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm);
3760}
3761
3762void Display::i_handleCrHgsmiControlProcess(PVBOXVDMACMD_CHROMIUM_CTL pCtl, uint32_t cbCtl)
3763{
3764 int rc = VERR_NOT_SUPPORTED;
3765 VBOXHGCMSVCPARM parm;
3766 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3767 parm.u.pointer.addr = pCtl;
3768 parm.u.pointer.size = cbCtl;
3769
3770 if (mhCrOglSvc)
3771 {
3772 VMMDev *pVMMDev = mParent->i_getVMMDev();
3773 if (pVMMDev)
3774 {
3775 bool fCheckPendingViewport = (pCtl->enmType == VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP);
3776 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm,
3777 Display::i_displayCrHgsmiControlCompletion, this);
3778 AssertRC(rc);
3779 if (RT_SUCCESS(rc))
3780 {
3781 if (fCheckPendingViewport)
3782 {
3783 ULONG ul;
3784 for (ul = 0; ul < mcMonitors; ul++)
3785 {
3786 DISPLAYFBINFO *pFb = &maFramebuffers[ul];
3787 if (!pFb->pendingViewportInfo.fPending)
3788 continue;
3789
3790 rc = i_crViewportNotify(ul, pFb->pendingViewportInfo.x, pFb->pendingViewportInfo.y,
3791 pFb->pendingViewportInfo.width, pFb->pendingViewportInfo.height);
3792 if (RT_SUCCESS(rc))
3793 pFb->pendingViewportInfo.fPending = false;
3794 else
3795 {
3796 AssertMsgFailed(("crViewportNotify failed (rc=%Rrc)\n", rc));
3797 rc = VINF_SUCCESS;
3798 }
3799 }
3800 }
3801 return;
3802 }
3803 }
3804 else
3805 rc = VERR_INVALID_STATE;
3806 }
3807
3808 /* we are here because something went wrong with command processing, complete it */
3809 i_handleCrHgsmiControlCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm);
3810}
3811
3812DECLCALLBACK(void) Display::i_displayCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd,
3813 uint32_t cbCmd)
3814{
3815 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3816
3817 pDrv->pDisplay->i_handleCrHgsmiCommandProcess(pCmd, cbCmd);
3818}
3819
3820DECLCALLBACK(void) Display::i_displayCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd,
3821 uint32_t cbCmd)
3822{
3823 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3824
3825 pDrv->pDisplay->i_handleCrHgsmiControlProcess(pCmd, cbCmd);
3826}
3827
3828DECLCALLBACK(void) Display::i_displayCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
3829 void *pvContext)
3830{
3831 AssertMsgFailed(("not expected!\n"));
3832 Display *pDisplay = (Display *)pvContext;
3833 pDisplay->i_handleCrHgsmiCommandCompletion(result, u32Function, pParam);
3834}
3835
3836DECLCALLBACK(void) Display::i_displayCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
3837 void *pvContext)
3838{
3839 Display *pDisplay = (Display *)pvContext;
3840 pDisplay->i_handleCrHgsmiControlCompletion(result, u32Function, pParam);
3841
3842}
3843#endif
3844
3845#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3846DECLCALLBACK(void) Display::i_displayCrHgcmCtlSubmitCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
3847 void *pvContext)
3848{
3849 RT_NOREF(u32Function);
3850 VBOXCRCMDCTL *pCmd = (VBOXCRCMDCTL*)pParam->u.pointer.addr;
3851 if (pCmd->u.pfnInternal)
3852 ((PFNCRCTLCOMPLETION)pCmd->u.pfnInternal)(pCmd, pParam->u.pointer.size, result, pvContext);
3853}
3854
3855int Display::i_handleCrHgcmCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
3856 PFNCRCTLCOMPLETION pfnCompletion,
3857 void *pvCompletion)
3858{
3859 VMMDev *pVMMDev = mParent ? mParent->i_getVMMDev() : NULL;
3860 if (!pVMMDev)
3861 {
3862 AssertMsgFailed(("no vmmdev\n"));
3863 return VERR_INVALID_STATE;
3864 }
3865
3866 Assert(mhCrOglSvc);
3867 VBOXHGCMSVCPARM parm;
3868 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3869 parm.u.pointer.addr = pCmd;
3870 parm.u.pointer.size = cbCmd;
3871
3872 pCmd->u.pfnInternal = (void(*)())pfnCompletion;
3873 int rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CTL, &parm, i_displayCrHgcmCtlSubmitCompletion,
3874 pvCompletion);
3875 if (!RT_SUCCESS(rc))
3876 AssertMsgFailed(("hgcmHostFastCallAsync failed (rc=%Rrc)\n", rc));
3877
3878 return rc;
3879}
3880
3881DECLCALLBACK(int) Display::i_displayCrHgcmCtlSubmit(PPDMIDISPLAYCONNECTOR pInterface,
3882 struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
3883 PFNCRCTLCOMPLETION pfnCompletion,
3884 void *pvCompletion)
3885{
3886 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3887 Display *pThis = pDrv->pDisplay;
3888 return pThis->i_handleCrHgcmCtlSubmit(pCmd, cbCmd, pfnCompletion, pvCompletion);
3889}
3890
3891int Display::i_crCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, PFNCRCTLCOMPLETION pfnCompletion, void *pvCompletion)
3892{
3893 int rc = RTCritSectRwEnterShared(&mCrOglLock);
3894 if (RT_SUCCESS(rc))
3895 {
3896 if (mhCrOglSvc)
3897 rc = mpDrv->pVBVACallbacks->pfnCrCtlSubmit(mpDrv->pVBVACallbacks, pCmd, cbCmd, pfnCompletion, pvCompletion);
3898 else
3899 rc = VERR_NOT_SUPPORTED;
3900
3901 RTCritSectRwLeaveShared(&mCrOglLock);
3902 }
3903 return rc;
3904}
3905
3906int Display::i_crCtlSubmitSync(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
3907{
3908 int rc = RTCritSectRwEnterShared(&mCrOglLock);
3909 if (RT_SUCCESS(rc))
3910 {
3911 if (mhCrOglSvc)
3912 rc = mpDrv->pVBVACallbacks->pfnCrCtlSubmitSync(mpDrv->pVBVACallbacks, pCmd, cbCmd);
3913 else
3914 rc = VERR_NOT_SUPPORTED;
3915
3916 RTCritSectRwLeaveShared(&mCrOglLock);
3917 }
3918 return rc;
3919}
3920
3921int Display::i_crCtlSubmitAsyncCmdCopy(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
3922{
3923 VBOXCRCMDCTL* pCmdCopy = (VBOXCRCMDCTL*)RTMemAlloc(cbCmd);
3924 if (!pCmdCopy)
3925 {
3926 LogRel(("RTMemAlloc failed\n"));
3927 return VERR_NO_MEMORY;
3928 }
3929
3930 memcpy(pCmdCopy, pCmd, cbCmd);
3931
3932 int rc = i_crCtlSubmit(pCmdCopy, cbCmd, i_displayCrCmdFree, pCmdCopy);
3933 if (RT_FAILURE(rc))
3934 {
3935 LogRel(("crCtlSubmit failed (rc=%Rrc)\n", rc));
3936 RTMemFree(pCmdCopy);
3937 return rc;
3938 }
3939
3940 return VINF_SUCCESS;
3941}
3942
3943int Display::i_crCtlSubmitSyncIfHasDataForScreen(uint32_t u32ScreenID, struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
3944{
3945 int rc = RTCritSectRwEnterShared(&mCrOglLock);
3946 AssertRCReturn(rc, rc);
3947
3948 if ( mCrOglCallbacks.pfnHasDataForScreen
3949 && mCrOglCallbacks.pfnHasDataForScreen(u32ScreenID))
3950 rc = i_crCtlSubmitSync(pCmd, cbCmd);
3951 else
3952 rc = i_crCtlSubmitAsyncCmdCopy(pCmd, cbCmd);
3953
3954 RTCritSectRwLeaveShared(&mCrOglLock);
3955
3956 return rc;
3957}
3958
3959bool Display::i_handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t u64Timestamp)
3960{
3961 /** @todo r=bird: u64Timestamp - using the 'u64' prefix add nothing.
3962 * However, using one of the prefixes indicating the timestamp unit
3963 * would be very valuable! */
3964# ifdef VBOX_WITH_VIDEOREC
3965 return VideoRecIsReady(mpVideoRecCtx, uScreen, u64Timestamp);
3966# else
3967 RT_NOREF(uScreen, u64Timestamp);
3968 return false;
3969# endif
3970}
3971
3972void Display::i_handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t u64Timestamp)
3973{
3974 RT_NOREF(uScreen, u64Timestamp);
3975}
3976
3977void Display::i_handleCrVRecScreenshotPerform(uint32_t uScreen,
3978 uint32_t x, uint32_t y, uint32_t uPixelFormat,
3979 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
3980 uint32_t uGuestWidth, uint32_t uGuestHeight,
3981 uint8_t *pu8BufferAddress, uint64_t u64Timestamp)
3982{
3983 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
3984# ifdef VBOX_WITH_VIDEOREC
3985 if ( VideoRecIsActive(mpVideoRecCtx)
3986 && VideoRecGetEnabled(&mVideoRecCfg) & VIDEORECFEATURE_VIDEO)
3987 {
3988 int rc2 = VideoRecSendVideoFrame(mpVideoRecCtx, uScreen, x, y,
3989 uPixelFormat,
3990 uBitsPerPixel, uBytesPerLine,
3991 uGuestWidth, uGuestHeight,
3992 pu8BufferAddress, u64Timestamp);
3993 RT_NOREF(rc2);
3994 Assert(rc2 == VINF_SUCCESS /* || rc == VERR_TRY_AGAIN || rc == VINF_TRY_AGAIN*/);
3995 }
3996# else
3997 RT_NOREF(uScreen, x, y, uPixelFormat, \
3998 uBitsPerPixel, uBytesPerLine, uGuestWidth, uGuestHeight, pu8BufferAddress, u64Timestamp);
3999# endif /* VBOX_WITH_VIDEOREC */
4000}
4001
4002void Display::i_handleVRecCompletion()
4003{
4004 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
4005 ASMAtomicWriteU32(&mfCrOglVideoRecState, CRVREC_STATE_IDLE);
4006}
4007
4008#endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
4009
4010HRESULT Display::notifyScaleFactorChange(ULONG aScreenId, ULONG aScaleFactorWMultiplied, ULONG aScaleFactorHMultiplied)
4011{
4012#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4013 HRESULT hr = E_UNEXPECTED;
4014
4015 if (aScreenId >= mcMonitors)
4016 return E_INVALIDARG;
4017
4018 /* 3D acceleration enabled in VM config. */
4019 if (mfIsCr3DEnabled)
4020 {
4021 /* VBoxSharedCrOpenGL HGCM host service is running. */
4022 if (mhCrOglSvc)
4023 {
4024 VMMDev *pVMMDev = mParent->i_getVMMDev();
4025 if (pVMMDev)
4026 {
4027 VBOXCRCMDCTL_HGCM *pCtl;
4028 pCtl = (VBOXCRCMDCTL_HGCM *)RTMemAlloc(sizeof(CRVBOXHGCMSETSCALEFACTOR) + sizeof(VBOXCRCMDCTL_HGCM));
4029 if (pCtl)
4030 {
4031 CRVBOXHGCMSETSCALEFACTOR *pData = (CRVBOXHGCMSETSCALEFACTOR *)(pCtl + 1);
4032 int rc;
4033
4034 pData->u32Screen = aScreenId;
4035 pData->u32ScaleFactorWMultiplied = aScaleFactorWMultiplied;
4036 pData->u32ScaleFactorHMultiplied = aScaleFactorHMultiplied;
4037
4038 pCtl->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
4039 pCtl->Hdr.u32Function = SHCRGL_HOST_FN_SET_SCALE_FACTOR;
4040 pCtl->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
4041 pCtl->aParms[0].u.pointer.addr = pData;
4042 pCtl->aParms[0].u.pointer.size = sizeof(*pData);
4043
4044 rc = i_crCtlSubmitSync(&pCtl->Hdr, sizeof(*pCtl));
4045 if (RT_FAILURE(rc))
4046 AssertMsgFailed(("crCtlSubmitSync failed (rc=%Rrc)\n", rc));
4047 else
4048 hr = S_OK;
4049
4050 RTMemFree(pCtl);
4051 }
4052 else
4053 {
4054 LogRel(("Running out of memory on attempt to set OpenGL content scale factor. Ignored.\n"));
4055 hr = E_OUTOFMEMORY;
4056 }
4057 }
4058 else
4059 LogRel(("Internal error occurred on attempt to set OpenGL content scale factor. Ignored.\n"));
4060 }
4061 else
4062 LogRel(("Attempt to specify OpenGL content scale factor while corresponding HGCM host service not yet runing. Ignored.\n"));
4063 }
4064 else
4065# if 0 /** @todo Thank you so very much from anyone using VMSVGA3d! */
4066 AssertMsgFailed(("Attempt to specify OpenGL content scale factor while 3D acceleration is disabled in VM config. Ignored.\n"));
4067# else
4068 {
4069 hr = S_OK;
4070 /* Need an interface like this here (and the #ifdefs needs adjusting):
4071 PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
4072 if (pUpPort && pUpPort->pfnSetScaleFactor)
4073 pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
4074 }
4075# endif
4076
4077 return hr;
4078
4079#else /* !VBOX_WITH_HGCM || !VBOX_WITH_CROGL */
4080 RT_NOREF(aScreenId, aScaleFactorWMultiplied, aScaleFactorHMultiplied);
4081 AssertMsgFailed(("Attempt to specify OpenGL content scale factor while corresponding functionality is disabled."));
4082 return E_UNEXPECTED;
4083#endif /* !VBOX_WITH_HGCM || !VBOX_WITH_CROGL */
4084}
4085
4086HRESULT Display::notifyHiDPIOutputPolicyChange(BOOL fUnscaledHiDPI)
4087{
4088#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4089 HRESULT hr = E_UNEXPECTED;
4090
4091 /* 3D acceleration enabled in VM config. */
4092 if (mfIsCr3DEnabled)
4093 {
4094 /* VBoxSharedCrOpenGL HGCM host service is running. */
4095 if (mhCrOglSvc)
4096 {
4097 VMMDev *pVMMDev = mParent->i_getVMMDev();
4098 if (pVMMDev)
4099 {
4100 VBOXCRCMDCTL_HGCM *pCtl;
4101 pCtl = (VBOXCRCMDCTL_HGCM *)RTMemAlloc(sizeof(CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT) + sizeof(VBOXCRCMDCTL_HGCM));
4102 if (pCtl)
4103 {
4104 CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT *pData = (CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT *)(pCtl + 1);
4105 int rc;
4106
4107 pData->fUnscaledHiDPI = RT_BOOL(fUnscaledHiDPI);
4108
4109 pCtl->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
4110 pCtl->Hdr.u32Function = SHCRGL_HOST_FN_SET_UNSCALED_HIDPI;
4111 pCtl->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
4112 pCtl->aParms[0].u.pointer.addr = pData;
4113 pCtl->aParms[0].u.pointer.size = sizeof(*pData);
4114
4115 rc = i_crCtlSubmitSync(&pCtl->Hdr, sizeof(*pCtl));
4116 if (RT_FAILURE(rc))
4117 AssertMsgFailed(("crCtlSubmitSync failed (rc=%Rrc)\n", rc));
4118 else
4119 hr = S_OK;
4120
4121 RTMemFree(pCtl);
4122 }
4123 else
4124 {
4125 LogRel(("Running out of memory on attempt to notify OpenGL about HiDPI output scaling policy change. Ignored.\n"));
4126 hr = E_OUTOFMEMORY;
4127 }
4128 }
4129 else
4130 LogRel(("Internal error occurred on attempt to notify OpenGL about HiDPI output scaling policy change. Ignored.\n"));
4131 }
4132 else
4133 LogRel(("Attempt to notify OpenGL about HiDPI output scaling policy change while corresponding HGCM host service not yet runing. Ignored.\n"));
4134 }
4135 else
4136 {
4137 hr = S_OK;
4138 /* Need an interface like this here (and the #ifdefs needs adjusting):
4139 PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
4140 if (pUpPort && pUpPort->pfnSetScaleFactor)
4141 pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
4142 }
4143
4144 return hr;
4145
4146#else /* !VBOX_WITH_HGCM || !VBOX_WITH_CROGL */
4147 RT_NOREF(fUnscaledHiDPI);
4148 AssertMsgFailed(("Attempt to notify OpenGL about HiDPI output scaling policy change while corresponding functionality is disabled."));
4149 return E_UNEXPECTED;
4150#endif /* !VBOX_WITH_HGCM || !VBOX_WITH_CROGL */
4151}
4152
4153#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4154DECLCALLBACK(void) Display::i_displayCrVRecScreenshotPerform(void *pvCtx, uint32_t uScreen,
4155 uint32_t x, uint32_t y,
4156 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
4157 uint32_t uGuestWidth, uint32_t uGuestHeight,
4158 uint8_t *pu8BufferAddress, uint64_t u64Timestamp)
4159{
4160 Display *pDisplay = (Display *)pvCtx;
4161 pDisplay->i_handleCrVRecScreenshotPerform(uScreen,
4162 x, y, BitmapFormat_BGR, uBitsPerPixel,
4163 uBytesPerLine, uGuestWidth, uGuestHeight,
4164 pu8BufferAddress, u64Timestamp);
4165}
4166
4167DECLCALLBACK(bool) Display::i_displayCrVRecScreenshotBegin(void *pvCtx, uint32_t uScreen, uint64_t u64Timestamp)
4168{
4169 Display *pDisplay = (Display *)pvCtx;
4170 return pDisplay->i_handleCrVRecScreenshotBegin(uScreen, u64Timestamp);
4171}
4172
4173DECLCALLBACK(void) Display::i_displayCrVRecScreenshotEnd(void *pvCtx, uint32_t uScreen, uint64_t u64Timestamp)
4174{
4175 Display *pDisplay = (Display *)pvCtx;
4176 pDisplay->i_handleCrVRecScreenshotEnd(uScreen, u64Timestamp);
4177}
4178
4179DECLCALLBACK(void) Display::i_displayVRecCompletion(struct VBOXCRCMDCTL *pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
4180{
4181 RT_NOREF(pCmd, cbCmd, rc);
4182 Display *pDisplay = (Display *)pvCompletion;
4183 pDisplay->i_handleVRecCompletion();
4184}
4185
4186#endif
4187
4188
4189#ifdef VBOX_WITH_HGSMI
4190DECLCALLBACK(int) Display::i_displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, PVBVAHOSTFLAGS pHostFlags,
4191 bool fRenderThreadMode)
4192{
4193 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
4194
4195 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4196 Display *pThis = pDrv->pDisplay;
4197
4198 if (pThis->maFramebuffers[uScreenId].fVBVAEnabled && pThis->maFramebuffers[uScreenId].fRenderThreadMode != fRenderThreadMode)
4199 {
4200 LogRel(("Enabling different vbva mode\n"));
4201#ifdef DEBUG_misha
4202 AssertMsgFailed(("enabling different vbva mode\n"));
4203#endif
4204 return VERR_INVALID_STATE;
4205 }
4206
4207 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
4208 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
4209 pThis->maFramebuffers[uScreenId].fRenderThreadMode = fRenderThreadMode;
4210 pThis->maFramebuffers[uScreenId].fVBVAForceResize = true;
4211
4212 vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
4213
4214 return VINF_SUCCESS;
4215}
4216
4217DECLCALLBACK(void) Display::i_displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
4218{
4219 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
4220
4221 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4222 Display *pThis = pDrv->pDisplay;
4223
4224 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4225
4226 bool fRenderThreadMode = pFBInfo->fRenderThreadMode;
4227
4228 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
4229 {
4230 /* Make sure that the primary screen is visible now.
4231 * The guest can't use VBVA anymore, so only only the VGA device output works.
4232 */
4233 pFBInfo->flags = 0;
4234 if (pFBInfo->fDisabled)
4235 {
4236 pFBInfo->fDisabled = false;
4237 fireGuestMonitorChangedEvent(pThis->mParent->i_getEventSource(),
4238 GuestMonitorChangedEventType_Enabled,
4239 uScreenId,
4240 pFBInfo->xOrigin, pFBInfo->yOrigin,
4241 pFBInfo->w, pFBInfo->h);
4242 }
4243 }
4244
4245 pFBInfo->fVBVAEnabled = false;
4246 pFBInfo->fVBVAForceResize = false;
4247 pFBInfo->fRenderThreadMode = false;
4248
4249 vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, pFBInfo);
4250
4251 pFBInfo->pVBVAHostFlags = NULL;
4252
4253 if (!fRenderThreadMode && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
4254 {
4255 /* Force full screen update, because VGA device must take control, do resize, etc. */
4256 pThis->mpDrv->pUpPort->pfnUpdateDisplayAll(pThis->mpDrv->pUpPort, /* fFailOnResize = */ false);
4257 }
4258}
4259
4260DECLCALLBACK(void) Display::i_displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
4261{
4262 RT_NOREF(uScreenId);
4263 LogFlowFunc(("uScreenId %d\n", uScreenId));
4264
4265 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4266 Display *pThis = pDrv->pDisplay;
4267
4268 if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
4269 {
4270 vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers,
4271 pThis->mcMonitors);
4272 ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
4273 }
4274}
4275
4276DECLCALLBACK(void) Display::i_displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
4277 PCVBVACMDHDR pCmd, size_t cbCmd)
4278{
4279 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d, @%d,%d %dx%d\n", uScreenId, pCmd, cbCmd, pCmd->x, pCmd->y, pCmd->w, pCmd->h));
4280
4281 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4282 Display *pThis = pDrv->pDisplay;
4283 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4284
4285 if (pFBInfo->fDefaultFormat)
4286 {
4287 /* Make sure that framebuffer contains the same image as the guest VRAM. */
4288 if ( uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
4289 && !pFBInfo->fDisabled)
4290 {
4291 pDrv->pUpPort->pfnUpdateDisplayRect(pDrv->pUpPort, pCmd->x, pCmd->y, pCmd->w, pCmd->h);
4292 }
4293 else if ( !pFBInfo->pSourceBitmap.isNull()
4294 && !pFBInfo->fDisabled)
4295 {
4296 /* Render VRAM content to the framebuffer. */
4297 BYTE *pAddress = NULL;
4298 ULONG ulWidth = 0;
4299 ULONG ulHeight = 0;
4300 ULONG ulBitsPerPixel = 0;
4301 ULONG ulBytesPerLine = 0;
4302 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
4303
4304 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
4305 &ulWidth,
4306 &ulHeight,
4307 &ulBitsPerPixel,
4308 &ulBytesPerLine,
4309 &bitmapFormat);
4310 if (SUCCEEDED(hrc))
4311 {
4312 uint32_t width = pCmd->w;
4313 uint32_t height = pCmd->h;
4314
4315 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
4316 int32_t xSrc = pCmd->x - pFBInfo->xOrigin;
4317 int32_t ySrc = pCmd->y - pFBInfo->yOrigin;
4318 uint32_t u32SrcWidth = pFBInfo->w;
4319 uint32_t u32SrcHeight = pFBInfo->h;
4320 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
4321 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
4322
4323 uint8_t *pu8Dst = pAddress;
4324 int32_t xDst = xSrc;
4325 int32_t yDst = ySrc;
4326 uint32_t u32DstWidth = u32SrcWidth;
4327 uint32_t u32DstHeight = u32SrcHeight;
4328 uint32_t u32DstLineSize = u32DstWidth * 4;
4329 uint32_t u32DstBitsPerPixel = 32;
4330
4331 pDrv->pUpPort->pfnCopyRect(pDrv->pUpPort,
4332 width, height,
4333 pu8Src,
4334 xSrc, ySrc,
4335 u32SrcWidth, u32SrcHeight,
4336 u32SrcLineSize, u32SrcBitsPerPixel,
4337 pu8Dst,
4338 xDst, yDst,
4339 u32DstWidth, u32DstHeight,
4340 u32DstLineSize, u32DstBitsPerPixel);
4341 }
4342 }
4343 }
4344
4345 VBVACMDHDR hdrSaved = *pCmd;
4346
4347 VBVACMDHDR *pHdrUnconst = (VBVACMDHDR *)pCmd;
4348
4349 pHdrUnconst->x -= (int16_t)pFBInfo->xOrigin;
4350 pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
4351
4352 /** @todo new SendUpdate entry which can get a separate cmd header or coords. */
4353 pThis->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, pHdrUnconst, (uint32_t)cbCmd);
4354
4355 *pHdrUnconst = hdrSaved;
4356}
4357
4358DECLCALLBACK(void) Display::i_displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y,
4359 uint32_t cx, uint32_t cy)
4360{
4361 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
4362
4363 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4364 Display *pThis = pDrv->pDisplay;
4365 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
4366
4367 /** @todo handleFramebufferUpdate (uScreenId,
4368 * x - pThis->maFramebuffers[uScreenId].xOrigin,
4369 * y - pThis->maFramebuffers[uScreenId].yOrigin,
4370 * cx, cy);
4371 */
4372 pThis->i_handleDisplayUpdate(uScreenId, x - pFBInfo->xOrigin, y - pFBInfo->yOrigin, cx, cy);
4373}
4374
4375#ifdef DEBUG_sunlover
4376static void logVBVAResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, const DISPLAYFBINFO *pFBInfo)
4377{
4378 LogRel(("displayVBVAResize: [%d] %s\n"
4379 " pView->u32ViewIndex %d\n"
4380 " pView->u32ViewOffset 0x%08X\n"
4381 " pView->u32ViewSize 0x%08X\n"
4382 " pView->u32MaxScreenSize 0x%08X\n"
4383 " pScreen->i32OriginX %d\n"
4384 " pScreen->i32OriginY %d\n"
4385 " pScreen->u32StartOffset 0x%08X\n"
4386 " pScreen->u32LineSize 0x%08X\n"
4387 " pScreen->u32Width %d\n"
4388 " pScreen->u32Height %d\n"
4389 " pScreen->u16BitsPerPixel %d\n"
4390 " pScreen->u16Flags 0x%04X\n"
4391 " pFBInfo->u32Offset 0x%08X\n"
4392 " pFBInfo->u32MaxFramebufferSize 0x%08X\n"
4393 " pFBInfo->u32InformationSize 0x%08X\n"
4394 " pFBInfo->fDisabled %d\n"
4395 " xOrigin, yOrigin, w, h: %d,%d %dx%d\n"
4396 " pFBInfo->u16BitsPerPixel %d\n"
4397 " pFBInfo->pu8FramebufferVRAM %p\n"
4398 " pFBInfo->u32LineSize 0x%08X\n"
4399 " pFBInfo->flags 0x%04X\n"
4400 " pFBInfo->pHostEvents %p\n"
4401 " pFBInfo->fDefaultFormat %d\n"
4402 " pFBInfo->fVBVAEnabled %d\n"
4403 " pFBInfo->fVBVAForceResize %d\n"
4404 " pFBInfo->pVBVAHostFlags %p\n"
4405 "",
4406 pScreen->u32ViewIndex,
4407 (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)? "DISABLED": "ENABLED",
4408 pView->u32ViewIndex,
4409 pView->u32ViewOffset,
4410 pView->u32ViewSize,
4411 pView->u32MaxScreenSize,
4412 pScreen->i32OriginX,
4413 pScreen->i32OriginY,
4414 pScreen->u32StartOffset,
4415 pScreen->u32LineSize,
4416 pScreen->u32Width,
4417 pScreen->u32Height,
4418 pScreen->u16BitsPerPixel,
4419 pScreen->u16Flags,
4420 pFBInfo->u32Offset,
4421 pFBInfo->u32MaxFramebufferSize,
4422 pFBInfo->u32InformationSize,
4423 pFBInfo->fDisabled,
4424 pFBInfo->xOrigin,
4425 pFBInfo->yOrigin,
4426 pFBInfo->w,
4427 pFBInfo->h,
4428 pFBInfo->u16BitsPerPixel,
4429 pFBInfo->pu8FramebufferVRAM,
4430 pFBInfo->u32LineSize,
4431 pFBInfo->flags,
4432 pFBInfo->pHostEvents,
4433 pFBInfo->fDefaultFormat,
4434 pFBInfo->fVBVAEnabled,
4435 pFBInfo->fVBVAForceResize,
4436 pFBInfo->pVBVAHostFlags
4437 ));
4438}
4439#endif /* DEBUG_sunlover */
4440
4441DECLCALLBACK(int) Display::i_displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, PCVBVAINFOVIEW pView,
4442 PCVBVAINFOSCREEN pScreen, void *pvVRAM, bool fResetInputMapping)
4443{
4444 LogRelFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
4445
4446 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4447 Display *pThis = pDrv->pDisplay;
4448
4449 return pThis->processVBVAResize(pView, pScreen, pvVRAM, fResetInputMapping);
4450}
4451
4452int Display::processVBVAResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, void *pvVRAM, bool fResetInputMapping)
4453{
4454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4455
4456 DISPLAYFBINFO *pFBInfo = &maFramebuffers[pScreen->u32ViewIndex];
4457
4458 if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
4459 {
4460 /* Ask the framebuffer to resize using a default format. The framebuffer will be black.
4461 * So if the frontend does not support GuestMonitorChangedEventType_Disabled event,
4462 * the VM window will be black. */
4463 uint32_t u32Width = pFBInfo->w ? pFBInfo->w : 640;
4464 uint32_t u32Height = pFBInfo->h ? pFBInfo->h : 480;
4465 int32_t xOrigin = pFBInfo->xOrigin;
4466 int32_t yOrigin = pFBInfo->yOrigin;
4467
4468 alock.release();
4469
4470 i_notifyCroglResize(pView, pScreen, pvVRAM);
4471
4472 i_handleDisplayResize(pScreen->u32ViewIndex, 0, (uint8_t *)NULL, 0,
4473 u32Width, u32Height, pScreen->u16Flags, xOrigin, yOrigin, false);
4474
4475 return VINF_SUCCESS;
4476 }
4477
4478 VBVAINFOSCREEN screenInfo;
4479 RT_ZERO(screenInfo);
4480
4481 if (pScreen->u16Flags & VBVA_SCREEN_F_BLANK2)
4482 {
4483 /* Init a local VBVAINFOSCREEN structure, which will be used instead of
4484 * the original pScreen. Set VBVA_SCREEN_F_BLANK, which will force
4485 * the code below to choose the "blanking" branches.
4486 */
4487 screenInfo.u32ViewIndex = pScreen->u32ViewIndex;
4488 screenInfo.i32OriginX = pFBInfo->xOrigin;
4489 screenInfo.i32OriginY = pFBInfo->yOrigin;
4490 screenInfo.u32StartOffset = 0; /* Irrelevant */
4491 screenInfo.u32LineSize = pFBInfo->u32LineSize;
4492 screenInfo.u32Width = pFBInfo->w;
4493 screenInfo.u32Height = pFBInfo->h;
4494 screenInfo.u16BitsPerPixel = pFBInfo->u16BitsPerPixel;
4495 screenInfo.u16Flags = pScreen->u16Flags | VBVA_SCREEN_F_BLANK;
4496
4497 pScreen = &screenInfo;
4498 }
4499
4500 if (fResetInputMapping)
4501 {
4502 /// @todo Rename to m* and verify whether some kind of lock is required.
4503 xInputMappingOrigin = 0;
4504 yInputMappingOrigin = 0;
4505 cxInputMapping = 0;
4506 cyInputMapping = 0;
4507 }
4508
4509 alock.release();
4510
4511 i_notifyCroglResize(pView, pScreen, pvVRAM);
4512
4513 return i_handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
4514 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
4515 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height, pScreen->u16Flags,
4516 pScreen->i32OriginX, pScreen->i32OriginY, false);
4517}
4518
4519DECLCALLBACK(int) Display::i_displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
4520 uint32_t xHot, uint32_t yHot,
4521 uint32_t cx, uint32_t cy,
4522 const void *pvShape)
4523{
4524 LogFlowFunc(("\n"));
4525
4526 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4527
4528 uint32_t cbShape = 0;
4529 if (pvShape)
4530 {
4531 cbShape = (cx + 7) / 8 * cy; /* size of the AND mask */
4532 cbShape = ((cbShape + 3) & ~3) + cx * 4 * cy; /* + gap + size of the XOR mask */
4533 }
4534
4535 /* Tell the console about it */
4536 pDrv->pDisplay->mParent->i_onMousePointerShapeChange(fVisible, fAlpha,
4537 xHot, yHot, cx, cy, (uint8_t *)pvShape, cbShape);
4538
4539 return VINF_SUCCESS;
4540}
4541
4542DECLCALLBACK(void) Display::i_displayVBVAGuestCapabilityUpdate(PPDMIDISPLAYCONNECTOR pInterface, uint32_t fCapabilities)
4543{
4544 LogFlowFunc(("\n"));
4545
4546 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4547 Display *pThis = pDrv->pDisplay;
4548
4549 pThis->i_handleUpdateGuestVBVACapabilities(fCapabilities);
4550}
4551
4552DECLCALLBACK(void) Display::i_displayVBVAInputMappingUpdate(PPDMIDISPLAYCONNECTOR pInterface, int32_t xOrigin, int32_t yOrigin,
4553 uint32_t cx, uint32_t cy)
4554{
4555 LogFlowFunc(("\n"));
4556
4557 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4558 Display *pThis = pDrv->pDisplay;
4559
4560 pThis->i_handleUpdateVBVAInputMapping(xOrigin, yOrigin, cx, cy);
4561}
4562
4563DECLCALLBACK(void) Display::i_displayVBVAReportCursorPosition(PPDMIDISPLAYCONNECTOR pInterface, bool fData, uint32_t x, uint32_t y)
4564{
4565 LogFlowFunc(("\n"));
4566
4567 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4568 Display *pThis = pDrv->pDisplay;
4569
4570 fireCursorPositionChangedEvent(pThis->mParent->i_getEventSource(), fData, x, y);
4571}
4572
4573#endif /* VBOX_WITH_HGSMI */
4574
4575/**
4576 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4577 */
4578DECLCALLBACK(void *) Display::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4579{
4580 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
4581 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4582 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
4583 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYCONNECTOR, &pDrv->IConnector);
4584 return NULL;
4585}
4586
4587
4588/**
4589 * Destruct a display driver instance.
4590 *
4591 * @returns VBox status code.
4592 * @param pDrvIns The driver instance data.
4593 */
4594DECLCALLBACK(void) Display::i_drvDestruct(PPDMDRVINS pDrvIns)
4595{
4596 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4597 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4598 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
4599
4600 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
4601
4602 pThis->IConnector.pbData = NULL;
4603 pThis->IConnector.cbScanline = 0;
4604 pThis->IConnector.cBits = 32;
4605 pThis->IConnector.cx = 0;
4606 pThis->IConnector.cy = 0;
4607
4608 if (pThis->pDisplay)
4609 {
4610 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
4611#ifdef VBOX_WITH_VIDEOREC
4612 pThis->pDisplay->i_videoRecStop();
4613#endif
4614#ifdef VBOX_WITH_CRHGSMI
4615 pThis->pDisplay->i_destructCrHgsmiData();
4616#endif
4617 pThis->pDisplay->mpDrv = NULL;
4618 }
4619}
4620
4621
4622/**
4623 * Construct a display driver instance.
4624 *
4625 * @copydoc FNPDMDRVCONSTRUCT
4626 */
4627DECLCALLBACK(int) Display::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
4628{
4629 RT_NOREF(fFlags);
4630 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
4631 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4632 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
4633
4634 /*
4635 * Validate configuration.
4636 */
4637 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
4638 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
4639 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
4640 ("Configuration error: Not possible to attach anything to this driver!\n"),
4641 VERR_PDM_DRVINS_NO_ATTACH);
4642
4643 /*
4644 * Init Interfaces.
4645 */
4646 pDrvIns->IBase.pfnQueryInterface = Display::i_drvQueryInterface;
4647
4648 pThis->IConnector.pfnResize = Display::i_displayResizeCallback;
4649 pThis->IConnector.pfnUpdateRect = Display::i_displayUpdateCallback;
4650 pThis->IConnector.pfnRefresh = Display::i_displayRefreshCallback;
4651 pThis->IConnector.pfnReset = Display::i_displayResetCallback;
4652 pThis->IConnector.pfnLFBModeChange = Display::i_displayLFBModeChangeCallback;
4653 pThis->IConnector.pfnProcessAdapterData = Display::i_displayProcessAdapterDataCallback;
4654 pThis->IConnector.pfnProcessDisplayData = Display::i_displayProcessDisplayDataCallback;
4655#ifdef VBOX_WITH_VIDEOHWACCEL
4656 pThis->IConnector.pfnVHWACommandProcess = Display::i_displayVHWACommandProcess;
4657#endif
4658#ifdef VBOX_WITH_CRHGSMI
4659 pThis->IConnector.pfnCrHgsmiCommandProcess = Display::i_displayCrHgsmiCommandProcess;
4660 pThis->IConnector.pfnCrHgsmiControlProcess = Display::i_displayCrHgsmiControlProcess;
4661#endif
4662#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4663 pThis->IConnector.pfnCrHgcmCtlSubmit = Display::i_displayCrHgcmCtlSubmit;
4664#endif
4665#ifdef VBOX_WITH_HGSMI
4666 pThis->IConnector.pfnVBVAEnable = Display::i_displayVBVAEnable;
4667 pThis->IConnector.pfnVBVADisable = Display::i_displayVBVADisable;
4668 pThis->IConnector.pfnVBVAUpdateBegin = Display::i_displayVBVAUpdateBegin;
4669 pThis->IConnector.pfnVBVAUpdateProcess = Display::i_displayVBVAUpdateProcess;
4670 pThis->IConnector.pfnVBVAUpdateEnd = Display::i_displayVBVAUpdateEnd;
4671 pThis->IConnector.pfnVBVAResize = Display::i_displayVBVAResize;
4672 pThis->IConnector.pfnVBVAMousePointerShape = Display::i_displayVBVAMousePointerShape;
4673 pThis->IConnector.pfnVBVAGuestCapabilityUpdate = Display::i_displayVBVAGuestCapabilityUpdate;
4674 pThis->IConnector.pfnVBVAInputMappingUpdate = Display::i_displayVBVAInputMappingUpdate;
4675 pThis->IConnector.pfnVBVAReportCursorPosition = Display::i_displayVBVAReportCursorPosition;
4676#endif
4677
4678 /*
4679 * Get the IDisplayPort interface of the above driver/device.
4680 */
4681 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
4682 if (!pThis->pUpPort)
4683 {
4684 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
4685 return VERR_PDM_MISSING_INTERFACE_ABOVE;
4686 }
4687#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
4688 pThis->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
4689 if (!pThis->pVBVACallbacks)
4690 {
4691 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
4692 return VERR_PDM_MISSING_INTERFACE_ABOVE;
4693 }
4694#endif
4695 /*
4696 * Get the Display object pointer and update the mpDrv member.
4697 */
4698 void *pv;
4699 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
4700 if (RT_FAILURE(rc))
4701 {
4702 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
4703 return rc;
4704 }
4705 Display *pDisplay = (Display *)pv; /** @todo Check this cast! */
4706 pThis->pDisplay = pDisplay;
4707 pThis->pDisplay->mpDrv = pThis;
4708
4709 /* Disable VRAM to a buffer copy initially. */
4710 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
4711 pThis->IConnector.cBits = 32; /* DevVGA does nothing otherwise. */
4712
4713 /*
4714 * Start periodic screen refreshes
4715 */
4716 pThis->pUpPort->pfnSetRefreshRate(pThis->pUpPort, 20);
4717
4718#ifdef VBOX_WITH_CRHGSMI
4719 pDisplay->i_setupCrHgsmiData();
4720#endif
4721
4722#ifdef VBOX_WITH_VIDEOREC
4723 if (pDisplay->i_videoRecGetEnabled())
4724 {
4725 int rc2 = pDisplay->i_videoRecStart();
4726 if (RT_SUCCESS(rc2))
4727 fireVideoCaptureChangedEvent(pDisplay->mParent->i_getEventSource());
4728
4729 /* If video recording fails for whatever reason here, this is
4730 * non-critical and should not be returned at this point -- otherwise
4731 * the display driver construction fails completely. */
4732 }
4733#endif
4734
4735 return rc;
4736}
4737
4738
4739/**
4740 * Display driver registration record.
4741 */
4742const PDMDRVREG Display::DrvReg =
4743{
4744 /* u32Version */
4745 PDM_DRVREG_VERSION,
4746 /* szName */
4747 "MainDisplay",
4748 /* szRCMod */
4749 "",
4750 /* szR0Mod */
4751 "",
4752 /* pszDescription */
4753 "Main display driver (Main as in the API).",
4754 /* fFlags */
4755 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
4756 /* fClass. */
4757 PDM_DRVREG_CLASS_DISPLAY,
4758 /* cMaxInstances */
4759 ~0U,
4760 /* cbInstance */
4761 sizeof(DRVMAINDISPLAY),
4762 /* pfnConstruct */
4763 Display::i_drvConstruct,
4764 /* pfnDestruct */
4765 Display::i_drvDestruct,
4766 /* pfnRelocate */
4767 NULL,
4768 /* pfnIOCtl */
4769 NULL,
4770 /* pfnPowerOn */
4771 NULL,
4772 /* pfnReset */
4773 NULL,
4774 /* pfnSuspend */
4775 NULL,
4776 /* pfnResume */
4777 NULL,
4778 /* pfnAttach */
4779 NULL,
4780 /* pfnDetach */
4781 NULL,
4782 /* pfnPowerOff */
4783 NULL,
4784 /* pfnSoftReset */
4785 NULL,
4786 /* u32EndVersion */
4787 PDM_DRVREG_VERSION
4788};
4789
4790/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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