VirtualBox

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

Last change on this file since 105006 was 105006, checked in by vboxsync, 9 months ago

Video Recording: Big revamp to improve overall performance. We now don't rely on the periodic display refresh callback anymore to render the entire framebuffer but now rely on delta updates ("dirty rectangles"). Also, we now only encode new frames when an area has changed. This also needed cursor position + change change notifications, as we render the cursor on the host side if mouse integration is enabled (requires 7.1 Guest Additions as of now). Optimized the BGRA32->YUV IV420 color space conversion as well as the overall amount of pixel data shuffled forth and back. Added a new testcase for the cropping/centering code. bugref:10650

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

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