VirtualBox

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

Last change on this file since 2511 was 2417, checked in by vboxsync, 18 years ago

Prevent VRDP updates when framebuffer is being resized.

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

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