VirtualBox

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

Last change on this file since 35553 was 35547, checked in by vboxsync, 14 years ago

Main/Display: disabled screens should be black

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

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