VirtualBox

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

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

Main/DisplayImpl: Build fix if VBOX_WITH_VIDEOHWACCEL is not defined.

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