VirtualBox

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

Last change on this file since 105065 was 105065, checked in by vboxsync, 5 months ago

Video Recording/Main: Fixed (release) logging when recording is disabled. bugref:10650

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