VirtualBox

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

最後變更 在這個檔案從52442是 52427,由 vboxsync 提交於 11 年 前

#6219: Segment duration calculation fixes. Coding style fixes.

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

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