VirtualBox

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

Last change on this file since 55747 was 55133, checked in by vboxsync, 10 years ago

Main,Frontends: framebuffer identifier.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 149.4 KB
Line 
1/* $Id: DisplayImpl.cpp 55133 2015-04-08 13:12:53Z 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, com::Guid &aId)
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 pFBInfo->framebufferId.create();
1621 aId = pFBInfo->framebufferId;
1622
1623 SafeArray<FramebufferCapabilities_T> caps;
1624 pFBInfo->pFramebuffer->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(caps));
1625 pFBInfo->u32Caps = 0;
1626 size_t i;
1627 for (i = 0; i < caps.size(); ++i)
1628 pFBInfo->u32Caps |= caps[i];
1629
1630 alock.release();
1631
1632 /* The driver might not have been constructed yet */
1633 if (mpDrv)
1634 {
1635 /* Setup the new framebuffer. */
1636 i_handleDisplayResize(aScreenId, pFBInfo->u16BitsPerPixel,
1637 pFBInfo->pu8FramebufferVRAM,
1638 pFBInfo->u32LineSize,
1639 pFBInfo->w,
1640 pFBInfo->h,
1641 pFBInfo->flags);
1642 }
1643
1644 Console::SafeVMPtrQuiet ptrVM(mParent);
1645 if (ptrVM.isOk())
1646 {
1647#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1648 if (mfIsCr3DEnabled)
1649 {
1650 VBOXCRCMDCTL_HGCM data;
1651 RT_ZERO(data);
1652 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1653 data.Hdr.u32Function = SHCRGL_HOST_FN_SCREEN_CHANGED;
1654
1655 data.aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
1656 data.aParms[0].u.uint32 = aScreenId;
1657
1658 int vrc = i_crCtlSubmitSync(&data.Hdr, sizeof(data));
1659 AssertRC(vrc);
1660 }
1661#endif /* defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL) */
1662
1663 VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
1664 3, this, aScreenId, false);
1665 }
1666
1667 LogRelFlowFunc(("Attached to %d %RTuuid\n", aScreenId, aId.raw()));
1668 return S_OK;
1669}
1670
1671HRESULT Display::detachFramebuffer(ULONG aScreenId, const com::Guid &aId)
1672{
1673 LogRelFlowFunc(("aScreenId = %d %RTuuid\n", aScreenId, aId.raw()));
1674
1675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1676
1677 if (aScreenId >= mcMonitors)
1678 return setError(E_INVALIDARG, tr("DetachFramebuffer: Invalid screen %d (total %d)"),
1679 aScreenId, mcMonitors);
1680
1681 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1682
1683 if (pFBInfo->framebufferId != aId)
1684 {
1685 LogRelFlowFunc(("Invalid framebuffer aScreenId = %d, attached %p\n", aScreenId, pFBInfo->framebufferId.raw()));
1686 return setError(E_FAIL, tr("DetachFramebuffer: Invalid framebuffer object"));
1687 }
1688
1689 pFBInfo->pFramebuffer.setNull();
1690 pFBInfo->framebufferId.clear();
1691
1692 alock.release();
1693
1694#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1695 Console::SafeVMPtrQuiet ptrVM(mParent);
1696 if (ptrVM.isOk())
1697 {
1698 if (mfIsCr3DEnabled)
1699 {
1700 VBOXCRCMDCTL_HGCM data;
1701 RT_ZERO(data);
1702 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1703 data.Hdr.u32Function = SHCRGL_HOST_FN_SCREEN_CHANGED;
1704
1705 data.aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
1706 data.aParms[0].u.uint32 = aScreenId;
1707
1708 int vrc = i_crCtlSubmitSync(&data.Hdr, sizeof(data));
1709 AssertRC(vrc);
1710 }
1711 }
1712#endif /* defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL) */
1713
1714 return S_OK;
1715}
1716
1717HRESULT Display::queryFramebuffer(ULONG aScreenId, ComPtr<IFramebuffer> &aFramebuffer)
1718{
1719 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
1720
1721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 if (aScreenId >= mcMonitors)
1724 return setError(E_INVALIDARG, tr("QueryFramebuffer: Invalid screen %d (total %d)"),
1725 aScreenId, mcMonitors);
1726
1727 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1728
1729 pFBInfo->pFramebuffer.queryInterfaceTo(aFramebuffer.asOutParam());
1730
1731 return S_OK;
1732}
1733
1734HRESULT Display::setVideoModeHint(ULONG aDisplay, BOOL aEnabled,
1735 BOOL aChangeOrigin, LONG aOriginX, LONG aOriginY,
1736 ULONG aWidth, ULONG aHeight, ULONG aBitsPerPixel)
1737{
1738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1739
1740 CHECK_CONSOLE_DRV(mpDrv);
1741
1742 /*
1743 * Do some rough checks for valid input.
1744 */
1745 ULONG width = aWidth;
1746 if (!width)
1747 width = mpDrv->IConnector.cx;
1748 ULONG height = aHeight;
1749 if (!height)
1750 height = mpDrv->IConnector.cy;
1751 ULONG bpp = aBitsPerPixel;
1752 if (!bpp)
1753 {
1754 alock.release();
1755
1756 uint32_t cBits = 0;
1757 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
1758 AssertRC(rc);
1759 bpp = cBits;
1760
1761 alock.acquire();
1762 }
1763 ULONG cMonitors;
1764 mParent->i_machine()->COMGETTER(MonitorCount)(&cMonitors);
1765 if (cMonitors == 0 && aDisplay > 0)
1766 return E_INVALIDARG;
1767 if (aDisplay >= cMonitors)
1768 return E_INVALIDARG;
1769
1770 /*
1771 * sunlover 20070614: It is up to the guest to decide whether the hint is
1772 * valid. Therefore don't do any VRAM sanity checks here!
1773 */
1774
1775 /* Have to release the lock because the pfnRequestDisplayChange
1776 * will call EMT. */
1777 alock.release();
1778
1779 /* We always send the hint to the graphics card in case the guest enables
1780 * support later. For now we notify exactly when support is enabled. */
1781 mpDrv->pUpPort->pfnSendModeHint(mpDrv->pUpPort, aWidth, aHeight,
1782 aBitsPerPixel, aDisplay,
1783 aChangeOrigin ? aOriginX : ~0,
1784 aChangeOrigin ? aOriginY : ~0,
1785 RT_BOOL(aEnabled),
1786 mfGuestVBVACapabilities
1787 & VBVACAPS_VIDEO_MODE_HINTS);
1788 if ( mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS
1789 && !(mfGuestVBVACapabilities & VBVACAPS_IRQ))
1790 {
1791 HRESULT hrc = mParent->i_sendACPIMonitorHotPlugEvent();
1792 if (FAILED(hrc))
1793 return hrc;
1794 }
1795
1796 /* We currently never suppress the VMMDev hint if the guest has requested
1797 * it. Specifically the video graphics driver may not be responsible for
1798 * screen positioning in the guest virtual desktop, and the component
1799 * responsible may want to get the hint from VMMDev. */
1800 VMMDev *pVMMDev = mParent->i_getVMMDev();
1801 if (pVMMDev)
1802 {
1803 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
1804 if (pVMMDevPort)
1805 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aBitsPerPixel,
1806 aDisplay, aOriginX, aOriginY,
1807 RT_BOOL(aEnabled), RT_BOOL(aChangeOrigin));
1808 }
1809 return S_OK;
1810}
1811
1812HRESULT Display::setSeamlessMode(BOOL enabled)
1813{
1814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1815
1816 /* Have to release the lock because the pfnRequestSeamlessChange will call EMT. */
1817 alock.release();
1818
1819 VMMDev *pVMMDev = mParent->i_getVMMDev();
1820 if (pVMMDev)
1821 {
1822 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
1823 if (pVMMDevPort)
1824 pVMMDevPort->pfnRequestSeamlessChange(pVMMDevPort, !!enabled);
1825 }
1826
1827#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1828 if (!enabled)
1829 {
1830 VMMDev *vmmDev = mParent->i_getVMMDev();
1831 if (mfIsCr3DEnabled && vmmDev)
1832 {
1833 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM*)RTMemAlloc(sizeof(VBOXCRCMDCTL_HGCM));
1834 if (!pData)
1835 {
1836 AssertMsgFailed(("RTMemAlloc failed\n"));
1837 return VERR_NO_MEMORY;
1838 }
1839
1840 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1841 pData->Hdr.u32Function = SHCRGL_HOST_FN_SET_VISIBLE_REGION;
1842
1843 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
1844 pData->aParms[0].u.pointer.addr = NULL;
1845 pData->aParms[0].u.pointer.size = 0; /* <- means null rects, NULL pRects address and 0 rects means "disable" */
1846
1847 int rc = i_crCtlSubmit(&pData->Hdr, sizeof(*pData), i_displayCrCmdFree, pData);
1848 if (!RT_SUCCESS(rc))
1849 {
1850 AssertMsgFailed(("crCtlSubmit failed (rc=%Rrc)\n", rc));
1851 RTMemFree(pData);
1852 }
1853 }
1854 }
1855#endif
1856 return S_OK;
1857}
1858
1859#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1860BOOL Display::i_displayCheckTakeScreenshotCrOgl(Display *pDisplay, ULONG aScreenId, uint8_t *pu8Data,
1861 uint32_t u32Width, uint32_t u32Height)
1862{
1863 if ( pDisplay->mfIsCr3DEnabled
1864 && pDisplay->mCrOglCallbacks.pfnHasData
1865 && pDisplay->mCrOglCallbacks.pfnHasData())
1866 {
1867 VMMDev *pVMMDev = pDisplay->mParent->i_getVMMDev();
1868 if (pVMMDev)
1869 {
1870 CRVBOXHGCMTAKESCREENSHOT *pScreenshot = (CRVBOXHGCMTAKESCREENSHOT*)RTMemAlloc(sizeof(*pScreenshot));
1871 if (pScreenshot)
1872 {
1873 /* screen id or CRSCREEN_ALL to specify all enabled */
1874 pScreenshot->u32Screen = aScreenId;
1875 pScreenshot->u32Width = u32Width;
1876 pScreenshot->u32Height = u32Height;
1877 pScreenshot->u32Pitch = u32Width * 4;
1878 pScreenshot->pvBuffer = pu8Data;
1879 pScreenshot->pvContext = NULL;
1880 pScreenshot->pfnScreenshotBegin = NULL;
1881 pScreenshot->pfnScreenshotPerform = NULL;
1882 pScreenshot->pfnScreenshotEnd = NULL;
1883
1884 VBOXCRCMDCTL_HGCM data;
1885 data.Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
1886 data.Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
1887
1888 data.aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
1889 data.aParms[0].u.pointer.addr = pScreenshot;
1890 data.aParms[0].u.pointer.size = sizeof(*pScreenshot);
1891
1892 int rc = pDisplay->i_crCtlSubmitSync(&data.Hdr, sizeof(data));
1893
1894 RTMemFree(pScreenshot);
1895
1896 if (RT_SUCCESS(rc))
1897 return TRUE;
1898 AssertMsgFailed(("failed to get screenshot data from crOgl (rc=%Rrc)\n", rc));
1899 /* fall back to the non-3d mechanism */
1900 }
1901 }
1902 }
1903 return FALSE;
1904}
1905#endif
1906
1907int Display::i_displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_t **ppu8Data, size_t *pcbData,
1908 uint32_t *pu32Width, uint32_t *pu32Height)
1909{
1910 int rc;
1911
1912 if ( aScreenId == VBOX_VIDEO_PRIMARY_SCREEN
1913 && pDisplay->maFramebuffers[aScreenId].fVBVAEnabled == false) /* A non-VBVA mode. */
1914 {
1915 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppu8Data, pcbData, pu32Width, pu32Height);
1916 }
1917 else if (aScreenId < pDisplay->mcMonitors)
1918 {
1919 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
1920
1921 uint32_t width = pFBInfo->w;
1922 uint32_t height = pFBInfo->h;
1923
1924 /* Allocate 32 bit per pixel bitmap. */
1925 size_t cbRequired = width * 4 * height;
1926
1927 if (cbRequired)
1928 {
1929 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbRequired);
1930
1931 if (pu8Data == NULL)
1932 {
1933 rc = VERR_NO_MEMORY;
1934 }
1935 else
1936 {
1937 /* Copy guest VRAM to the allocated 32bpp buffer. */
1938 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
1939 int32_t xSrc = 0;
1940 int32_t ySrc = 0;
1941 uint32_t u32SrcWidth = width;
1942 uint32_t u32SrcHeight = height;
1943 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
1944 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
1945
1946 uint8_t *pu8Dst = pu8Data;
1947 int32_t xDst = 0;
1948 int32_t yDst = 0;
1949 uint32_t u32DstWidth = u32SrcWidth;
1950 uint32_t u32DstHeight = u32SrcHeight;
1951 uint32_t u32DstLineSize = u32DstWidth * 4;
1952 uint32_t u32DstBitsPerPixel = 32;
1953
1954 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
1955 width, height,
1956 pu8Src,
1957 xSrc, ySrc,
1958 u32SrcWidth, u32SrcHeight,
1959 u32SrcLineSize, u32SrcBitsPerPixel,
1960 pu8Dst,
1961 xDst, yDst,
1962 u32DstWidth, u32DstHeight,
1963 u32DstLineSize, u32DstBitsPerPixel);
1964 if (RT_SUCCESS(rc))
1965 {
1966 *ppu8Data = pu8Data;
1967 *pcbData = cbRequired;
1968 *pu32Width = width;
1969 *pu32Height = height;
1970 }
1971 else
1972 {
1973 RTMemFree(pu8Data);
1974
1975 /* CopyRect can fail if VBVA was paused in VGA device, retry using the generic method. */
1976 if ( rc == VERR_INVALID_STATE
1977 && aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
1978 {
1979 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort,
1980 ppu8Data, pcbData, pu32Width, pu32Height);
1981 }
1982 }
1983 }
1984 }
1985 else
1986 {
1987 /* No image. */
1988 *ppu8Data = NULL;
1989 *pcbData = 0;
1990 *pu32Width = 0;
1991 *pu32Height = 0;
1992 rc = VINF_SUCCESS;
1993 }
1994 }
1995 else
1996 {
1997 rc = VERR_INVALID_PARAMETER;
1998 }
1999
2000 return rc;
2001}
2002
2003static int i_displayTakeScreenshot(PUVM pUVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, ULONG aScreenId,
2004 BYTE *address, ULONG width, ULONG height)
2005{
2006 uint8_t *pu8Data = NULL;
2007 size_t cbData = 0;
2008 uint32_t cx = 0;
2009 uint32_t cy = 0;
2010 int vrc = VINF_SUCCESS;
2011
2012# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2013 if (Display::i_displayCheckTakeScreenshotCrOgl(pDisplay, aScreenId, (uint8_t*)address, width, height))
2014 return VINF_SUCCESS;
2015#endif
2016
2017 int cRetries = 5;
2018
2019 while (cRetries-- > 0)
2020 {
2021 /* Note! Not sure if the priority call is such a good idea here, but
2022 it would be nice to have an accurate screenshot for the bug
2023 report if the VM deadlocks. */
2024 vrc = VMR3ReqPriorityCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)Display::i_displayTakeScreenshotEMT, 6,
2025 pDisplay, aScreenId, &pu8Data, &cbData, &cx, &cy);
2026 if (vrc != VERR_TRY_AGAIN)
2027 {
2028 break;
2029 }
2030
2031 RTThreadSleep(10);
2032 }
2033
2034 if (RT_SUCCESS(vrc) && pu8Data)
2035 {
2036 if (cx == width && cy == height)
2037 {
2038 /* No scaling required. */
2039 memcpy(address, pu8Data, cbData);
2040 }
2041 else
2042 {
2043 /* Scale. */
2044 LogRelFlowFunc(("SCALE: %dx%d -> %dx%d\n", cx, cy, width, height));
2045
2046 uint8_t *dst = address;
2047 uint8_t *src = pu8Data;
2048 int dstW = width;
2049 int dstH = height;
2050 int srcW = cx;
2051 int srcH = cy;
2052 int iDeltaLine = cx * 4;
2053
2054 BitmapScale32(dst,
2055 dstW, dstH,
2056 src,
2057 iDeltaLine,
2058 srcW, srcH);
2059 }
2060
2061 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2062 {
2063 /* This can be called from any thread. */
2064 pDrv->pUpPort->pfnFreeScreenshot(pDrv->pUpPort, pu8Data);
2065 }
2066 else
2067 {
2068 RTMemFree(pu8Data);
2069 }
2070 }
2071
2072 return vrc;
2073}
2074
2075HRESULT Display::takeScreenShotWorker(ULONG aScreenId,
2076 BYTE *aAddress,
2077 ULONG aWidth,
2078 ULONG aHeight,
2079 BitmapFormat_T aBitmapFormat,
2080 ULONG *pcbOut)
2081{
2082 HRESULT rc = S_OK;
2083
2084 /* Do not allow too small and too large screenshots. This also filters out negative
2085 * values passed as either 'aWidth' or 'aHeight'.
2086 */
2087 CheckComArgExpr(aWidth, aWidth != 0 && aWidth <= 32767);
2088 CheckComArgExpr(aHeight, aHeight != 0 && aHeight <= 32767);
2089
2090 if ( aBitmapFormat != BitmapFormat_BGR0
2091 && aBitmapFormat != BitmapFormat_BGRA
2092 && aBitmapFormat != BitmapFormat_RGBA
2093 && aBitmapFormat != BitmapFormat_PNG)
2094 {
2095 return setError(E_NOTIMPL,
2096 tr("Unsupported screenshot format 0x%08X"), aBitmapFormat);
2097 }
2098
2099 Console::SafeVMPtr ptrVM(mParent);
2100 if (!ptrVM.isOk())
2101 return ptrVM.rc();
2102
2103 int vrc = i_displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, aAddress, aWidth, aHeight);
2104
2105 if (RT_SUCCESS(vrc))
2106 {
2107 const size_t cbData = aWidth * 4 * aHeight;
2108
2109 /* Most of uncompressed formats. */
2110 *pcbOut = (ULONG)cbData;
2111
2112 if (aBitmapFormat == BitmapFormat_BGR0)
2113 {
2114 /* Do nothing. */
2115 }
2116 else if (aBitmapFormat == BitmapFormat_BGRA)
2117 {
2118 uint32_t *pu32 = (uint32_t *)aAddress;
2119 size_t cPixels = aWidth * aHeight;
2120 while (cPixels--)
2121 {
2122 *pu32++ |= UINT32_C(0xFF000000);
2123 }
2124 }
2125 else if (aBitmapFormat == BitmapFormat_RGBA)
2126 {
2127 uint8_t *pu8 = aAddress;
2128 size_t cPixels = aWidth * aHeight;
2129 while (cPixels--)
2130 {
2131 uint8_t u8 = pu8[0];
2132 pu8[0] = pu8[2];
2133 pu8[2] = u8;
2134 pu8[3] = 0xFF;
2135
2136 pu8 += 4;
2137 }
2138 }
2139 else if (aBitmapFormat == BitmapFormat_PNG)
2140 {
2141 uint8_t *pu8PNG = NULL;
2142 uint32_t cbPNG = 0;
2143 uint32_t cxPNG = 0;
2144 uint32_t cyPNG = 0;
2145
2146 vrc = DisplayMakePNG(aAddress, aWidth, aHeight, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
2147 if (RT_SUCCESS(vrc))
2148 {
2149 if (cbPNG <= cbData)
2150 {
2151 memcpy(aAddress, pu8PNG, cbPNG);
2152 *pcbOut = cbPNG;
2153 }
2154 else
2155 {
2156 rc = setError(E_FAIL,
2157 tr("PNG is larger than 32bpp bitmap"));
2158 }
2159 }
2160 else
2161 {
2162 rc = setError(VBOX_E_IPRT_ERROR,
2163 tr("Could not convert screenshot to PNG (%Rrc)"), vrc);
2164 }
2165 RTMemFree(pu8PNG);
2166 }
2167 }
2168 else if (vrc == VERR_TRY_AGAIN)
2169 rc = setError(E_UNEXPECTED,
2170 tr("Screenshot is not available at this time"));
2171 else if (RT_FAILURE(vrc))
2172 rc = setError(VBOX_E_IPRT_ERROR,
2173 tr("Could not take a screenshot (%Rrc)"), vrc);
2174
2175 return rc;
2176}
2177
2178HRESULT Display::takeScreenShot(ULONG aScreenId,
2179 BYTE *aAddress,
2180 ULONG aWidth,
2181 ULONG aHeight,
2182 BitmapFormat_T aBitmapFormat)
2183{
2184 HRESULT rc = S_OK;
2185
2186 LogRelFlowFunc(("[%d] address=%p, width=%d, height=%d, format 0x%08X\n",
2187 aScreenId, aAddress, aWidth, aHeight, aBitmapFormat));
2188
2189 ULONG cbOut = 0;
2190 rc = takeScreenShotWorker(aScreenId, aAddress, aWidth, aHeight, aBitmapFormat, &cbOut);
2191 NOREF(cbOut);
2192
2193 LogRelFlowFunc(("%Rhrc\n", rc));
2194 return rc;
2195}
2196
2197HRESULT Display::takeScreenShotToArray(ULONG aScreenId,
2198 ULONG aWidth,
2199 ULONG aHeight,
2200 BitmapFormat_T aBitmapFormat,
2201 std::vector<BYTE> &aScreenData)
2202{
2203 HRESULT rc = S_OK;
2204
2205 LogRelFlowFunc(("[%d] width=%d, height=%d, format 0x%08X\n",
2206 aScreenId, aWidth, aHeight, aBitmapFormat));
2207
2208 /* Do not allow too small and too large screenshots. This also filters out negative
2209 * values passed as either 'aWidth' or 'aHeight'.
2210 */
2211 CheckComArgExpr(aWidth, aWidth != 0 && aWidth <= 32767);
2212 CheckComArgExpr(aHeight, aHeight != 0 && aHeight <= 32767);
2213
2214 const size_t cbData = aWidth * 4 * aHeight;
2215 aScreenData.resize(cbData);
2216
2217 ULONG cbOut = 0;
2218 rc = takeScreenShotWorker(aScreenId, &aScreenData.front(), aWidth, aHeight, aBitmapFormat, &cbOut);
2219 if (FAILED(rc))
2220 cbOut = 0;
2221
2222 aScreenData.resize(cbOut);
2223
2224 LogRelFlowFunc(("%Rhrc\n", rc));
2225 return rc;
2226}
2227
2228
2229int Display::i_VideoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens))
2230{
2231#ifdef VBOX_WITH_VPX
2232 com::SafeArray<BOOL> Screens(ComSafeArrayInArg(aScreens));
2233 for (unsigned i = 0; i < Screens.size(); i++)
2234 maVideoRecEnabled[i] = RT_BOOL(Screens[i]);
2235 return VINF_SUCCESS;
2236#else
2237 return VERR_NOT_IMPLEMENTED;
2238#endif
2239}
2240
2241/**
2242 * Start video capturing. Does nothing if capturing is already active.
2243 */
2244int Display::i_VideoCaptureStart()
2245{
2246#ifdef VBOX_WITH_VPX
2247 if (VideoRecIsEnabled(mpVideoRecCtx))
2248 return VINF_SUCCESS;
2249
2250 int rc = VideoRecContextCreate(&mpVideoRecCtx, mcMonitors);
2251 if (RT_FAILURE(rc))
2252 {
2253 LogFlow(("Failed to create video recording context (%Rrc)!\n", rc));
2254 return rc;
2255 }
2256 ComPtr<IMachine> pMachine = mParent->i_machine();
2257 com::SafeArray<BOOL> screens;
2258 HRESULT hrc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens));
2259 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2260 for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++)
2261 maVideoRecEnabled[i] = i < screens.size() && screens[i];
2262 ULONG ulWidth;
2263 hrc = pMachine->COMGETTER(VideoCaptureWidth)(&ulWidth);
2264 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2265 ULONG ulHeight;
2266 hrc = pMachine->COMGETTER(VideoCaptureHeight)(&ulHeight);
2267 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2268 ULONG ulRate;
2269 hrc = pMachine->COMGETTER(VideoCaptureRate)(&ulRate);
2270 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2271 ULONG ulFPS;
2272 hrc = pMachine->COMGETTER(VideoCaptureFPS)(&ulFPS);
2273 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2274 BSTR strFile;
2275 hrc = pMachine->COMGETTER(VideoCaptureFile)(&strFile);
2276 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2277 ULONG ulMaxTime;
2278 hrc = pMachine->COMGETTER(VideoCaptureMaxTime)(&ulMaxTime);
2279 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2280 ULONG ulMaxSize;
2281 hrc = pMachine->COMGETTER(VideoCaptureMaxFileSize)(&ulMaxSize);
2282 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2283 BSTR strOptions;
2284 hrc = pMachine->COMGETTER(VideoCaptureOptions)(&strOptions);
2285 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
2286
2287 RTTIMESPEC ts;
2288 RTTimeNow(&ts);
2289 RTTIME time;
2290 RTTimeExplode(&time, &ts);
2291 for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
2292 {
2293 char *pszAbsPath = RTPathAbsDup(com::Utf8Str(strFile).c_str());
2294 char *pszSuff = RTPathSuffix(pszAbsPath);
2295 if (pszSuff)
2296 pszSuff = RTStrDup(pszSuff);
2297 RTPathStripSuffix(pszAbsPath);
2298 if (!pszAbsPath)
2299 rc = VERR_INVALID_PARAMETER;
2300 if (!pszSuff)
2301 pszSuff = RTStrDup(".webm");
2302 char *pszName = NULL;
2303 if (RT_SUCCESS(rc))
2304 {
2305 if (mcMonitors > 1)
2306 rc = RTStrAPrintf(&pszName, "%s-%u%s", pszAbsPath, uScreen+1, pszSuff);
2307 else
2308 rc = RTStrAPrintf(&pszName, "%s%s", pszAbsPath, pszSuff);
2309 }
2310 if (RT_SUCCESS(rc))
2311 {
2312 rc = VideoRecStrmInit(mpVideoRecCtx, uScreen,
2313 pszName, ulWidth, ulHeight,
2314 ulRate, ulFPS, ulMaxTime,
2315 ulMaxSize, com::Utf8Str(strOptions).c_str());
2316 if (rc == VERR_ALREADY_EXISTS)
2317 {
2318 RTStrFree(pszName);
2319 pszName = NULL;
2320
2321 if (mcMonitors > 1)
2322 rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s",
2323 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
2324 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
2325 uScreen+1, pszSuff);
2326 else
2327 rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s",
2328 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay,
2329 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond,
2330 pszSuff);
2331 if (RT_SUCCESS(rc))
2332 rc = VideoRecStrmInit(mpVideoRecCtx, uScreen,
2333 pszName, ulWidth, ulHeight, ulRate,
2334 ulFPS, ulMaxTime,
2335 ulMaxSize, com::Utf8Str(strOptions).c_str());
2336 }
2337 }
2338
2339 if (RT_SUCCESS(rc))
2340 LogRel(("WebM/VP8 video recording screen #%u with %ux%u @ %u kbps, %u fps to '%s' enabled.\n",
2341 uScreen, ulWidth, ulHeight, ulRate, ulFPS, pszName));
2342 else
2343 LogRel(("Failed to initialize video recording context #%u (%Rrc)!\n", uScreen, rc));
2344 RTStrFree(pszName);
2345 RTStrFree(pszSuff);
2346 RTStrFree(pszAbsPath);
2347 }
2348 return rc;
2349#else
2350 return VERR_NOT_IMPLEMENTED;
2351#endif
2352}
2353
2354/**
2355 * Stop video capturing. Does nothing if video capturing is not active.
2356 */
2357void Display::i_VideoCaptureStop()
2358{
2359#ifdef VBOX_WITH_VPX
2360 if (VideoRecIsEnabled(mpVideoRecCtx))
2361 LogRel(("WebM/VP8 video recording stopped.\n"));
2362 VideoRecContextClose(mpVideoRecCtx);
2363 mpVideoRecCtx = NULL;
2364#endif
2365}
2366
2367int Display::i_drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address,
2368 ULONG x, ULONG y, ULONG width, ULONG height)
2369{
2370 int rc = VINF_SUCCESS;
2371
2372 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2373
2374 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2375 {
2376 rc = pDisplay->mpDrv->pUpPort->pfnDisplayBlt(pDisplay->mpDrv->pUpPort, address, x, y, width, height);
2377 }
2378 else if (aScreenId < pDisplay->mcMonitors)
2379 {
2380 /* Copy the bitmap to the guest VRAM. */
2381 const uint8_t *pu8Src = address;
2382 int32_t xSrc = 0;
2383 int32_t ySrc = 0;
2384 uint32_t u32SrcWidth = width;
2385 uint32_t u32SrcHeight = height;
2386 uint32_t u32SrcLineSize = width * 4;
2387 uint32_t u32SrcBitsPerPixel = 32;
2388
2389 uint8_t *pu8Dst = pFBInfo->pu8FramebufferVRAM;
2390 int32_t xDst = x;
2391 int32_t yDst = y;
2392 uint32_t u32DstWidth = pFBInfo->w;
2393 uint32_t u32DstHeight = pFBInfo->h;
2394 uint32_t u32DstLineSize = pFBInfo->u32LineSize;
2395 uint32_t u32DstBitsPerPixel = pFBInfo->u16BitsPerPixel;
2396
2397 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2398 width, height,
2399 pu8Src,
2400 xSrc, ySrc,
2401 u32SrcWidth, u32SrcHeight,
2402 u32SrcLineSize, u32SrcBitsPerPixel,
2403 pu8Dst,
2404 xDst, yDst,
2405 u32DstWidth, u32DstHeight,
2406 u32DstLineSize, u32DstBitsPerPixel);
2407 if (RT_SUCCESS(rc))
2408 {
2409 if (!pFBInfo->pSourceBitmap.isNull())
2410 {
2411 /* Update the changed screen area. When source bitmap uses VRAM directly, just notify
2412 * frontend to update. And for default format, render the guest VRAM to the source bitmap.
2413 */
2414 if ( pFBInfo->fDefaultFormat
2415 && !pFBInfo->fDisabled)
2416 {
2417 BYTE *pAddress = NULL;
2418 ULONG ulWidth = 0;
2419 ULONG ulHeight = 0;
2420 ULONG ulBitsPerPixel = 0;
2421 ULONG ulBytesPerLine = 0;
2422 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2423
2424 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
2425 &ulWidth,
2426 &ulHeight,
2427 &ulBitsPerPixel,
2428 &ulBytesPerLine,
2429 &bitmapFormat);
2430 if (SUCCEEDED(hrc))
2431 {
2432 pu8Src = pFBInfo->pu8FramebufferVRAM;
2433 xSrc = x;
2434 ySrc = y;
2435 u32SrcWidth = pFBInfo->w;
2436 u32SrcHeight = pFBInfo->h;
2437 u32SrcLineSize = pFBInfo->u32LineSize;
2438 u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2439
2440 /* Default format is 32 bpp. */
2441 pu8Dst = pAddress;
2442 xDst = xSrc;
2443 yDst = ySrc;
2444 u32DstWidth = u32SrcWidth;
2445 u32DstHeight = u32SrcHeight;
2446 u32DstLineSize = u32DstWidth * 4;
2447 u32DstBitsPerPixel = 32;
2448
2449 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2450 width, height,
2451 pu8Src,
2452 xSrc, ySrc,
2453 u32SrcWidth, u32SrcHeight,
2454 u32SrcLineSize, u32SrcBitsPerPixel,
2455 pu8Dst,
2456 xDst, yDst,
2457 u32DstWidth, u32DstHeight,
2458 u32DstLineSize, u32DstBitsPerPixel);
2459 }
2460 }
2461 }
2462
2463 pDisplay->i_handleDisplayUpdate(aScreenId, x, y, width, height);
2464 }
2465 }
2466 else
2467 {
2468 rc = VERR_INVALID_PARAMETER;
2469 }
2470
2471 if (RT_SUCCESS(rc))
2472 pDisplay->mParent->i_consoleVRDPServer()->SendUpdateBitmap(aScreenId, x, y, width, height);
2473
2474 return rc;
2475}
2476
2477HRESULT Display::drawToScreen(ULONG aScreenId, BYTE *aAddress, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
2478{
2479 /// @todo (r=dmik) this function may take too long to complete if the VM
2480 // is doing something like saving state right now. Which, in case if it
2481 // is called on the GUI thread, will make it unresponsive. We should
2482 // check the machine state here (by enclosing the check and VMRequCall
2483 // within the Console lock to make it atomic).
2484
2485 LogRelFlowFunc(("aAddress=%p, x=%d, y=%d, width=%d, height=%d\n",
2486 (void *)aAddress, aX, aY, aWidth, aHeight));
2487
2488 CheckComArgExpr(aWidth, aWidth != 0);
2489 CheckComArgExpr(aHeight, aHeight != 0);
2490
2491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2492
2493 CHECK_CONSOLE_DRV(mpDrv);
2494
2495 Console::SafeVMPtr ptrVM(mParent);
2496 if (!ptrVM.isOk())
2497 return ptrVM.rc();
2498
2499 /* Release lock because the call scheduled on EMT may also try to take it. */
2500 alock.release();
2501
2502 /*
2503 * Again we're lazy and make the graphics device do all the
2504 * dirty conversion work.
2505 */
2506 int rcVBox = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_drawToScreenEMT, 7,
2507 this, aScreenId, aAddress, aX, aY, aWidth, aHeight);
2508
2509 /*
2510 * If the function returns not supported, we'll have to do all the
2511 * work ourselves using the framebuffer.
2512 */
2513 HRESULT rc = S_OK;
2514 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
2515 {
2516 /** @todo implement generic fallback for screen blitting. */
2517 rc = E_NOTIMPL;
2518 }
2519 else if (RT_FAILURE(rcVBox))
2520 rc = setError(VBOX_E_IPRT_ERROR,
2521 tr("Could not draw to the screen (%Rrc)"), rcVBox);
2522//@todo
2523// else
2524// {
2525// /* All ok. Redraw the screen. */
2526// handleDisplayUpdate (x, y, width, height);
2527// }
2528
2529 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2530 return rc;
2531}
2532
2533int Display::i_InvalidateAndUpdateEMT(Display *pDisplay, unsigned uId, bool fUpdateAll)
2534{
2535 LogRelFlowFunc(("uId=%d, fUpdateAll %d\n", uId, fUpdateAll));
2536
2537 unsigned uScreenId;
2538 for (uScreenId = (fUpdateAll ? 0 : uId); uScreenId < pDisplay->mcMonitors; uScreenId++)
2539 {
2540 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2541
2542 if ( !pFBInfo->fVBVAEnabled
2543 && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2544 {
2545 pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort, /* fFailOnResize = */ true);
2546 }
2547 else
2548 {
2549 if (!pFBInfo->fDisabled)
2550 {
2551 /* Render complete VRAM screen to the framebuffer.
2552 * When framebuffer uses VRAM directly, just notify it to update.
2553 */
2554 if (pFBInfo->fDefaultFormat && !pFBInfo->pSourceBitmap.isNull())
2555 {
2556 BYTE *pAddress = NULL;
2557 ULONG ulWidth = 0;
2558 ULONG ulHeight = 0;
2559 ULONG ulBitsPerPixel = 0;
2560 ULONG ulBytesPerLine = 0;
2561 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2562
2563 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
2564 &ulWidth,
2565 &ulHeight,
2566 &ulBitsPerPixel,
2567 &ulBytesPerLine,
2568 &bitmapFormat);
2569 if (SUCCEEDED(hrc))
2570 {
2571 uint32_t width = pFBInfo->w;
2572 uint32_t height = pFBInfo->h;
2573
2574 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
2575 int32_t xSrc = 0;
2576 int32_t ySrc = 0;
2577 uint32_t u32SrcWidth = pFBInfo->w;
2578 uint32_t u32SrcHeight = pFBInfo->h;
2579 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
2580 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2581
2582 /* Default format is 32 bpp. */
2583 uint8_t *pu8Dst = pAddress;
2584 int32_t xDst = xSrc;
2585 int32_t yDst = ySrc;
2586 uint32_t u32DstWidth = u32SrcWidth;
2587 uint32_t u32DstHeight = u32SrcHeight;
2588 uint32_t u32DstLineSize = u32DstWidth * 4;
2589 uint32_t u32DstBitsPerPixel = 32;
2590
2591 /* if uWidth != pFBInfo->w and uHeight != pFBInfo->h
2592 * implies resize of Framebuffer is in progress and
2593 * copyrect should not be called.
2594 */
2595 if (ulWidth == pFBInfo->w && ulHeight == pFBInfo->h)
2596 {
2597 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2598 width, height,
2599 pu8Src,
2600 xSrc, ySrc,
2601 u32SrcWidth, u32SrcHeight,
2602 u32SrcLineSize, u32SrcBitsPerPixel,
2603 pu8Dst,
2604 xDst, yDst,
2605 u32DstWidth, u32DstHeight,
2606 u32DstLineSize, u32DstBitsPerPixel);
2607 }
2608 }
2609 }
2610
2611 pDisplay->i_handleDisplayUpdate(uScreenId, 0, 0, pFBInfo->w, pFBInfo->h);
2612 }
2613 }
2614 if (!fUpdateAll)
2615 break;
2616 }
2617 LogRelFlowFunc(("done\n"));
2618 return VINF_SUCCESS;
2619}
2620
2621/**
2622 * Does a full invalidation of the VM display and instructs the VM
2623 * to update it immediately.
2624 *
2625 * @returns COM status code
2626 */
2627
2628HRESULT Display::invalidateAndUpdate()
2629{
2630 LogRelFlowFunc(("\n"));
2631
2632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 CHECK_CONSOLE_DRV(mpDrv);
2635
2636 Console::SafeVMPtr ptrVM(mParent);
2637 if (!ptrVM.isOk())
2638 return ptrVM.rc();
2639
2640 HRESULT rc = S_OK;
2641
2642 LogRelFlowFunc(("Sending DPYUPDATE request\n"));
2643
2644 /* Have to release the lock when calling EMT. */
2645 alock.release();
2646
2647 int rcVBox = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
2648 3, this, 0, true);
2649 alock.acquire();
2650
2651 if (RT_FAILURE(rcVBox))
2652 rc = setError(VBOX_E_IPRT_ERROR,
2653 tr("Could not invalidate and update the screen (%Rrc)"), rcVBox);
2654
2655 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2656 return rc;
2657}
2658
2659HRESULT Display::invalidateAndUpdateScreen(ULONG aScreenId)
2660{
2661 LogRelFlowFunc(("\n"));
2662
2663 HRESULT rc = S_OK;
2664
2665 Console::SafeVMPtr ptrVM(mParent);
2666 if (!ptrVM.isOk())
2667 return ptrVM.rc();
2668
2669 int rcVBox = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
2670 3, this, aScreenId, false);
2671 if (RT_FAILURE(rcVBox))
2672 rc = setError(VBOX_E_IPRT_ERROR,
2673 tr("Could not invalidate and update the screen %d (%Rrc)"), aScreenId, rcVBox);
2674
2675 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2676 return rc;
2677}
2678
2679HRESULT Display::completeVHWACommand(BYTE *aCommand)
2680{
2681#ifdef VBOX_WITH_VIDEOHWACCEL
2682 mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsync(mpDrv->pVBVACallbacks, (PVBOXVHWACMD)aCommand);
2683 return S_OK;
2684#else
2685 return E_NOTIMPL;
2686#endif
2687}
2688
2689HRESULT Display::viewportChanged(ULONG aScreenId, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
2690{
2691 AssertMsgReturn(aScreenId < mcMonitors, ("aScreendId=%d mcMonitors=%d\n", aScreenId, mcMonitors), E_INVALIDARG);
2692
2693#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2694 if (mfIsCr3DEnabled)
2695 {
2696 int rc = i_crViewportNotify(aScreenId, aX, aY, aWidth, aHeight);
2697 if (RT_FAILURE(rc))
2698 {
2699 DISPLAYFBINFO *pFb = &maFramebuffers[aScreenId];
2700 pFb->pendingViewportInfo.fPending = true;
2701 pFb->pendingViewportInfo.x = aX;
2702 pFb->pendingViewportInfo.y = aY;
2703 pFb->pendingViewportInfo.width = aWidth;
2704 pFb->pendingViewportInfo.height = aHeight;
2705 }
2706 }
2707#endif /* VBOX_WITH_CROGL && VBOX_WITH_HGCM */
2708
2709#ifdef VBOX_WITH_VMSVGA
2710 /* The driver might not have been constructed yet */
2711 if (mpDrv)
2712 mpDrv->pUpPort->pfnSetViewPort(mpDrv->pUpPort, aScreenId, aX, aY, aWidth, aHeight);
2713#endif
2714
2715 return S_OK;
2716}
2717
2718HRESULT Display::querySourceBitmap(ULONG aScreenId,
2719 ComPtr<IDisplaySourceBitmap> &aDisplaySourceBitmap)
2720{
2721 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2722
2723 Console::SafeVMPtr ptrVM(mParent);
2724 if (!ptrVM.isOk())
2725 return ptrVM.rc();
2726
2727 bool fSetRenderVRAM = false;
2728 bool fInvalidate = false;
2729
2730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 if (aScreenId >= mcMonitors)
2733 return setError(E_INVALIDARG, tr("QuerySourceBitmap: Invalid screen %d (total %d)"),
2734 aScreenId, mcMonitors);
2735
2736 if (!mfSourceBitmapEnabled)
2737 {
2738 aDisplaySourceBitmap = NULL;
2739 return E_FAIL;
2740 }
2741
2742 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2743
2744 /* No source bitmap for a blank guest screen. */
2745 if (pFBInfo->flags & VBVA_SCREEN_F_BLANK)
2746 {
2747 aDisplaySourceBitmap = NULL;
2748 return E_FAIL;
2749 }
2750
2751 HRESULT hr = S_OK;
2752
2753 if (pFBInfo->pSourceBitmap.isNull())
2754 {
2755 /* Create a new object. */
2756 ComObjPtr<DisplaySourceBitmap> obj;
2757 hr = obj.createObject();
2758 if (SUCCEEDED(hr))
2759 hr = obj->init(this, aScreenId, pFBInfo);
2760
2761 if (SUCCEEDED(hr))
2762 {
2763 bool fDefaultFormat = !obj->i_usesVRAM();
2764
2765 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2766 {
2767 /* Start buffer updates. */
2768 BYTE *pAddress = NULL;
2769 ULONG ulWidth = 0;
2770 ULONG ulHeight = 0;
2771 ULONG ulBitsPerPixel = 0;
2772 ULONG ulBytesPerLine = 0;
2773 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2774
2775 obj->QueryBitmapInfo(&pAddress,
2776 &ulWidth,
2777 &ulHeight,
2778 &ulBitsPerPixel,
2779 &ulBytesPerLine,
2780 &bitmapFormat);
2781
2782 mpDrv->IConnector.pu8Data = pAddress;
2783 mpDrv->IConnector.cbScanline = ulBytesPerLine;
2784 mpDrv->IConnector.cBits = ulBitsPerPixel;
2785 mpDrv->IConnector.cx = ulWidth;
2786 mpDrv->IConnector.cy = ulHeight;
2787
2788 fSetRenderVRAM = fDefaultFormat;
2789 }
2790
2791 /* Make sure that the bitmap contains the latest image. */
2792 fInvalidate = fDefaultFormat;
2793
2794 pFBInfo->pSourceBitmap = obj;
2795 pFBInfo->fDefaultFormat = fDefaultFormat;
2796 }
2797 }
2798
2799 if (SUCCEEDED(hr))
2800 {
2801 pFBInfo->pSourceBitmap.queryInterfaceTo(aDisplaySourceBitmap.asOutParam());
2802 }
2803
2804 /* Leave the IDisplay lock because the VGA device must not be called under it. */
2805 alock.release();
2806
2807 if (SUCCEEDED(hr))
2808 {
2809 if (fSetRenderVRAM)
2810 {
2811 mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, true);
2812 }
2813
2814 if (fInvalidate)
2815 VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
2816 3, this, aScreenId, false);
2817 }
2818
2819 LogRelFlowFunc(("%Rhrc\n", hr));
2820 return hr;
2821}
2822
2823// wrapped IEventListener method
2824HRESULT Display::handleEvent(const ComPtr<IEvent> &aEvent)
2825{
2826 VBoxEventType_T aType = VBoxEventType_Invalid;
2827
2828 aEvent->COMGETTER(Type)(&aType);
2829 switch (aType)
2830 {
2831 case VBoxEventType_OnStateChanged:
2832 {
2833 ComPtr<IStateChangedEvent> scev = aEvent;
2834 Assert(scev);
2835 MachineState_T machineState;
2836 scev->COMGETTER(State)(&machineState);
2837 if ( machineState == MachineState_Running
2838 || machineState == MachineState_Teleporting
2839 || machineState == MachineState_LiveSnapshotting
2840 || machineState == MachineState_DeletingSnapshotOnline
2841 )
2842 {
2843 LogRelFlowFunc(("Machine is running.\n"));
2844
2845#ifdef VBOX_WITH_CROGL
2846 i_crOglWindowsShow(true);
2847#endif
2848 }
2849 else
2850 {
2851#ifdef VBOX_WITH_CROGL
2852 if (machineState == MachineState_Paused)
2853 i_crOglWindowsShow(false);
2854#endif
2855 }
2856 break;
2857 }
2858 default:
2859 AssertFailed();
2860 }
2861
2862 return S_OK;
2863}
2864
2865
2866// private methods
2867/////////////////////////////////////////////////////////////////////////////
2868
2869#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2870int Display::i_crViewportNotify(ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
2871{
2872 VMMDev *pVMMDev = mParent->i_getVMMDev();
2873 if (!pVMMDev)
2874 return VERR_INVALID_STATE;
2875
2876 size_t cbData = RT_UOFFSETOF(VBOXCRCMDCTL_HGCM, aParms[5]);
2877 VBOXCRCMDCTL_HGCM *pData = (VBOXCRCMDCTL_HGCM*)alloca(cbData);
2878
2879 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
2880 pData->Hdr.u32Function = SHCRGL_HOST_FN_VIEWPORT_CHANGED;
2881
2882 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
2883 pData->aParms[0].u.uint32 = aScreenId;
2884
2885 pData->aParms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
2886 pData->aParms[1].u.uint32 = x;
2887
2888 pData->aParms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
2889 pData->aParms[2].u.uint32 = y;
2890
2891 pData->aParms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
2892 pData->aParms[3].u.uint32 = width;
2893
2894 pData->aParms[4].type = VBOX_HGCM_SVC_PARM_32BIT;
2895 pData->aParms[4].u.uint32 = height;
2896
2897 return i_crCtlSubmitSyncIfHasDataForScreen(aScreenId, &pData->Hdr, (uint32_t)cbData);
2898}
2899#endif
2900
2901#ifdef VBOX_WITH_CRHGSMI
2902void Display::i_setupCrHgsmiData(void)
2903{
2904 VMMDev *pVMMDev = mParent->i_getVMMDev();
2905 Assert(pVMMDev);
2906 int rc = RTCritSectRwEnterExcl(&mCrOglLock);
2907 AssertRC(rc);
2908
2909 if (pVMMDev)
2910 rc = pVMMDev->hgcmHostSvcHandleCreate("VBoxSharedCrOpenGL", &mhCrOglSvc);
2911 else
2912 rc = VERR_GENERAL_FAILURE;
2913
2914 if (RT_SUCCESS(rc))
2915 {
2916 Assert(mhCrOglSvc);
2917 /* setup command completion callback */
2918 VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_MAINCB Completion;
2919 Completion.Hdr.enmType = VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_MAINCB;
2920 Completion.Hdr.cbCmd = sizeof(Completion);
2921 Completion.hCompletion = mpDrv->pVBVACallbacks;
2922 Completion.pfnCompletion = mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync;
2923
2924 VBOXHGCMSVCPARM parm;
2925 parm.type = VBOX_HGCM_SVC_PARM_PTR;
2926 parm.u.pointer.addr = &Completion;
2927 parm.u.pointer.size = 0;
2928
2929 rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_CRHGSMI_CTL, 1, &parm);
2930 if (RT_SUCCESS(rc))
2931 mCrOglCallbacks = Completion.MainInterface;
2932 else
2933 AssertMsgFailed(("VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION failed (rc=%Rrc)\n", rc));
2934 }
2935
2936 if (RT_FAILURE(rc))
2937 mhCrOglSvc = NULL;
2938
2939 RTCritSectRwLeaveExcl(&mCrOglLock);
2940}
2941
2942void Display::i_destructCrHgsmiData(void)
2943{
2944 int rc = RTCritSectRwEnterExcl(&mCrOglLock);
2945 AssertRC(rc);
2946 mhCrOglSvc = NULL;
2947 RTCritSectRwLeaveExcl(&mCrOglLock);
2948}
2949#endif /* VBOX_WITH_CRHGSMI */
2950
2951/**
2952 * Handle display resize event issued by the VGA device for the primary screen.
2953 *
2954 * @see PDMIDISPLAYCONNECTOR::pfnResize
2955 */
2956DECLCALLBACK(int) Display::i_displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
2957 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
2958{
2959 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2960 Display *pThis = pDrv->pDisplay;
2961
2962 LogRelFlowFunc(("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
2963 bpp, pvVRAM, cbLine, cx, cy));
2964
2965 bool f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, true, false);
2966 if (!f)
2967 {
2968 /* This is a result of recursive call when the source bitmap is being updated
2969 * during a VGA resize. Tell the VGA device to ignore the call.
2970 *
2971 * @todo It is a workaround, actually pfnUpdateDisplayAll must
2972 * fail on resize.
2973 */
2974 LogRel(("displayResizeCallback: already processing\n"));
2975 return VINF_VGA_RESIZE_IN_PROGRESS;
2976 }
2977
2978 int rc = pThis->i_handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy, VBVA_SCREEN_F_ACTIVE);
2979
2980 /* Restore the flag. */
2981 f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, false, true);
2982 AssertRelease(f);
2983
2984 return rc;
2985}
2986
2987/**
2988 * Handle display update.
2989 *
2990 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
2991 */
2992DECLCALLBACK(void) Display::i_displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
2993 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
2994{
2995 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2996
2997#ifdef DEBUG_sunlover
2998 LogFlowFunc(("fVideoAccelEnabled = %d, %d,%d %dx%d\n",
2999 pDrv->pDisplay->mVideoAccelLegacy.fVideoAccelEnabled, x, y, cx, cy));
3000#endif /* DEBUG_sunlover */
3001
3002 /* This call does update regardless of VBVA status.
3003 * But in VBVA mode this is called only as result of
3004 * pfnUpdateDisplayAll in the VGA device.
3005 */
3006
3007 pDrv->pDisplay->i_handleDisplayUpdate(VBOX_VIDEO_PRIMARY_SCREEN, x, y, cx, cy);
3008}
3009
3010/**
3011 * Periodic display refresh callback.
3012 *
3013 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
3014 * @thread EMT
3015 */
3016/*static*/ DECLCALLBACK(void) Display::i_displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
3017{
3018 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3019
3020#ifdef DEBUG_sunlover_2
3021 LogFlowFunc(("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
3022 pDrv->pDisplay->mfVideoAccelEnabled));
3023#endif /* DEBUG_sunlover_2 */
3024
3025 Display *pDisplay = pDrv->pDisplay;
3026 unsigned uScreenId;
3027
3028 int rc = pDisplay->i_videoAccelRefreshProcess(pDrv->pUpPort);
3029 if (rc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
3030 {
3031 if (rc == VWRN_INVALID_STATE)
3032 {
3033 /* No VBVA do a display update. */
3034 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN];
3035 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
3036 }
3037
3038 /* Inform the VRDP server that the current display update sequence is
3039 * completed. At this moment the framebuffer memory contains a definite
3040 * image, that is synchronized with the orders already sent to VRDP client.
3041 * The server can now process redraw requests from clients or initial
3042 * fullscreen updates for new clients.
3043 */
3044 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3045 {
3046 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3047
3048 Assert(pDisplay->mParent && pDisplay->mParent->i_consoleVRDPServer());
3049 pDisplay->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, NULL, 0);
3050 }
3051 }
3052
3053#ifdef VBOX_WITH_VPX
3054 if (VideoRecIsEnabled(pDisplay->mpVideoRecCtx))
3055 {
3056 do {
3057# if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3058 if (pDisplay->mfIsCr3DEnabled)
3059 {
3060 if (ASMAtomicCmpXchgU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_SUBMITTED, CRVREC_STATE_IDLE))
3061 {
3062 if ( pDisplay->mCrOglCallbacks.pfnHasData
3063 && pDisplay->mCrOglCallbacks.pfnHasData())
3064 {
3065 /* submit */
3066 VBOXCRCMDCTL_HGCM *pData = &pDisplay->mCrOglScreenshotCtl;
3067
3068 pData->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3069 pData->Hdr.u32Function = SHCRGL_HOST_FN_TAKE_SCREENSHOT;
3070
3071 pData->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
3072 pData->aParms[0].u.pointer.addr = &pDisplay->mCrOglScreenshotData;
3073 pData->aParms[0].u.pointer.size = sizeof(pDisplay->mCrOglScreenshotData);
3074 rc = pDisplay->i_crCtlSubmit(&pData->Hdr, sizeof(*pData), Display::i_displayVRecCompletion, pDisplay);
3075 if (RT_SUCCESS(rc))
3076 break;
3077 AssertMsgFailed(("crCtlSubmit failed (rc=%Rrc)\n", rc));
3078 }
3079
3080 /* no 3D data available, or error has occured,
3081 * go the straight way */
3082 ASMAtomicWriteU32(&pDisplay->mfCrOglVideoRecState, CRVREC_STATE_IDLE);
3083 }
3084 else
3085 {
3086 /* record request is still in progress, don't do anything */
3087 break;
3088 }
3089 }
3090# endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
3091
3092 uint64_t u64Now = RTTimeProgramMilliTS();
3093 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3094 {
3095 if (!pDisplay->maVideoRecEnabled[uScreenId])
3096 continue;
3097
3098 if (VideoRecIsFull(pDisplay->mpVideoRecCtx, uScreenId, u64Now))
3099 {
3100 pDisplay->i_VideoCaptureStop();
3101 pDisplay->mParent->i_machine()->COMSETTER(VideoCaptureEnabled)(false);
3102 break;
3103 }
3104
3105 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3106
3107 if ( !pFBInfo->pFramebuffer.isNull()
3108 && !pFBInfo->fDisabled)
3109 {
3110 rc = VERR_NOT_SUPPORTED;
3111 if ( pFBInfo->fVBVAEnabled
3112 && pFBInfo->pu8FramebufferVRAM)
3113 {
3114 rc = VideoRecCopyToIntBuf(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
3115 BitmapFormat_BGR,
3116 pFBInfo->u16BitsPerPixel,
3117 pFBInfo->u32LineSize, pFBInfo->w, pFBInfo->h,
3118 pFBInfo->pu8FramebufferVRAM, u64Now);
3119 }
3120 else if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && pDrv->IConnector.pu8Data)
3121 {
3122 rc = VideoRecCopyToIntBuf(pDisplay->mpVideoRecCtx, uScreenId, 0, 0,
3123 BitmapFormat_BGR,
3124 pDrv->IConnector.cBits,
3125 pDrv->IConnector.cbScanline, pDrv->IConnector.cx,
3126 pDrv->IConnector.cy, pDrv->IConnector.pu8Data, u64Now);
3127 }
3128 if (rc == VINF_TRY_AGAIN)
3129 break;
3130 }
3131 }
3132 } while (0);
3133 }
3134#endif /* VBOX_WITH_VPX */
3135
3136#ifdef DEBUG_sunlover_2
3137 LogFlowFunc(("leave\n"));
3138#endif /* DEBUG_sunlover_2 */
3139}
3140
3141/**
3142 * Reset notification
3143 *
3144 * @see PDMIDISPLAYCONNECTOR::pfnReset
3145 */
3146DECLCALLBACK(void) Display::i_displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
3147{
3148 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3149
3150 LogRelFlowFunc(("\n"));
3151
3152 /* Disable VBVA mode. */
3153 pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
3154}
3155
3156/**
3157 * LFBModeChange notification
3158 *
3159 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
3160 */
3161DECLCALLBACK(void) Display::i_displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
3162{
3163 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3164
3165 LogRelFlowFunc(("fEnabled=%d\n", fEnabled));
3166
3167 NOREF(fEnabled);
3168
3169 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
3170 pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
3171}
3172
3173/**
3174 * Adapter information change notification.
3175 *
3176 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
3177 */
3178DECLCALLBACK(void) Display::i_displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM,
3179 uint32_t u32VRAMSize)
3180{
3181 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3182 pDrv->pDisplay->processAdapterData(pvVRAM, u32VRAMSize);
3183}
3184
3185/**
3186 * Display information change notification.
3187 *
3188 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
3189 */
3190DECLCALLBACK(void) Display::i_displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface,
3191 void *pvVRAM, unsigned uScreenId)
3192{
3193 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3194 pDrv->pDisplay->processDisplayData(pvVRAM, uScreenId);
3195}
3196
3197#ifdef VBOX_WITH_VIDEOHWACCEL
3198
3199#ifndef S_FALSE
3200# define S_FALSE ((HRESULT)1L)
3201#endif
3202
3203int Display::i_handleVHWACommandProcess(PVBOXVHWACMD pCommand)
3204{
3205 unsigned id = (unsigned)pCommand->iDisplay;
3206 int rc = VINF_SUCCESS;
3207 if (id >= mcMonitors)
3208 return VERR_INVALID_PARAMETER;
3209
3210 ComPtr<IFramebuffer> pFramebuffer;
3211 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
3212 pFramebuffer = maFramebuffers[id].pFramebuffer;
3213 bool fVHWASupported = RT_BOOL(maFramebuffers[id].u32Caps & FramebufferCapabilities_VHWA);
3214 arlock.release();
3215
3216 if (pFramebuffer == NULL || !fVHWASupported)
3217 return VERR_NOT_IMPLEMENTED; /* Implementation is not available. */
3218
3219 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
3220 if (hr == S_FALSE)
3221 return VINF_SUCCESS;
3222 else if (SUCCEEDED(hr))
3223 return VINF_CALLBACK_RETURN;
3224 else if (hr == E_ACCESSDENIED)
3225 return VERR_INVALID_STATE; /* notify we can not handle request atm */
3226 else if (hr == E_NOTIMPL)
3227 return VERR_NOT_IMPLEMENTED;
3228 return VERR_GENERAL_FAILURE;
3229}
3230
3231DECLCALLBACK(int) Display::i_displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
3232{
3233 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3234
3235 return pDrv->pDisplay->i_handleVHWACommandProcess(pCommand);
3236}
3237#endif
3238
3239#ifdef VBOX_WITH_CRHGSMI
3240void Display::i_handleCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
3241{
3242 mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync(mpDrv->pVBVACallbacks,
3243 (PVBOXVDMACMD_CHROMIUM_CMD)pParam->u.pointer.addr, result);
3244}
3245
3246void Display::i_handleCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
3247{
3248 PVBOXVDMACMD_CHROMIUM_CTL pCtl = (PVBOXVDMACMD_CHROMIUM_CTL)pParam->u.pointer.addr;
3249 mpDrv->pVBVACallbacks->pfnCrHgsmiControlCompleteAsync(mpDrv->pVBVACallbacks, pCtl, result);
3250}
3251
3252void Display::i_handleCrHgsmiCommandProcess(PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
3253{
3254 int rc = VERR_NOT_SUPPORTED;
3255 VBOXHGCMSVCPARM parm;
3256 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3257 parm.u.pointer.addr = pCmd;
3258 parm.u.pointer.size = cbCmd;
3259
3260 if (mhCrOglSvc)
3261 {
3262 VMMDev *pVMMDev = mParent->i_getVMMDev();
3263 if (pVMMDev)
3264 {
3265 /* no completion callback is specified with this call,
3266 * the CrOgl code will complete the CrHgsmi command once it processes it */
3267 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm, NULL, NULL);
3268 AssertRC(rc);
3269 if (RT_SUCCESS(rc))
3270 return;
3271 }
3272 else
3273 rc = VERR_INVALID_STATE;
3274 }
3275
3276 /* we are here because something went wrong with command processing, complete it */
3277 i_handleCrHgsmiCommandCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm);
3278}
3279
3280void Display::i_handleCrHgsmiControlProcess(PVBOXVDMACMD_CHROMIUM_CTL pCtl, uint32_t cbCtl)
3281{
3282 int rc = VERR_NOT_SUPPORTED;
3283 VBOXHGCMSVCPARM parm;
3284 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3285 parm.u.pointer.addr = pCtl;
3286 parm.u.pointer.size = cbCtl;
3287
3288 if (mhCrOglSvc)
3289 {
3290 VMMDev *pVMMDev = mParent->i_getVMMDev();
3291 if (pVMMDev)
3292 {
3293 bool fCheckPendingViewport = (pCtl->enmType == VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP);
3294 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm,
3295 Display::i_displayCrHgsmiControlCompletion, this);
3296 AssertRC(rc);
3297 if (RT_SUCCESS(rc))
3298 {
3299 if (fCheckPendingViewport)
3300 {
3301 ULONG ul;
3302 for (ul = 0; ul < mcMonitors; ul++)
3303 {
3304 DISPLAYFBINFO *pFb = &maFramebuffers[ul];
3305 if (!pFb->pendingViewportInfo.fPending)
3306 continue;
3307
3308 rc = i_crViewportNotify(ul, pFb->pendingViewportInfo.x, pFb->pendingViewportInfo.y,
3309 pFb->pendingViewportInfo.width, pFb->pendingViewportInfo.height);
3310 if (RT_SUCCESS(rc))
3311 pFb->pendingViewportInfo.fPending = false;
3312 else
3313 {
3314 AssertMsgFailed(("crViewportNotify failed (rc=%Rrc)\n", rc));
3315 rc = VINF_SUCCESS;
3316 }
3317 }
3318 }
3319 return;
3320 }
3321 }
3322 else
3323 rc = VERR_INVALID_STATE;
3324 }
3325
3326 /* we are here because something went wrong with command processing, complete it */
3327 i_handleCrHgsmiControlCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm);
3328}
3329
3330DECLCALLBACK(void) Display::i_displayCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd,
3331 uint32_t cbCmd)
3332{
3333 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3334
3335 pDrv->pDisplay->i_handleCrHgsmiCommandProcess(pCmd, cbCmd);
3336}
3337
3338DECLCALLBACK(void) Display::i_displayCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd,
3339 uint32_t cbCmd)
3340{
3341 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3342
3343 pDrv->pDisplay->i_handleCrHgsmiControlProcess(pCmd, cbCmd);
3344}
3345
3346DECLCALLBACK(void) Display::i_displayCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
3347 void *pvContext)
3348{
3349 AssertMsgFailed(("not expected!\n"));
3350 Display *pDisplay = (Display *)pvContext;
3351 pDisplay->i_handleCrHgsmiCommandCompletion(result, u32Function, pParam);
3352}
3353
3354DECLCALLBACK(void) Display::i_displayCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
3355 void *pvContext)
3356{
3357 Display *pDisplay = (Display *)pvContext;
3358 pDisplay->i_handleCrHgsmiControlCompletion(result, u32Function, pParam);
3359
3360}
3361#endif
3362
3363#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3364DECLCALLBACK(void) Display::i_displayCrHgcmCtlSubmitCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam,
3365 void *pvContext)
3366{
3367 VBOXCRCMDCTL *pCmd = (VBOXCRCMDCTL*)pParam->u.pointer.addr;
3368 if (pCmd->u.pfnInternal)
3369 ((PFNCRCTLCOMPLETION)pCmd->u.pfnInternal)(pCmd, pParam->u.pointer.size, result, pvContext);
3370}
3371
3372int Display::i_handleCrHgcmCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
3373 PFNCRCTLCOMPLETION pfnCompletion,
3374 void *pvCompletion)
3375{
3376 VMMDev *pVMMDev = mParent ? mParent->i_getVMMDev() : NULL;
3377 if (!pVMMDev)
3378 {
3379 AssertMsgFailed(("no vmmdev\n"));
3380 return VERR_INVALID_STATE;
3381 }
3382
3383 Assert(mhCrOglSvc);
3384 VBOXHGCMSVCPARM parm;
3385 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3386 parm.u.pointer.addr = pCmd;
3387 parm.u.pointer.size = cbCmd;
3388
3389 pCmd->u.pfnInternal = (void(*)())pfnCompletion;
3390 int rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CTL, &parm, i_displayCrHgcmCtlSubmitCompletion,
3391 pvCompletion);
3392 if (!RT_SUCCESS(rc))
3393 AssertMsgFailed(("hgcmHostFastCallAsync failed (rc=%Rrc)\n", rc));
3394
3395 return rc;
3396}
3397
3398DECLCALLBACK(int) Display::i_displayCrHgcmCtlSubmit(PPDMIDISPLAYCONNECTOR pInterface,
3399 struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd,
3400 PFNCRCTLCOMPLETION pfnCompletion,
3401 void *pvCompletion)
3402{
3403 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3404 Display *pThis = pDrv->pDisplay;
3405 return pThis->i_handleCrHgcmCtlSubmit(pCmd, cbCmd, pfnCompletion, pvCompletion);
3406}
3407
3408int Display::i_crCtlSubmit(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, PFNCRCTLCOMPLETION pfnCompletion, void *pvCompletion)
3409{
3410 int rc = RTCritSectRwEnterShared(&mCrOglLock);
3411 if (RT_SUCCESS(rc))
3412 {
3413 if (mhCrOglSvc)
3414 rc = mpDrv->pVBVACallbacks->pfnCrCtlSubmit(mpDrv->pVBVACallbacks, pCmd, cbCmd, pfnCompletion, pvCompletion);
3415 else
3416 rc = VERR_NOT_SUPPORTED;
3417
3418 RTCritSectRwLeaveShared(&mCrOglLock);
3419 }
3420 return rc;
3421}
3422
3423int Display::i_crCtlSubmitSync(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
3424{
3425 int rc = RTCritSectRwEnterShared(&mCrOglLock);
3426 if (RT_SUCCESS(rc))
3427 {
3428 if (mhCrOglSvc)
3429 rc = mpDrv->pVBVACallbacks->pfnCrCtlSubmitSync(mpDrv->pVBVACallbacks, pCmd, cbCmd);
3430 else
3431 rc = VERR_NOT_SUPPORTED;
3432
3433 RTCritSectRwLeaveShared(&mCrOglLock);
3434 }
3435 return rc;
3436}
3437
3438int Display::i_crCtlSubmitAsyncCmdCopy(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
3439{
3440 VBOXCRCMDCTL* pCmdCopy = (VBOXCRCMDCTL*)RTMemAlloc(cbCmd);
3441 if (!pCmdCopy)
3442 {
3443 LogRel(("RTMemAlloc failed\n"));
3444 return VERR_NO_MEMORY;
3445 }
3446
3447 memcpy(pCmdCopy, pCmd, cbCmd);
3448
3449 int rc = i_crCtlSubmit(pCmdCopy, cbCmd, i_displayCrCmdFree, pCmdCopy);
3450 if (RT_FAILURE(rc))
3451 {
3452 LogRel(("crCtlSubmit failed (rc=%Rrc)\n", rc));
3453 RTMemFree(pCmdCopy);
3454 return rc;
3455 }
3456
3457 return VINF_SUCCESS;
3458}
3459
3460int Display::i_crCtlSubmitSyncIfHasDataForScreen(uint32_t u32ScreenID, struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd)
3461{
3462 int rc = RTCritSectRwEnterShared(&mCrOglLock);
3463 AssertRCReturn(rc, rc);
3464
3465 if ( mCrOglCallbacks.pfnHasDataForScreen
3466 && mCrOglCallbacks.pfnHasDataForScreen(u32ScreenID))
3467 rc = i_crCtlSubmitSync(pCmd, cbCmd);
3468 else
3469 rc = i_crCtlSubmitAsyncCmdCopy(pCmd, cbCmd);
3470
3471 RTCritSectRwLeaveShared(&mCrOglLock);
3472
3473 return rc;
3474}
3475
3476bool Display::i_handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t u64TimeStamp)
3477{
3478# if VBOX_WITH_VPX
3479 return VideoRecIsReady(mpVideoRecCtx, uScreen, u64TimeStamp);
3480# else
3481 return false;
3482# endif
3483}
3484
3485void Display::i_handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t u64TimeStamp)
3486{
3487}
3488
3489void Display::i_handleCrVRecScreenshotPerform(uint32_t uScreen,
3490 uint32_t x, uint32_t y, uint32_t uPixelFormat,
3491 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
3492 uint32_t uGuestWidth, uint32_t uGuestHeight,
3493 uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
3494{
3495 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
3496# if VBOX_WITH_VPX
3497 int rc = VideoRecCopyToIntBuf(mpVideoRecCtx, uScreen, x, y,
3498 uPixelFormat,
3499 uBitsPerPixel, uBytesPerLine,
3500 uGuestWidth, uGuestHeight,
3501 pu8BufferAddress, u64TimeStamp);
3502 Assert(rc == VINF_SUCCESS /* || rc == VERR_TRY_AGAIN || rc == VINF_TRY_AGAIN*/);
3503# endif
3504}
3505
3506void Display::i_handleVRecCompletion()
3507{
3508 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED);
3509 ASMAtomicWriteU32(&mfCrOglVideoRecState, CRVREC_STATE_IDLE);
3510}
3511
3512#endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
3513
3514HRESULT Display::notifyScaleFactorChange(ULONG aScreenId, ULONG aScaleFactorWMultiplied, ULONG aScaleFactorHMultiplied)
3515{
3516#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3517 HRESULT hr = E_UNEXPECTED;
3518
3519 if (aScreenId >= mcMonitors)
3520 return E_INVALIDARG;
3521
3522 /* 3D acceleration enabled in VM config. */
3523 if (mfIsCr3DEnabled)
3524 {
3525 /* VBoxSharedCrOpenGL HGCM host service is running. */
3526 if (mhCrOglSvc)
3527 {
3528 VMMDev *pVMMDev = mParent->i_getVMMDev();
3529 if (pVMMDev)
3530 {
3531 VBOXCRCMDCTL_HGCM *pCtl;
3532 pCtl = (VBOXCRCMDCTL_HGCM *)RTMemAlloc(sizeof(CRVBOXHGCMSETSCALEFACTOR) + sizeof(VBOXCRCMDCTL_HGCM));
3533 if (pCtl)
3534 {
3535 CRVBOXHGCMSETSCALEFACTOR *pData = (CRVBOXHGCMSETSCALEFACTOR *)(pCtl + 1);
3536 int rc;
3537
3538 pData->u32Screen = aScreenId;
3539 pData->u32ScaleFactorWMultiplied = aScaleFactorWMultiplied;
3540 pData->u32ScaleFactorHMultiplied = aScaleFactorHMultiplied;
3541
3542 pCtl->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3543 pCtl->Hdr.u32Function = SHCRGL_HOST_FN_SET_SCALE_FACTOR;
3544 pCtl->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
3545 pCtl->aParms[0].u.pointer.addr = pData;
3546 pCtl->aParms[0].u.pointer.size = sizeof(*pData);
3547
3548 rc = i_crCtlSubmitSync(&pCtl->Hdr, sizeof(*pCtl));
3549 if (RT_FAILURE(rc))
3550 AssertMsgFailed(("crCtlSubmitSync failed (rc=%Rrc)\n", rc));
3551 else
3552 hr = S_OK;
3553
3554 RTMemFree(pCtl);
3555 }
3556 else
3557 {
3558 LogRel(("Running out of memory on attempt to set OpenGL content scale factor. Ignored.\n"));
3559 hr = E_OUTOFMEMORY;
3560 }
3561 }
3562 else
3563 LogRel(("Internal error occurred on attempt to set OpenGL content scale factor. Ignored.\n"));
3564 }
3565 else
3566 LogRel(("Attempt to specify OpenGL content scale factor while corresponding HGCM host service not yet runing. Ignored.\n"));
3567 }
3568 else
3569# if 0 /** @todo Thank you so very much from anyone using VMSVGA3d! */
3570 AssertMsgFailed(("Attempt to specify OpenGL content scale factor while 3D acceleration is disabled in VM config. Ignored.\n"));
3571# else
3572 {
3573 hr = S_OK;
3574 /* Need an interface like this here (and the #ifdefs needs adjusting):
3575 PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
3576 if (pUpPort && pUpPort->pfnSetScaleFactor)
3577 pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
3578 }
3579# endif
3580
3581 return hr;
3582#else
3583 AssertMsgFailed(("Attempt to specify OpenGL content scale factor while corresponding functionality is disabled."));
3584 return E_UNEXPECTED;
3585#endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
3586}
3587
3588HRESULT Display::notifyHiDPIOutputPolicyChange(BOOL fUnscaledHiDPI)
3589{
3590#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3591 HRESULT hr = E_UNEXPECTED;
3592
3593 /* 3D acceleration enabled in VM config. */
3594 if (mfIsCr3DEnabled)
3595 {
3596 /* VBoxSharedCrOpenGL HGCM host service is running. */
3597 if (mhCrOglSvc)
3598 {
3599 VMMDev *pVMMDev = mParent->i_getVMMDev();
3600 if (pVMMDev)
3601 {
3602 VBOXCRCMDCTL_HGCM *pCtl;
3603 pCtl = (VBOXCRCMDCTL_HGCM *)RTMemAlloc(sizeof(CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT) + sizeof(VBOXCRCMDCTL_HGCM));
3604 if (pCtl)
3605 {
3606 CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT *pData = (CRVBOXHGCMSETUNSCALEDHIDPIOUTPUT *)(pCtl + 1);
3607 int rc;
3608
3609 pData->fUnscaledHiDPI = fUnscaledHiDPI;
3610
3611 pCtl->Hdr.enmType = VBOXCRCMDCTL_TYPE_HGCM;
3612 pCtl->Hdr.u32Function = SHCRGL_HOST_FN_SET_UNSCALED_HIDPI;
3613 pCtl->aParms[0].type = VBOX_HGCM_SVC_PARM_PTR;
3614 pCtl->aParms[0].u.pointer.addr = pData;
3615 pCtl->aParms[0].u.pointer.size = sizeof(*pData);
3616
3617 rc = i_crCtlSubmitSync(&pCtl->Hdr, sizeof(*pCtl));
3618 if (RT_FAILURE(rc))
3619 AssertMsgFailed(("crCtlSubmitSync failed (rc=%Rrc)\n", rc));
3620 else
3621 hr = S_OK;
3622
3623 RTMemFree(pCtl);
3624 }
3625 else
3626 {
3627 LogRel(("Running out of memory on attempt to notify OpenGL about HiDPI output scaling policy change. Ignored.\n"));
3628 hr = E_OUTOFMEMORY;
3629 }
3630 }
3631 else
3632 LogRel(("Internal error occurred on attempt to notify OpenGL about HiDPI output scaling policy change. Ignored.\n"));
3633 }
3634 else
3635 LogRel(("Attempt to notify OpenGL about HiDPI output scaling policy change while corresponding HGCM host service not yet runing. Ignored.\n"));
3636 }
3637 else
3638 {
3639 hr = S_OK;
3640 /* Need an interface like this here (and the #ifdefs needs adjusting):
3641 PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
3642 if (pUpPort && pUpPort->pfnSetScaleFactor)
3643 pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
3644 }
3645
3646 return hr;
3647#else
3648 AssertMsgFailed(("Attempt to notify OpenGL about HiDPI output scaling policy change while corresponding functionality is disabled."));
3649 return E_UNEXPECTED;
3650#endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */
3651}
3652
3653#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
3654DECLCALLBACK(void) Display::i_displayCrVRecScreenshotPerform(void *pvCtx, uint32_t uScreen,
3655 uint32_t x, uint32_t y,
3656 uint32_t uBitsPerPixel, uint32_t uBytesPerLine,
3657 uint32_t uGuestWidth, uint32_t uGuestHeight,
3658 uint8_t *pu8BufferAddress, uint64_t u64TimeStamp)
3659{
3660 Display *pDisplay = (Display *)pvCtx;
3661 pDisplay->i_handleCrVRecScreenshotPerform(uScreen,
3662 x, y, BitmapFormat_BGR, uBitsPerPixel,
3663 uBytesPerLine, uGuestWidth, uGuestHeight,
3664 pu8BufferAddress, u64TimeStamp);
3665}
3666
3667DECLCALLBACK(bool) Display::i_displayCrVRecScreenshotBegin(void *pvCtx, uint32_t uScreen, uint64_t u64TimeStamp)
3668{
3669 Display *pDisplay = (Display *)pvCtx;
3670 return pDisplay->i_handleCrVRecScreenshotBegin(uScreen, u64TimeStamp);
3671}
3672
3673DECLCALLBACK(void) Display::i_displayCrVRecScreenshotEnd(void *pvCtx, uint32_t uScreen, uint64_t u64TimeStamp)
3674{
3675 Display *pDisplay = (Display *)pvCtx;
3676 pDisplay->i_handleCrVRecScreenshotEnd(uScreen, u64TimeStamp);
3677}
3678
3679DECLCALLBACK(void) Display::i_displayVRecCompletion(struct VBOXCRCMDCTL* pCmd, uint32_t cbCmd, int rc, void *pvCompletion)
3680{
3681 Display *pDisplay = (Display *)pvCompletion;
3682 pDisplay->i_handleVRecCompletion();
3683}
3684
3685#endif
3686
3687
3688#ifdef VBOX_WITH_HGSMI
3689DECLCALLBACK(int) Display::i_displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, PVBVAHOSTFLAGS pHostFlags,
3690 bool fRenderThreadMode)
3691{
3692 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
3693
3694 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3695 Display *pThis = pDrv->pDisplay;
3696
3697 if (pThis->maFramebuffers[uScreenId].fVBVAEnabled && pThis->maFramebuffers[uScreenId].fRenderThreadMode != fRenderThreadMode)
3698 {
3699 LogRel(("Enabling different vbva mode\n"));
3700#ifdef DEBUG_misha
3701 AssertMsgFailed(("enabling different vbva mode\n"));
3702#endif
3703 return VERR_INVALID_STATE;
3704 }
3705
3706 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
3707 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
3708 pThis->maFramebuffers[uScreenId].fRenderThreadMode = fRenderThreadMode;
3709 pThis->maFramebuffers[uScreenId].fVBVAForceResize = true;
3710
3711 vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
3712
3713 return VINF_SUCCESS;
3714}
3715
3716DECLCALLBACK(void) Display::i_displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
3717{
3718 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
3719
3720 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3721 Display *pThis = pDrv->pDisplay;
3722
3723 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3724
3725 bool fRenderThreadMode = pFBInfo->fRenderThreadMode;
3726
3727 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3728 {
3729 /* Make sure that the primary screen is visible now.
3730 * The guest can't use VBVA anymore, so only only the VGA device output works.
3731 */
3732 if (pFBInfo->fDisabled)
3733 {
3734 pFBInfo->fDisabled = false;
3735 fireGuestMonitorChangedEvent(pThis->mParent->i_getEventSource(),
3736 GuestMonitorChangedEventType_Enabled,
3737 uScreenId,
3738 pFBInfo->xOrigin, pFBInfo->yOrigin,
3739 pFBInfo->w, pFBInfo->h);
3740 }
3741 }
3742
3743 pFBInfo->fVBVAEnabled = false;
3744 pFBInfo->fVBVAForceResize = false;
3745 pFBInfo->fRenderThreadMode = false;
3746
3747 vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, pFBInfo);
3748
3749 pFBInfo->pVBVAHostFlags = NULL;
3750
3751 if (!fRenderThreadMode && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3752 {
3753 /* Force full screen update, because VGA device must take control, do resize, etc. */
3754 pThis->mpDrv->pUpPort->pfnUpdateDisplayAll(pThis->mpDrv->pUpPort, /* fFailOnResize = */ false);
3755 }
3756}
3757
3758DECLCALLBACK(void) Display::i_displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
3759{
3760 LogFlowFunc(("uScreenId %d\n", uScreenId));
3761
3762 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3763 Display *pThis = pDrv->pDisplay;
3764 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3765
3766 if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
3767 {
3768 vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers,
3769 pThis->mcMonitors);
3770 ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
3771 }
3772}
3773
3774DECLCALLBACK(void) Display::i_displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
3775 const PVBVACMDHDR pCmd, size_t cbCmd)
3776{
3777 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d, @%d,%d %dx%d\n", uScreenId, pCmd, cbCmd, pCmd->x, pCmd->y, pCmd->w, pCmd->h));
3778
3779 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3780 Display *pThis = pDrv->pDisplay;
3781 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3782
3783 if (pFBInfo->fDefaultFormat)
3784 {
3785 /* Make sure that framebuffer contains the same image as the guest VRAM. */
3786 if ( uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
3787 && !pFBInfo->fDisabled)
3788 {
3789 pDrv->pUpPort->pfnUpdateDisplayRect(pDrv->pUpPort, pCmd->x, pCmd->y, pCmd->w, pCmd->h);
3790 }
3791 else if ( !pFBInfo->pSourceBitmap.isNull()
3792 && !pFBInfo->fDisabled)
3793 {
3794 /* Render VRAM content to the framebuffer. */
3795 BYTE *pAddress = NULL;
3796 ULONG ulWidth = 0;
3797 ULONG ulHeight = 0;
3798 ULONG ulBitsPerPixel = 0;
3799 ULONG ulBytesPerLine = 0;
3800 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
3801
3802 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
3803 &ulWidth,
3804 &ulHeight,
3805 &ulBitsPerPixel,
3806 &ulBytesPerLine,
3807 &bitmapFormat);
3808 if (SUCCEEDED(hrc))
3809 {
3810 uint32_t width = pCmd->w;
3811 uint32_t height = pCmd->h;
3812
3813 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
3814 int32_t xSrc = pCmd->x - pFBInfo->xOrigin;
3815 int32_t ySrc = pCmd->y - pFBInfo->yOrigin;
3816 uint32_t u32SrcWidth = pFBInfo->w;
3817 uint32_t u32SrcHeight = pFBInfo->h;
3818 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
3819 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3820
3821 uint8_t *pu8Dst = pAddress;
3822 int32_t xDst = xSrc;
3823 int32_t yDst = ySrc;
3824 uint32_t u32DstWidth = u32SrcWidth;
3825 uint32_t u32DstHeight = u32SrcHeight;
3826 uint32_t u32DstLineSize = u32DstWidth * 4;
3827 uint32_t u32DstBitsPerPixel = 32;
3828
3829 pDrv->pUpPort->pfnCopyRect(pDrv->pUpPort,
3830 width, height,
3831 pu8Src,
3832 xSrc, ySrc,
3833 u32SrcWidth, u32SrcHeight,
3834 u32SrcLineSize, u32SrcBitsPerPixel,
3835 pu8Dst,
3836 xDst, yDst,
3837 u32DstWidth, u32DstHeight,
3838 u32DstLineSize, u32DstBitsPerPixel);
3839 }
3840 }
3841 }
3842
3843 VBVACMDHDR hdrSaved = *pCmd;
3844
3845 VBVACMDHDR *pHdrUnconst = (VBVACMDHDR *)pCmd;
3846
3847 pHdrUnconst->x -= (int16_t)pFBInfo->xOrigin;
3848 pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
3849
3850 /* @todo new SendUpdate entry which can get a separate cmd header or coords. */
3851 pThis->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, pCmd, (uint32_t)cbCmd);
3852
3853 *pHdrUnconst = hdrSaved;
3854}
3855
3856DECLCALLBACK(void) Display::i_displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y,
3857 uint32_t cx, uint32_t cy)
3858{
3859 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
3860
3861 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3862 Display *pThis = pDrv->pDisplay;
3863 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3864
3865 /* @todo handleFramebufferUpdate (uScreenId,
3866 * x - pThis->maFramebuffers[uScreenId].xOrigin,
3867 * y - pThis->maFramebuffers[uScreenId].yOrigin,
3868 * cx, cy);
3869 */
3870 pThis->i_handleDisplayUpdate(uScreenId, x - pFBInfo->xOrigin, y - pFBInfo->yOrigin, cx, cy);
3871}
3872
3873#ifdef DEBUG_sunlover
3874static void logVBVAResize(const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, const DISPLAYFBINFO *pFBInfo)
3875{
3876 LogRel(("displayVBVAResize: [%d] %s\n"
3877 " pView->u32ViewIndex %d\n"
3878 " pView->u32ViewOffset 0x%08X\n"
3879 " pView->u32ViewSize 0x%08X\n"
3880 " pView->u32MaxScreenSize 0x%08X\n"
3881 " pScreen->i32OriginX %d\n"
3882 " pScreen->i32OriginY %d\n"
3883 " pScreen->u32StartOffset 0x%08X\n"
3884 " pScreen->u32LineSize 0x%08X\n"
3885 " pScreen->u32Width %d\n"
3886 " pScreen->u32Height %d\n"
3887 " pScreen->u16BitsPerPixel %d\n"
3888 " pScreen->u16Flags 0x%04X\n"
3889 " pFBInfo->u32Offset 0x%08X\n"
3890 " pFBInfo->u32MaxFramebufferSize 0x%08X\n"
3891 " pFBInfo->u32InformationSize 0x%08X\n"
3892 " pFBInfo->fDisabled %d\n"
3893 " xOrigin, yOrigin, w, h: %d,%d %dx%d\n"
3894 " pFBInfo->u16BitsPerPixel %d\n"
3895 " pFBInfo->pu8FramebufferVRAM %p\n"
3896 " pFBInfo->u32LineSize 0x%08X\n"
3897 " pFBInfo->flags 0x%04X\n"
3898 " pFBInfo->pHostEvents %p\n"
3899 " pFBInfo->fDefaultFormat %d\n"
3900 " pFBInfo->fVBVAEnabled %d\n"
3901 " pFBInfo->fVBVAForceResize %d\n"
3902 " pFBInfo->pVBVAHostFlags %p\n"
3903 "",
3904 pScreen->u32ViewIndex,
3905 (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)? "DISABLED": "ENABLED",
3906 pView->u32ViewIndex,
3907 pView->u32ViewOffset,
3908 pView->u32ViewSize,
3909 pView->u32MaxScreenSize,
3910 pScreen->i32OriginX,
3911 pScreen->i32OriginY,
3912 pScreen->u32StartOffset,
3913 pScreen->u32LineSize,
3914 pScreen->u32Width,
3915 pScreen->u32Height,
3916 pScreen->u16BitsPerPixel,
3917 pScreen->u16Flags,
3918 pFBInfo->u32Offset,
3919 pFBInfo->u32MaxFramebufferSize,
3920 pFBInfo->u32InformationSize,
3921 pFBInfo->fDisabled,
3922 pFBInfo->xOrigin,
3923 pFBInfo->yOrigin,
3924 pFBInfo->w,
3925 pFBInfo->h,
3926 pFBInfo->u16BitsPerPixel,
3927 pFBInfo->pu8FramebufferVRAM,
3928 pFBInfo->u32LineSize,
3929 pFBInfo->flags,
3930 pFBInfo->pHostEvents,
3931 pFBInfo->fDefaultFormat,
3932 pFBInfo->fVBVAEnabled,
3933 pFBInfo->fVBVAForceResize,
3934 pFBInfo->pVBVAHostFlags
3935 ));
3936}
3937#endif /* DEBUG_sunlover */
3938
3939DECLCALLBACK(int) Display::i_displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, const PVBVAINFOVIEW pView,
3940 const PVBVAINFOSCREEN pScreen, void *pvVRAM)
3941{
3942 LogRelFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
3943
3944 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3945 Display *pThis = pDrv->pDisplay;
3946
3947 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[pScreen->u32ViewIndex];
3948
3949 if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
3950 {
3951 pThis->i_notifyCroglResize(pView, pScreen, pvVRAM);
3952
3953 pFBInfo->fDisabled = true;
3954 pFBInfo->flags = pScreen->u16Flags;
3955
3956 /* Ask the framebuffer to resize using a default format. The framebuffer will be black.
3957 * So if the frontend does not support GuestMonitorChangedEventType_Disabled event,
3958 * the VM window will be black. */
3959 uint32_t u32Width = pFBInfo->w ? pFBInfo->w : 640;
3960 uint32_t u32Height = pFBInfo->h ? pFBInfo->h : 480;
3961 pThis->i_handleDisplayResize(pScreen->u32ViewIndex, 0, (uint8_t *)NULL, 0,
3962 u32Width, u32Height, pScreen->u16Flags);
3963
3964 fireGuestMonitorChangedEvent(pThis->mParent->i_getEventSource(),
3965 GuestMonitorChangedEventType_Disabled,
3966 pScreen->u32ViewIndex,
3967 0, 0, 0, 0);
3968 return VINF_SUCCESS;
3969 }
3970
3971 /* If display was disabled or there is no framebuffer, a resize will be required,
3972 * because the framebuffer was/will be changed.
3973 */
3974 bool fResize = pFBInfo->fDisabled || pFBInfo->pFramebuffer.isNull();
3975
3976 if (pFBInfo->fVBVAForceResize)
3977 {
3978 /* VBVA was just enabled. Do the resize. */
3979 fResize = true;
3980 pFBInfo->fVBVAForceResize = false;
3981 }
3982
3983 /* If the screen if blanked, then do a resize request to make sure that the framebuffer
3984 * switches to the default format.
3985 */
3986 fResize = fResize || RT_BOOL((pScreen->u16Flags ^ pFBInfo->flags) & VBVA_SCREEN_F_BLANK);
3987
3988 /* Check if this is a real resize or a notification about the screen origin.
3989 * The guest uses this VBVAResize call for both.
3990 */
3991 fResize = fResize
3992 || pFBInfo->u16BitsPerPixel != pScreen->u16BitsPerPixel
3993 || pFBInfo->pu8FramebufferVRAM != (uint8_t *)pvVRAM + pScreen->u32StartOffset
3994 || pFBInfo->u32LineSize != pScreen->u32LineSize
3995 || pFBInfo->w != pScreen->u32Width
3996 || pFBInfo->h != pScreen->u32Height;
3997
3998 bool fNewOrigin = pFBInfo->xOrigin != pScreen->i32OriginX
3999 || pFBInfo->yOrigin != pScreen->i32OriginY;
4000
4001 if (fNewOrigin || fResize)
4002 pThis->i_notifyCroglResize(pView, pScreen, pvVRAM);
4003
4004 if (pFBInfo->fDisabled)
4005 {
4006 pFBInfo->fDisabled = false;
4007 fireGuestMonitorChangedEvent(pThis->mParent->i_getEventSource(),
4008 GuestMonitorChangedEventType_Enabled,
4009 pScreen->u32ViewIndex,
4010 pScreen->i32OriginX, pScreen->i32OriginY,
4011 pScreen->u32Width, pScreen->u32Height);
4012 /* Continue to update pFBInfo. */
4013 }
4014
4015 pFBInfo->u32Offset = pView->u32ViewOffset; /* Not used in HGSMI. */
4016 pFBInfo->u32MaxFramebufferSize = pView->u32MaxScreenSize; /* Not used in HGSMI. */
4017 pFBInfo->u32InformationSize = 0; /* Not used in HGSMI. */
4018
4019 pFBInfo->xOrigin = pScreen->i32OriginX;
4020 pFBInfo->yOrigin = pScreen->i32OriginY;
4021
4022 pFBInfo->w = pScreen->u32Width;
4023 pFBInfo->h = pScreen->u32Height;
4024
4025 pFBInfo->u16BitsPerPixel = pScreen->u16BitsPerPixel;
4026 pFBInfo->pu8FramebufferVRAM = (uint8_t *)pvVRAM + pScreen->u32StartOffset;
4027 pFBInfo->u32LineSize = pScreen->u32LineSize;
4028
4029 pFBInfo->flags = pScreen->u16Flags;
4030
4031 pThis->xInputMappingOrigin = 0;
4032 pThis->yInputMappingOrigin = 0;
4033 pThis->cxInputMapping = 0;
4034 pThis->cyInputMapping = 0;
4035
4036 if (fNewOrigin)
4037 {
4038 fireGuestMonitorChangedEvent(pThis->mParent->i_getEventSource(),
4039 GuestMonitorChangedEventType_NewOrigin,
4040 pScreen->u32ViewIndex,
4041 pScreen->i32OriginX, pScreen->i32OriginY,
4042 0, 0);
4043 }
4044
4045 if (!fResize)
4046 {
4047 /* No parameters of the framebuffer have actually changed. */
4048 if (fNewOrigin)
4049 {
4050 /* VRDP server still need this notification. */
4051 LogRelFlowFunc(("Calling VRDP\n"));
4052 pThis->mParent->i_consoleVRDPServer()->SendResize();
4053 }
4054 return VINF_SUCCESS;
4055 }
4056
4057 /* Do a regular resize. */
4058 return pThis->i_handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
4059 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
4060 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height, pScreen->u16Flags);
4061}
4062
4063DECLCALLBACK(int) Display::i_displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
4064 uint32_t xHot, uint32_t yHot,
4065 uint32_t cx, uint32_t cy,
4066 const void *pvShape)
4067{
4068 LogFlowFunc(("\n"));
4069
4070 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4071 Display *pThis = pDrv->pDisplay;
4072
4073 uint32_t cbShape = 0;
4074 if (pvShape)
4075 {
4076 cbShape = (cx + 7) / 8 * cy; /* size of the AND mask */
4077 cbShape = ((cbShape + 3) & ~3) + cx * 4 * cy; /* + gap + size of the XOR mask */
4078 }
4079
4080 /* Tell the console about it */
4081 pDrv->pDisplay->mParent->i_onMousePointerShapeChange(fVisible, fAlpha,
4082 xHot, yHot, cx, cy, (uint8_t *)pvShape, cbShape);
4083
4084 return VINF_SUCCESS;
4085}
4086
4087DECLCALLBACK(void) Display::i_displayVBVAGuestCapabilityUpdate(PPDMIDISPLAYCONNECTOR pInterface, uint32_t fCapabilities)
4088{
4089 LogFlowFunc(("\n"));
4090
4091 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4092 Display *pThis = pDrv->pDisplay;
4093
4094 pThis->i_handleUpdateGuestVBVACapabilities(fCapabilities);
4095}
4096
4097DECLCALLBACK(void) Display::i_displayVBVAInputMappingUpdate(PPDMIDISPLAYCONNECTOR pInterface, int32_t xOrigin, int32_t yOrigin,
4098 uint32_t cx, uint32_t cy)
4099{
4100 LogFlowFunc(("\n"));
4101
4102 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4103 Display *pThis = pDrv->pDisplay;
4104
4105 pThis->i_handleUpdateVBVAInputMapping(xOrigin, yOrigin, cx, cy);
4106}
4107
4108#endif /* VBOX_WITH_HGSMI */
4109
4110/**
4111 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4112 */
4113DECLCALLBACK(void *) Display::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4114{
4115 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
4116 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4117 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
4118 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYCONNECTOR, &pDrv->IConnector);
4119 return NULL;
4120}
4121
4122
4123/**
4124 * Destruct a display driver instance.
4125 *
4126 * @returns VBox status.
4127 * @param pDrvIns The driver instance data.
4128 */
4129DECLCALLBACK(void) Display::i_drvDestruct(PPDMDRVINS pDrvIns)
4130{
4131 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4132 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4133 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
4134
4135 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
4136
4137 pThis->IConnector.pu8Data = NULL;
4138 pThis->IConnector.cbScanline = 0;
4139 pThis->IConnector.cBits = 32;
4140 pThis->IConnector.cx = 0;
4141 pThis->IConnector.cy = 0;
4142
4143 if (pThis->pDisplay)
4144 {
4145 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
4146#ifdef VBOX_WITH_VPX
4147 pThis->pDisplay->i_VideoCaptureStop();
4148#endif
4149#ifdef VBOX_WITH_CRHGSMI
4150 pThis->pDisplay->i_destructCrHgsmiData();
4151#endif
4152 pThis->pDisplay->mpDrv = NULL;
4153 pThis->pDisplay->mpVMMDev = NULL;
4154 }
4155}
4156
4157
4158/**
4159 * Construct a display driver instance.
4160 *
4161 * @copydoc FNPDMDRVCONSTRUCT
4162 */
4163DECLCALLBACK(int) Display::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
4164{
4165 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
4166 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4167 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
4168
4169 /*
4170 * Validate configuration.
4171 */
4172 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
4173 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
4174 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
4175 ("Configuration error: Not possible to attach anything to this driver!\n"),
4176 VERR_PDM_DRVINS_NO_ATTACH);
4177
4178 /*
4179 * Init Interfaces.
4180 */
4181 pDrvIns->IBase.pfnQueryInterface = Display::i_drvQueryInterface;
4182
4183 pThis->IConnector.pfnResize = Display::i_displayResizeCallback;
4184 pThis->IConnector.pfnUpdateRect = Display::i_displayUpdateCallback;
4185 pThis->IConnector.pfnRefresh = Display::i_displayRefreshCallback;
4186 pThis->IConnector.pfnReset = Display::i_displayResetCallback;
4187 pThis->IConnector.pfnLFBModeChange = Display::i_displayLFBModeChangeCallback;
4188 pThis->IConnector.pfnProcessAdapterData = Display::i_displayProcessAdapterDataCallback;
4189 pThis->IConnector.pfnProcessDisplayData = Display::i_displayProcessDisplayDataCallback;
4190#ifdef VBOX_WITH_VIDEOHWACCEL
4191 pThis->IConnector.pfnVHWACommandProcess = Display::i_displayVHWACommandProcess;
4192#endif
4193#ifdef VBOX_WITH_CRHGSMI
4194 pThis->IConnector.pfnCrHgsmiCommandProcess = Display::i_displayCrHgsmiCommandProcess;
4195 pThis->IConnector.pfnCrHgsmiControlProcess = Display::i_displayCrHgsmiControlProcess;
4196#endif
4197#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4198 pThis->IConnector.pfnCrHgcmCtlSubmit = Display::i_displayCrHgcmCtlSubmit;
4199#endif
4200#ifdef VBOX_WITH_HGSMI
4201 pThis->IConnector.pfnVBVAEnable = Display::i_displayVBVAEnable;
4202 pThis->IConnector.pfnVBVADisable = Display::i_displayVBVADisable;
4203 pThis->IConnector.pfnVBVAUpdateBegin = Display::i_displayVBVAUpdateBegin;
4204 pThis->IConnector.pfnVBVAUpdateProcess = Display::i_displayVBVAUpdateProcess;
4205 pThis->IConnector.pfnVBVAUpdateEnd = Display::i_displayVBVAUpdateEnd;
4206 pThis->IConnector.pfnVBVAResize = Display::i_displayVBVAResize;
4207 pThis->IConnector.pfnVBVAMousePointerShape = Display::i_displayVBVAMousePointerShape;
4208 pThis->IConnector.pfnVBVAGuestCapabilityUpdate = Display::i_displayVBVAGuestCapabilityUpdate;
4209 pThis->IConnector.pfnVBVAInputMappingUpdate = Display::i_displayVBVAInputMappingUpdate;
4210#endif
4211
4212 /*
4213 * Get the IDisplayPort interface of the above driver/device.
4214 */
4215 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
4216 if (!pThis->pUpPort)
4217 {
4218 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
4219 return VERR_PDM_MISSING_INTERFACE_ABOVE;
4220 }
4221#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
4222 pThis->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
4223 if (!pThis->pVBVACallbacks)
4224 {
4225 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
4226 return VERR_PDM_MISSING_INTERFACE_ABOVE;
4227 }
4228#endif
4229 /*
4230 * Get the Display object pointer and update the mpDrv member.
4231 */
4232 void *pv;
4233 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
4234 if (RT_FAILURE(rc))
4235 {
4236 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
4237 return rc;
4238 }
4239 Display *pDisplay = (Display *)pv; /** @todo Check this cast! */
4240 pThis->pDisplay = pDisplay;
4241 pThis->pDisplay->mpDrv = pThis;
4242
4243 /* Disable VRAM to a buffer copy initially. */
4244 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
4245 pThis->IConnector.cBits = 32; /* DevVGA does nothing otherwise. */
4246
4247 /*
4248 * Start periodic screen refreshes
4249 */
4250 pThis->pUpPort->pfnSetRefreshRate(pThis->pUpPort, 20);
4251
4252#ifdef VBOX_WITH_CRHGSMI
4253 pDisplay->i_setupCrHgsmiData();
4254#endif
4255
4256#ifdef VBOX_WITH_VPX
4257 ComPtr<IMachine> pMachine = pDisplay->mParent->i_machine();
4258 BOOL fEnabled = false;
4259 HRESULT hrc = pMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);
4260 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED);
4261 if (fEnabled)
4262 {
4263 rc = pDisplay->i_VideoCaptureStart();
4264 fireVideoCaptureChangedEvent(pDisplay->mParent->i_getEventSource());
4265 }
4266#endif
4267
4268 return rc;
4269}
4270
4271
4272/**
4273 * Display driver registration record.
4274 */
4275const PDMDRVREG Display::DrvReg =
4276{
4277 /* u32Version */
4278 PDM_DRVREG_VERSION,
4279 /* szName */
4280 "MainDisplay",
4281 /* szRCMod */
4282 "",
4283 /* szR0Mod */
4284 "",
4285 /* pszDescription */
4286 "Main display driver (Main as in the API).",
4287 /* fFlags */
4288 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
4289 /* fClass. */
4290 PDM_DRVREG_CLASS_DISPLAY,
4291 /* cMaxInstances */
4292 ~0U,
4293 /* cbInstance */
4294 sizeof(DRVMAINDISPLAY),
4295 /* pfnConstruct */
4296 Display::i_drvConstruct,
4297 /* pfnDestruct */
4298 Display::i_drvDestruct,
4299 /* pfnRelocate */
4300 NULL,
4301 /* pfnIOCtl */
4302 NULL,
4303 /* pfnPowerOn */
4304 NULL,
4305 /* pfnReset */
4306 NULL,
4307 /* pfnSuspend */
4308 NULL,
4309 /* pfnResume */
4310 NULL,
4311 /* pfnAttach */
4312 NULL,
4313 /* pfnDetach */
4314 NULL,
4315 /* pfnPowerOff */
4316 NULL,
4317 /* pfnSoftReset */
4318 NULL,
4319 /* u32EndVersion */
4320 PDM_DRVREG_VERSION
4321};
4322
4323/* 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