VirtualBox

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

Last change on this file since 80360 was 80360, checked in by vboxsync, 6 years ago

HostServices/SharedOpenGL: Remove most of the chromium related HGCM service bits leaving only the renderspu and OpenGLTest app for now as they are used for VMSVGA as well (OpenGLTest needs to be rewritten), bugref:9529

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 129.2 KB
Line 
1/* $Id: DisplayImpl.cpp 80360 2019-08-21 08:41:18Z 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.215389.xyz. 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 return E_NOTIMPL;
2448#endif
2449}
2450
2451HRESULT Display::viewportChanged(ULONG aScreenId, ULONG aX, ULONG aY, ULONG aWidth, ULONG aHeight)
2452{
2453 AssertMsgReturn(aScreenId < mcMonitors, ("aScreendId=%d mcMonitors=%d\n", aScreenId, mcMonitors), E_INVALIDARG);
2454
2455 /* The driver might not have been constructed yet */
2456 if (mpDrv && mpDrv->pUpPort->pfnSetViewport)
2457 mpDrv->pUpPort->pfnSetViewport(mpDrv->pUpPort, aScreenId, aX, aY, aWidth, aHeight);
2458
2459 return S_OK;
2460}
2461
2462HRESULT Display::querySourceBitmap(ULONG aScreenId,
2463 ComPtr<IDisplaySourceBitmap> &aDisplaySourceBitmap)
2464{
2465 LogRelFlowFunc(("aScreenId = %d\n", aScreenId));
2466
2467 Console::SafeVMPtr ptrVM(mParent);
2468 if (!ptrVM.isOk())
2469 return ptrVM.rc();
2470
2471 CHECK_CONSOLE_DRV(mpDrv);
2472
2473 bool fSetRenderVRAM = false;
2474 bool fInvalidate = false;
2475
2476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 if (aScreenId >= mcMonitors)
2479 return setError(E_INVALIDARG, tr("QuerySourceBitmap: Invalid screen %d (total %d)"),
2480 aScreenId, mcMonitors);
2481
2482 if (!mfSourceBitmapEnabled)
2483 {
2484 aDisplaySourceBitmap = NULL;
2485 return E_FAIL;
2486 }
2487
2488 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2489
2490 /* No source bitmap for a blank guest screen. */
2491 if (pFBInfo->flags & VBVA_SCREEN_F_BLANK)
2492 {
2493 aDisplaySourceBitmap = NULL;
2494 return E_FAIL;
2495 }
2496
2497 HRESULT hr = S_OK;
2498
2499 if (pFBInfo->pSourceBitmap.isNull())
2500 {
2501 /* Create a new object. */
2502 ComObjPtr<DisplaySourceBitmap> obj;
2503 hr = obj.createObject();
2504 if (SUCCEEDED(hr))
2505 hr = obj->init(this, aScreenId, pFBInfo);
2506
2507 if (SUCCEEDED(hr))
2508 {
2509 pFBInfo->pSourceBitmap = obj;
2510 pFBInfo->fDefaultFormat = !obj->i_usesVRAM();
2511
2512 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2513 {
2514 /* Start buffer updates. */
2515 BYTE *pAddress = NULL;
2516 ULONG ulWidth = 0;
2517 ULONG ulHeight = 0;
2518 ULONG ulBitsPerPixel = 0;
2519 ULONG ulBytesPerLine = 0;
2520 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2521
2522 pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
2523 &ulWidth,
2524 &ulHeight,
2525 &ulBitsPerPixel,
2526 &ulBytesPerLine,
2527 &bitmapFormat);
2528
2529 mpDrv->IConnector.pbData = pAddress;
2530 mpDrv->IConnector.cbScanline = ulBytesPerLine;
2531 mpDrv->IConnector.cBits = ulBitsPerPixel;
2532 mpDrv->IConnector.cx = ulWidth;
2533 mpDrv->IConnector.cy = ulHeight;
2534
2535 fSetRenderVRAM = pFBInfo->fDefaultFormat;
2536 }
2537
2538 /* Make sure that the bitmap contains the latest image. */
2539 fInvalidate = pFBInfo->fDefaultFormat;
2540 }
2541 }
2542
2543 if (SUCCEEDED(hr))
2544 {
2545 pFBInfo->pSourceBitmap.queryInterfaceTo(aDisplaySourceBitmap.asOutParam());
2546 }
2547
2548 /* Leave the IDisplay lock because the VGA device must not be called under it. */
2549 alock.release();
2550
2551 if (SUCCEEDED(hr))
2552 {
2553 if (fSetRenderVRAM)
2554 {
2555 mpDrv->pUpPort->pfnSetRenderVRAM(mpDrv->pUpPort, true);
2556 }
2557
2558 if (fInvalidate)
2559 VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY, (PFNRT)Display::i_InvalidateAndUpdateEMT,
2560 3, this, aScreenId, false);
2561 }
2562
2563 LogRelFlowFunc(("%Rhrc\n", hr));
2564 return hr;
2565}
2566
2567HRESULT Display::getGuestScreenLayout(std::vector<ComPtr<IGuestScreenInfo> > &aGuestScreenLayout)
2568{
2569 NOREF(aGuestScreenLayout);
2570 return E_NOTIMPL;
2571}
2572
2573HRESULT Display::setScreenLayout(ScreenLayoutMode_T aScreenLayoutMode,
2574 const std::vector<ComPtr<IGuestScreenInfo> > &aGuestScreenInfo)
2575{
2576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 if (aGuestScreenInfo.size() != mcMonitors)
2579 return E_INVALIDARG;
2580
2581 CHECK_CONSOLE_DRV(mpDrv);
2582
2583 /*
2584 * It is up to the guest to decide whether the hint is
2585 * valid. Therefore don't do any VRAM sanity checks here.
2586 */
2587
2588 /* Have to release the lock because the pfnRequestDisplayChange
2589 * will call EMT. */
2590 alock.release();
2591
2592 VMMDev *pVMMDev = mParent->i_getVMMDev();
2593 if (pVMMDev)
2594 {
2595 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
2596 if (pVMMDevPort)
2597 {
2598 uint32_t const cDisplays = (uint32_t)aGuestScreenInfo.size();
2599
2600 size_t const cbAlloc = cDisplays * sizeof(VMMDevDisplayDef);
2601 VMMDevDisplayDef *paDisplayDefs = (VMMDevDisplayDef *)RTMemAlloc(cbAlloc);
2602 if (paDisplayDefs)
2603 {
2604 for (uint32_t i = 0; i < cDisplays; ++i)
2605 {
2606 VMMDevDisplayDef *p = &paDisplayDefs[i];
2607 ComPtr<IGuestScreenInfo> pScreenInfo = aGuestScreenInfo[i];
2608
2609 ULONG screenId = 0;
2610 GuestMonitorStatus_T guestMonitorStatus = GuestMonitorStatus_Enabled;
2611 BOOL origin = FALSE;
2612 BOOL primary = FALSE;
2613 LONG originX = 0;
2614 LONG originY = 0;
2615 ULONG width = 0;
2616 ULONG height = 0;
2617 ULONG bitsPerPixel = 0;
2618
2619 pScreenInfo->COMGETTER(ScreenId) (&screenId);
2620 pScreenInfo->COMGETTER(GuestMonitorStatus)(&guestMonitorStatus);
2621 pScreenInfo->COMGETTER(Primary) (&primary);
2622 pScreenInfo->COMGETTER(Origin) (&origin);
2623 pScreenInfo->COMGETTER(OriginX) (&originX);
2624 pScreenInfo->COMGETTER(OriginY) (&originY);
2625 pScreenInfo->COMGETTER(Width) (&width);
2626 pScreenInfo->COMGETTER(Height) (&height);
2627 pScreenInfo->COMGETTER(BitsPerPixel)(&bitsPerPixel);
2628
2629 LogFlowFunc(("%d %d,%d %dx%d\n", screenId, originX, originY, width, height));
2630
2631 p->idDisplay = screenId;
2632 p->xOrigin = originX;
2633 p->yOrigin = originY;
2634 p->cx = width;
2635 p->cy = height;
2636 p->cBitsPerPixel = bitsPerPixel;
2637 p->fDisplayFlags = VMMDEV_DISPLAY_CX | VMMDEV_DISPLAY_CY | VMMDEV_DISPLAY_BPP;
2638 if (guestMonitorStatus == GuestMonitorStatus_Disabled)
2639 p->fDisplayFlags |= VMMDEV_DISPLAY_DISABLED;
2640 if (origin)
2641 p->fDisplayFlags |= VMMDEV_DISPLAY_ORIGIN;
2642 if (primary)
2643 p->fDisplayFlags |= VMMDEV_DISPLAY_PRIMARY;
2644 }
2645
2646 bool const fForce = aScreenLayoutMode == ScreenLayoutMode_Reset
2647 || aScreenLayoutMode == ScreenLayoutMode_Apply;
2648 bool const fNotify = aScreenLayoutMode != ScreenLayoutMode_Silent;
2649 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, cDisplays, paDisplayDefs, fForce, fNotify);
2650
2651 RTMemFree(paDisplayDefs);
2652 }
2653 }
2654 }
2655 return S_OK;
2656}
2657
2658HRESULT Display::detachScreens(const std::vector<LONG> &aScreenIds)
2659{
2660 NOREF(aScreenIds);
2661 return E_NOTIMPL;
2662}
2663
2664HRESULT Display::createGuestScreenInfo(ULONG aDisplay,
2665 GuestMonitorStatus_T aStatus,
2666 BOOL aPrimary,
2667 BOOL aChangeOrigin,
2668 LONG aOriginX,
2669 LONG aOriginY,
2670 ULONG aWidth,
2671 ULONG aHeight,
2672 ULONG aBitsPerPixel,
2673 ComPtr<IGuestScreenInfo> &aGuestScreenInfo)
2674{
2675 /* Create a new object. */
2676 ComObjPtr<GuestScreenInfo> obj;
2677 HRESULT hr = obj.createObject();
2678 if (SUCCEEDED(hr))
2679 hr = obj->init(aDisplay, aStatus, aPrimary, aChangeOrigin, aOriginX, aOriginY,
2680 aWidth, aHeight, aBitsPerPixel);
2681 if (SUCCEEDED(hr))
2682 obj.queryInterfaceTo(aGuestScreenInfo.asOutParam());
2683
2684 return hr;
2685}
2686
2687
2688/*
2689 * GuestScreenInfo implementation.
2690 */
2691DEFINE_EMPTY_CTOR_DTOR(GuestScreenInfo)
2692
2693HRESULT GuestScreenInfo::FinalConstruct()
2694{
2695 return BaseFinalConstruct();
2696}
2697
2698void GuestScreenInfo::FinalRelease()
2699{
2700 uninit();
2701
2702 BaseFinalRelease();
2703}
2704
2705HRESULT GuestScreenInfo::init(ULONG aDisplay,
2706 GuestMonitorStatus_T aGuestMonitorStatus,
2707 BOOL aPrimary,
2708 BOOL aChangeOrigin,
2709 LONG aOriginX,
2710 LONG aOriginY,
2711 ULONG aWidth,
2712 ULONG aHeight,
2713 ULONG aBitsPerPixel)
2714{
2715 LogFlowThisFunc(("[%u]\n", aDisplay));
2716
2717 /* Enclose the state transition NotReady->InInit->Ready */
2718 AutoInitSpan autoInitSpan(this);
2719 AssertReturn(autoInitSpan.isOk(), E_FAIL);
2720
2721 mScreenId = aDisplay;
2722 mGuestMonitorStatus = aGuestMonitorStatus;
2723 mPrimary = aPrimary;
2724 mOrigin = aChangeOrigin;
2725 mOriginX = aOriginX;
2726 mOriginY = aOriginY;
2727 mWidth = aWidth;
2728 mHeight = aHeight;
2729 mBitsPerPixel = aBitsPerPixel;
2730
2731 /* Confirm a successful initialization */
2732 autoInitSpan.setSucceeded();
2733
2734 return S_OK;
2735}
2736
2737void GuestScreenInfo::uninit()
2738{
2739 /* Enclose the state transition Ready->InUninit->NotReady */
2740 AutoUninitSpan autoUninitSpan(this);
2741 if (autoUninitSpan.uninitDone())
2742 return;
2743
2744 LogFlowThisFunc(("[%u]\n", mScreenId));
2745}
2746
2747HRESULT GuestScreenInfo::getScreenId(ULONG *aScreenId)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750 *aScreenId = mScreenId;
2751 return S_OK;
2752}
2753
2754HRESULT GuestScreenInfo::getGuestMonitorStatus(GuestMonitorStatus_T *aGuestMonitorStatus)
2755{
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757 *aGuestMonitorStatus = mGuestMonitorStatus;
2758 return S_OK;
2759}
2760
2761HRESULT GuestScreenInfo::getPrimary(BOOL *aPrimary)
2762{
2763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2764 *aPrimary = mPrimary;
2765 return S_OK;
2766}
2767
2768HRESULT GuestScreenInfo::getOrigin(BOOL *aOrigin)
2769{
2770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2771 *aOrigin = mOrigin;
2772 return S_OK;
2773}
2774
2775HRESULT GuestScreenInfo::getOriginX(LONG *aOriginX)
2776{
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778 *aOriginX = mOriginX;
2779 return S_OK;
2780}
2781
2782HRESULT GuestScreenInfo::getOriginY(LONG *aOriginY)
2783{
2784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2785 *aOriginY = mOriginY;
2786 return S_OK;
2787}
2788
2789HRESULT GuestScreenInfo::getWidth(ULONG *aWidth)
2790{
2791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2792 *aWidth = mWidth;
2793 return S_OK;
2794}
2795
2796HRESULT GuestScreenInfo::getHeight(ULONG *aHeight)
2797{
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799 *aHeight = mHeight;
2800 return S_OK;
2801}
2802
2803HRESULT GuestScreenInfo::getBitsPerPixel(ULONG *aBitsPerPixel)
2804{
2805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2806 *aBitsPerPixel = mBitsPerPixel;
2807 return S_OK;
2808}
2809
2810HRESULT GuestScreenInfo::getExtendedInfo(com::Utf8Str &aExtendedInfo)
2811{
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813 aExtendedInfo = com::Utf8Str();
2814 return S_OK;
2815}
2816
2817// wrapped IEventListener method
2818HRESULT Display::handleEvent(const ComPtr<IEvent> &aEvent)
2819{
2820 VBoxEventType_T aType = VBoxEventType_Invalid;
2821
2822 aEvent->COMGETTER(Type)(&aType);
2823 switch (aType)
2824 {
2825 case VBoxEventType_OnStateChanged:
2826 {
2827 ComPtr<IStateChangedEvent> scev = aEvent;
2828 Assert(scev);
2829 MachineState_T machineState;
2830 scev->COMGETTER(State)(&machineState);
2831 if ( machineState == MachineState_Running
2832 || machineState == MachineState_Teleporting
2833 || machineState == MachineState_LiveSnapshotting
2834 || machineState == MachineState_DeletingSnapshotOnline
2835 )
2836 {
2837 LogRelFlowFunc(("Machine is running.\n"));
2838
2839 }
2840 break;
2841 }
2842 default:
2843 AssertFailed();
2844 }
2845
2846 return S_OK;
2847}
2848
2849
2850// private methods
2851/////////////////////////////////////////////////////////////////////////////
2852
2853/**
2854 * Handle display resize event issued by the VGA device for the primary screen.
2855 *
2856 * @see PDMIDISPLAYCONNECTOR::pfnResize
2857 */
2858DECLCALLBACK(int) Display::i_displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
2859 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
2860{
2861 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2862 Display *pThis = pDrv->pDisplay;
2863
2864 LogRelFlowFunc(("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
2865 bpp, pvVRAM, cbLine, cx, cy));
2866
2867 bool f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, true, false);
2868 if (!f)
2869 {
2870 /* This is a result of recursive call when the source bitmap is being updated
2871 * during a VGA resize. Tell the VGA device to ignore the call.
2872 *
2873 * @todo It is a workaround, actually pfnUpdateDisplayAll must
2874 * fail on resize.
2875 */
2876 LogRel(("displayResizeCallback: already processing\n"));
2877 return VINF_VGA_RESIZE_IN_PROGRESS;
2878 }
2879
2880 int rc = pThis->i_handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy, 0, 0, 0, true);
2881
2882 /* Restore the flag. */
2883 f = ASMAtomicCmpXchgBool(&pThis->fVGAResizing, false, true);
2884 AssertRelease(f);
2885
2886 return rc;
2887}
2888
2889/**
2890 * Handle display update.
2891 *
2892 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
2893 */
2894DECLCALLBACK(void) Display::i_displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
2895 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
2896{
2897 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2898
2899#ifdef DEBUG_sunlover
2900 LogFlowFunc(("fVideoAccelEnabled = %d, %d,%d %dx%d\n",
2901 pDrv->pDisplay->mVideoAccelLegacy.fVideoAccelEnabled, x, y, cx, cy));
2902#endif /* DEBUG_sunlover */
2903
2904 /* This call does update regardless of VBVA status.
2905 * But in VBVA mode this is called only as result of
2906 * pfnUpdateDisplayAll in the VGA device.
2907 */
2908
2909 pDrv->pDisplay->i_handleDisplayUpdate(VBOX_VIDEO_PRIMARY_SCREEN, x, y, cx, cy);
2910}
2911
2912/**
2913 * Periodic display refresh callback.
2914 *
2915 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
2916 * @thread EMT
2917 */
2918/*static*/ DECLCALLBACK(void) Display::i_displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
2919{
2920 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2921
2922#ifdef DEBUG_sunlover_2
2923 LogFlowFunc(("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
2924 pDrv->pDisplay->mfVideoAccelEnabled));
2925#endif /* DEBUG_sunlover_2 */
2926
2927 Display *pDisplay = pDrv->pDisplay;
2928 unsigned uScreenId;
2929
2930 int rc = pDisplay->i_videoAccelRefreshProcess(pDrv->pUpPort);
2931 if (rc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
2932 {
2933 if (rc == VWRN_INVALID_STATE)
2934 {
2935 /* No VBVA do a display update. */
2936 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
2937 }
2938
2939 /* Inform the VRDP server that the current display update sequence is
2940 * completed. At this moment the framebuffer memory contains a definite
2941 * image, that is synchronized with the orders already sent to VRDP client.
2942 * The server can now process redraw requests from clients or initial
2943 * fullscreen updates for new clients.
2944 */
2945 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
2946 {
2947 Assert(pDisplay->mParent && pDisplay->mParent->i_consoleVRDPServer());
2948 pDisplay->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, NULL, 0);
2949 }
2950 }
2951
2952#ifdef VBOX_WITH_RECORDING
2953 AssertPtr(pDisplay->mParent);
2954 RecordingContext *pCtx = pDisplay->mParent->i_recordingGetContext();
2955
2956 if ( pCtx
2957 && pCtx->IsStarted()
2958 && pCtx->IsFeatureEnabled(RecordingFeature_Video))
2959 {
2960 do
2961 {
2962 /* If the recording context has reached the configured recording
2963 * limit, disable recording. */
2964 if (pCtx->IsLimitReached())
2965 {
2966 pDisplay->mParent->i_onRecordingChange(FALSE /* Disable */);
2967 break;
2968 }
2969
2970 uint64_t tsNowMs = RTTimeProgramMilliTS();
2971 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
2972 {
2973 if (!pDisplay->maRecordingEnabled[uScreenId])
2974 continue;
2975
2976 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2977 if (!pFBInfo->fDisabled)
2978 {
2979 ComPtr<IDisplaySourceBitmap> pSourceBitmap;
2980 int rc2 = RTCritSectEnter(&pDisplay->mVideoRecLock);
2981 if (RT_SUCCESS(rc2))
2982 {
2983 pSourceBitmap = pFBInfo->Recording.pSourceBitmap;
2984 RTCritSectLeave(&pDisplay->mVideoRecLock);
2985 }
2986
2987 if (!pSourceBitmap.isNull())
2988 {
2989 BYTE *pbAddress = NULL;
2990 ULONG ulWidth = 0;
2991 ULONG ulHeight = 0;
2992 ULONG ulBitsPerPixel = 0;
2993 ULONG ulBytesPerLine = 0;
2994 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
2995 HRESULT hr = pSourceBitmap->QueryBitmapInfo(&pbAddress,
2996 &ulWidth,
2997 &ulHeight,
2998 &ulBitsPerPixel,
2999 &ulBytesPerLine,
3000 &bitmapFormat);
3001 if (SUCCEEDED(hr) && pbAddress)
3002 rc = pCtx->SendVideoFrame(uScreenId, 0, 0, BitmapFormat_BGR,
3003 ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight,
3004 pbAddress, tsNowMs);
3005 else
3006 rc = VERR_NOT_SUPPORTED;
3007
3008 pSourceBitmap.setNull();
3009 }
3010 else
3011 rc = VERR_NOT_SUPPORTED;
3012
3013 if (rc == VINF_TRY_AGAIN)
3014 break;
3015 }
3016 }
3017 } while (0);
3018 }
3019#endif /* VBOX_WITH_RECORDING */
3020
3021#ifdef DEBUG_sunlover_2
3022 LogFlowFunc(("leave\n"));
3023#endif /* DEBUG_sunlover_2 */
3024}
3025
3026/**
3027 * Reset notification
3028 *
3029 * @see PDMIDISPLAYCONNECTOR::pfnReset
3030 */
3031DECLCALLBACK(void) Display::i_displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
3032{
3033 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3034
3035 LogRelFlowFunc(("\n"));
3036
3037 /* Disable VBVA mode. */
3038 pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
3039}
3040
3041/**
3042 * LFBModeChange notification
3043 *
3044 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
3045 */
3046DECLCALLBACK(void) Display::i_displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
3047{
3048 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3049
3050 LogRelFlowFunc(("fEnabled=%d\n", fEnabled));
3051
3052 NOREF(fEnabled);
3053
3054 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
3055 pDrv->pDisplay->VideoAccelEnableVGA(false, NULL);
3056}
3057
3058/**
3059 * Adapter information change notification.
3060 *
3061 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
3062 */
3063DECLCALLBACK(void) Display::i_displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM,
3064 uint32_t u32VRAMSize)
3065{
3066 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3067 pDrv->pDisplay->processAdapterData(pvVRAM, u32VRAMSize);
3068}
3069
3070/**
3071 * Display information change notification.
3072 *
3073 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
3074 */
3075DECLCALLBACK(void) Display::i_displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface,
3076 void *pvVRAM, unsigned uScreenId)
3077{
3078 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3079 pDrv->pDisplay->processDisplayData(pvVRAM, uScreenId);
3080}
3081
3082#ifdef VBOX_WITH_VIDEOHWACCEL
3083
3084int Display::i_handleVHWACommandProcess(int enmCmd, bool fGuestCmd, VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand)
3085{
3086 unsigned id = (unsigned)pCommand->iDisplay;
3087 if (id >= mcMonitors)
3088 return VERR_INVALID_PARAMETER;
3089
3090 ComPtr<IFramebuffer> pFramebuffer;
3091 AutoReadLock arlock(this COMMA_LOCKVAL_SRC_POS);
3092 pFramebuffer = maFramebuffers[id].pFramebuffer;
3093 bool fVHWASupported = RT_BOOL(maFramebuffers[id].u32Caps & FramebufferCapabilities_VHWA);
3094 arlock.release();
3095
3096 if (pFramebuffer == NULL || !fVHWASupported)
3097 return VERR_NOT_IMPLEMENTED; /* Implementation is not available. */
3098
3099 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE *)pCommand, enmCmd, fGuestCmd);
3100 if (hr == S_FALSE)
3101 return VINF_SUCCESS;
3102 if (SUCCEEDED(hr))
3103 return VINF_CALLBACK_RETURN;
3104 if (hr == E_ACCESSDENIED)
3105 return VERR_INVALID_STATE; /* notify we can not handle request atm */
3106 if (hr == E_NOTIMPL)
3107 return VERR_NOT_IMPLEMENTED;
3108 return VERR_GENERAL_FAILURE;
3109}
3110
3111DECLCALLBACK(int) Display::i_displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, int enmCmd, bool fGuestCmd,
3112 VBOXVHWACMD RT_UNTRUSTED_VOLATILE_GUEST *pCommand)
3113{
3114 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3115
3116 return pDrv->pDisplay->i_handleVHWACommandProcess(enmCmd, fGuestCmd, pCommand);
3117}
3118
3119#endif /* VBOX_WITH_VIDEOHWACCEL */
3120
3121HRESULT Display::notifyScaleFactorChange(ULONG aScreenId, ULONG aScaleFactorWMultiplied, ULONG aScaleFactorHMultiplied)
3122{
3123 RT_NOREF(aScreenId, aScaleFactorWMultiplied, aScaleFactorHMultiplied);
3124# if 0 /** @todo Thank you so very much from anyone using VMSVGA3d! */
3125 AssertMsgFailed(("Attempt to specify OpenGL content scale factor while 3D acceleration is disabled in VM config. Ignored.\n"));
3126# else
3127 /* Need an interface like this here (and the #ifdefs needs adjusting):
3128 PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
3129 if (pUpPort && pUpPort->pfnSetScaleFactor)
3130 pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
3131# endif
3132 return S_OK;
3133}
3134
3135HRESULT Display::notifyHiDPIOutputPolicyChange(BOOL fUnscaledHiDPI)
3136{
3137 RT_NOREF(fUnscaledHiDPI);
3138
3139 /* Need an interface like this here (and the #ifdefs needs adjusting):
3140 PPDMIDISPLAYPORT pUpPort = mpDrv ? mpDrv->pUpPort : NULL;
3141 if (pUpPort && pUpPort->pfnSetScaleFactor)
3142 pUpPort->pfnSetScaleFactor(pUpPort, aScreeId, aScaleFactorWMultiplied, aScaleFactorHMultiplied); */
3143
3144 return S_OK;
3145}
3146
3147#ifdef VBOX_WITH_HGSMI
3148/**
3149 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnVBVAEnable}
3150 */
3151DECLCALLBACK(int) Display::i_displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
3152 VBVAHOSTFLAGS RT_UNTRUSTED_VOLATILE_GUEST *pHostFlags,
3153 bool fRenderThreadMode)
3154{
3155 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
3156
3157 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3158 Display *pThis = pDrv->pDisplay;
3159
3160 if (pThis->maFramebuffers[uScreenId].fVBVAEnabled && pThis->maFramebuffers[uScreenId].fRenderThreadMode != fRenderThreadMode)
3161 {
3162 LogRel(("Enabling different vbva mode\n"));
3163#ifdef DEBUG_misha
3164 AssertMsgFailed(("enabling different vbva mode\n"));
3165#endif
3166 return VERR_INVALID_STATE;
3167 }
3168
3169 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
3170 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
3171 pThis->maFramebuffers[uScreenId].fRenderThreadMode = fRenderThreadMode;
3172 pThis->maFramebuffers[uScreenId].fVBVAForceResize = true;
3173
3174 vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
3175
3176 return VINF_SUCCESS;
3177}
3178
3179/**
3180 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnVBVADisable}
3181 */
3182DECLCALLBACK(void) Display::i_displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
3183{
3184 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
3185
3186 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3187 Display *pThis = pDrv->pDisplay;
3188
3189 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3190
3191 bool fRenderThreadMode = pFBInfo->fRenderThreadMode;
3192
3193 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3194 {
3195 /* Make sure that the primary screen is visible now.
3196 * The guest can't use VBVA anymore, so only only the VGA device output works.
3197 */
3198 pFBInfo->flags = 0;
3199 if (pFBInfo->fDisabled)
3200 {
3201 pFBInfo->fDisabled = false;
3202 fireGuestMonitorChangedEvent(pThis->mParent->i_getEventSource(),
3203 GuestMonitorChangedEventType_Enabled,
3204 uScreenId,
3205 pFBInfo->xOrigin, pFBInfo->yOrigin,
3206 pFBInfo->w, pFBInfo->h);
3207 }
3208 }
3209
3210 pFBInfo->fVBVAEnabled = false;
3211 pFBInfo->fVBVAForceResize = false;
3212 pFBInfo->fRenderThreadMode = false;
3213
3214 vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, pFBInfo);
3215
3216 pFBInfo->pVBVAHostFlags = NULL;
3217
3218 if (!fRenderThreadMode && uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3219 {
3220 /* Force full screen update, because VGA device must take control, do resize, etc. */
3221 pThis->mpDrv->pUpPort->pfnUpdateDisplayAll(pThis->mpDrv->pUpPort, /* fFailOnResize = */ false);
3222 }
3223}
3224
3225DECLCALLBACK(void) Display::i_displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
3226{
3227 RT_NOREF(uScreenId);
3228 LogFlowFunc(("uScreenId %d\n", uScreenId));
3229
3230 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3231 Display *pThis = pDrv->pDisplay;
3232
3233 if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
3234 {
3235 vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers,
3236 pThis->mcMonitors);
3237 ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
3238 }
3239}
3240
3241/**
3242 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnVBVAUpdateProcess}
3243 */
3244DECLCALLBACK(void) Display::i_displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId,
3245 struct VBVACMDHDR const RT_UNTRUSTED_VOLATILE_GUEST *pCmd, size_t cbCmd)
3246{
3247 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d, @%d,%d %dx%d\n", uScreenId, pCmd, cbCmd, pCmd->x, pCmd->y, pCmd->w, pCmd->h));
3248 VBVACMDHDR hdrSaved;
3249 RT_COPY_VOLATILE(hdrSaved, *pCmd);
3250 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
3251
3252 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3253 Display *pThis = pDrv->pDisplay;
3254 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3255
3256 if (pFBInfo->fDefaultFormat)
3257 {
3258 /* Make sure that framebuffer contains the same image as the guest VRAM. */
3259 if ( uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
3260 && !pFBInfo->fDisabled)
3261 {
3262 pDrv->pUpPort->pfnUpdateDisplayRect(pDrv->pUpPort, hdrSaved.x, hdrSaved.y, hdrSaved.w, hdrSaved.h);
3263 }
3264 else if ( !pFBInfo->pSourceBitmap.isNull()
3265 && !pFBInfo->fDisabled)
3266 {
3267 /* Render VRAM content to the framebuffer. */
3268 BYTE *pAddress = NULL;
3269 ULONG ulWidth = 0;
3270 ULONG ulHeight = 0;
3271 ULONG ulBitsPerPixel = 0;
3272 ULONG ulBytesPerLine = 0;
3273 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
3274
3275 HRESULT hrc = pFBInfo->pSourceBitmap->QueryBitmapInfo(&pAddress,
3276 &ulWidth,
3277 &ulHeight,
3278 &ulBitsPerPixel,
3279 &ulBytesPerLine,
3280 &bitmapFormat);
3281 if (SUCCEEDED(hrc))
3282 {
3283 uint32_t width = hdrSaved.w;
3284 uint32_t height = hdrSaved.h;
3285
3286 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
3287 int32_t xSrc = hdrSaved.x - pFBInfo->xOrigin;
3288 int32_t ySrc = hdrSaved.y - pFBInfo->yOrigin;
3289 uint32_t u32SrcWidth = pFBInfo->w;
3290 uint32_t u32SrcHeight = pFBInfo->h;
3291 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
3292 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3293
3294 uint8_t *pu8Dst = pAddress;
3295 int32_t xDst = xSrc;
3296 int32_t yDst = ySrc;
3297 uint32_t u32DstWidth = u32SrcWidth;
3298 uint32_t u32DstHeight = u32SrcHeight;
3299 uint32_t u32DstLineSize = u32DstWidth * 4;
3300 uint32_t u32DstBitsPerPixel = 32;
3301
3302 pDrv->pUpPort->pfnCopyRect(pDrv->pUpPort,
3303 width, height,
3304 pu8Src,
3305 xSrc, ySrc,
3306 u32SrcWidth, u32SrcHeight,
3307 u32SrcLineSize, u32SrcBitsPerPixel,
3308 pu8Dst,
3309 xDst, yDst,
3310 u32DstWidth, u32DstHeight,
3311 u32DstLineSize, u32DstBitsPerPixel);
3312 }
3313 }
3314 }
3315
3316 /*
3317 * Here is your classic 'temporary' solution.
3318 */
3319 /** @todo New SendUpdate entry which can get a separate cmd header or coords. */
3320 VBVACMDHDR *pHdrUnconst = (VBVACMDHDR *)pCmd;
3321
3322 pHdrUnconst->x -= (int16_t)pFBInfo->xOrigin;
3323 pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
3324
3325 pThis->mParent->i_consoleVRDPServer()->SendUpdate(uScreenId, pHdrUnconst, (uint32_t)cbCmd);
3326
3327 *pHdrUnconst = hdrSaved;
3328}
3329
3330DECLCALLBACK(void) Display::i_displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y,
3331 uint32_t cx, uint32_t cy)
3332{
3333 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
3334
3335 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3336 Display *pThis = pDrv->pDisplay;
3337 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3338
3339 /** @todo handleFramebufferUpdate (uScreenId,
3340 * x - pThis->maFramebuffers[uScreenId].xOrigin,
3341 * y - pThis->maFramebuffers[uScreenId].yOrigin,
3342 * cx, cy);
3343 */
3344 pThis->i_handleDisplayUpdate(uScreenId, x - pFBInfo->xOrigin, y - pFBInfo->yOrigin, cx, cy);
3345}
3346
3347#ifdef DEBUG_sunlover
3348static void logVBVAResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, const DISPLAYFBINFO *pFBInfo)
3349{
3350 LogRel(("displayVBVAResize: [%d] %s\n"
3351 " pView->u32ViewIndex %d\n"
3352 " pView->u32ViewOffset 0x%08X\n"
3353 " pView->u32ViewSize 0x%08X\n"
3354 " pView->u32MaxScreenSize 0x%08X\n"
3355 " pScreen->i32OriginX %d\n"
3356 " pScreen->i32OriginY %d\n"
3357 " pScreen->u32StartOffset 0x%08X\n"
3358 " pScreen->u32LineSize 0x%08X\n"
3359 " pScreen->u32Width %d\n"
3360 " pScreen->u32Height %d\n"
3361 " pScreen->u16BitsPerPixel %d\n"
3362 " pScreen->u16Flags 0x%04X\n"
3363 " pFBInfo->u32Offset 0x%08X\n"
3364 " pFBInfo->u32MaxFramebufferSize 0x%08X\n"
3365 " pFBInfo->u32InformationSize 0x%08X\n"
3366 " pFBInfo->fDisabled %d\n"
3367 " xOrigin, yOrigin, w, h: %d,%d %dx%d\n"
3368 " pFBInfo->u16BitsPerPixel %d\n"
3369 " pFBInfo->pu8FramebufferVRAM %p\n"
3370 " pFBInfo->u32LineSize 0x%08X\n"
3371 " pFBInfo->flags 0x%04X\n"
3372 " pFBInfo->pHostEvents %p\n"
3373 " pFBInfo->fDefaultFormat %d\n"
3374 " pFBInfo->fVBVAEnabled %d\n"
3375 " pFBInfo->fVBVAForceResize %d\n"
3376 " pFBInfo->pVBVAHostFlags %p\n"
3377 "",
3378 pScreen->u32ViewIndex,
3379 (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)? "DISABLED": "ENABLED",
3380 pView->u32ViewIndex,
3381 pView->u32ViewOffset,
3382 pView->u32ViewSize,
3383 pView->u32MaxScreenSize,
3384 pScreen->i32OriginX,
3385 pScreen->i32OriginY,
3386 pScreen->u32StartOffset,
3387 pScreen->u32LineSize,
3388 pScreen->u32Width,
3389 pScreen->u32Height,
3390 pScreen->u16BitsPerPixel,
3391 pScreen->u16Flags,
3392 pFBInfo->u32Offset,
3393 pFBInfo->u32MaxFramebufferSize,
3394 pFBInfo->u32InformationSize,
3395 pFBInfo->fDisabled,
3396 pFBInfo->xOrigin,
3397 pFBInfo->yOrigin,
3398 pFBInfo->w,
3399 pFBInfo->h,
3400 pFBInfo->u16BitsPerPixel,
3401 pFBInfo->pu8FramebufferVRAM,
3402 pFBInfo->u32LineSize,
3403 pFBInfo->flags,
3404 pFBInfo->pHostEvents,
3405 pFBInfo->fDefaultFormat,
3406 pFBInfo->fVBVAEnabled,
3407 pFBInfo->fVBVAForceResize,
3408 pFBInfo->pVBVAHostFlags
3409 ));
3410}
3411#endif /* DEBUG_sunlover */
3412
3413DECLCALLBACK(int) Display::i_displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, PCVBVAINFOVIEW pView,
3414 PCVBVAINFOSCREEN pScreen, void *pvVRAM, bool fResetInputMapping)
3415{
3416 LogRelFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
3417
3418 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3419 Display *pThis = pDrv->pDisplay;
3420
3421 return pThis->processVBVAResize(pView, pScreen, pvVRAM, fResetInputMapping);
3422}
3423
3424int Display::processVBVAResize(PCVBVAINFOVIEW pView, PCVBVAINFOSCREEN pScreen, void *pvVRAM, bool fResetInputMapping)
3425{
3426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3427
3428 RT_NOREF(pView);
3429
3430 DISPLAYFBINFO *pFBInfo = &maFramebuffers[pScreen->u32ViewIndex];
3431
3432 if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
3433 {
3434 /* Ask the framebuffer to resize using a default format. The framebuffer will be black.
3435 * So if the frontend does not support GuestMonitorChangedEventType_Disabled event,
3436 * the VM window will be black. */
3437 uint32_t u32Width = pFBInfo->w ? pFBInfo->w : 640;
3438 uint32_t u32Height = pFBInfo->h ? pFBInfo->h : 480;
3439 int32_t xOrigin = pFBInfo->xOrigin;
3440 int32_t yOrigin = pFBInfo->yOrigin;
3441
3442 alock.release();
3443
3444 i_handleDisplayResize(pScreen->u32ViewIndex, 0, (uint8_t *)NULL, 0,
3445 u32Width, u32Height, pScreen->u16Flags, xOrigin, yOrigin, false);
3446
3447 return VINF_SUCCESS;
3448 }
3449
3450 VBVAINFOSCREEN screenInfo;
3451 RT_ZERO(screenInfo);
3452
3453 if (pScreen->u16Flags & VBVA_SCREEN_F_BLANK2)
3454 {
3455 /* Init a local VBVAINFOSCREEN structure, which will be used instead of
3456 * the original pScreen. Set VBVA_SCREEN_F_BLANK, which will force
3457 * the code below to choose the "blanking" branches.
3458 */
3459 screenInfo.u32ViewIndex = pScreen->u32ViewIndex;
3460 screenInfo.i32OriginX = pFBInfo->xOrigin;
3461 screenInfo.i32OriginY = pFBInfo->yOrigin;
3462 screenInfo.u32StartOffset = 0; /* Irrelevant */
3463 screenInfo.u32LineSize = pFBInfo->u32LineSize;
3464 screenInfo.u32Width = pFBInfo->w;
3465 screenInfo.u32Height = pFBInfo->h;
3466 screenInfo.u16BitsPerPixel = pFBInfo->u16BitsPerPixel;
3467 screenInfo.u16Flags = pScreen->u16Flags | VBVA_SCREEN_F_BLANK;
3468
3469 pScreen = &screenInfo;
3470 }
3471
3472 if (fResetInputMapping)
3473 {
3474 /// @todo Rename to m* and verify whether some kind of lock is required.
3475 xInputMappingOrigin = 0;
3476 yInputMappingOrigin = 0;
3477 cxInputMapping = 0;
3478 cyInputMapping = 0;
3479 }
3480
3481 alock.release();
3482
3483 return i_handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
3484 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
3485 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height, pScreen->u16Flags,
3486 pScreen->i32OriginX, pScreen->i32OriginY, false);
3487}
3488
3489DECLCALLBACK(int) Display::i_displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
3490 uint32_t xHot, uint32_t yHot,
3491 uint32_t cx, uint32_t cy,
3492 const void *pvShape)
3493{
3494 LogFlowFunc(("\n"));
3495 LogRel2(("%s: fVisible=%RTbool\n", __PRETTY_FUNCTION__, fVisible));
3496
3497 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3498
3499 uint32_t cbShape = 0;
3500 if (pvShape)
3501 {
3502 cbShape = (cx + 7) / 8 * cy; /* size of the AND mask */
3503 cbShape = ((cbShape + 3) & ~3) + cx * 4 * cy; /* + gap + size of the XOR mask */
3504 }
3505
3506 /* Tell the console about it */
3507 pDrv->pDisplay->mParent->i_onMousePointerShapeChange(fVisible, fAlpha,
3508 xHot, yHot, cx, cy, (uint8_t *)pvShape, cbShape);
3509
3510 return VINF_SUCCESS;
3511}
3512
3513DECLCALLBACK(void) Display::i_displayVBVAGuestCapabilityUpdate(PPDMIDISPLAYCONNECTOR pInterface, uint32_t fCapabilities)
3514{
3515 LogFlowFunc(("\n"));
3516
3517 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3518 Display *pThis = pDrv->pDisplay;
3519
3520 pThis->i_handleUpdateGuestVBVACapabilities(fCapabilities);
3521}
3522
3523DECLCALLBACK(void) Display::i_displayVBVAInputMappingUpdate(PPDMIDISPLAYCONNECTOR pInterface, int32_t xOrigin, int32_t yOrigin,
3524 uint32_t cx, uint32_t cy)
3525{
3526 LogFlowFunc(("\n"));
3527
3528 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3529 Display *pThis = pDrv->pDisplay;
3530
3531 pThis->i_handleUpdateVBVAInputMapping(xOrigin, yOrigin, cx, cy);
3532}
3533
3534DECLCALLBACK(void) Display::i_displayVBVAReportCursorPosition(PPDMIDISPLAYCONNECTOR pInterface, uint32_t fFlags, uint32_t aScreenId, uint32_t x, uint32_t y)
3535{
3536 LogFlowFunc(("\n"));
3537 LogRel2(("%s: fFlags=%RU32, aScreenId=%RU32, x=%RU32, y=%RU32\n",
3538 __PRETTY_FUNCTION__, fFlags, aScreenId, x, y));
3539
3540 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3541 Display *pThis = pDrv->pDisplay;
3542
3543 if (fFlags & VBVA_CURSOR_SCREEN_RELATIVE)
3544 {
3545 x += pThis->maFramebuffers[aScreenId].xOrigin;
3546 y += pThis->maFramebuffers[aScreenId].yOrigin;
3547 }
3548 fireCursorPositionChangedEvent(pThis->mParent->i_getEventSource(), RT_BOOL(fFlags & VBVA_CURSOR_VALID_DATA), x, y);
3549}
3550
3551#endif /* VBOX_WITH_HGSMI */
3552
3553/**
3554 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
3555 */
3556DECLCALLBACK(void *) Display::i_drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
3557{
3558 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
3559 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3560 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
3561 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYCONNECTOR, &pDrv->IConnector);
3562 return NULL;
3563}
3564
3565
3566/**
3567 * @interface_method_impl{PDMDRVREG,pfnPowerOff,
3568 * Tries to ensure no client calls gets to HGCM or the VGA device from here on.}
3569 */
3570DECLCALLBACK(void) Display::i_drvPowerOff(PPDMDRVINS pDrvIns)
3571{
3572 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3573 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
3574
3575 /*
3576 * Do much of the work that i_drvDestruct does.
3577 */
3578 if (pThis->pUpPort)
3579 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
3580
3581 pThis->IConnector.pbData = NULL;
3582 pThis->IConnector.cbScanline = 0;
3583 pThis->IConnector.cBits = 32;
3584 pThis->IConnector.cx = 0;
3585 pThis->IConnector.cy = 0;
3586
3587 if (pThis->pDisplay)
3588 {
3589 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
3590#ifdef VBOX_WITH_RECORDING
3591 pThis->pDisplay->mParent->i_recordingStop();
3592#endif
3593#if defined(VBOX_WITH_VIDEOHWACCEL)
3594 pThis->pVBVACallbacks = NULL;
3595#endif
3596 }
3597}
3598
3599
3600/**
3601 * Destruct a display driver instance.
3602 *
3603 * @returns VBox status code.
3604 * @param pDrvIns The driver instance data.
3605 */
3606DECLCALLBACK(void) Display::i_drvDestruct(PPDMDRVINS pDrvIns)
3607{
3608 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
3609 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3610 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
3611
3612 /*
3613 * We repeat much of what i_drvPowerOff does in case it wasn't called.
3614 * In addition we sever the connection between us and the display.
3615 */
3616 if (pThis->pUpPort)
3617 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
3618
3619 pThis->IConnector.pbData = NULL;
3620 pThis->IConnector.cbScanline = 0;
3621 pThis->IConnector.cBits = 32;
3622 pThis->IConnector.cx = 0;
3623 pThis->IConnector.cy = 0;
3624
3625 if (pThis->pDisplay)
3626 {
3627 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS);
3628#ifdef VBOX_WITH_RECORDING
3629 pThis->pDisplay->mParent->i_recordingStop();
3630#endif
3631#if defined(VBOX_WITH_VIDEOHWACCEL)
3632 pThis->pVBVACallbacks = NULL;
3633#endif
3634
3635 pThis->pDisplay->mpDrv = NULL;
3636 pThis->pDisplay = NULL;
3637 }
3638#if defined(VBOX_WITH_VIDEOHWACCEL)
3639 pThis->pVBVACallbacks = NULL;
3640#endif
3641}
3642
3643
3644/**
3645 * Construct a display driver instance.
3646 *
3647 * @copydoc FNPDMDRVCONSTRUCT
3648 */
3649DECLCALLBACK(int) Display::i_drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
3650{
3651 RT_NOREF(fFlags);
3652 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
3653 PDRVMAINDISPLAY pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
3654 LogRelFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
3655
3656 /*
3657 * Validate configuration.
3658 */
3659 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
3660 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
3661 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
3662 ("Configuration error: Not possible to attach anything to this driver!\n"),
3663 VERR_PDM_DRVINS_NO_ATTACH);
3664
3665 /*
3666 * Init Interfaces.
3667 */
3668 pDrvIns->IBase.pfnQueryInterface = Display::i_drvQueryInterface;
3669
3670 pThis->IConnector.pfnResize = Display::i_displayResizeCallback;
3671 pThis->IConnector.pfnUpdateRect = Display::i_displayUpdateCallback;
3672 pThis->IConnector.pfnRefresh = Display::i_displayRefreshCallback;
3673 pThis->IConnector.pfnReset = Display::i_displayResetCallback;
3674 pThis->IConnector.pfnLFBModeChange = Display::i_displayLFBModeChangeCallback;
3675 pThis->IConnector.pfnProcessAdapterData = Display::i_displayProcessAdapterDataCallback;
3676 pThis->IConnector.pfnProcessDisplayData = Display::i_displayProcessDisplayDataCallback;
3677#ifdef VBOX_WITH_VIDEOHWACCEL
3678 pThis->IConnector.pfnVHWACommandProcess = Display::i_displayVHWACommandProcess;
3679#endif
3680#ifdef VBOX_WITH_HGSMI
3681 pThis->IConnector.pfnVBVAEnable = Display::i_displayVBVAEnable;
3682 pThis->IConnector.pfnVBVADisable = Display::i_displayVBVADisable;
3683 pThis->IConnector.pfnVBVAUpdateBegin = Display::i_displayVBVAUpdateBegin;
3684 pThis->IConnector.pfnVBVAUpdateProcess = Display::i_displayVBVAUpdateProcess;
3685 pThis->IConnector.pfnVBVAUpdateEnd = Display::i_displayVBVAUpdateEnd;
3686 pThis->IConnector.pfnVBVAResize = Display::i_displayVBVAResize;
3687 pThis->IConnector.pfnVBVAMousePointerShape = Display::i_displayVBVAMousePointerShape;
3688 pThis->IConnector.pfnVBVAGuestCapabilityUpdate = Display::i_displayVBVAGuestCapabilityUpdate;
3689 pThis->IConnector.pfnVBVAInputMappingUpdate = Display::i_displayVBVAInputMappingUpdate;
3690 pThis->IConnector.pfnVBVAReportCursorPosition = Display::i_displayVBVAReportCursorPosition;
3691#endif
3692
3693 /*
3694 * Get the IDisplayPort interface of the above driver/device.
3695 */
3696 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
3697 if (!pThis->pUpPort)
3698 {
3699 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
3700 return VERR_PDM_MISSING_INTERFACE_ABOVE;
3701 }
3702#if defined(VBOX_WITH_VIDEOHWACCEL)
3703 pThis->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
3704 if (!pThis->pVBVACallbacks)
3705 {
3706 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
3707 return VERR_PDM_MISSING_INTERFACE_ABOVE;
3708 }
3709#endif
3710 /*
3711 * Get the Display object pointer and update the mpDrv member.
3712 */
3713 void *pv;
3714 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
3715 if (RT_FAILURE(rc))
3716 {
3717 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
3718 return rc;
3719 }
3720 Display *pDisplay = (Display *)pv; /** @todo Check this cast! */
3721 pThis->pDisplay = pDisplay;
3722 pThis->pDisplay->mpDrv = pThis;
3723
3724 /* Disable VRAM to a buffer copy initially. */
3725 pThis->pUpPort->pfnSetRenderVRAM(pThis->pUpPort, false);
3726 pThis->IConnector.cBits = 32; /* DevVGA does nothing otherwise. */
3727
3728 /*
3729 * Start periodic screen refreshes
3730 */
3731 pThis->pUpPort->pfnSetRefreshRate(pThis->pUpPort, 20);
3732
3733 return rc;
3734}
3735
3736
3737/**
3738 * Display driver registration record.
3739 */
3740const PDMDRVREG Display::DrvReg =
3741{
3742 /* u32Version */
3743 PDM_DRVREG_VERSION,
3744 /* szName */
3745 "MainDisplay",
3746 /* szRCMod */
3747 "",
3748 /* szR0Mod */
3749 "",
3750 /* pszDescription */
3751 "Main display driver (Main as in the API).",
3752 /* fFlags */
3753 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
3754 /* fClass. */
3755 PDM_DRVREG_CLASS_DISPLAY,
3756 /* cMaxInstances */
3757 ~0U,
3758 /* cbInstance */
3759 sizeof(DRVMAINDISPLAY),
3760 /* pfnConstruct */
3761 Display::i_drvConstruct,
3762 /* pfnDestruct */
3763 Display::i_drvDestruct,
3764 /* pfnRelocate */
3765 NULL,
3766 /* pfnIOCtl */
3767 NULL,
3768 /* pfnPowerOn */
3769 NULL,
3770 /* pfnReset */
3771 NULL,
3772 /* pfnSuspend */
3773 NULL,
3774 /* pfnResume */
3775 NULL,
3776 /* pfnAttach */
3777 NULL,
3778 /* pfnDetach */
3779 NULL,
3780 /* pfnPowerOff */
3781 Display::i_drvPowerOff,
3782 /* pfnSoftReset */
3783 NULL,
3784 /* u32EndVersion */
3785 PDM_DRVREG_VERSION
3786};
3787
3788/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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