VirtualBox

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

Last change on this file since 55043 was 54841, checked in by vboxsync, 10 years ago

Main/Display: fixes to new screen blanking feature.

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