VirtualBox

source: vbox/trunk/src/VBox/Main/DisplayImpl.cpp@ 23185

Last change on this file since 23185 was 23185, checked in by vboxsync, 15 years ago

Skip VBVA update if framebufer is being resized.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 84.3 KB
Line 
1/* $Id: DisplayImpl.cpp 23185 2009-09-21 12:23:57Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#include "DisplayImpl.h"
25#include "ConsoleImpl.h"
26#include "ConsoleVRDPServer.h"
27#include "VMMDev.h"
28
29#include "Logging.h"
30
31#include <iprt/semaphore.h>
32#include <iprt/thread.h>
33#include <iprt/asm.h>
34
35#include <VBox/pdmdrv.h>
36#ifdef DEBUG /* for VM_ASSERT_EMT(). */
37# include <VBox/vm.h>
38#endif
39
40#ifdef VBOX_WITH_VIDEOHWACCEL
41# include <VBox/VBoxVideo.h>
42#endif
43/**
44 * Display driver instance data.
45 */
46typedef struct DRVMAINDISPLAY
47{
48 /** Pointer to the display object. */
49 Display *pDisplay;
50 /** Pointer to the driver instance structure. */
51 PPDMDRVINS pDrvIns;
52 /** Pointer to the keyboard port interface of the driver/device above us. */
53 PPDMIDISPLAYPORT pUpPort;
54 /** Our display connector interface. */
55 PDMIDISPLAYCONNECTOR Connector;
56#if defined(VBOX_WITH_VIDEOHWACCEL)
57 /** VBVA callbacks */
58 PPDMDDISPLAYVBVACALLBACKS pVBVACallbacks;
59#endif
60} DRVMAINDISPLAY, *PDRVMAINDISPLAY;
61
62/** Converts PDMIDISPLAYCONNECTOR pointer to a DRVMAINDISPLAY pointer. */
63#define PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface) ( (PDRVMAINDISPLAY) ((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINDISPLAY, Connector)) )
64
65#ifdef DEBUG_sunlover
66static STAMPROFILE StatDisplayRefresh;
67static int stam = 0;
68#endif /* DEBUG_sunlover */
69
70// constructor / destructor
71/////////////////////////////////////////////////////////////////////////////
72
73DEFINE_EMPTY_CTOR_DTOR (Display)
74
75HRESULT Display::FinalConstruct()
76{
77 mpVbvaMemory = NULL;
78 mfVideoAccelEnabled = false;
79 mfVideoAccelVRDP = false;
80 mfu32SupportedOrders = 0;
81 mcVideoAccelVRDPRefs = 0;
82
83 mpPendingVbvaMemory = NULL;
84 mfPendingVideoAccelEnable = false;
85
86 mfMachineRunning = false;
87
88 mpu8VbvaPartial = NULL;
89 mcbVbvaPartial = 0;
90
91 mpDrv = NULL;
92 mpVMMDev = NULL;
93 mfVMMDevInited = false;
94
95 mLastAddress = NULL;
96 mLastBytesPerLine = 0;
97 mLastBitsPerPixel = 0,
98 mLastWidth = 0;
99 mLastHeight = 0;
100
101 return S_OK;
102}
103
104void Display::FinalRelease()
105{
106 uninit();
107}
108
109// public initializer/uninitializer for internal purposes only
110/////////////////////////////////////////////////////////////////////////////
111
112#define sSSMDisplayVer 0x00010001
113
114/**
115 * Save/Load some important guest state
116 */
117DECLCALLBACK(void)
118Display::displaySSMSave(PSSMHANDLE pSSM, void *pvUser)
119{
120 Display *that = static_cast<Display*>(pvUser);
121
122 SSMR3PutU32(pSSM, that->mcMonitors);
123 for (unsigned i = 0; i < that->mcMonitors; i++)
124 {
125 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32Offset);
126 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32MaxFramebufferSize);
127 SSMR3PutU32(pSSM, that->maFramebuffers[i].u32InformationSize);
128 }
129}
130
131DECLCALLBACK(int)
132Display::displaySSMLoad(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
133{
134 Display *that = static_cast<Display*>(pvUser);
135
136 if (uVersion != sSSMDisplayVer)
137 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
138 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
139
140 uint32_t cMonitors;
141 int rc = SSMR3GetU32(pSSM, &cMonitors);
142 if (cMonitors != that->mcMonitors)
143 {
144 LogRel(("Display: Number of monitors changed (%d->%d)!\n",
145 cMonitors, that->mcMonitors));
146 return VERR_SSM_LOAD_CONFIG_MISMATCH;
147 }
148
149 for (uint32_t i = 0; i < cMonitors; i++)
150 {
151 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32Offset);
152 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32MaxFramebufferSize);
153 SSMR3GetU32(pSSM, &that->maFramebuffers[i].u32InformationSize);
154 }
155
156 return VINF_SUCCESS;
157}
158
159/**
160 * Initializes the display object.
161 *
162 * @returns COM result indicator
163 * @param parent handle of our parent object
164 * @param qemuConsoleData address of common console data structure
165 */
166HRESULT Display::init (Console *aParent)
167{
168 LogFlowThisFunc(("aParent=%p\n", aParent));
169
170 ComAssertRet (aParent, E_INVALIDARG);
171
172 /* Enclose the state transition NotReady->InInit->Ready */
173 AutoInitSpan autoInitSpan(this);
174 AssertReturn(autoInitSpan.isOk(), E_FAIL);
175
176 unconst(mParent) = aParent;
177
178 // by default, we have an internal framebuffer which is
179 // NULL, i.e. a black hole for no display output
180 mFramebufferOpened = false;
181
182 ULONG ul;
183 mParent->machine()->COMGETTER(MonitorCount)(&ul);
184 mcMonitors = ul;
185
186 for (ul = 0; ul < mcMonitors; ul++)
187 {
188 maFramebuffers[ul].u32Offset = 0;
189 maFramebuffers[ul].u32MaxFramebufferSize = 0;
190 maFramebuffers[ul].u32InformationSize = 0;
191
192 maFramebuffers[ul].pFramebuffer = NULL;
193
194 maFramebuffers[ul].xOrigin = 0;
195 maFramebuffers[ul].yOrigin = 0;
196
197 maFramebuffers[ul].w = 0;
198 maFramebuffers[ul].h = 0;
199
200 maFramebuffers[ul].pHostEvents = NULL;
201
202 maFramebuffers[ul].u32ResizeStatus = ResizeStatus_Void;
203
204 maFramebuffers[ul].fDefaultFormat = false;
205
206 memset (&maFramebuffers[ul].dirtyRect, 0 , sizeof (maFramebuffers[ul].dirtyRect));
207 memset (&maFramebuffers[ul].pendingResize, 0 , sizeof (maFramebuffers[ul].pendingResize));
208#ifdef VBOX_WITH_HGSMI
209 maFramebuffers[ul].fVBVAEnabled = false;
210 maFramebuffers[ul].cVBVASkipUpdate = 0;
211 memset (&maFramebuffers[ul].vbvaSkippedRect, 0, sizeof (maFramebuffers[ul].vbvaSkippedRect));
212#endif /* VBOX_WITH_HGSMI */
213 }
214
215 mParent->RegisterCallback (this);
216
217 /* Confirm a successful initialization */
218 autoInitSpan.setSucceeded();
219
220 return S_OK;
221}
222
223/**
224 * Uninitializes the instance and sets the ready flag to FALSE.
225 * Called either from FinalRelease() or by the parent when it gets destroyed.
226 */
227void Display::uninit()
228{
229 LogFlowThisFunc(("\n"));
230
231 /* Enclose the state transition Ready->InUninit->NotReady */
232 AutoUninitSpan autoUninitSpan(this);
233 if (autoUninitSpan.uninitDone())
234 return;
235
236 ULONG ul;
237 for (ul = 0; ul < mcMonitors; ul++)
238 maFramebuffers[ul].pFramebuffer = NULL;
239
240 if (mParent)
241 mParent->UnregisterCallback (this);
242
243 unconst(mParent).setNull();
244
245 if (mpDrv)
246 mpDrv->pDisplay = NULL;
247
248 mpDrv = NULL;
249 mpVMMDev = NULL;
250 mfVMMDevInited = true;
251}
252
253/**
254 * Register the SSM methods. Called by the power up thread to be able to
255 * pass pVM
256 */
257int Display::registerSSM(PVM pVM)
258{
259 int rc = SSMR3RegisterExternal(pVM, "DisplayData", 0, sSSMDisplayVer,
260 mcMonitors * sizeof(uint32_t) * 3 + sizeof(uint32_t),
261 NULL, NULL, NULL,
262 NULL, displaySSMSave, NULL,
263 NULL, displaySSMLoad, NULL, this);
264
265 AssertRCReturn(rc, rc);
266
267 /*
268 * Register loaders for old saved states where iInstance was 3 * sizeof(uint32_t *).
269 */
270 rc = SSMR3RegisterExternal(pVM, "DisplayData", 12 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
271 NULL, NULL, NULL,
272 NULL, NULL, NULL,
273 NULL, displaySSMLoad, NULL, this);
274 AssertRCReturn(rc, rc);
275
276 rc = SSMR3RegisterExternal(pVM, "DisplayData", 24 /*uInstance*/, sSSMDisplayVer, 0 /*cbGuess*/,
277 NULL, NULL, NULL,
278 NULL, NULL, NULL,
279 NULL, displaySSMLoad, NULL, this);
280 AssertRCReturn(rc, rc);
281 return VINF_SUCCESS;
282}
283
284// IConsoleCallback method
285STDMETHODIMP Display::OnStateChange(MachineState_T machineState)
286{
287 if (machineState == MachineState_Running)
288 {
289 LogFlowFunc (("Machine is running.\n"));
290
291 mfMachineRunning = true;
292 }
293 else
294 mfMachineRunning = false;
295
296 return S_OK;
297}
298
299// public methods only for internal purposes
300/////////////////////////////////////////////////////////////////////////////
301
302/**
303 * @thread EMT
304 */
305static int callFramebufferResize (IFramebuffer *pFramebuffer, unsigned uScreenId,
306 ULONG pixelFormat, void *pvVRAM,
307 uint32_t bpp, uint32_t cbLine,
308 int w, int h)
309{
310 Assert (pFramebuffer);
311
312 /* Call the framebuffer to try and set required pixelFormat. */
313 BOOL finished = TRUE;
314
315 pFramebuffer->RequestResize (uScreenId, pixelFormat, (BYTE *) pvVRAM,
316 bpp, cbLine, w, h, &finished);
317
318 if (!finished)
319 {
320 LogFlowFunc (("External framebuffer wants us to wait!\n"));
321 return VINF_VGA_RESIZE_IN_PROGRESS;
322 }
323
324 return VINF_SUCCESS;
325}
326
327/**
328 * Handles display resize event.
329 * Disables access to VGA device;
330 * calls the framebuffer RequestResize method;
331 * if framebuffer resizes synchronously,
332 * updates the display connector data and enables access to the VGA device.
333 *
334 * @param w New display width
335 * @param h New display height
336 *
337 * @thread EMT
338 */
339int Display::handleDisplayResize (unsigned uScreenId, uint32_t bpp, void *pvVRAM,
340 uint32_t cbLine, int w, int h)
341{
342 LogRel (("Display::handleDisplayResize(): uScreenId = %d, pvVRAM=%p "
343 "w=%d h=%d bpp=%d cbLine=0x%X\n",
344 uScreenId, pvVRAM, w, h, bpp, cbLine));
345
346 /* If there is no framebuffer, this call is not interesting. */
347 if ( uScreenId >= mcMonitors
348 || maFramebuffers[uScreenId].pFramebuffer.isNull())
349 {
350 return VINF_SUCCESS;
351 }
352
353 mLastAddress = pvVRAM;
354 mLastBytesPerLine = cbLine;
355 mLastBitsPerPixel = bpp,
356 mLastWidth = w;
357 mLastHeight = h;
358
359 ULONG pixelFormat;
360
361 switch (bpp)
362 {
363 case 32:
364 case 24:
365 case 16:
366 pixelFormat = FramebufferPixelFormat_FOURCC_RGB;
367 break;
368 default:
369 pixelFormat = FramebufferPixelFormat_Opaque;
370 bpp = cbLine = 0;
371 break;
372 }
373
374 /* Atomically set the resize status before calling the framebuffer. The new InProgress status will
375 * disable access to the VGA device by the EMT thread.
376 */
377 bool f = ASMAtomicCmpXchgU32 (&maFramebuffers[uScreenId].u32ResizeStatus,
378 ResizeStatus_InProgress, ResizeStatus_Void);
379 if (!f)
380 {
381 /* This could be a result of the screenshot taking call Display::TakeScreenShot:
382 * if the framebuffer is processing the resize request and GUI calls the TakeScreenShot
383 * and the guest has reprogrammed the virtual VGA devices again so a new resize is required.
384 *
385 * Save the resize information and return the pending status code.
386 *
387 * Note: the resize information is only accessed on EMT so no serialization is required.
388 */
389 LogRel (("Display::handleDisplayResize(): Warning: resize postponed.\n"));
390
391 maFramebuffers[uScreenId].pendingResize.fPending = true;
392 maFramebuffers[uScreenId].pendingResize.pixelFormat = pixelFormat;
393 maFramebuffers[uScreenId].pendingResize.pvVRAM = pvVRAM;
394 maFramebuffers[uScreenId].pendingResize.bpp = bpp;
395 maFramebuffers[uScreenId].pendingResize.cbLine = cbLine;
396 maFramebuffers[uScreenId].pendingResize.w = w;
397 maFramebuffers[uScreenId].pendingResize.h = h;
398
399 return VINF_VGA_RESIZE_IN_PROGRESS;
400 }
401
402 int rc = callFramebufferResize (maFramebuffers[uScreenId].pFramebuffer, uScreenId,
403 pixelFormat, pvVRAM, bpp, cbLine, w, h);
404 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
405 {
406 /* Immediately return to the caller. ResizeCompleted will be called back by the
407 * GUI thread. The ResizeCompleted callback will change the resize status from
408 * InProgress to UpdateDisplayData. The latter status will be checked by the
409 * display timer callback on EMT and all required adjustments will be done there.
410 */
411 return rc;
412 }
413
414 /* Set the status so the 'handleResizeCompleted' would work. */
415 f = ASMAtomicCmpXchgU32 (&maFramebuffers[uScreenId].u32ResizeStatus,
416 ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
417 AssertRelease(f);NOREF(f);
418
419 AssertRelease(!maFramebuffers[uScreenId].pendingResize.fPending);
420
421 /* The method also unlocks the framebuffer. */
422 handleResizeCompletedEMT();
423
424 return VINF_SUCCESS;
425}
426
427/**
428 * Framebuffer has been resized.
429 * Read the new display data and unlock the framebuffer.
430 *
431 * @thread EMT
432 */
433void Display::handleResizeCompletedEMT (void)
434{
435 LogFlowFunc(("\n"));
436
437 unsigned uScreenId;
438 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
439 {
440 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
441
442 /* Try to into non resizing state. */
443 bool f = ASMAtomicCmpXchgU32 (&pFBInfo->u32ResizeStatus, ResizeStatus_Void, ResizeStatus_UpdateDisplayData);
444
445 if (f == false)
446 {
447 /* This is not the display that has completed resizing. */
448 continue;
449 }
450
451 /* Check whether a resize is pending for this framebuffer. */
452 if (pFBInfo->pendingResize.fPending)
453 {
454 /* Reset the condition, call the display resize with saved data and continue.
455 *
456 * Note: handleDisplayResize can call handleResizeCompletedEMT back,
457 * but infinite recursion is not possible, because when the handleResizeCompletedEMT
458 * is called, the pFBInfo->pendingResize.fPending is equal to false.
459 */
460 pFBInfo->pendingResize.fPending = false;
461 handleDisplayResize (uScreenId, pFBInfo->pendingResize.bpp, pFBInfo->pendingResize.pvVRAM,
462 pFBInfo->pendingResize.cbLine, pFBInfo->pendingResize.w, pFBInfo->pendingResize.h);
463 continue;
464 }
465
466 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN && !pFBInfo->pFramebuffer.isNull())
467 {
468 /* Primary framebuffer has completed the resize. Update the connector data for VGA device. */
469 updateDisplayData();
470
471 /* Check the framebuffer pixel format to setup the rendering in VGA device. */
472 BOOL usesGuestVRAM = FALSE;
473 pFBInfo->pFramebuffer->COMGETTER(UsesGuestVRAM) (&usesGuestVRAM);
474
475 pFBInfo->fDefaultFormat = (usesGuestVRAM == FALSE);
476
477 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort, pFBInfo->fDefaultFormat);
478 }
479
480#ifdef DEBUG_sunlover
481 if (!stam)
482 {
483 /* protect mpVM */
484 Console::SafeVMPtr pVM (mParent);
485 AssertComRC (pVM.rc());
486
487 STAM_REG(pVM, &StatDisplayRefresh, STAMTYPE_PROFILE, "/PROF/Display/Refresh", STAMUNIT_TICKS_PER_CALL, "Time spent in EMT for display updates.");
488 stam = 1;
489 }
490#endif /* DEBUG_sunlover */
491
492 /* Inform VRDP server about the change of display parameters. */
493 LogFlowFunc (("Calling VRDP\n"));
494 mParent->consoleVRDPServer()->SendResize();
495 }
496}
497
498static void checkCoordBounds (int *px, int *py, int *pw, int *ph, int cx, int cy)
499{
500 /* Correct negative x and y coordinates. */
501 if (*px < 0)
502 {
503 *px += *pw; /* Compute xRight which is also the new width. */
504
505 *pw = (*px < 0)? 0: *px;
506
507 *px = 0;
508 }
509
510 if (*py < 0)
511 {
512 *py += *ph; /* Compute xBottom, which is also the new height. */
513
514 *ph = (*py < 0)? 0: *py;
515
516 *py = 0;
517 }
518
519 /* Also check if coords are greater than the display resolution. */
520 if (*px + *pw > cx)
521 {
522 *pw = cx > *px? cx - *px: 0;
523 }
524
525 if (*py + *ph > cy)
526 {
527 *ph = cy > *py? cy - *py: 0;
528 }
529}
530
531unsigned mapCoordsToScreen(DISPLAYFBINFO *pInfos, unsigned cInfos, int *px, int *py, int *pw, int *ph)
532{
533 DISPLAYFBINFO *pInfo = pInfos;
534 unsigned uScreenId;
535 LogSunlover (("mapCoordsToScreen: %d,%d %dx%d\n", *px, *py, *pw, *ph));
536 for (uScreenId = 0; uScreenId < cInfos; uScreenId++, pInfo++)
537 {
538 LogSunlover ((" [%d] %d,%d %dx%d\n", uScreenId, pInfo->xOrigin, pInfo->yOrigin, pInfo->w, pInfo->h));
539 if ( (pInfo->xOrigin <= *px && *px < pInfo->xOrigin + (int)pInfo->w)
540 && (pInfo->yOrigin <= *py && *py < pInfo->yOrigin + (int)pInfo->h))
541 {
542 /* The rectangle belongs to the screen. Correct coordinates. */
543 *px -= pInfo->xOrigin;
544 *py -= pInfo->yOrigin;
545 LogSunlover ((" -> %d,%d", *px, *py));
546 break;
547 }
548 }
549 if (uScreenId == cInfos)
550 {
551 /* Map to primary screen. */
552 uScreenId = 0;
553 }
554 LogSunlover ((" scr %d\n", uScreenId));
555 return uScreenId;
556}
557
558
559/**
560 * Handles display update event.
561 *
562 * @param x Update area x coordinate
563 * @param y Update area y coordinate
564 * @param w Update area width
565 * @param h Update area height
566 *
567 * @thread EMT
568 */
569void Display::handleDisplayUpdate (int x, int y, int w, int h)
570{
571#ifdef DEBUG_sunlover
572 LogFlowFunc (("%d,%d %dx%d (%d,%d)\n",
573 x, y, w, h, mpDrv->Connector.cx, mpDrv->Connector.cy));
574#endif /* DEBUG_sunlover */
575
576 unsigned uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
577
578#ifdef DEBUG_sunlover
579 LogFlowFunc (("%d,%d %dx%d (checked)\n", x, y, w, h));
580#endif /* DEBUG_sunlover */
581
582 IFramebuffer *pFramebuffer = maFramebuffers[uScreenId].pFramebuffer;
583
584 // if there is no framebuffer, this call is not interesting
585 if (pFramebuffer == NULL)
586 return;
587
588 pFramebuffer->Lock();
589
590 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
591 checkCoordBounds (&x, &y, &w, &h, mpDrv->Connector.cx, mpDrv->Connector.cy);
592 else
593 checkCoordBounds (&x, &y, &w, &h, maFramebuffers[uScreenId].w,
594 maFramebuffers[uScreenId].h);
595
596 if (w != 0 && h != 0)
597 pFramebuffer->NotifyUpdate(x, y, w, h);
598
599 pFramebuffer->Unlock();
600
601#ifndef VBOX_WITH_HGSMI
602 if (!mfVideoAccelEnabled)
603 {
604#else
605 if (!mfVideoAccelEnabled && !maFramebuffers[uScreenId].fVBVAEnabled)
606 {
607#endif /* VBOX_WITH_HGSMI */
608 /* When VBVA is enabled, the VRDP server is informed in the VideoAccelFlush.
609 * Inform the server here only if VBVA is disabled.
610 */
611 if (maFramebuffers[uScreenId].u32ResizeStatus == ResizeStatus_Void)
612 mParent->consoleVRDPServer()->SendUpdateBitmap(uScreenId, x, y, w, h);
613 }
614}
615
616typedef struct _VBVADIRTYREGION
617{
618 /* Copies of object's pointers used by vbvaRgn functions. */
619 DISPLAYFBINFO *paFramebuffers;
620 unsigned cMonitors;
621 Display *pDisplay;
622 PPDMIDISPLAYPORT pPort;
623
624} VBVADIRTYREGION;
625
626static void vbvaRgnInit (VBVADIRTYREGION *prgn, DISPLAYFBINFO *paFramebuffers, unsigned cMonitors, Display *pd, PPDMIDISPLAYPORT pp)
627{
628 prgn->paFramebuffers = paFramebuffers;
629 prgn->cMonitors = cMonitors;
630 prgn->pDisplay = pd;
631 prgn->pPort = pp;
632
633 unsigned uScreenId;
634 for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
635 {
636 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
637
638 memset (&pFBInfo->dirtyRect, 0, sizeof (pFBInfo->dirtyRect));
639 }
640}
641
642static void vbvaRgnDirtyRect (VBVADIRTYREGION *prgn, unsigned uScreenId, VBVACMDHDR *phdr)
643{
644 LogSunlover (("x = %d, y = %d, w = %d, h = %d\n",
645 phdr->x, phdr->y, phdr->w, phdr->h));
646
647 /*
648 * Here update rectangles are accumulated to form an update area.
649 * @todo
650 * Now the simpliest method is used which builds one rectangle that
651 * includes all update areas. A bit more advanced method can be
652 * employed here. The method should be fast however.
653 */
654 if (phdr->w == 0 || phdr->h == 0)
655 {
656 /* Empty rectangle. */
657 return;
658 }
659
660 int32_t xRight = phdr->x + phdr->w;
661 int32_t yBottom = phdr->y + phdr->h;
662
663 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
664
665 if (pFBInfo->dirtyRect.xRight == 0)
666 {
667 /* This is the first rectangle to be added. */
668 pFBInfo->dirtyRect.xLeft = phdr->x;
669 pFBInfo->dirtyRect.yTop = phdr->y;
670 pFBInfo->dirtyRect.xRight = xRight;
671 pFBInfo->dirtyRect.yBottom = yBottom;
672 }
673 else
674 {
675 /* Adjust region coordinates. */
676 if (pFBInfo->dirtyRect.xLeft > phdr->x)
677 {
678 pFBInfo->dirtyRect.xLeft = phdr->x;
679 }
680
681 if (pFBInfo->dirtyRect.yTop > phdr->y)
682 {
683 pFBInfo->dirtyRect.yTop = phdr->y;
684 }
685
686 if (pFBInfo->dirtyRect.xRight < xRight)
687 {
688 pFBInfo->dirtyRect.xRight = xRight;
689 }
690
691 if (pFBInfo->dirtyRect.yBottom < yBottom)
692 {
693 pFBInfo->dirtyRect.yBottom = yBottom;
694 }
695 }
696
697 if (pFBInfo->fDefaultFormat)
698 {
699 //@todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
700 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, phdr->x, phdr->y, phdr->w, phdr->h);
701 prgn->pDisplay->handleDisplayUpdate (phdr->x + pFBInfo->xOrigin,
702 phdr->y + pFBInfo->yOrigin, phdr->w, phdr->h);
703 }
704
705 return;
706}
707
708static void vbvaRgnUpdateFramebuffer (VBVADIRTYREGION *prgn, unsigned uScreenId)
709{
710 DISPLAYFBINFO *pFBInfo = &prgn->paFramebuffers[uScreenId];
711
712 uint32_t w = pFBInfo->dirtyRect.xRight - pFBInfo->dirtyRect.xLeft;
713 uint32_t h = pFBInfo->dirtyRect.yBottom - pFBInfo->dirtyRect.yTop;
714
715 if (!pFBInfo->fDefaultFormat && pFBInfo->pFramebuffer && w != 0 && h != 0)
716 {
717 //@todo pfnUpdateDisplayRect must take the vram offset parameter for the framebuffer
718 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, pFBInfo->dirtyRect.xLeft, pFBInfo->dirtyRect.yTop, w, h);
719 prgn->pDisplay->handleDisplayUpdate (pFBInfo->dirtyRect.xLeft + pFBInfo->xOrigin,
720 pFBInfo->dirtyRect.yTop + pFBInfo->yOrigin, w, h);
721 }
722}
723
724static void vbvaSetMemoryFlags (VBVAMEMORY *pVbvaMemory,
725 bool fVideoAccelEnabled,
726 bool fVideoAccelVRDP,
727 uint32_t fu32SupportedOrders,
728 DISPLAYFBINFO *paFBInfos,
729 unsigned cFBInfos)
730{
731 if (pVbvaMemory)
732 {
733 /* This called only on changes in mode. So reset VRDP always. */
734 uint32_t fu32Flags = VBVA_F_MODE_VRDP_RESET;
735
736 if (fVideoAccelEnabled)
737 {
738 fu32Flags |= VBVA_F_MODE_ENABLED;
739
740 if (fVideoAccelVRDP)
741 {
742 fu32Flags |= VBVA_F_MODE_VRDP | VBVA_F_MODE_VRDP_ORDER_MASK;
743
744 pVbvaMemory->fu32SupportedOrders = fu32SupportedOrders;
745 }
746 }
747
748 pVbvaMemory->fu32ModeFlags = fu32Flags;
749 }
750
751 unsigned uScreenId;
752 for (uScreenId = 0; uScreenId < cFBInfos; uScreenId++)
753 {
754 if (paFBInfos[uScreenId].pHostEvents)
755 {
756 paFBInfos[uScreenId].pHostEvents->fu32Events |= VBOX_VIDEO_INFO_HOST_EVENTS_F_VRDP_RESET;
757 }
758 }
759}
760
761bool Display::VideoAccelAllowed (void)
762{
763 return true;
764}
765
766/**
767 * @thread EMT
768 */
769int Display::VideoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
770{
771 int rc = VINF_SUCCESS;
772
773 /* Called each time the guest wants to use acceleration,
774 * or when the VGA device disables acceleration,
775 * or when restoring the saved state with accel enabled.
776 *
777 * VGA device disables acceleration on each video mode change
778 * and on reset.
779 *
780 * Guest enabled acceleration at will. And it has to enable
781 * acceleration after a mode change.
782 */
783 LogFlowFunc (("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
784 mfVideoAccelEnabled, fEnable, pVbvaMemory));
785
786 /* Strictly check parameters. Callers must not pass anything in the case. */
787 Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));
788
789 if (!VideoAccelAllowed ())
790 {
791 return VERR_NOT_SUPPORTED;
792 }
793
794 /*
795 * Verify that the VM is in running state. If it is not,
796 * then this must be postponed until it goes to running.
797 */
798 if (!mfMachineRunning)
799 {
800 Assert (!mfVideoAccelEnabled);
801
802 LogFlowFunc (("Machine is not yet running.\n"));
803
804 if (fEnable)
805 {
806 mfPendingVideoAccelEnable = fEnable;
807 mpPendingVbvaMemory = pVbvaMemory;
808 }
809
810 return rc;
811 }
812
813 /* Check that current status is not being changed */
814 if (mfVideoAccelEnabled == fEnable)
815 {
816 return rc;
817 }
818
819 if (mfVideoAccelEnabled)
820 {
821 /* Process any pending orders and empty the VBVA ring buffer. */
822 VideoAccelFlush ();
823 }
824
825 if (!fEnable && mpVbvaMemory)
826 {
827 mpVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;
828 }
829
830 /* Safety precaution. There is no more VBVA until everything is setup! */
831 mpVbvaMemory = NULL;
832 mfVideoAccelEnabled = false;
833
834 /* Update entire display. */
835 if (maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].u32ResizeStatus == ResizeStatus_Void)
836 {
837 mpDrv->pUpPort->pfnUpdateDisplayAll(mpDrv->pUpPort);
838 }
839
840 /* Everything OK. VBVA status can be changed. */
841
842 /* Notify the VMMDev, which saves VBVA status in the saved state,
843 * and needs to know current status.
844 */
845 PPDMIVMMDEVPORT pVMMDevPort = mParent->getVMMDev()->getVMMDevPort ();
846
847 if (pVMMDevPort)
848 {
849 pVMMDevPort->pfnVBVAChange (pVMMDevPort, fEnable);
850 }
851
852 if (fEnable)
853 {
854 mpVbvaMemory = pVbvaMemory;
855 mfVideoAccelEnabled = true;
856
857 /* Initialize the hardware memory. */
858 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
859 mpVbvaMemory->off32Data = 0;
860 mpVbvaMemory->off32Free = 0;
861
862 memset (mpVbvaMemory->aRecords, 0, sizeof (mpVbvaMemory->aRecords));
863 mpVbvaMemory->indexRecordFirst = 0;
864 mpVbvaMemory->indexRecordFree = 0;
865
866 LogRel(("VBVA: Enabled.\n"));
867 }
868 else
869 {
870 LogRel(("VBVA: Disabled.\n"));
871 }
872
873 LogFlowFunc (("VideoAccelEnable: rc = %Rrc.\n", rc));
874
875 return rc;
876}
877
878#ifdef VBOX_WITH_VRDP
879/* Called always by one VRDP server thread. Can be thread-unsafe.
880 */
881void Display::VideoAccelVRDP (bool fEnable)
882{
883 int c = fEnable?
884 ASMAtomicIncS32 (&mcVideoAccelVRDPRefs):
885 ASMAtomicDecS32 (&mcVideoAccelVRDPRefs);
886
887 Assert (c >= 0);
888
889 if (c == 0)
890 {
891 /* The last client has disconnected, and the accel can be
892 * disabled.
893 */
894 Assert (fEnable == false);
895
896 mfVideoAccelVRDP = false;
897 mfu32SupportedOrders = 0;
898
899 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
900
901 LogRel(("VBVA: VRDP acceleration has been disabled.\n"));
902 }
903 else if ( c == 1
904 && !mfVideoAccelVRDP)
905 {
906 /* The first client has connected. Enable the accel.
907 */
908 Assert (fEnable == true);
909
910 mfVideoAccelVRDP = true;
911 /* Supporting all orders. */
912 mfu32SupportedOrders = ~0;
913
914 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders, maFramebuffers, mcMonitors);
915
916 LogRel(("VBVA: VRDP acceleration has been requested.\n"));
917 }
918 else
919 {
920 /* A client is connected or disconnected but there is no change in the
921 * accel state. It remains enabled.
922 */
923 Assert (mfVideoAccelVRDP == true);
924 }
925}
926#endif /* VBOX_WITH_VRDP */
927
928static bool vbvaVerifyRingBuffer (VBVAMEMORY *pVbvaMemory)
929{
930 return true;
931}
932
933static void vbvaFetchBytes (VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t cbDst)
934{
935 if (cbDst >= VBVA_RING_BUFFER_SIZE)
936 {
937 AssertMsgFailed (("cbDst = 0x%08X, ring buffer size 0x%08X", cbDst, VBVA_RING_BUFFER_SIZE));
938 return;
939 }
940
941 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
942 uint8_t *src = &pVbvaMemory->au8RingBuffer[pVbvaMemory->off32Data];
943 int32_t i32Diff = cbDst - u32BytesTillBoundary;
944
945 if (i32Diff <= 0)
946 {
947 /* Chunk will not cross buffer boundary. */
948 memcpy (pu8Dst, src, cbDst);
949 }
950 else
951 {
952 /* Chunk crosses buffer boundary. */
953 memcpy (pu8Dst, src, u32BytesTillBoundary);
954 memcpy (pu8Dst + u32BytesTillBoundary, &pVbvaMemory->au8RingBuffer[0], i32Diff);
955 }
956
957 /* Advance data offset. */
958 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbDst) % VBVA_RING_BUFFER_SIZE;
959
960 return;
961}
962
963
964static bool vbvaPartialRead (uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
965{
966 uint8_t *pu8New;
967
968 LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
969 *ppu8, *pcb, cbRecord));
970
971 if (*ppu8)
972 {
973 Assert (*pcb);
974 pu8New = (uint8_t *)RTMemRealloc (*ppu8, cbRecord);
975 }
976 else
977 {
978 Assert (!*pcb);
979 pu8New = (uint8_t *)RTMemAlloc (cbRecord);
980 }
981
982 if (!pu8New)
983 {
984 /* Memory allocation failed, fail the function. */
985 Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
986 cbRecord));
987
988 if (*ppu8)
989 {
990 RTMemFree (*ppu8);
991 }
992
993 *ppu8 = NULL;
994 *pcb = 0;
995
996 return false;
997 }
998
999 /* Fetch data from the ring buffer. */
1000 vbvaFetchBytes (pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
1001
1002 *ppu8 = pu8New;
1003 *pcb = cbRecord;
1004
1005 return true;
1006}
1007
1008/* For contiguous chunks just return the address in the buffer.
1009 * For crossing boundary - allocate a buffer from heap.
1010 */
1011bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
1012{
1013 uint32_t indexRecordFirst = mpVbvaMemory->indexRecordFirst;
1014 uint32_t indexRecordFree = mpVbvaMemory->indexRecordFree;
1015
1016#ifdef DEBUG_sunlover
1017 LogFlowFunc (("first = %d, free = %d\n",
1018 indexRecordFirst, indexRecordFree));
1019#endif /* DEBUG_sunlover */
1020
1021 if (!vbvaVerifyRingBuffer (mpVbvaMemory))
1022 {
1023 return false;
1024 }
1025
1026 if (indexRecordFirst == indexRecordFree)
1027 {
1028 /* No records to process. Return without assigning output variables. */
1029 return true;
1030 }
1031
1032 VBVARECORD *pRecord = &mpVbvaMemory->aRecords[indexRecordFirst];
1033
1034#ifdef DEBUG_sunlover
1035 LogFlowFunc (("cbRecord = 0x%08X\n", pRecord->cbRecord));
1036#endif /* DEBUG_sunlover */
1037
1038 uint32_t cbRecord = pRecord->cbRecord & ~VBVA_F_RECORD_PARTIAL;
1039
1040 if (mcbVbvaPartial)
1041 {
1042 /* There is a partial read in process. Continue with it. */
1043
1044 Assert (mpu8VbvaPartial);
1045
1046 LogFlowFunc (("continue partial record mcbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
1047 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
1048
1049 if (cbRecord > mcbVbvaPartial)
1050 {
1051 /* New data has been added to the record. */
1052 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
1053 {
1054 return false;
1055 }
1056 }
1057
1058 if (!(pRecord->cbRecord & VBVA_F_RECORD_PARTIAL))
1059 {
1060 /* The record is completed by guest. Return it to the caller. */
1061 *ppHdr = (VBVACMDHDR *)mpu8VbvaPartial;
1062 *pcbCmd = mcbVbvaPartial;
1063
1064 mpu8VbvaPartial = NULL;
1065 mcbVbvaPartial = 0;
1066
1067 /* Advance the record index. */
1068 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
1069
1070#ifdef DEBUG_sunlover
1071 LogFlowFunc (("partial done ok, data = %d, free = %d\n",
1072 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1073#endif /* DEBUG_sunlover */
1074 }
1075
1076 return true;
1077 }
1078
1079 /* A new record need to be processed. */
1080 if (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)
1081 {
1082 /* Current record is being written by guest. '=' is important here. */
1083 if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
1084 {
1085 /* Partial read must be started. */
1086 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
1087 {
1088 return false;
1089 }
1090
1091 LogFlowFunc (("started partial record mcbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
1092 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
1093 }
1094
1095 return true;
1096 }
1097
1098 /* Current record is complete. If it is not empty, process it. */
1099 if (cbRecord)
1100 {
1101 /* The size of largest contiguos chunk in the ring biffer. */
1102 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - mpVbvaMemory->off32Data;
1103
1104 /* The ring buffer pointer. */
1105 uint8_t *au8RingBuffer = &mpVbvaMemory->au8RingBuffer[0];
1106
1107 /* The pointer to data in the ring buffer. */
1108 uint8_t *src = &au8RingBuffer[mpVbvaMemory->off32Data];
1109
1110 /* Fetch or point the data. */
1111 if (u32BytesTillBoundary >= cbRecord)
1112 {
1113 /* The command does not cross buffer boundary. Return address in the buffer. */
1114 *ppHdr = (VBVACMDHDR *)src;
1115
1116 /* Advance data offset. */
1117 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
1118 }
1119 else
1120 {
1121 /* The command crosses buffer boundary. Rare case, so not optimized. */
1122 uint8_t *dst = (uint8_t *)RTMemAlloc (cbRecord);
1123
1124 if (!dst)
1125 {
1126 LogFlowFunc (("could not allocate %d bytes from heap!!!\n", cbRecord));
1127 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
1128 return false;
1129 }
1130
1131 vbvaFetchBytes (mpVbvaMemory, dst, cbRecord);
1132
1133 *ppHdr = (VBVACMDHDR *)dst;
1134
1135#ifdef DEBUG_sunlover
1136 LogFlowFunc (("Allocated from heap %p\n", dst));
1137#endif /* DEBUG_sunlover */
1138 }
1139 }
1140
1141 *pcbCmd = cbRecord;
1142
1143 /* Advance the record index. */
1144 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
1145
1146#ifdef DEBUG_sunlover
1147 LogFlowFunc (("done ok, data = %d, free = %d\n",
1148 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1149#endif /* DEBUG_sunlover */
1150
1151 return true;
1152}
1153
1154void Display::vbvaReleaseCmd (VBVACMDHDR *pHdr, int32_t cbCmd)
1155{
1156 uint8_t *au8RingBuffer = mpVbvaMemory->au8RingBuffer;
1157
1158 if ( (uint8_t *)pHdr >= au8RingBuffer
1159 && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
1160 {
1161 /* The pointer is inside ring buffer. Must be continuous chunk. */
1162 Assert (VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
1163
1164 /* Do nothing. */
1165
1166 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1167 }
1168 else
1169 {
1170 /* The pointer is outside. It is then an allocated copy. */
1171
1172#ifdef DEBUG_sunlover
1173 LogFlowFunc (("Free heap %p\n", pHdr));
1174#endif /* DEBUG_sunlover */
1175
1176 if ((uint8_t *)pHdr == mpu8VbvaPartial)
1177 {
1178 mpu8VbvaPartial = NULL;
1179 mcbVbvaPartial = 0;
1180 }
1181 else
1182 {
1183 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1184 }
1185
1186 RTMemFree (pHdr);
1187 }
1188
1189 return;
1190}
1191
1192
1193/**
1194 * Called regularly on the DisplayRefresh timer.
1195 * Also on behalf of guest, when the ring buffer is full.
1196 *
1197 * @thread EMT
1198 */
1199void Display::VideoAccelFlush (void)
1200{
1201#ifdef DEBUG_sunlover_2
1202 LogFlowFunc (("mfVideoAccelEnabled = %d\n", mfVideoAccelEnabled));
1203#endif /* DEBUG_sunlover_2 */
1204
1205 if (!mfVideoAccelEnabled)
1206 {
1207 Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
1208 return;
1209 }
1210
1211 /* Here VBVA is enabled and we have the accelerator memory pointer. */
1212 Assert(mpVbvaMemory);
1213
1214#ifdef DEBUG_sunlover_2
1215 LogFlowFunc (("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
1216 mpVbvaMemory->indexRecordFirst, mpVbvaMemory->indexRecordFree, mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1217#endif /* DEBUG_sunlover_2 */
1218
1219 /* Quick check for "nothing to update" case. */
1220 if (mpVbvaMemory->indexRecordFirst == mpVbvaMemory->indexRecordFree)
1221 {
1222 return;
1223 }
1224
1225 /* Process the ring buffer */
1226 unsigned uScreenId;
1227 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1228 {
1229 if (!maFramebuffers[uScreenId].pFramebuffer.isNull())
1230 {
1231 maFramebuffers[uScreenId].pFramebuffer->Lock ();
1232 }
1233 }
1234
1235 /* Initialize dirty rectangles accumulator. */
1236 VBVADIRTYREGION rgn;
1237 vbvaRgnInit (&rgn, maFramebuffers, mcMonitors, this, mpDrv->pUpPort);
1238
1239 for (;;)
1240 {
1241 VBVACMDHDR *phdr = NULL;
1242 uint32_t cbCmd = ~0;
1243
1244 /* Fetch the command data. */
1245 if (!vbvaFetchCmd (&phdr, &cbCmd))
1246 {
1247 Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
1248 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1249
1250 /* Disable VBVA on those processing errors. */
1251 VideoAccelEnable (false, NULL);
1252
1253 break;
1254 }
1255
1256 if (cbCmd == uint32_t(~0))
1257 {
1258 /* No more commands yet in the queue. */
1259 break;
1260 }
1261
1262 if (cbCmd != 0)
1263 {
1264#ifdef DEBUG_sunlover
1265 LogFlowFunc (("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
1266 cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
1267#endif /* DEBUG_sunlover */
1268
1269 VBVACMDHDR hdrSaved = *phdr;
1270
1271 int x = phdr->x;
1272 int y = phdr->y;
1273 int w = phdr->w;
1274 int h = phdr->h;
1275
1276 uScreenId = mapCoordsToScreen(maFramebuffers, mcMonitors, &x, &y, &w, &h);
1277
1278 phdr->x = (int16_t)x;
1279 phdr->y = (int16_t)y;
1280 phdr->w = (uint16_t)w;
1281 phdr->h = (uint16_t)h;
1282
1283 DISPLAYFBINFO *pFBInfo = &maFramebuffers[uScreenId];
1284
1285 if (pFBInfo->u32ResizeStatus == ResizeStatus_Void)
1286 {
1287 /* Handle the command.
1288 *
1289 * Guest is responsible for updating the guest video memory.
1290 * The Windows guest does all drawing using Eng*.
1291 *
1292 * For local output, only dirty rectangle information is used
1293 * to update changed areas.
1294 *
1295 * Dirty rectangles are accumulated to exclude overlapping updates and
1296 * group small updates to a larger one.
1297 */
1298
1299 /* Accumulate the update. */
1300 vbvaRgnDirtyRect (&rgn, uScreenId, phdr);
1301
1302 /* Forward the command to VRDP server. */
1303 mParent->consoleVRDPServer()->SendUpdate (uScreenId, phdr, cbCmd);
1304
1305 *phdr = hdrSaved;
1306 }
1307 }
1308
1309 vbvaReleaseCmd (phdr, cbCmd);
1310 }
1311
1312 for (uScreenId = 0; uScreenId < mcMonitors; uScreenId++)
1313 {
1314 if (!maFramebuffers[uScreenId].pFramebuffer.isNull())
1315 {
1316 maFramebuffers[uScreenId].pFramebuffer->Unlock ();
1317 }
1318
1319 if (maFramebuffers[uScreenId].u32ResizeStatus == ResizeStatus_Void)
1320 {
1321 /* Draw the framebuffer. */
1322 vbvaRgnUpdateFramebuffer (&rgn, uScreenId);
1323 }
1324 }
1325}
1326
1327
1328// IDisplay properties
1329/////////////////////////////////////////////////////////////////////////////
1330
1331/**
1332 * Returns the current display width in pixel
1333 *
1334 * @returns COM status code
1335 * @param width Address of result variable.
1336 */
1337STDMETHODIMP Display::COMGETTER(Width) (ULONG *width)
1338{
1339 CheckComArgNotNull(width);
1340
1341 AutoCaller autoCaller(this);
1342 CheckComRCReturnRC(autoCaller.rc());
1343
1344 AutoWriteLock alock(this);
1345
1346 CHECK_CONSOLE_DRV (mpDrv);
1347
1348 *width = mpDrv->Connector.cx;
1349
1350 return S_OK;
1351}
1352
1353/**
1354 * Returns the current display height in pixel
1355 *
1356 * @returns COM status code
1357 * @param height Address of result variable.
1358 */
1359STDMETHODIMP Display::COMGETTER(Height) (ULONG *height)
1360{
1361 CheckComArgNotNull(height);
1362
1363 AutoCaller autoCaller(this);
1364 CheckComRCReturnRC(autoCaller.rc());
1365
1366 AutoWriteLock alock(this);
1367
1368 CHECK_CONSOLE_DRV (mpDrv);
1369
1370 *height = mpDrv->Connector.cy;
1371
1372 return S_OK;
1373}
1374
1375/**
1376 * Returns the current display color depth in bits
1377 *
1378 * @returns COM status code
1379 * @param bitsPerPixel Address of result variable.
1380 */
1381STDMETHODIMP Display::COMGETTER(BitsPerPixel) (ULONG *bitsPerPixel)
1382{
1383 if (!bitsPerPixel)
1384 return E_INVALIDARG;
1385
1386 AutoCaller autoCaller(this);
1387 CheckComRCReturnRC(autoCaller.rc());
1388
1389 AutoWriteLock alock(this);
1390
1391 CHECK_CONSOLE_DRV (mpDrv);
1392
1393 uint32_t cBits = 0;
1394 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
1395 AssertRC(rc);
1396 *bitsPerPixel = cBits;
1397
1398 return S_OK;
1399}
1400
1401
1402// IDisplay methods
1403/////////////////////////////////////////////////////////////////////////////
1404
1405STDMETHODIMP Display::SetFramebuffer (ULONG aScreenId,
1406 IFramebuffer *aFramebuffer)
1407{
1408 LogFlowFunc (("\n"));
1409
1410 if (aFramebuffer != NULL)
1411 CheckComArgOutPointerValid(aFramebuffer);
1412
1413 AutoCaller autoCaller(this);
1414 CheckComRCReturnRC(autoCaller.rc());
1415
1416 AutoWriteLock alock(this);
1417
1418 Console::SafeVMPtrQuiet pVM (mParent);
1419 if (pVM.isOk())
1420 {
1421 /* Must leave the lock here because the changeFramebuffer will
1422 * also obtain it. */
1423 alock.leave ();
1424
1425 /* send request to the EMT thread */
1426 int vrc = VMR3ReqCallWait (pVM, VMCPUID_ANY,
1427 (PFNRT) changeFramebuffer, 3, this, aFramebuffer, aScreenId);
1428
1429 alock.enter ();
1430
1431 ComAssertRCRet (vrc, E_FAIL);
1432 }
1433 else
1434 {
1435 /* No VM is created (VM is powered off), do a direct call */
1436 int vrc = changeFramebuffer (this, aFramebuffer, aScreenId);
1437 ComAssertRCRet (vrc, E_FAIL);
1438 }
1439
1440 return S_OK;
1441}
1442
1443STDMETHODIMP Display::GetFramebuffer (ULONG aScreenId,
1444 IFramebuffer **aFramebuffer, LONG *aXOrigin, LONG *aYOrigin)
1445{
1446 LogFlowFunc (("aScreenId = %d\n", aScreenId));
1447
1448 CheckComArgOutPointerValid(aFramebuffer);
1449
1450 AutoCaller autoCaller(this);
1451 CheckComRCReturnRC(autoCaller.rc());
1452
1453 AutoWriteLock alock(this);
1454
1455 /* @todo this should be actually done on EMT. */
1456 DISPLAYFBINFO *pFBInfo = &maFramebuffers[aScreenId];
1457
1458 *aFramebuffer = pFBInfo->pFramebuffer;
1459 if (*aFramebuffer)
1460 (*aFramebuffer)->AddRef ();
1461 if (aXOrigin)
1462 *aXOrigin = pFBInfo->xOrigin;
1463 if (aYOrigin)
1464 *aYOrigin = pFBInfo->yOrigin;
1465
1466 return S_OK;
1467}
1468
1469STDMETHODIMP Display::SetVideoModeHint(ULONG aWidth, ULONG aHeight,
1470 ULONG aBitsPerPixel, ULONG aDisplay)
1471{
1472 AutoCaller autoCaller(this);
1473 CheckComRCReturnRC(autoCaller.rc());
1474
1475 AutoWriteLock alock(this);
1476
1477 CHECK_CONSOLE_DRV (mpDrv);
1478
1479 /*
1480 * Do some rough checks for valid input
1481 */
1482 ULONG width = aWidth;
1483 if (!width)
1484 width = mpDrv->Connector.cx;
1485 ULONG height = aHeight;
1486 if (!height)
1487 height = mpDrv->Connector.cy;
1488 ULONG bpp = aBitsPerPixel;
1489 if (!bpp)
1490 {
1491 uint32_t cBits = 0;
1492 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
1493 AssertRC(rc);
1494 bpp = cBits;
1495 }
1496 ULONG cMonitors;
1497 mParent->machine()->COMGETTER(MonitorCount)(&cMonitors);
1498 if (cMonitors == 0 && aDisplay > 0)
1499 return E_INVALIDARG;
1500 if (aDisplay >= cMonitors)
1501 return E_INVALIDARG;
1502
1503// sunlover 20070614: It is up to the guest to decide whether the hint is valid.
1504// ULONG vramSize;
1505// mParent->machine()->COMGETTER(VRAMSize)(&vramSize);
1506// /* enough VRAM? */
1507// if ((width * height * (bpp / 8)) > (vramSize * 1024 * 1024))
1508// return setError(E_FAIL, tr("Not enough VRAM for the selected video mode"));
1509
1510 /* Have to leave the lock because the pfnRequestDisplayChange
1511 * will call EMT. */
1512 alock.leave ();
1513 if (mParent->getVMMDev())
1514 mParent->getVMMDev()->getVMMDevPort()->
1515 pfnRequestDisplayChange (mParent->getVMMDev()->getVMMDevPort(),
1516 aWidth, aHeight, aBitsPerPixel, aDisplay);
1517 return S_OK;
1518}
1519
1520STDMETHODIMP Display::SetSeamlessMode (BOOL enabled)
1521{
1522 AutoCaller autoCaller(this);
1523 CheckComRCReturnRC(autoCaller.rc());
1524
1525 AutoWriteLock alock(this);
1526
1527 /* Have to leave the lock because the pfnRequestSeamlessChange will call EMT. */
1528 alock.leave ();
1529 if (mParent->getVMMDev())
1530 mParent->getVMMDev()->getVMMDevPort()->
1531 pfnRequestSeamlessChange (mParent->getVMMDev()->getVMMDevPort(),
1532 !!enabled);
1533 return S_OK;
1534}
1535
1536STDMETHODIMP Display::TakeScreenShot (BYTE *address, ULONG width, ULONG height)
1537{
1538 /// @todo (r=dmik) this function may take too long to complete if the VM
1539 // is doing something like saving state right now. Which, in case if it
1540 // is called on the GUI thread, will make it unresponsive. We should
1541 // check the machine state here (by enclosing the check and VMRequCall
1542 // within the Console lock to make it atomic).
1543
1544 LogFlowFuncEnter();
1545 LogFlowFunc (("address=%p, width=%d, height=%d\n",
1546 address, width, height));
1547
1548 CheckComArgNotNull(address);
1549 CheckComArgExpr(width, width != 0);
1550 CheckComArgExpr(height, height != 0);
1551
1552 AutoCaller autoCaller(this);
1553 CheckComRCReturnRC(autoCaller.rc());
1554
1555 AutoWriteLock alock(this);
1556
1557 CHECK_CONSOLE_DRV (mpDrv);
1558
1559 Console::SafeVMPtr pVM (mParent);
1560 CheckComRCReturnRC(pVM.rc());
1561
1562 HRESULT rc = S_OK;
1563
1564 LogFlowFunc (("Sending SCREENSHOT request\n"));
1565
1566 /*
1567 * First try use the graphics device features for making a snapshot.
1568 * This does not support stretching, is an optional feature (returns
1569 * not supported).
1570 *
1571 * Note: It may cause a display resize. Watch out for deadlocks.
1572 */
1573 int rcVBox = VERR_NOT_SUPPORTED;
1574 if ( mpDrv->Connector.cx == width
1575 && mpDrv->Connector.cy == height)
1576 {
1577 size_t cbData = RT_ALIGN_Z(width, 4) * 4 * height;
1578 rcVBox = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)mpDrv->pUpPort->pfnSnapshot, 6, mpDrv->pUpPort,
1579 address, cbData, (uintptr_t)NULL, (uintptr_t)NULL, (uintptr_t)NULL);
1580 }
1581
1582 /*
1583 * If the function returns not supported, or if stretching is requested,
1584 * we'll have to do all the work ourselves using the framebuffer data.
1585 */
1586 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
1587 {
1588 /** @todo implement snapshot stretching & generic snapshot fallback. */
1589 rc = setError (E_NOTIMPL, tr ("This feature is not implemented"));
1590 }
1591 else if (RT_FAILURE(rcVBox))
1592 rc = setError (VBOX_E_IPRT_ERROR,
1593 tr ("Could not take a screenshot (%Rrc)"), rcVBox);
1594
1595 LogFlowFunc (("rc=%08X\n", rc));
1596 LogFlowFuncLeave();
1597 return rc;
1598}
1599
1600STDMETHODIMP Display::TakeScreenShotSlow (ULONG width, ULONG height,
1601 ComSafeArrayOut(BYTE, aScreenData))
1602{
1603 HRESULT rc = S_OK;
1604
1605 rc = setError (E_NOTIMPL, tr ("This feature is not implemented"));
1606
1607 return rc;
1608}
1609
1610
1611STDMETHODIMP Display::DrawToScreen (BYTE *address, ULONG x, ULONG y,
1612 ULONG width, ULONG height)
1613{
1614 /// @todo (r=dmik) this function may take too long to complete if the VM
1615 // is doing something like saving state right now. Which, in case if it
1616 // is called on the GUI thread, will make it unresponsive. We should
1617 // check the machine state here (by enclosing the check and VMRequCall
1618 // within the Console lock to make it atomic).
1619
1620 LogFlowFuncEnter();
1621 LogFlowFunc (("address=%p, x=%d, y=%d, width=%d, height=%d\n",
1622 (void *)address, x, y, width, height));
1623
1624 CheckComArgNotNull(address);
1625 CheckComArgExpr(width, width != 0);
1626 CheckComArgExpr(height, height != 0);
1627
1628 AutoCaller autoCaller(this);
1629 CheckComRCReturnRC(autoCaller.rc());
1630
1631 AutoWriteLock alock(this);
1632
1633 CHECK_CONSOLE_DRV (mpDrv);
1634
1635 Console::SafeVMPtr pVM (mParent);
1636 CheckComRCReturnRC(pVM.rc());
1637
1638 /*
1639 * Again we're lazy and make the graphics device do all the
1640 * dirty conversion work.
1641 */
1642 int rcVBox = VMR3ReqCallWait(pVM, VMCPUID_ANY, (PFNRT)mpDrv->pUpPort->pfnDisplayBlt, 6,
1643 mpDrv->pUpPort, address, x, y, width, height);
1644
1645 /*
1646 * If the function returns not supported, we'll have to do all the
1647 * work ourselves using the framebuffer.
1648 */
1649 HRESULT rc = S_OK;
1650 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
1651 {
1652 /** @todo implement generic fallback for screen blitting. */
1653 rc = E_NOTIMPL;
1654 }
1655 else if (RT_FAILURE(rcVBox))
1656 rc = setError (VBOX_E_IPRT_ERROR,
1657 tr ("Could not draw to the screen (%Rrc)"), rcVBox);
1658//@todo
1659// else
1660// {
1661// /* All ok. Redraw the screen. */
1662// handleDisplayUpdate (x, y, width, height);
1663// }
1664
1665 LogFlowFunc (("rc=%08X\n", rc));
1666 LogFlowFuncLeave();
1667 return rc;
1668}
1669
1670/**
1671 * Does a full invalidation of the VM display and instructs the VM
1672 * to update it immediately.
1673 *
1674 * @returns COM status code
1675 */
1676STDMETHODIMP Display::InvalidateAndUpdate()
1677{
1678 LogFlowFuncEnter();
1679
1680 AutoCaller autoCaller(this);
1681 CheckComRCReturnRC(autoCaller.rc());
1682
1683 AutoWriteLock alock(this);
1684
1685 CHECK_CONSOLE_DRV (mpDrv);
1686
1687 Console::SafeVMPtr pVM (mParent);
1688 CheckComRCReturnRC(pVM.rc());
1689
1690 HRESULT rc = S_OK;
1691
1692 LogFlowFunc (("Sending DPYUPDATE request\n"));
1693
1694 /* Have to leave the lock when calling EMT. */
1695 alock.leave ();
1696
1697 /* pdm.h says that this has to be called from the EMT thread */
1698 int rcVBox = VMR3ReqCallVoidWait(pVM, VMCPUID_ANY,
1699 (PFNRT)mpDrv->pUpPort->pfnUpdateDisplayAll, 1, mpDrv->pUpPort);
1700 alock.enter ();
1701
1702 if (RT_FAILURE(rcVBox))
1703 rc = setError (VBOX_E_IPRT_ERROR,
1704 tr ("Could not invalidate and update the screen (%Rrc)"), rcVBox);
1705
1706 LogFlowFunc (("rc=%08X\n", rc));
1707 LogFlowFuncLeave();
1708 return rc;
1709}
1710
1711/**
1712 * Notification that the framebuffer has completed the
1713 * asynchronous resize processing
1714 *
1715 * @returns COM status code
1716 */
1717STDMETHODIMP Display::ResizeCompleted(ULONG aScreenId)
1718{
1719 LogFlowFunc (("\n"));
1720
1721 /// @todo (dmik) can we AutoWriteLock alock(this); here?
1722 // do it when we switch this class to VirtualBoxBase_NEXT.
1723 // This will require general code review and may add some details.
1724 // In particular, we may want to check whether EMT is really waiting for
1725 // this notification, etc. It might be also good to obey the caller to make
1726 // sure this method is not called from more than one thread at a time
1727 // (and therefore don't use Display lock at all here to save some
1728 // milliseconds).
1729 AutoCaller autoCaller(this);
1730 CheckComRCReturnRC(autoCaller.rc());
1731
1732 /* this is only valid for external framebuffers */
1733 if (maFramebuffers[aScreenId].pFramebuffer == NULL)
1734 return setError (VBOX_E_NOT_SUPPORTED,
1735 tr ("Resize completed notification is valid only "
1736 "for external framebuffers"));
1737
1738 /* Set the flag indicating that the resize has completed and display
1739 * data need to be updated. */
1740 bool f = ASMAtomicCmpXchgU32 (&maFramebuffers[aScreenId].u32ResizeStatus,
1741 ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
1742 AssertRelease(f);NOREF(f);
1743
1744 return S_OK;
1745}
1746
1747/**
1748 * Notification that the framebuffer has completed the
1749 * asynchronous update processing
1750 *
1751 * @returns COM status code
1752 */
1753STDMETHODIMP Display::UpdateCompleted()
1754{
1755 LogFlowFunc (("\n"));
1756
1757 /// @todo (dmik) can we AutoWriteLock alock(this); here?
1758 // do it when we switch this class to VirtualBoxBase_NEXT.
1759 // Tthis will require general code review and may add some details.
1760 // In particular, we may want to check whether EMT is really waiting for
1761 // this notification, etc. It might be also good to obey the caller to make
1762 // sure this method is not called from more than one thread at a time
1763 // (and therefore don't use Display lock at all here to save some
1764 // milliseconds).
1765 AutoCaller autoCaller(this);
1766 CheckComRCReturnRC(autoCaller.rc());
1767
1768 /* this is only valid for external framebuffers */
1769 if (maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].pFramebuffer == NULL)
1770 return setError (VBOX_E_NOT_SUPPORTED,
1771 tr ("Resize completed notification is valid only "
1772 "for external framebuffers"));
1773
1774 return S_OK;
1775}
1776
1777STDMETHODIMP Display::CompleteVHWACommand(BYTE *pCommand)
1778{
1779#ifdef VBOX_WITH_VIDEOHWACCEL
1780 mpDrv->pVBVACallbacks->pfnVHWACommandCompleteAsynch(mpDrv->pVBVACallbacks, (PVBOXVHWACMD)pCommand);
1781 return S_OK;
1782#else
1783 return E_NOTIMPL;
1784#endif
1785}
1786
1787// private methods
1788/////////////////////////////////////////////////////////////////////////////
1789
1790/**
1791 * Helper to update the display information from the framebuffer.
1792 *
1793 * @param aCheckParams true to compare the parameters of the current framebuffer
1794 * and the new one and issue handleDisplayResize()
1795 * if they differ.
1796 * @thread EMT
1797 */
1798void Display::updateDisplayData (bool aCheckParams /* = false */)
1799{
1800 /* the driver might not have been constructed yet */
1801 if (!mpDrv)
1802 return;
1803
1804#if DEBUG
1805 /*
1806 * Sanity check. Note that this method may be called on EMT after Console
1807 * has started the power down procedure (but before our #drvDestruct() is
1808 * called, in which case pVM will aleady be NULL but mpDrv will not). Since
1809 * we don't really need pVM to proceed, we avoid this check in the release
1810 * build to save some ms (necessary to construct SafeVMPtrQuiet) in this
1811 * time-critical method.
1812 */
1813 Console::SafeVMPtrQuiet pVM (mParent);
1814 if (pVM.isOk())
1815 VM_ASSERT_EMT (pVM.raw());
1816#endif
1817
1818 /* The method is only relevant to the primary framebuffer. */
1819 IFramebuffer *pFramebuffer = maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN].pFramebuffer;
1820
1821 if (pFramebuffer)
1822 {
1823 HRESULT rc;
1824 BYTE *address = 0;
1825 rc = pFramebuffer->COMGETTER(Address) (&address);
1826 AssertComRC (rc);
1827 ULONG bytesPerLine = 0;
1828 rc = pFramebuffer->COMGETTER(BytesPerLine) (&bytesPerLine);
1829 AssertComRC (rc);
1830 ULONG bitsPerPixel = 0;
1831 rc = pFramebuffer->COMGETTER(BitsPerPixel) (&bitsPerPixel);
1832 AssertComRC (rc);
1833 ULONG width = 0;
1834 rc = pFramebuffer->COMGETTER(Width) (&width);
1835 AssertComRC (rc);
1836 ULONG height = 0;
1837 rc = pFramebuffer->COMGETTER(Height) (&height);
1838 AssertComRC (rc);
1839
1840 /*
1841 * Check current parameters with new ones and issue handleDisplayResize()
1842 * to let the new frame buffer adjust itself properly. Note that it will
1843 * result into a recursive updateDisplayData() call but with
1844 * aCheckOld = false.
1845 */
1846 if (aCheckParams &&
1847 (mLastAddress != address ||
1848 mLastBytesPerLine != bytesPerLine ||
1849 mLastBitsPerPixel != bitsPerPixel ||
1850 mLastWidth != (int) width ||
1851 mLastHeight != (int) height))
1852 {
1853 handleDisplayResize (VBOX_VIDEO_PRIMARY_SCREEN, mLastBitsPerPixel,
1854 mLastAddress,
1855 mLastBytesPerLine,
1856 mLastWidth,
1857 mLastHeight);
1858 return;
1859 }
1860
1861 mpDrv->Connector.pu8Data = (uint8_t *) address;
1862 mpDrv->Connector.cbScanline = bytesPerLine;
1863 mpDrv->Connector.cBits = bitsPerPixel;
1864 mpDrv->Connector.cx = width;
1865 mpDrv->Connector.cy = height;
1866 }
1867 else
1868 {
1869 /* black hole */
1870 mpDrv->Connector.pu8Data = NULL;
1871 mpDrv->Connector.cbScanline = 0;
1872 mpDrv->Connector.cBits = 0;
1873 mpDrv->Connector.cx = 0;
1874 mpDrv->Connector.cy = 0;
1875 }
1876}
1877
1878/**
1879 * Changes the current frame buffer. Called on EMT to avoid both
1880 * race conditions and excessive locking.
1881 *
1882 * @note locks this object for writing
1883 * @thread EMT
1884 */
1885/* static */
1886DECLCALLBACK(int) Display::changeFramebuffer (Display *that, IFramebuffer *aFB,
1887 unsigned uScreenId)
1888{
1889 LogFlowFunc (("uScreenId = %d\n", uScreenId));
1890
1891 AssertReturn(that, VERR_INVALID_PARAMETER);
1892 AssertReturn(uScreenId < that->mcMonitors, VERR_INVALID_PARAMETER);
1893
1894 AutoCaller autoCaller(that);
1895 CheckComRCReturnRC(autoCaller.rc());
1896
1897 AutoWriteLock alock(that);
1898
1899 DISPLAYFBINFO *pDisplayFBInfo = &that->maFramebuffers[uScreenId];
1900 pDisplayFBInfo->pFramebuffer = aFB;
1901
1902 that->mParent->consoleVRDPServer()->SendResize ();
1903
1904 that->updateDisplayData (true /* aCheckParams */);
1905
1906 return VINF_SUCCESS;
1907}
1908
1909/**
1910 * Handle display resize event issued by the VGA device for the primary screen.
1911 *
1912 * @see PDMIDISPLAYCONNECTOR::pfnResize
1913 */
1914DECLCALLBACK(int) Display::displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
1915 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
1916{
1917 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1918
1919 LogFlowFunc (("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
1920 bpp, pvVRAM, cbLine, cx, cy));
1921
1922 return pDrv->pDisplay->handleDisplayResize(VBOX_VIDEO_PRIMARY_SCREEN, bpp, pvVRAM, cbLine, cx, cy);
1923}
1924
1925/**
1926 * Handle display update.
1927 *
1928 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
1929 */
1930DECLCALLBACK(void) Display::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
1931 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
1932{
1933 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1934
1935#ifdef DEBUG_sunlover
1936 LogFlowFunc (("mfVideoAccelEnabled = %d, %d,%d %dx%d\n",
1937 pDrv->pDisplay->mfVideoAccelEnabled, x, y, cx, cy));
1938#endif /* DEBUG_sunlover */
1939
1940 /* This call does update regardless of VBVA status.
1941 * But in VBVA mode this is called only as result of
1942 * pfnUpdateDisplayAll in the VGA device.
1943 */
1944
1945 pDrv->pDisplay->handleDisplayUpdate(x, y, cx, cy);
1946}
1947
1948/**
1949 * Periodic display refresh callback.
1950 *
1951 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
1952 */
1953DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
1954{
1955 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1956
1957#ifdef DEBUG_sunlover
1958 STAM_PROFILE_START(&StatDisplayRefresh, a);
1959#endif /* DEBUG_sunlover */
1960
1961#ifdef DEBUG_sunlover_2
1962 LogFlowFunc (("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
1963 pDrv->pDisplay->mfVideoAccelEnabled));
1964#endif /* DEBUG_sunlover_2 */
1965
1966 Display *pDisplay = pDrv->pDisplay;
1967 bool fNoUpdate = false; /* Do not update the display if any of the framebuffers is being resized. */
1968 unsigned uScreenId;
1969
1970 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
1971 {
1972 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
1973
1974 /* Check the resize status. The status can be checked normally because
1975 * the status affects only the EMT.
1976 */
1977 uint32_t u32ResizeStatus = pFBInfo->u32ResizeStatus;
1978
1979 if (u32ResizeStatus == ResizeStatus_UpdateDisplayData)
1980 {
1981 LogFlowFunc (("ResizeStatus_UpdateDisplayData %d\n", uScreenId));
1982 fNoUpdate = true; /* Always set it here, because pfnUpdateDisplayAll can cause a new resize. */
1983 /* The framebuffer was resized and display data need to be updated. */
1984 pDisplay->handleResizeCompletedEMT ();
1985 if (pFBInfo->u32ResizeStatus != ResizeStatus_Void)
1986 {
1987 /* The resize status could be not Void here because a pending resize is issued. */
1988 continue;
1989 }
1990 /* Continue with normal processing because the status here is ResizeStatus_Void. */
1991 if (uScreenId == VBOX_VIDEO_PRIMARY_SCREEN)
1992 {
1993 /* Repaint the display because VM continued to run during the framebuffer resize. */
1994 if (!pFBInfo->pFramebuffer.isNull())
1995 pDrv->pUpPort->pfnUpdateDisplayAll(pDrv->pUpPort);
1996 }
1997 }
1998 else if (u32ResizeStatus == ResizeStatus_InProgress)
1999 {
2000 /* The framebuffer is being resized. Do not call the VGA device back. Immediately return. */
2001 LogFlowFunc (("ResizeStatus_InProcess\n"));
2002 fNoUpdate = true;
2003 continue;
2004 }
2005 }
2006
2007 if (!fNoUpdate)
2008 {
2009 if (pDisplay->mfPendingVideoAccelEnable)
2010 {
2011 /* Acceleration was enabled while machine was not yet running
2012 * due to restoring from saved state. Update entire display and
2013 * actually enable acceleration.
2014 */
2015 Assert(pDisplay->mpPendingVbvaMemory);
2016
2017 /* Acceleration can not be yet enabled.*/
2018 Assert(pDisplay->mpVbvaMemory == NULL);
2019 Assert(!pDisplay->mfVideoAccelEnabled);
2020
2021 if (pDisplay->mfMachineRunning)
2022 {
2023 pDisplay->VideoAccelEnable (pDisplay->mfPendingVideoAccelEnable,
2024 pDisplay->mpPendingVbvaMemory);
2025
2026 /* Reset the pending state. */
2027 pDisplay->mfPendingVideoAccelEnable = false;
2028 pDisplay->mpPendingVbvaMemory = NULL;
2029 }
2030 }
2031 else
2032 {
2033 Assert(pDisplay->mpPendingVbvaMemory == NULL);
2034
2035 if (pDisplay->mfVideoAccelEnabled)
2036 {
2037 Assert(pDisplay->mpVbvaMemory);
2038 pDisplay->VideoAccelFlush ();
2039 }
2040 else
2041 {
2042 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[VBOX_VIDEO_PRIMARY_SCREEN];
2043 if (!pFBInfo->pFramebuffer.isNull())
2044 {
2045 Assert(pDrv->Connector.pu8Data);
2046 Assert(pFBInfo->u32ResizeStatus == ResizeStatus_Void);
2047 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
2048 }
2049 }
2050
2051 /* Inform the VRDP server that the current display update sequence is
2052 * completed. At this moment the framebuffer memory contains a definite
2053 * image, that is synchronized with the orders already sent to VRDP client.
2054 * The server can now process redraw requests from clients or initial
2055 * fullscreen updates for new clients.
2056 */
2057 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)
2058 {
2059 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];
2060
2061 if (!pFBInfo->pFramebuffer.isNull() && pFBInfo->u32ResizeStatus == ResizeStatus_Void)
2062 {
2063 Assert (pDisplay->mParent && pDisplay->mParent->consoleVRDPServer());
2064 pDisplay->mParent->consoleVRDPServer()->SendUpdate (uScreenId, NULL, 0);
2065 }
2066 }
2067 }
2068 }
2069
2070#ifdef DEBUG_sunlover
2071 STAM_PROFILE_STOP(&StatDisplayRefresh, a);
2072#endif /* DEBUG_sunlover */
2073#ifdef DEBUG_sunlover_2
2074 LogFlowFunc (("leave\n"));
2075#endif /* DEBUG_sunlover_2 */
2076}
2077
2078/**
2079 * Reset notification
2080 *
2081 * @see PDMIDISPLAYCONNECTOR::pfnReset
2082 */
2083DECLCALLBACK(void) Display::displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
2084{
2085 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2086
2087 LogFlowFunc (("\n"));
2088
2089 /* Disable VBVA mode. */
2090 pDrv->pDisplay->VideoAccelEnable (false, NULL);
2091}
2092
2093/**
2094 * LFBModeChange notification
2095 *
2096 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
2097 */
2098DECLCALLBACK(void) Display::displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
2099{
2100 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2101
2102 LogFlowFunc (("fEnabled=%d\n", fEnabled));
2103
2104 NOREF(fEnabled);
2105
2106 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
2107 pDrv->pDisplay->VideoAccelEnable (false, NULL);
2108}
2109
2110/**
2111 * Adapter information change notification.
2112 *
2113 * @see PDMIDISPLAYCONNECTOR::pfnProcessAdapterData
2114 */
2115DECLCALLBACK(void) Display::displayProcessAdapterDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, uint32_t u32VRAMSize)
2116{
2117 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2118
2119 if (pvVRAM == NULL)
2120 {
2121 unsigned i;
2122 for (i = 0; i < pDrv->pDisplay->mcMonitors; i++)
2123 {
2124 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[i];
2125
2126 pFBInfo->u32Offset = 0;
2127 pFBInfo->u32MaxFramebufferSize = 0;
2128 pFBInfo->u32InformationSize = 0;
2129 }
2130 }
2131#ifndef VBOX_WITH_HGSMI
2132 else
2133 {
2134 uint8_t *pu8 = (uint8_t *)pvVRAM;
2135 pu8 += u32VRAMSize - VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
2136
2137 // @todo
2138 uint8_t *pu8End = pu8 + VBOX_VIDEO_ADAPTER_INFORMATION_SIZE;
2139
2140 VBOXVIDEOINFOHDR *pHdr;
2141
2142 for (;;)
2143 {
2144 pHdr = (VBOXVIDEOINFOHDR *)pu8;
2145 pu8 += sizeof (VBOXVIDEOINFOHDR);
2146
2147 if (pu8 >= pu8End)
2148 {
2149 LogRel(("VBoxVideo: Guest adapter information overflow!!!\n"));
2150 break;
2151 }
2152
2153 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_DISPLAY)
2154 {
2155 if (pHdr->u16Length != sizeof (VBOXVIDEOINFODISPLAY))
2156 {
2157 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "DISPLAY", pHdr->u16Length));
2158 break;
2159 }
2160
2161 VBOXVIDEOINFODISPLAY *pDisplay = (VBOXVIDEOINFODISPLAY *)pu8;
2162
2163 if (pDisplay->u32Index >= pDrv->pDisplay->mcMonitors)
2164 {
2165 LogRel(("VBoxVideo: Guest adapter information invalid display index %d!!!\n", pDisplay->u32Index));
2166 break;
2167 }
2168
2169 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[pDisplay->u32Index];
2170
2171 pFBInfo->u32Offset = pDisplay->u32Offset;
2172 pFBInfo->u32MaxFramebufferSize = pDisplay->u32FramebufferSize;
2173 pFBInfo->u32InformationSize = pDisplay->u32InformationSize;
2174
2175 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));
2176 }
2177 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_QUERY_CONF32)
2178 {
2179 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOQUERYCONF32))
2180 {
2181 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "CONF32", pHdr->u16Length));
2182 break;
2183 }
2184
2185 VBOXVIDEOINFOQUERYCONF32 *pConf32 = (VBOXVIDEOINFOQUERYCONF32 *)pu8;
2186
2187 switch (pConf32->u32Index)
2188 {
2189 case VBOX_VIDEO_QCI32_MONITOR_COUNT:
2190 {
2191 pConf32->u32Value = pDrv->pDisplay->mcMonitors;
2192 } break;
2193
2194 case VBOX_VIDEO_QCI32_OFFSCREEN_HEAP_SIZE:
2195 {
2196 /* @todo make configurable. */
2197 pConf32->u32Value = _1M;
2198 } break;
2199
2200 default:
2201 LogRel(("VBoxVideo: CONF32 %d not supported!!! Skipping.\n", pConf32->u32Index));
2202 }
2203 }
2204 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
2205 {
2206 if (pHdr->u16Length != 0)
2207 {
2208 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
2209 break;
2210 }
2211
2212 break;
2213 }
2214 else if (pHdr->u8Type != VBOX_VIDEO_INFO_TYPE_NV_HEAP) /** @todo why is Additions/WINNT/Graphics/Miniport/VBoxVideo.cpp pushing this to us? */
2215 {
2216 LogRel(("Guest adapter information contains unsupported type %d. The block has been skipped.\n", pHdr->u8Type));
2217 }
2218
2219 pu8 += pHdr->u16Length;
2220 }
2221 }
2222#endif /* !VBOX_WITH_HGSMI */
2223}
2224
2225/**
2226 * Display information change notification.
2227 *
2228 * @see PDMIDISPLAYCONNECTOR::pfnProcessDisplayData
2229 */
2230DECLCALLBACK(void) Display::displayProcessDisplayDataCallback(PPDMIDISPLAYCONNECTOR pInterface, void *pvVRAM, unsigned uScreenId)
2231{
2232 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2233
2234 if (uScreenId >= pDrv->pDisplay->mcMonitors)
2235 {
2236 LogRel(("VBoxVideo: Guest display information invalid display index %d!!!\n", uScreenId));
2237 return;
2238 }
2239
2240 /* Get the display information structure. */
2241 DISPLAYFBINFO *pFBInfo = &pDrv->pDisplay->maFramebuffers[uScreenId];
2242
2243 uint8_t *pu8 = (uint8_t *)pvVRAM;
2244 pu8 += pFBInfo->u32Offset + pFBInfo->u32MaxFramebufferSize;
2245
2246 // @todo
2247 uint8_t *pu8End = pu8 + pFBInfo->u32InformationSize;
2248
2249 VBOXVIDEOINFOHDR *pHdr;
2250
2251 for (;;)
2252 {
2253 pHdr = (VBOXVIDEOINFOHDR *)pu8;
2254 pu8 += sizeof (VBOXVIDEOINFOHDR);
2255
2256 if (pu8 >= pu8End)
2257 {
2258 LogRel(("VBoxVideo: Guest display information overflow!!!\n"));
2259 break;
2260 }
2261
2262 if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_SCREEN)
2263 {
2264 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOSCREEN))
2265 {
2266 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "SCREEN", pHdr->u16Length));
2267 break;
2268 }
2269
2270 VBOXVIDEOINFOSCREEN *pScreen = (VBOXVIDEOINFOSCREEN *)pu8;
2271
2272 pFBInfo->xOrigin = pScreen->xOrigin;
2273 pFBInfo->yOrigin = pScreen->yOrigin;
2274
2275 pFBInfo->w = pScreen->u16Width;
2276 pFBInfo->h = pScreen->u16Height;
2277
2278 LogFlow(("VBOX_VIDEO_INFO_TYPE_SCREEN: (%p) %d: at %d,%d, linesize 0x%X, size %dx%d, bpp %d, flags 0x%02X\n",
2279 pHdr, uScreenId, pScreen->xOrigin, pScreen->yOrigin, pScreen->u32LineSize, pScreen->u16Width, pScreen->u16Height, pScreen->bitsPerPixel, pScreen->u8Flags));
2280
2281 if (uScreenId != VBOX_VIDEO_PRIMARY_SCREEN)
2282 {
2283 /* Primary screen resize is initiated by the VGA device. */
2284 pDrv->pDisplay->handleDisplayResize(uScreenId, pScreen->bitsPerPixel, (uint8_t *)pvVRAM + pFBInfo->u32Offset, pScreen->u32LineSize, pScreen->u16Width, pScreen->u16Height);
2285 }
2286 }
2287 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_END)
2288 {
2289 if (pHdr->u16Length != 0)
2290 {
2291 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "END", pHdr->u16Length));
2292 break;
2293 }
2294
2295 break;
2296 }
2297 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_HOST_EVENTS)
2298 {
2299 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOHOSTEVENTS))
2300 {
2301 LogRel(("VBoxVideo: Guest display information %s invalid length %d!!!\n", "HOST_EVENTS", pHdr->u16Length));
2302 break;
2303 }
2304
2305 VBOXVIDEOINFOHOSTEVENTS *pHostEvents = (VBOXVIDEOINFOHOSTEVENTS *)pu8;
2306
2307 pFBInfo->pHostEvents = pHostEvents;
2308
2309 LogFlow(("VBOX_VIDEO_INFO_TYPE_HOSTEVENTS: (%p)\n",
2310 pHostEvents));
2311 }
2312 else if (pHdr->u8Type == VBOX_VIDEO_INFO_TYPE_LINK)
2313 {
2314 if (pHdr->u16Length != sizeof (VBOXVIDEOINFOLINK))
2315 {
2316 LogRel(("VBoxVideo: Guest adapter information %s invalid length %d!!!\n", "LINK", pHdr->u16Length));
2317 break;
2318 }
2319
2320 VBOXVIDEOINFOLINK *pLink = (VBOXVIDEOINFOLINK *)pu8;
2321 pu8 += pLink->i32Offset;
2322 }
2323 else
2324 {
2325 LogRel(("Guest display information contains unsupported type %d\n", pHdr->u8Type));
2326 }
2327
2328 pu8 += pHdr->u16Length;
2329 }
2330}
2331
2332#ifdef VBOX_WITH_VIDEOHWACCEL
2333
2334void Display::handleVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
2335{
2336 unsigned id = (unsigned)pCommand->iDisplay;
2337 int rc = VINF_SUCCESS;
2338 if(id < mcMonitors)
2339 {
2340 IFramebuffer *pFramebuffer = maFramebuffers[id].pFramebuffer;
2341
2342 // if there is no framebuffer, this call is not interesting
2343 if (pFramebuffer == NULL)
2344 return;
2345
2346 pFramebuffer->Lock();
2347
2348 HRESULT hr = pFramebuffer->ProcessVHWACommand((BYTE*)pCommand);
2349 if(FAILED(hr))
2350 {
2351 rc = (hr == E_NOTIMPL) ? VERR_NOT_IMPLEMENTED : VERR_GENERAL_FAILURE;
2352 }
2353
2354 pFramebuffer->Unlock();
2355
2356 }
2357 else
2358 {
2359 rc = VERR_INVALID_PARAMETER;
2360 }
2361
2362 if(RT_FAILURE(rc))
2363 {
2364 /* tell the guest the command is complete */
2365 pCommand->Flags &= (~VBOXVHWACMD_FLAG_HG_ASYNCH);
2366 pCommand->rc = rc;
2367 }
2368}
2369
2370DECLCALLBACK(void) Display::displayVHWACommandProcess(PPDMIDISPLAYCONNECTOR pInterface, PVBOXVHWACMD pCommand)
2371{
2372 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2373
2374 pDrv->pDisplay->handleVHWACommandProcess(pInterface, pCommand);
2375}
2376#endif
2377
2378#ifdef VBOX_WITH_HGSMI
2379DECLCALLBACK(int) Display::displayVBVAEnable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
2380{
2381 LogFlowFunc(("uScreenId %d\n", uScreenId));
2382
2383 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2384 Display *pThis = pDrv->pDisplay;
2385
2386 pThis->maFramebuffers[uScreenId].fVBVAEnabled = true;
2387
2388 return VINF_SUCCESS;
2389}
2390
2391DECLCALLBACK(void) Display::displayVBVADisable(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
2392{
2393 LogFlowFunc(("uScreenId %d\n", uScreenId));
2394
2395 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2396 Display *pThis = pDrv->pDisplay;
2397
2398 pThis->maFramebuffers[uScreenId].fVBVAEnabled = false;
2399}
2400
2401DECLCALLBACK(void) Display::displayVBVAUpdateBegin(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId)
2402{
2403 LogFlowFunc(("uScreenId %d\n", uScreenId));
2404
2405 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2406 Display *pThis = pDrv->pDisplay;
2407 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
2408
2409 if (RT_LIKELY(pFBInfo->u32ResizeStatus == ResizeStatus_Void))
2410 {
2411 if (RT_UNLIKELY(pFBInfo->cVBVASkipUpdate != 0))
2412 {
2413 /* Some updates were skipped. Note: displayVBVAUpdate* callbacks are called
2414 * under display device lock, so thread safe.
2415 */
2416 pFBInfo->cVBVASkipUpdate = 0;
2417 pThis->handleDisplayUpdate(pFBInfo->vbvaSkippedRect.xLeft,
2418 pFBInfo->vbvaSkippedRect.yTop,
2419 pFBInfo->vbvaSkippedRect.xRight - pFBInfo->vbvaSkippedRect.xLeft,
2420 pFBInfo->vbvaSkippedRect.yBottom - pFBInfo->vbvaSkippedRect.yTop);
2421 }
2422 }
2423 else
2424 {
2425 /* The framebuffer is being resized. */
2426 pFBInfo->cVBVASkipUpdate++;
2427 }
2428}
2429
2430DECLCALLBACK(void) Display::displayVBVAUpdateProcess(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, const PVBVACMDHDR pCmd, size_t cbCmd)
2431{
2432 LogFlowFunc(("uScreenId %d pCmd %p cbCmd %d\n", uScreenId, pCmd, cbCmd));
2433
2434 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2435 Display *pThis = pDrv->pDisplay;
2436 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
2437
2438 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
2439 {
2440 pThis->mParent->consoleVRDPServer()->SendUpdate (uScreenId, pCmd, cbCmd);
2441 }
2442}
2443
2444DECLCALLBACK(void) Display::displayVBVAUpdateEnd(PPDMIDISPLAYCONNECTOR pInterface, unsigned uScreenId, int32_t x, int32_t y, uint32_t cx, uint32_t cy)
2445{
2446 LogFlowFunc(("uScreenId %d %d,%d %dx%d\n", uScreenId, x, y, cx, cy));
2447
2448 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2449 Display *pThis = pDrv->pDisplay;
2450 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[uScreenId];
2451
2452 /* @todo handleFramebufferUpdate (uScreenId,
2453 * x - pThis->maFramebuffers[uScreenId].xOrigin,
2454 * y - pThis->maFramebuffers[uScreenId].yOrigin,
2455 * cx, cy);
2456 */
2457 if (RT_LIKELY(pFBInfo->cVBVASkipUpdate == 0))
2458 {
2459 pThis->handleDisplayUpdate(x, y, cx, cy);
2460 }
2461 else
2462 {
2463 /* Save the updated rectangle. */
2464 int32_t xRight = x + cx;
2465 int32_t yBottom = y + cy;
2466
2467 if (pFBInfo->cVBVASkipUpdate == 1)
2468 {
2469 pFBInfo->vbvaSkippedRect.xLeft = x;
2470 pFBInfo->vbvaSkippedRect.yTop = y;
2471 pFBInfo->vbvaSkippedRect.xRight = xRight;
2472 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
2473 }
2474 else
2475 {
2476 if (pFBInfo->vbvaSkippedRect.xLeft > x)
2477 {
2478 pFBInfo->vbvaSkippedRect.xLeft = x;
2479 }
2480 if (pFBInfo->vbvaSkippedRect.yTop > y)
2481 {
2482 pFBInfo->vbvaSkippedRect.yTop = y;
2483 }
2484 if (pFBInfo->vbvaSkippedRect.xRight < xRight)
2485 {
2486 pFBInfo->vbvaSkippedRect.xRight = xRight;
2487 }
2488 if (pFBInfo->vbvaSkippedRect.yBottom < yBottom)
2489 {
2490 pFBInfo->vbvaSkippedRect.yBottom = yBottom;
2491 }
2492 }
2493 }
2494}
2495
2496DECLCALLBACK(int) Display::displayVBVAResize(PPDMIDISPLAYCONNECTOR pInterface, const PVBVAINFOVIEW pView, const PVBVAINFOSCREEN pScreen, void *pvVRAM)
2497{
2498 LogFlowFunc(("pScreen %p, pvVRAM %p\n", pScreen, pvVRAM));
2499
2500 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2501 Display *pThis = pDrv->pDisplay;
2502
2503 DISPLAYFBINFO *pFBInfo = &pThis->maFramebuffers[pScreen->u32ViewIndex];
2504
2505 pFBInfo->u32Offset = pView->u32ViewOffset; /* Not used in HGSMI. */
2506 pFBInfo->u32MaxFramebufferSize = pView->u32MaxScreenSize; /* Not used in HGSMI. */
2507 pFBInfo->u32InformationSize = 0; /* Not used in HGSMI. */
2508
2509 pFBInfo->xOrigin = pScreen->i32OriginX;
2510 pFBInfo->yOrigin = pScreen->i32OriginY;
2511
2512 pFBInfo->w = pScreen->u32Width;
2513 pFBInfo->h = pScreen->u32Height;
2514
2515 return pThis->handleDisplayResize(pScreen->u32ViewIndex, pScreen->u16BitsPerPixel,
2516 (uint8_t *)pvVRAM + pScreen->u32StartOffset,
2517 pScreen->u32LineSize, pScreen->u32Width, pScreen->u32Height);
2518}
2519
2520DECLCALLBACK(int) Display::displayVBVAMousePointerShape(PPDMIDISPLAYCONNECTOR pInterface, bool fVisible, bool fAlpha,
2521 uint32_t xHot, uint32_t yHot,
2522 uint32_t cx, uint32_t cy,
2523 const void *pvShape)
2524{
2525 LogFlowFunc(("\n"));
2526
2527 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
2528 Display *pThis = pDrv->pDisplay;
2529
2530 /* Tell the console about it */
2531 pDrv->pDisplay->mParent->onMousePointerShapeChange(fVisible, fAlpha,
2532 xHot, yHot, cx, cy, (void *)pvShape);
2533
2534 return VINF_SUCCESS;
2535}
2536#endif /* VBOX_WITH_HGSMI */
2537
2538/**
2539 * Queries an interface to the driver.
2540 *
2541 * @returns Pointer to interface.
2542 * @returns NULL if the interface was not supported by the driver.
2543 * @param pInterface Pointer to this interface structure.
2544 * @param enmInterface The requested interface identification.
2545 */
2546DECLCALLBACK(void *) Display::drvQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
2547{
2548 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2549 PDRVMAINDISPLAY pDrv = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
2550 switch (enmInterface)
2551 {
2552 case PDMINTERFACE_BASE:
2553 return &pDrvIns->IBase;
2554 case PDMINTERFACE_DISPLAY_CONNECTOR:
2555 return &pDrv->Connector;
2556 default:
2557 return NULL;
2558 }
2559}
2560
2561
2562/**
2563 * Destruct a display driver instance.
2564 *
2565 * @returns VBox status.
2566 * @param pDrvIns The driver instance data.
2567 */
2568DECLCALLBACK(void) Display::drvDestruct(PPDMDRVINS pDrvIns)
2569{
2570 PDRVMAINDISPLAY pData = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
2571 LogFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
2572 if (pData->pDisplay)
2573 {
2574 AutoWriteLock displayLock (pData->pDisplay);
2575 pData->pDisplay->mpDrv = NULL;
2576 pData->pDisplay->mpVMMDev = NULL;
2577 pData->pDisplay->mLastAddress = NULL;
2578 pData->pDisplay->mLastBytesPerLine = 0;
2579 pData->pDisplay->mLastBitsPerPixel = 0,
2580 pData->pDisplay->mLastWidth = 0;
2581 pData->pDisplay->mLastHeight = 0;
2582 }
2583}
2584
2585
2586/**
2587 * Construct a display driver instance.
2588 *
2589 * @copydoc FNPDMDRVCONSTRUCT
2590 */
2591DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
2592{
2593 PDRVMAINDISPLAY pData = PDMINS_2_DATA(pDrvIns, PDRVMAINDISPLAY);
2594 LogFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
2595
2596 /*
2597 * Validate configuration.
2598 */
2599 if (!CFGMR3AreValuesValid(pCfgHandle, "Object\0"))
2600 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
2601 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
2602 ("Configuration error: Not possible to attach anything to this driver!\n"),
2603 VERR_PDM_DRVINS_NO_ATTACH);
2604
2605 /*
2606 * Init Interfaces.
2607 */
2608 pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
2609
2610 pData->Connector.pfnResize = Display::displayResizeCallback;
2611 pData->Connector.pfnUpdateRect = Display::displayUpdateCallback;
2612 pData->Connector.pfnRefresh = Display::displayRefreshCallback;
2613 pData->Connector.pfnReset = Display::displayResetCallback;
2614 pData->Connector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
2615 pData->Connector.pfnProcessAdapterData = Display::displayProcessAdapterDataCallback;
2616 pData->Connector.pfnProcessDisplayData = Display::displayProcessDisplayDataCallback;
2617#ifdef VBOX_WITH_VIDEOHWACCEL
2618 pData->Connector.pfnVHWACommandProcess = Display::displayVHWACommandProcess;
2619#endif
2620#ifdef VBOX_WITH_HGSMI
2621 pData->Connector.pfnVBVAEnable = Display::displayVBVAEnable;
2622 pData->Connector.pfnVBVADisable = Display::displayVBVADisable;
2623 pData->Connector.pfnVBVAUpdateBegin = Display::displayVBVAUpdateBegin;
2624 pData->Connector.pfnVBVAUpdateProcess = Display::displayVBVAUpdateProcess;
2625 pData->Connector.pfnVBVAUpdateEnd = Display::displayVBVAUpdateEnd;
2626 pData->Connector.pfnVBVAResize = Display::displayVBVAResize;
2627 pData->Connector.pfnVBVAMousePointerShape = Display::displayVBVAMousePointerShape;
2628#endif
2629
2630
2631 /*
2632 * Get the IDisplayPort interface of the above driver/device.
2633 */
2634 pData->pUpPort = (PPDMIDISPLAYPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_DISPLAY_PORT);
2635 if (!pData->pUpPort)
2636 {
2637 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
2638 return VERR_PDM_MISSING_INTERFACE_ABOVE;
2639 }
2640#if defined(VBOX_WITH_VIDEOHWACCEL)
2641 pData->pVBVACallbacks = (PPDMDDISPLAYVBVACALLBACKS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_DISPLAY_VBVA_CALLBACKS);
2642 if (!pData->pVBVACallbacks)
2643 {
2644 AssertMsgFailed(("Configuration error: No VBVA callback interface above!\n"));
2645 return VERR_PDM_MISSING_INTERFACE_ABOVE;
2646 }
2647#endif
2648 /*
2649 * Get the Display object pointer and update the mpDrv member.
2650 */
2651 void *pv;
2652 int rc = CFGMR3QueryPtr(pCfgHandle, "Object", &pv);
2653 if (RT_FAILURE(rc))
2654 {
2655 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
2656 return rc;
2657 }
2658 pData->pDisplay = (Display *)pv; /** @todo Check this cast! */
2659 pData->pDisplay->mpDrv = pData;
2660
2661 /*
2662 * Update our display information according to the framebuffer
2663 */
2664 pData->pDisplay->updateDisplayData();
2665
2666 /*
2667 * Start periodic screen refreshes
2668 */
2669 pData->pUpPort->pfnSetRefreshRate(pData->pUpPort, 20);
2670
2671 return VINF_SUCCESS;
2672}
2673
2674
2675/**
2676 * Display driver registration record.
2677 */
2678const PDMDRVREG Display::DrvReg =
2679{
2680 /* u32Version */
2681 PDM_DRVREG_VERSION,
2682 /* szDriverName */
2683 "MainDisplay",
2684 /* pszDescription */
2685 "Main display driver (Main as in the API).",
2686 /* fFlags */
2687 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2688 /* fClass. */
2689 PDM_DRVREG_CLASS_DISPLAY,
2690 /* cMaxInstances */
2691 ~0,
2692 /* cbInstance */
2693 sizeof(DRVMAINDISPLAY),
2694 /* pfnConstruct */
2695 Display::drvConstruct,
2696 /* pfnDestruct */
2697 Display::drvDestruct,
2698 /* pfnIOCtl */
2699 NULL,
2700 /* pfnPowerOn */
2701 NULL,
2702 /* pfnReset */
2703 NULL,
2704 /* pfnSuspend */
2705 NULL,
2706 /* pfnResume */
2707 NULL,
2708 /* pfnAttach */
2709 NULL,
2710 /* pfnDetach */
2711 NULL,
2712 /* pfnPowerOff */
2713 NULL,
2714 /* pfnSoftReset */
2715 NULL,
2716 /* u32EndVersion */
2717 PDM_DRVREG_VERSION
2718};
2719/* 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