VirtualBox

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

Last change on this file since 94967 was 94916, checked in by vboxsync, 3 years ago

Main/src-client/DisplayImpl.cpp: Adjust to the new rules wrt. to rc -> hrc,vrc usage, ​bugref:10223

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