VirtualBox

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

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

DevVGA,DisplayImpl.cpp,pdmifs.h: Don't require EMT for pfnUpdateDisplayAll and pfnCopyRect. (Prep for moving real clock timers to thread.) bugref:9943

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

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