VirtualBox

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

Last change on this file since 42248 was 42248, checked in by vboxsync, 13 years ago

API change for SetVideoModeHint to be able to disable guest screens and to set the origin of guest screens

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 141.1 KB
Line 
1/* $Id: DisplayImpl.cpp 42248 2012-07-20 08:39:45Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2012 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#include "DisplayImpl.h"
19#include "DisplayUtils.h"
20#include "ConsoleImpl.h"
21#include "ConsoleVRDPServer.h"
22#include "VMMDev.h"
23
24#include "AutoCaller.h"
25#include "Logging.h"
26
27/* generated header */
28#include "VBoxEvents.h"
29
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32#include <iprt/asm.h>
33#include <iprt/cpp/utils.h>
34
35#include <VBox/vmm/pdmdrv.h>
36#ifdef DEBUG /* for VM_ASSERT_EMT(). */
37# include <VBox/vmm/vm.h>
38#endif
39
40#ifdef VBOX_WITH_VIDEOHWACCEL
41# include <VBox/VBoxVideo.h>
42#endif
43
44#if defined(VBOX_WITH_CROGL) || defined(VBOX_WITH_CRHGSMI)
45# include <VBox/HostServices/VBoxCrOpenGLSvc.h>
46#endif
47
48#include <VBox/com/array.h>
49
50/**
51 * Display driver instance data.
52 *
53 * @implements PDMIDISPLAYCONNECTOR
54 */
55typedef struct DRVMAINDISPLAY
56{
57 /** Pointer to the display object. */
58 Display *pDisplay;
59 /** Pointer to the driver instance structure. */
60 PPDMDRVINS pDrvIns;
61 /** Pointer to the keyboard port interface of the driver/device above us. */
62 PPDMIDISPLAYPORT pUpPort;
63 /** Our display connector interface. */
64 PDMIDISPLAYCONNECTOR IConnector;
65#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
66 /** VBVA callbacks */
67 PPDMIDISPLAYVBVACALLBACKS pVBVACallbacks;
68#endif
69} DRVMAINDISPLAY, *PDRVMAINDISPLAY;
70
71/** Converts PDMIDISPLAYCONNECTOR pointer to a DRVMAINDISPLAY pointer. */
72#define PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface) RT_FROM_MEMBER(pInterface, DRVMAINDISPLAY, IConnector)
73
74#ifdef DEBUG_sunlover
75static STAMPROFILE StatDisplayRefresh;
76static int stam = 0;
77#endif /* DEBUG_sunlover */
78
79// constructor / destructor
80/////////////////////////////////////////////////////////////////////////////
81
82Display::Display()
83 : mParent(NULL)
84{
85}
86
87Display::~Display()
88{
89}
90
91
92HRESULT Display::FinalConstruct()
93{
94 mpVbvaMemory = NULL;
95 mfVideoAccelEnabled = false;
96 mfVideoAccelVRDP = false;
97 mfu32SupportedOrders = 0;
98 mcVideoAccelVRDPRefs = 0;
99
100 mpPendingVbvaMemory = NULL;
101 mfPendingVideoAccelEnable = false;
102
103 mfMachineRunning = false;
104
105 mpu8VbvaPartial = NULL;
106 mcbVbvaPartial = 0;
107
108 mpDrv = NULL;
109 mpVMMDev = NULL;
110 mfVMMDevInited = false;
111
112 mLastAddress = NULL;
113 mLastBytesPerLine = 0;
114 mLastBitsPerPixel = 0,
115 mLastWidth = 0;
116 mLastHeight = 0;
117
118 int rc = RTCritSectInit(&mVBVALock);
119 AssertRC(rc);
120 mfu32PendingVideoAccelDisable = false;
121
122#ifdef VBOX_WITH_HGSMI
123 mu32UpdateVBVAFlags = 0;
124#endif
125
126 return BaseFinalConstruct();
127}
128
129void Display::FinalRelease()
130{
131 uninit();
132
133 if (RTCritSectIsInitialized (&mVBVALock))
134 {
135 RTCritSectDelete (&mVBVALock);
136 memset (&mVBVALock, 0, sizeof (mVBVALock));
137 }
138 BaseFinalRelease();
139}
140
141// public initializer/uninitializer for internal purposes only
142/////////////////////////////////////////////////////////////////////////////
143
144#define kMaxSizeThumbnail 64
145
146/**
147 * Save thumbnail and screenshot of the guest screen.
148 */
149static int displayMakeThumbnail(uint8_t *pu8Data, uint32_t cx, uint32_t cy,
150 uint8_t **ppu8Thumbnail, uint32_t *pcbThumbnail, uint32_t *pcxThumbnail, uint32_t *pcyThumbnail)
151{
152 int rc = VINF_SUCCESS;
153
154 uint8_t *pu8Thumbnail = NULL;
155 uint32_t cbThumbnail = 0;
156 uint32_t cxThumbnail = 0;
157 uint32_t cyThumbnail = 0;
158
159 if (cx > cy)
160 {
161 cxThumbnail = kMaxSizeThumbnail;
162 cyThumbnail = (kMaxSizeThumbnail * cy) / cx;
163 }
164 else
165 {
166 cyThumbnail = kMaxSizeThumbnail;
167 cxThumbnail = (kMaxSizeThumbnail * cx) / cy;
168 }
169
170 LogRelFlowFunc(("%dx%d -> %dx%d\n", cx, cy, cxThumbnail, cyThumbnail));
171
172 cbThumbnail = cxThumbnail * 4 * cyThumbnail;
173 pu8Thumbnail = (uint8_t *)RTMemAlloc(cbThumbnail);
174
175 if (pu8Thumbnail)
176 {
177 uint8_t *dst = pu8Thumbnail;
178 uint8_t *src = pu8Data;
179 int dstW = cxThumbnail;
180 int dstH = cyThumbnail;
181 int srcW = cx;
182 int srcH = cy;
183 int iDeltaLine = cx * 4;
184
185 BitmapScale32 (dst,
186 dstW, dstH,
187 src,
188 iDeltaLine,
189 srcW, srcH);
190
191 *ppu8Thumbnail = pu8Thumbnail;
192 *pcbThumbnail = cbThumbnail;
193 *pcxThumbnail = cxThumbnail;
194 *pcyThumbnail = cyThumbnail;
195 }
196 else
197 {
198 rc = VERR_NO_MEMORY;
199 }
200
201 return rc;
202}
203
204DECLCALLBACK(void)
205Display::displaySSMSaveScreenshot(PSSMHANDLE pSSM, void *pvUser)
206{
207 Display *that = static_cast<Display*>(pvUser);
208
209 /* 32bpp small RGB image. */
210 uint8_t *pu8Thumbnail = NULL;
211 uint32_t cbThumbnail = 0;
212 uint32_t cxThumbnail = 0;
213 uint32_t cyThumbnail = 0;
214
215 /* PNG screenshot. */
216 uint8_t *pu8PNG = NULL;
217 uint32_t cbPNG = 0;
218 uint32_t cxPNG = 0;
219 uint32_t cyPNG = 0;
220
221 Console::SafeVMPtr pVM (that->mParent);
222 if (SUCCEEDED(pVM.rc()))
223 {
224 /* Query RGB bitmap. */
225 uint8_t *pu8Data = NULL;
226 size_t cbData = 0;
227 uint32_t cx = 0;
228 uint32_t cy = 0;
229
230 /* SSM code is executed on EMT(0), therefore no need to use VMR3ReqCallWait. */
231 int rc = Display::displayTakeScreenshotEMT(that, VBOX_VIDEO_PRIMARY_SCREEN, &pu8Data, &cbData, &cx, &cy);
232
233 /*
234 * It is possible that success is returned but everything is 0 or NULL.
235 * (no display attached if a VM is running with VBoxHeadless on OSE for example)
236 */
237 if (RT_SUCCESS(rc) && pu8Data)
238 {
239 Assert(cx && cy);
240
241 /* Prepare a small thumbnail and a PNG screenshot. */
242 displayMakeThumbnail(pu8Data, cx, cy, &pu8Thumbnail, &cbThumbnail, &cxThumbnail, &cyThumbnail);
243 rc = DisplayMakePNG(pu8Data, cx, cy, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 1);
244 if (RT_FAILURE(rc))
245 {
246 if (pu8PNG)
247 {
248 RTMemFree(pu8PNG);
249 pu8PNG = NULL;
250 }
251 cbPNG = 0;
252 cxPNG = 0;
253 cyPNG = 0;
254 }
255
256 /* This can be called from any thread. */
257 that->mpDrv->pUpPort->pfnFreeScreenshot(that->mpDrv->pUpPort, pu8Data);
258 }
259 }
260 else
261 {
262 LogFunc(("Failed to get VM pointer 0x%x\n", pVM.rc()));
263 }
264
265 /* Regardless of rc, save what is available:
266 * Data format:
267 * uint32_t cBlocks;
268 * [blocks]
269 *
270 * Each block is:
271 * uint32_t cbBlock; if 0 - no 'block data'.
272 * uint32_t typeOfBlock; 0 - 32bpp RGB bitmap, 1 - PNG, ignored if 'cbBlock' is 0.
273 * [block data]
274 *
275 * Block data for bitmap and PNG:
276 * uint32_t cx;
277 * uint32_t cy;
278 * [image data]
279 */
280 SSMR3PutU32(pSSM, 2); /* Write thumbnail and PNG screenshot. */
281
282 /* First block. */
283 SSMR3PutU32(pSSM, cbThumbnail + 2 * sizeof (uint32_t));
284 SSMR3PutU32(pSSM, 0); /* Block type: thumbnail. */
285
286 if (cbThumbnail)
287 {
288 SSMR3PutU32(pSSM, cxThumbnail);
289 SSMR3PutU32(pSSM, cyThumbnail);
290 SSMR3PutMem(pSSM, pu8Thumbnail, cbThumbnail);
291 }
292
293 /* Second block. */
294 SSMR3PutU32(pSSM, cbPNG + 2 * sizeof (uint32_t));
295 SSMR3PutU32(pSSM, 1); /* Block type: png. */
296
297 if (cbPNG)
298 {
299 SSMR3PutU32(pSSM, cxPNG);
300 SSMR3PutU32(pSSM, cyPNG);
301 SSMR3PutMem(pSSM, pu8PNG, cbPNG);
302 }
303
304 RTMemFree(pu8PNG);
305 RTMemFree(pu8Thumbnail);
306}
307
308DECLCALLBACK(int)
309Display::displaySSMLoadScreenshot(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
310{
311 Display *that = static_cast<Display*>(pvUser);
312
313 if (uVersion != sSSMDisplayScreenshotVer)
314 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
315 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
316
317 /* Skip data. */
318 uint32_t cBlocks;
319 int rc = SSMR3GetU32(pSSM, &cBlocks);
320 AssertRCReturn(rc, rc);
321
322 for (uint32_t i = 0; i < cBlocks; i++)
323 {
324 uint32_t cbBlock;
325 rc = SSMR3GetU32(pSSM, &cbBlock);
326 AssertRCBreak(rc);
327
328 uint32_t typeOfBlock;
329 rc = SSMR3GetU32(pSSM, &typeOfBlock);
330 AssertRCBreak(rc);
331
332 LogRelFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
333
334 /* Note: displaySSMSaveScreenshot writes size of a block = 8 and
335 * do not write any data if the image size was 0.
336 * @todo Fix and increase saved state version.
337 */
338 if (cbBlock > 2 * sizeof (uint32_t))
339 {
340 rc = SSMR3Skip(pSSM, cbBlock);
341 AssertRCBreak(rc);
342 }
343 }
344
345 return rc;
346}
347
348/**
349 * Save/Load some important guest state
350 */
351DECLCALLBACK(void)
352Display::displaySSMSave(PSSMHANDLE pSSM, void *pvUser)
353{
354 Display *that = static_cast<Display*>(pvUser);
355
356 SSMR3PutU32(pSSM, that->mcMonitors);
357 for (unsigned i = 0; i < that->mcMonitors; i++)
358 {
359 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32Offset);
360 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32MaxFramebufferSize);
361 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32InformationSize);
362 SSMR3PutU32(pSSM, that->maFramebuffers[i].w);
363 SSMR3PutU32(pSSM, that->maFramebuffers[i].h);
364 SSMR3PutS32(pSSM, that->maFramebuffers[i].xOrigin);
365 SSMR3PutS32(pSSM, that->maFramebuffers[i].yOrigin);
366 SSMR3PutU32(pSSM, that->maFramebuffers[i].flags);
367 }
368}
369
370DECLCALLBACK(int)
371Display::displaySSMLoad(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
372{
373 Display *that = static_cast<Display*>(pvUser);
374
375 if (!( uVersion == sSSMDisplayVer
376 || uVersion == sSSMDisplayVer2
377 || uVersion == sSSMDisplayVer3))
378 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
379 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
380
381 uint32_t cMonitors;
382 int rc = SSMR3GetU32(pSSM, &cMonitors);
383 if (cMonitors != that->mcMonitors)
384 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Number of monitors changed (%d->%d)!"), cMonitors, that->mcMonitors);
385
386 for (uint32_t i = 0; i < cMonitors; i++)
387 {
388 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32Offset);
389 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32MaxFramebufferSize);
390 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32InformationSize);
391 if ( uVersion == sSSMDisplayVer2
392 || uVersion == sSSMDisplayVer3)
393 {
394 uint32_t w;
395 uint32_t h;
396 SSMR3GetU32(pSSM, &w);
397 SSMR3GetU32(pSSM, &h);
398 that->maFramebuffers[i].w = w;
399 that->maFramebuffers[i].h = h;
400 }
401 if (uVersion == sSSMDisplayVer3)
402 {
403 int32_t xOrigin;
404 int32_t yOrigin;
405 uint32_t flags;
406 SSMR3GetS32(pSSM, &xOrigin);
407 SSMR3GetS32(pSSM, &yOrigin);
408 SSMR3GetU32(pSSM, &flags);
409 that->maFramebuffers[i].xOrigin = xOrigin;
410 that->maFramebuffers[i].yOrigin = yOrigin;
411 that->maFramebuffers[i].flags = (uint16_t)flags;
412 that->maFramebuffers[i].fDisabled = (that->maFramebuffers[i].flags & VBVA_SCREEN_F_DISABLED) != 0;
413 }
414 }
415
416 return VINF_SUCCESS;
417}
418
419/**
420 * Initializes the display object.
421 *
422 * @returns COM result indicator
423 * @param parent handle of our parent object
424 * @param qemuConsoleData address of common console data structure
425 */
426HRESULT Display::init (Console *aParent)
427{
428 LogRelFlowFunc(("this=%p: aParent=%p\n", this, aParent));
429
430 ComAssertRet(aParent, E_INVALIDARG);
431
432 /* Enclose the state transition NotReady->InInit->Ready */
433 AutoInitSpan autoInitSpan(this);
434 AssertReturn(autoInitSpan.isOk(), E_FAIL);
435
436 unconst(mParent) = aParent;
437
438 // by default, we have an internal framebuffer which is
439 // NULL, i.e. a black hole for no display output
440 mFramebufferOpened = false;
441
442 ULONG ul;
443 mParent->machine()->COMGETTER(MonitorCount)(&ul);
444 mcMonitors = ul;
445
446 for (ul = 0; ul < mcMonitors; ul++)
447 {
448 maFramebuffers[ul].u32Offset = 0;
449 maFramebuffers[ul].u32MaxFramebufferSize = 0;
450 maFramebuffers[ul].u32InformationSize = 0;
451
452 maFramebuffers[ul].pFramebuffer = NULL;
453 /* All secondary monitors are disabled at startup. */
454 maFramebuffers[ul].fDisabled = ul > 0;
455
456 maFramebuffers[ul].xOrigin = 0;
457 maFramebuffers[ul].yOrigin = 0;
458
459 maFramebuffers[ul].w = 0;
460 maFramebuffers[ul].h = 0;
461
462 maFramebuffers[ul].flags = maFramebuffers[ul].fDisabled? VBVA_SCREEN_F_DISABLED: 0;
463
464 maFramebuffers[ul].u16BitsPerPixel = 0;
465 maFramebuffers[ul].pu8FramebufferVRAM = NULL;
466 maFramebuffers[ul].u32LineSize = 0;
467
468 maFramebuffers[ul].pHostEvents = NULL;
469
470 maFramebuffers[ul].u32ResizeStatus = ResizeStatus_Void;
471
472 maFramebuffers[ul].fDefaultFormat = false;
473
474 memset (&maFramebuffers[ul].dirtyRect, 0 , sizeof (maFramebuffers[ul].dirtyRect));
475 memset (&maFramebuffers[ul].pendingResize, 0 , sizeof (maFramebuffers[ul].pendingResize));
476#ifdef VBOX_WITH_HGSMI
477 maFramebuffers[ul].fVBVAEnabled = false;
478 maFramebuffers[ul].cVBVASkipUpdate = 0;
479 memset (&maFramebuffers[ul].vbvaSkippedRect, 0, sizeof (maFramebuffers[ul].vbvaSkippedRect));
480 maFramebuffers[ul].pVBVAHostFlags = NULL;
481#endif /* VBOX_WITH_HGSMI */
482 }
483
484 {
485 // register listener for state change events
486 ComPtr<IEventSource> es;
487 mParent->COMGETTER(EventSource)(es.asOutParam());
488 com::SafeArray <VBoxEventType_T> eventTypes;
489 eventTypes.push_back(VBoxEventType_OnStateChanged);
490 es->RegisterListener(this, ComSafeArrayAsInParam(eventTypes), true);
491 }
492
493 /* Confirm a successful initialization */
494 autoInitSpan.setSucceeded();
495
496 return S_OK;
497}
498
499/**
500 * Uninitializes the instance and sets the ready flag to FALSE.
501 * Called either from FinalRelease() or by the parent when it gets destroyed.
502 */
503void Display::uninit()
504{
505 LogRelFlowFunc(("this=%p\n", this));
506
507 /* Enclose the state transition Ready->InUninit->NotReady */
508 AutoUninitSpan autoUninitSpan(this);
509 if (autoUninitSpan.uninitDone())
510 return;
511
512 ULONG ul;
513 for (ul = 0; ul < mcMonitors; ul++)
514 maFramebuffers[ul].pFramebuffer = NULL;
515
516 if (mParent)
517 {
518 ComPtr<IEventSource> es;
519 mParent->COMGETTER(EventSource)(es.asOutParam());
520 es->UnregisterListener(this);
521 }
522
523 unconst(mParent) = NULL;
524
525 if (mpDrv)
526 mpDrv->pDisplay = NULL;
527
528 mpDrv = NULL;
529 mpVMMDev = NULL;
530 mfVMMDevInited = true;
531}
532
533/**
534 * Register the SSM methods. Called by the power up thread to be able to
535 * pass pVM
536 */
537int Display::registerSSM(PVM pVM)
538{
539 /* Version 2 adds width and height of the framebuffer; version 3 adds
540 * the framebuffer offset in the virtual desktop and the framebuffer flags.
541 */
542 int rc = SSMR3RegisterExternal(pVM, "DisplayData", 0, sSSMDisplayVer3,
543 mcMonitors * sizeof(uint32_t) * 8 + sizeof(uint32_t),
544 NULL, NULL, NULL,
545 NULL, displaySSMSave, NULL,
546 NULL, displaySSMLoad, NULL, this);
547 AssertRCReturn(rc, rc);
548
549 /*
550 * Register loaders for old saved states where iInstance was
551 * 3 * sizeof(uint32_t *) due to a code mistake.
552 */
553 rc = SSMR3RegisterExternal(pVM, "DisplayData", 12 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
554 NULL, NULL, NULL,
555 NULL, NULL, NULL,
556 NULL, displaySSMLoad, NULL, this);
557 AssertRCReturn(rc, rc);
558
559 rc = SSMR3RegisterExternal(pVM, "DisplayData", 24 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
560 NULL, NULL, NULL,
561 NULL, NULL, NULL,
562 NULL, displaySSMLoad, NULL, this);
563 AssertRCReturn(rc, rc);
564
565 /* uInstance is an arbitrary value greater than 1024. Such a value will ensure a quick seek in saved state file. */
566 rc = SSMR3RegisterExternal(pVM, "DisplayScreenshot", 1100 /*uInstance*/, sSSMDisplayScreenshotVer, 0 /*cbGuess*/,
567 NULL, NULL, NULL,
568 NULL, displaySSMSaveScreenshot, NULL,
569 NULL, displaySSMLoadScreenshot, NULL, this);
570
571 AssertRCReturn(rc, rc);
572
573 return VINF_SUCCESS;
574}
575
576// IEventListener method
577STDMETHODIMP Display::HandleEvent(IEvent * aEvent)
578{
579 VBoxEventType_T aType = VBoxEventType_Invalid;
580
581 aEvent->COMGETTER(Type)(&aType);
582 switch (aType)
583 {
584 case VBoxEventType_OnStateChanged:
585 {
586 ComPtr<IStateChangedEvent> scev = aEvent;
587 Assert(scev);
588 MachineState_T machineState;
589 scev->COMGETTER(State)(&machineState);
590 if ( machineState == MachineState_Running
591 || machineState == MachineState_Teleporting
592 || machineState == MachineState_LiveSnapshotting
593 )
594 {
595 LogRelFlowFunc(("Machine is running.\n"));
596
597 mfMachineRunning = true;
598 }
599 else
600 mfMachineRunning = false;
601 break;
602 }
603 default:
604 AssertFailed();
605 }
606
607 return S_OK;
608}
609
610// public methods only for internal purposes
611/////////////////////////////////////////////////////////////////////////////
612
613/**
614 * @thread EMT
615 */
616static int callFramebufferResize (IFramebuffer *pFramebuffer, unsigned uScreenId,
617 ULONG pixelFormat, void *pvVRAM,
618 uint32_t bpp, uint32_t cbLine,
619 int w, int h)
620{
621 Assert (pFramebuffer);
622
623 /* Call the framebuffer to try and set required pixelFormat. */
624 BOOL finished = TRUE;
625
626 pFramebuffer->RequestResize (uScreenId, pixelFormat, (BYTE *) pvVRAM,
627 bpp, cbLine, w, h, &finished);
628
629 if (!finished)
630 {
631 LogRelFlowFunc (("External framebuffer wants us to wait!\n"));
632 return VINF_VGA_RESIZE_IN_PROGRESS;
633 }
634
635 return VINF_SUCCESS;
636}
637
638/**
639 * Handles display resize event.
640 * Disables access to VGA device;
641 * calls the framebuffer RequestResize method;
642 * if framebuffer resizes synchronously,
643 * updates the display connector data and enables access to the VGA device.
644 *
645 * @param w New display width
646 * @param h New display height
647 *
648 * @thread EMT
649 */
650int Display::handleDisplayResize (unsigned uScreenId, uint32_t bpp, void *pvVRAM,
651 uint32_t cbLine, int w, int h, uint16_t flags)
652{
653 LogRel (("Display::handleDisplayResize(): uScreenId = %d, pvVRAM=%p "
654 "w=%d h=%d bpp=%d cbLine=0x%X, flags=0x%X\n",
655 uScreenId, pvVRAM, w, h, bpp, cbLine, flags));
656
657 /* If there is no framebuffer, this call is not interesting. */
658 if ( uScreenId >= mcMonitors
659 || maFramebuffers[uScreenId].pFramebuffer.isNull())
660 {
661 return VINF_SUCCESS;
662 }
663
664 mLastAddress = pvVRAM;
665 mLastBytesPerLine = cbLine;
666 mLastBitsPerPixel = bpp,
667 mLastWidth = w;
668 mLastHeight = h;
669 mLastFlags = flags;
670
671 ULONG pixelFormat;
672
673 switch (bpp)
674 {
675 case 32:
676 case 24:
677 case 16:
678 pixelFormat = FramebufferPixelFormat_FOURCC_RGB;
679 break;
680 default:
681 pixelFormat = FramebufferPixelFormat_Opaque;
682 bpp = cbLine = 0;
683 break;
684 }
685
686 /* Atomically set the resize status before calling the framebuffer. The new InProgress status will
687 * disable access to the VGA device by the EMT thread.
688 */
689 bool f = ASMAtomicCmpXchgU32 (&maFramebuffers[uScreenId].u32ResizeStatus,
690 ResizeStatus_InProgress, ResizeStatus_Void);
691 if (!f)
692 {
693 /* This could be a result of the screenshot taking call Display::TakeScreenShot:
694 * if the framebuffer is processing the resize request and GUI calls the TakeScreenShot
695 * and the guest has reprogrammed the virtual VGA devices again so a new resize is required.
696 *
697 * Save the resize information and return the pending status code.
698 *
699 * Note: the resize information is only accessed on EMT so no serialization is required.
700 */
701 LogRel (("Display::handleDisplayResize(): Warning: resize postponed.\n"));
702
703 maFramebuffers[uScreenId].pendingResize.fPending = true;
704 maFramebuffers[uScreenId].pendingResize.pixelFormat = pixelFormat;
705 maFramebuffers[uScreenId].pendingResize.pvVRAM = pvVRAM;
706 maFramebuffers[uScreenId].pendingResize.bpp = bpp;
707 maFramebuffers[uScreenId].pendingResize.cbLine = cbLine;
708 maFramebuffers[uScreenId].pendingResize.w = w;
709 maFramebuffers[uScreenId].pendingResize.h = h;
710 maFramebuffers[uScreenId].pendingResize.flags = flags;
711
712 return VINF_VGA_RESIZE_IN_PROGRESS;
713 }
714
715 int rc = callFramebufferResize (maFramebuffers[uScreenId].pFramebuffer, uScreenId,
716 pixelFormat, pvVRAM, bpp, cbLine, w, h);
717 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
718 {
719 /* Immediately return to the caller. ResizeCompleted will be called back by the
720 * GUI thread. The ResizeCompleted callback will change the resize status from
721 * InProgress to UpdateDisplayData. The latter status will be checked by the
722 * display timer callback on EMT and all required adjustments will be done there.
723 */
724 return rc;
725 }
726
727 /* Set the status so the 'handleResizeCompleted' would work. */
728 f = ASMAtomicCmpXchgU32 (&maFramebuffers[uScreenId].u32ResizeStatus,
729 ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
730 AssertRelease(f);NOREF(f);
731
732 AssertRelease(!maFramebuffers[uScreenId].pendingResize.fPending);
733
734 /* The method also unlocks the framebuffer. */
735 handleResizeCompletedEMT();
736
737 return VINF_SUCCESS;
738}
739
740/**
741 * Framebuffer has been resized.
742 * Read the new display data and unlock the framebuffer.
743 *
744 * @thread EMT
745 */
746void Display::handleResizeCompletedEMT (void)
747{
748 LogRelFlowFunc(("\n"));
749
750 unsigned uScreenId;
751 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
752 {
753 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
754
755 /* Try to into non resizing state. */
756 bool f = ASMAtomicCmpXchgU32 (&pFBInfo->u32ResizeStatus, ResizeStatus_Void, ResizeStatus_UpdateDisplayData);
757
758 if (f == false)
759 {
760 /* This is not the display that has completed resizing. */
761 continue;
762 }
763
764 /* Check whether a resize is pending for this framebuffer. */
765 if (pFBInfo->pendingResize.fPending)
766 {
767 /* Reset the condition, call the display resize with saved data and continue.
768 *
769 * Note: handleDisplayResize can call handleResizeCompletedEMT back,
770 * but infinite recursion is not possible, because when the handleResizeCompletedEMT
771 * is called, the pFBInfo->pendingResize.fPending is equal to false.
772 */
773 pFBInfo->pendingResize.fPending = false;
774 handleDisplayResize (uScreenId, pFBInfo->pendingResize.bpp, pFBInfo->pendingResize.pvVRAM,
775 pFBInfo->pendingResize.cbLine, pFBInfo->pendingResize.w, pFBInfo->pendingResize.h, pFBInfo->pendingResize.flags);
776 continue;
777 }
778
779 /* @todo Merge these two 'if's within one 'if (!pFBInfo->pFramebuffer.isNull())' */
780 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && !pFBInfo->pFramebuffer.isNull())
781 {
782 /* Primary framebuffer has completed the resize. Update the connector data for VGA device. */
783 updateDisplayData();
784
785 /* Check the framebuffer pixel format to setup the rendering in VGA device. */
786 BOOL usesGuestVRAM = FALSE;
787 pFBInfo->pFramebuffer->COMGETTER(UsesGuestVRAM) (&usesGuestVRAM);
788
789 pFBInfo->fDefaultFormat = (usesGuestVRAM == FALSE);
790
791 /* If the primary framebuffer is disabled, tell the VGA device to not to copy
792 * pixels from VRAM to the framebuffer.
793 */
794 if (pFBInfo->fDisabled)
795 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort, false);
796 else
797 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort,
798 pFBInfo->fDefaultFormat);
799
800 /* If the screen resize was because of disabling, tell framebuffer to repaint.
801 * The framebuffer if now in default format so it will not use guest VRAM
802 * and will show usually black image which is there after framebuffer resize.
803 */
804 if (pFBInfo->fDisabled)
805 pFBInfo->pFramebuffer->NotifyUpdate(0, 0, mpDrv->IConnector.cx, mpDrv->IConnector.cy);
806 }
807 else if (!pFBInfo->pFramebuffer.isNull())
808 {
809 BOOL usesGuestVRAM = FALSE;
810 pFBInfo->pFramebuffer->COMGETTER(UsesGuestVRAM) (&usesGuestVRAM);
811
812 pFBInfo->fDefaultFormat = (usesGuestVRAM == FALSE);
813
814 /* If the screen resize was because of disabling, tell framebuffer to repaint.
815 * The framebuffer if now in default format so it will not use guest VRAM
816 * and will show usually black image which is there after framebuffer resize.
817 */
818 if (pFBInfo->fDisabled)
819 pFBInfo->pFramebuffer->NotifyUpdate(0, 0, pFBInfo->w, pFBInfo->h);
820 }
821 LogRelFlow(("[%d]: default format %d\n", uScreenId, pFBInfo->fDefaultFormat));
822
823#ifdef DEBUG_sunlover
824 if (!stam)
825 {
826 /* protect mpVM */
827 Console::SafeVMPtr pVM (mParent);
828 AssertComRC (pVM.rc());
829
830 STAM_REG(pVM, &StatDisplayRefresh, STAMTYPE_PROFILE, "/PROF/Display/Refresh", STAMUNIT_TICKS_PER_CALL, "Time spent in EMT for display updates.");
831 stam = 1;
832 }
833#endif /* DEBUG_sunlover */
834
835 /* Inform VRDP server about the change of display parameters. */
836 LogRelFlowFunc (("Calling VRDP\n"));
837 mParent->consoleVRDPServer()->SendResize();
838
839#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
840 {
841 BOOL is3denabled;
842 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
843
844 if (is3denabled)
845 {
846 VBOXHGCMSVCPARM parm;
847
848 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
849 parm.u.uint32 = uScreenId;
850
851 VMMDev *pVMMDev = mParent->getVMMDev();
852 if (pVMMDev)
853 pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SCREEN_CHANGED, SHCRGL_CPARMS_SCREEN_CHANGED, &parm);
854 }
855 }
856#endif /* VBOX_WITH_CROGL */
857 }
858}
859
860static void checkCoordBounds (int *px, int *py, int *pw, int *ph, int cx, int cy)
861{
862 /* Correct negative x and y coordinates. */
863 if (*px < 0)
864 {
865 *px += *pw; /* Compute xRight which is also the new width. */
866
867 *pw = (*px < 0)? 0: *px;
868
869 *px = 0;
870 }
871
872 if (*py < 0)
873 {
874 *py += *ph; /* Compute xBottom, which is also the new height. */
875
876 *ph = (*py < 0)? 0: *py;
877
878 *py = 0;
879 }
880
881 /* Also check if coords are greater than the display resolution. */
882 if (*px + *pw > cx)
883 {
884 *pw = cx > *px? cx - *px: 0;
885 }
886
887 if (*py + *ph > cy)
888 {
889 *ph = cy > *py? cy - *py: 0;
890 }
891}
892
893unsigned mapCoordsToScreen(DISPLAYFBINFO *pInfos, unsigned cInfos, int *px, int *py, int *pw, int *ph)
894{
895 DISPLAYFBINFO *pInfo = pInfos;
896 unsigned uScreenId;
897 LogSunlover (("mapCoordsToScreen: %d,%d %dx%d\n", *px, *py, *pw, *ph));
898 for (uScreenId = 0; uScreenId < cInfos; uScreenId++, pInfo++)
899 {
900 LogSunlover ((" [%d] %d,%d %dx%d\n", uScreenId, pInfo->xOrigin, pInfo->yOrigin, pInfo->w, pInfo->h));
901 if ( (pInfo->xOrigin <= *px && *px < pInfo->xOrigin + (int)pInfo->w)
902 && (pInfo->yOrigin <= *py && *py < pInfo->yOrigin + (int)pInfo->h))
903 {
904 /* The rectangle belongs to the screen. Correct coordinates. */
905 *px -= pInfo->xOrigin;
906 *py -= pInfo->yOrigin;
907 LogSunlover ((" -> %d,%d", *px, *py));
908 break;
909 }
910 }
911 if (uScreenId == cInfos)
912 {
913 /* Map to primary screen. */
914 uScreenId = 0;
915 }
916 LogSunlover ((" scr %d\n", uScreenId));
917 return uScreenId;
918}
919
920
921/**
922 * Handles display update event.
923 *
924 * @param x Update area x coordinate
925 * @param y Update area y coordinate
926 * @param w Update area width
927 * @param h Update area height
928 *
929 * @thread EMT
930 */
931void Display::handleDisplayUpdateLegacy (int x, int y, int w, int h)
932{
933 unsigned uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
934
935#ifdef DEBUG_sunlover
936 LogFlowFunc (("%d,%d %dx%d (checked)\n", x, y, w, h));
937#endif /* DEBUG_sunlover */
938
939 handleDisplayUpdate (uScreenId, x, y, w, h);
940}
941
942void Display::handleDisplayUpdate (unsigned uScreenId, int x, int y, int w, int h)
943{
944 /*
945 * Always runs under either VBVA lock or, for HGSMI, DevVGA lock.
946 * Safe to use VBVA vars and take the framebuffer lock.
947 */
948
949#ifdef DEBUG_sunlover
950 LogFlowFunc (("[%d] %d,%d %dx%d (%d,%d)\n",
951 uScreenId, x, y, w, h, mpDrv->IConnector.cx, mpDrv->IConnector.cy));
952#endif /* DEBUG_sunlover */
953
954 IFramebuffer *pFramebuffer = maFramebuffers[uScreenId].pFramebuffer;
955
956 // if there is no framebuffer, this call is not interesting
957 if ( pFramebuffer == NULL
958 || maFramebuffers[uScreenId].fDisabled)
959 return;
960
961 pFramebuffer->Lock();
962
963 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
964 checkCoordBounds (&x, &y, &w, &h, mpDrv->IConnector.cx, mpDrv->IConnector.cy);
965 else
966 checkCoordBounds (&x, &y, &w, &h, maFramebuffers[uScreenId].w,
967 maFramebuffers[uScreenId].h);
968
969 if (w != 0 && h != 0)
970 pFramebuffer->NotifyUpdate(x, y, w, h);
971
972 pFramebuffer->Unlock();
973
974#ifndef VBOX_WITH_HGSMI
975 if (!mfVideoAccelEnabled)
976 {
977#else
978 if (!mfVideoAccelEnabled && !maFramebuffers[uScreenId].fVBVAEnabled)
979 {
980#endif /* VBOX_WITH_HGSMI */
981 /* When VBVA is enabled, the VRDP server is informed in the VideoAccelFlush.
982 * Inform the server here only if VBVA is disabled.
983 */
984 if (maFramebuffers[uScreenId].u32ResizeStatus == ResizeStatus_Void)
985 mParent->consoleVRDPServer()->SendUpdateBitmap(uScreenId, x, y, w, h);
986 }
987}
988
989/**
990 * Returns the upper left and lower right corners of the virtual framebuffer.
991 * The lower right is "exclusive" (i.e. first pixel beyond the framebuffer),
992 * and the origin is (0, 0), not (1, 1) like the GUI returns.
993 */
994void Display::getFramebufferDimensions(int32_t *px1, int32_t *py1,
995 int32_t *px2, int32_t *py2)
996{
997 int32_t x1 = 0, y1 = 0, x2 = 0, y2 = 0;
998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 AssertPtrReturnVoid(px1);
1001 AssertPtrReturnVoid(py1);
1002 AssertPtrReturnVoid(px2);
1003 AssertPtrReturnVoid(py2);
1004 LogRelFlowFunc(("\n"));
1005
1006 if (!mpDrv)
1007 return;
1008 /* If VBVA is not in use then this flag will not be set and this
1009 * will still work as it should. */
1010 if (!(maFramebuffers[0].fDisabled))
1011 {
1012 x1 = (int32_t)maFramebuffers[0].xOrigin;
1013 y1 = (int32_t)maFramebuffers[0].yOrigin;
1014 x2 = mpDrv->IConnector.cx + (int32_t)maFramebuffers[0].xOrigin;
1015 y2 = mpDrv->IConnector.cy + (int32_t)maFramebuffers[0].yOrigin;
1016 }
1017 for (unsigned i = 1; i < mcMonitors; ++i)
1018 {
1019 if (!(maFramebuffers[i].fDisabled))
1020 {
1021 x1 = RT_MIN(x1, maFramebuffers[i].xOrigin);
1022 y1 = RT_MIN(y1, maFramebuffers[i].yOrigin);
1023 x2 = RT_MAX(x2, maFramebuffers[i].xOrigin
1024 + (int32_t)maFramebuffers[i].w);
1025 y2 = RT_MAX(y2, maFramebuffers[i].yOrigin
1026 + (int32_t)maFramebuffers[i].h);
1027 }
1028 }
1029 *px1 = x1;
1030 *py1 = y1;
1031 *px2 = x2;
1032 *py2 = y2;
1033}
1034
1035static bool displayIntersectRect(RTRECT *prectResult,
1036 const RTRECT *prect1,
1037 const RTRECT *prect2)
1038{
1039 /* Initialize result to an empty record. */
1040 memset (prectResult, 0, sizeof (RTRECT));
1041
1042 int xLeftResult = RT_MAX(prect1->xLeft, prect2->xLeft);
1043 int xRightResult = RT_MIN(prect1->xRight, prect2->xRight);
1044
1045 if (xLeftResult < xRightResult)
1046 {
1047 /* There is intersection by X. */
1048
1049 int yTopResult = RT_MAX(prect1->yTop, prect2->yTop);
1050 int yBottomResult = RT_MIN(prect1->yBottom, prect2->yBottom);
1051
1052 if (yTopResult < yBottomResult)
1053 {
1054 /* There is intersection by Y. */
1055
1056 prectResult->xLeft = xLeftResult;
1057 prectResult->yTop = yTopResult;
1058 prectResult->xRight = xRightResult;
1059 prectResult->yBottom = yBottomResult;
1060
1061 return true;
1062 }
1063 }
1064
1065 return false;
1066}
1067
1068int Display::handleSetVisibleRegion(uint32_t cRect, PRTRECT pRect)
1069{
1070 RTRECT *pVisibleRegion = (RTRECT *)RTMemTmpAlloc( RT_MAX(cRect, 1)
1071 * sizeof (RTRECT));
1072 if (!pVisibleRegion)
1073 {
1074 return VERR_NO_TMP_MEMORY;
1075 }
1076
1077 unsigned uScreenId;
1078 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1079 {
1080 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1081
1082 if (!pFBInfo->pFramebuffer.isNull())
1083 {
1084 /* Prepare a new array of rectangles which intersect with the framebuffer.
1085 */
1086 RTRECT rectFramebuffer;
1087 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
1088 {
1089 rectFramebuffer.xLeft = 0;
1090 rectFramebuffer.yTop = 0;
1091 if (mpDrv)
1092 {
1093 rectFramebuffer.xRight = mpDrv->IConnector.cx;
1094 rectFramebuffer.yBottom = mpDrv->IConnector.cy;
1095 }
1096 else
1097 {
1098 rectFramebuffer.xRight = 0;
1099 rectFramebuffer.yBottom = 0;
1100 }
1101 }
1102 else
1103 {
1104 rectFramebuffer.xLeft = pFBInfo->xOrigin;
1105 rectFramebuffer.yTop = pFBInfo->yOrigin;
1106 rectFramebuffer.xRight = pFBInfo->xOrigin + pFBInfo->w;
1107 rectFramebuffer.yBottom = pFBInfo->yOrigin + pFBInfo->h;
1108 }
1109
1110 uint32_t cRectVisibleRegion = 0;
1111
1112 uint32_t i;
1113 for (i = 0; i < cRect; i++)
1114 {
1115 if (displayIntersectRect(&pVisibleRegion[cRectVisibleRegion], &pRect[i], &rectFramebuffer))
1116 {
1117 pVisibleRegion[cRectVisibleRegion].xLeft -= pFBInfo->xOrigin;
1118 pVisibleRegion[cRectVisibleRegion].yTop -= pFBInfo->yOrigin;
1119 pVisibleRegion[cRectVisibleRegion].xRight -= pFBInfo->xOrigin;
1120 pVisibleRegion[cRectVisibleRegion].yBottom -= pFBInfo->yOrigin;
1121
1122 cRectVisibleRegion++;
1123 }
1124 }
1125
1126 pFBInfo->pFramebuffer->SetVisibleRegion((BYTE *)pVisibleRegion, cRectVisibleRegion);
1127 }
1128 }
1129
1130#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
1131 // @todo fix for multimonitor
1132 BOOL is3denabled = FALSE;
1133
1134 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
1135
1136 VMMDev *vmmDev = mParent->getVMMDev();
1137 if (is3denabled && vmmDev)
1138 {
1139 VBOXHGCMSVCPARM parms[2];
1140
1141 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
1142 parms[0].u.pointer.addr = pRect;
1143 parms[0].u.pointer.size = 0; /* We don't actually care. */
1144 parms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
1145 parms[1].u.uint32 = cRect;
1146
1147 vmmDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SET_VISIBLE_REGION, 2, &parms[0]);
1148 }
1149#endif
1150
1151 RTMemTmpFree(pVisibleRegion);
1152
1153 return VINF_SUCCESS;
1154}
1155
1156int Display::handleQueryVisibleRegion(uint32_t *pcRect, PRTRECT pRect)
1157{
1158 // @todo Currently not used by the guest and is not implemented in framebuffers. Remove?
1159 return VERR_NOT_SUPPORTED;
1160}
1161
1162typedef struct _VBVADIRTYREGION
1163{
1164 /* Copies of object's pointers used by vbvaRgn functions. */
1165 DISPLAYFBINFO *paFramebuffers;
1166 unsigned cMonitors;
1167 Display *pDisplay;
1168 PPDMIDISPLAYPORT pPort;
1169
1170} VBVADIRTYREGION;
1171
1172static void vbvaRgnInit (VBVADIRTYREGION *prgn, DISPLAYFBINFO *paFramebuffers, unsigned cMonitors, Display *pd, PPDMIDISPLAYPORT pp)
1173{
1174 prgn->paFramebuffers = paFramebuffers;
1175 prgn->cMonitors = cMonitors;
1176 prgn->pDisplay = pd;
1177 prgn->pPort = pp;
1178
1179 unsigned uScreenId;
1180 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
1181 {
1182 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1183
1184 memset (&pFBInfo->dirtyRect, 0, sizeof (pFBInfo->dirtyRect));
1185 }
1186}
1187
1188static void vbvaRgnDirtyRect (VBVADIRTYREGION *prgn, unsigned uScreenId, VBVACMDHDR *phdr)
1189{
1190 LogSunlover (("x = %d, y = %d, w = %d, h = %d\n",
1191 phdr->x, phdr->y, phdr->w, phdr->h));
1192
1193 /*
1194 * Here update rectangles are accumulated to form an update area.
1195 * @todo
1196 * Now the simplest method is used which builds one rectangle that
1197 * includes all update areas. A bit more advanced method can be
1198 * employed here. The method should be fast however.
1199 */
1200 if (phdr->w == 0 || phdr->h == 0)
1201 {
1202 /* Empty rectangle. */
1203 return;
1204 }
1205
1206 int32_t xRight = phdr->x + phdr->w;
1207 int32_t yBottom = phdr->y + phdr->h;
1208
1209 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1210
1211 if (pFBInfo->dirtyRect.xRight == 0)
1212 {
1213 /* This is the first rectangle to be added. */
1214 pFBInfo->dirtyRect.xLeft = phdr->x;
1215 pFBInfo->dirtyRect.yTop = phdr->y;
1216 pFBInfo->dirtyRect.xRight = xRight;
1217 pFBInfo->dirtyRect.yBottom = yBottom;
1218 }
1219 else
1220 {
1221 /* Adjust region coordinates. */
1222 if (pFBInfo->dirtyRect.xLeft > phdr->x)
1223 {
1224 pFBInfo->dirtyRect.xLeft = phdr->x;
1225 }
1226
1227 if (pFBInfo->dirtyRect.yTop > phdr->y)
1228 {
1229 pFBInfo->dirtyRect.yTop = phdr->y;
1230 }
1231
1232 if (pFBInfo->dirtyRect.xRight < xRight)
1233 {
1234 pFBInfo->dirtyRect.xRight = xRight;
1235 }
1236
1237 if (pFBInfo->dirtyRect.yBottom < yBottom)
1238 {
1239 pFBInfo->dirtyRect.yBottom = yBottom;
1240 }
1241 }
1242
1243 if (pFBInfo->fDefaultFormat)
1244 {
1245 //@todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
1246 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, phdr->x, phdr->y, phdr->w, phdr->h);
1247 prgn->pDisplay->handleDisplayUpdateLegacy (phdr->x + pFBInfo->xOrigin,
1248 phdr->y + pFBInfo->yOrigin, phdr->w, phdr->h);
1249 }
1250
1251 return;
1252}
1253
1254static void vbvaRgnUpdateFramebuffer (VBVADIRTYREGION *prgn, unsigned uScreenId)
1255{
1256 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
1257
1258 uint32_t w = pFBInfo->dirtyRect.xRight - pFBInfo->dirtyRect.xLeft;
1259 uint32_t h = pFBInfo->dirtyRect.yBottom - pFBInfo->dirtyRect.yTop;
1260
1261 if (!pFBInfo->fDefaultFormat && pFBInfo->pFramebuffer && w != 0 && h != 0)
1262 {
1263 //@todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
1264 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, pFBInfo->dirtyRect.xLeft, pFBInfo->dirtyRect.yTop, w, h);
1265 prgn->pDisplay->handleDisplayUpdateLegacy (pFBInfo->dirtyRect.xLeft + pFBInfo->xOrigin,
1266 pFBInfo->dirtyRect.yTop + pFBInfo->yOrigin, w, h);
1267 }
1268}
1269
1270static void vbvaSetMemoryFlags (VBVAMEMORY *pVbvaMemory,
1271 bool fVideoAccelEnabled,
1272 bool fVideoAccelVRDP,
1273 uint32_t fu32SupportedOrders,
1274 DISPLAYFBINFO *paFBInfos,
1275 unsigned cFBInfos)
1276{
1277 if (pVbvaMemory)
1278 {
1279 /* This called only on changes in mode. So reset VRDP always. */
1280 uint32_t fu32Flags = VBVA_F_MODE_VRDP_RESET;
1281
1282 if (fVideoAccelEnabled)
1283 {
1284 fu32Flags |= VBVA_F_MODE_ENABLED;
1285
1286 if (fVideoAccelVRDP)
1287 {
1288 fu32Flags |= VBVA_F_MODE_VRDP | VBVA_F_MODE_VRDP_ORDER_MASK;
1289
1290 pVbvaMemory->fu32SupportedOrders = fu32SupportedOrders;
1291 }
1292 }
1293
1294 pVbvaMemory->fu32ModeFlags = fu32Flags;
1295 }
1296
1297 unsigned uScreenId;
1298 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
1299 {
1300 if (paFBInfos[uScreenId].pHostEvents)
1301 {
1302 paFBInfos[uScreenId].pHostEvents->fu32Events |= VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
1303 }
1304 }
1305}
1306
1307#ifdef VBOX_WITH_HGSMI
1308static void vbvaSetMemoryFlagsHGSMI (unsigned uScreenId,
1309 uint32_t fu32SupportedOrders,
1310 bool fVideoAccelVRDP,
1311 DISPLAYFBINFO *pFBInfo)
1312{
1313 LogRelFlowFunc(("HGSMI[%d]: %p\n", uScreenId, pFBInfo->pVBVAHostFlags));
1314
1315 if (pFBInfo->pVBVAHostFlags)
1316 {
1317 uint32_t fu32HostEvents = VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
1318
1319 if (pFBInfo->fVBVAEnabled)
1320 {
1321 fu32HostEvents |= VBVA_F_MODE_ENABLED;
1322
1323 if (fVideoAccelVRDP)
1324 {
1325 fu32HostEvents |= VBVA_F_MODE_VRDP;
1326 }
1327 }
1328
1329 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32HostEvents, fu32HostEvents);
1330 ASMAtomicWriteU32(&pFBInfo->pVBVAHostFlags->u32SupportedOrders, fu32SupportedOrders);
1331
1332 LogRelFlowFunc((" fu32HostEvents = 0x%08X, fu32SupportedOrders = 0x%08X\n", fu32HostEvents, fu32SupportedOrders));
1333 }
1334}
1335
1336static void vbvaSetMemoryFlagsAllHGSMI (uint32_t fu32SupportedOrders,
1337 bool fVideoAccelVRDP,
1338 DISPLAYFBINFO *paFBInfos,
1339 unsigned cFBInfos)
1340{
1341 unsigned uScreenId;
1342
1343 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
1344 {
1345 vbvaSetMemoryFlagsHGSMI(uScreenId, fu32SupportedOrders, fVideoAccelVRDP, &paFBInfos[uScreenId]);
1346 }
1347}
1348#endif /* VBOX_WITH_HGSMI */
1349
1350bool Display::VideoAccelAllowed (void)
1351{
1352 return true;
1353}
1354
1355int Display::vbvaLock(void)
1356{
1357 return RTCritSectEnter(&mVBVALock);
1358}
1359
1360void Display::vbvaUnlock(void)
1361{
1362 RTCritSectLeave(&mVBVALock);
1363}
1364
1365/**
1366 * @thread EMT
1367 */
1368int Display::VideoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
1369{
1370 int rc;
1371 vbvaLock();
1372 rc = videoAccelEnable (fEnable, pVbvaMemory);
1373 vbvaUnlock();
1374 return rc;
1375}
1376
1377int Display::videoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
1378{
1379 int rc = VINF_SUCCESS;
1380
1381 /* Called each time the guest wants to use acceleration,
1382 * or when the VGA device disables acceleration,
1383 * or when restoring the saved state with accel enabled.
1384 *
1385 * VGA device disables acceleration on each video mode change
1386 * and on reset.
1387 *
1388 * Guest enabled acceleration at will. And it has to enable
1389 * acceleration after a mode change.
1390 */
1391 LogRelFlowFunc (("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
1392 mfVideoAccelEnabled, fEnable, pVbvaMemory));
1393
1394 /* Strictly check parameters. Callers must not pass anything in the case. */
1395 Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));
1396
1397 if (!VideoAccelAllowed ())
1398 return VERR_NOT_SUPPORTED;
1399
1400 /*
1401 * Verify that the VM is in running state. If it is not,
1402 * then this must be postponed until it goes to running.
1403 */
1404 if (!mfMachineRunning)
1405 {
1406 Assert (!mfVideoAccelEnabled);
1407
1408 LogRelFlowFunc (("Machine is not yet running.\n"));
1409
1410 if (fEnable)
1411 {
1412 mfPendingVideoAccelEnable = fEnable;
1413 mpPendingVbvaMemory = pVbvaMemory;
1414 }
1415
1416 return rc;
1417 }
1418
1419 /* Check that current status is not being changed */
1420 if (mfVideoAccelEnabled == fEnable)
1421 return rc;
1422
1423 if (mfVideoAccelEnabled)
1424 {
1425 /* Process any pending orders and empty the VBVA ring buffer. */
1426 videoAccelFlush ();
1427 }
1428
1429 if (!fEnable && mpVbvaMemory)
1430 mpVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;
1431
1432 /* Safety precaution. There is no more VBVA until everything is setup! */
1433 mpVbvaMemory = NULL;
1434 mfVideoAccelEnabled = false;
1435
1436 /* Update entire display. */
1437 if (maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].u32ResizeStatus == ResizeStatus_Void)
1438 mpDrv->pUpPort->pfnUpdateDisplayAll(mpDrv->pUpPort);
1439
1440 /* Everything OK. VBVA status can be changed. */
1441
1442 /* Notify the VMMDev, which saves VBVA status in the saved state,
1443 * and needs to know current status.
1444 */
1445 VMMDev *pVMMDev = mParent->getVMMDev();
1446 if (pVMMDev)
1447 {
1448 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
1449 if (pVMMDevPort)
1450 pVMMDevPort->pfnVBVAChange(pVMMDevPort, fEnable);
1451 }
1452
1453 if (fEnable)
1454 {
1455 mpVbvaMemory = pVbvaMemory;
1456 mfVideoAccelEnabled = true;
1457
1458 /* Initialize the hardware memory. */
1459 vbvaSetMemoryFlags(mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
1460 mpVbvaMemory->off32Data = 0;
1461 mpVbvaMemory->off32Free = 0;
1462
1463 memset(mpVbvaMemory->aRecords, 0, sizeof (mpVbvaMemory->aRecords));
1464 mpVbvaMemory->indexRecordFirst = 0;
1465 mpVbvaMemory->indexRecordFree = 0;
1466
1467 mfu32PendingVideoAccelDisable = false;
1468
1469 LogRel(("VBVA: Enabled.\n"));
1470 }
1471 else
1472 {
1473 LogRel(("VBVA: Disabled.\n"));
1474 }
1475
1476 LogRelFlowFunc (("VideoAccelEnable: rc = %Rrc.\n", rc));
1477
1478 return rc;
1479}
1480
1481/* Called always by one VRDP server thread. Can be thread-unsafe.
1482 */
1483void Display::VideoAccelVRDP (bool fEnable)
1484{
1485 LogRelFlowFunc(("fEnable = %d\n", fEnable));
1486
1487 vbvaLock();
1488
1489 int c = fEnable?
1490 ASMAtomicIncS32 (&mcVideoAccelVRDPRefs):
1491 ASMAtomicDecS32 (&mcVideoAccelVRDPRefs);
1492
1493 Assert (c >= 0);
1494
1495 if (c == 0)
1496 {
1497 /* The last client has disconnected, and the accel can be
1498 * disabled.
1499 */
1500 Assert (fEnable == false);
1501
1502 mfVideoAccelVRDP = false;
1503 mfu32SupportedOrders = 0;
1504
1505 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
1506#ifdef VBOX_WITH_HGSMI
1507 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1508 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1509#endif /* VBOX_WITH_HGSMI */
1510
1511 LogRel(("VBVA: VRDP acceleration has been disabled.\n"));
1512 }
1513 else if ( c == 1
1514 && !mfVideoAccelVRDP)
1515 {
1516 /* The first client has connected. Enable the accel.
1517 */
1518 Assert (fEnable == true);
1519
1520 mfVideoAccelVRDP = true;
1521 /* Supporting all orders. */
1522 mfu32SupportedOrders = ~0;
1523
1524 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
1525#ifdef VBOX_WITH_HGSMI
1526 /* Here is VRDP-IN thread. Process the request in vbvaUpdateBegin under DevVGA lock on an EMT. */
1527 ASMAtomicIncU32(&mu32UpdateVBVAFlags);
1528#endif /* VBOX_WITH_HGSMI */
1529
1530 LogRel(("VBVA: VRDP acceleration has been requested.\n"));
1531 }
1532 else
1533 {
1534 /* A client is connected or disconnected but there is no change in the
1535 * accel state. It remains enabled.
1536 */
1537 Assert (mfVideoAccelVRDP == true);
1538 }
1539 vbvaUnlock();
1540}
1541
1542static bool vbvaVerifyRingBuffer (VBVAMEMORY *pVbvaMemory)
1543{
1544 return true;
1545}
1546
1547static void vbvaFetchBytes (VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t cbDst)
1548{
1549 if (cbDst >= VBVA_RING_BUFFER_SIZE)
1550 {
1551 AssertMsgFailed (("cbDst = 0x%08X, ring buffer size 0x%08X", cbDst, VBVA_RING_BUFFER_SIZE));
1552 return;
1553 }
1554
1555 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
1556 uint8_t *src = &pVbvaMemory->au8RingBuffer[pVbvaMemory->off32Data];
1557 int32_t i32Diff = cbDst - u32BytesTillBoundary;
1558
1559 if (i32Diff <= 0)
1560 {
1561 /* Chunk will not cross buffer boundary. */
1562 memcpy (pu8Dst, src, cbDst);
1563 }
1564 else
1565 {
1566 /* Chunk crosses buffer boundary. */
1567 memcpy (pu8Dst, src, u32BytesTillBoundary);
1568 memcpy (pu8Dst + u32BytesTillBoundary, &pVbvaMemory->au8RingBuffer[0], i32Diff);
1569 }
1570
1571 /* Advance data offset. */
1572 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbDst) % VBVA_RING_BUFFER_SIZE;
1573
1574 return;
1575}
1576
1577
1578static bool vbvaPartialRead (uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
1579{
1580 uint8_t *pu8New;
1581
1582 LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
1583 *ppu8, *pcb, cbRecord));
1584
1585 if (*ppu8)
1586 {
1587 Assert (*pcb);
1588 pu8New = (uint8_t *)RTMemRealloc (*ppu8, cbRecord);
1589 }
1590 else
1591 {
1592 Assert (!*pcb);
1593 pu8New = (uint8_t *)RTMemAlloc (cbRecord);
1594 }
1595
1596 if (!pu8New)
1597 {
1598 /* Memory allocation failed, fail the function. */
1599 Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
1600 cbRecord));
1601
1602 if (*ppu8)
1603 {
1604 RTMemFree (*ppu8);
1605 }
1606
1607 *ppu8 = NULL;
1608 *pcb = 0;
1609
1610 return false;
1611 }
1612
1613 /* Fetch data from the ring buffer. */
1614 vbvaFetchBytes (pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
1615
1616 *ppu8 = pu8New;
1617 *pcb = cbRecord;
1618
1619 return true;
1620}
1621
1622/* For contiguous chunks just return the address in the buffer.
1623 * For crossing boundary - allocate a buffer from heap.
1624 */
1625bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
1626{
1627 uint32_t indexRecordFirst = mpVbvaMemory->indexRecordFirst;
1628 uint32_t indexRecordFree = mpVbvaMemory->indexRecordFree;
1629
1630#ifdef DEBUG_sunlover
1631 LogFlowFunc (("first = %d, free = %d\n",
1632 indexRecordFirst, indexRecordFree));
1633#endif /* DEBUG_sunlover */
1634
1635 if (!vbvaVerifyRingBuffer (mpVbvaMemory))
1636 {
1637 return false;
1638 }
1639
1640 if (indexRecordFirst == indexRecordFree)
1641 {
1642 /* No records to process. Return without assigning output variables. */
1643 return true;
1644 }
1645
1646 VBVARECORD *pRecord = &mpVbvaMemory->aRecords[indexRecordFirst];
1647
1648#ifdef DEBUG_sunlover
1649 LogFlowFunc (("cbRecord = 0x%08X\n", pRecord->cbRecord));
1650#endif /* DEBUG_sunlover */
1651
1652 uint32_t cbRecord = pRecord->cbRecord & ~VBVA_F_RECORD_PARTIAL;
1653
1654 if (mcbVbvaPartial)
1655 {
1656 /* There is a partial read in process. Continue with it. */
1657
1658 Assert (mpu8VbvaPartial);
1659
1660 LogFlowFunc (("continue partial record mcbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
1661 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
1662
1663 if (cbRecord > mcbVbvaPartial)
1664 {
1665 /* New data has been added to the record. */
1666 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
1667 {
1668 return false;
1669 }
1670 }
1671
1672 if (!(pRecord->cbRecord & VBVA_F_RECORD_PARTIAL))
1673 {
1674 /* The record is completed by guest. Return it to the caller. */
1675 *ppHdr = (VBVACMDHDR *)mpu8VbvaPartial;
1676 *pcbCmd = mcbVbvaPartial;
1677
1678 mpu8VbvaPartial = NULL;
1679 mcbVbvaPartial = 0;
1680
1681 /* Advance the record index. */
1682 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
1683
1684#ifdef DEBUG_sunlover
1685 LogFlowFunc (("partial done ok, data = %d, free = %d\n",
1686 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1687#endif /* DEBUG_sunlover */
1688 }
1689
1690 return true;
1691 }
1692
1693 /* A new record need to be processed. */
1694 if (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)
1695 {
1696 /* Current record is being written by guest. '=' is important here. */
1697 if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
1698 {
1699 /* Partial read must be started. */
1700 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
1701 {
1702 return false;
1703 }
1704
1705 LogFlowFunc (("started partial record mcbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
1706 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
1707 }
1708
1709 return true;
1710 }
1711
1712 /* Current record is complete. If it is not empty, process it. */
1713 if (cbRecord)
1714 {
1715 /* The size of largest contiguous chunk in the ring biffer. */
1716 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - mpVbvaMemory->off32Data;
1717
1718 /* The ring buffer pointer. */
1719 uint8_t *au8RingBuffer = &mpVbvaMemory->au8RingBuffer[0];
1720
1721 /* The pointer to data in the ring buffer. */
1722 uint8_t *src = &au8RingBuffer[mpVbvaMemory->off32Data];
1723
1724 /* Fetch or point the data. */
1725 if (u32BytesTillBoundary >= cbRecord)
1726 {
1727 /* The command does not cross buffer boundary. Return address in the buffer. */
1728 *ppHdr = (VBVACMDHDR *)src;
1729
1730 /* Advance data offset. */
1731 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
1732 }
1733 else
1734 {
1735 /* The command crosses buffer boundary. Rare case, so not optimized. */
1736 uint8_t *dst = (uint8_t *)RTMemAlloc (cbRecord);
1737
1738 if (!dst)
1739 {
1740 LogRelFlowFunc (("could not allocate %d bytes from heap!!!\n", cbRecord));
1741 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
1742 return false;
1743 }
1744
1745 vbvaFetchBytes (mpVbvaMemory, dst, cbRecord);
1746
1747 *ppHdr = (VBVACMDHDR *)dst;
1748
1749#ifdef DEBUG_sunlover
1750 LogFlowFunc (("Allocated from heap %p\n", dst));
1751#endif /* DEBUG_sunlover */
1752 }
1753 }
1754
1755 *pcbCmd = cbRecord;
1756
1757 /* Advance the record index. */
1758 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
1759
1760#ifdef DEBUG_sunlover
1761 LogFlowFunc (("done ok, data = %d, free = %d\n",
1762 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1763#endif /* DEBUG_sunlover */
1764
1765 return true;
1766}
1767
1768void Display::vbvaReleaseCmd (VBVACMDHDR *pHdr, int32_t cbCmd)
1769{
1770 uint8_t *au8RingBuffer = mpVbvaMemory->au8RingBuffer;
1771
1772 if ( (uint8_t *)pHdr >= au8RingBuffer
1773 && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
1774 {
1775 /* The pointer is inside ring buffer. Must be continuous chunk. */
1776 Assert (VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
1777
1778 /* Do nothing. */
1779
1780 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1781 }
1782 else
1783 {
1784 /* The pointer is outside. It is then an allocated copy. */
1785
1786#ifdef DEBUG_sunlover
1787 LogFlowFunc (("Free heap %p\n", pHdr));
1788#endif /* DEBUG_sunlover */
1789
1790 if ((uint8_t *)pHdr == mpu8VbvaPartial)
1791 {
1792 mpu8VbvaPartial = NULL;
1793 mcbVbvaPartial = 0;
1794 }
1795 else
1796 {
1797 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1798 }
1799
1800 RTMemFree (pHdr);
1801 }
1802
1803 return;
1804}
1805
1806
1807/**
1808 * Called regularly on the DisplayRefresh timer.
1809 * Also on behalf of guest, when the ring buffer is full.
1810 *
1811 * @thread EMT
1812 */
1813void Display::VideoAccelFlush (void)
1814{
1815 vbvaLock();
1816 videoAccelFlush();
1817 vbvaUnlock();
1818}
1819
1820/* Under VBVA lock. DevVGA is not taken. */
1821void Display::videoAccelFlush (void)
1822{
1823#ifdef DEBUG_sunlover_2
1824 LogFlowFunc (("mfVideoAccelEnabled = %d\n", mfVideoAccelEnabled));
1825#endif /* DEBUG_sunlover_2 */
1826
1827 if (!mfVideoAccelEnabled)
1828 {
1829 Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
1830 return;
1831 }
1832
1833 /* Here VBVA is enabled and we have the accelerator memory pointer. */
1834 Assert(mpVbvaMemory);
1835
1836#ifdef DEBUG_sunlover_2
1837 LogFlowFunc (("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
1838 mpVbvaMemory->indexRecordFirst, mpVbvaMemory->indexRecordFree, mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1839#endif /* DEBUG_sunlover_2 */
1840
1841 /* Quick check for "nothing to update" case. */
1842 if (mpVbvaMemory->indexRecordFirst == mpVbvaMemory->indexRecordFree)
1843 {
1844 return;
1845 }
1846
1847 /* Process the ring buffer */
1848 unsigned uScreenId;
1849
1850 /* Initialize dirty rectangles accumulator. */
1851 VBVADIRTYREGION rgn;
1852 vbvaRgnInit (&rgn, maFramebuffers, mcMonitors, this, mpDrv->pUpPort);
1853
1854 for (;;)
1855 {
1856 VBVACMDHDR *phdr = NULL;
1857 uint32_t cbCmd = ~0;
1858
1859 /* Fetch the command data. */
1860 if (!vbvaFetchCmd (&phdr, &cbCmd))
1861 {
1862 Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
1863 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1864
1865 /* Disable VBVA on those processing errors. */
1866 videoAccelEnable (false, NULL);
1867
1868 break;
1869 }
1870
1871 if (cbCmd == uint32_t(~0))
1872 {
1873 /* No more commands yet in the queue. */
1874 break;
1875 }
1876
1877 if (cbCmd != 0)
1878 {
1879#ifdef DEBUG_sunlover
1880 LogFlowFunc (("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
1881 cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
1882#endif /* DEBUG_sunlover */
1883
1884 VBVACMDHDR hdrSaved = *phdr;
1885
1886 int x = phdr->x;
1887 int y = phdr->y;
1888 int w = phdr->w;
1889 int h = phdr->h;
1890
1891 uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
1892
1893 phdr->x = (int16_t)x;
1894 phdr->y = (int16_t)y;
1895 phdr->w = (uint16_t)w;
1896 phdr->h = (uint16_t)h;
1897
1898 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1899
1900 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
1901 {
1902 /* Handle the command.
1903 *
1904 * Guest is responsible for updating the guest video memory.
1905 * The Windows guest does all drawing using Eng*.
1906 *
1907 * For local output, only dirty rectangle information is used
1908 * to update changed areas.
1909 *
1910 * Dirty rectangles are accumulated to exclude overlapping updates and
1911 * group small updates to a larger one.
1912 */
1913
1914 /* Accumulate the update. */
1915 vbvaRgnDirtyRect (&rgn, uScreenId, phdr);
1916
1917 /* Forward the command to VRDP server. */
1918 mParent->consoleVRDPServer()->SendUpdate (uScreenId, phdr, cbCmd);
1919
1920 *phdr = hdrSaved;
1921 }
1922 }
1923
1924 vbvaReleaseCmd (phdr, cbCmd);
1925 }
1926
1927 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1928 {
1929 if (maFramebuffers[uScreenId].u32ResizeStatus == ResizeStatus_Void)
1930 {
1931 /* Draw the framebuffer. */
1932 vbvaRgnUpdateFramebuffer (&rgn, uScreenId);
1933 }
1934 }
1935}
1936
1937int Display::videoAccelRefreshProcess(void)
1938{
1939 int rc = VWRN_INVALID_STATE; /* Default is to do a display update in VGA device. */
1940
1941 vbvaLock();
1942
1943 if (ASMAtomicCmpXchgU32(&mfu32PendingVideoAccelDisable, false, true))
1944 {
1945 videoAccelEnable (false, NULL);
1946 }
1947 else if (mfPendingVideoAccelEnable)
1948 {
1949 /* Acceleration was enabled while machine was not yet running
1950 * due to restoring from saved state. Update entire display and
1951 * actually enable acceleration.
1952 */
1953 Assert(mpPendingVbvaMemory);
1954
1955 /* Acceleration can not be yet enabled.*/
1956 Assert(mpVbvaMemory == NULL);
1957 Assert(!mfVideoAccelEnabled);
1958
1959 if (mfMachineRunning)
1960 {
1961 videoAccelEnable (mfPendingVideoAccelEnable,
1962 mpPendingVbvaMemory);
1963
1964 /* Reset the pending state. */
1965 mfPendingVideoAccelEnable = false;
1966 mpPendingVbvaMemory = NULL;
1967 }
1968
1969 rc = VINF_TRY_AGAIN;
1970 }
1971 else
1972 {
1973 Assert(mpPendingVbvaMemory == NULL);
1974
1975 if (mfVideoAccelEnabled)
1976 {
1977 Assert(mpVbvaMemory);
1978 videoAccelFlush ();
1979
1980 rc = VINF_SUCCESS; /* VBVA processed, no need to a display update. */
1981 }
1982 }
1983
1984 vbvaUnlock();
1985
1986 return rc;
1987}
1988
1989
1990// IDisplay methods
1991/////////////////////////////////////////////////////////////////////////////
1992STDMETHODIMP Display::GetScreenResolution (ULONG aScreenId,
1993 ULONG *aWidth, ULONG *aHeight, ULONG *aBitsPerPixel)
1994{
1995 LogRelFlowFunc (("aScreenId = %d\n", aScreenId));
1996
1997 AutoCaller autoCaller(this);
1998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1999
2000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2001
2002 uint32_t u32Width = 0;
2003 uint32_t u32Height = 0;
2004 uint32_t u32BitsPerPixel = 0;
2005
2006 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2007 {
2008 CHECK_CONSOLE_DRV (mpDrv);
2009
2010 u32Width = mpDrv->IConnector.cx;
2011 u32Height = mpDrv->IConnector.cy;
2012 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &u32BitsPerPixel);
2013 AssertRC(rc);
2014 }
2015 else if (aScreenId < mcMonitors)
2016 {
2017 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2018 u32Width = pFBInfo->w;
2019 u32Height = pFBInfo->h;
2020 u32BitsPerPixel = pFBInfo->u16BitsPerPixel;
2021 }
2022 else
2023 {
2024 return E_INVALIDARG;
2025 }
2026
2027 if (aWidth)
2028 *aWidth = u32Width;
2029 if (aHeight)
2030 *aHeight = u32Height;
2031 if (aBitsPerPixel)
2032 *aBitsPerPixel = u32BitsPerPixel;
2033
2034 return S_OK;
2035}
2036
2037STDMETHODIMP Display::SetFramebuffer (ULONG aScreenId,
2038 IFramebuffer *aFramebuffer)
2039{
2040 LogRelFlowFunc (("\n"));
2041
2042 if (aFramebuffer != NULL)
2043 CheckComArgOutPointerValid(aFramebuffer);
2044
2045 AutoCaller autoCaller(this);
2046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2047
2048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 Console::SafeVMPtrQuiet pVM (mParent);
2051 if (pVM.isOk())
2052 {
2053 /* Must release the lock here because the changeFramebuffer will
2054 * also obtain it. */
2055 alock.release();
2056
2057 /* send request to the EMT thread */
2058 int vrc = VMR3ReqCallWait (pVM, VMCPUID_ANY,
2059 (PFNRT) changeFramebuffer, 3, this, aFramebuffer, aScreenId);
2060
2061 alock.acquire();
2062
2063 ComAssertRCRet (vrc, E_FAIL);
2064
2065#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2066 {
2067 BOOL is3denabled;
2068 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2069
2070 if (is3denabled)
2071 {
2072 VBOXHGCMSVCPARM parm;
2073
2074 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
2075 parm.u.uint32 = aScreenId;
2076
2077 VMMDev *pVMMDev = mParent->getVMMDev();
2078
2079 alock.release();
2080
2081 if (pVMMDev)
2082 vrc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SCREEN_CHANGED, SHCRGL_CPARMS_SCREEN_CHANGED, &parm);
2083 /*ComAssertRCRet (vrc, E_FAIL);*/
2084
2085 alock.acquire();
2086 }
2087 }
2088#endif /* VBOX_WITH_CROGL */
2089 }
2090 else
2091 {
2092 /* No VM is created (VM is powered off), do a direct call */
2093 int vrc = changeFramebuffer (this, aFramebuffer, aScreenId);
2094 ComAssertRCRet (vrc, E_FAIL);
2095 }
2096
2097 return S_OK;
2098}
2099
2100STDMETHODIMP Display::GetFramebuffer (ULONG aScreenId,
2101 IFramebuffer **aFramebuffer, LONG *aXOrigin, LONG *aYOrigin)
2102{
2103 LogRelFlowFunc (("aScreenId = %d\n", aScreenId));
2104
2105 CheckComArgOutPointerValid(aFramebuffer);
2106
2107 AutoCaller autoCaller(this);
2108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2109
2110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2111
2112 if (aScreenId != 0 && aScreenId >= mcMonitors)
2113 return E_INVALIDARG;
2114
2115 /* @todo this should be actually done on EMT. */
2116 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
2117
2118 *aFramebuffer = pFBInfo->pFramebuffer;
2119 if (*aFramebuffer)
2120 (*aFramebuffer)->AddRef ();
2121 if (aXOrigin)
2122 *aXOrigin = pFBInfo->xOrigin;
2123 if (aYOrigin)
2124 *aYOrigin = pFBInfo->yOrigin;
2125
2126 return S_OK;
2127}
2128
2129STDMETHODIMP Display::SetVideoModeHint(ULONG aDisplay, BOOL aEnabled,
2130 BOOL aChangeOrigin, LONG aOriginX, LONG aOriginY,
2131 ULONG aWidth, ULONG aHeight, ULONG aBitsPerPixel)
2132{
2133 AutoCaller autoCaller(this);
2134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2135
2136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2137
2138 CHECK_CONSOLE_DRV (mpDrv);
2139
2140 /* XXX Ignore these parameters for now: */
2141 NOREF(aChangeOrigin);
2142 NOREF(aOriginX);
2143 NOREF(aOriginY);
2144 NOREF(aEnabled);
2145
2146 /*
2147 * Do some rough checks for valid input
2148 */
2149 ULONG width = aWidth;
2150 if (!width)
2151 width = mpDrv->IConnector.cx;
2152 ULONG height = aHeight;
2153 if (!height)
2154 height = mpDrv->IConnector.cy;
2155 ULONG bpp = aBitsPerPixel;
2156 if (!bpp)
2157 {
2158 uint32_t cBits = 0;
2159 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
2160 AssertRC(rc);
2161 bpp = cBits;
2162 }
2163 ULONG cMonitors;
2164 mParent->machine()->COMGETTER(MonitorCount)(&cMonitors);
2165 if (cMonitors == 0 && aDisplay > 0)
2166 return E_INVALIDARG;
2167 if (aDisplay >= cMonitors)
2168 return E_INVALIDARG;
2169
2170// sunlover 20070614: It is up to the guest to decide whether the hint is valid.
2171// ULONG vramSize;
2172// mParent->machine()->COMGETTER(VRAMSize)(&vramSize);
2173// /* enough VRAM? */
2174// if ((width * height * (bpp / 8)) > (vramSize * 1024 * 1024))
2175// return setError(E_FAIL, tr("Not enough VRAM for the selected video mode"));
2176
2177 /* Have to release the lock because the pfnRequestDisplayChange
2178 * will call EMT. */
2179 alock.release();
2180
2181 VMMDev *pVMMDev = mParent->getVMMDev();
2182 if (pVMMDev)
2183 {
2184 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
2185 if (pVMMDevPort)
2186 pVMMDevPort->pfnRequestDisplayChange(pVMMDevPort, aWidth, aHeight, aBitsPerPixel, aDisplay);
2187 }
2188 return S_OK;
2189}
2190
2191STDMETHODIMP Display::SetSeamlessMode (BOOL enabled)
2192{
2193 AutoCaller autoCaller(this);
2194 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2195
2196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2197
2198 /* Have to release the lock because the pfnRequestSeamlessChange will call EMT. */
2199 alock.release();
2200
2201 VMMDev *pVMMDev = mParent->getVMMDev();
2202 if (pVMMDev)
2203 {
2204 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
2205 if (pVMMDevPort)
2206 pVMMDevPort->pfnRequestSeamlessChange(pVMMDevPort, !!enabled);
2207 }
2208 return S_OK;
2209}
2210
2211int Display::displayTakeScreenshotEMT(Display *pDisplay, ULONG aScreenId, uint8_t **ppu8Data, size_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height)
2212{
2213 int rc;
2214 pDisplay->vbvaLock();
2215 if ( aScreenId == VBOX_VIDEO_PRIMARY_SCREEN
2216 && pDisplay->maFramebuffers[aScreenId].fVBVAEnabled == false) /* A non-VBVA mode. */
2217 {
2218 rc = pDisplay->mpDrv->pUpPort->pfnTakeScreenshot(pDisplay->mpDrv->pUpPort, ppu8Data, pcbData, pu32Width, pu32Height);
2219 }
2220 else if (aScreenId < pDisplay->mcMonitors)
2221 {
2222 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2223
2224 uint32_t width = pFBInfo->w;
2225 uint32_t height = pFBInfo->h;
2226
2227 /* Allocate 32 bit per pixel bitmap. */
2228 size_t cbRequired = width * 4 * height;
2229
2230 if (cbRequired)
2231 {
2232 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbRequired);
2233
2234 if (pu8Data == NULL)
2235 {
2236 rc = VERR_NO_MEMORY;
2237 }
2238 else
2239 {
2240 /* Copy guest VRAM to the allocated 32bpp buffer. */
2241 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
2242 int32_t xSrc = 0;
2243 int32_t ySrc = 0;
2244 uint32_t u32SrcWidth = width;
2245 uint32_t u32SrcHeight = height;
2246 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
2247 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2248
2249 uint8_t *pu8Dst = pu8Data;
2250 int32_t xDst = 0;
2251 int32_t yDst = 0;
2252 uint32_t u32DstWidth = u32SrcWidth;
2253 uint32_t u32DstHeight = u32SrcHeight;
2254 uint32_t u32DstLineSize = u32DstWidth * 4;
2255 uint32_t u32DstBitsPerPixel = 32;
2256
2257 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2258 width, height,
2259 pu8Src,
2260 xSrc, ySrc,
2261 u32SrcWidth, u32SrcHeight,
2262 u32SrcLineSize, u32SrcBitsPerPixel,
2263 pu8Dst,
2264 xDst, yDst,
2265 u32DstWidth, u32DstHeight,
2266 u32DstLineSize, u32DstBitsPerPixel);
2267 if (RT_SUCCESS(rc))
2268 {
2269 *ppu8Data = pu8Data;
2270 *pcbData = cbRequired;
2271 *pu32Width = width;
2272 *pu32Height = height;
2273 }
2274 else
2275 {
2276 RTMemFree(pu8Data);
2277 }
2278 }
2279 }
2280 else
2281 {
2282 /* No image. */
2283 *ppu8Data = NULL;
2284 *pcbData = 0;
2285 *pu32Width = 0;
2286 *pu32Height = 0;
2287 rc = VINF_SUCCESS;
2288 }
2289 }
2290 else
2291 {
2292 rc = VERR_INVALID_PARAMETER;
2293 }
2294 pDisplay->vbvaUnlock();
2295 return rc;
2296}
2297
2298static int displayTakeScreenshot(PVM pVM, Display *pDisplay, struct DRVMAINDISPLAY *pDrv, ULONG aScreenId, BYTE *address, ULONG width, ULONG height)
2299{
2300 uint8_t *pu8Data = NULL;
2301 size_t cbData = 0;
2302 uint32_t cx = 0;
2303 uint32_t cy = 0;
2304 int vrc = VINF_SUCCESS;
2305
2306 int cRetries = 5;
2307
2308 while (cRetries-- > 0)
2309 {
2310 /* Note! Not sure if the priority call is such a good idea here, but
2311 it would be nice to have an accurate screenshot for the bug
2312 report if the VM deadlocks. */
2313 vrc = VMR3ReqPriorityCallWait(pVM, VMCPUID_ANY, (PFNRT)Display::displayTakeScreenshotEMT, 6,
2314 pDisplay, aScreenId, &pu8Data, &cbData, &cx, &cy);
2315 if (vrc != VERR_TRY_AGAIN)
2316 {
2317 break;
2318 }
2319
2320 RTThreadSleep(10);
2321 }
2322
2323 if (RT_SUCCESS(vrc) && pu8Data)
2324 {
2325 if (cx == width && cy == height)
2326 {
2327 /* No scaling required. */
2328 memcpy(address, pu8Data, cbData);
2329 }
2330 else
2331 {
2332 /* Scale. */
2333 LogRelFlowFunc(("SCALE: %dx%d -> %dx%d\n", cx, cy, width, height));
2334
2335 uint8_t *dst = address;
2336 uint8_t *src = pu8Data;
2337 int dstW = width;
2338 int dstH = height;
2339 int srcW = cx;
2340 int srcH = cy;
2341 int iDeltaLine = cx * 4;
2342
2343 BitmapScale32(dst,
2344 dstW, dstH,
2345 src,
2346 iDeltaLine,
2347 srcW, srcH);
2348 }
2349
2350 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2351 {
2352 /* This can be called from any thread. */
2353 pDrv->pUpPort->pfnFreeScreenshot(pDrv->pUpPort, pu8Data);
2354 }
2355 else
2356 {
2357 RTMemFree(pu8Data);
2358 }
2359 }
2360
2361 return vrc;
2362}
2363
2364STDMETHODIMP Display::TakeScreenShot(ULONG aScreenId, BYTE *address, ULONG width, ULONG height)
2365{
2366 /// @todo (r=dmik) this function may take too long to complete if the VM
2367 // is doing something like saving state right now. Which, in case if it
2368 // is called on the GUI thread, will make it unresponsive. We should
2369 // check the machine state here (by enclosing the check and VMRequCall
2370 // within the Console lock to make it atomic).
2371
2372 LogRelFlowFunc(("address=%p, width=%d, height=%d\n",
2373 address, width, height));
2374
2375 CheckComArgNotNull(address);
2376 CheckComArgExpr(width, width != 0);
2377 CheckComArgExpr(height, height != 0);
2378
2379 /* Do not allow too large screenshots. This also filters out negative
2380 * values passed as either 'width' or 'height'.
2381 */
2382 CheckComArgExpr(width, width <= 32767);
2383 CheckComArgExpr(height, height <= 32767);
2384
2385 AutoCaller autoCaller(this);
2386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2387
2388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2389
2390 CHECK_CONSOLE_DRV(mpDrv);
2391
2392 Console::SafeVMPtr pVM(mParent);
2393 if (FAILED(pVM.rc())) return pVM.rc();
2394
2395 HRESULT rc = S_OK;
2396
2397 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
2398
2399 /* Release lock because other thread (EMT) is called and it may initiate a resize
2400 * which also needs lock.
2401 *
2402 * This method does not need the lock anymore.
2403 */
2404 alock.release();
2405
2406 int vrc = displayTakeScreenshot(pVM, this, mpDrv, aScreenId, address, width, height);
2407
2408 if (vrc == VERR_NOT_IMPLEMENTED)
2409 rc = setError(E_NOTIMPL,
2410 tr("This feature is not implemented"));
2411 else if (vrc == VERR_TRY_AGAIN)
2412 rc = setError(E_UNEXPECTED,
2413 tr("This feature is not available at this time"));
2414 else if (RT_FAILURE(vrc))
2415 rc = setError(VBOX_E_IPRT_ERROR,
2416 tr("Could not take a screenshot (%Rrc)"), vrc);
2417
2418 LogRelFlowFunc(("rc=%08X\n", rc));
2419 return rc;
2420}
2421
2422STDMETHODIMP Display::TakeScreenShotToArray(ULONG aScreenId, ULONG width, ULONG height,
2423 ComSafeArrayOut(BYTE, aScreenData))
2424{
2425 LogRelFlowFunc(("width=%d, height=%d\n", width, height));
2426
2427 CheckComArgOutSafeArrayPointerValid(aScreenData);
2428 CheckComArgExpr(width, width != 0);
2429 CheckComArgExpr(height, height != 0);
2430
2431 /* Do not allow too large screenshots. This also filters out negative
2432 * values passed as either 'width' or 'height'.
2433 */
2434 CheckComArgExpr(width, width <= 32767);
2435 CheckComArgExpr(height, height <= 32767);
2436
2437 AutoCaller autoCaller(this);
2438 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2439
2440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2441
2442 CHECK_CONSOLE_DRV(mpDrv);
2443
2444 Console::SafeVMPtr pVM(mParent);
2445 if (FAILED(pVM.rc())) return pVM.rc();
2446
2447 HRESULT rc = S_OK;
2448
2449 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
2450
2451 /* Release lock because other thread (EMT) is called and it may initiate a resize
2452 * which also needs lock.
2453 *
2454 * This method does not need the lock anymore.
2455 */
2456 alock.release();
2457
2458 size_t cbData = width * 4 * height;
2459 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbData);
2460
2461 if (!pu8Data)
2462 return E_OUTOFMEMORY;
2463
2464 int vrc = displayTakeScreenshot(pVM, this, mpDrv, aScreenId, pu8Data, width, height);
2465
2466 if (RT_SUCCESS(vrc))
2467 {
2468 /* Convert pixels to format expected by the API caller: [0] R, [1] G, [2] B, [3] A. */
2469 uint8_t *pu8 = pu8Data;
2470 unsigned cPixels = width * height;
2471 while (cPixels)
2472 {
2473 uint8_t u8 = pu8[0];
2474 pu8[0] = pu8[2];
2475 pu8[2] = u8;
2476 pu8[3] = 0xff;
2477 cPixels--;
2478 pu8 += 4;
2479 }
2480
2481 com::SafeArray<BYTE> screenData(cbData);
2482 screenData.initFrom(pu8Data, cbData);
2483 screenData.detachTo(ComSafeArrayOutArg(aScreenData));
2484 }
2485 else if (vrc == VERR_NOT_IMPLEMENTED)
2486 rc = setError(E_NOTIMPL,
2487 tr("This feature is not implemented"));
2488 else
2489 rc = setError(VBOX_E_IPRT_ERROR,
2490 tr("Could not take a screenshot (%Rrc)"), vrc);
2491
2492 RTMemFree(pu8Data);
2493
2494 LogRelFlowFunc(("rc=%08X\n", rc));
2495 return rc;
2496}
2497
2498STDMETHODIMP Display::TakeScreenShotPNGToArray(ULONG aScreenId, ULONG width, ULONG height,
2499 ComSafeArrayOut(BYTE, aScreenData))
2500{
2501 LogRelFlowFunc(("width=%d, height=%d\n", width, height));
2502
2503 CheckComArgOutSafeArrayPointerValid(aScreenData);
2504 CheckComArgExpr(width, width != 0);
2505 CheckComArgExpr(height, height != 0);
2506
2507 /* Do not allow too large screenshots. This also filters out negative
2508 * values passed as either 'width' or 'height'.
2509 */
2510 CheckComArgExpr(width, width <= 32767);
2511 CheckComArgExpr(height, height <= 32767);
2512
2513 AutoCaller autoCaller(this);
2514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2515
2516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 CHECK_CONSOLE_DRV(mpDrv);
2519
2520 Console::SafeVMPtr pVM(mParent);
2521 if (FAILED(pVM.rc())) return pVM.rc();
2522
2523 HRESULT rc = S_OK;
2524
2525 LogRelFlowFunc(("Sending SCREENSHOT request\n"));
2526
2527 /* Release lock because other thread (EMT) is called and it may initiate a resize
2528 * which also needs lock.
2529 *
2530 * This method does not need the lock anymore.
2531 */
2532 alock.release();
2533
2534 size_t cbData = width * 4 * height;
2535 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbData);
2536
2537 if (!pu8Data)
2538 return E_OUTOFMEMORY;
2539
2540 int vrc = displayTakeScreenshot(pVM, this, mpDrv, aScreenId, pu8Data, width, height);
2541
2542 if (RT_SUCCESS(vrc))
2543 {
2544 uint8_t *pu8PNG = NULL;
2545 uint32_t cbPNG = 0;
2546 uint32_t cxPNG = 0;
2547 uint32_t cyPNG = 0;
2548
2549 vrc = DisplayMakePNG(pu8Data, width, height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
2550 if (RT_SUCCESS(vrc))
2551 {
2552 com::SafeArray<BYTE> screenData(cbPNG);
2553 screenData.initFrom(pu8PNG, cbPNG);
2554 if (pu8PNG)
2555 RTMemFree(pu8PNG);
2556
2557 screenData.detachTo(ComSafeArrayOutArg(aScreenData));
2558 }
2559 else
2560 {
2561 if (pu8PNG)
2562 RTMemFree(pu8PNG);
2563 rc = setError(VBOX_E_IPRT_ERROR,
2564 tr("Could not convert screenshot to PNG (%Rrc)"), vrc);
2565 }
2566 }
2567 else if (vrc == VERR_NOT_IMPLEMENTED)
2568 rc = setError(E_NOTIMPL,
2569 tr("This feature is not implemented"));
2570 else
2571 rc = setError(VBOX_E_IPRT_ERROR,
2572 tr("Could not take a screenshot (%Rrc)"), vrc);
2573
2574 RTMemFree(pu8Data);
2575
2576 LogRelFlowFunc(("rc=%08X\n", rc));
2577 return rc;
2578}
2579
2580
2581int Display::drawToScreenEMT(Display *pDisplay, ULONG aScreenId, BYTE *address, ULONG x, ULONG y, ULONG width, ULONG height)
2582{
2583 int rc = VINF_SUCCESS;
2584 pDisplay->vbvaLock();
2585
2586 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[aScreenId];
2587
2588 if (aScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
2589 {
2590 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
2591 {
2592 rc = pDisplay->mpDrv->pUpPort->pfnDisplayBlt(pDisplay->mpDrv->pUpPort, address, x, y, width, height);
2593 }
2594 }
2595 else if (aScreenId < pDisplay->mcMonitors)
2596 {
2597 /* Copy the bitmap to the guest VRAM. */
2598 const uint8_t *pu8Src = address;
2599 int32_t xSrc = 0;
2600 int32_t ySrc = 0;
2601 uint32_t u32SrcWidth = width;
2602 uint32_t u32SrcHeight = height;
2603 uint32_t u32SrcLineSize = width * 4;
2604 uint32_t u32SrcBitsPerPixel = 32;
2605
2606 uint8_t *pu8Dst = pFBInfo->pu8FramebufferVRAM;
2607 int32_t xDst = x;
2608 int32_t yDst = y;
2609 uint32_t u32DstWidth = pFBInfo->w;
2610 uint32_t u32DstHeight = pFBInfo->h;
2611 uint32_t u32DstLineSize = pFBInfo->u32LineSize;
2612 uint32_t u32DstBitsPerPixel = pFBInfo->u16BitsPerPixel;
2613
2614 rc = pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2615 width, height,
2616 pu8Src,
2617 xSrc, ySrc,
2618 u32SrcWidth, u32SrcHeight,
2619 u32SrcLineSize, u32SrcBitsPerPixel,
2620 pu8Dst,
2621 xDst, yDst,
2622 u32DstWidth, u32DstHeight,
2623 u32DstLineSize, u32DstBitsPerPixel);
2624 if (RT_SUCCESS(rc))
2625 {
2626 if (!pFBInfo->pFramebuffer.isNull())
2627 {
2628 /* Update the changed screen area. When framebuffer uses VRAM directly, just notify
2629 * it to update. And for default format, render the guest VRAM to framebuffer.
2630 */
2631 if ( pFBInfo->fDefaultFormat
2632 && !(pFBInfo->fDisabled))
2633 {
2634 address = NULL;
2635 HRESULT hrc = pFBInfo->pFramebuffer->COMGETTER(Address) (&address);
2636 if (SUCCEEDED(hrc) && address != NULL)
2637 {
2638 pu8Src = pFBInfo->pu8FramebufferVRAM;
2639 xSrc = x;
2640 ySrc = y;
2641 u32SrcWidth = pFBInfo->w;
2642 u32SrcHeight = pFBInfo->h;
2643 u32SrcLineSize = pFBInfo->u32LineSize;
2644 u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2645
2646 /* Default format is 32 bpp. */
2647 pu8Dst = address;
2648 xDst = xSrc;
2649 yDst = ySrc;
2650 u32DstWidth = u32SrcWidth;
2651 u32DstHeight = u32SrcHeight;
2652 u32DstLineSize = u32DstWidth * 4;
2653 u32DstBitsPerPixel = 32;
2654
2655 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2656 width, height,
2657 pu8Src,
2658 xSrc, ySrc,
2659 u32SrcWidth, u32SrcHeight,
2660 u32SrcLineSize, u32SrcBitsPerPixel,
2661 pu8Dst,
2662 xDst, yDst,
2663 u32DstWidth, u32DstHeight,
2664 u32DstLineSize, u32DstBitsPerPixel);
2665 }
2666 }
2667
2668 pDisplay->handleDisplayUpdate(aScreenId, x, y, width, height);
2669 }
2670 }
2671 }
2672 else
2673 {
2674 rc = VERR_INVALID_PARAMETER;
2675 }
2676
2677 if (RT_SUCCESS(rc) && pDisplay->maFramebuffers[aScreenId].u32ResizeStatus == ResizeStatus_Void)
2678 pDisplay->mParent->consoleVRDPServer()->SendUpdateBitmap(aScreenId, x, y, width, height);
2679
2680 pDisplay->vbvaUnlock();
2681 return rc;
2682}
2683
2684STDMETHODIMP Display::DrawToScreen (ULONG aScreenId, BYTE *address, ULONG x, ULONG y,
2685 ULONG width, ULONG height)
2686{
2687 /// @todo (r=dmik) this function may take too long to complete if the VM
2688 // is doing something like saving state right now. Which, in case if it
2689 // is called on the GUI thread, will make it unresponsive. We should
2690 // check the machine state here (by enclosing the check and VMRequCall
2691 // within the Console lock to make it atomic).
2692
2693 LogRelFlowFunc (("address=%p, x=%d, y=%d, width=%d, height=%d\n",
2694 (void *)address, x, y, width, height));
2695
2696 CheckComArgNotNull(address);
2697 CheckComArgExpr(width, width != 0);
2698 CheckComArgExpr(height, height != 0);
2699
2700 AutoCaller autoCaller(this);
2701 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2702
2703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 CHECK_CONSOLE_DRV (mpDrv);
2706
2707 Console::SafeVMPtr pVM(mParent);
2708 if (FAILED(pVM.rc())) return pVM.rc();
2709
2710 /* Release lock because the call scheduled on EMT may also try to take it. */
2711 alock.release();
2712
2713 /*
2714 * Again we're lazy and make the graphics device do all the
2715 * dirty conversion work.
2716 */
2717 int rcVBox = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)Display::drawToScreenEMT, 7,
2718 this, aScreenId, address, x, y, width, height);
2719
2720 /*
2721 * If the function returns not supported, we'll have to do all the
2722 * work ourselves using the framebuffer.
2723 */
2724 HRESULT rc = S_OK;
2725 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
2726 {
2727 /** @todo implement generic fallback for screen blitting. */
2728 rc = E_NOTIMPL;
2729 }
2730 else if (RT_FAILURE(rcVBox))
2731 rc = setError(VBOX_E_IPRT_ERROR,
2732 tr("Could not draw to the screen (%Rrc)"), rcVBox);
2733//@todo
2734// else
2735// {
2736// /* All ok. Redraw the screen. */
2737// handleDisplayUpdate (x, y, width, height);
2738// }
2739
2740 LogRelFlowFunc (("rc=%08X\n", rc));
2741 return rc;
2742}
2743
2744void Display::InvalidateAndUpdateEMT(Display *pDisplay)
2745{
2746 pDisplay->vbvaLock();
2747 unsigned uScreenId;
2748 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
2749 {
2750 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2751
2752 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && !pFBInfo->pFramebuffer.isNull())
2753 {
2754 pDisplay->mpDrv->pUpPort->pfnUpdateDisplayAll(pDisplay->mpDrv->pUpPort);
2755 }
2756 else
2757 {
2758 if ( !pFBInfo->pFramebuffer.isNull()
2759 && !(pFBInfo->fDisabled))
2760 {
2761 /* Render complete VRAM screen to the framebuffer.
2762 * When framebuffer uses VRAM directly, just notify it to update.
2763 */
2764 if (pFBInfo->fDefaultFormat)
2765 {
2766 BYTE *address = NULL;
2767 HRESULT hrc = pFBInfo->pFramebuffer->COMGETTER(Address) (&address);
2768 if (SUCCEEDED(hrc) && address != NULL)
2769 {
2770 uint32_t width = pFBInfo->w;
2771 uint32_t height = pFBInfo->h;
2772
2773 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
2774 int32_t xSrc = 0;
2775 int32_t ySrc = 0;
2776 uint32_t u32SrcWidth = pFBInfo->w;
2777 uint32_t u32SrcHeight = pFBInfo->h;
2778 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
2779 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
2780
2781 /* Default format is 32 bpp. */
2782 uint8_t *pu8Dst = address;
2783 int32_t xDst = xSrc;
2784 int32_t yDst = ySrc;
2785 uint32_t u32DstWidth = u32SrcWidth;
2786 uint32_t u32DstHeight = u32SrcHeight;
2787 uint32_t u32DstLineSize = u32DstWidth * 4;
2788 uint32_t u32DstBitsPerPixel = 32;
2789
2790 pDisplay->mpDrv->pUpPort->pfnCopyRect(pDisplay->mpDrv->pUpPort,
2791 width, height,
2792 pu8Src,
2793 xSrc, ySrc,
2794 u32SrcWidth, u32SrcHeight,
2795 u32SrcLineSize, u32SrcBitsPerPixel,
2796 pu8Dst,
2797 xDst, yDst,
2798 u32DstWidth, u32DstHeight,
2799 u32DstLineSize, u32DstBitsPerPixel);
2800 }
2801 }
2802
2803 pDisplay->handleDisplayUpdate (uScreenId, 0, 0, pFBInfo->w, pFBInfo->h);
2804 }
2805 }
2806 }
2807 pDisplay->vbvaUnlock();
2808}
2809
2810/**
2811 * Does a full invalidation of the VM display and instructs the VM
2812 * to update it immediately.
2813 *
2814 * @returns COM status code
2815 */
2816STDMETHODIMP Display::InvalidateAndUpdate()
2817{
2818 LogRelFlowFunc(("\n"));
2819
2820 AutoCaller autoCaller(this);
2821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2822
2823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2824
2825 CHECK_CONSOLE_DRV (mpDrv);
2826
2827 Console::SafeVMPtr pVM(mParent);
2828 if (FAILED(pVM.rc())) return pVM.rc();
2829
2830 HRESULT rc = S_OK;
2831
2832 LogRelFlowFunc (("Sending DPYUPDATE request\n"));
2833
2834 /* Have to release the lock when calling EMT. */
2835 alock.release();
2836
2837 /* pdm.h says that this has to be called from the EMT thread */
2838 int rcVBox = VMR3ReqCallVoidWait(pVM, VMCPUID_ANY, (PFNRT)Display::InvalidateAndUpdateEMT,
2839 1, this);
2840 alock.acquire();
2841
2842 if (RT_FAILURE(rcVBox))
2843 rc = setError(VBOX_E_IPRT_ERROR,
2844 tr("Could not invalidate and update the screen (%Rrc)"), rcVBox);
2845
2846 LogRelFlowFunc (("rc=%08X\n", rc));
2847 return rc;
2848}
2849
2850/**
2851 * Notification that the framebuffer has completed the
2852 * asynchronous resize processing
2853 *
2854 * @returns COM status code
2855 */
2856STDMETHODIMP Display::ResizeCompleted(ULONG aScreenId)
2857{
2858 LogRelFlowFunc (("\n"));
2859
2860 /// @todo (dmik) can we AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); here?
2861 // This will require general code review and may add some details.
2862 // In particular, we may want to check whether EMT is really waiting for
2863 // this notification, etc. It might be also good to obey the caller to make
2864 // sure this method is not called from more than one thread at a time
2865 // (and therefore don't use Display lock at all here to save some
2866 // milliseconds).
2867 AutoCaller autoCaller(this);
2868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2869
2870 /* this is only valid for external framebuffers */
2871 if (maFramebuffers[aScreenId].pFramebuffer == NULL)
2872 return setError(VBOX_E_NOT_SUPPORTED,
2873 tr("Resize completed notification is valid only for external framebuffers"));
2874
2875 /* Set the flag indicating that the resize has completed and display
2876 * data need to be updated. */
2877 bool f = ASMAtomicCmpXchgU32 (&maFramebuffers[aScreenId].u32ResizeStatus,
2878 ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
2879 AssertRelease(f);NOREF(f);
2880
2881 return S_OK;
2882}
2883
2884STDMETHODIMP Display::CompleteVHWACommand(BYTE *pCommand)
2885{
2886#ifdef VBOX_WITH_VIDEOHWACCEL
2887 mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsynch(mpDrv->pVBVACallbacks, (PVBOXVHWACMD)pCommand);
2888 return S_OK;
2889#else
2890 return E_NOTIMPL;
2891#endif
2892}
2893
2894STDMETHODIMP Display::ViewportChanged(ULONG aScreenId, ULONG x, ULONG y, ULONG width, ULONG height)
2895{
2896#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
2897 BOOL is3denabled;
2898 mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
2899
2900 if (is3denabled)
2901 {
2902 VBOXHGCMSVCPARM aParms[5];
2903
2904 aParms[0].type = VBOX_HGCM_SVC_PARM_32BIT;
2905 aParms[0].u.uint32 = aScreenId;
2906
2907 aParms[1].type = VBOX_HGCM_SVC_PARM_32BIT;
2908 aParms[1].u.uint32 = x;
2909
2910 aParms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
2911 aParms[2].u.uint32 = y;
2912
2913
2914 aParms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
2915 aParms[3].u.uint32 = width;
2916
2917 aParms[4].type = VBOX_HGCM_SVC_PARM_32BIT;
2918 aParms[4].u.uint32 = height;
2919
2920 VMMDev *pVMMDev = mParent->getVMMDev();
2921
2922 if (pVMMDev)
2923 pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_VIEWPORT_CHANGED, SHCRGL_CPARMS_VIEWPORT_CHANGED, aParms);
2924 }
2925#endif /* VBOX_WITH_CROGL && VBOX_WITH_HGCM */
2926 return S_OK;
2927}
2928
2929// private methods
2930/////////////////////////////////////////////////////////////////////////////
2931
2932/**
2933 * Helper to update the display information from the framebuffer.
2934 *
2935 * @thread EMT
2936 */
2937void Display::updateDisplayData(void)
2938{
2939 LogRelFlowFunc (("\n"));
2940
2941 /* the driver might not have been constructed yet */
2942 if (!mpDrv)
2943 return;
2944
2945#if DEBUG
2946 /*
2947 * Sanity check. Note that this method may be called on EMT after Console
2948 * has started the power down procedure (but before our #drvDestruct() is
2949 * called, in which case pVM will already be NULL but mpDrv will not). Since
2950 * we don't really need pVM to proceed, we avoid this check in the release
2951 * build to save some ms (necessary to construct SafeVMPtrQuiet) in this
2952 * time-critical method.
2953 */
2954 Console::SafeVMPtrQuiet pVM (mParent);
2955 if (pVM.isOk())
2956 VM_ASSERT_EMT (pVM.raw());
2957#endif
2958
2959 /* The method is only relevant to the primary framebuffer. */
2960 IFramebuffer *pFramebuffer = maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].pFramebuffer;
2961
2962 if (pFramebuffer)
2963 {
2964 HRESULT rc;
2965 BYTE *address = 0;
2966 rc = pFramebuffer->COMGETTER(Address) (&address);
2967 AssertComRC (rc);
2968 ULONG bytesPerLine = 0;
2969 rc = pFramebuffer->COMGETTER(BytesPerLine) (&bytesPerLine);
2970 AssertComRC (rc);
2971 ULONG bitsPerPixel = 0;
2972 rc = pFramebuffer->COMGETTER(BitsPerPixel) (&bitsPerPixel);
2973 AssertComRC (rc);
2974 ULONG width = 0;
2975 rc = pFramebuffer->COMGETTER(Width) (&width);
2976 AssertComRC (rc);
2977 ULONG height = 0;
2978 rc = pFramebuffer->COMGETTER(Height) (&height);
2979 AssertComRC (rc);
2980
2981 mpDrv->IConnector.pu8Data = (uint8_t *) address;
2982 mpDrv->IConnector.cbScanline = bytesPerLine;
2983 mpDrv->IConnector.cBits = bitsPerPixel;
2984 mpDrv->IConnector.cx = width;
2985 mpDrv->IConnector.cy = height;
2986 }
2987 else
2988 {
2989 /* black hole */
2990 mpDrv->IConnector.pu8Data = NULL;
2991 mpDrv->IConnector.cbScanline = 0;
2992 mpDrv->IConnector.cBits = 0;
2993 mpDrv->IConnector.cx = 0;
2994 mpDrv->IConnector.cy = 0;
2995 }
2996 LogRelFlowFunc (("leave\n"));
2997}
2998
2999#ifdef VBOX_WITH_CRHGSMI
3000void Display::setupCrHgsmiData(void)
3001{
3002 VMMDev *pVMMDev = mParent->getVMMDev();
3003 Assert(pVMMDev);
3004 int rc = VERR_GENERAL_FAILURE;
3005 if (pVMMDev)
3006 rc = pVMMDev->hgcmHostSvcHandleCreate("VBoxSharedCrOpenGL", &mhCrOglSvc);
3007
3008 if (RT_SUCCESS(rc))
3009 {
3010 Assert(mhCrOglSvc);
3011 /* setup command completion callback */
3012 VBOXVDMACMD_CHROMIUM_CTL_CRHGSMI_SETUP_COMPLETION Completion;
3013 Completion.Hdr.enmType = VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION;
3014 Completion.Hdr.cbCmd = sizeof (Completion);
3015 Completion.hCompletion = mpDrv->pVBVACallbacks;
3016 Completion.pfnCompletion = mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync;
3017
3018 VBOXHGCMSVCPARM parm;
3019 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3020 parm.u.pointer.addr = &Completion;
3021 parm.u.pointer.size = 0;
3022
3023 rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_CRHGSMI_CTL, 1, &parm);
3024 if (RT_SUCCESS(rc))
3025 return;
3026
3027 AssertMsgFailed(("VBOXVDMACMD_CHROMIUM_CTL_TYPE_CRHGSMI_SETUP_COMPLETION failed rc %d", rc));
3028 }
3029
3030 mhCrOglSvc = NULL;
3031}
3032
3033void Display::destructCrHgsmiData(void)
3034{
3035 mhCrOglSvc = NULL;
3036}
3037#endif
3038
3039/**
3040 * Changes the current frame buffer. Called on EMT to avoid both
3041 * race conditions and excessive locking.
3042 *
3043 * @note locks this object for writing
3044 * @thread EMT
3045 */
3046/* static */
3047DECLCALLBACK(int) Display::changeFramebuffer (Display *that, IFramebuffer *aFB,
3048 unsigned uScreenId)
3049{
3050 LogRelFlowFunc (("uScreenId = %d\n", uScreenId));
3051
3052 AssertReturn(that, VERR_INVALID_PARAMETER);
3053 AssertReturn(uScreenId < that->mcMonitors, VERR_INVALID_PARAMETER);
3054
3055 AutoCaller autoCaller(that);
3056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3057
3058 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
3059
3060 DISPLAYFBINFO *pDisplayFBInfo = &that->maFramebuffers[uScreenId];
3061 pDisplayFBInfo->pFramebuffer = aFB;
3062
3063 that->mParent->consoleVRDPServer()->SendResize ();
3064
3065 /* The driver might not have been constructed yet */
3066 if (that->mpDrv)
3067 {
3068 /* Setup the new framebuffer, the resize will lead to an updateDisplayData call. */
3069 DISPLAYFBINFO *pFBInfo = &that->maFramebuffers[uScreenId];
3070
3071#if defined(VBOX_WITH_CROGL)
3072 /* Release the lock, because SHCRGL_HOST_FN_SCREEN_CHANGED will read current framebuffer */
3073 {
3074 BOOL is3denabled;
3075 that->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
3076
3077 if (is3denabled)
3078 {
3079 alock.release();
3080 }
3081 }
3082#endif
3083
3084 if (pFBInfo->fVBVAEnabled && pFBInfo->pu8FramebufferVRAM)
3085 {
3086 /* This display in VBVA mode. Resize it to the last guest resolution,
3087 * if it has been reported.
3088 */
3089 that->handleDisplayResize(uScreenId, pFBInfo->u16BitsPerPixel,
3090 pFBInfo->pu8FramebufferVRAM,
3091 pFBInfo->u32LineSize,
3092 pFBInfo->w,
3093 pFBInfo->h,
3094 pFBInfo->flags);
3095 }
3096 else if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3097 {
3098 /* VGA device mode, only for the primary screen. */
3099 that->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, that->mLastBitsPerPixel,
3100 that->mLastAddress,
3101 that->mLastBytesPerLine,
3102 that->mLastWidth,
3103 that->mLastHeight,
3104 that->mLastFlags);
3105 }
3106 }
3107
3108 LogRelFlowFunc (("leave\n"));
3109 return VINF_SUCCESS;
3110}
3111
3112/**
3113 * Handle display resize event issued by the VGA device for the primary screen.
3114 *
3115 * @see PDMIDISPLAYCONNECTOR::pfnResize
3116 */
3117DECLCALLBACK(int) Display::displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
3118 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
3119{
3120 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3121
3122 LogRelFlowFunc (("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
3123 bpp, pvVRAM, cbLine, cx, cy));
3124
3125 return pDrv->pDisplay->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy, VBVA_SCREEN_F_ACTIVE);
3126}
3127
3128/**
3129 * Handle display update.
3130 *
3131 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
3132 */
3133DECLCALLBACK(void) Display::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
3134 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3135{
3136 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3137
3138#ifdef DEBUG_sunlover
3139 LogFlowFunc (("mfVideoAccelEnabled = %d, %d,%d %dx%d\n",
3140 pDrv->pDisplay->mfVideoAccelEnabled, x, y, cx, cy));
3141#endif /* DEBUG_sunlover */
3142
3143 /* This call does update regardless of VBVA status.
3144 * But in VBVA mode this is called only as result of
3145 * pfnUpdateDisplayAll in the VGA device.
3146 */
3147
3148 pDrv->pDisplay->handleDisplayUpdate(VBOX_VIDEO_PRIMARY_SCREEN, x, y, cx, cy);
3149}
3150
3151/**
3152 * Periodic display refresh callback.
3153 *
3154 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
3155 */
3156DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
3157{
3158 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3159
3160#ifdef DEBUG_sunlover
3161 STAM_PROFILE_START(&StatDisplayRefresh, a);
3162#endif /* DEBUG_sunlover */
3163
3164#ifdef DEBUG_sunlover_2
3165 LogFlowFunc (("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
3166 pDrv->pDisplay->mfVideoAccelEnabled));
3167#endif /* DEBUG_sunlover_2 */
3168
3169 Display *pDisplay = pDrv->pDisplay;
3170 bool fNoUpdate = false; /* Do not update the display if any of the framebuffers is being resized. */
3171 unsigned uScreenId;
3172
3173 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3174 {
3175 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3176
3177 /* Check the resize status. The status can be checked normally because
3178 * the status affects only the EMT.
3179 */
3180 uint32_t u32ResizeStatus = pFBInfo->u32ResizeStatus;
3181
3182 if (u32ResizeStatus == ResizeStatus_UpdateDisplayData)
3183 {
3184 LogRelFlowFunc (("ResizeStatus_UpdateDisplayData %d\n", uScreenId));
3185 fNoUpdate = true; /* Always set it here, because pfnUpdateDisplayAll can cause a new resize. */
3186 /* The framebuffer was resized and display data need to be updated. */
3187 pDisplay->handleResizeCompletedEMT ();
3188 if (pFBInfo->u32ResizeStatus != ResizeStatus_Void)
3189 {
3190 /* The resize status could be not Void here because a pending resize is issued. */
3191 continue;
3192 }
3193 /* Continue with normal processing because the status here is ResizeStatus_Void.
3194 * Repaint all displays because VM continued to run during the framebuffer resize.
3195 */
3196 pDisplay->InvalidateAndUpdateEMT(pDisplay);
3197 }
3198 else if (u32ResizeStatus == ResizeStatus_InProgress)
3199 {
3200 /* The framebuffer is being resized. Do not call the VGA device back. Immediately return. */
3201 LogRelFlowFunc (("ResizeStatus_InProcess\n"));
3202 fNoUpdate = true;
3203 continue;
3204 }
3205 }
3206
3207 if (!fNoUpdate)
3208 {
3209 int rc = pDisplay->videoAccelRefreshProcess();
3210
3211 if (rc != VINF_TRY_AGAIN) /* Means 'do nothing' here. */
3212 {
3213 if (rc == VWRN_INVALID_STATE)
3214 {
3215 /* No VBVA do a display update. */
3216 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN];
3217 if (!pFBInfo->pFramebuffer.isNull() && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3218 {
3219 Assert(pDrv->IConnector.pu8Data);
3220 pDisplay->vbvaLock();
3221 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
3222 pDisplay->vbvaUnlock();
3223 }
3224 }
3225
3226 /* Inform the VRDP server that the current display update sequence is
3227 * completed. At this moment the framebuffer memory contains a definite
3228 * image, that is synchronized with the orders already sent to VRDP client.
3229 * The server can now process redraw requests from clients or initial
3230 * fullscreen updates for new clients.
3231 */
3232 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
3233 {
3234 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
3235
3236 if (!pFBInfo->pFramebuffer.isNull() && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
3237 {
3238 Assert (pDisplay->mParent && pDisplay->mParent->consoleVRDPServer());
3239 pDisplay->mParent->consoleVRDPServer()->SendUpdate (uScreenId, NULL, 0);
3240 }
3241 }
3242 }
3243 }
3244
3245#ifdef DEBUG_sunlover
3246 STAM_PROFILE_STOP(&StatDisplayRefresh, a);
3247#endif /* DEBUG_sunlover */
3248#ifdef DEBUG_sunlover_2
3249 LogFlowFunc (("leave\n"));
3250#endif /* DEBUG_sunlover_2 */
3251}
3252
3253/**
3254 * Reset notification
3255 *
3256 * @see PDMIDISPLAYCONNECTOR::pfnReset
3257 */
3258DECLCALLBACK(void) Display::displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
3259{
3260 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3261
3262 LogRelFlowFunc (("\n"));
3263
3264 /* Disable VBVA mode. */
3265 pDrv->pDisplay->VideoAccelEnable (false, NULL);
3266}
3267
3268/**
3269 * LFBModeChange notification
3270 *
3271 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
3272 */
3273DECLCALLBACK(void) Display::displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
3274{
3275 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3276
3277 LogRelFlowFunc (("fEnabled=%d\n", fEnabled));
3278
3279 NOREF(fEnabled);
3280
3281 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
3282 /* The LFBModeChange function is called under DevVGA lock. Postpone disabling VBVA, do it in the refresh timer. */
3283 ASMAtomicWriteU32(&pDrv->pDisplay->mfu32PendingVideoAccelDisable, true);
3284}
3285
3286/**
3287 * Adapter information change notification.
3288 *
3289 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
3290 */
3291DECLCALLBACK(void) Display::displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, uint32_t u32VRAMSize)
3292{
3293 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3294
3295 if (pvVRAM == NULL)
3296 {
3297 unsigned i;
3298 for (i = 0; i < pDrv->pDisplay->mcMonitors; i++)
3299 {
3300 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[i];
3301
3302 pFBInfo->u32Offset = 0;
3303 pFBInfo->u32MaxFramebufferSize = 0;
3304 pFBInfo->u32InformationSize = 0;
3305 }
3306 }
3307#ifndef VBOX_WITH_HGSMI
3308 else
3309 {
3310 uint8_t *pu8 = (uint8_t *)pvVRAM;
3311 pu8 += u32VRAMSize - VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
3312
3313 // @todo
3314 uint8_t *pu8End = pu8 + VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
3315
3316 VBOXVIDEOINFOHDR *pHdr;
3317
3318 for (;;)
3319 {
3320 pHdr = (VBOXVIDEOINFOHDR *)pu8;
3321 pu8 += sizeof (VBOXVIDEOINFOHDR);
3322
3323 if (pu8 >= pu8End)
3324 {
3325 LogRel(("VBoxVideo: Guest adapter information overflow!!!\n"));
3326 break;
3327 }
3328
3329 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_DISPLAY)
3330 {
3331 if (pHdr->u16Length != sizeof (VBOXVIDEOINFODISPLAY))
3332 {
3333 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "DISPLAY", pHdr->u16Length));
3334 break;
3335 }
3336
3337 VBOXVIDEOINFODISPLAY *pDisplay = (VBOXVIDEOINFODISPLAY *)pu8;
3338
3339 if (pDisplay->u32Index >= pDrv->pDisplay->mcMonitors)
3340 {
3341 LogRel(("VBoxVideo: Guest adapter information invalid display index %d!!!\n", pDisplay->u32Index));
3342 break;
3343 }
3344
3345 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[pDisplay->u32Index];
3346
3347 pFBInfo->u32Offset = pDisplay->u32Offset;
3348 pFBInfo->u32MaxFramebufferSize = pDisplay->u32FramebufferSize;
3349 pFBInfo->u32InformationSize = pDisplay->u32InformationSize;
3350
3351 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_DISPLAY: %d: at 0x%08X, size 0x%08X, info 0x%08X\n", pDisplay->u32Index, pDisplay->u32Offset, pDisplay->u32FramebufferSize, pDisplay->u32InformationSize));
3352 }
3353 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_QUERY_CONF32)
3354 {
3355 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOQUERYCONF32))
3356 {
3357 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "CONF32", pHdr->u16Length));
3358 break;
3359 }
3360
3361 VBOXVIDEOINFOQUERYCONF32 *pConf32 = (VBOXVIDEOINFOQUERYCONF32 *)pu8;
3362
3363 switch (pConf32->u32Index)
3364 {
3365 case VBOX_VIDEO_QCI32_MONITOR_COUNT:
3366 {
3367 pConf32->u32Value = pDrv->pDisplay->mcMonitors;
3368 } break;
3369
3370 case VBOX_VIDEO_QCI32_OFFSCREEN_HEAP_SIZE:
3371 {
3372 /* @todo make configurable. */
3373 pConf32->u32Value = _1M;
3374 } break;
3375
3376 default:
3377 LogRel(("VBoxVideo: CONF32 %d not supported!!! Skipping.\n", pConf32->u32Index));
3378 }
3379 }
3380 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
3381 {
3382 if (pHdr->u16Length != 0)
3383 {
3384 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
3385 break;
3386 }
3387
3388 break;
3389 }
3390 else if (pHdr->u8Type != VBOX_VIDEO_INFO_TYPE_NV_HEAP) /** @todo why is Additions/WINNT/Graphics/Miniport/VBoxVideo.cpp pushing this to us? */
3391 {
3392 LogRel(("Guest adapter information contains unsupported type %d. The block has been skipped.\n", pHdr->u8Type));
3393 }
3394
3395 pu8 += pHdr->u16Length;
3396 }
3397 }
3398#endif /* !VBOX_WITH_HGSMI */
3399}
3400
3401/**
3402 * Display information change notification.
3403 *
3404 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
3405 */
3406DECLCALLBACK(void) Display::displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, unsigned uScreenId)
3407{
3408 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3409
3410 if (uScreenId >= pDrv->pDisplay->mcMonitors)
3411 {
3412 LogRel(("VBoxVideo: Guest display information invalid display index %d!!!\n", uScreenId));
3413 return;
3414 }
3415
3416 /* Get the display information structure. */
3417 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[uScreenId];
3418
3419 uint8_t *pu8 = (uint8_t *)pvVRAM;
3420 pu8 += pFBInfo->u32Offset + pFBInfo->u32MaxFramebufferSize;
3421
3422 // @todo
3423 uint8_t *pu8End = pu8 + pFBInfo->u32InformationSize;
3424
3425 VBOXVIDEOINFOHDR *pHdr;
3426
3427 for (;;)
3428 {
3429 pHdr = (VBOXVIDEOINFOHDR *)pu8;
3430 pu8 += sizeof (VBOXVIDEOINFOHDR);
3431
3432 if (pu8 >= pu8End)
3433 {
3434 LogRel(("VBoxVideo: Guest display information overflow!!!\n"));
3435 break;
3436 }
3437
3438 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_SCREEN)
3439 {
3440 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOSCREEN))
3441 {
3442 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "SCREEN", pHdr->u16Length));
3443 break;
3444 }
3445
3446 VBOXVIDEOINFOSCREEN *pScreen = (VBOXVIDEOINFOSCREEN *)pu8;
3447
3448 pFBInfo->xOrigin = pScreen->xOrigin;
3449 pFBInfo->yOrigin = pScreen->yOrigin;
3450
3451 pFBInfo->w = pScreen->u16Width;
3452 pFBInfo->h = pScreen->u16Height;
3453
3454 LogRelFlow(("VBOX_VIDEO_INFO_TYPE_SCREEN: (%p) %d: at %d,%d, linesize 0x%X, size %dx%d, bpp %d, flags 0x%02X\n",
3455 pHdr, uScreenId, pScreen->xOrigin, pScreen->yOrigin, pScreen->u32LineSize, pScreen->u16Width, pScreen->u16Height, pScreen->bitsPerPixel, pScreen->u8Flags));
3456
3457 if (uScreenId != VBOX_VIDEO_PRIMARY_SCREEN)
3458 {
3459 /* Primary screen resize is initiated by the VGA device. */
3460 if (pFBInfo->fDisabled)
3461 {
3462 pFBInfo->fDisabled = false;
3463 fireGuestMonitorChangedEvent(pDrv->pDisplay->mParent->getEventSource(),
3464 GuestMonitorChangedEventType_Enabled,
3465 uScreenId,
3466 pFBInfo->xOrigin, pFBInfo->yOrigin,
3467 pFBInfo->w, pFBInfo->h);
3468 }
3469
3470 pDrv->pDisplay->handleDisplayResize(uScreenId, pScreen->bitsPerPixel, (uint8_t *)pvVRAM + pFBInfo->u32Offset, pScreen->u32LineSize, pScreen->u16Width, pScreen->u16Height, VBVA_SCREEN_F_ACTIVE);
3471 }
3472 }
3473 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
3474 {
3475 if (pHdr->u16Length != 0)
3476 {
3477 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
3478 break;
3479 }
3480
3481 break;
3482 }
3483 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_HOST_EVENTS)
3484 {
3485 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOHOSTEVENTS))
3486 {
3487 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "HOST_EVENTS", pHdr->u16Length));
3488 break;
3489 }
3490
3491 VBOXVIDEOINFOHOSTEVENTS *pHostEvents = (VBOXVIDEOINFOHOSTEVENTS *)pu8;
3492
3493 pFBInfo->pHostEvents = pHostEvents;
3494
3495 LogFlow(("VBOX_VIDEO_INFO_TYPE_HOSTEVENTS: (%p)\n",
3496 pHostEvents));
3497 }
3498 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_LINK)
3499 {
3500 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOLINK))
3501 {
3502 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "LINK", pHdr->u16Length));
3503 break;
3504 }
3505
3506 VBOXVIDEOINFOLINK *pLink = (VBOXVIDEOINFOLINK *)pu8;
3507 pu8 += pLink->i32Offset;
3508 }
3509 else
3510 {
3511 LogRel(("Guest display information contains unsupported type %d\n", pHdr->u8Type));
3512 }
3513
3514 pu8 += pHdr->u16Length;
3515 }
3516}
3517
3518#ifdef VBOX_WITH_VIDEOHWACCEL
3519
3520void Display::handleVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
3521{
3522 unsigned id = (unsigned)pCommand->iDisplay;
3523 int rc = VINF_SUCCESS;
3524 if (id < mcMonitors)
3525 {
3526 IFramebuffer *pFramebuffer = maFramebuffers[id].pFramebuffer;
3527#ifdef DEBUG_misha
3528 Assert (pFramebuffer);
3529#endif
3530
3531 if (pFramebuffer != NULL)
3532 {
3533 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
3534 if (FAILED(hr))
3535 {
3536 rc = (hr == E_NOTIMPL) ? VERR_NOT_IMPLEMENTED : VERR_GENERAL_FAILURE;
3537 }
3538 }
3539 else
3540 {
3541 rc = VERR_NOT_IMPLEMENTED;
3542 }
3543 }
3544 else
3545 {
3546 rc = VERR_INVALID_PARAMETER;
3547 }
3548
3549 if (RT_FAILURE(rc))
3550 {
3551 /* tell the guest the command is complete */
3552 pCommand->Flags &= (~VBOXVHWACMD_FLAG_HG_ASYNCH);
3553 pCommand->rc = rc;
3554 }
3555}
3556
3557DECLCALLBACK(void) Display::displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
3558{
3559 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3560
3561 pDrv->pDisplay->handleVHWACommandProcess(pInterface, pCommand);
3562}
3563#endif
3564
3565#ifdef VBOX_WITH_CRHGSMI
3566void Display::handleCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
3567{
3568 mpDrv->pVBVACallbacks->pfnCrHgsmiCommandCompleteAsync(mpDrv->pVBVACallbacks, (PVBOXVDMACMD_CHROMIUM_CMD)pParam->u.pointer.addr, result);
3569}
3570
3571void Display::handleCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam)
3572{
3573 mpDrv->pVBVACallbacks->pfnCrHgsmiControlCompleteAsync(mpDrv->pVBVACallbacks, (PVBOXVDMACMD_CHROMIUM_CTL)pParam->u.pointer.addr, result);
3574}
3575
3576void Display::handleCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
3577{
3578 int rc = VERR_INVALID_FUNCTION;
3579 VBOXHGCMSVCPARM parm;
3580 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3581 parm.u.pointer.addr = pCmd;
3582 parm.u.pointer.size = cbCmd;
3583
3584 if (mhCrOglSvc)
3585 {
3586 VMMDev *pVMMDev = mParent->getVMMDev();
3587 if (pVMMDev)
3588 {
3589 /* no completion callback is specified with this call,
3590 * the CrOgl code will complete the CrHgsmi command once it processes it */
3591 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm, NULL, NULL);
3592 AssertRC(rc);
3593 if (RT_SUCCESS(rc))
3594 return;
3595 }
3596 else
3597 rc = VERR_INVALID_STATE;
3598 }
3599
3600 /* we are here because something went wrong with command processing, complete it */
3601 handleCrHgsmiCommandCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CMD, &parm);
3602}
3603
3604void Display::handleCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCtl, uint32_t cbCtl)
3605{
3606 int rc = VERR_INVALID_FUNCTION;
3607 VBOXHGCMSVCPARM parm;
3608 parm.type = VBOX_HGCM_SVC_PARM_PTR;
3609 parm.u.pointer.addr = pCtl;
3610 parm.u.pointer.size = cbCtl;
3611
3612 if (mhCrOglSvc)
3613 {
3614 VMMDev *pVMMDev = mParent->getVMMDev();
3615 if (pVMMDev)
3616 {
3617 rc = pVMMDev->hgcmHostFastCallAsync(mhCrOglSvc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm, Display::displayCrHgsmiControlCompletion, this);
3618 AssertRC(rc);
3619 if (RT_SUCCESS(rc))
3620 return;
3621 }
3622 else
3623 rc = VERR_INVALID_STATE;
3624 }
3625
3626 /* we are here because something went wrong with command processing, complete it */
3627 handleCrHgsmiControlCompletion(rc, SHCRGL_HOST_FN_CRHGSMI_CTL, &parm);
3628}
3629
3630
3631DECLCALLBACK(void) Display::displayCrHgsmiCommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CMD pCmd, uint32_t cbCmd)
3632{
3633 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3634
3635 pDrv->pDisplay->handleCrHgsmiCommandProcess(pInterface, pCmd, cbCmd);
3636}
3637
3638DECLCALLBACK(void) Display::displayCrHgsmiControlProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVDMACMD_CHROMIUM_CTL pCmd, uint32_t cbCmd)
3639{
3640 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3641
3642 pDrv->pDisplay->handleCrHgsmiControlProcess(pInterface, pCmd, cbCmd);
3643}
3644
3645DECLCALLBACK(void) Display::displayCrHgsmiCommandCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
3646{
3647 AssertMsgFailed(("not expected!"));
3648 Display *pDisplay = (Display *)pvContext;
3649 pDisplay->handleCrHgsmiCommandCompletion(result, u32Function, pParam);
3650}
3651
3652DECLCALLBACK(void) Display::displayCrHgsmiControlCompletion(int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext)
3653{
3654 Display *pDisplay = (Display *)pvContext;
3655 pDisplay->handleCrHgsmiControlCompletion(result, u32Function, pParam);
3656}
3657#endif
3658
3659
3660#ifdef VBOX_WITH_HGSMI
3661DECLCALLBACK(int) Display::displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, PVBVAHOSTFLAGS pHostFlags)
3662{
3663 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
3664
3665 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3666 Display *pThis = pDrv->pDisplay;
3667
3668 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
3669 pThis->maFramebuffers[uScreenId].pVBVAHostFlags = pHostFlags;
3670
3671 vbvaSetMemoryFlagsHGSMI(uScreenId, pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, &pThis->maFramebuffers[uScreenId]);
3672
3673 return VINF_SUCCESS;
3674}
3675
3676DECLCALLBACK(void) Display::displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
3677{
3678 LogRelFlowFunc(("uScreenId %d\n", uScreenId));
3679
3680 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3681 Display *pThis = pDrv->pDisplay;
3682
3683 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3684
3685 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
3686 {
3687 /* Make sure that the primary screen is visible now.
3688 * The guest can't use VBVA anymore, so only only the VGA device output works.
3689 */
3690 if (pFBInfo->fDisabled)
3691 {
3692 pFBInfo->fDisabled = false;
3693 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
3694 GuestMonitorChangedEventType_Enabled,
3695 uScreenId,
3696 pFBInfo->xOrigin, pFBInfo->yOrigin,
3697 pFBInfo->w, pFBInfo->h);
3698 }
3699 }
3700
3701 pFBInfo->fVBVAEnabled = false;
3702
3703 vbvaSetMemoryFlagsHGSMI(uScreenId, 0, false, pFBInfo);
3704
3705 pFBInfo->pVBVAHostFlags = NULL;
3706
3707 pFBInfo->u32Offset = 0; /* Not used in HGSMI. */
3708 pFBInfo->u32MaxFramebufferSize = 0; /* Not used in HGSMI. */
3709 pFBInfo->u32InformationSize = 0; /* Not used in HGSMI. */
3710
3711 pFBInfo->xOrigin = 0;
3712 pFBInfo->yOrigin = 0;
3713
3714 pFBInfo->w = 0;
3715 pFBInfo->h = 0;
3716
3717 pFBInfo->u16BitsPerPixel = 0;
3718 pFBInfo->pu8FramebufferVRAM = NULL;
3719 pFBInfo->u32LineSize = 0;
3720}
3721
3722DECLCALLBACK(void) Display::displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
3723{
3724 LogFlowFunc(("uScreenId %d\n", uScreenId));
3725
3726 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3727 Display *pThis = pDrv->pDisplay;
3728 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3729
3730 if (ASMAtomicReadU32(&pThis->mu32UpdateVBVAFlags) > 0)
3731 {
3732 vbvaSetMemoryFlagsAllHGSMI(pThis->mfu32SupportedOrders, pThis->mfVideoAccelVRDP, pThis->maFramebuffers, pThis->mcMonitors);
3733 ASMAtomicDecU32(&pThis->mu32UpdateVBVAFlags);
3734 }
3735
3736 if (RT_LIKELY(pFBInfo->u32ResizeStatus == ResizeStatus_Void))
3737 {
3738 if (RT_UNLIKELY(pFBInfo->cVBVASkipUpdate != 0))
3739 {
3740 /* Some updates were skipped. Note: displayVBVAUpdate* callbacks are called
3741 * under display device lock, so thread safe.
3742 */
3743 pFBInfo->cVBVASkipUpdate = 0;
3744 pThis->handleDisplayUpdate(uScreenId, pFBInfo->vbvaSkippedRect.xLeft - pFBInfo->xOrigin,
3745 pFBInfo->vbvaSkippedRect.yTop - pFBInfo->yOrigin,
3746 pFBInfo->vbvaSkippedRect.xRight - pFBInfo->vbvaSkippedRect.xLeft,
3747 pFBInfo->vbvaSkippedRect.yBottom - pFBInfo->vbvaSkippedRect.yTop);
3748 }
3749 }
3750 else
3751 {
3752 /* The framebuffer is being resized. */
3753 pFBInfo->cVBVASkipUpdate++;
3754 }
3755}
3756
3757DECLCALLBACK(void) Display::displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, const PVBVACMDHDR pCmd, size_t cbCmd)
3758{
3759 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d, @%d,%d %dx%d\n", uScreenId, pCmd, cbCmd, pCmd->x, pCmd->y, pCmd->w, pCmd->h));
3760
3761 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3762 Display *pThis = pDrv->pDisplay;
3763 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3764
3765 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
3766 {
3767 if (pFBInfo->fDefaultFormat)
3768 {
3769 /* Make sure that framebuffer contains the same image as the guest VRAM. */
3770 if ( uScreenId == VBOX_VIDEO_PRIMARY_SCREEN
3771 && !pFBInfo->pFramebuffer.isNull()
3772 && !pFBInfo->fDisabled)
3773 {
3774 pDrv->pUpPort->pfnUpdateDisplayRect (pDrv->pUpPort, pCmd->x, pCmd->y, pCmd->w, pCmd->h);
3775 }
3776 else if ( !pFBInfo->pFramebuffer.isNull()
3777 && !(pFBInfo->fDisabled))
3778 {
3779 /* Render VRAM content to the framebuffer. */
3780 BYTE *address = NULL;
3781 HRESULT hrc = pFBInfo->pFramebuffer->COMGETTER(Address) (&address);
3782 if (SUCCEEDED(hrc) && address != NULL)
3783 {
3784 uint32_t width = pCmd->w;
3785 uint32_t height = pCmd->h;
3786
3787 const uint8_t *pu8Src = pFBInfo->pu8FramebufferVRAM;
3788 int32_t xSrc = pCmd->x - pFBInfo->xOrigin;
3789 int32_t ySrc = pCmd->y - pFBInfo->yOrigin;
3790 uint32_t u32SrcWidth = pFBInfo->w;
3791 uint32_t u32SrcHeight = pFBInfo->h;
3792 uint32_t u32SrcLineSize = pFBInfo->u32LineSize;
3793 uint32_t u32SrcBitsPerPixel = pFBInfo->u16BitsPerPixel;
3794
3795 uint8_t *pu8Dst = address;
3796 int32_t xDst = xSrc;
3797 int32_t yDst = ySrc;
3798 uint32_t u32DstWidth = u32SrcWidth;
3799 uint32_t u32DstHeight = u32SrcHeight;
3800 uint32_t u32DstLineSize = u32DstWidth * 4;
3801 uint32_t u32DstBitsPerPixel = 32;
3802
3803 pDrv->pUpPort->pfnCopyRect(pDrv->pUpPort,
3804 width, height,
3805 pu8Src,
3806 xSrc, ySrc,
3807 u32SrcWidth, u32SrcHeight,
3808 u32SrcLineSize, u32SrcBitsPerPixel,
3809 pu8Dst,
3810 xDst, yDst,
3811 u32DstWidth, u32DstHeight,
3812 u32DstLineSize, u32DstBitsPerPixel);
3813 }
3814 }
3815 }
3816
3817 VBVACMDHDR hdrSaved = *pCmd;
3818
3819 VBVACMDHDR *pHdrUnconst = (VBVACMDHDR *)pCmd;
3820
3821 pHdrUnconst->x -= (int16_t)pFBInfo->xOrigin;
3822 pHdrUnconst->y -= (int16_t)pFBInfo->yOrigin;
3823
3824 /* @todo new SendUpdate entry which can get a separate cmd header or coords. */
3825 pThis->mParent->consoleVRDPServer()->SendUpdate (uScreenId, pCmd, cbCmd);
3826
3827 *pHdrUnconst = hdrSaved;
3828 }
3829}
3830
3831DECLCALLBACK(void) Display::displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y, uint32_t cx, uint32_t cy)
3832{
3833 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
3834
3835 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3836 Display *pThis = pDrv->pDisplay;
3837 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
3838
3839 /* @todo handleFramebufferUpdate (uScreenId,
3840 * x - pThis->maFramebuffers[uScreenId].xOrigin,
3841 * y - pThis->maFramebuffers[uScreenId].yOrigin,
3842 * cx, cy);
3843 */
3844 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
3845 {
3846 pThis->handleDisplayUpdate(uScreenId, x - pFBInfo->xOrigin, y - pFBInfo->yOrigin, cx, cy);
3847 }
3848 else
3849 {
3850 /* Save the updated rectangle. */
3851 int32_t xRight = x + cx;
3852 int32_t yBottom = y + cy;
3853
3854 if (pFBInfo->cVBVASkipUpdate == 1)
3855 {
3856 pFBInfo->vbvaSkippedRect.xLeft = x;
3857 pFBInfo->vbvaSkippedRect.yTop = y;
3858 pFBInfo->vbvaSkippedRect.xRight = xRight;
3859 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
3860 }
3861 else
3862 {
3863 if (pFBInfo->vbvaSkippedRect.xLeft > x)
3864 {
3865 pFBInfo->vbvaSkippedRect.xLeft = x;
3866 }
3867 if (pFBInfo->vbvaSkippedRect.yTop > y)
3868 {
3869 pFBInfo->vbvaSkippedRect.yTop = y;
3870 }
3871 if (pFBInfo->vbvaSkippedRect.xRight < xRight)
3872 {
3873 pFBInfo->vbvaSkippedRect.xRight = xRight;
3874 }
3875 if (pFBInfo->vbvaSkippedRect.yBottom < yBottom)
3876 {
3877 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
3878 }
3879 }
3880 }
3881}
3882
3883#ifdef DEBUG_sunlover
3884static void logVBVAResize(const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, const DISPLAYFBINFO *pFBInfo)
3885{
3886 LogRel(("displayVBVAResize: [%d] %s\n"
3887 " pView->u32ViewIndex %d\n"
3888 " pView->u32ViewOffset 0x%08X\n"
3889 " pView->u32ViewSize 0x%08X\n"
3890 " pView->u32MaxScreenSize 0x%08X\n"
3891 " pScreen->i32OriginX %d\n"
3892 " pScreen->i32OriginY %d\n"
3893 " pScreen->u32StartOffset 0x%08X\n"
3894 " pScreen->u32LineSize 0x%08X\n"
3895 " pScreen->u32Width %d\n"
3896 " pScreen->u32Height %d\n"
3897 " pScreen->u16BitsPerPixel %d\n"
3898 " pScreen->u16Flags 0x%04X\n"
3899 " pFBInfo->u32Offset 0x%08X\n"
3900 " pFBInfo->u32MaxFramebufferSize 0x%08X\n"
3901 " pFBInfo->u32InformationSize 0x%08X\n"
3902 " pFBInfo->fDisabled %d\n"
3903 " xOrigin, yOrigin, w, h: %d,%d %dx%d\n"
3904 " pFBInfo->u16BitsPerPixel %d\n"
3905 " pFBInfo->pu8FramebufferVRAM %p\n"
3906 " pFBInfo->u32LineSize 0x%08X\n"
3907 " pFBInfo->flags 0x%04X\n"
3908 " pFBInfo->pHostEvents %p\n"
3909 " pFBInfo->u32ResizeStatus %d\n"
3910 " pFBInfo->fDefaultFormat %d\n"
3911 " dirtyRect %d-%d %d-%d\n"
3912 " pFBInfo->pendingResize.fPending %d\n"
3913 " pFBInfo->pendingResize.pixelFormat %d\n"
3914 " pFBInfo->pendingResize.pvVRAM %p\n"
3915 " pFBInfo->pendingResize.bpp %d\n"
3916 " pFBInfo->pendingResize.cbLine 0x%08X\n"
3917 " pFBInfo->pendingResize.w,h %dx%d\n"
3918 " pFBInfo->pendingResize.flags 0x%04X\n"
3919 " pFBInfo->fVBVAEnabled %d\n"
3920 " pFBInfo->cVBVASkipUpdate %d\n"
3921 " pFBInfo->vbvaSkippedRect %d-%d %d-%d\n"
3922 " pFBInfo->pVBVAHostFlags %p\n"
3923 "",
3924 pScreen->u32ViewIndex,
3925 (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)? "DISABLED": "ENABLED",
3926 pView->u32ViewIndex,
3927 pView->u32ViewOffset,
3928 pView->u32ViewSize,
3929 pView->u32MaxScreenSize,
3930 pScreen->i32OriginX,
3931 pScreen->i32OriginY,
3932 pScreen->u32StartOffset,
3933 pScreen->u32LineSize,
3934 pScreen->u32Width,
3935 pScreen->u32Height,
3936 pScreen->u16BitsPerPixel,
3937 pScreen->u16Flags,
3938 pFBInfo->u32Offset,
3939 pFBInfo->u32MaxFramebufferSize,
3940 pFBInfo->u32InformationSize,
3941 pFBInfo->fDisabled,
3942 pFBInfo->xOrigin,
3943 pFBInfo->yOrigin,
3944 pFBInfo->w,
3945 pFBInfo->h,
3946 pFBInfo->u16BitsPerPixel,
3947 pFBInfo->pu8FramebufferVRAM,
3948 pFBInfo->u32LineSize,
3949 pFBInfo->flags,
3950 pFBInfo->pHostEvents,
3951 pFBInfo->u32ResizeStatus,
3952 pFBInfo->fDefaultFormat,
3953 pFBInfo->dirtyRect.xLeft,
3954 pFBInfo->dirtyRect.xRight,
3955 pFBInfo->dirtyRect.yTop,
3956 pFBInfo->dirtyRect.yBottom,
3957 pFBInfo->pendingResize.fPending,
3958 pFBInfo->pendingResize.pixelFormat,
3959 pFBInfo->pendingResize.pvVRAM,
3960 pFBInfo->pendingResize.bpp,
3961 pFBInfo->pendingResize.cbLine,
3962 pFBInfo->pendingResize.w,
3963 pFBInfo->pendingResize.h,
3964 pFBInfo->pendingResize.flags,
3965 pFBInfo->fVBVAEnabled,
3966 pFBInfo->cVBVASkipUpdate,
3967 pFBInfo->vbvaSkippedRect.xLeft,
3968 pFBInfo->vbvaSkippedRect.yTop,
3969 pFBInfo->vbvaSkippedRect.xRight,
3970 pFBInfo->vbvaSkippedRect.yBottom,
3971 pFBInfo->pVBVAHostFlags
3972 ));
3973}
3974#endif /* DEBUG_sunlover */
3975
3976DECLCALLBACK(int) Display::displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, void *pvVRAM)
3977{
3978 LogRelFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
3979
3980 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
3981 Display *pThis = pDrv->pDisplay;
3982
3983 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[pScreen->u32ViewIndex];
3984
3985 if (pScreen->u16Flags & VBVA_SCREEN_F_DISABLED)
3986 {
3987 pFBInfo->fDisabled = true;
3988 pFBInfo->flags = pScreen->u16Flags;
3989
3990 /* Temporary: ask framebuffer to resize using a default format. The framebuffer will be black. */
3991 pThis->handleDisplayResize(pScreen->u32ViewIndex, 0,
3992 (uint8_t *)NULL,
3993 pScreen->u32LineSize, pScreen->u32Width,
3994 pScreen->u32Height, pScreen->u16Flags);
3995
3996 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
3997 GuestMonitorChangedEventType_Disabled,
3998 pScreen->u32ViewIndex,
3999 0, 0, 0, 0);
4000 return VINF_SUCCESS;
4001 }
4002
4003 /* If display was disabled or there is no framebuffer, a resize will be required,
4004 * because the framebuffer was/will be changed.
4005 */
4006 bool fResize = pFBInfo->fDisabled || pFBInfo->pFramebuffer.isNull();
4007
4008 if (pFBInfo->fDisabled)
4009 {
4010 pFBInfo->fDisabled = false;
4011 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
4012 GuestMonitorChangedEventType_Enabled,
4013 pScreen->u32ViewIndex,
4014 pScreen->i32OriginX, pScreen->i32OriginY,
4015 pScreen->u32Width, pScreen->u32Height);
4016 /* Continue to update pFBInfo. */
4017 }
4018
4019 /* Check if this is a real resize or a notification about the screen origin.
4020 * The guest uses this VBVAResize call for both.
4021 */
4022 fResize = fResize
4023 || pFBInfo->u16BitsPerPixel != pScreen->u16BitsPerPixel
4024 || pFBInfo->pu8FramebufferVRAM != (uint8_t *)pvVRAM + pScreen->u32StartOffset
4025 || pFBInfo->u32LineSize != pScreen->u32LineSize
4026 || pFBInfo->w != pScreen->u32Width
4027 || pFBInfo->h != pScreen->u32Height;
4028
4029 bool fNewOrigin = pFBInfo->xOrigin != pScreen->i32OriginX
4030 || pFBInfo->yOrigin != pScreen->i32OriginY;
4031
4032 pFBInfo->u32Offset = pView->u32ViewOffset; /* Not used in HGSMI. */
4033 pFBInfo->u32MaxFramebufferSize = pView->u32MaxScreenSize; /* Not used in HGSMI. */
4034 pFBInfo->u32InformationSize = 0; /* Not used in HGSMI. */
4035
4036 pFBInfo->xOrigin = pScreen->i32OriginX;
4037 pFBInfo->yOrigin = pScreen->i32OriginY;
4038
4039 pFBInfo->w = pScreen->u32Width;
4040 pFBInfo->h = pScreen->u32Height;
4041
4042 pFBInfo->u16BitsPerPixel = pScreen->u16BitsPerPixel;
4043 pFBInfo->pu8FramebufferVRAM = (uint8_t *)pvVRAM + pScreen->u32StartOffset;
4044 pFBInfo->u32LineSize = pScreen->u32LineSize;
4045
4046 pFBInfo->flags = pScreen->u16Flags;
4047
4048 if (fNewOrigin)
4049 {
4050 fireGuestMonitorChangedEvent(pThis->mParent->getEventSource(),
4051 GuestMonitorChangedEventType_NewOrigin,
4052 pScreen->u32ViewIndex,
4053 pScreen->i32OriginX, pScreen->i32OriginY,
4054 0, 0);
4055 }
4056
4057#if defined(VBOX_WITH_HGCM) && defined(VBOX_WITH_CROGL)
4058 if (fNewOrigin && !fResize)
4059 {
4060 BOOL is3denabled;
4061 pThis->mParent->machine()->COMGETTER(Accelerate3DEnabled)(&is3denabled);
4062
4063 if (is3denabled)
4064 {
4065 VBOXHGCMSVCPARM parm;
4066
4067 parm.type = VBOX_HGCM_SVC_PARM_32BIT;
4068 parm.u.uint32 = pScreen->u32ViewIndex;
4069
4070 VMMDev *pVMMDev = pThis->mParent->getVMMDev();
4071
4072 if (pVMMDev)
4073 pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL", SHCRGL_HOST_FN_SCREEN_CHANGED, SHCRGL_CPARMS_SCREEN_CHANGED, &parm);
4074 }
4075 }
4076#endif /* VBOX_WITH_CROGL */
4077
4078 if (!fResize)
4079 {
4080 /* No parameters of the framebuffer have actually changed. */
4081 if (fNewOrigin)
4082 {
4083 /* VRDP server still need this notification. */
4084 LogRelFlowFunc (("Calling VRDP\n"));
4085 pThis->mParent->consoleVRDPServer()->SendResize();
4086 }
4087 return VINF_SUCCESS;
4088 }
4089
4090 if (pFBInfo->pFramebuffer.isNull())
4091 {
4092 /* If no framebuffer, the resize will be done later when a new framebuffer will be set in changeFramebuffer. */
4093 return VINF_SUCCESS;
4094 }
4095
4096 /* If the framebuffer already set for the screen, do a regular resize. */
4097 return pThis->handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
4098 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
4099 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height, pScreen->u16Flags);
4100}
4101
4102DECLCALLBACK(int) Display::displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
4103 uint32_t xHot, uint32_t yHot,
4104 uint32_t cx, uint32_t cy,
4105 const void *pvShape)
4106{
4107 LogFlowFunc(("\n"));
4108
4109 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
4110 Display *pThis = pDrv->pDisplay;
4111
4112 size_t cbShapeSize = 0;
4113
4114 if (pvShape)
4115 {
4116 cbShapeSize = (cx + 7) / 8 * cy; /* size of the AND mask */
4117 cbShapeSize = ((cbShapeSize + 3) & ~3) + cx * 4 * cy; /* + gap + size of the XOR mask */
4118 }
4119 com::SafeArray<BYTE> shapeData(cbShapeSize);
4120
4121 if (pvShape)
4122 ::memcpy(shapeData.raw(), pvShape, cbShapeSize);
4123
4124 /* Tell the console about it */
4125 pDrv->pDisplay->mParent->onMousePointerShapeChange(fVisible, fAlpha,
4126 xHot, yHot, cx, cy, ComSafeArrayAsInParam(shapeData));
4127
4128 return VINF_SUCCESS;
4129}
4130#endif /* VBOX_WITH_HGSMI */
4131
4132/**
4133 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4134 */
4135DECLCALLBACK(void *) Display::drvQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4136{
4137 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
4138 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4139 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
4140 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYCONNECTOR, &pDrv->IConnector);
4141 return NULL;
4142}
4143
4144
4145/**
4146 * Destruct a display driver instance.
4147 *
4148 * @returns VBox status.
4149 * @param pDrvIns The driver instance data.
4150 */
4151DECLCALLBACK(void) Display::drvDestruct(PPDMDRVINS pDrvIns)
4152{
4153 PDRVMAINDISPLAY pData = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4154 LogRelFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
4155 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4156
4157 if (pData->pDisplay)
4158 {
4159 AutoWriteLock displayLock(pData->pDisplay COMMA_LOCKVAL_SRC_POS);
4160#ifdef VBOX_WITH_CRHGSMI
4161 pData->pDisplay->destructCrHgsmiData();
4162#endif
4163 pData->pDisplay->mpDrv = NULL;
4164 pData->pDisplay->mpVMMDev = NULL;
4165 pData->pDisplay->mLastAddress = NULL;
4166 pData->pDisplay->mLastBytesPerLine = 0;
4167 pData->pDisplay->mLastBitsPerPixel = 0,
4168 pData->pDisplay->mLastWidth = 0;
4169 pData->pDisplay->mLastHeight = 0;
4170 }
4171}
4172
4173
4174/**
4175 * Construct a display driver instance.
4176 *
4177 * @copydoc FNPDMDRVCONSTRUCT
4178 */
4179DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
4180{
4181 PDRVMAINDISPLAY pData = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
4182 LogRelFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
4183 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
4184
4185 /*
4186 * Validate configuration.
4187 */
4188 if (!CFGMR3AreValuesValid(pCfg, "Object\0"))
4189 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
4190 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
4191 ("Configuration error: Not possible to attach anything to this driver!\n"),
4192 VERR_PDM_DRVINS_NO_ATTACH);
4193
4194 /*
4195 * Init Interfaces.
4196 */
4197 pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
4198
4199 pData->IConnector.pfnResize = Display::displayResizeCallback;
4200 pData->IConnector.pfnUpdateRect = Display::displayUpdateCallback;
4201 pData->IConnector.pfnRefresh = Display::displayRefreshCallback;
4202 pData->IConnector.pfnReset = Display::displayResetCallback;
4203 pData->IConnector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
4204 pData->IConnector.pfnProcessAdapterData = Display::displayProcessAdapterDataCallback;
4205 pData->IConnector.pfnProcessDisplayData = Display::displayProcessDisplayDataCallback;
4206#ifdef VBOX_WITH_VIDEOHWACCEL
4207 pData->IConnector.pfnVHWACommandProcess = Display::displayVHWACommandProcess;
4208#endif
4209#ifdef VBOX_WITH_CRHGSMI
4210 pData->IConnector.pfnCrHgsmiCommandProcess = Display::displayCrHgsmiCommandProcess;
4211 pData->IConnector.pfnCrHgsmiControlProcess = Display::displayCrHgsmiControlProcess;
4212#endif
4213#ifdef VBOX_WITH_HGSMI
4214 pData->IConnector.pfnVBVAEnable = Display::displayVBVAEnable;
4215 pData->IConnector.pfnVBVADisable = Display::displayVBVADisable;
4216 pData->IConnector.pfnVBVAUpdateBegin = Display::displayVBVAUpdateBegin;
4217 pData->IConnector.pfnVBVAUpdateProcess = Display::displayVBVAUpdateProcess;
4218 pData->IConnector.pfnVBVAUpdateEnd = Display::displayVBVAUpdateEnd;
4219 pData->IConnector.pfnVBVAResize = Display::displayVBVAResize;
4220 pData->IConnector.pfnVBVAMousePointerShape = Display::displayVBVAMousePointerShape;
4221#endif
4222
4223
4224 /*
4225 * Get the IDisplayPort interface of the above driver/device.
4226 */
4227 pData->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYPORT);
4228 if (!pData->pUpPort)
4229 {
4230 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
4231 return VERR_PDM_MISSING_INTERFACE_ABOVE;
4232 }
4233#if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI)
4234 pData->pVBVACallbacks = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIDISPLAYVBVACALLBACKS);
4235 if (!pData->pVBVACallbacks)
4236 {
4237 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
4238 return VERR_PDM_MISSING_INTERFACE_ABOVE;
4239 }
4240#endif
4241 /*
4242 * Get the Display object pointer and update the mpDrv member.
4243 */
4244 void *pv;
4245 int rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
4246 if (RT_FAILURE(rc))
4247 {
4248 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
4249 return rc;
4250 }
4251 pData->pDisplay = (Display *)pv; /** @todo Check this cast! */
4252 pData->pDisplay->mpDrv = pData;
4253
4254 /*
4255 * Update our display information according to the framebuffer
4256 */
4257 pData->pDisplay->updateDisplayData();
4258
4259 /*
4260 * Start periodic screen refreshes
4261 */
4262 pData->pUpPort->pfnSetRefreshRate(pData->pUpPort, 20);
4263
4264#ifdef VBOX_WITH_CRHGSMI
4265 pData->pDisplay->setupCrHgsmiData();
4266#endif
4267
4268 return VINF_SUCCESS;
4269}
4270
4271
4272/**
4273 * Display driver registration record.
4274 */
4275const PDMDRVREG Display::DrvReg =
4276{
4277 /* u32Version */
4278 PDM_DRVREG_VERSION,
4279 /* szName */
4280 "MainDisplay",
4281 /* szRCMod */
4282 "",
4283 /* szR0Mod */
4284 "",
4285 /* pszDescription */
4286 "Main display driver (Main as in the API).",
4287 /* fFlags */
4288 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
4289 /* fClass. */
4290 PDM_DRVREG_CLASS_DISPLAY,
4291 /* cMaxInstances */
4292 ~0U,
4293 /* cbInstance */
4294 sizeof(DRVMAINDISPLAY),
4295 /* pfnConstruct */
4296 Display::drvConstruct,
4297 /* pfnDestruct */
4298 Display::drvDestruct,
4299 /* pfnRelocate */
4300 NULL,
4301 /* pfnIOCtl */
4302 NULL,
4303 /* pfnPowerOn */
4304 NULL,
4305 /* pfnReset */
4306 NULL,
4307 /* pfnSuspend */
4308 NULL,
4309 /* pfnResume */
4310 NULL,
4311 /* pfnAttach */
4312 NULL,
4313 /* pfnDetach */
4314 NULL,
4315 /* pfnPowerOff */
4316 NULL,
4317 /* pfnSoftReset */
4318 NULL,
4319 /* u32EndVersion */
4320 PDM_DRVREG_VERSION
4321};
4322/* 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