VirtualBox

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

Last change on this file since 96391 was 96391, checked in by vboxsync, 2 years ago

Recording/Main: Removed unnecessary VPX includes from DisplayImpl.cpp. bugref:10275

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