VirtualBox

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

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

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use