VirtualBox

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

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

displayRefreshCallback: removed obsolete assertion (xTracker #3489).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use