VirtualBox

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

Last change on this file since 107491 was 106061, checked in by vboxsync, 4 months ago

Copyright year updates by scm.

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

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