VirtualBox

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

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

Devices/Graphics,Main,include: Experimental graphics output. bugref:9695

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 131.4 KB
Line 
1/* $Id: DisplayImpl.cpp 85368 2020-07-17 09:55:56Z 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 {
979#else
980 if (!mVideoAccelLegacy.fVideoAccelEnabled && !maFramebuffers[uScreenId].fVBVAEnabled)
981 {
982#endif /* VBOX_WITH_HGSMI */
983 /* When VBVA is enabled, the VRDP server is informed
984 * either in VideoAccelFlush or displayVBVAUpdateProcess.
985 * Inform the server here only if VBVA is disabled.
986 */
987 mParent->i_consoleVRDPServer()->SendUpdateBitmap(uScreenId, x, y, w, h);
988 }
989}
990
991void Display::i_updateGuestGraphicsFacility(void)
992{
993 Guest* pGuest = mParent->i_getGuest();
994 AssertPtrReturnVoid(pGuest);
995 /* The following is from GuestImpl.cpp. */
996 /** @todo A nit: The timestamp is wrong on saved state restore. Would be better
997 * to move the graphics and seamless capability -> facility translation to
998 * VMMDev so this could be saved. */
999 RTTIMESPEC TimeSpecTS;
1000 RTTimeNow(&TimeSpecTS);
1001
1002 if ( mfVMMDevSupportsGraphics
1003 || (mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS) != 0)
1004 pGuest->i_setAdditionsStatus(VBoxGuestFacilityType_Graphics,
1005 VBoxGuestFacilityStatus_Active,
1006 0 /*fFlags*/, &TimeSpecTS);
1007 else
1008 pGuest->i_setAdditionsStatus(VBoxGuestFacilityType_Graphics,
1009 VBoxGuestFacilityStatus_Inactive,
1010 0 /*fFlags*/, &TimeSpecTS);
1011}
1012
1013void Display::i_handleUpdateVMMDevSupportsGraphics(bool fSupportsGraphics)
1014{
1015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1016 if (mfVMMDevSupportsGraphics == fSupportsGraphics)
1017 return;
1018 mfVMMDevSupportsGraphics = fSupportsGraphics;
1019 i_updateGuestGraphicsFacility();
1020 /* The VMMDev interface notifies the console. */
1021}
1022
1023void Display::i_handleUpdateGuestVBVACapabilities(uint32_t fNewCapabilities)
1024{
1025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1026 bool fNotify = (fNewCapabilities & VBVACAPS_VIDEO_MODE_HINTS) != (mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS);
1027
1028 mfGuestVBVACapabilities = fNewCapabilities;
1029 if (!fNotify)
1030 return;
1031 i_updateGuestGraphicsFacility();
1032 /* Tell the console about it */
1033 mParent->i_onAdditionsStateChange();
1034}
1035
1036void Display::i_handleUpdateVBVAInputMapping(int32_t xOrigin, int32_t yOrigin, uint32_t cx, uint32_t cy)
1037{
1038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 xInputMappingOrigin = xOrigin;
1041 yInputMappingOrigin = yOrigin;
1042 cxInputMapping = cx;
1043 cyInputMapping = cy;
1044
1045 /* Re-send the seamless rectangles if necessary. */
1046 if (mfSeamlessEnabled)
1047 i_handleSetVisibleRegion(mcRectVisibleRegion, mpRectVisibleRegion);
1048}
1049
1050/**
1051 * Returns the upper left and lower right corners of the virtual framebuffer.
1052 * The lower right is "exclusive" (i.e. first pixel beyond the framebuffer),
1053 * and the origin is (0, 0), not (1, 1) like the GUI returns.
1054 */
1055void Display::i_getFramebufferDimensions(int32_t *px1, int32_t *py1,
1056 int32_t *px2, int32_t *py2)
1057{
1058 int32_t x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1060
1061 AssertPtrReturnVoid(px1);
1062 AssertPtrReturnVoid(py1);
1063 AssertPtrReturnVoid(px2);
1064 AssertPtrReturnVoid(py2);
1065 LogRelFlowFunc(("\n"));
1066
1067 if (!mpDrv)
1068 return;
1069 /* If VBVA is not in use then this flag will not be set and this
1070 * will still work as it should. */
1071 if (!maFramebuffers[0].fDisabled)
1072 {
1073 x1 = (int32_t)maFramebuffers[0].xOrigin;
1074 y1 = (int32_t)maFramebuffers[0].yOrigin;
1075 x2 = (int32_t)maFramebuffers[0].w + (int32_t)maFramebuffers[0].xOrigin;
1076 y2 = (int32_t)maFramebuffers[0].h + (int32_t)maFramebuffers[0].yOrigin;
1077 }
1078 if (cxInputMapping && cyInputMapping)
1079 {
1080 x1 = xInputMappingOrigin;
1081 y1 = yInputMappingOrigin;
1082 x2 = xInputMappingOrigin + cxInputMapping;
1083 y2 = yInputMappingOrigin + cyInputMapping;
1084 }
1085 else
1086 for (unsigned i = 1; i < mcMonitors; ++i)
1087 {
1088 if (!maFramebuffers[i].fDisabled)
1089 {
1090 x1 = RT_MIN(x1, maFramebuffers[i].xOrigin);
1091 y1 = RT_MIN(y1, maFramebuffers[i].yOrigin);
1092 x2 = RT_MAX(x2, maFramebuffers[i].xOrigin + (int32_t)maFramebuffers[i].w);
1093 y2 = RT_MAX(y2, maFramebuffers[i].yOrigin + (int32_t)maFramebuffers[i].h);
1094 }
1095 }
1096 *px1 = x1;
1097 *py1 = y1;
1098 *px2 = x2;
1099 *py2 = y2;
1100}
1101
1102/** Updates the device's view of the host cursor handling capabilities.
1103 * Calls into mpDrv->pUpPort. */
1104void Display::i_UpdateDeviceCursorCapabilities(void)
1105{
1106 bool fRenderCursor = true;
1107 bool fMoveCursor = mcVRDPRefs == 0;
1108#ifdef VBOX_WITH_RECORDING
1109 RecordingContext *pCtx = mParent->i_recordingGetContext();
1110
1111 if ( pCtx
1112 && pCtx->IsStarted()
1113 && pCtx->IsFeatureEnabled(RecordingFeature_Video))
1114 fRenderCursor = fMoveCursor = false;
1115 else
1116#endif /* VBOX_WITH_RECORDING */
1117 {
1118 for (unsigned uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1119 {
1120 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1121 if (!(pFBInfo->u32Caps & FramebufferCapabilities_RenderCursor))
1122 fRenderCursor = false;
1123 if (!(pFBInfo->u32Caps & FramebufferCapabilities_MoveCursor))
1124 fMoveCursor = false;
1125 }
1126 }
1127
1128 if (mpDrv)
1129 mpDrv->pUpPort->pfnReportHostCursorCapabilities(mpDrv->pUpPort, fRenderCursor, fMoveCursor);
1130}
1131
1132HRESULT Display::i_reportHostCursorCapabilities(uint32_t fCapabilitiesAdded, uint32_t fCapabilitiesRemoved)
1133{
1134 /* Do we need this to access mParent? I presume that the safe VM pointer
1135 * ensures that mpDrv will remain valid. */
1136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137 uint32_t fHostCursorCapabilities = (mfHostCursorCapabilities | fCapabilitiesAdded)
1138 & ~fCapabilitiesRemoved;
1139
1140 Console::SafeVMPtr ptrVM(mParent);
1141 if (!ptrVM.isOk())
1142 return ptrVM.rc();
1143 if (mfHostCursorCapabilities == fHostCursorCapabilities)
1144 return S_OK;
1145 CHECK_CONSOLE_DRV(mpDrv);
1146 alock.release(); /* Release before calling up for lock order reasons. */
1147 mfHostCursorCapabilities = fHostCursorCapabilities;
1148 i_UpdateDeviceCursorCapabilities();
1149 return S_OK;
1150}
1151
1152HRESULT Display::i_reportHostCursorPosition(int32_t x, int32_t y, bool fOutOfRange)
1153{
1154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1155 uint32_t xAdj = (uint32_t)RT_MAX(x - xInputMappingOrigin, 0);
1156 uint32_t yAdj = (uint32_t)RT_MAX(y - yInputMappingOrigin, 0);
1157 xAdj = RT_MIN(xAdj, cxInputMapping);
1158 yAdj = RT_MIN(yAdj, cyInputMapping);
1159
1160 Console::SafeVMPtr ptrVM(mParent);
1161 if (!ptrVM.isOk())
1162 return ptrVM.rc();
1163 CHECK_CONSOLE_DRV(mpDrv);
1164 alock.release(); /* Release before calling up for lock order reasons. */
1165 if (fOutOfRange)
1166 mpDrv->pUpPort->pfnReportHostCursorPosition(mpDrv->pUpPort, 0, 0, true);
1167 else
1168 mpDrv->pUpPort->pfnReportHostCursorPosition(mpDrv->pUpPort, xAdj, yAdj, false);
1169 return S_OK;
1170}
1171
1172static bool displayIntersectRect(RTRECT *prectResult,
1173 const RTRECT *prect1,
1174 const RTRECT *prect2)
1175{
1176 /* Initialize result to an empty record. */
1177 memset(prectResult, 0, sizeof(RTRECT));
1178
1179 int xLeftResult = RT_MAX(prect1->xLeft, prect2->xLeft);
1180 int xRightResult = RT_MIN(prect1->xRight, prect2->xRight);
1181
1182 if (xLeftResult < xRightResult)
1183 {
1184 /* There is intersection by X. */
1185
1186 int yTopResult = RT_MAX(prect1->yTop, prect2->yTop);
1187 int yBottomResult = RT_MIN(prect1->yBottom, prect2->yBottom);
1188
1189 if (yTopResult < yBottomResult)
1190 {
1191 /* There is intersection by Y. */
1192
1193 prectResult->xLeft = xLeftResult;
1194 prectResult->yTop = yTopResult;
1195 prectResult->xRight = xRightResult;
1196 prectResult->yBottom = yBottomResult;
1197
1198 return true;
1199 }
1200 }
1201
1202 return false;
1203}
1204
1205int Display::i_saveVisibleRegion(uint32_t cRect, PRTRECT pRect)
1206{
1207 RTRECT *pRectVisibleRegion = NULL;
1208
1209 if (pRect == mpRectVisibleRegion)
1210 return VINF_SUCCESS;
1211 if (cRect != 0)
1212 {
1213 pRectVisibleRegion = (RTRECT *)RTMemAlloc(cRect * sizeof(RTRECT));
1214 if (!pRectVisibleRegion)
1215 {
1216 return VERR_NO_MEMORY;
1217 }
1218 memcpy(pRectVisibleRegion, pRect, cRect * sizeof(RTRECT));
1219 }
1220 if (mpRectVisibleRegion)
1221 RTMemFree(mpRectVisibleRegion);
1222 mcRectVisibleRegion = cRect;
1223 mpRectVisibleRegion = pRectVisibleRegion;
1224 return VINF_SUCCESS;
1225}
1226
1227int Display::i_handleSetVisibleRegion(uint32_t cRect, PRTRECT pRect)
1228{
1229 RTRECT *pVisibleRegion = (RTRECT *)RTMemTmpAlloc( RT_MAX(cRect, 1)
1230 * sizeof(RTRECT));
1231 LogRel2(("%s: cRect=%u\n", __PRETTY_FUNCTION__, cRect));
1232 if (!pVisibleRegion)
1233 {
1234 return VERR_NO_TMP_MEMORY;
1235 }
1236 int rc = i_saveVisibleRegion(cRect, pRect);
1237 if (RT_FAILURE(rc))
1238 {
1239 RTMemTmpFree(pVisibleRegion);
1240 return rc;
1241 }
1242
1243 unsigned uScreenId;
1244 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1245 {
1246 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1247
1248 if ( !pFBInfo->pFramebuffer.isNull()
1249 && RT_BOOL(pFBInfo->u32Caps & FramebufferCapabilities_VisibleRegion))
1250 {
1251 /* Prepare a new array of rectangles which intersect with the framebuffer.
1252 */
1253 RTRECT rectFramebuffer;
1254 rectFramebuffer.xLeft = pFBInfo->xOrigin - xInputMappingOrigin;
1255 rectFramebuffer.yTop = pFBInfo->yOrigin - yInputMappingOrigin;
1256 rectFramebuffer.xRight = rectFramebuffer.xLeft + pFBInfo->w;
1257 rectFramebuffer.yBottom = rectFramebuffer.yTop + pFBInfo->h;
1258
1259 uint32_t cRectVisibleRegion = 0;
1260
1261 uint32_t i;
1262 for (i = 0; i < cRect; i++)
1263 {
1264 if (displayIntersectRect(&pVisibleRegion[cRectVisibleRegion], &pRect[i], &rectFramebuffer))
1265 {
1266 pVisibleRegion[cRectVisibleRegion].xLeft -= rectFramebuffer.xLeft;
1267 pVisibleRegion[cRectVisibleRegion].yTop -= rectFramebuffer.yTop;
1268 pVisibleRegion[cRectVisibleRegion].xRight -= rectFramebuffer.xLeft;
1269 pVisibleRegion[cRectVisibleRegion].yBottom -= rectFramebuffer.yTop;
1270
1271 cRectVisibleRegion++;
1272 }
1273 }
1274 pFBInfo->pFramebuffer->SetVisibleRegion((BYTE *)pVisibleRegion, cRectVisibleRegion);
1275 }
1276 }
1277
1278 RTMemTmpFree(pVisibleRegion);
1279
1280 return VINF_SUCCESS;
1281}
1282
1283int Display::i_handleUpdateMonitorPositions(uint32_t cPositions, PRTPOINT pPosition)
1284{
1285 AssertMsgReturn(pPosition, ("Empty monitor position array\n"), E_INVALIDARG);
1286 for (unsigned i = 0; i < cPositions; ++i)
1287 LogRel2(("Display::i_handleUpdateMonitorPositions: uScreenId=%d xOrigin=%d yOrigin=%dX\n",
1288 i, pPosition[i].x, pPosition[i].y));
1289
1290 if (mpDrv && mpDrv->pUpPort->pfnReportMonitorPositions)
1291 mpDrv->pUpPort->pfnReportMonitorPositions(mpDrv->pUpPort, cPositions, pPosition);
1292 return VINF_SUCCESS;
1293}
1294
1295int Display::i_handleQueryVisibleRegion(uint32_t *pcRects, PRTRECT paRects)
1296{
1297 /// @todo Currently not used by the guest and is not implemented in
1298 /// framebuffers. Remove?
1299 RT_NOREF(pcRects, paRects);
1300 return VERR_NOT_SUPPORTED;
1301}
1302
1303#ifdef VBOX_WITH_HGSMI
1304static void vbvaSetMemoryFlagsHGSMI(unsigned uScreenId,
1305 uint32_t fu32SupportedOrders,
1306 bool fVideoAccelVRDP,
1307 DISPLAYFBINFO *pFBInfo)
1308{
1309 LogRelFlowFunc(("HGSMI[%d]: %p\n", uScreenId, pFBInfo->pVBVAHostFlags));
1310
1311 if (pFBInfo->pVBVAHostFlags)
1312 {
1313 uint32_t fu32HostEvents = VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
1314
1315 if (pFBInfo->fVBVAEnabled)
1316 {
1317 fu32HostEvents |= VBVA_F_MODE_ENABLED;
1318
1319 if (fVideoAccelVRDP)
1320 {
1321 fu32HostEvents |= VBVA_F_MODE_VRDP;
1322 }
1323 }
1324
1325 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32HostEvents, fu32HostEvents);
1326 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32SupportedOrders, fu32SupportedOrders);
1327
1328 LogRelFlowFunc((" fu32HostEvents = 0x%08X, fu32SupportedOrders = 0x%08X\n", fu32HostEvents, fu32SupportedOrders));
1329 }
1330}
1331
1332static void vbvaSetMemoryFlagsAllHGSMI(uint32_t fu32SupportedOrders,
1333 bool fVideoAccelVRDP,
1334 DISPLAYFBINFO *paFBInfos,
1335 unsigned cFBInfos)
1336{
1337 unsigned uScreenId;
1338
1339 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
1340 {
1341 vbvaSetMemoryFlagsHGSMI(uScreenId, fu32SupportedOrders, fVideoAccelVRDP, &paFBInfos[uScreenId]);
1342 }
1343}
1344#endif /* VBOX_WITH_HGSMI */
1345
1346int Display::VideoAccelEnableVMMDev(bool fEnable, VBVAMEMORY *pVbvaMemory)
1347{
1348 LogFlowFunc(("%d %p\n", fEnable, pVbvaMemory));
1349 int rc = videoAccelEnterVMMDev(&mVideoAccelLegacy);
1350 if (RT_SUCCESS(rc))
1351 {
1352 rc = i_VideoAccelEnable(fEnable, pVbvaMemory, mpDrv->pUpPort);
1353 videoAccelLeaveVMMDev(&mVideoAccelLegacy);
1354 }
1355 LogFlowFunc(("leave %Rrc\n", rc));
1356 return rc;
1357}
1358
1359int Display::VideoAccelEnableVGA(bool fEnable, VBVAMEMORY *pVbvaMemory)
1360{
1361 LogFlowFunc(("%d %p\n", fEnable, pVbvaMemory));
1362 int rc = videoAccelEnterVGA(&mVideoAccelLegacy);
1363 if (RT_SUCCESS(rc))
1364 {
1365 rc = i_VideoAccelEnable(fEnable, pVbvaMemory, mpDrv->pUpPort);
1366 videoAccelLeaveVGA(&mVideoAccelLegacy);
1367 }
1368 LogFlowFunc(("leave %Rrc\n", rc));
1369 return rc;
1370}
1371
1372void Display::VideoAccelFlushVMMDev(void)
1373{
1374 LogFlowFunc(("enter\n"));
1375 int rc = videoAccelEnterVMMDev(&mVideoAccelLegacy);
1376 if (RT_SUCCESS(rc))
1377 {
1378 i_VideoAccelFlush(mpDrv->pUpPort);
1379 videoAccelLeaveVMMDev(&mVideoAccelLegacy);
1380 }
1381 LogFlowFunc(("leave\n"));
1382}
1383
1384/* Called always by one VRDP server thread. Can be thread-unsafe.
1385 */
1386void Display::i_VRDPConnectionEvent(bool fConnect)
1387{
1388 LogRelFlowFunc(("fConnect = %d\n", fConnect));
1389
1390 int c = fConnect?
1391 ASMAtomicIncS32(&mcVRDPRefs):
1392 ASMAtomicDecS32(&mcVRDPRefs);
1393
1394 i_VideoAccelVRDP(fConnect, c);
1395 i_UpdateDeviceCursorCapabilities();
1396}
1397
1398
1399void Display::i_VideoAccelVRDP(bool fEnable, int c)
1400{
1401 VIDEOACCEL *pVideoAccel = &mVideoAccelLegacy;
1402
1403 Assert (c >= 0);
1404 RT_NOREF(fEnable);
1405
1406 /* This can run concurrently with Display videoaccel state change. */
1407 RTCritSectEnter(&mVideoAccelLock);
1408
1409 if (c == 0)
1410 {
1411 /* The last client has disconnected, and the accel can be
1412 * disabled.
1413 */
1414 Assert(fEnable == false);
1415
1416 mfVideoAccelVRDP = false;
1417 mfu32SupportedOrders = 0;
1418
1419 i_vbvaSetMemoryFlags(pVideoAccel->pVbvaMemory, pVideoAccel->fVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders,
1420 maFramebuffers, mcMonitors);
1421#ifdef VBOX_WITH_HGSMI
1422 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1423 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1424#endif /* VBOX_WITH_HGSMI */
1425
1426 LogRel(("VBVA: VRDP acceleration has been disabled.\n"));
1427 }
1428 else if ( c == 1
1429 && !mfVideoAccelVRDP)
1430 {
1431 /* The first client has connected. Enable the accel.
1432 */
1433 Assert(fEnable == true);
1434
1435 mfVideoAccelVRDP = true;
1436 /* Supporting all orders. */
1437 mfu32SupportedOrders = UINT32_MAX;
1438
1439 i_vbvaSetMemoryFlags(pVideoAccel->pVbvaMemory, pVideoAccel->fVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders,
1440 maFramebuffers, mcMonitors);
1441#ifdef VBOX_WITH_HGSMI
1442 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1443 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1444#endif /* VBOX_WITH_HGSMI */
1445
1446 LogRel(("VBVA: VRDP acceleration has been requested.\n"));
1447 }
1448 else
1449 {
1450 /* A client is connected or disconnected but there is no change in the
1451 * accel state. It remains enabled.
1452 */
1453 Assert(mfVideoAccelVRDP == true);
1454 }
1455
1456 RTCritSectLeave(&mVideoAccelLock);
1457}
1458
1459void Display::i_notifyPowerDown(void)
1460{
1461 LogRelFlowFunc(("\n"));
1462
1463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1464
1465 /* Source bitmaps are not available anymore. */
1466 mfSourceBitmapEnabled = false;
1467
1468 alock.release();
1469
1470 /* Resize all displays to tell framebuffers to forget current source bitmap. */
1471 unsigned uScreenId = mcMonitors;
1472 while (uScreenId > 0)
1473 {
1474 --uScreenId;
1475
1476 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1477 if (!pFBInfo->fDisabled)
1478 {
1479 i_handleDisplayResize(uScreenId, 32,
1480 pFBInfo->pu8FramebufferVRAM,
1481 pFBInfo->u32LineSize,
1482 pFBInfo->w,
1483 pFBInfo->h,
1484 pFBInfo->flags,
1485 pFBInfo->xOrigin,
1486 pFBInfo->yOrigin,
1487 false);
1488 }
1489 }
1490}
1491
1492// Wrapped IDisplay methods
1493/////////////////////////////////////////////////////////////////////////////
1494HRESULT Display::getScreenResolution(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ULONG *aBitsPerPixel,
1495 LONG *aXOrigin, LONG *aYOrigin, GuestMonitorStatus_T *aGuestMonitorStatus)
1496{
1497 LogRelFlowFunc(("aScreenId=%RU32\n", aScreenId));
1498
1499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1500
1501 if (aScreenId >= mcMonitors)
1502 return E_INVALIDARG;
1503
1504 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1505
1506 GuestMonitorStatus_T guestMonitorStatus = GuestMonitorStatus_Enabled;
1507
1508 if (pFBInfo->flags & VBVA_SCREEN_F_DISABLED)
1509 guestMonitorStatus = GuestMonitorStatus_Disabled;
1510 else if (pFBInfo->flags & (VBVA_SCREEN_F_BLANK | VBVA_SCREEN_F_BLANK2))
1511 guestMonitorStatus = GuestMonitorStatus_Blank;
1512
1513 if (aWidth)
1514 *aWidth = pFBInfo->w;
1515 if (aHeight)
1516 *aHeight = pFBInfo->h;
1517 if (aBitsPerPixel)
1518 *aBitsPerPixel = pFBInfo->u16BitsPerPixel;
1519 if (aXOrigin)
1520 *aXOrigin = pFBInfo->xOrigin;
1521 if (aYOrigin)
1522 *aYOrigin = pFBInfo->yOrigin;
1523 if (aGuestMonitorStatus)
1524 *aGuestMonitorStatus = guestMonitorStatus;
1525
1526 return S_OK;
1527}
1528
1529
1530HRESULT Display::attachFramebuffer(ULONG aScreenId, const ComPtr<IFramebuffer> &aFramebuffer, com::Guid &aId)
1531{
1532 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
1533
1534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 if (aScreenId >= mcMonitors)
1537 return setError(E_INVALIDARG, tr("AttachFramebuffer: Invalid screen %d (total %d)"),
1538 aScreenId, mcMonitors);
1539
1540 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1541 if (!pFBInfo->pFramebuffer.isNull())
1542 return setError(E_FAIL, tr("AttachFramebuffer: Framebuffer already attached to %d"),
1543 aScreenId);
1544
1545 pFBInfo->pFramebuffer = aFramebuffer;
1546 pFBInfo->framebufferId.create();
1547 aId = pFBInfo->framebufferId;
1548
1549 SafeArray<FramebufferCapabilities_T> caps;
1550 pFBInfo->pFramebuffer->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(caps));
1551 pFBInfo->u32Caps = 0;
1552 size_t i;
1553 for (i = 0; i < caps.size(); ++i)
1554 pFBInfo->u32Caps |= caps[i];
1555
1556 alock.release();
1557
1558 /* The driver might not have been constructed yet */
1559 if (mpDrv)
1560 {
1561 /* Inform the framebuffer about the actual screen size. */
1562 HRESULT hr = aFramebuffer->NotifyChange(aScreenId, 0, 0, pFBInfo->w, pFBInfo->h); /** @todo origin */
1563 LogFunc(("NotifyChange hr %08X\n", hr)); NOREF(hr);
1564
1565 /* Re-send the seamless rectangles if necessary. */
1566 if (mfSeamlessEnabled)
1567 i_handleSetVisibleRegion(mcRectVisibleRegion, mpRectVisibleRegion);
1568 }
1569
1570 Console::SafeVMPtrQuiet ptrVM(mParent);
1571 if (ptrVM.isOk())
1572 {
1573 VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
1574 3, this, aScreenId, false);
1575 }
1576
1577 LogRelFlowFunc(("Attached to %d %RTuuid\n", aScreenId, aId.raw()));
1578 return S_OK;
1579}
1580
1581HRESULT Display::detachFramebuffer(ULONG aScreenId, const com::Guid &aId)
1582{
1583 LogRelFlowFunc(("aScreenId = %d %RTuuid\n", aScreenId, aId.raw()));
1584
1585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1586
1587 if (aScreenId >= mcMonitors)
1588 return setError(E_INVALIDARG, tr("DetachFramebuffer: Invalid screen %d (total %d)"),
1589 aScreenId, mcMonitors);
1590
1591 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1592
1593 if (pFBInfo->framebufferId != aId)
1594 {
1595 LogRelFlowFunc(("Invalid framebuffer aScreenId = %d, attached %p\n", aScreenId, pFBInfo->framebufferId.raw()));
1596 return setError(E_FAIL, tr("DetachFramebuffer: Invalid framebuffer object"));
1597 }
1598
1599 pFBInfo->pFramebuffer.setNull();
1600 pFBInfo->framebufferId.clear();
1601
1602 alock.release();
1603 return S_OK;
1604}
1605
1606HRESULT Display::queryFramebuffer(ULONG aScreenId, ComPtr<IFramebuffer> &aFramebuffer)
1607{
1608 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
1609
1610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1611
1612 if (aScreenId >= mcMonitors)
1613 return setError(E_INVALIDARG, tr("QueryFramebuffer: Invalid screen %d (total %d)"),
1614 aScreenId, mcMonitors);
1615
1616 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1617
1618 pFBInfo->pFramebuffer.queryInterfaceTo(aFramebuffer.asOutParam());
1619
1620 return S_OK;
1621}
1622
1623HRESULT Display::setVideoModeHint(ULONG aDisplay, BOOL aEnabled,
1624 BOOL aChangeOrigin, LONG aOriginX, LONG aOriginY,
1625 ULONG aWidth, ULONG aHeight, ULONG aBitsPerPixel,
1626 BOOL aNotify)
1627{
1628 if (aWidth == 0 || aHeight == 0 || aBitsPerPixel == 0)
1629 {
1630 /* Some of parameters must not change. Query current mode. */
1631 ULONG ulWidth = 0;
1632 ULONG ulHeight = 0;
1633 ULONG ulBitsPerPixel = 0;
1634 HRESULT hr = getScreenResolution(aDisplay, &ulWidth, &ulHeight, &ulBitsPerPixel, NULL, NULL, NULL);
1635 if (FAILED(hr))
1636 return hr;
1637
1638 /* Assign current values to not changing parameters. */
1639 if (aWidth == 0)
1640 aWidth = ulWidth;
1641 if (aHeight == 0)
1642 aHeight = ulHeight;
1643 if (aBitsPerPixel == 0)
1644 aBitsPerPixel = ulBitsPerPixel;
1645 }
1646
1647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1648
1649 if (aDisplay >= mcMonitors)
1650 return E_INVALIDARG;
1651
1652 VMMDevDisplayDef d;
1653 d.idDisplay = aDisplay;
1654 d.xOrigin = aOriginX;
1655 d.yOrigin = aOriginY;
1656 d.cx = aWidth;
1657 d.cy = aHeight;
1658 d.cBitsPerPixel = aBitsPerPixel;
1659 d.fDisplayFlags = VMMDEV_DISPLAY_CX | VMMDEV_DISPLAY_CY | VMMDEV_DISPLAY_BPP;
1660 if (!aEnabled)
1661 d.fDisplayFlags |= VMMDEV_DISPLAY_DISABLED;
1662 if (aChangeOrigin)
1663 d.fDisplayFlags |= VMMDEV_DISPLAY_ORIGIN;
1664 if (aDisplay == 0)
1665 d.fDisplayFlags |= VMMDEV_DISPLAY_PRIMARY;
1666
1667 /* Remember the monitor information. */
1668 maFramebuffers[aDisplay].monitorDesc = d;
1669
1670 CHECK_CONSOLE_DRV(mpDrv);
1671
1672 /*
1673 * It is up to the guest to decide whether the hint is
1674 * valid. Therefore don't do any VRAM sanity checks here.
1675 */
1676
1677 /* Have to release the lock because the pfnRequestDisplayChange
1678 * will call EMT. */
1679 alock.release();
1680
1681 /* We always send the hint to the graphics card in case the guest enables
1682 * support later. For now we notify exactly when support is enabled. */
1683 mpDrv->pUpPort->pfnSendModeHint(mpDrv->pUpPort, aWidth, aHeight,
1684 aBitsPerPixel, aDisplay,
1685 aChangeOrigin ? aOriginX : ~0,
1686 aChangeOrigin ? aOriginY : ~0,
1687 RT_BOOL(aEnabled),
1688 ( mfGuestVBVACapabilities
1689 & VBVACAPS_VIDEO_MODE_HINTS)
1690 && aNotify);
1691 if ( mfGuestVBVACapabilities & VBVACAPS_VIDEO_MODE_HINTS
1692 && !(mfGuestVBVACapabilities & VBVACAPS_IRQ)
1693 && aNotify)
1694 {
1695 mParent->i_sendACPIMonitorHotPlugEvent();
1696 }
1697
1698 /* We currently never suppress the VMMDev hint if the guest has requested
1699 * it. Specifically the video graphics driver may not be responsible for
1700 * screen positioning in the guest virtual desktop, and the component
1701 * responsible may want to get the hint from VMMDev. */
1702 VMMDev *pVMMDev = mParent->i_getVMMDev();
1703 if (pVMMDev)
1704 {
1705 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
1706 if (pVMMDevPort)
1707 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, 1, &d, false, RT_BOOL(aNotify));
1708 }
1709 /* Notify listeners. */
1710 ::FireGuestMonitorInfoChangedEvent(mParent->i_getEventSource(), aDisplay);
1711 return S_OK;
1712}
1713
1714HRESULT Display::getVideoModeHint(ULONG cDisplay, BOOL *pfEnabled,
1715 BOOL *pfChangeOrigin, LONG *pxOrigin, LONG *pyOrigin,
1716 ULONG *pcx, ULONG *pcy, ULONG *pcBitsPerPixel)
1717{
1718 if (cDisplay >= mcMonitors)
1719 return E_INVALIDARG;
1720
1721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1722 if (pfEnabled)
1723 *pfEnabled = !( maFramebuffers[cDisplay].monitorDesc.fDisplayFlags
1724 & VMMDEV_DISPLAY_DISABLED);
1725 if (pfChangeOrigin)
1726 *pfChangeOrigin = RT_BOOL( maFramebuffers[cDisplay].monitorDesc.fDisplayFlags
1727 & VMMDEV_DISPLAY_ORIGIN);
1728 if (pxOrigin)
1729 *pxOrigin = maFramebuffers[cDisplay].monitorDesc.xOrigin;
1730 if (pyOrigin)
1731 *pyOrigin = maFramebuffers[cDisplay].monitorDesc.yOrigin;
1732 if (pcx)
1733 *pcx = maFramebuffers[cDisplay].monitorDesc.cx;
1734 if (pcy)
1735 *pcy = maFramebuffers[cDisplay].monitorDesc.cy;
1736 if (pcBitsPerPixel)
1737 *pcBitsPerPixel = maFramebuffers[cDisplay].monitorDesc.cBitsPerPixel;
1738 return S_OK;
1739}
1740
1741HRESULT Display::setSeamlessMode(BOOL enabled)
1742{
1743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1744
1745 /* Have to release the lock because the pfnRequestSeamlessChange will call EMT. */
1746 alock.release();
1747
1748 VMMDev *pVMMDev = mParent->i_getVMMDev();
1749 if (pVMMDev)
1750 {
1751 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
1752 if (pVMMDevPort)
1753 pVMMDevPort->pfnRequestSeamlessChange(pVMMDevPort, !!enabled);
1754 }
1755 mfSeamlessEnabled = RT_BOOL(enabled);
1756 return S_OK;
1757}
1758
1759/*static*/ DECLCALLBACK(int)
1760Display::i_displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_t **ppbData, size_t *pcbData,
1761 uint32_t *pcx, uint32_t *pcy, bool *pfMemFree)
1762{
1763 int rc;
1764 if ( aScreenId == VBOX_VIDEO_PRIMARY_SCREEN
1765 && pDisplay->maFramebuffers[aScreenId].fVBVAEnabled == false) /* A non-VBVA mode. */
1766 {
1767 if (pDisplay->mpDrv)
1768 {
1769 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppbData, pcbData, pcx, pcy);
1770 *pfMemFree = false;
1771 }
1772 else
1773 {
1774 /* No image. */
1775 *ppbData = NULL;
1776 *pcbData = 0;
1777 *pcx = 0;
1778 *pcy = 0;
1779 *pfMemFree = true;
1780 rc = VINF_SUCCESS;
1781 }
1782 }
1783 else if (aScreenId < pDisplay->mcMonitors)
1784 {
1785 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
1786
1787 uint32_t width = pFBInfo->w;
1788 uint32_t height = pFBInfo->h;
1789
1790 /* Allocate 32 bit per pixel bitmap. */
1791 size_t cbRequired = width * 4 * height;
1792
1793 if (cbRequired)
1794 {
1795 uint8_t *pbDst = (uint8_t *)RTMemAlloc(cbRequired);
1796 if (pbDst != NULL)
1797 {
1798 if (pFBInfo->flags & VBVA_SCREEN_F_ACTIVE)
1799 {
1800 /* Copy guest VRAM to the allocated 32bpp buffer. */
1801 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
1802 int32_t xSrc = 0;
1803 int32_t ySrc = 0;
1804 uint32_t u32SrcWidth = width;
1805 uint32_t u32SrcHeight = height;
1806 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
1807 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
1808
1809 int32_t xDst = 0;
1810 int32_t yDst = 0;
1811 uint32_t u32DstWidth = u32SrcWidth;
1812 uint32_t u32DstHeight = u32SrcHeight;
1813 uint32_t u32DstLineSize = u32DstWidth * 4;
1814 uint32_t u32DstBitsPerPixel = 32;
1815
1816 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
1817 width, height,
1818 pu8Src,
1819 xSrc, ySrc,
1820 u32SrcWidth, u32SrcHeight,
1821 u32SrcLineSize, u32SrcBitsPerPixel,
1822 pbDst,
1823 xDst, yDst,
1824 u32DstWidth, u32DstHeight,
1825 u32DstLineSize, u32DstBitsPerPixel);
1826 }
1827 else
1828 {
1829 memset(pbDst, 0, cbRequired);
1830 rc = VINF_SUCCESS;
1831 }
1832 if (RT_SUCCESS(rc))
1833 {
1834 *ppbData = pbDst;
1835 *pcbData = cbRequired;
1836 *pcx = width;
1837 *pcy = height;
1838 *pfMemFree = true;
1839 }
1840 else
1841 {
1842 RTMemFree(pbDst);
1843
1844 /* CopyRect can fail if VBVA was paused in VGA device, retry using the generic method. */
1845 if ( rc == VERR_INVALID_STATE
1846 && aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
1847 {
1848 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppbData, pcbData, pcx, pcy);
1849 *pfMemFree = false;
1850 }
1851 }
1852 }
1853 else
1854 rc = VERR_NO_MEMORY;
1855 }
1856 else
1857 {
1858 /* No image. */
1859 *ppbData = NULL;
1860 *pcbData = 0;
1861 *pcx = 0;
1862 *pcy = 0;
1863 *pfMemFree = true;
1864 rc = VINF_SUCCESS;
1865 }
1866 }
1867 else
1868 rc = VERR_INVALID_PARAMETER;
1869 return rc;
1870}
1871
1872static int i_displayTakeScreenshot(PUVM pUVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, ULONG aScreenId,
1873 BYTE *address, ULONG width, ULONG height)
1874{
1875 uint8_t *pbData = NULL;
1876 size_t cbData = 0;
1877 uint32_t cx = 0;
1878 uint32_t cy = 0;
1879 bool fFreeMem = false;
1880 int vrc = VINF_SUCCESS;
1881
1882 int cRetries = 5;
1883 while (cRetries-- > 0)
1884 {
1885 /* Note! Not sure if the priority call is such a good idea here, but
1886 it would be nice to have an accurate screenshot for the bug
1887 report if the VM deadlocks. */
1888 vrc = VMR3ReqPriorityCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)Display::i_displayTakeScreenshotEMT, 7,
1889 pDisplay, aScreenId, &pbData, &cbData, &cx, &cy, &fFreeMem);
1890 if (vrc != VERR_TRY_AGAIN)
1891 {
1892 break;
1893 }
1894
1895 RTThreadSleep(10);
1896 }
1897
1898 if (RT_SUCCESS(vrc) && pbData)
1899 {
1900 if (cx == width && cy == height)
1901 {
1902 /* No scaling required. */
1903 memcpy(address, pbData, cbData);
1904 }
1905 else
1906 {
1907 /* Scale. */
1908 LogRelFlowFunc(("SCALE: %dx%d -> %dx%d\n", cx, cy, width, height));
1909
1910 uint8_t *dst = address;
1911 uint8_t *src = pbData;
1912 int dstW = width;
1913 int dstH = height;
1914 int srcW = cx;
1915 int srcH = cy;
1916 int iDeltaLine = cx * 4;
1917
1918 BitmapScale32(dst,
1919 dstW, dstH,
1920 src,
1921 iDeltaLine,
1922 srcW, srcH);
1923 }
1924
1925 if (fFreeMem)
1926 RTMemFree(pbData);
1927 else
1928 {
1929 /* This can be called from any thread. */
1930 pDrv->pUpPort->pfnFreeScreenshot(pDrv->pUpPort, pbData);
1931 }
1932 }
1933
1934 return vrc;
1935}
1936
1937HRESULT Display::takeScreenShotWorker(ULONG aScreenId,
1938 BYTE *aAddress,
1939 ULONG aWidth,
1940 ULONG aHeight,
1941 BitmapFormat_T aBitmapFormat,
1942 ULONG *pcbOut)
1943{
1944 HRESULT rc = S_OK;
1945
1946 /* Do not allow too small and too large screenshots. This also filters out negative
1947 * values passed as either 'aWidth' or 'aHeight'.
1948 */
1949 CheckComArgExpr(aWidth, aWidth != 0 && aWidth <= 32767);
1950 CheckComArgExpr(aHeight, aHeight != 0 && aHeight <= 32767);
1951
1952 if ( aBitmapFormat != BitmapFormat_BGR0
1953 && aBitmapFormat != BitmapFormat_BGRA
1954 && aBitmapFormat != BitmapFormat_RGBA
1955 && aBitmapFormat != BitmapFormat_PNG)
1956 {
1957 return setError(E_NOTIMPL,
1958 tr("Unsupported screenshot format 0x%08X"), aBitmapFormat);
1959 }
1960
1961 Console::SafeVMPtr ptrVM(mParent);
1962 if (!ptrVM.isOk())
1963 return ptrVM.rc();
1964
1965 int vrc = i_displayTakeScreenshot(ptrVM.rawUVM(), this, mpDrv, aScreenId, aAddress, aWidth, aHeight);
1966
1967 if (RT_SUCCESS(vrc))
1968 {
1969 const size_t cbData = aWidth * 4 * aHeight;
1970
1971 /* Most of uncompressed formats. */
1972 *pcbOut = (ULONG)cbData;
1973
1974 if (aBitmapFormat == BitmapFormat_BGR0)
1975 {
1976 /* Do nothing. */
1977 }
1978 else if (aBitmapFormat == BitmapFormat_BGRA)
1979 {
1980 uint32_t *pu32 = (uint32_t *)aAddress;
1981 size_t cPixels = aWidth * aHeight;
1982 while (cPixels--)
1983 {
1984 *pu32++ |= UINT32_C(0xFF000000);
1985 }
1986 }
1987 else if (aBitmapFormat == BitmapFormat_RGBA)
1988 {
1989 uint8_t *pu8 = aAddress;
1990 size_t cPixels = aWidth * aHeight;
1991 while (cPixels--)
1992 {
1993 uint8_t u8 = pu8[0];
1994 pu8[0] = pu8[2];
1995 pu8[2] = u8;
1996 pu8[3] = 0xFF;
1997
1998 pu8 += 4;
1999 }
2000 }
2001 else if (aBitmapFormat == BitmapFormat_PNG)
2002 {
2003 uint8_t *pu8PNG = NULL;
2004 uint32_t cbPNG = 0;
2005 uint32_t cxPNG = 0;
2006 uint32_t cyPNG = 0;
2007
2008 vrc = DisplayMakePNG(aAddress, aWidth, aHeight, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
2009 if (RT_SUCCESS(vrc))
2010 {
2011 if (cbPNG <= cbData)
2012 {
2013 memcpy(aAddress, pu8PNG, cbPNG);
2014 *pcbOut = cbPNG;
2015 }
2016 else
2017 {
2018 rc = setError(E_FAIL,
2019 tr("PNG is larger than 32bpp bitmap"));
2020 }
2021 }
2022 else
2023 rc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("Could not convert screenshot to PNG (%Rrc)"), vrc);
2024 RTMemFree(pu8PNG);
2025 }
2026 }
2027 else if (vrc == VERR_TRY_AGAIN)
2028 rc = setErrorBoth(E_UNEXPECTED, vrc, tr("Screenshot is not available at this time"));
2029 else if (RT_FAILURE(vrc))
2030 rc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("Could not take a screenshot (%Rrc)"), vrc);
2031
2032 return rc;
2033}
2034
2035HRESULT Display::takeScreenShot(ULONG aScreenId,
2036 BYTE *aAddress,
2037 ULONG aWidth,
2038 ULONG aHeight,
2039 BitmapFormat_T aBitmapFormat)
2040{
2041 HRESULT rc = S_OK;
2042
2043 LogRelFlowFunc(("[%d] address=%p, width=%d, height=%d, format 0x%08X\n",
2044 aScreenId, aAddress, aWidth, aHeight, aBitmapFormat));
2045
2046 ULONG cbOut = 0;
2047 rc = takeScreenShotWorker(aScreenId, aAddress, aWidth, aHeight, aBitmapFormat, &cbOut);
2048 NOREF(cbOut);
2049
2050 LogRelFlowFunc(("%Rhrc\n", rc));
2051 return rc;
2052}
2053
2054HRESULT Display::takeScreenShotToArray(ULONG aScreenId,
2055 ULONG aWidth,
2056 ULONG aHeight,
2057 BitmapFormat_T aBitmapFormat,
2058 std::vector<BYTE> &aScreenData)
2059{
2060 HRESULT rc = S_OK;
2061
2062 LogRelFlowFunc(("[%d] width=%d, height=%d, format 0x%08X\n",
2063 aScreenId, aWidth, aHeight, aBitmapFormat));
2064
2065 /* Do not allow too small and too large screenshots. This also filters out negative
2066 * values passed as either 'aWidth' or 'aHeight'.
2067 */
2068 CheckComArgExpr(aWidth, aWidth != 0 && aWidth <= 32767);
2069 CheckComArgExpr(aHeight, aHeight != 0 && aHeight <= 32767);
2070
2071 const size_t cbData = aWidth * 4 * aHeight;
2072 aScreenData.resize(cbData);
2073
2074 ULONG cbOut = 0;
2075 rc = takeScreenShotWorker(aScreenId, &aScreenData.front(), aWidth, aHeight, aBitmapFormat, &cbOut);
2076 if (FAILED(rc))
2077 cbOut = 0;
2078
2079 aScreenData.resize(cbOut);
2080
2081 LogRelFlowFunc(("%Rhrc\n", rc));
2082 return rc;
2083}
2084
2085#ifdef VBOX_WITH_RECORDING
2086/**
2087 * Invalidates the recording configuration.
2088 *
2089 * @returns IPRT status code.
2090 */
2091int Display::i_recordingInvalidate(void)
2092{
2093 RecordingContext *pCtx = mParent->i_recordingGetContext();
2094 if (!pCtx || !pCtx->IsStarted())
2095 return VINF_SUCCESS;
2096
2097 /*
2098 * Invalidate screens.
2099 */
2100 for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)
2101 {
2102 RecordingStream *pRecordingStream = pCtx->GetStream(uScreen);
2103
2104 const bool fStreamEnabled = pRecordingStream->IsReady();
2105 bool fChanged = maRecordingEnabled[uScreen] != fStreamEnabled;
2106
2107 maRecordingEnabled[uScreen] = fStreamEnabled;
2108
2109 if (fChanged && uScreen < mcMonitors)
2110 i_recordingScreenChanged(uScreen);
2111 }
2112
2113 return VINF_SUCCESS;
2114}
2115
2116void Display::i_recordingScreenChanged(unsigned uScreenId)
2117{
2118 RecordingContext *pCtx = mParent->i_recordingGetContext();
2119
2120 i_UpdateDeviceCursorCapabilities();
2121 if ( RT_LIKELY(!maRecordingEnabled[uScreenId])
2122 || !pCtx || !pCtx->IsStarted())
2123 {
2124 /* Skip recording this screen. */
2125 return;
2126 }
2127
2128 /* Get a new source bitmap which will be used by video recording code. */
2129 ComPtr<IDisplaySourceBitmap> pSourceBitmap;
2130 QuerySourceBitmap(uScreenId, pSourceBitmap.asOutParam());
2131
2132 int rc2 = RTCritSectEnter(&mVideoRecLock);
2133 if (RT_SUCCESS(rc2))
2134 {
2135 maFramebuffers[uScreenId].Recording.pSourceBitmap = pSourceBitmap;
2136
2137 rc2 = RTCritSectLeave(&mVideoRecLock);
2138 AssertRC(rc2);
2139 }
2140}
2141#endif /* VBOX_WITH_RECORDING */
2142
2143/*static*/ DECLCALLBACK(int)
2144Display::i_drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address, ULONG x, ULONG y, ULONG width, ULONG height)
2145{
2146 int rc = VINF_SUCCESS;
2147
2148 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2149
2150 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2151 {
2152 rc = pDisplay->mpDrv->pUpPort->pfnDisplayBlt(pDisplay->mpDrv->pUpPort, address, x, y, width, height);
2153 }
2154 else if (aScreenId < pDisplay->mcMonitors)
2155 {
2156 /* Copy the bitmap to the guest VRAM. */
2157 const uint8_t *pu8Src = address;
2158 int32_t xSrc = 0;
2159 int32_t ySrc = 0;
2160 uint32_t u32SrcWidth = width;
2161 uint32_t u32SrcHeight = height;
2162 uint32_t u32SrcLineSize = width * 4;
2163 uint32_t u32SrcBitsPerPixel = 32;
2164
2165 uint8_t *pu8Dst = pFBInfo->pu8FramebufferVRAM;
2166 int32_t xDst = x;
2167 int32_t yDst = y;
2168 uint32_t u32DstWidth = pFBInfo->w;
2169 uint32_t u32DstHeight = pFBInfo->h;
2170 uint32_t u32DstLineSize = pFBInfo->u32LineSize;
2171 uint32_t u32DstBitsPerPixel = pFBInfo->u16BitsPerPixel;
2172
2173 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2174 width, height,
2175 pu8Src,
2176 xSrc, ySrc,
2177 u32SrcWidth, u32SrcHeight,
2178 u32SrcLineSize, u32SrcBitsPerPixel,
2179 pu8Dst,
2180 xDst, yDst,
2181 u32DstWidth, u32DstHeight,
2182 u32DstLineSize, u32DstBitsPerPixel);
2183 if (RT_SUCCESS(rc))
2184 {
2185 if (!pFBInfo->pSourceBitmap.isNull())
2186 {
2187 /* Update the changed screen area. When source bitmap uses VRAM directly, just notify
2188 * frontend to update. And for default format, render the guest VRAM to the source bitmap.
2189 */
2190 if ( pFBInfo->fDefaultFormat
2191 && !pFBInfo->fDisabled)
2192 {
2193 BYTE *pAddress = NULL;
2194 ULONG ulWidth = 0;
2195 ULONG ulHeight = 0;
2196 ULONG ulBitsPerPixel = 0;
2197 ULONG ulBytesPerLine = 0;
2198 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2199
2200 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
2201 &ulWidth,
2202 &ulHeight,
2203 &ulBitsPerPixel,
2204 &ulBytesPerLine,
2205 &bitmapFormat);
2206 if (SUCCEEDED(hrc))
2207 {
2208 pu8Src = pFBInfo->pu8FramebufferVRAM;
2209 xSrc = x;
2210 ySrc = y;
2211 u32SrcWidth = pFBInfo->w;
2212 u32SrcHeight = pFBInfo->h;
2213 u32SrcLineSize = pFBInfo->u32LineSize;
2214 u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2215
2216 /* Default format is 32 bpp. */
2217 pu8Dst = pAddress;
2218 xDst = xSrc;
2219 yDst = ySrc;
2220 u32DstWidth = u32SrcWidth;
2221 u32DstHeight = u32SrcHeight;
2222 u32DstLineSize = u32DstWidth * 4;
2223 u32DstBitsPerPixel = 32;
2224
2225 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2226 width, height,
2227 pu8Src,
2228 xSrc, ySrc,
2229 u32SrcWidth, u32SrcHeight,
2230 u32SrcLineSize, u32SrcBitsPerPixel,
2231 pu8Dst,
2232 xDst, yDst,
2233 u32DstWidth, u32DstHeight,
2234 u32DstLineSize, u32DstBitsPerPixel);
2235 }
2236 }
2237 }
2238
2239 pDisplay->i_handleDisplayUpdate(aScreenId, x, y, width, height);
2240 }
2241 }
2242 else
2243 {
2244 rc = VERR_INVALID_PARAMETER;
2245 }
2246
2247 if (RT_SUCCESS(rc))
2248 pDisplay->mParent->i_consoleVRDPServer()->SendUpdateBitmap(aScreenId, x, y, width, height);
2249
2250 return rc;
2251}
2252
2253HRESULT Display::drawToScreen(ULONG aScreenId, BYTE *aAddress, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
2254{
2255 /// @todo (r=dmik) this function may take too long to complete if the VM
2256 // is doing something like saving state right now. Which, in case if it
2257 // is called on the GUI thread, will make it unresponsive. We should
2258 // check the machine state here (by enclosing the check and VMRequCall
2259 // within the Console lock to make it atomic).
2260
2261 LogRelFlowFunc(("aAddress=%p, x=%d, y=%d, width=%d, height=%d\n",
2262 (void *)aAddress, aX, aY, aWidth, aHeight));
2263
2264 CheckComArgExpr(aWidth, aWidth != 0);
2265 CheckComArgExpr(aHeight, aHeight != 0);
2266
2267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2268
2269 CHECK_CONSOLE_DRV(mpDrv);
2270
2271 Console::SafeVMPtr ptrVM(mParent);
2272 if (!ptrVM.isOk())
2273 return ptrVM.rc();
2274
2275 /* Release lock because the call scheduled on EMT may also try to take it. */
2276 alock.release();
2277
2278 /*
2279 * Again we're lazy and make the graphics device do all the
2280 * dirty conversion work.
2281 */
2282 int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_drawToScreenEMT, 7,
2283 this, aScreenId, aAddress, aX, aY, aWidth, aHeight);
2284
2285 /*
2286 * If the function returns not supported, we'll have to do all the
2287 * work ourselves using the framebuffer.
2288 */
2289 HRESULT rc = S_OK;
2290 if (vrc == VERR_NOT_SUPPORTED || vrc == VERR_NOT_IMPLEMENTED)
2291 {
2292 /** @todo implement generic fallback for screen blitting. */
2293 rc = E_NOTIMPL;
2294 }
2295 else if (RT_FAILURE(vrc))
2296 rc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("Could not draw to the screen (%Rrc)"), vrc);
2297/// @todo
2298// else
2299// {
2300// /* All ok. Redraw the screen. */
2301// handleDisplayUpdate(x, y, width, height);
2302// }
2303
2304 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2305 return rc;
2306}
2307
2308/*static*/ DECLCALLBACK(int) Display::i_InvalidateAndUpdateEMT(Display *pDisplay, unsigned uId, bool fUpdateAll)
2309{
2310 LogRelFlowFunc(("uId=%d, fUpdateAll %d\n", uId, fUpdateAll));
2311
2312 unsigned uScreenId;
2313 for (uScreenId = (fUpdateAll ? 0 : uId); uScreenId < pDisplay->mcMonitors; uScreenId++)
2314 {
2315 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2316
2317 if ( !pFBInfo->fVBVAEnabled
2318 && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2319 {
2320 pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort, /* fFailOnResize = */ true);
2321 }
2322 else
2323 {
2324 if (!pFBInfo->fDisabled)
2325 {
2326 /* Render complete VRAM screen to the framebuffer.
2327 * When framebuffer uses VRAM directly, just notify it to update.
2328 */
2329 if (pFBInfo->fDefaultFormat && !pFBInfo->pSourceBitmap.isNull())
2330 {
2331 BYTE *pAddress = NULL;
2332 ULONG ulWidth = 0;
2333 ULONG ulHeight = 0;
2334 ULONG ulBitsPerPixel = 0;
2335 ULONG ulBytesPerLine = 0;
2336 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2337
2338 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
2339 &ulWidth,
2340 &ulHeight,
2341 &ulBitsPerPixel,
2342 &ulBytesPerLine,
2343 &bitmapFormat);
2344 if (SUCCEEDED(hrc))
2345 {
2346 uint32_t width = pFBInfo->w;
2347 uint32_t height = pFBInfo->h;
2348
2349 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
2350 int32_t xSrc = 0;
2351 int32_t ySrc = 0;
2352 uint32_t u32SrcWidth = pFBInfo->w;
2353 uint32_t u32SrcHeight = pFBInfo->h;
2354 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
2355 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2356
2357 /* Default format is 32 bpp. */
2358 uint8_t *pu8Dst = pAddress;
2359 int32_t xDst = xSrc;
2360 int32_t yDst = ySrc;
2361 uint32_t u32DstWidth = u32SrcWidth;
2362 uint32_t u32DstHeight = u32SrcHeight;
2363 uint32_t u32DstLineSize = u32DstWidth * 4;
2364 uint32_t u32DstBitsPerPixel = 32;
2365
2366 /* if uWidth != pFBInfo->w and uHeight != pFBInfo->h
2367 * implies resize of Framebuffer is in progress and
2368 * copyrect should not be called.
2369 */
2370 if (ulWidth == pFBInfo->w && ulHeight == pFBInfo->h)
2371 {
2372 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2373 width, height,
2374 pu8Src,
2375 xSrc, ySrc,
2376 u32SrcWidth, u32SrcHeight,
2377 u32SrcLineSize, u32SrcBitsPerPixel,
2378 pu8Dst,
2379 xDst, yDst,
2380 u32DstWidth, u32DstHeight,
2381 u32DstLineSize, u32DstBitsPerPixel);
2382 }
2383 }
2384 }
2385
2386 pDisplay->i_handleDisplayUpdate(uScreenId, 0, 0, pFBInfo->w, pFBInfo->h);
2387 }
2388 }
2389 if (!fUpdateAll)
2390 break;
2391 }
2392 LogRelFlowFunc(("done\n"));
2393 return VINF_SUCCESS;
2394}
2395
2396/**
2397 * Does a full invalidation of the VM display and instructs the VM
2398 * to update it immediately.
2399 *
2400 * @returns COM status code
2401 */
2402
2403HRESULT Display::invalidateAndUpdate()
2404{
2405 LogRelFlowFunc(("\n"));
2406
2407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 CHECK_CONSOLE_DRV(mpDrv);
2410
2411 Console::SafeVMPtr ptrVM(mParent);
2412 if (!ptrVM.isOk())
2413 return ptrVM.rc();
2414
2415 HRESULT rc = S_OK;
2416
2417 LogRelFlowFunc(("Sending DPYUPDATE request\n"));
2418
2419 /* Have to release the lock when calling EMT. */
2420 alock.release();
2421
2422 int vrc = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
2423 3, this, 0, true);
2424 alock.acquire();
2425
2426 if (RT_FAILURE(vrc))
2427 rc = setErrorBoth(VBOX_E_VM_ERROR, vrc, tr("Could not invalidate and update the screen (%Rrc)"), vrc);
2428
2429 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2430 return rc;
2431}
2432
2433HRESULT Display::invalidateAndUpdateScreen(ULONG aScreenId)
2434{
2435 LogRelFlowFunc(("\n"));
2436
2437 HRESULT rc = S_OK;
2438
2439 Console::SafeVMPtr ptrVM(mParent);
2440 if (!ptrVM.isOk())
2441 return ptrVM.rc();
2442
2443 int vrc = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
2444 3, this, aScreenId, false);
2445 if (RT_FAILURE(vrc))
2446 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Could not invalidate and update the screen %d (%Rrc)"), aScreenId, vrc);
2447
2448 LogRelFlowFunc(("rc=%Rhrc\n", rc));
2449 return rc;
2450}
2451
2452HRESULT Display::completeVHWACommand(BYTE *aCommand)
2453{
2454#ifdef VBOX_WITH_VIDEOHWACCEL
2455 mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsync(mpDrv->pVBVACallbacks, (VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *)aCommand);
2456 return S_OK;
2457#else
2458 RT_NOREF(aCommand);
2459 return E_NOTIMPL;
2460#endif
2461}
2462
2463HRESULT Display::viewportChanged(ULONG aScreenId, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
2464{
2465 AssertMsgReturn(aScreenId < mcMonitors, ("aScreendId=%d mcMonitors=%d\n", aScreenId, mcMonitors), E_INVALIDARG);
2466
2467 /* The driver might not have been constructed yet */
2468 if (mpDrv && mpDrv->pUpPort->pfnSetViewport)
2469 mpDrv->pUpPort->pfnSetViewport(mpDrv->pUpPort, aScreenId, aX, aY, aWidth, aHeight);
2470
2471 return S_OK;
2472}
2473
2474HRESULT Display::querySourceBitmap(ULONG aScreenId,
2475 ComPtr<IDisplaySourceBitmap> &aDisplaySourceBitmap)
2476{
2477 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2478
2479 Console::SafeVMPtr ptrVM(mParent);
2480 if (!ptrVM.isOk())
2481 return ptrVM.rc();
2482
2483 CHECK_CONSOLE_DRV(mpDrv);
2484
2485 bool fSetRenderVRAM = false;
2486 bool fInvalidate = false;
2487
2488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2489
2490 if (aScreenId >= mcMonitors)
2491 return setError(E_INVALIDARG, tr("QuerySourceBitmap: Invalid screen %d (total %d)"),
2492 aScreenId, mcMonitors);
2493
2494 if (!mfSourceBitmapEnabled)
2495 {
2496 aDisplaySourceBitmap = NULL;
2497 return E_FAIL;
2498 }
2499
2500 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2501
2502 /* No source bitmap for a blank guest screen. */
2503 if (pFBInfo->flags & VBVA_SCREEN_F_BLANK)
2504 {
2505 aDisplaySourceBitmap = NULL;
2506 return E_FAIL;
2507 }
2508
2509 HRESULT hr = S_OK;
2510
2511 if (pFBInfo->pSourceBitmap.isNull())
2512 {
2513 /* Create a new object. */
2514 ComObjPtr<DisplaySourceBitmap> obj;
2515 hr = obj.createObject();
2516 if (SUCCEEDED(hr))
2517 hr = obj->init(this, aScreenId, pFBInfo);
2518
2519 if (SUCCEEDED(hr))
2520 {
2521 pFBInfo->pSourceBitmap = obj;
2522 pFBInfo->fDefaultFormat = !obj->i_usesVRAM();
2523
2524 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2525 {
2526 /* Start buffer updates. */
2527 BYTE *pAddress = NULL;
2528 ULONG ulWidth = 0;
2529 ULONG ulHeight = 0;
2530 ULONG ulBitsPerPixel = 0;
2531 ULONG ulBytesPerLine = 0;
2532 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2533
2534 pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
2535 &ulWidth,
2536 &ulHeight,
2537 &ulBitsPerPixel,
2538 &ulBytesPerLine,
2539 &bitmapFormat);
2540
2541 mpDrv->IConnector.pbData = pAddress;
2542 mpDrv->IConnector.cbScanline = ulBytesPerLine;
2543 mpDrv->IConnector.cBits = ulBitsPerPixel;
2544 mpDrv->IConnector.cx = ulWidth;
2545 mpDrv->IConnector.cy = ulHeight;
2546
2547 fSetRenderVRAM = pFBInfo->fDefaultFormat;
2548 }
2549
2550 /* Make sure that the bitmap contains the latest image. */
2551 fInvalidate = pFBInfo->fDefaultFormat;
2552 }
2553 }
2554
2555 if (SUCCEEDED(hr))
2556 {
2557 pFBInfo->pSourceBitmap.queryInterfaceTo(aDisplaySourceBitmap.asOutParam());
2558 }
2559
2560 /* Leave the IDisplay lock because the VGA device must not be called under it. */
2561 alock.release();
2562
2563 if (SUCCEEDED(hr))
2564 {
2565 if (fSetRenderVRAM)
2566 {
2567 mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, true);
2568 }
2569
2570 if (fInvalidate)
2571 VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
2572 3, this, aScreenId, false);
2573 }
2574
2575 LogRelFlowFunc(("%Rhrc\n", hr));
2576 return hr;
2577}
2578
2579HRESULT Display::getGuestScreenLayout(std::vector<ComPtr<IGuestScreenInfo> > &aGuestScreenLayout)
2580{
2581 NOREF(aGuestScreenLayout);
2582 return E_NOTIMPL;
2583}
2584
2585HRESULT Display::setScreenLayout(ScreenLayoutMode_T aScreenLayoutMode,
2586 const std::vector<ComPtr<IGuestScreenInfo> > &aGuestScreenInfo)
2587{
2588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2589
2590 if (aGuestScreenInfo.size() != mcMonitors)
2591 return E_INVALIDARG;
2592
2593 CHECK_CONSOLE_DRV(mpDrv);
2594
2595 /*
2596 * It is up to the guest to decide whether the hint is
2597 * valid. Therefore don't do any VRAM sanity checks here.
2598 */
2599
2600 /* Have to release the lock because the pfnRequestDisplayChange
2601 * will call EMT. */
2602 alock.release();
2603
2604 VMMDev *pVMMDev = mParent->i_getVMMDev();
2605 if (pVMMDev)
2606 {
2607 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
2608 if (pVMMDevPort)
2609 {
2610 uint32_t const cDisplays = (uint32_t)aGuestScreenInfo.size();
2611
2612 size_t const cbAlloc = cDisplays * sizeof(VMMDevDisplayDef);
2613 VMMDevDisplayDef *paDisplayDefs = (VMMDevDisplayDef *)RTMemAlloc(cbAlloc);
2614 if (paDisplayDefs)
2615 {
2616 for (uint32_t i = 0; i < cDisplays; ++i)
2617 {
2618 VMMDevDisplayDef *p = &paDisplayDefs[i];
2619 ComPtr<IGuestScreenInfo> pScreenInfo = aGuestScreenInfo[i];
2620
2621 ULONG screenId = 0;
2622 GuestMonitorStatus_T guestMonitorStatus = GuestMonitorStatus_Enabled;
2623 BOOL origin = FALSE;
2624 BOOL primary = FALSE;
2625 LONG originX = 0;
2626 LONG originY = 0;
2627 ULONG width = 0;
2628 ULONG height = 0;
2629 ULONG bitsPerPixel = 0;
2630
2631 pScreenInfo->COMGETTER(ScreenId) (&screenId);
2632 pScreenInfo->COMGETTER(GuestMonitorStatus)(&guestMonitorStatus);
2633 pScreenInfo->COMGETTER(Primary) (&primary);
2634 pScreenInfo->COMGETTER(Origin) (&origin);
2635 pScreenInfo->COMGETTER(OriginX) (&originX);
2636 pScreenInfo->COMGETTER(OriginY) (&originY);
2637 pScreenInfo->COMGETTER(Width) (&width);
2638 pScreenInfo->COMGETTER(Height) (&height);
2639 pScreenInfo->COMGETTER(BitsPerPixel)(&bitsPerPixel);
2640
2641 LogFlowFunc(("%d %d,%d %dx%d\n", screenId, originX, originY, width, height));
2642
2643 p->idDisplay = screenId;
2644 p->xOrigin = originX;
2645 p->yOrigin = originY;
2646 p->cx = width;
2647 p->cy = height;
2648 p->cBitsPerPixel = bitsPerPixel;
2649 p->fDisplayFlags = VMMDEV_DISPLAY_CX | VMMDEV_DISPLAY_CY | VMMDEV_DISPLAY_BPP;
2650 if (guestMonitorStatus == GuestMonitorStatus_Disabled)
2651 p->fDisplayFlags |= VMMDEV_DISPLAY_DISABLED;
2652 if (origin)
2653 p->fDisplayFlags |= VMMDEV_DISPLAY_ORIGIN;
2654 if (primary)
2655 p->fDisplayFlags |= VMMDEV_DISPLAY_PRIMARY;
2656 }
2657
2658 bool const fForce = aScreenLayoutMode == ScreenLayoutMode_Reset
2659 || aScreenLayoutMode == ScreenLayoutMode_Apply;
2660 bool const fNotify = aScreenLayoutMode != ScreenLayoutMode_Silent;
2661 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, cDisplays, paDisplayDefs, fForce, fNotify);
2662
2663 RTMemFree(paDisplayDefs);
2664 }
2665 }
2666 }
2667 return S_OK;
2668}
2669
2670HRESULT Display::detachScreens(const std::vector<LONG> &aScreenIds)
2671{
2672 NOREF(aScreenIds);
2673 return E_NOTIMPL;
2674}
2675
2676HRESULT Display::createGuestScreenInfo(ULONG aDisplay,
2677 GuestMonitorStatus_T aStatus,
2678 BOOL aPrimary,
2679 BOOL aChangeOrigin,
2680 LONG aOriginX,
2681 LONG aOriginY,
2682 ULONG aWidth,
2683 ULONG aHeight,
2684 ULONG aBitsPerPixel,
2685 ComPtr<IGuestScreenInfo> &aGuestScreenInfo)
2686{
2687 /* Create a new object. */
2688 ComObjPtr<GuestScreenInfo> obj;
2689 HRESULT hr = obj.createObject();
2690 if (SUCCEEDED(hr))
2691 hr = obj->init(aDisplay, aStatus, aPrimary, aChangeOrigin, aOriginX, aOriginY,
2692 aWidth, aHeight, aBitsPerPixel);
2693 if (SUCCEEDED(hr))
2694 obj.queryInterfaceTo(aGuestScreenInfo.asOutParam());
2695
2696 return hr;
2697}
2698
2699
2700/*
2701 * GuestScreenInfo implementation.
2702 */
2703DEFINE_EMPTY_CTOR_DTOR(GuestScreenInfo)
2704
2705HRESULT GuestScreenInfo::FinalConstruct()
2706{
2707 return BaseFinalConstruct();
2708}
2709
2710void GuestScreenInfo::FinalRelease()
2711{
2712 uninit();
2713
2714 BaseFinalRelease();
2715}
2716
2717HRESULT GuestScreenInfo::init(ULONG aDisplay,
2718 GuestMonitorStatus_T aGuestMonitorStatus,
2719 BOOL aPrimary,
2720 BOOL aChangeOrigin,
2721 LONG aOriginX,
2722 LONG aOriginY,
2723 ULONG aWidth,
2724 ULONG aHeight,
2725 ULONG aBitsPerPixel)
2726{
2727 LogFlowThisFunc(("[%u]\n", aDisplay));
2728
2729 /* Enclose the state transition NotReady->InInit->Ready */
2730 AutoInitSpan autoInitSpan(this);
2731 AssertReturn(autoInitSpan.isOk(), E_FAIL);
2732
2733 mScreenId = aDisplay;
2734 mGuestMonitorStatus = aGuestMonitorStatus;
2735 mPrimary = aPrimary;
2736 mOrigin = aChangeOrigin;
2737 mOriginX = aOriginX;
2738 mOriginY = aOriginY;
2739 mWidth = aWidth;
2740 mHeight = aHeight;
2741 mBitsPerPixel = aBitsPerPixel;
2742
2743 /* Confirm a successful initialization */
2744 autoInitSpan.setSucceeded();
2745
2746 return S_OK;
2747}
2748
2749void GuestScreenInfo::uninit()
2750{
2751 /* Enclose the state transition Ready->InUninit->NotReady */
2752 AutoUninitSpan autoUninitSpan(this);
2753 if (autoUninitSpan.uninitDone())
2754 return;
2755
2756 LogFlowThisFunc(("[%u]\n", mScreenId));
2757}
2758
2759HRESULT GuestScreenInfo::getScreenId(ULONG *aScreenId)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762 *aScreenId = mScreenId;
2763 return S_OK;
2764}
2765
2766HRESULT GuestScreenInfo::getGuestMonitorStatus(GuestMonitorStatus_T *aGuestMonitorStatus)
2767{
2768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2769 *aGuestMonitorStatus = mGuestMonitorStatus;
2770 return S_OK;
2771}
2772
2773HRESULT GuestScreenInfo::getPrimary(BOOL *aPrimary)
2774{
2775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2776 *aPrimary = mPrimary;
2777 return S_OK;
2778}
2779
2780HRESULT GuestScreenInfo::getOrigin(BOOL *aOrigin)
2781{
2782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2783 *aOrigin = mOrigin;
2784 return S_OK;
2785}
2786
2787HRESULT GuestScreenInfo::getOriginX(LONG *aOriginX)
2788{
2789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2790 *aOriginX = mOriginX;
2791 return S_OK;
2792}
2793
2794HRESULT GuestScreenInfo::getOriginY(LONG *aOriginY)
2795{
2796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2797 *aOriginY = mOriginY;
2798 return S_OK;
2799}
2800
2801HRESULT GuestScreenInfo::getWidth(ULONG *aWidth)
2802{
2803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2804 *aWidth = mWidth;
2805 return S_OK;
2806}
2807
2808HRESULT GuestScreenInfo::getHeight(ULONG *aHeight)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811 *aHeight = mHeight;
2812 return S_OK;
2813}
2814
2815HRESULT GuestScreenInfo::getBitsPerPixel(ULONG *aBitsPerPixel)
2816{
2817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2818 *aBitsPerPixel = mBitsPerPixel;
2819 return S_OK;
2820}
2821
2822HRESULT GuestScreenInfo::getExtendedInfo(com::Utf8Str &aExtendedInfo)
2823{
2824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2825 aExtendedInfo = com::Utf8Str();
2826 return S_OK;
2827}
2828
2829// wrapped IEventListener method
2830HRESULT Display::handleEvent(const ComPtr<IEvent> &aEvent)
2831{
2832 VBoxEventType_T aType = VBoxEventType_Invalid;
2833
2834 aEvent->COMGETTER(Type)(&aType);
2835 switch (aType)
2836 {
2837 case VBoxEventType_OnStateChanged:
2838 {
2839 ComPtr<IStateChangedEvent> scev = aEvent;
2840 Assert(scev);
2841 MachineState_T machineState;
2842 scev->COMGETTER(State)(&machineState);
2843 if ( machineState == MachineState_Running
2844 || machineState == MachineState_Teleporting
2845 || machineState == MachineState_LiveSnapshotting
2846 || machineState == MachineState_DeletingSnapshotOnline
2847 )
2848 {
2849 LogRelFlowFunc(("Machine is running.\n"));
2850
2851 }
2852 break;
2853 }
2854 default:
2855 AssertFailed();
2856 }
2857
2858 return S_OK;
2859}
2860
2861
2862// private methods
2863/////////////////////////////////////////////////////////////////////////////
2864
2865/**
2866 * Handle display resize event issued by the VGA device for the primary screen.
2867 *
2868 * @see PDMIDISPLAYCONNECTOR::pfnResize
2869 */
2870DECLCALLBACK(int) Display::i_displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
2871 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
2872{
2873 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2874 Display *pThis = pDrv->pDisplay;
2875
2876 LogRelFlowFunc(("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
2877 bpp, pvVRAM, cbLine, cx, cy));
2878
2879 bool f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, true, false);
2880 if (!f)
2881 {
2882 /* This is a result of recursive call when the source bitmap is being updated
2883 * during a VGA resize. Tell the VGA device to ignore the call.
2884 *
2885 * @todo It is a workaround, actually pfnUpdateDisplayAll must
2886 * fail on resize.
2887 */
2888 LogRel(("displayResizeCallback: already processing\n"));
2889 return VINF_VGA_RESIZE_IN_PROGRESS;
2890 }
2891
2892 int rc = pThis->i_handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy, 0, 0, 0, true);
2893
2894 /* Restore the flag. */
2895 f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, false, true);
2896 AssertRelease(f);
2897
2898 return rc;
2899}
2900
2901/**
2902 * Handle display update.
2903 *
2904 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
2905 */
2906DECLCALLBACK(void) Display::i_displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
2907 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
2908{
2909 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2910
2911#ifdef DEBUG_sunlover
2912 LogFlowFunc(("fVideoAccelEnabled = %d, %d,%d %dx%d\n",
2913 pDrv->pDisplay->mVideoAccelLegacy.fVideoAccelEnabled, x, y, cx, cy));
2914#endif /* DEBUG_sunlover */
2915
2916 /* This call does update regardless of VBVA status.
2917 * But in VBVA mode this is called only as result of
2918 * pfnUpdateDisplayAll in the VGA device.
2919 */
2920
2921 pDrv->pDisplay->i_handleDisplayUpdate(VBOX_VIDEO_PRIMARY_SCREEN, x, y, cx, cy);
2922}
2923
2924/**
2925 * Periodic display refresh callback.
2926 *
2927 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
2928 * @thread EMT
2929 */
2930/*static*/ DECLCALLBACK(void) Display::i_displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
2931{
2932 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2933
2934#ifdef DEBUG_sunlover_2
2935 LogFlowFunc(("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
2936 pDrv->pDisplay->mfVideoAccelEnabled));
2937#endif /* DEBUG_sunlover_2 */
2938
2939 Display *pDisplay = pDrv->pDisplay;
2940 unsigned uScreenId;
2941
2942 int rc = pDisplay->i_videoAccelRefreshProcess(pDrv->pUpPort);
2943 if (rc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
2944 {
2945 if (rc == VWRN_INVALID_STATE)
2946 {
2947 /* No VBVA do a display update. */
2948 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
2949 }
2950
2951 /* Inform the VRDP server that the current display update sequence is
2952 * completed. At this moment the framebuffer memory contains a definite
2953 * image, that is synchronized with the orders already sent to VRDP client.
2954 * The server can now process redraw requests from clients or initial
2955 * fullscreen updates for new clients.
2956 */
2957 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
2958 {
2959 Assert(pDisplay->mParent && pDisplay->mParent->i_consoleVRDPServer());
2960 pDisplay->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, NULL, 0);
2961 }
2962 }
2963
2964#ifdef VBOX_WITH_RECORDING
2965 AssertPtr(pDisplay->mParent);
2966 RecordingContext *pCtx = pDisplay->mParent->i_recordingGetContext();
2967
2968 if ( pCtx
2969 && pCtx->IsStarted()
2970 && pCtx->IsFeatureEnabled(RecordingFeature_Video))
2971 {
2972 do
2973 {
2974 /* If the recording context has reached the configured recording
2975 * limit, disable recording. */
2976 if (pCtx->IsLimitReached())
2977 {
2978 pDisplay->mParent->i_onRecordingChange(FALSE /* Disable */);
2979 break;
2980 }
2981
2982 uint64_t tsNowMs = RTTimeProgramMilliTS();
2983 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
2984 {
2985 if (!pDisplay->maRecordingEnabled[uScreenId])
2986 continue;
2987
2988 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2989 if (!pFBInfo->fDisabled)
2990 {
2991 ComPtr<IDisplaySourceBitmap> pSourceBitmap;
2992 int rc2 = RTCritSectEnter(&pDisplay->mVideoRecLock);
2993 if (RT_SUCCESS(rc2))
2994 {
2995 pSourceBitmap = pFBInfo->Recording.pSourceBitmap;
2996 RTCritSectLeave(&pDisplay->mVideoRecLock);
2997 }
2998
2999 if (!pSourceBitmap.isNull())
3000 {
3001 BYTE *pbAddress = NULL;
3002 ULONG ulWidth = 0;
3003 ULONG ulHeight = 0;
3004 ULONG ulBitsPerPixel = 0;
3005 ULONG ulBytesPerLine = 0;
3006 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
3007 HRESULT hr = pSourceBitmap->QueryBitmapInfo(&pbAddress,
3008 &ulWidth,
3009 &ulHeight,
3010 &ulBitsPerPixel,
3011 &ulBytesPerLine,
3012 &bitmapFormat);
3013 if (SUCCEEDED(hr) && pbAddress)
3014 rc = pCtx->SendVideoFrame(uScreenId, 0, 0, BitmapFormat_BGR,
3015 ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight,
3016 pbAddress, tsNowMs);
3017 else
3018 rc = VERR_NOT_SUPPORTED;
3019
3020 pSourceBitmap.setNull();
3021 }
3022 else
3023 rc = VERR_NOT_SUPPORTED;
3024
3025 if (rc == VINF_TRY_AGAIN)
3026 break;
3027 }
3028 }
3029 } while (0);
3030 }
3031#endif /* VBOX_WITH_RECORDING */
3032
3033#ifdef DEBUG_sunlover_2
3034 LogFlowFunc(("leave\n"));
3035#endif /* DEBUG_sunlover_2 */
3036}
3037
3038/**
3039 * Reset notification
3040 *
3041 * @see PDMIDISPLAYCONNECTOR::pfnReset
3042 */
3043DECLCALLBACK(void) Display::i_displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
3044{
3045 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3046
3047 LogRelFlowFunc(("\n"));
3048
3049 /* Disable VBVA mode. */
3050 pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
3051}
3052
3053/**
3054 * LFBModeChange notification
3055 *
3056 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
3057 */
3058DECLCALLBACK(void) Display::i_displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
3059{
3060 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3061
3062 LogRelFlowFunc(("fEnabled=%d\n", fEnabled));
3063
3064 NOREF(fEnabled);
3065
3066 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
3067 pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
3068}
3069
3070/**
3071 * Adapter information change notification.
3072 *
3073 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
3074 */
3075DECLCALLBACK(void) Display::i_displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM,
3076 uint32_t u32VRAMSize)
3077{
3078 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3079 pDrv->pDisplay->processAdapterData(pvVRAM, u32VRAMSize);
3080}
3081
3082/**
3083 * Display information change notification.
3084 *
3085 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
3086 */
3087DECLCALLBACK(void) Display::i_displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface,
3088 void *pvVRAM, unsigned uScreenId)
3089{
3090 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3091 pDrv->pDisplay->processDisplayData(pvVRAM, uScreenId);
3092}
3093
3094#ifdef VBOX_WITH_VIDEOHWACCEL
3095
3096int Display::i_handleVHWACommandProcess(int enmCmd, bool fGuestCmd, VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand)
3097{
3098 /* bugref:9691 Disable the legacy VHWA interface.
3099 * Keep the host commands enabled because they are needed when an old saved state is loaded.
3100 */
3101 if (fGuestCmd)
3102 return VERR_NOT_IMPLEMENTED;
3103
3104 unsigned id = (unsigned)pCommand->iDisplay;
3105 if (id >= mcMonitors)
3106 return VERR_INVALID_PARAMETER;
3107
3108 ComPtr<IFramebuffer> pFramebuffer;
3109 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
3110 pFramebuffer = maFramebuffers[id].pFramebuffer;
3111 bool fVHWASupported = RT_BOOL(maFramebuffers[id].u32Caps & FramebufferCapabilities_VHWA);
3112 arlock.release();
3113
3114 if (pFramebuffer == NULL || !fVHWASupported)
3115 return VERR_NOT_IMPLEMENTED; /* Implementation is not available. */
3116
3117 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE *)pCommand, enmCmd, fGuestCmd);
3118 if (hr == S_FALSE)
3119 return VINF_SUCCESS;
3120 if (SUCCEEDED(hr))
3121 return VINF_CALLBACK_RETURN;
3122 if (hr == E_ACCESSDENIED)
3123 return VERR_INVALID_STATE; /* notify we can not handle request atm */
3124 if (hr == E_NOTIMPL)
3125 return VERR_NOT_IMPLEMENTED;
3126 return VERR_GENERAL_FAILURE;
3127}
3128
3129DECLCALLBACK(int) Display::i_displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, int enmCmd, bool fGuestCmd,
3130 VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand)
3131{
3132 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3133
3134 return pDrv->pDisplay->i_handleVHWACommandProcess(enmCmd, fGuestCmd, pCommand);
3135}
3136
3137#endif /* VBOX_WITH_VIDEOHWACCEL */
3138
3139int Display::i_handle3DNotifyProcess(VBOX3DNOTIFY *p3DNotify)
3140{
3141 unsigned const id = (unsigned)p3DNotify->iDisplay;
3142 if (id >= mcMonitors)
3143 return VERR_INVALID_PARAMETER;
3144
3145 ComPtr<IFramebuffer> pFramebuffer;
3146 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
3147 pFramebuffer = maFramebuffers[id].pFramebuffer;
3148 arlock.release();
3149
3150 int rc = VINF_SUCCESS;
3151
3152 if (!pFramebuffer.isNull())
3153 {
3154 if (p3DNotify->enmNotification == VBOX3D_NOTIFY_TYPE_HW_OVERLAY_GET_ID)
3155 {
3156 LONG64 winId = 0;
3157 HRESULT hr = pFramebuffer->COMGETTER(WinId)(&winId);
3158 if (SUCCEEDED(hr))
3159 {
3160 *(uint64_t *)&p3DNotify->au8Data[0] = winId;
3161 }
3162 else
3163 rc = VERR_NOT_SUPPORTED;
3164 }
3165 else
3166 {
3167 com::SafeArray<BYTE> data;
3168 data.initFrom((BYTE *)&p3DNotify->au8Data[0], p3DNotify->cbData);
3169
3170 HRESULT hr = pFramebuffer->Notify3DEvent((ULONG)p3DNotify->enmNotification, ComSafeArrayAsInParam(data));
3171 if (FAILED(hr))
3172 rc = VERR_NOT_SUPPORTED;
3173 }
3174 }
3175 else
3176 rc = VERR_NOT_IMPLEMENTED;
3177
3178 return rc;
3179}
3180
3181DECLCALLBACK(int) Display::i_display3DNotifyProcess(PPDMIDISPLAYCONNECTOR pInterface,
3182 VBOX3DNOTIFY *p3DNotify)
3183{
3184 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3185 return pDrv->pDisplay->i_handle3DNotifyProcess(p3DNotify);
3186}
3187
3188HRESULT Display::notifyScaleFactorChange(ULONG aScreenId, ULONG aScaleFactorWMultiplied, ULONG aScaleFactorHMultiplied)
3189{
3190 RT_NOREF(aScreenId, aScaleFactorWMultiplied, aScaleFactorHMultiplied);
3191# if 0 /** @todo Thank you so very much from anyone using VMSVGA3d! */
3192 AssertMsgFailed(("Attempt to specify OpenGL content scale factor while 3D acceleration is disabled in VM config. Ignored.\n"));
3193# else
3194 /* Need an interface like this here (and the #ifdefs needs adjusting):
3195 PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
3196 if (pUpPort && pUpPort->pfnSetScaleFactor)
3197 pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
3198# endif
3199 return S_OK;
3200}
3201
3202HRESULT Display::notifyHiDPIOutputPolicyChange(BOOL fUnscaledHiDPI)
3203{
3204 RT_NOREF(fUnscaledHiDPI);
3205
3206 /* Need an interface like this here (and the #ifdefs needs adjusting):
3207 PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
3208 if (pUpPort && pUpPort->pfnSetScaleFactor)
3209 pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
3210
3211 return S_OK;
3212}
3213
3214#ifdef VBOX_WITH_HGSMI
3215/**
3216 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnVBVAEnable}
3217 */
3218DECLCALLBACK(int) Display::i_displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
3219 VBVAHOSTFLAGS RT_UNTRUSTED_VOLATILE_GUEST *pHostFlags)
3220{
3221 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
3222
3223 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3224 Display *pThis = pDrv->pDisplay;
3225 AssertReturn(uScreenId < pThis->mcMonitors, VERR_INVALID_PARAMETER);
3226
3227 if (pThis->maFramebuffers[uScreenId].fVBVAEnabled)
3228 {
3229 LogRel(("Enabling different vbva mode\n"));
3230#ifdef DEBUG_misha
3231 AssertMsgFailed(("enabling different vbva mode\n"));
3232#endif
3233 return VERR_INVALID_STATE;
3234 }
3235
3236 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
3237 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
3238 pThis->maFramebuffers[uScreenId].fVBVAForceResize = true;
3239
3240 vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
3241
3242 return VINF_SUCCESS;
3243}
3244
3245/**
3246 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnVBVADisable}
3247 */
3248DECLCALLBACK(void) Display::i_displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
3249{
3250 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
3251
3252 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3253 Display *pThis = pDrv->pDisplay;
3254 AssertReturnVoid(uScreenId < pThis->mcMonitors);
3255
3256 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3257
3258 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3259 {
3260 /* Make sure that the primary screen is visible now.
3261 * The guest can't use VBVA anymore, so only only the VGA device output works.
3262 */
3263 pFBInfo->flags = 0;
3264 if (pFBInfo->fDisabled)
3265 {
3266 pFBInfo->fDisabled = false;
3267 ::FireGuestMonitorChangedEvent(pThis->mParent->i_getEventSource(), GuestMonitorChangedEventType_Enabled, uScreenId,
3268 pFBInfo->xOrigin, pFBInfo->yOrigin, pFBInfo->w, pFBInfo->h);
3269 }
3270 }
3271
3272 pFBInfo->fVBVAEnabled = false;
3273 pFBInfo->fVBVAForceResize = false;
3274
3275 vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, pFBInfo);
3276
3277 pFBInfo->pVBVAHostFlags = NULL;
3278
3279 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3280 {
3281 /* Force full screen update, because VGA device must take control, do resize, etc. */
3282 pThis->mpDrv->pUpPort->pfnUpdateDisplayAll(pThis->mpDrv->pUpPort, /* fFailOnResize = */ false);
3283 }
3284}
3285
3286DECLCALLBACK(void) Display::i_displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
3287{
3288 RT_NOREF(uScreenId);
3289 LogFlowFunc(("uScreenId %d\n", uScreenId));
3290
3291 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3292 Display *pThis = pDrv->pDisplay;
3293
3294 if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
3295 {
3296 vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers,
3297 pThis->mcMonitors);
3298 ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
3299 }
3300}
3301
3302/**
3303 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnVBVAUpdateProcess}
3304 */
3305DECLCALLBACK(void) Display::i_displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
3306 struct VBVACMDHDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmd, size_t cbCmd)
3307{
3308 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d, @%d,%d %dx%d\n", uScreenId, pCmd, cbCmd, pCmd->x, pCmd->y, pCmd->w, pCmd->h));
3309 VBVACMDHDR hdrSaved;
3310 RT_COPY_VOLATILE(hdrSaved, *pCmd);
3311 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
3312
3313 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3314 Display *pThis = pDrv->pDisplay;
3315 DISPLAYFBINFO *pFBInfo;
3316 AssertReturnVoid(uScreenId < pThis->mcMonitors);
3317
3318 pFBInfo = &pThis->maFramebuffers[uScreenId];
3319
3320 if (pFBInfo->fDefaultFormat)
3321 {
3322 /* Make sure that framebuffer contains the same image as the guest VRAM. */
3323 if ( uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
3324 && !pFBInfo->fDisabled)
3325 {
3326 pDrv->pUpPort->pfnUpdateDisplayRect(pDrv->pUpPort, hdrSaved.x, hdrSaved.y, hdrSaved.w, hdrSaved.h);
3327 }
3328 else if ( !pFBInfo->pSourceBitmap.isNull()
3329 && !pFBInfo->fDisabled)
3330 {
3331 /* Render VRAM content to the framebuffer. */
3332 BYTE *pAddress = NULL;
3333 ULONG ulWidth = 0;
3334 ULONG ulHeight = 0;
3335 ULONG ulBitsPerPixel = 0;
3336 ULONG ulBytesPerLine = 0;
3337 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
3338
3339 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
3340 &ulWidth,
3341 &ulHeight,
3342 &ulBitsPerPixel,
3343 &ulBytesPerLine,
3344 &bitmapFormat);
3345 if (SUCCEEDED(hrc))
3346 {
3347 uint32_t width = hdrSaved.w;
3348 uint32_t height = hdrSaved.h;
3349
3350 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
3351 int32_t xSrc = hdrSaved.x - pFBInfo->xOrigin;
3352 int32_t ySrc = hdrSaved.y - pFBInfo->yOrigin;
3353 uint32_t u32SrcWidth = pFBInfo->w;
3354 uint32_t u32SrcHeight = pFBInfo->h;
3355 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
3356 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3357
3358 uint8_t *pu8Dst = pAddress;
3359 int32_t xDst = xSrc;
3360 int32_t yDst = ySrc;
3361 uint32_t u32DstWidth = u32SrcWidth;
3362 uint32_t u32DstHeight = u32SrcHeight;
3363 uint32_t u32DstLineSize = u32DstWidth * 4;
3364 uint32_t u32DstBitsPerPixel = 32;
3365
3366 pDrv->pUpPort->pfnCopyRect(pDrv->pUpPort,
3367 width, height,
3368 pu8Src,
3369 xSrc, ySrc,
3370 u32SrcWidth, u32SrcHeight,
3371 u32SrcLineSize, u32SrcBitsPerPixel,
3372 pu8Dst,
3373 xDst, yDst,
3374 u32DstWidth, u32DstHeight,
3375 u32DstLineSize, u32DstBitsPerPixel);
3376 }
3377 }
3378 }
3379
3380 /*
3381 * Here is your classic 'temporary' solution.
3382 */
3383 /** @todo New SendUpdate entry which can get a separate cmd header or coords. */
3384 VBVACMDHDR *pHdrUnconst = (VBVACMDHDR *)pCmd;
3385
3386 pHdrUnconst->x -= (int16_t)pFBInfo->xOrigin;
3387 pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
3388
3389 pThis->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, pHdrUnconst, (uint32_t)cbCmd);
3390
3391 *pHdrUnconst = hdrSaved;
3392}
3393
3394DECLCALLBACK(void) Display::i_displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y,
3395 uint32_t cx, uint32_t cy)
3396{
3397 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
3398
3399 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3400 Display *pThis = pDrv->pDisplay;
3401 DISPLAYFBINFO *pFBInfo;
3402 AssertReturnVoid(uScreenId < pThis->mcMonitors);
3403
3404 pFBInfo = &pThis->maFramebuffers[uScreenId];
3405
3406 /** @todo handleFramebufferUpdate (uScreenId,
3407 * x - pThis->maFramebuffers[uScreenId].xOrigin,
3408 * y - pThis->maFramebuffers[uScreenId].yOrigin,
3409 * cx, cy);
3410 */
3411 pThis->i_handleDisplayUpdate(uScreenId, x - pFBInfo->xOrigin, y - pFBInfo->yOrigin, cx, cy);
3412}
3413
3414#ifdef DEBUG_sunlover
3415static void logVBVAResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, const DISPLAYFBINFO *pFBInfo)
3416{
3417 LogRel(("displayVBVAResize: [%d] %s\n"
3418 " pView->u32ViewIndex %d\n"
3419 " pView->u32ViewOffset 0x%08X\n"
3420 " pView->u32ViewSize 0x%08X\n"
3421 " pView->u32MaxScreenSize 0x%08X\n"
3422 " pScreen->i32OriginX %d\n"
3423 " pScreen->i32OriginY %d\n"
3424 " pScreen->u32StartOffset 0x%08X\n"
3425 " pScreen->u32LineSize 0x%08X\n"
3426 " pScreen->u32Width %d\n"
3427 " pScreen->u32Height %d\n"
3428 " pScreen->u16BitsPerPixel %d\n"
3429 " pScreen->u16Flags 0x%04X\n"
3430 " pFBInfo->u32Offset 0x%08X\n"
3431 " pFBInfo->u32MaxFramebufferSize 0x%08X\n"
3432 " pFBInfo->u32InformationSize 0x%08X\n"
3433 " pFBInfo->fDisabled %d\n"
3434 " xOrigin, yOrigin, w, h: %d,%d %dx%d\n"
3435 " pFBInfo->u16BitsPerPixel %d\n"
3436 " pFBInfo->pu8FramebufferVRAM %p\n"
3437 " pFBInfo->u32LineSize 0x%08X\n"
3438 " pFBInfo->flags 0x%04X\n"
3439 " pFBInfo->pHostEvents %p\n"
3440 " pFBInfo->fDefaultFormat %d\n"
3441 " pFBInfo->fVBVAEnabled %d\n"
3442 " pFBInfo->fVBVAForceResize %d\n"
3443 " pFBInfo->pVBVAHostFlags %p\n"
3444 "",
3445 pScreen->u32ViewIndex,
3446 (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)? "DISABLED": "ENABLED",
3447 pView->u32ViewIndex,
3448 pView->u32ViewOffset,
3449 pView->u32ViewSize,
3450 pView->u32MaxScreenSize,
3451 pScreen->i32OriginX,
3452 pScreen->i32OriginY,
3453 pScreen->u32StartOffset,
3454 pScreen->u32LineSize,
3455 pScreen->u32Width,
3456 pScreen->u32Height,
3457 pScreen->u16BitsPerPixel,
3458 pScreen->u16Flags,
3459 pFBInfo->u32Offset,
3460 pFBInfo->u32MaxFramebufferSize,
3461 pFBInfo->u32InformationSize,
3462 pFBInfo->fDisabled,
3463 pFBInfo->xOrigin,
3464 pFBInfo->yOrigin,
3465 pFBInfo->w,
3466 pFBInfo->h,
3467 pFBInfo->u16BitsPerPixel,
3468 pFBInfo->pu8FramebufferVRAM,
3469 pFBInfo->u32LineSize,
3470 pFBInfo->flags,
3471 pFBInfo->pHostEvents,
3472 pFBInfo->fDefaultFormat,
3473 pFBInfo->fVBVAEnabled,
3474 pFBInfo->fVBVAForceResize,
3475 pFBInfo->pVBVAHostFlags
3476 ));
3477}
3478#endif /* DEBUG_sunlover */
3479
3480DECLCALLBACK(int) Display::i_displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, PCVBVAINFOVIEW pView,
3481 PCVBVAINFOSCREEN pScreen, void *pvVRAM, bool fResetInputMapping)
3482{
3483 LogRelFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
3484
3485 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3486 Display *pThis = pDrv->pDisplay;
3487
3488 return pThis->processVBVAResize(pView, pScreen, pvVRAM, fResetInputMapping);
3489}
3490
3491int Display::processVBVAResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, void *pvVRAM, bool fResetInputMapping)
3492{
3493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3494
3495 RT_NOREF(pView);
3496
3497 DISPLAYFBINFO *pFBInfo = &maFramebuffers[pScreen->u32ViewIndex];
3498
3499#ifdef DEBUG_sunlover
3500 logVBVAResize(pView, pScreen, pFBInfo);
3501#endif
3502
3503 if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
3504 {
3505 /* Ask the framebuffer to resize using a default format. The framebuffer will be black.
3506 * So if the frontend does not support GuestMonitorChangedEventType_Disabled event,
3507 * the VM window will be black. */
3508 uint32_t u32Width = pFBInfo->w ? pFBInfo->w : 640;
3509 uint32_t u32Height = pFBInfo->h ? pFBInfo->h : 480;
3510 int32_t xOrigin = pFBInfo->xOrigin;
3511 int32_t yOrigin = pFBInfo->yOrigin;
3512
3513 alock.release();
3514
3515 i_handleDisplayResize(pScreen->u32ViewIndex, 0, (uint8_t *)NULL, 0,
3516 u32Width, u32Height, pScreen->u16Flags, xOrigin, yOrigin, false);
3517
3518 return VINF_SUCCESS;
3519 }
3520
3521 VBVAINFOSCREEN screenInfo;
3522 RT_ZERO(screenInfo);
3523
3524 if (pScreen->u16Flags & VBVA_SCREEN_F_BLANK2)
3525 {
3526 /* Init a local VBVAINFOSCREEN structure, which will be used instead of
3527 * the original pScreen. Set VBVA_SCREEN_F_BLANK, which will force
3528 * the code below to choose the "blanking" branches.
3529 */
3530 screenInfo.u32ViewIndex = pScreen->u32ViewIndex;
3531 screenInfo.i32OriginX = pFBInfo->xOrigin;
3532 screenInfo.i32OriginY = pFBInfo->yOrigin;
3533 screenInfo.u32StartOffset = 0; /* Irrelevant */
3534 screenInfo.u32LineSize = pFBInfo->u32LineSize;
3535 screenInfo.u32Width = pFBInfo->w;
3536 screenInfo.u32Height = pFBInfo->h;
3537 screenInfo.u16BitsPerPixel = pFBInfo->u16BitsPerPixel;
3538 screenInfo.u16Flags = pScreen->u16Flags | VBVA_SCREEN_F_BLANK;
3539
3540 pScreen = &screenInfo;
3541 }
3542
3543 if (fResetInputMapping)
3544 {
3545 /// @todo Rename to m* and verify whether some kind of lock is required.
3546 xInputMappingOrigin = 0;
3547 yInputMappingOrigin = 0;
3548 cxInputMapping = 0;
3549 cyInputMapping = 0;
3550 }
3551
3552 alock.release();
3553
3554 return i_handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
3555 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
3556 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height, pScreen->u16Flags,
3557 pScreen->i32OriginX, pScreen->i32OriginY, false);
3558}
3559
3560DECLCALLBACK(int) Display::i_displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
3561 uint32_t xHot, uint32_t yHot,
3562 uint32_t cx, uint32_t cy,
3563 const void *pvShape)
3564{
3565 LogFlowFunc(("\n"));
3566 LogRel2(("%s: fVisible=%RTbool\n", __PRETTY_FUNCTION__, fVisible));
3567
3568 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3569
3570 uint32_t cbShape = 0;
3571 if (pvShape)
3572 {
3573 cbShape = (cx + 7) / 8 * cy; /* size of the AND mask */
3574 cbShape = ((cbShape + 3) & ~3) + cx * 4 * cy; /* + gap + size of the XOR mask */
3575 }
3576
3577 /* Tell the console about it */
3578 pDrv->pDisplay->mParent->i_onMousePointerShapeChange(fVisible, fAlpha,
3579 xHot, yHot, cx, cy, (uint8_t *)pvShape, cbShape);
3580
3581 return VINF_SUCCESS;
3582}
3583
3584DECLCALLBACK(void) Display::i_displayVBVAGuestCapabilityUpdate(PPDMIDISPLAYCONNECTOR pInterface, uint32_t fCapabilities)
3585{
3586 LogFlowFunc(("\n"));
3587
3588 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3589 Display *pThis = pDrv->pDisplay;
3590
3591 pThis->i_handleUpdateGuestVBVACapabilities(fCapabilities);
3592}
3593
3594DECLCALLBACK(void) Display::i_displayVBVAInputMappingUpdate(PPDMIDISPLAYCONNECTOR pInterface, int32_t xOrigin, int32_t yOrigin,
3595 uint32_t cx, uint32_t cy)
3596{
3597 LogFlowFunc(("\n"));
3598
3599 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3600 Display *pThis = pDrv->pDisplay;
3601
3602 pThis->i_handleUpdateVBVAInputMapping(xOrigin, yOrigin, cx, cy);
3603}
3604
3605DECLCALLBACK(void) Display::i_displayVBVAReportCursorPosition(PPDMIDISPLAYCONNECTOR pInterface, uint32_t fFlags, uint32_t aScreenId, uint32_t x, uint32_t y)
3606{
3607 LogFlowFunc(("\n"));
3608 LogRel2(("%s: fFlags=%RU32, aScreenId=%RU32, x=%RU32, y=%RU32\n",
3609 __PRETTY_FUNCTION__, fFlags, aScreenId, x, y));
3610
3611 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3612 Display *pThis = pDrv->pDisplay;
3613
3614 if (fFlags & VBVA_CURSOR_SCREEN_RELATIVE)
3615 {
3616 AssertReturnVoid(aScreenId < pThis->mcMonitors);
3617
3618 x += pThis->maFramebuffers[aScreenId].xOrigin;
3619 y += pThis->maFramebuffers[aScreenId].yOrigin;
3620 }
3621 ::FireCursorPositionChangedEvent(pThis->mParent->i_getEventSource(), RT_BOOL(fFlags & VBVA_CURSOR_VALID_DATA), x, y);
3622}
3623
3624#endif /* VBOX_WITH_HGSMI */
3625
3626/**
3627 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3628 */
3629DECLCALLBACK(void *) Display::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3630{
3631 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
3632 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3633 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
3634 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYCONNECTOR, &pDrv->IConnector);
3635 return NULL;
3636}
3637
3638
3639/**
3640 * @interface_method_impl{PDMDRVREG,pfnPowerOff,
3641 * Tries to ensure no client calls gets to HGCM or the VGA device from here on.}
3642 */
3643DECLCALLBACK(void) Display::i_drvPowerOff(PPDMDRVINS pDrvIns)
3644{
3645 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3646 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
3647
3648 /*
3649 * Do much of the work that i_drvDestruct does.
3650 */
3651 if (pThis->pUpPort)
3652 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
3653
3654 pThis->IConnector.pbData = NULL;
3655 pThis->IConnector.cbScanline = 0;
3656 pThis->IConnector.cBits = 32;
3657 pThis->IConnector.cx = 0;
3658 pThis->IConnector.cy = 0;
3659
3660 if (pThis->pDisplay)
3661 {
3662 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
3663#ifdef VBOX_WITH_RECORDING
3664 pThis->pDisplay->mParent->i_recordingStop();
3665#endif
3666#if defined(VBOX_WITH_VIDEOHWACCEL)
3667 pThis->pVBVACallbacks = NULL;
3668#endif
3669 }
3670}
3671
3672
3673/**
3674 * Destruct a display driver instance.
3675 *
3676 * @returns VBox status code.
3677 * @param pDrvIns The driver instance data.
3678 */
3679DECLCALLBACK(void) Display::i_drvDestruct(PPDMDRVINS pDrvIns)
3680{
3681 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3682 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3683 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
3684
3685 /*
3686 * We repeat much of what i_drvPowerOff does in case it wasn't called.
3687 * In addition we sever the connection between us and the display.
3688 */
3689 if (pThis->pUpPort)
3690 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
3691
3692 pThis->IConnector.pbData = NULL;
3693 pThis->IConnector.cbScanline = 0;
3694 pThis->IConnector.cBits = 32;
3695 pThis->IConnector.cx = 0;
3696 pThis->IConnector.cy = 0;
3697
3698 if (pThis->pDisplay)
3699 {
3700 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
3701#ifdef VBOX_WITH_RECORDING
3702 pThis->pDisplay->mParent->i_recordingStop();
3703#endif
3704#if defined(VBOX_WITH_VIDEOHWACCEL)
3705 pThis->pVBVACallbacks = NULL;
3706#endif
3707
3708 pThis->pDisplay->mpDrv = NULL;
3709 pThis->pDisplay = NULL;
3710 }
3711#if defined(VBOX_WITH_VIDEOHWACCEL)
3712 pThis->pVBVACallbacks = NULL;
3713#endif
3714}
3715
3716
3717/**
3718 * Construct a display driver instance.
3719 *
3720 * @copydoc FNPDMDRVCONSTRUCT
3721 */
3722DECLCALLBACK(int) Display::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
3723{
3724 RT_NOREF(fFlags);
3725 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3726 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3727 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
3728
3729 /*
3730 * Validate configuration.
3731 */
3732 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
3733 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
3734 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
3735 ("Configuration error: Not possible to attach anything to this driver!\n"),
3736 VERR_PDM_DRVINS_NO_ATTACH);
3737
3738 /*
3739 * Init Interfaces.
3740 */
3741 pDrvIns->IBase.pfnQueryInterface = Display::i_drvQueryInterface;
3742
3743 pThis->IConnector.pfnResize = Display::i_displayResizeCallback;
3744 pThis->IConnector.pfnUpdateRect = Display::i_displayUpdateCallback;
3745 pThis->IConnector.pfnRefresh = Display::i_displayRefreshCallback;
3746 pThis->IConnector.pfnReset = Display::i_displayResetCallback;
3747 pThis->IConnector.pfnLFBModeChange = Display::i_displayLFBModeChangeCallback;
3748 pThis->IConnector.pfnProcessAdapterData = Display::i_displayProcessAdapterDataCallback;
3749 pThis->IConnector.pfnProcessDisplayData = Display::i_displayProcessDisplayDataCallback;
3750#ifdef VBOX_WITH_VIDEOHWACCEL
3751 pThis->IConnector.pfnVHWACommandProcess = Display::i_displayVHWACommandProcess;
3752#endif
3753#ifdef VBOX_WITH_HGSMI
3754 pThis->IConnector.pfnVBVAEnable = Display::i_displayVBVAEnable;
3755 pThis->IConnector.pfnVBVADisable = Display::i_displayVBVADisable;
3756 pThis->IConnector.pfnVBVAUpdateBegin = Display::i_displayVBVAUpdateBegin;
3757 pThis->IConnector.pfnVBVAUpdateProcess = Display::i_displayVBVAUpdateProcess;
3758 pThis->IConnector.pfnVBVAUpdateEnd = Display::i_displayVBVAUpdateEnd;
3759 pThis->IConnector.pfnVBVAResize = Display::i_displayVBVAResize;
3760 pThis->IConnector.pfnVBVAMousePointerShape = Display::i_displayVBVAMousePointerShape;
3761 pThis->IConnector.pfnVBVAGuestCapabilityUpdate = Display::i_displayVBVAGuestCapabilityUpdate;
3762 pThis->IConnector.pfnVBVAInputMappingUpdate = Display::i_displayVBVAInputMappingUpdate;
3763 pThis->IConnector.pfnVBVAReportCursorPosition = Display::i_displayVBVAReportCursorPosition;
3764#endif
3765 pThis->IConnector.pfn3DNotifyProcess = Display::i_display3DNotifyProcess;
3766
3767 /*
3768 * Get the IDisplayPort interface of the above driver/device.
3769 */
3770 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
3771 if (!pThis->pUpPort)
3772 {
3773 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
3774 return VERR_PDM_MISSING_INTERFACE_ABOVE;
3775 }
3776#if defined(VBOX_WITH_VIDEOHWACCEL)
3777 pThis->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
3778 if (!pThis->pVBVACallbacks)
3779 {
3780 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
3781 return VERR_PDM_MISSING_INTERFACE_ABOVE;
3782 }
3783#endif
3784 /*
3785 * Get the Display object pointer and update the mpDrv member.
3786 */
3787 void *pv;
3788 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
3789 if (RT_FAILURE(rc))
3790 {
3791 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
3792 return rc;
3793 }
3794 Display *pDisplay = (Display *)pv; /** @todo Check this cast! */
3795 pThis->pDisplay = pDisplay;
3796 pThis->pDisplay->mpDrv = pThis;
3797
3798 /* Disable VRAM to a buffer copy initially. */
3799 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
3800 pThis->IConnector.cBits = 32; /* DevVGA does nothing otherwise. */
3801
3802 /*
3803 * Start periodic screen refreshes
3804 */
3805 pThis->pUpPort->pfnSetRefreshRate(pThis->pUpPort, 20);
3806
3807 return rc;
3808}
3809
3810
3811/**
3812 * Display driver registration record.
3813 */
3814const PDMDRVREG Display::DrvReg =
3815{
3816 /* u32Version */
3817 PDM_DRVREG_VERSION,
3818 /* szName */
3819 "MainDisplay",
3820 /* szRCMod */
3821 "",
3822 /* szR0Mod */
3823 "",
3824 /* pszDescription */
3825 "Main display driver (Main as in the API).",
3826 /* fFlags */
3827 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
3828 /* fClass. */
3829 PDM_DRVREG_CLASS_DISPLAY,
3830 /* cMaxInstances */
3831 ~0U,
3832 /* cbInstance */
3833 sizeof(DRVMAINDISPLAY),
3834 /* pfnConstruct */
3835 Display::i_drvConstruct,
3836 /* pfnDestruct */
3837 Display::i_drvDestruct,
3838 /* pfnRelocate */
3839 NULL,
3840 /* pfnIOCtl */
3841 NULL,
3842 /* pfnPowerOn */
3843 NULL,
3844 /* pfnReset */
3845 NULL,
3846 /* pfnSuspend */
3847 NULL,
3848 /* pfnResume */
3849 NULL,
3850 /* pfnAttach */
3851 NULL,
3852 /* pfnDetach */
3853 NULL,
3854 /* pfnPowerOff */
3855 Display::i_drvPowerOff,
3856 /* pfnSoftReset */
3857 NULL,
3858 /* u32EndVersion */
3859 PDM_DRVREG_VERSION
3860};
3861
3862/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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