VirtualBox

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

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

HGCM: Applied fix for bugref:10038.

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

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