VirtualBox

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

Last change on this file since 85121 was 85121, checked in by vboxsync, 4 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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