VirtualBox

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

Last change on this file since 2922 was 2798, checked in by vboxsync, 18 years ago

Framebuffer, which does not use the guest VRAM directly, must be updated before the update is passed to VRDP server.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 59.1 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22#include "DisplayImpl.h"
23#include "FramebufferImpl.h"
24#include "ConsoleImpl.h"
25#include "ConsoleVRDPServer.h"
26#include "VMMDev.h"
27
28#include "Logging.h"
29
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32#include <iprt/asm.h>
33
34#include <VBox/pdm.h>
35#include <VBox/cfgm.h>
36#include <VBox/err.h>
37#include <VBox/vm.h>
38
39/**
40 * Display driver instance data.
41 */
42typedef struct DRVMAINDISPLAY
43{
44 /** Pointer to the display object. */
45 Display *pDisplay;
46 /** Pointer to the driver instance structure. */
47 PPDMDRVINS pDrvIns;
48 /** Pointer to the keyboard port interface of the driver/device above us. */
49 PPDMIDISPLAYPORT pUpPort;
50 /** Our display connector interface. */
51 PDMIDISPLAYCONNECTOR Connector;
52} DRVMAINDISPLAY, *PDRVMAINDISPLAY;
53
54/** Converts PDMIDISPLAYCONNECTOR pointer to a DRVMAINDISPLAY pointer. */
55#define PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface) ( (PDRVMAINDISPLAY) ((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINDISPLAY, Connector)) )
56
57#ifdef DEBUG_sunlover
58static STAMPROFILE StatDisplayRefresh;
59static int stam = 0;
60#endif /* DEBUG_sunlover */
61
62// constructor / destructor
63/////////////////////////////////////////////////////////////////////////////
64
65HRESULT Display::FinalConstruct()
66{
67 mpVbvaMemory = NULL;
68 mfVideoAccelEnabled = false;
69 mfVideoAccelVRDP = false;
70 mfu32SupportedOrders = 0;
71 mcVideoAccelVRDPRefs = 0;
72
73 mpPendingVbvaMemory = NULL;
74 mfPendingVideoAccelEnable = false;
75
76 mfMachineRunning = false;
77
78 mpu8VbvaPartial = NULL;
79 mcbVbvaPartial = 0;
80
81 mParent = NULL;
82 mpDrv = NULL;
83 mpVMMDev = NULL;
84 mfVMMDevInited = false;
85 RTSemEventMultiCreate(&mUpdateSem);
86
87 mLastAddress = NULL;
88 mLastLineSize = 0;
89 mLastColorDepth = 0,
90 mLastWidth = 0;
91 mLastHeight = 0;
92
93 mu32ResizeStatus = ResizeStatus_Void;
94
95 return S_OK;
96}
97
98void Display::FinalRelease()
99{
100 if (isReady())
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 *parent)
115{
116 LogFlowFunc (("isReady=%d", isReady()));
117
118 ComAssertRet (parent, E_INVALIDARG);
119
120 AutoLock alock (this);
121 ComAssertRet (!isReady(), E_UNEXPECTED);
122
123 mParent = parent;
124
125 /* reset the event sems */
126 RTSemEventMultiReset(mUpdateSem);
127
128 // by default, we have an internal framebuffer which is
129 // NULL, i.e. a black hole for no display output
130 mFramebuffer = 0;
131 mInternalFramebuffer = true;
132 mFramebufferOpened = false;
133 mSupportedAccelOps = 0;
134
135 mParent->RegisterCallback(this);
136
137 setReady (true);
138 return S_OK;
139}
140
141/**
142 * Uninitializes the instance and sets the ready flag to FALSE.
143 * Called either from FinalRelease() or by the parent when it gets destroyed.
144 */
145void Display::uninit()
146{
147 LogFlowFunc (("isReady=%d\n", isReady()));
148
149 AutoLock alock (this);
150 AssertReturn (isReady(), (void) 0);
151
152 mFramebuffer.setNull();
153 RTSemEventMultiDestroy(mUpdateSem);
154
155 if (mParent)
156 {
157 mParent->UnregisterCallback(this);
158 }
159
160 if (mpDrv)
161 mpDrv->pDisplay = NULL;
162 mpDrv = NULL;
163 mpVMMDev = NULL;
164 mfVMMDevInited = true;
165
166 setReady (false);
167}
168
169// IConsoleCallback method
170STDMETHODIMP Display::OnStateChange(MachineState_T machineState)
171{
172 if (machineState == MachineState_Running)
173 {
174 LogFlowFunc (("Machine running\n"));
175
176 mfMachineRunning = true;
177 }
178 else
179 {
180 mfMachineRunning = false;
181 }
182 return S_OK;
183}
184
185// public methods only for internal purposes
186/////////////////////////////////////////////////////////////////////////////
187
188/**
189 * @thread EMT
190 */
191static int callFramebufferResize (IFramebuffer *pFramebuffer, FramebufferPixelFormat_T pixelFormat, void *pvVRAM, uint32_t cbLine, int w, int h)
192{
193 Assert (pFramebuffer);
194
195 /* Call the framebuffer to try and set required pixelFormat. */
196 BOOL finished = TRUE;
197
198 pFramebuffer->RequestResize (pixelFormat, (BYTE *) pvVRAM, cbLine, w, h, &finished);
199
200 if (!finished)
201 {
202 LogFlowFunc (("External framebuffer wants us to wait!\n"));
203 return VINF_VGA_RESIZE_IN_PROGRESS;
204 }
205
206 return VINF_SUCCESS;
207}
208
209/**
210 * Handles display resize event.
211 * Disables access to VGA device;
212 * calls the framebuffer RequestResize method;
213 * if framebuffer resizes synchronously,
214 * updates the display connector data and enables access to the VGA device.
215 *
216 * @param w New display width
217 * @param h New display height
218 *
219 * @thread EMT
220 */
221int Display::handleDisplayResize (uint32_t bpp, void *pvVRAM, uint32_t cbLine, int w, int h)
222{
223 LogRel (("Display::handleDisplayResize(): pvVRAM=%p w=%d h=%d bpp=%d cbLine=0x%X\n",
224 pvVRAM, w, h, bpp, cbLine));
225
226 /* If there is no framebuffer, this call is not interesting. */
227 if (mFramebuffer.isNull())
228 {
229 return VINF_SUCCESS;
230 }
231
232 mLastAddress = pvVRAM;
233 mLastLineSize = cbLine;
234 mLastColorDepth = bpp,
235 mLastWidth = w;
236 mLastHeight = h;
237
238 FramebufferPixelFormat_T pixelFormat;
239
240 switch (bpp)
241 {
242 case 32: pixelFormat = FramebufferPixelFormat_PixelFormatRGB32; break;
243 case 24: pixelFormat = FramebufferPixelFormat_PixelFormatRGB24; break;
244 case 16: pixelFormat = FramebufferPixelFormat_PixelFormatRGB16; break;
245 default: pixelFormat = FramebufferPixelFormat_PixelFormatDefault; cbLine = 0;
246 }
247
248 /* Atomically set the resize status before calling the framebuffer. The new InProgress status will
249 * disable access to the VGA device by the EMT thread.
250 */
251 bool f = ASMAtomicCmpXchgU32 (&mu32ResizeStatus, ResizeStatus_InProgress, ResizeStatus_Void);
252 AssertReleaseMsg(f, ("f = %d\n", f));NOREF(f);
253
254 /* The framebuffer is locked in the state.
255 * The lock is kept, because the framebuffer is in undefined state.
256 */
257 mFramebuffer->Lock();
258
259 int rc = callFramebufferResize (mFramebuffer, pixelFormat, pvVRAM, cbLine, w, h);
260 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
261 {
262 /* Immediately return to the caller. ResizeCompleted will be called back by the
263 * GUI thread. The ResizeCompleted callback will change the resize status from
264 * InProgress to UpdateDisplayData. The latter status will be checked by the
265 * display timer callback on EMT and all required adjustments will be done there.
266 */
267 return rc;
268 }
269
270 /* Set the status so the 'handleResizeCompleted' would work. */
271 f = ASMAtomicCmpXchgU32 (&mu32ResizeStatus, ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
272 AssertRelease(f);NOREF(f);
273
274 /* The method also unlocks the framebuffer. */
275 handleResizeCompletedEMT();
276
277 return VINF_SUCCESS;
278}
279
280/**
281 * Framebuffer has been resized.
282 * Read the new display data and unlock the framebuffer.
283 *
284 * @thread EMT
285 */
286void Display::handleResizeCompletedEMT (void)
287{
288 LogFlowFunc(("\n"));
289 if (!mFramebuffer.isNull())
290 {
291 /* Framebuffer has completed the resize. Update the connector data. */
292 updateDisplayData();
293
294 /* Check the framebuffer pixel format to setup the rendering in VGA device. */
295 FramebufferPixelFormat_T newPixelFormat;
296
297 mFramebuffer->COMGETTER(PixelFormat) (&newPixelFormat);
298
299 mpDrv->pUpPort->pfnSetRenderVRAM (mpDrv->pUpPort, newPixelFormat == FramebufferPixelFormat_PixelFormatDefault);
300 }
301
302#ifdef DEBUG_sunlover
303 if (!stam)
304 {
305 /* protect mpVM */
306 Console::SafeVMPtr pVM (mParent);
307 AssertComRC (pVM.rc());
308
309 STAM_REG(pVM, &StatDisplayRefresh, STAMTYPE_PROFILE, "/PROF/Display/Refresh", STAMUNIT_TICKS_PER_CALL, "Time spent in EMT for display updates.");
310 stam = 1;
311 }
312#endif /* DEBUG_sunlover */
313
314 /* Inform VRDP server about the change of display parameters. */
315 LogFlowFunc (("Calling VRDP\n"));
316 mParent->consoleVRDPServer()->SendResize();
317
318 /* Go into non resizing state. */
319 bool f = ASMAtomicCmpXchgU32 (&mu32ResizeStatus, ResizeStatus_Void, ResizeStatus_UpdateDisplayData);
320 AssertRelease(f);NOREF(f);
321
322 if (!mFramebuffer.isNull())
323 {
324 /* Unlock framebuffer after evrything is done. */
325 mFramebuffer->Unlock();
326 }
327}
328
329static void checkCoordBounds (int *px, int *py, int *pw, int *ph, int cx, int cy)
330{
331 /* Correct negative x and y coordinates. */
332 if (*px < 0)
333 {
334 *px += *pw; /* Compute xRight which is also the new width. */
335
336 *pw = (*px < 0)? 0: *px;
337
338 *px = 0;
339 }
340
341 if (*py < 0)
342 {
343 *py += *ph; /* Compute xBottom, which is also the new height. */
344
345 *ph = (*py < 0)? 0: *py;
346
347 *py = 0;
348 }
349
350 /* Also check if coords are greater than the display resolution. */
351 if (*px + *pw > cx)
352 {
353 *pw = cx > *px? cx - *px: 0;
354 }
355
356 if (*py + *ph > cy)
357 {
358 *ph = cy > *py? cy - *py: 0;
359 }
360}
361
362/**
363 * Handles display update event.
364 *
365 * @param x Update area x coordinate
366 * @param y Update area y coordinate
367 * @param w Update area width
368 * @param h Update area height
369 *
370 * @thread EMT
371 */
372void Display::handleDisplayUpdate (int x, int y, int w, int h)
373{
374 // if there is no framebuffer, this call is not interesting
375 if (mFramebuffer.isNull())
376 return;
377
378 mFramebuffer->Lock();
379
380#ifdef DEBUG_sunlover
381 LogFlowFunc (("%d,%d %dx%d (%d,%d)\n",
382 x, y, w, h, mpDrv->Connector.cx, mpDrv->Connector.cy));
383#endif /* DEBUG_sunlover */
384
385 checkCoordBounds (&x, &y, &w, &h, mpDrv->Connector.cx, mpDrv->Connector.cy);
386
387#ifdef DEBUG_sunlover
388 LogFlowFunc (("%d,%d %dx%d (checked)\n", x, y, w, h));
389#endif /* DEBUG_sunlover */
390
391 /* special processing for the internal framebuffer */
392 if (mInternalFramebuffer)
393 {
394 mFramebuffer->Unlock();
395 } else
396 {
397 /* callback into the framebuffer to notify it */
398 BOOL finished = FALSE;
399
400 RTSemEventMultiReset(mUpdateSem);
401
402 mFramebuffer->NotifyUpdate(x, y, w, h, &finished);
403
404 if (!finished)
405 {
406 /*
407 * the framebuffer needs more time to process
408 * the event so we have to halt the VM until it's done
409 */
410 mFramebuffer->Unlock();
411 RTSemEventMultiWait(mUpdateSem, RT_INDEFINITE_WAIT);
412 } else
413 {
414 mFramebuffer->Unlock();
415 }
416
417 if (!mfVideoAccelEnabled)
418 {
419 /* When VBVA is enabled, the VRDP server is informed in the VideoAccelFlush.
420 * Inform the server here only if VBVA is disabled.
421 */
422 mParent->consoleVRDPServer()->SendUpdateBitmap(x, y, w, h);
423 }
424 }
425 return;
426}
427
428typedef struct _VBVADIRTYREGION
429{
430 /* Copies of object's pointers used by vbvaRgn functions. */
431 IFramebuffer *pFramebuffer;
432 Display *pDisplay;
433 PPDMIDISPLAYPORT pPort;
434
435 /* The Framebuffer has default format and must be updates immediately. */
436 bool fDefaultFormat;
437
438 /* Merged rectangles. */
439 int32_t xLeft;
440 int32_t xRight;
441 int32_t yTop;
442 int32_t yBottom;
443
444} VBVADIRTYREGION;
445
446static void vbvaRgnInit (VBVADIRTYREGION *prgn, IFramebuffer *pfb, Display *pd, PPDMIDISPLAYPORT pp)
447{
448 memset (prgn, 0, sizeof (VBVADIRTYREGION));
449
450 prgn->pFramebuffer = pfb;
451 prgn->pDisplay = pd;
452 prgn->pPort = pp;
453
454 if (pfb)
455 {
456 FramebufferPixelFormat_T pixelFormat;
457 pfb->COMGETTER(PixelFormat) (&pixelFormat);
458 prgn->fDefaultFormat = (pixelFormat == FramebufferPixelFormat_PixelFormatDefault);
459 }
460
461 return;
462}
463
464static void vbvaRgnDirtyRect (VBVADIRTYREGION *prgn, VBVACMDHDR *phdr)
465{
466 LogFlowFunc (("x = %d, y = %d, w = %d, h = %d\n",
467 phdr->x, phdr->y, phdr->w, phdr->h));
468
469 /*
470 * Here update rectangles are accumulated to form an update area.
471 * @todo
472 * Now the simpliest method is used which builds one rectangle that
473 * includes all update areas. A bit more advanced method can be
474 * employed here. The method should be fast however.
475 */
476 if (phdr->w == 0 || phdr->h == 0)
477 {
478 /* Empty rectangle. */
479 return;
480 }
481
482 int32_t xRight = phdr->x + phdr->w;
483 int32_t yBottom = phdr->y + phdr->h;
484
485 if (prgn->xRight == 0)
486 {
487 /* This is the first rectangle to be added. */
488 prgn->xLeft = phdr->x;
489 prgn->yTop = phdr->y;
490 prgn->xRight = xRight;
491 prgn->yBottom = yBottom;
492 }
493 else
494 {
495 /* Adjust region coordinates. */
496 if (prgn->xLeft > phdr->x)
497 {
498 prgn->xLeft = phdr->x;
499 }
500
501 if (prgn->yTop > phdr->y)
502 {
503 prgn->yTop = phdr->y;
504 }
505
506 if (prgn->xRight < xRight)
507 {
508 prgn->xRight = xRight;
509 }
510
511 if (prgn->yBottom < yBottom)
512 {
513 prgn->yBottom = yBottom;
514 }
515 }
516
517 if (prgn->fDefaultFormat)
518 {
519 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, phdr->x, phdr->y, phdr->w, phdr->h);
520 prgn->pDisplay->handleDisplayUpdate (phdr->x, phdr->y, phdr->w, phdr->h);
521 }
522
523 return;
524}
525
526static void vbvaRgnUpdateFramebuffer (VBVADIRTYREGION *prgn)
527{
528 uint32_t w = prgn->xRight - prgn->xLeft;
529 uint32_t h = prgn->yBottom - prgn->yTop;
530
531 if (!prgn->fDefaultFormat && prgn->pFramebuffer && w != 0 && h != 0)
532 {
533 prgn->pPort->pfnUpdateDisplayRect (prgn->pPort, prgn->xLeft, prgn->yTop, w, h);
534 prgn->pDisplay->handleDisplayUpdate (prgn->xLeft, prgn->yTop, w, h);
535 }
536}
537
538static void vbvaSetMemoryFlags (VBVAMEMORY *pVbvaMemory,
539 bool fVideoAccelEnabled,
540 bool fVideoAccelVRDP,
541 uint32_t fu32SupportedOrders)
542{
543 if (pVbvaMemory)
544 {
545 /* This called only on changes in mode. So reset VRDP always. */
546 uint32_t fu32Flags = VBVA_F_MODE_VRDP_RESET;
547
548 if (fVideoAccelEnabled)
549 {
550 fu32Flags |= VBVA_F_MODE_ENABLED;
551
552 if (fVideoAccelVRDP)
553 {
554 fu32Flags |= VBVA_F_MODE_VRDP | VBVA_F_MODE_VRDP_ORDER_MASK;
555
556 pVbvaMemory->fu32SupportedOrders = fu32SupportedOrders;
557 }
558 }
559
560 pVbvaMemory->fu32ModeFlags = fu32Flags;
561 }
562}
563
564bool Display::VideoAccelAllowed (void)
565{
566 return true;
567}
568
569/**
570 * @thread EMT
571 */
572int Display::VideoAccelEnable (bool fEnable, VBVAMEMORY *pVbvaMemory)
573{
574 int rc = VINF_SUCCESS;
575
576 /* Called each time the guest wants to use acceleration,
577 * or when the VGA device disables acceleration,
578 * or when restoring the saved state with accel enabled.
579 *
580 * VGA device disables acceleration on each video mode change
581 * and on reset.
582 *
583 * Guest enabled acceleration at will. And it has to enable
584 * acceleration after a mode change.
585 */
586 LogFlowFunc (("mfVideoAccelEnabled = %d, fEnable = %d, pVbvaMemory = %p\n",
587 mfVideoAccelEnabled, fEnable, pVbvaMemory));
588
589 /* Strictly check parameters. Callers must not pass anything in the case. */
590 Assert((fEnable && pVbvaMemory) || (!fEnable && pVbvaMemory == NULL));
591
592 if (!VideoAccelAllowed ())
593 {
594 return VERR_NOT_SUPPORTED;
595 }
596
597 /*
598 * Verify that the VM is in running state. If it is not,
599 * then this must be postponed until it goes to running.
600 */
601 if (!mfMachineRunning)
602 {
603 Assert (!mfVideoAccelEnabled);
604
605 LogFlowFunc (("Machine is not yet running.\n"));
606
607 if (fEnable)
608 {
609 mfPendingVideoAccelEnable = fEnable;
610 mpPendingVbvaMemory = pVbvaMemory;
611 }
612
613 return rc;
614 }
615
616 /* Check that current status is not being changed */
617 if (mfVideoAccelEnabled == fEnable)
618 {
619 return rc;
620 }
621
622 if (mfVideoAccelEnabled)
623 {
624 /* Process any pending orders and empty the VBVA ring buffer. */
625 VideoAccelFlush ();
626 }
627
628 if (!fEnable && mpVbvaMemory)
629 {
630 mpVbvaMemory->fu32ModeFlags &= ~VBVA_F_MODE_ENABLED;
631 }
632
633 /* Safety precaution. There is no more VBVA until everything is setup! */
634 mpVbvaMemory = NULL;
635 mfVideoAccelEnabled = false;
636
637 /* Update entire display. */
638 if (mu32ResizeStatus == ResizeStatus_Void)
639 {
640 mpDrv->pUpPort->pfnUpdateDisplayAll(mpDrv->pUpPort);
641 }
642
643 /* Everything OK. VBVA status can be changed. */
644
645 /* Notify the VMMDev, which saves VBVA status in the saved state,
646 * and needs to know current status.
647 */
648 PPDMIVMMDEVPORT pVMMDevPort = mParent->getVMMDev()->getVMMDevPort ();
649
650 if (pVMMDevPort)
651 {
652 pVMMDevPort->pfnVBVAChange (pVMMDevPort, fEnable);
653 }
654
655 if (fEnable)
656 {
657 mpVbvaMemory = pVbvaMemory;
658 mfVideoAccelEnabled = true;
659
660 /* Initialize the hardware memory. */
661 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders);
662 mpVbvaMemory->off32Data = 0;
663 mpVbvaMemory->off32Free = 0;
664
665 memset (mpVbvaMemory->aRecords, 0, sizeof (mpVbvaMemory->aRecords));
666 mpVbvaMemory->indexRecordFirst = 0;
667 mpVbvaMemory->indexRecordFree = 0;
668
669 LogRel(("VBVA: Enabled.\n"));
670 }
671 else
672 {
673 LogRel(("VBVA: Disabled.\n"));
674 }
675
676 LogFlowFunc (("VideoAccelEnable: rc = %Vrc.\n", rc));
677
678 return rc;
679}
680
681#ifdef VBOX_VRDP
682#ifdef VRDP_MC
683/* Called always by one VRDP server thread. Can be thread-unsafe.
684 */
685void Display::VideoAccelVRDP (bool fEnable)
686{
687#if 0
688 /* Supporting all orders. */
689 uint32_t fu32SupportedOrders = ~0;
690#endif
691
692 int c = fEnable?
693 ASMAtomicIncS32 (&mcVideoAccelVRDPRefs):
694 ASMAtomicDecS32 (&mcVideoAccelVRDPRefs);
695
696 Assert (c >= 0);
697
698 if (c == 0)
699 {
700 /* The last client has disconnected, and the accel can be
701 * disabled.
702 */
703 Assert (fEnable == false);
704
705 mfVideoAccelVRDP = false;
706 mfu32SupportedOrders = 0;
707
708 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders);
709
710 LogRel(("VBVA: VRDP acceleration has been disabled.\n"));
711 }
712 else if ( c == 1
713 && !mfVideoAccelVRDP)
714 {
715 /* The first client has connected. Enable the accel.
716 */
717 Assert (fEnable == true);
718
719 mfVideoAccelVRDP = true;
720 /* Supporting all orders. */
721 mfu32SupportedOrders = ~0;
722
723 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders);
724
725 LogRel(("VBVA: VRDP acceleration has been requested.\n"));
726 }
727 else
728 {
729 /* A client is connected or disconnected but there is no change in the
730 * accel state. It remains enabled.
731 */
732 Assert (mfVideoAccelVRDP == true);
733 }
734}
735#else
736void Display::VideoAccelVRDP (bool fEnable, uint32_t fu32SupportedOrders)
737{
738 Assert (mfVideoAccelVRDP != fEnable);
739
740 mfVideoAccelVRDP = fEnable;
741
742 if (fEnable)
743 {
744 mfu32SupportedOrders = fu32SupportedOrders;
745 }
746 else
747 {
748 mfu32SupportedOrders = 0;
749 }
750
751 vbvaSetMemoryFlags (mpVbvaMemory, mfVideoAccelEnabled, mfVideoAccelVRDP, mfu32SupportedOrders);
752
753 LogRel(("VBVA: VRDP acceleration has been %s.\n", fEnable? "requested": "disabled"));
754}
755#endif /* VRDP_MC */
756#endif /* VBOX_VRDP */
757
758static bool vbvaVerifyRingBuffer (VBVAMEMORY *pVbvaMemory)
759{
760 return true;
761}
762
763static void vbvaFetchBytes (VBVAMEMORY *pVbvaMemory, uint8_t *pu8Dst, uint32_t cbDst)
764{
765 if (cbDst >= VBVA_RING_BUFFER_SIZE)
766 {
767 AssertMsgFailed (("cbDst = 0x%08X, ring buffer size 0x%08X", cbDst, VBVA_RING_BUFFER_SIZE));
768 return;
769 }
770
771 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - pVbvaMemory->off32Data;
772 uint8_t *src = &pVbvaMemory->au8RingBuffer[pVbvaMemory->off32Data];
773 int32_t i32Diff = cbDst - u32BytesTillBoundary;
774
775 if (i32Diff <= 0)
776 {
777 /* Chunk will not cross buffer boundary. */
778 memcpy (pu8Dst, src, cbDst);
779 }
780 else
781 {
782 /* Chunk crosses buffer boundary. */
783 memcpy (pu8Dst, src, u32BytesTillBoundary);
784 memcpy (pu8Dst + u32BytesTillBoundary, &pVbvaMemory->au8RingBuffer[0], i32Diff);
785 }
786
787 /* Advance data offset. */
788 pVbvaMemory->off32Data = (pVbvaMemory->off32Data + cbDst) % VBVA_RING_BUFFER_SIZE;
789
790 return;
791}
792
793
794static bool vbvaPartialRead (uint8_t **ppu8, uint32_t *pcb, uint32_t cbRecord, VBVAMEMORY *pVbvaMemory)
795{
796 uint8_t *pu8New;
797
798 LogFlow(("MAIN::DisplayImpl::vbvaPartialRead: p = %p, cb = %d, cbRecord 0x%08X\n",
799 *ppu8, *pcb, cbRecord));
800
801 if (*ppu8)
802 {
803 Assert (*pcb);
804 pu8New = (uint8_t *)RTMemRealloc (*ppu8, cbRecord);
805 }
806 else
807 {
808 Assert (!*pcb);
809 pu8New = (uint8_t *)RTMemAlloc (cbRecord);
810 }
811
812 if (!pu8New)
813 {
814 /* Memory allocation failed, fail the function. */
815 Log(("MAIN::vbvaPartialRead: failed to (re)alocate memory for partial record!!! cbRecord 0x%08X\n",
816 cbRecord));
817
818 if (*ppu8)
819 {
820 RTMemFree (*ppu8);
821 }
822
823 *ppu8 = NULL;
824 *pcb = 0;
825
826 return false;
827 }
828
829 /* Fetch data from the ring buffer. */
830 vbvaFetchBytes (pVbvaMemory, pu8New + *pcb, cbRecord - *pcb);
831
832 *ppu8 = pu8New;
833 *pcb = cbRecord;
834
835 return true;
836}
837
838/* For contiguous chunks just return the address in the buffer.
839 * For crossing boundary - allocate a buffer from heap.
840 */
841bool Display::vbvaFetchCmd (VBVACMDHDR **ppHdr, uint32_t *pcbCmd)
842{
843 uint32_t indexRecordFirst = mpVbvaMemory->indexRecordFirst;
844 uint32_t indexRecordFree = mpVbvaMemory->indexRecordFree;
845
846#ifdef DEBUG_sunlover
847 LogFlowFunc (("first = %d, free = %d\n",
848 indexRecordFirst, indexRecordFree));
849#endif /* DEBUG_sunlover */
850
851 if (!vbvaVerifyRingBuffer (mpVbvaMemory))
852 {
853 return false;
854 }
855
856 if (indexRecordFirst == indexRecordFree)
857 {
858 /* No records to process. Return without assigning output variables. */
859 return true;
860 }
861
862 VBVARECORD *pRecord = &mpVbvaMemory->aRecords[indexRecordFirst];
863
864#ifdef DEBUG_sunlover
865 LogFlowFunc (("cbRecord = 0x%08X\n", pRecord->cbRecord));
866#endif /* DEBUG_sunlover */
867
868 uint32_t cbRecord = pRecord->cbRecord & ~VBVA_F_RECORD_PARTIAL;
869
870 if (mcbVbvaPartial)
871 {
872 /* There is a partial read in process. Continue with it. */
873
874 Assert (mpu8VbvaPartial);
875
876 LogFlowFunc (("continue partial record mcbVbvaPartial = %d cbRecord 0x%08X, first = %d, free = %d\n",
877 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
878
879 if (cbRecord > mcbVbvaPartial)
880 {
881 /* New data has been added to the record. */
882 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
883 {
884 return false;
885 }
886 }
887
888 if (!(pRecord->cbRecord & VBVA_F_RECORD_PARTIAL))
889 {
890 /* The record is completed by guest. Return it to the caller. */
891 *ppHdr = (VBVACMDHDR *)mpu8VbvaPartial;
892 *pcbCmd = mcbVbvaPartial;
893
894 mpu8VbvaPartial = NULL;
895 mcbVbvaPartial = 0;
896
897 /* Advance the record index. */
898 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
899
900#ifdef DEBUG_sunlover
901 LogFlowFunc (("partial done ok, data = %d, free = %d\n",
902 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
903#endif /* DEBUG_sunlover */
904 }
905
906 return true;
907 }
908
909 /* A new record need to be processed. */
910 if (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)
911 {
912 /* Current record is being written by guest. '=' is important here. */
913 if (cbRecord >= VBVA_RING_BUFFER_SIZE - VBVA_RING_BUFFER_THRESHOLD)
914 {
915 /* Partial read must be started. */
916 if (!vbvaPartialRead (&mpu8VbvaPartial, &mcbVbvaPartial, cbRecord, mpVbvaMemory))
917 {
918 return false;
919 }
920
921 LogFlowFunc (("started partial record mcbVbvaPartial = 0x%08X cbRecord 0x%08X, first = %d, free = %d\n",
922 mcbVbvaPartial, pRecord->cbRecord, indexRecordFirst, indexRecordFree));
923 }
924
925 return true;
926 }
927
928 /* Current record is complete. If it is not empty, process it. */
929 if (cbRecord)
930 {
931 /* The size of largest contiguos chunk in the ring biffer. */
932 uint32_t u32BytesTillBoundary = VBVA_RING_BUFFER_SIZE - mpVbvaMemory->off32Data;
933
934 /* The ring buffer pointer. */
935 uint8_t *au8RingBuffer = &mpVbvaMemory->au8RingBuffer[0];
936
937 /* The pointer to data in the ring buffer. */
938 uint8_t *src = &au8RingBuffer[mpVbvaMemory->off32Data];
939
940 /* Fetch or point the data. */
941 if (u32BytesTillBoundary >= cbRecord)
942 {
943 /* The command does not cross buffer boundary. Return address in the buffer. */
944 *ppHdr = (VBVACMDHDR *)src;
945
946 /* Advance data offset. */
947 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
948 }
949 else
950 {
951 /* The command crosses buffer boundary. Rare case, so not optimized. */
952 uint8_t *dst = (uint8_t *)RTMemAlloc (cbRecord);
953
954 if (!dst)
955 {
956 LogFlowFunc (("could not allocate %d bytes from heap!!!\n", cbRecord));
957 mpVbvaMemory->off32Data = (mpVbvaMemory->off32Data + cbRecord) % VBVA_RING_BUFFER_SIZE;
958 return false;
959 }
960
961 vbvaFetchBytes (mpVbvaMemory, dst, cbRecord);
962
963 *ppHdr = (VBVACMDHDR *)dst;
964
965#ifdef DEBUG_sunlover
966 LogFlowFunc (("Allocated from heap %p\n", dst));
967#endif /* DEBUG_sunlover */
968 }
969 }
970
971 *pcbCmd = cbRecord;
972
973 /* Advance the record index. */
974 mpVbvaMemory->indexRecordFirst = (indexRecordFirst + 1) % VBVA_MAX_RECORDS;
975
976#ifdef DEBUG_sunlover
977 LogFlowFunc (("done ok, data = %d, free = %d\n",
978 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
979#endif /* DEBUG_sunlover */
980
981 return true;
982}
983
984void Display::vbvaReleaseCmd (VBVACMDHDR *pHdr, int32_t cbCmd)
985{
986 uint8_t *au8RingBuffer = mpVbvaMemory->au8RingBuffer;
987
988 if ( (uint8_t *)pHdr >= au8RingBuffer
989 && (uint8_t *)pHdr < &au8RingBuffer[VBVA_RING_BUFFER_SIZE])
990 {
991 /* The pointer is inside ring buffer. Must be continuous chunk. */
992 Assert (VBVA_RING_BUFFER_SIZE - ((uint8_t *)pHdr - au8RingBuffer) >= cbCmd);
993
994 /* Do nothing. */
995
996 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
997 }
998 else
999 {
1000 /* The pointer is outside. It is then an allocated copy. */
1001
1002#ifdef DEBUG_sunlover
1003 LogFlowFunc (("Free heap %p\n", pHdr));
1004#endif /* DEBUG_sunlover */
1005
1006 if ((uint8_t *)pHdr == mpu8VbvaPartial)
1007 {
1008 mpu8VbvaPartial = NULL;
1009 mcbVbvaPartial = 0;
1010 }
1011 else
1012 {
1013 Assert (!mpu8VbvaPartial && mcbVbvaPartial == 0);
1014 }
1015
1016 RTMemFree (pHdr);
1017 }
1018
1019 return;
1020}
1021
1022
1023/**
1024 * Called regularly on the DisplayRefresh timer.
1025 * Also on behalf of guest, when the ring buffer is full.
1026 *
1027 * @thread EMT
1028 */
1029void Display::VideoAccelFlush (void)
1030{
1031#ifdef DEBUG_sunlover
1032 LogFlowFunc (("mfVideoAccelEnabled = %d\n", mfVideoAccelEnabled));
1033#endif /* DEBUG_sunlover */
1034
1035 if (!mfVideoAccelEnabled)
1036 {
1037 Log(("Display::VideoAccelFlush: called with disabled VBVA!!! Ignoring.\n"));
1038 return;
1039 }
1040
1041 /* Here VBVA is enabled and we have the accelerator memory pointer. */
1042 Assert(mpVbvaMemory);
1043
1044#ifdef DEBUG_sunlover
1045 LogFlowFunc (("indexRecordFirst = %d, indexRecordFree = %d, off32Data = %d, off32Free = %d\n",
1046 mpVbvaMemory->indexRecordFirst, mpVbvaMemory->indexRecordFree, mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1047#endif /* DEBUG_sunlover */
1048
1049 /* Quick check for "nothing to update" case. */
1050 if (mpVbvaMemory->indexRecordFirst == mpVbvaMemory->indexRecordFree)
1051 {
1052 return;
1053 }
1054
1055 /* Process the ring buffer */
1056
1057 bool fFramebufferIsNull = mFramebuffer.isNull();
1058
1059 if (!fFramebufferIsNull)
1060 {
1061 mFramebuffer->Lock();
1062 }
1063
1064 /* Initialize dirty rectangles accumulator. */
1065 VBVADIRTYREGION rgn;
1066 vbvaRgnInit (&rgn, mFramebuffer, this, mpDrv->pUpPort);
1067
1068 for (;;)
1069 {
1070 VBVACMDHDR *phdr = NULL;
1071 uint32_t cbCmd = ~0;
1072
1073 /* Fetch the command data. */
1074 if (!vbvaFetchCmd (&phdr, &cbCmd))
1075 {
1076 Log(("Display::VideoAccelFlush: unable to fetch command. off32Data = %d, off32Free = %d. Disabling VBVA!!!\n",
1077 mpVbvaMemory->off32Data, mpVbvaMemory->off32Free));
1078
1079 /* Disable VBVA on those processing errors. */
1080 VideoAccelEnable (false, NULL);
1081
1082 break;
1083 }
1084
1085 if (cbCmd == uint32_t(~0))
1086 {
1087 /* No more commands yet in the queue. */
1088 break;
1089 }
1090
1091 if (cbCmd != 0 && !fFramebufferIsNull)
1092 {
1093#ifdef DEBUG_sunlover
1094 LogFlowFunc (("hdr: cbCmd = %d, x=%d, y=%d, w=%d, h=%d\n",
1095 cbCmd, phdr->x, phdr->y, phdr->w, phdr->h));
1096#endif /* DEBUG_sunlover */
1097
1098 if (mu32ResizeStatus == ResizeStatus_Void)
1099 {
1100 /* Handle the command.
1101 *
1102 * Guest is responsible for updating the guest video memory.
1103 * The Windows guest does all drawing using Eng*.
1104 *
1105 * For local output, only dirty rectangle information is used
1106 * to update changed areas.
1107 *
1108 * Dirty rectangles are accumulated to exclude overlapping updates and
1109 * group small updates to a larger one.
1110 */
1111
1112 /* Accumulate the update. */
1113 vbvaRgnDirtyRect (&rgn, phdr);
1114
1115 /* Forward the command to VRDP server. */
1116 mParent->consoleVRDPServer()->SendUpdate (phdr, cbCmd);
1117 }
1118 }
1119
1120 vbvaReleaseCmd (phdr, cbCmd);
1121 }
1122
1123 if (!fFramebufferIsNull)
1124 {
1125 mFramebuffer->Unlock ();
1126 }
1127
1128 if (mu32ResizeStatus == ResizeStatus_Void)
1129 {
1130 /* Draw the framebuffer. */
1131 vbvaRgnUpdateFramebuffer (&rgn);
1132 }
1133}
1134
1135
1136// IDisplay properties
1137/////////////////////////////////////////////////////////////////////////////
1138
1139/**
1140 * Returns the current display width in pixel
1141 *
1142 * @returns COM status code
1143 * @param width Address of result variable.
1144 */
1145STDMETHODIMP Display::COMGETTER(Width) (ULONG *width)
1146{
1147 if (!width)
1148 return E_POINTER;
1149
1150 AutoLock alock (this);
1151 CHECK_READY();
1152
1153 CHECK_CONSOLE_DRV (mpDrv);
1154
1155 *width = mpDrv->Connector.cx;
1156 return S_OK;
1157}
1158
1159/**
1160 * Returns the current display height in pixel
1161 *
1162 * @returns COM status code
1163 * @param height Address of result variable.
1164 */
1165STDMETHODIMP Display::COMGETTER(Height) (ULONG *height)
1166{
1167 if (!height)
1168 return E_POINTER;
1169
1170 AutoLock alock (this);
1171 CHECK_READY();
1172
1173 CHECK_CONSOLE_DRV (mpDrv);
1174
1175 *height = mpDrv->Connector.cy;
1176 return S_OK;
1177}
1178
1179/**
1180 * Returns the current display color depth in bits
1181 *
1182 * @returns COM status code
1183 * @param colorDepth Address of result variable.
1184 */
1185STDMETHODIMP Display::COMGETTER(ColorDepth) (ULONG *colorDepth)
1186{
1187 if (!colorDepth)
1188 return E_INVALIDARG;
1189
1190 AutoLock alock (this);
1191 CHECK_READY();
1192
1193 CHECK_CONSOLE_DRV (mpDrv);
1194
1195 uint32_t cBits = 0;
1196 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
1197 AssertRC(rc);
1198 *colorDepth = cBits;
1199 return S_OK;
1200}
1201
1202
1203// IDisplay methods
1204/////////////////////////////////////////////////////////////////////////////
1205
1206STDMETHODIMP Display::SetupInternalFramebuffer (ULONG depth)
1207{
1208 LogFlowFunc (("\n"));
1209
1210 AutoLock lock (this);
1211 CHECK_READY();
1212
1213 /*
1214 * Create an internal framebuffer only if depth is not zero. Otherwise, we
1215 * reset back to the "black hole" state as it was at Display construction.
1216 */
1217 ComPtr <IFramebuffer> frameBuf;
1218 if (depth)
1219 {
1220 ComObjPtr <InternalFramebuffer> internal;
1221 internal.createObject();
1222 internal->init (640, 480, depth);
1223 frameBuf = internal; // query interface
1224 }
1225
1226 Console::SafeVMPtrQuiet pVM (mParent);
1227 if (pVM.isOk())
1228 {
1229 /* Must leave the lock here because the changeFramebuffer will also obtain it. */
1230 lock.leave ();
1231
1232 /* send request to the EMT thread */
1233 PVMREQ pReq = NULL;
1234 int vrc = VMR3ReqCall (pVM, &pReq, RT_INDEFINITE_WAIT,
1235 (PFNRT) changeFramebuffer, 3,
1236 this, static_cast <IFramebuffer *> (frameBuf),
1237 true /* aInternal */);
1238 if (VBOX_SUCCESS (vrc))
1239 vrc = pReq->iStatus;
1240 VMR3ReqFree (pReq);
1241
1242 lock.enter ();
1243
1244 ComAssertRCRet (vrc, E_FAIL);
1245 }
1246 else
1247 {
1248 /* No VM is created (VM is powered off), do a direct call */
1249 int vrc = changeFramebuffer (this, frameBuf, true /* aInternal */);
1250 ComAssertRCRet (vrc, E_FAIL);
1251 }
1252
1253 return S_OK;
1254}
1255
1256STDMETHODIMP Display::LockFramebuffer (BYTE **address)
1257{
1258 if (!address)
1259 return E_POINTER;
1260
1261 AutoLock lock(this);
1262 CHECK_READY();
1263
1264 /* only allowed for internal framebuffers */
1265 if (mInternalFramebuffer && !mFramebufferOpened && !mFramebuffer.isNull())
1266 {
1267 CHECK_CONSOLE_DRV (mpDrv);
1268
1269 mFramebuffer->Lock();
1270 mFramebufferOpened = true;
1271 *address = mpDrv->Connector.pu8Data;
1272 return S_OK;
1273 }
1274
1275 return setError (E_FAIL,
1276 tr ("Framebuffer locking is allowed only for the internal framebuffer"));
1277}
1278
1279STDMETHODIMP Display::UnlockFramebuffer()
1280{
1281 AutoLock lock(this);
1282 CHECK_READY();
1283
1284 if (mFramebufferOpened)
1285 {
1286 CHECK_CONSOLE_DRV (mpDrv);
1287
1288 mFramebuffer->Unlock();
1289 mFramebufferOpened = false;
1290 return S_OK;
1291 }
1292
1293 return setError (E_FAIL,
1294 tr ("Framebuffer locking is allowed only for the internal framebuffer"));
1295}
1296
1297STDMETHODIMP Display::RegisterExternalFramebuffer (IFramebuffer *frameBuf)
1298{
1299 LogFlowFunc (("\n"));
1300
1301 if (!frameBuf)
1302 return E_POINTER;
1303
1304 AutoLock lock (this);
1305 CHECK_READY();
1306
1307 Console::SafeVMPtrQuiet pVM (mParent);
1308 if (pVM.isOk())
1309 {
1310 /* Must leave the lock here because the changeFramebuffer will also obtain it. */
1311 lock.leave ();
1312
1313 /* send request to the EMT thread */
1314 PVMREQ pReq = NULL;
1315 int vrc = VMR3ReqCall (pVM, &pReq, RT_INDEFINITE_WAIT,
1316 (PFNRT) changeFramebuffer, 3,
1317 this, frameBuf, false /* aInternal */);
1318 if (VBOX_SUCCESS (vrc))
1319 vrc = pReq->iStatus;
1320 VMR3ReqFree (pReq);
1321
1322 lock.enter ();
1323
1324 ComAssertRCRet (vrc, E_FAIL);
1325 }
1326 else
1327 {
1328 /* No VM is created (VM is powered off), do a direct call */
1329 int vrc = changeFramebuffer (this, frameBuf, false /* aInternal */);
1330 ComAssertRCRet (vrc, E_FAIL);
1331 }
1332
1333 return S_OK;
1334}
1335
1336STDMETHODIMP Display::SetVideoModeHint(ULONG aWidth, ULONG aHeight, ULONG aColorDepth)
1337{
1338 AutoLock lock(this);
1339 CHECK_READY();
1340
1341 CHECK_CONSOLE_DRV (mpDrv);
1342
1343 /*
1344 * Do some rough checks for valid input
1345 */
1346 ULONG width = aWidth;
1347 if (!width)
1348 width = mpDrv->Connector.cx;
1349 ULONG height = aHeight;
1350 if (!height)
1351 height = mpDrv->Connector.cy;
1352 ULONG bpp = aColorDepth;
1353 if (!bpp)
1354 {
1355 uint32_t cBits = 0;
1356 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
1357 AssertRC(rc);
1358 bpp = cBits;
1359 }
1360 ULONG vramSize;
1361 mParent->machine()->COMGETTER(VRAMSize)(&vramSize);
1362 /* enough VRAM? */
1363 if ((width * height * (bpp / 8)) > (vramSize * 1024 * 1024))
1364 return setError(E_FAIL, tr("Not enough VRAM for the selected video mode"));
1365
1366 if (mParent->getVMMDev())
1367 mParent->getVMMDev()->getVMMDevPort()->pfnRequestDisplayChange(mParent->getVMMDev()->getVMMDevPort(), aWidth, aHeight, aColorDepth);
1368 return S_OK;
1369}
1370
1371STDMETHODIMP Display::TakeScreenShot (BYTE *address, ULONG width, ULONG height)
1372{
1373 /// @todo (r=dmik) this function may take too long to complete if the VM
1374 // is doing something like saving state right now. Which, in case if it
1375 // is called on the GUI thread, will make it unresponsive. We should
1376 // check the machine state here (by enclosing the check and VMRequCall
1377 // within the Console lock to make it atomic).
1378
1379 LogFlowFuncEnter();
1380 LogFlowFunc (("address=%p, width=%d, height=%d\n",
1381 address, width, height));
1382
1383 if (!address)
1384 return E_POINTER;
1385 if (!width || !height)
1386 return E_INVALIDARG;
1387
1388 AutoLock lock(this);
1389 CHECK_READY();
1390
1391 CHECK_CONSOLE_DRV (mpDrv);
1392
1393 Console::SafeVMPtr pVM (mParent);
1394 CheckComRCReturnRC (pVM.rc());
1395
1396 HRESULT rc = S_OK;
1397
1398 LogFlowFunc (("Sending SCREENSHOT request\n"));
1399
1400 /*
1401 * First try use the graphics device features for making a snapshot.
1402 * This does not support streatching, is an optional feature (returns not supported).
1403 *
1404 * Note: It may cause a display resize. Watch out for deadlocks.
1405 */
1406 int rcVBox = VERR_NOT_SUPPORTED;
1407 if ( mpDrv->Connector.cx == width
1408 && mpDrv->Connector.cy == height)
1409 {
1410 PVMREQ pReq;
1411 size_t cbData = RT_ALIGN_Z(width, 4) * 4 * height;
1412 rcVBox = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT,
1413 (PFNRT)mpDrv->pUpPort->pfnSnapshot, 6, mpDrv->pUpPort,
1414 address, cbData, NULL, NULL, NULL);
1415 if (VBOX_SUCCESS(rcVBox))
1416 {
1417 rcVBox = pReq->iStatus;
1418 VMR3ReqFree(pReq);
1419 }
1420 }
1421
1422 /*
1423 * If the function returns not supported, or if streaching is requested,
1424 * we'll have to do all the work ourselves using the framebuffer data.
1425 */
1426 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
1427 {
1428 /** @todo implement snapshot streching and generic snapshot fallback. */
1429 rc = setError (E_NOTIMPL, tr ("This feature is not implemented"));
1430 }
1431 else if (VBOX_FAILURE(rcVBox))
1432 rc = setError (E_FAIL,
1433 tr ("Could not take a screenshot (%Vrc)"), rcVBox);
1434
1435 LogFlowFunc (("rc=%08X\n", rc));
1436 LogFlowFuncLeave();
1437 return rc;
1438}
1439
1440STDMETHODIMP Display::DrawToScreen (BYTE *address, ULONG x, ULONG y,
1441 ULONG width, ULONG height)
1442{
1443 /// @todo (r=dmik) this function may take too long to complete if the VM
1444 // is doing something like saving state right now. Which, in case if it
1445 // is called on the GUI thread, will make it unresponsive. We should
1446 // check the machine state here (by enclosing the check and VMRequCall
1447 // within the Console lock to make it atomic).
1448
1449 LogFlowFuncEnter();
1450 LogFlowFunc (("address=%p, x=%d, y=%d, width=%d, height=%d\n",
1451 address, x, y, width, height));
1452
1453 if (!address)
1454 return E_POINTER;
1455 if (!width || !height)
1456 return E_INVALIDARG;
1457
1458 AutoLock lock(this);
1459 CHECK_READY();
1460
1461 CHECK_CONSOLE_DRV (mpDrv);
1462
1463 Console::SafeVMPtr pVM (mParent);
1464 CheckComRCReturnRC (pVM.rc());
1465
1466 /*
1467 * Again we're lazy and make the graphics device do all the
1468 * dirty convertion work.
1469 */
1470 PVMREQ pReq;
1471 int rcVBox = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT,
1472 (PFNRT)mpDrv->pUpPort->pfnDisplayBlt, 6, mpDrv->pUpPort,
1473 address, x, y, width, height);
1474 if (VBOX_SUCCESS(rcVBox))
1475 {
1476 rcVBox = pReq->iStatus;
1477 VMR3ReqFree(pReq);
1478 }
1479
1480 /*
1481 * If the function returns not supported, we'll have to do all the
1482 * work ourselves using the framebuffer.
1483 */
1484 HRESULT rc = S_OK;
1485 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
1486 {
1487 /** @todo implement generic fallback for screen blitting. */
1488 rc = E_NOTIMPL;
1489 }
1490 else if (VBOX_FAILURE(rcVBox))
1491 rc = setError (E_FAIL,
1492 tr ("Could not draw to the screen (%Vrc)"), rcVBox);
1493//@todo
1494// else
1495// {
1496// /* All ok. Redraw the screen. */
1497// handleDisplayUpdate (x, y, width, height);
1498// }
1499
1500 LogFlowFunc (("rc=%08X\n", rc));
1501 LogFlowFuncLeave();
1502 return rc;
1503}
1504
1505/**
1506 * Does a full invalidation of the VM display and instructs the VM
1507 * to update it immediately.
1508 *
1509 * @returns COM status code
1510 */
1511STDMETHODIMP Display::InvalidateAndUpdate()
1512{
1513 LogFlowFuncEnter();
1514
1515 AutoLock lock(this);
1516 CHECK_READY();
1517
1518 CHECK_CONSOLE_DRV (mpDrv);
1519
1520 Console::SafeVMPtr pVM (mParent);
1521 CheckComRCReturnRC (pVM.rc());
1522
1523 HRESULT rc = S_OK;
1524
1525 LogFlowFunc (("Sending DPYUPDATE request\n"));
1526
1527 /* pdm.h says that this has to be called from the EMT thread */
1528 PVMREQ pReq;
1529 int rcVBox = VMR3ReqCallVoid(pVM, &pReq, RT_INDEFINITE_WAIT,
1530 (PFNRT)mpDrv->pUpPort->pfnUpdateDisplayAll, 1, mpDrv->pUpPort);
1531 if (VBOX_SUCCESS(rcVBox))
1532 VMR3ReqFree(pReq);
1533
1534 if (VBOX_FAILURE(rcVBox))
1535 rc = setError (E_FAIL,
1536 tr ("Could not invalidate and update the screen (%Vrc)"), rcVBox);
1537
1538 LogFlowFunc (("rc=%08X\n", rc));
1539 LogFlowFuncLeave();
1540 return rc;
1541}
1542
1543/**
1544 * Notification that the framebuffer has completed the
1545 * asynchronous resize processing
1546 *
1547 * @returns COM status code
1548 */
1549STDMETHODIMP Display::ResizeCompleted()
1550{
1551 LogFlowFunc (("\n"));
1552
1553 /// @todo (dmik) can we AutoLock alock (this); here?
1554 // do it when we switch this class to VirtualBoxBase_NEXT.
1555 // This will require general code review and may add some details.
1556 // In particular, we may want to check whether EMT is really waiting for
1557 // this notification, etc. It might be also good to obey the caller to make
1558 // sure this method is not called from more than one thread at a time
1559 // (and therefore don't use Display lock at all here to save some
1560 // milliseconds).
1561 CHECK_READY();
1562
1563 /* this is only valid for external framebuffers */
1564 if (mInternalFramebuffer)
1565 return setError (E_FAIL,
1566 tr ("Resize completed notification is valid only "
1567 "for external framebuffers"));
1568
1569 /* Set the flag indicating that the resize has completed and display data need to be updated. */
1570 bool f = ASMAtomicCmpXchgU32 (&mu32ResizeStatus, ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
1571 AssertRelease(f);NOREF(f);
1572
1573 return S_OK;
1574}
1575
1576/**
1577 * Notification that the framebuffer has completed the
1578 * asynchronous update processing
1579 *
1580 * @returns COM status code
1581 */
1582STDMETHODIMP Display::UpdateCompleted()
1583{
1584 LogFlowFunc (("\n"));
1585
1586 /// @todo (dmik) can we AutoLock alock (this); here?
1587 // do it when we switch this class to VirtualBoxBase_NEXT.
1588 // Tthis will require general code review and may add some details.
1589 // In particular, we may want to check whether EMT is really waiting for
1590 // this notification, etc. It might be also good to obey the caller to make
1591 // sure this method is not called from more than one thread at a time
1592 // (and therefore don't use Display lock at all here to save some
1593 // milliseconds).
1594 CHECK_READY();
1595
1596 /* this is only valid for external framebuffers */
1597 if (mInternalFramebuffer)
1598 return setError (E_FAIL,
1599 tr ("Resize completed notification is valid only "
1600 "for external framebuffers"));
1601
1602 mFramebuffer->Lock();
1603 /* signal our semaphore */
1604 RTSemEventMultiSignal(mUpdateSem);
1605 mFramebuffer->Unlock();
1606
1607 return S_OK;
1608}
1609
1610// private methods
1611/////////////////////////////////////////////////////////////////////////////
1612
1613/**
1614 * Helper to update the display information from the framebuffer.
1615 *
1616 * @param aCheckParams true to compare the parameters of the current framebuffer
1617 * and the new one and issue handleDisplayResize()
1618 * if they differ.
1619 * @thread EMT
1620 */
1621void Display::updateDisplayData (bool aCheckParams /* = false */)
1622{
1623 /* the driver might not have been constructed yet */
1624 if (!mpDrv)
1625 return;
1626
1627#if DEBUG
1628 /*
1629 * Sanity check. Note that this method may be called on EMT after Console
1630 * has started the power down procedure (but before our #drvDestruct() is
1631 * called, in which case pVM will aleady be NULL but mpDrv will not). Since
1632 * we don't really need pVM to proceed, we avoid this check in the release
1633 * build to save some ms (necessary to construct SafeVMPtrQuiet) in this
1634 * time-critical method.
1635 */
1636 Console::SafeVMPtrQuiet pVM (mParent);
1637 if (pVM.isOk())
1638 VM_ASSERT_EMT (pVM.raw());
1639#endif
1640
1641 if (mFramebuffer)
1642 {
1643 HRESULT rc;
1644 BYTE *address = 0;
1645 rc = mFramebuffer->COMGETTER(Address) (&address);
1646 AssertComRC (rc);
1647 ULONG lineSize = 0;
1648 rc = mFramebuffer->COMGETTER(LineSize) (&lineSize);
1649 AssertComRC (rc);
1650 ULONG colorDepth = 0;
1651 rc = mFramebuffer->COMGETTER(ColorDepth) (&colorDepth);
1652 AssertComRC (rc);
1653 ULONG width = 0;
1654 rc = mFramebuffer->COMGETTER(Width) (&width);
1655 AssertComRC (rc);
1656 ULONG height = 0;
1657 rc = mFramebuffer->COMGETTER(Height) (&height);
1658 AssertComRC (rc);
1659
1660 /*
1661 * Check current parameters with new ones and issue handleDisplayResize()
1662 * to let the new frame buffer adjust itself properly. Note that it will
1663 * result into a recursive updateDisplayData() call but with
1664 * aCheckOld = false.
1665 */
1666 if (aCheckParams &&
1667 (mLastAddress != address ||
1668 mLastLineSize != lineSize ||
1669 mLastColorDepth != colorDepth ||
1670 mLastWidth != (int) width ||
1671 mLastHeight != (int) height))
1672 {
1673 handleDisplayResize (mLastColorDepth,
1674 mLastAddress,
1675 mLastLineSize,
1676 mLastWidth,
1677 mLastHeight);
1678 return;
1679 }
1680
1681 mpDrv->Connector.pu8Data = (uint8_t *) address;
1682 mpDrv->Connector.cbScanline = lineSize;
1683 mpDrv->Connector.cBits = colorDepth;
1684 mpDrv->Connector.cx = width;
1685 mpDrv->Connector.cy = height;
1686 }
1687 else
1688 {
1689 /* black hole */
1690 mpDrv->Connector.pu8Data = NULL;
1691 mpDrv->Connector.cbScanline = 0;
1692 mpDrv->Connector.cBits = 0;
1693 mpDrv->Connector.cx = 0;
1694 mpDrv->Connector.cy = 0;
1695 }
1696}
1697
1698/**
1699 * Changes the current frame buffer. Called on EMT to avoid both
1700 * race conditions and excessive locking.
1701 *
1702 * @note locks this object for writing
1703 * @thread EMT
1704 */
1705/* static */
1706DECLCALLBACK(int) Display::changeFramebuffer (Display *that, IFramebuffer *aFB,
1707 bool aInternal)
1708{
1709 LogFlowFunc (("\n"));
1710
1711 AssertReturn (that, VERR_INVALID_PARAMETER);
1712 AssertReturn (aFB || aInternal, VERR_INVALID_PARAMETER);
1713
1714 /// @todo (r=dmik) AutoCaller
1715
1716 AutoLock alock (that);
1717
1718 that->mFramebuffer = aFB;
1719 that->mInternalFramebuffer = aInternal;
1720 that->mSupportedAccelOps = 0;
1721
1722 /* determine which acceleration functions are supported by this framebuffer */
1723 if (aFB && !aInternal)
1724 {
1725 HRESULT rc;
1726 BOOL accelSupported = FALSE;
1727 rc = aFB->OperationSupported (
1728 FramebufferAccelerationOperation_SolidFillAcceleration, &accelSupported);
1729 AssertComRC (rc);
1730 if (accelSupported)
1731 that->mSupportedAccelOps |=
1732 FramebufferAccelerationOperation_SolidFillAcceleration;
1733 accelSupported = FALSE;
1734 rc = aFB->OperationSupported (
1735 FramebufferAccelerationOperation_ScreenCopyAcceleration, &accelSupported);
1736 AssertComRC (rc);
1737 if (accelSupported)
1738 that->mSupportedAccelOps |=
1739 FramebufferAccelerationOperation_ScreenCopyAcceleration;
1740 }
1741
1742 that->mParent->consoleVRDPServer()->
1743 SetFramebuffer (aFB, aFB ? VRDP_EXTERNAL_FRAMEBUFFER :
1744 VRDP_INTERNAL_FRAMEBUFFER);
1745
1746 that->updateDisplayData (true /* aCheckParams */);
1747
1748 return VINF_SUCCESS;
1749}
1750
1751/**
1752 * Handle display resize event.
1753 *
1754 * @see PDMIDISPLAYCONNECTOR::pfnResize
1755 */
1756DECLCALLBACK(int) Display::displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
1757 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
1758{
1759 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1760
1761 LogFlowFunc (("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
1762 bpp, pvVRAM, cbLine, cx, cy));
1763
1764 return pDrv->pDisplay->handleDisplayResize(bpp, pvVRAM, cbLine, cx, cy);
1765}
1766
1767/**
1768 * Handle display update.
1769 *
1770 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
1771 */
1772DECLCALLBACK(void) Display::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
1773 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
1774{
1775 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1776
1777#ifdef DEBUG_sunlover
1778 LogFlowFunc (("pDrv->pDisplay->mfVideoAccelEnabled = %d, %d,%d %dx%d\n",
1779 pDrv->pDisplay->mfVideoAccelEnabled, x, y, cx, cy));
1780#endif /* DEBUG_sunlover */
1781
1782 /* This call does update regardless of VBVA status.
1783 * But in VBVA mode this is called only as result of
1784 * pfnUpdateDisplayAll in the VGA device.
1785 */
1786
1787 pDrv->pDisplay->handleDisplayUpdate(x, y, cx, cy);
1788}
1789
1790/**
1791 * Periodic display refresh callback.
1792 *
1793 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
1794 */
1795DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
1796{
1797 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1798
1799#ifdef DEBUG_sunlover
1800 STAM_PROFILE_START(&StatDisplayRefresh, a);
1801#endif /* DEBUG_sunlover */
1802
1803#ifdef DEBUG_sunlover
1804 LogFlowFunc (("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
1805 pDrv->pDisplay->mfVideoAccelEnabled));
1806#endif /* DEBUG_sunlover */
1807
1808 Display *pDisplay = pDrv->pDisplay;
1809
1810 /* Check the resize status. The status can be checked normally because
1811 * the status affects only the EMT.
1812 */
1813 uint32_t u32ResizeStatus = pDisplay->mu32ResizeStatus;
1814
1815 if (u32ResizeStatus == ResizeStatus_UpdateDisplayData)
1816 {
1817 LogFlowFunc (("ResizeStatus_UpdateDisplayData\n"));
1818 /* The framebuffer was resized and display data need to be updated. */
1819 pDisplay->handleResizeCompletedEMT ();
1820 /* Continue with normal processing because the status here is ResizeStatus_Void. */
1821 Assert (pDisplay->mu32ResizeStatus == ResizeStatus_Void);
1822 /* Repaint the display because VM continued to run during the framebuffer resize. */
1823 if (!pDisplay->mFramebuffer.isNull())
1824 pDrv->pUpPort->pfnUpdateDisplayAll(pDrv->pUpPort);
1825 /* Ignore the refresh to replay the logic. */
1826 return;
1827 }
1828 else if (u32ResizeStatus == ResizeStatus_InProgress)
1829 {
1830 /* The framebuffer is being resized. Do not call the VGA device back. Immediately return. */
1831 LogFlowFunc (("ResizeStatus_InProcess\n"));
1832 return;
1833 }
1834
1835 if (pDisplay->mFramebuffer.isNull())
1836 {
1837 /*
1838 * Do nothing in the "black hole" mode to avoid copying guest
1839 * video memory to the frame buffer
1840 */
1841 }
1842 else
1843 {
1844 if (pDisplay->mfPendingVideoAccelEnable)
1845 {
1846 /* Acceleration was enabled while machine was not yet running
1847 * due to restoring from saved state. Update entire display and
1848 * actually enable acceleration.
1849 */
1850 Assert(pDisplay->mpPendingVbvaMemory);
1851
1852 /* Acceleration can not be yet enabled.*/
1853 Assert(pDisplay->mpVbvaMemory == NULL);
1854 Assert(!pDisplay->mfVideoAccelEnabled);
1855
1856 if (pDisplay->mfMachineRunning)
1857 {
1858 pDisplay->VideoAccelEnable (pDisplay->mfPendingVideoAccelEnable,
1859 pDisplay->mpPendingVbvaMemory);
1860
1861 /* Reset the pending state. */
1862 pDisplay->mfPendingVideoAccelEnable = false;
1863 pDisplay->mpPendingVbvaMemory = NULL;
1864 }
1865 }
1866 else
1867 {
1868 Assert(pDisplay->mpPendingVbvaMemory == NULL);
1869
1870 if (pDisplay->mfVideoAccelEnabled)
1871 {
1872 Assert(pDisplay->mpVbvaMemory);
1873 pDisplay->VideoAccelFlush ();
1874 }
1875 else
1876 {
1877 Assert(pDrv->Connector.pu8Data);
1878 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
1879 }
1880 }
1881#ifdef VRDP_MC
1882 /* Inform to VRDP server that the current display update sequence is
1883 * completed. At this moment the framebuffer memory contains a definite
1884 * image, that is synchronized with the orders already sent to VRDP client.
1885 * The server can now process redraw requests from clients or initial
1886 * fullscreen updates for new clients.
1887 */
1888 Assert (pDisplay->mParent && pDisplay->mParent->consoleVRDPServer());
1889 pDisplay->mParent->consoleVRDPServer()->SendUpdate (NULL, 0);
1890#endif /* VRDP_MC */
1891 }
1892
1893#ifdef DEBUG_sunlover
1894 STAM_PROFILE_STOP(&StatDisplayRefresh, a);
1895#endif /* DEBUG_sunlover */
1896}
1897
1898/**
1899 * Reset notification
1900 *
1901 * @see PDMIDISPLAYCONNECTOR::pfnReset
1902 */
1903DECLCALLBACK(void) Display::displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
1904{
1905 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1906
1907 LogFlowFunc (("\n"));
1908
1909 /* Disable VBVA mode. */
1910 pDrv->pDisplay->VideoAccelEnable (false, NULL);
1911}
1912
1913/**
1914 * LFBModeChange notification
1915 *
1916 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
1917 */
1918DECLCALLBACK(void) Display::displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
1919{
1920 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1921
1922 LogFlowFunc (("fEnabled=%d\n", fEnabled));
1923
1924 NOREF(fEnabled);
1925
1926 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
1927 pDrv->pDisplay->VideoAccelEnable (false, NULL);
1928}
1929
1930/**
1931 * Queries an interface to the driver.
1932 *
1933 * @returns Pointer to interface.
1934 * @returns NULL if the interface was not supported by the driver.
1935 * @param pInterface Pointer to this interface structure.
1936 * @param enmInterface The requested interface identification.
1937 */
1938DECLCALLBACK(void *) Display::drvQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
1939{
1940 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1941 PDRVMAINDISPLAY pDrv = PDMINS2DATA(pDrvIns, PDRVMAINDISPLAY);
1942 switch (enmInterface)
1943 {
1944 case PDMINTERFACE_BASE:
1945 return &pDrvIns->IBase;
1946 case PDMINTERFACE_DISPLAY_CONNECTOR:
1947 return &pDrv->Connector;
1948 default:
1949 return NULL;
1950 }
1951}
1952
1953
1954/**
1955 * Destruct a display driver instance.
1956 *
1957 * @returns VBox status.
1958 * @param pDrvIns The driver instance data.
1959 */
1960DECLCALLBACK(void) Display::drvDestruct(PPDMDRVINS pDrvIns)
1961{
1962 PDRVMAINDISPLAY pData = PDMINS2DATA(pDrvIns, PDRVMAINDISPLAY);
1963 LogFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
1964 if (pData->pDisplay)
1965 {
1966 AutoLock displayLock (pData->pDisplay);
1967 pData->pDisplay->mpDrv = NULL;
1968 pData->pDisplay->mpVMMDev = NULL;
1969 pData->pDisplay->mLastAddress = NULL;
1970 pData->pDisplay->mLastLineSize = 0;
1971 pData->pDisplay->mLastColorDepth = 0,
1972 pData->pDisplay->mLastWidth = 0;
1973 pData->pDisplay->mLastHeight = 0;
1974 }
1975}
1976
1977
1978/**
1979 * Construct a display driver instance.
1980 *
1981 * @returns VBox status.
1982 * @param pDrvIns The driver instance data.
1983 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
1984 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
1985 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
1986 * iInstance it's expected to be used a bit in this function.
1987 */
1988DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
1989{
1990 PDRVMAINDISPLAY pData = PDMINS2DATA(pDrvIns, PDRVMAINDISPLAY);
1991 LogFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
1992
1993 /*
1994 * Validate configuration.
1995 */
1996 if (!CFGMR3AreValuesValid(pCfgHandle, "Object\0"))
1997 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
1998 PPDMIBASE pBaseIgnore;
1999 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBaseIgnore);
2000 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
2001 {
2002 AssertMsgFailed(("Configuration error: Not possible to attach anything to this driver!\n"));
2003 return VERR_PDM_DRVINS_NO_ATTACH;
2004 }
2005
2006 /*
2007 * Init Interfaces.
2008 */
2009 pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
2010
2011 pData->Connector.pfnResize = Display::displayResizeCallback;
2012 pData->Connector.pfnUpdateRect = Display::displayUpdateCallback;
2013 pData->Connector.pfnRefresh = Display::displayRefreshCallback;
2014 pData->Connector.pfnReset = Display::displayResetCallback;
2015 pData->Connector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
2016
2017 /*
2018 * Get the IDisplayPort interface of the above driver/device.
2019 */
2020 pData->pUpPort = (PPDMIDISPLAYPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_DISPLAY_PORT);
2021 if (!pData->pUpPort)
2022 {
2023 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
2024 return VERR_PDM_MISSING_INTERFACE_ABOVE;
2025 }
2026
2027 /*
2028 * Get the Display object pointer and update the mpDrv member.
2029 */
2030 void *pv;
2031 rc = CFGMR3QueryPtr(pCfgHandle, "Object", &pv);
2032 if (VBOX_FAILURE(rc))
2033 {
2034 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Vrc\n", rc));
2035 return rc;
2036 }
2037 pData->pDisplay = (Display *)pv; /** @todo Check this cast! */
2038 pData->pDisplay->mpDrv = pData;
2039
2040 /*
2041 * Update our display information according to the framebuffer
2042 */
2043 pData->pDisplay->updateDisplayData();
2044
2045 /*
2046 * Start periodic screen refreshes
2047 */
2048 pData->pUpPort->pfnSetRefreshRate(pData->pUpPort, 20);
2049
2050 return VINF_SUCCESS;
2051}
2052
2053
2054/**
2055 * Display driver registration record.
2056 */
2057const PDMDRVREG Display::DrvReg =
2058{
2059 /* u32Version */
2060 PDM_DRVREG_VERSION,
2061 /* szDriverName */
2062 "MainDisplay",
2063 /* pszDescription */
2064 "Main display driver (Main as in the API).",
2065 /* fFlags */
2066 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2067 /* fClass. */
2068 PDM_DRVREG_CLASS_DISPLAY,
2069 /* cMaxInstances */
2070 ~0,
2071 /* cbInstance */
2072 sizeof(DRVMAINDISPLAY),
2073 /* pfnConstruct */
2074 Display::drvConstruct,
2075 /* pfnDestruct */
2076 Display::drvDestruct,
2077 /* pfnIOCtl */
2078 NULL,
2079 /* pfnPowerOn */
2080 NULL,
2081 /* pfnReset */
2082 NULL,
2083 /* pfnSuspend */
2084 NULL,
2085 /* pfnResume */
2086 NULL,
2087 /* pfnDetach */
2088 NULL
2089};
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