VirtualBox

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

Last change on this file since 83640 was 83622, checked in by vboxsync, 5 years ago

Main: disable the legacy VHWA interface. bugref:9691

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