VirtualBox

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

Last change on this file since 81355 was 80872, checked in by vboxsync, 5 years ago

Devices/Graphics,Main,include: remove obsolete Chromium code. bugref:9529

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