VirtualBox

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

Last change on this file since 24265 was 24265, checked in by vboxsync, 16 years ago

Devices,VMM: Replaced all VERR_SSM_LOAD_CONFIG_MISMATCH returns with SSMR3SetCfgError calls.

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

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