VirtualBox

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

Last change on this file since 2413 was 2266, checked in by vboxsync, 18 years ago

Do not access framebuffer when it is being resized.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.5 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 /* Handle the command.
1083 *
1084 * Guest is responsible for updating the guest video memory.
1085 * The Windows guest does all drawing using Eng*.
1086 *
1087 * For local output, only dirty rectangle information is used
1088 * to update changed areas.
1089 *
1090 * Dirty rectangles are accumulated to exclude overlapping updates and
1091 * group small updates to a larger one.
1092 */
1093
1094 /* Accumulate the update. */
1095 vbvaRgnDirtyRect (&rgn, phdr);
1096
1097 /* Forward the command to VRDP server. */
1098 mParent->consoleVRDPServer()->SendUpdate (phdr, cbCmd);
1099 }
1100
1101 vbvaReleaseCmd (phdr, cbCmd);
1102 }
1103
1104 if (!fFramebufferIsNull)
1105 {
1106 mFramebuffer->Unlock ();
1107 }
1108
1109 if (mu32ResizeStatus == ResizeStatus_Void)
1110 {
1111 /* Draw the framebuffer. */
1112 vbvaRgnUpdateFramebuffer (&rgn);
1113 }
1114}
1115
1116
1117// IDisplay properties
1118/////////////////////////////////////////////////////////////////////////////
1119
1120/**
1121 * Returns the current display width in pixel
1122 *
1123 * @returns COM status code
1124 * @param width Address of result variable.
1125 */
1126STDMETHODIMP Display::COMGETTER(Width) (ULONG *width)
1127{
1128 if (!width)
1129 return E_POINTER;
1130
1131 AutoLock alock (this);
1132 CHECK_READY();
1133
1134 CHECK_CONSOLE_DRV (mpDrv);
1135
1136 *width = mpDrv->Connector.cx;
1137 return S_OK;
1138}
1139
1140/**
1141 * Returns the current display height in pixel
1142 *
1143 * @returns COM status code
1144 * @param height Address of result variable.
1145 */
1146STDMETHODIMP Display::COMGETTER(Height) (ULONG *height)
1147{
1148 if (!height)
1149 return E_POINTER;
1150
1151 AutoLock alock (this);
1152 CHECK_READY();
1153
1154 CHECK_CONSOLE_DRV (mpDrv);
1155
1156 *height = mpDrv->Connector.cy;
1157 return S_OK;
1158}
1159
1160/**
1161 * Returns the current display color depth in bits
1162 *
1163 * @returns COM status code
1164 * @param colorDepth Address of result variable.
1165 */
1166STDMETHODIMP Display::COMGETTER(ColorDepth) (ULONG *colorDepth)
1167{
1168 if (!colorDepth)
1169 return E_INVALIDARG;
1170
1171 AutoLock alock (this);
1172 CHECK_READY();
1173
1174 CHECK_CONSOLE_DRV (mpDrv);
1175
1176 uint32_t cBits = 0;
1177 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
1178 AssertRC(rc);
1179 *colorDepth = cBits;
1180 return S_OK;
1181}
1182
1183
1184// IDisplay methods
1185/////////////////////////////////////////////////////////////////////////////
1186
1187STDMETHODIMP Display::SetupInternalFramebuffer (ULONG depth)
1188{
1189 LogFlowFunc (("\n"));
1190
1191 AutoLock lock (this);
1192 CHECK_READY();
1193
1194 /*
1195 * Create an internal framebuffer only if depth is not zero. Otherwise, we
1196 * reset back to the "black hole" state as it was at Display construction.
1197 */
1198 ComPtr <IFramebuffer> frameBuf;
1199 if (depth)
1200 {
1201 ComObjPtr <InternalFramebuffer> internal;
1202 internal.createObject();
1203 internal->init (640, 480, depth);
1204 frameBuf = internal; // query interface
1205 }
1206
1207 Console::SafeVMPtrQuiet pVM (mParent);
1208 if (pVM.isOk())
1209 {
1210 /* Must leave the lock here because the changeFramebuffer will also obtain it. */
1211 lock.leave ();
1212
1213 /* send request to the EMT thread */
1214 PVMREQ pReq = NULL;
1215 int vrc = VMR3ReqCall (pVM, &pReq, RT_INDEFINITE_WAIT,
1216 (PFNRT) changeFramebuffer, 3,
1217 this, static_cast <IFramebuffer *> (frameBuf),
1218 true /* aInternal */);
1219 if (VBOX_SUCCESS (vrc))
1220 vrc = pReq->iStatus;
1221 VMR3ReqFree (pReq);
1222
1223 lock.enter ();
1224
1225 ComAssertRCRet (vrc, E_FAIL);
1226 }
1227 else
1228 {
1229 /* No VM is created (VM is powered off), do a direct call */
1230 int vrc = changeFramebuffer (this, frameBuf, true /* aInternal */);
1231 ComAssertRCRet (vrc, E_FAIL);
1232 }
1233
1234 return S_OK;
1235}
1236
1237STDMETHODIMP Display::LockFramebuffer (BYTE **address)
1238{
1239 if (!address)
1240 return E_POINTER;
1241
1242 AutoLock lock(this);
1243 CHECK_READY();
1244
1245 /* only allowed for internal framebuffers */
1246 if (mInternalFramebuffer && !mFramebufferOpened && !mFramebuffer.isNull())
1247 {
1248 CHECK_CONSOLE_DRV (mpDrv);
1249
1250 mFramebuffer->Lock();
1251 mFramebufferOpened = true;
1252 *address = mpDrv->Connector.pu8Data;
1253 return S_OK;
1254 }
1255
1256 return setError (E_FAIL,
1257 tr ("Framebuffer locking is allowed only for the internal framebuffer"));
1258}
1259
1260STDMETHODIMP Display::UnlockFramebuffer()
1261{
1262 AutoLock lock(this);
1263 CHECK_READY();
1264
1265 if (mFramebufferOpened)
1266 {
1267 CHECK_CONSOLE_DRV (mpDrv);
1268
1269 mFramebuffer->Unlock();
1270 mFramebufferOpened = false;
1271 return S_OK;
1272 }
1273
1274 return setError (E_FAIL,
1275 tr ("Framebuffer locking is allowed only for the internal framebuffer"));
1276}
1277
1278STDMETHODIMP Display::RegisterExternalFramebuffer (IFramebuffer *frameBuf)
1279{
1280 LogFlowFunc (("\n"));
1281
1282 if (!frameBuf)
1283 return E_POINTER;
1284
1285 AutoLock lock (this);
1286 CHECK_READY();
1287
1288 Console::SafeVMPtrQuiet pVM (mParent);
1289 if (pVM.isOk())
1290 {
1291 /* Must leave the lock here because the changeFramebuffer will also obtain it. */
1292 lock.leave ();
1293
1294 /* send request to the EMT thread */
1295 PVMREQ pReq = NULL;
1296 int vrc = VMR3ReqCall (pVM, &pReq, RT_INDEFINITE_WAIT,
1297 (PFNRT) changeFramebuffer, 3,
1298 this, frameBuf, false /* aInternal */);
1299 if (VBOX_SUCCESS (vrc))
1300 vrc = pReq->iStatus;
1301 VMR3ReqFree (pReq);
1302
1303 lock.enter ();
1304
1305 ComAssertRCRet (vrc, E_FAIL);
1306 }
1307 else
1308 {
1309 /* No VM is created (VM is powered off), do a direct call */
1310 int vrc = changeFramebuffer (this, frameBuf, false /* aInternal */);
1311 ComAssertRCRet (vrc, E_FAIL);
1312 }
1313
1314 return S_OK;
1315}
1316
1317STDMETHODIMP Display::SetVideoModeHint(ULONG aWidth, ULONG aHeight, ULONG aColorDepth)
1318{
1319 AutoLock lock(this);
1320 CHECK_READY();
1321
1322 CHECK_CONSOLE_DRV (mpDrv);
1323
1324 /*
1325 * Do some rough checks for valid input
1326 */
1327 ULONG width = aWidth;
1328 if (!width)
1329 width = mpDrv->Connector.cx;
1330 ULONG height = aHeight;
1331 if (!height)
1332 height = mpDrv->Connector.cy;
1333 ULONG bpp = aColorDepth;
1334 if (!bpp)
1335 {
1336 uint32_t cBits = 0;
1337 int rc = mpDrv->pUpPort->pfnQueryColorDepth(mpDrv->pUpPort, &cBits);
1338 AssertRC(rc);
1339 bpp = cBits;
1340 }
1341 ULONG vramSize;
1342 mParent->machine()->COMGETTER(VRAMSize)(&vramSize);
1343 /* enough VRAM? */
1344 if ((width * height * (bpp / 8)) > (vramSize * 1024 * 1024))
1345 return setError(E_FAIL, tr("Not enough VRAM for the selected video mode"));
1346
1347 if (mParent->getVMMDev())
1348 mParent->getVMMDev()->getVMMDevPort()->pfnRequestDisplayChange(mParent->getVMMDev()->getVMMDevPort(), aWidth, aHeight, aColorDepth);
1349 return S_OK;
1350}
1351
1352STDMETHODIMP Display::TakeScreenShot (BYTE *address, ULONG width, ULONG height)
1353{
1354 /// @todo (r=dmik) this function may take too long to complete if the VM
1355 // is doing something like saving state right now. Which, in case if it
1356 // is called on the GUI thread, will make it unresponsive. We should
1357 // check the machine state here (by enclosing the check and VMRequCall
1358 // within the Console lock to make it atomic).
1359
1360 LogFlowFuncEnter();
1361 LogFlowFunc (("address=%p, width=%d, height=%d\n",
1362 address, width, height));
1363
1364 if (!address)
1365 return E_POINTER;
1366 if (!width || !height)
1367 return E_INVALIDARG;
1368
1369 AutoLock lock(this);
1370 CHECK_READY();
1371
1372 CHECK_CONSOLE_DRV (mpDrv);
1373
1374 Console::SafeVMPtr pVM (mParent);
1375 CheckComRCReturnRC (pVM.rc());
1376
1377 HRESULT rc = S_OK;
1378
1379 LogFlowFunc (("Sending SCREENSHOT request\n"));
1380
1381 /*
1382 * First try use the graphics device features for making a snapshot.
1383 * This does not support streatching, is an optional feature (returns not supported).
1384 *
1385 * Note: It may cause a display resize. Watch out for deadlocks.
1386 */
1387 int rcVBox = VERR_NOT_SUPPORTED;
1388 if ( mpDrv->Connector.cx == width
1389 && mpDrv->Connector.cy == height)
1390 {
1391 PVMREQ pReq;
1392 size_t cbData = RT_ALIGN_Z(width, 4) * 4 * height;
1393 rcVBox = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT,
1394 (PFNRT)mpDrv->pUpPort->pfnSnapshot, 6, mpDrv->pUpPort,
1395 address, cbData, NULL, NULL, NULL);
1396 if (VBOX_SUCCESS(rcVBox))
1397 {
1398 rcVBox = pReq->iStatus;
1399 VMR3ReqFree(pReq);
1400 }
1401 }
1402
1403 /*
1404 * If the function returns not supported, or if streaching is requested,
1405 * we'll have to do all the work ourselves using the framebuffer data.
1406 */
1407 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
1408 {
1409 /** @todo implement snapshot streching and generic snapshot fallback. */
1410 rc = setError (E_NOTIMPL, tr ("This feature is not implemented"));
1411 }
1412 else if (VBOX_FAILURE(rcVBox))
1413 rc = setError (E_FAIL,
1414 tr ("Could not take a screenshot (%Vrc)"), rcVBox);
1415
1416 LogFlowFunc (("rc=%08X\n", rc));
1417 LogFlowFuncLeave();
1418 return rc;
1419}
1420
1421STDMETHODIMP Display::DrawToScreen (BYTE *address, ULONG x, ULONG y,
1422 ULONG width, ULONG height)
1423{
1424 /// @todo (r=dmik) this function may take too long to complete if the VM
1425 // is doing something like saving state right now. Which, in case if it
1426 // is called on the GUI thread, will make it unresponsive. We should
1427 // check the machine state here (by enclosing the check and VMRequCall
1428 // within the Console lock to make it atomic).
1429
1430 LogFlowFuncEnter();
1431 LogFlowFunc (("address=%p, x=%d, y=%d, width=%d, height=%d\n",
1432 address, x, y, width, height));
1433
1434 if (!address)
1435 return E_POINTER;
1436 if (!width || !height)
1437 return E_INVALIDARG;
1438
1439 AutoLock lock(this);
1440 CHECK_READY();
1441
1442 CHECK_CONSOLE_DRV (mpDrv);
1443
1444 Console::SafeVMPtr pVM (mParent);
1445 CheckComRCReturnRC (pVM.rc());
1446
1447 /*
1448 * Again we're lazy and make the graphics device do all the
1449 * dirty convertion work.
1450 */
1451 PVMREQ pReq;
1452 int rcVBox = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT,
1453 (PFNRT)mpDrv->pUpPort->pfnDisplayBlt, 6, mpDrv->pUpPort,
1454 address, x, y, width, height);
1455 if (VBOX_SUCCESS(rcVBox))
1456 {
1457 rcVBox = pReq->iStatus;
1458 VMR3ReqFree(pReq);
1459 }
1460
1461 /*
1462 * If the function returns not supported, we'll have to do all the
1463 * work ourselves using the framebuffer.
1464 */
1465 HRESULT rc = S_OK;
1466 if (rcVBox == VERR_NOT_SUPPORTED || rcVBox == VERR_NOT_IMPLEMENTED)
1467 {
1468 /** @todo implement generic fallback for screen blitting. */
1469 rc = E_NOTIMPL;
1470 }
1471 else if (VBOX_FAILURE(rcVBox))
1472 rc = setError (E_FAIL,
1473 tr ("Could not draw to the screen (%Vrc)"), rcVBox);
1474//@todo
1475// else
1476// {
1477// /* All ok. Redraw the screen. */
1478// handleDisplayUpdate (x, y, width, height);
1479// }
1480
1481 LogFlowFunc (("rc=%08X\n", rc));
1482 LogFlowFuncLeave();
1483 return rc;
1484}
1485
1486/**
1487 * Does a full invalidation of the VM display and instructs the VM
1488 * to update it immediately.
1489 *
1490 * @returns COM status code
1491 */
1492STDMETHODIMP Display::InvalidateAndUpdate()
1493{
1494 LogFlowFuncEnter();
1495
1496 AutoLock lock(this);
1497 CHECK_READY();
1498
1499 CHECK_CONSOLE_DRV (mpDrv);
1500
1501 Console::SafeVMPtr pVM (mParent);
1502 CheckComRCReturnRC (pVM.rc());
1503
1504 HRESULT rc = S_OK;
1505
1506 LogFlowFunc (("Sending DPYUPDATE request\n"));
1507
1508 /* pdm.h says that this has to be called from the EMT thread */
1509 PVMREQ pReq;
1510 int rcVBox = VMR3ReqCallVoid(pVM, &pReq, RT_INDEFINITE_WAIT,
1511 (PFNRT)mpDrv->pUpPort->pfnUpdateDisplayAll, 1, mpDrv->pUpPort);
1512 if (VBOX_SUCCESS(rcVBox))
1513 VMR3ReqFree(pReq);
1514
1515 if (VBOX_FAILURE(rcVBox))
1516 rc = setError (E_FAIL,
1517 tr ("Could not invalidate and update the screen (%Vrc)"), rcVBox);
1518
1519 LogFlowFunc (("rc=%08X\n", rc));
1520 LogFlowFuncLeave();
1521 return rc;
1522}
1523
1524/**
1525 * Notification that the framebuffer has completed the
1526 * asynchronous resize processing
1527 *
1528 * @returns COM status code
1529 */
1530STDMETHODIMP Display::ResizeCompleted()
1531{
1532 LogFlowFunc (("\n"));
1533
1534 /// @todo (dmik) can we AutoLock alock (this); here?
1535 // do it when we switch this class to VirtualBoxBase_NEXT.
1536 // This will require general code review and may add some details.
1537 // In particular, we may want to check whether EMT is really waiting for
1538 // this notification, etc. It might be also good to obey the caller to make
1539 // sure this method is not called from more than one thread at a time
1540 // (and therefore don't use Display lock at all here to save some
1541 // milliseconds).
1542 CHECK_READY();
1543
1544 /* this is only valid for external framebuffers */
1545 if (mInternalFramebuffer)
1546 return setError (E_FAIL,
1547 tr ("Resize completed notification is valid only "
1548 "for external framebuffers"));
1549
1550 /* Set the flag indicating that the resize has completed and display data need to be updated. */
1551 bool f = ASMAtomicCmpXchgU32 (&mu32ResizeStatus, ResizeStatus_UpdateDisplayData, ResizeStatus_InProgress);
1552 AssertRelease(f);NOREF(f);
1553
1554 return S_OK;
1555}
1556
1557/**
1558 * Notification that the framebuffer has completed the
1559 * asynchronous update processing
1560 *
1561 * @returns COM status code
1562 */
1563STDMETHODIMP Display::UpdateCompleted()
1564{
1565 LogFlowFunc (("\n"));
1566
1567 /// @todo (dmik) can we AutoLock alock (this); here?
1568 // do it when we switch this class to VirtualBoxBase_NEXT.
1569 // Tthis will require general code review and may add some details.
1570 // In particular, we may want to check whether EMT is really waiting for
1571 // this notification, etc. It might be also good to obey the caller to make
1572 // sure this method is not called from more than one thread at a time
1573 // (and therefore don't use Display lock at all here to save some
1574 // milliseconds).
1575 CHECK_READY();
1576
1577 /* this is only valid for external framebuffers */
1578 if (mInternalFramebuffer)
1579 return setError (E_FAIL,
1580 tr ("Resize completed notification is valid only "
1581 "for external framebuffers"));
1582
1583 mFramebuffer->Lock();
1584 /* signal our semaphore */
1585 RTSemEventMultiSignal(mUpdateSem);
1586 mFramebuffer->Unlock();
1587
1588 return S_OK;
1589}
1590
1591// private methods
1592/////////////////////////////////////////////////////////////////////////////
1593
1594/**
1595 * Helper to update the display information from the framebuffer.
1596 *
1597 * @param aCheckParams true to compare the parameters of the current framebuffer
1598 * and the new one and issue handleDisplayResize()
1599 * if they differ.
1600 * @thread EMT
1601 */
1602void Display::updateDisplayData (bool aCheckParams /* = false */)
1603{
1604 /* the driver might not have been constructed yet */
1605 if (!mpDrv)
1606 return;
1607
1608#if DEBUG
1609 /*
1610 * Sanity check. Note that this method may be called on EMT after Console
1611 * has started the power down procedure (but before our #drvDestruct() is
1612 * called, in which case pVM will aleady be NULL but mpDrv will not). Since
1613 * we don't really need pVM to proceed, we avoid this check in the release
1614 * build to save some ms (necessary to construct SafeVMPtrQuiet) in this
1615 * time-critical method.
1616 */
1617 Console::SafeVMPtrQuiet pVM (mParent);
1618 if (pVM.isOk())
1619 VM_ASSERT_EMT (pVM.raw());
1620#endif
1621
1622 if (mFramebuffer)
1623 {
1624 HRESULT rc;
1625 BYTE *address = 0;
1626 rc = mFramebuffer->COMGETTER(Address) (&address);
1627 AssertComRC (rc);
1628 ULONG lineSize = 0;
1629 rc = mFramebuffer->COMGETTER(LineSize) (&lineSize);
1630 AssertComRC (rc);
1631 ULONG colorDepth = 0;
1632 rc = mFramebuffer->COMGETTER(ColorDepth) (&colorDepth);
1633 AssertComRC (rc);
1634 ULONG width = 0;
1635 rc = mFramebuffer->COMGETTER(Width) (&width);
1636 AssertComRC (rc);
1637 ULONG height = 0;
1638 rc = mFramebuffer->COMGETTER(Height) (&height);
1639 AssertComRC (rc);
1640
1641 /*
1642 * Check current parameters with new ones and issue handleDisplayResize()
1643 * to let the new frame buffer adjust itself properly. Note that it will
1644 * result into a recursive updateDisplayData() call but with
1645 * aCheckOld = false.
1646 */
1647 if (aCheckParams &&
1648 (mLastAddress != address ||
1649 mLastLineSize != lineSize ||
1650 mLastColorDepth != colorDepth ||
1651 mLastWidth != (int) width ||
1652 mLastHeight != (int) height))
1653 {
1654 handleDisplayResize (mLastColorDepth,
1655 mLastAddress,
1656 mLastLineSize,
1657 mLastWidth,
1658 mLastHeight);
1659 return;
1660 }
1661
1662 mpDrv->Connector.pu8Data = (uint8_t *) address;
1663 mpDrv->Connector.cbScanline = lineSize;
1664 mpDrv->Connector.cBits = colorDepth;
1665 mpDrv->Connector.cx = width;
1666 mpDrv->Connector.cy = height;
1667 }
1668 else
1669 {
1670 /* black hole */
1671 mpDrv->Connector.pu8Data = NULL;
1672 mpDrv->Connector.cbScanline = 0;
1673 mpDrv->Connector.cBits = 0;
1674 mpDrv->Connector.cx = 0;
1675 mpDrv->Connector.cy = 0;
1676 }
1677}
1678
1679/**
1680 * Changes the current frame buffer. Called on EMT to avoid both
1681 * race conditions and excessive locking.
1682 *
1683 * @note locks this object for writing
1684 * @thread EMT
1685 */
1686/* static */
1687DECLCALLBACK(int) Display::changeFramebuffer (Display *that, IFramebuffer *aFB,
1688 bool aInternal)
1689{
1690 LogFlowFunc (("\n"));
1691
1692 AssertReturn (that, VERR_INVALID_PARAMETER);
1693 AssertReturn (aFB || aInternal, VERR_INVALID_PARAMETER);
1694
1695 /// @todo (r=dmik) AutoCaller
1696
1697 AutoLock alock (that);
1698
1699 that->mFramebuffer = aFB;
1700 that->mInternalFramebuffer = aInternal;
1701 that->mSupportedAccelOps = 0;
1702
1703 /* determine which acceleration functions are supported by this framebuffer */
1704 if (aFB && !aInternal)
1705 {
1706 HRESULT rc;
1707 BOOL accelSupported = FALSE;
1708 rc = aFB->OperationSupported (
1709 FramebufferAccelerationOperation_SolidFillAcceleration, &accelSupported);
1710 AssertComRC (rc);
1711 if (accelSupported)
1712 that->mSupportedAccelOps |=
1713 FramebufferAccelerationOperation_SolidFillAcceleration;
1714 accelSupported = FALSE;
1715 rc = aFB->OperationSupported (
1716 FramebufferAccelerationOperation_ScreenCopyAcceleration, &accelSupported);
1717 AssertComRC (rc);
1718 if (accelSupported)
1719 that->mSupportedAccelOps |=
1720 FramebufferAccelerationOperation_ScreenCopyAcceleration;
1721 }
1722
1723 that->mParent->consoleVRDPServer()->
1724 SetFramebuffer (aFB, aFB ? VRDP_EXTERNAL_FRAMEBUFFER :
1725 VRDP_INTERNAL_FRAMEBUFFER);
1726
1727 that->updateDisplayData (true /* aCheckParams */);
1728
1729 return VINF_SUCCESS;
1730}
1731
1732/**
1733 * Handle display resize event.
1734 *
1735 * @see PDMIDISPLAYCONNECTOR::pfnResize
1736 */
1737DECLCALLBACK(int) Display::displayResizeCallback(PPDMIDISPLAYCONNECTOR pInterface,
1738 uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
1739{
1740 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1741
1742 LogFlowFunc (("bpp %d, pvVRAM %p, cbLine %d, cx %d, cy %d\n",
1743 bpp, pvVRAM, cbLine, cx, cy));
1744
1745 return pDrv->pDisplay->handleDisplayResize(bpp, pvVRAM, cbLine, cx, cy);
1746}
1747
1748/**
1749 * Handle display update.
1750 *
1751 * @see PDMIDISPLAYCONNECTOR::pfnUpdateRect
1752 */
1753DECLCALLBACK(void) Display::displayUpdateCallback(PPDMIDISPLAYCONNECTOR pInterface,
1754 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
1755{
1756 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1757
1758#ifdef DEBUG_sunlover
1759 LogFlowFunc (("pDrv->pDisplay->mfVideoAccelEnabled = %d, %d,%d %dx%d\n",
1760 pDrv->pDisplay->mfVideoAccelEnabled, x, y, cx, cy));
1761#endif /* DEBUG_sunlover */
1762
1763 /* This call does update regardless of VBVA status.
1764 * But in VBVA mode this is called only as result of
1765 * pfnUpdateDisplayAll in the VGA device.
1766 */
1767
1768 pDrv->pDisplay->handleDisplayUpdate(x, y, cx, cy);
1769}
1770
1771/**
1772 * Periodic display refresh callback.
1773 *
1774 * @see PDMIDISPLAYCONNECTOR::pfnRefresh
1775 */
1776DECLCALLBACK(void) Display::displayRefreshCallback(PPDMIDISPLAYCONNECTOR pInterface)
1777{
1778 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1779
1780#ifdef DEBUG_sunlover
1781 STAM_PROFILE_START(&StatDisplayRefresh, a);
1782#endif /* DEBUG_sunlover */
1783
1784#ifdef DEBUG_sunlover
1785 LogFlowFunc (("pDrv->pDisplay->mfVideoAccelEnabled = %d\n",
1786 pDrv->pDisplay->mfVideoAccelEnabled));
1787#endif /* DEBUG_sunlover */
1788
1789 Display *pDisplay = pDrv->pDisplay;
1790
1791 /* Check the resize status. The status can be checked normally because
1792 * the status affects only the EMT.
1793 */
1794 uint32_t u32ResizeStatus = pDisplay->mu32ResizeStatus;
1795
1796 if (u32ResizeStatus == ResizeStatus_UpdateDisplayData)
1797 {
1798 LogFlowFunc (("ResizeStatus_UpdateDisplayData\n"));
1799 /* The framebuffer was resized and display data need to be updated. */
1800 pDisplay->handleResizeCompletedEMT ();
1801 /* Continue with normal processing because the status here is ResizeStatus_Void. */
1802 Assert (pDisplay->mu32ResizeStatus == ResizeStatus_Void);
1803 /* Repaint the display because VM continued to run during the framebuffer resize. */
1804 if (!pDisplay->mFramebuffer.isNull())
1805 pDrv->pUpPort->pfnUpdateDisplayAll(pDrv->pUpPort);
1806 /* Ignore the refresh to replay the logic. */
1807 return;
1808 }
1809 else if (u32ResizeStatus == ResizeStatus_InProgress)
1810 {
1811 /* The framebuffer is being resized. Do not call the VGA device back. Immediately return. */
1812 LogFlowFunc (("ResizeStatus_InProcess\n"));
1813 return;
1814 }
1815
1816 if (pDisplay->mFramebuffer.isNull())
1817 {
1818 /*
1819 * Do nothing in the "black hole" mode to avoid copying guest
1820 * video memory to the frame buffer
1821 */
1822 }
1823 else
1824 {
1825 if (pDisplay->mfPendingVideoAccelEnable)
1826 {
1827 /* Acceleration was enabled while machine was not yet running
1828 * due to restoring from saved state. Update entire display and
1829 * actually enable acceleration.
1830 */
1831 Assert(pDisplay->mpPendingVbvaMemory);
1832
1833 /* Acceleration can not be yet enabled.*/
1834 Assert(pDisplay->mpVbvaMemory == NULL);
1835 Assert(!pDisplay->mfVideoAccelEnabled);
1836
1837 if (pDisplay->mfMachineRunning)
1838 {
1839 pDisplay->VideoAccelEnable (pDisplay->mfPendingVideoAccelEnable,
1840 pDisplay->mpPendingVbvaMemory);
1841
1842 /* Reset the pending state. */
1843 pDisplay->mfPendingVideoAccelEnable = false;
1844 pDisplay->mpPendingVbvaMemory = NULL;
1845 }
1846 }
1847 else
1848 {
1849 Assert(pDisplay->mpPendingVbvaMemory == NULL);
1850
1851 if (pDisplay->mfVideoAccelEnabled)
1852 {
1853 Assert(pDisplay->mpVbvaMemory);
1854 pDisplay->VideoAccelFlush ();
1855 }
1856 else
1857 {
1858 Assert(pDrv->Connector.pu8Data);
1859 pDrv->pUpPort->pfnUpdateDisplay(pDrv->pUpPort);
1860 }
1861 }
1862#ifdef VRDP_MC
1863 /* Inform to VRDP server that the current display update sequence is
1864 * completed. At this moment the framebuffer memory contains a definite
1865 * image, that is synchronized with the orders already sent to VRDP client.
1866 * The server can now process redraw requests from clients or initial
1867 * fullscreen updates for new clients.
1868 */
1869 Assert (pDisplay->mParent && pDisplay->mParent->consoleVRDPServer());
1870 pDisplay->mParent->consoleVRDPServer()->SendUpdate (NULL, 0);
1871#endif /* VRDP_MC */
1872 }
1873
1874#ifdef DEBUG_sunlover
1875 STAM_PROFILE_STOP(&StatDisplayRefresh, a);
1876#endif /* DEBUG_sunlover */
1877}
1878
1879/**
1880 * Reset notification
1881 *
1882 * @see PDMIDISPLAYCONNECTOR::pfnReset
1883 */
1884DECLCALLBACK(void) Display::displayResetCallback(PPDMIDISPLAYCONNECTOR pInterface)
1885{
1886 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1887
1888 LogFlowFunc (("\n"));
1889
1890 /* Disable VBVA mode. */
1891 pDrv->pDisplay->VideoAccelEnable (false, NULL);
1892}
1893
1894/**
1895 * LFBModeChange notification
1896 *
1897 * @see PDMIDISPLAYCONNECTOR::pfnLFBModeChange
1898 */
1899DECLCALLBACK(void) Display::displayLFBModeChangeCallback(PPDMIDISPLAYCONNECTOR pInterface, bool fEnabled)
1900{
1901 PDRVMAINDISPLAY pDrv = PDMIDISPLAYCONNECTOR_2_MAINDISPLAY(pInterface);
1902
1903 LogFlowFunc (("fEnabled=%d\n", fEnabled));
1904
1905 NOREF(fEnabled);
1906
1907 /* Disable VBVA mode in any case. The guest driver reenables VBVA mode if necessary. */
1908 pDrv->pDisplay->VideoAccelEnable (false, NULL);
1909}
1910
1911/**
1912 * Queries an interface to the driver.
1913 *
1914 * @returns Pointer to interface.
1915 * @returns NULL if the interface was not supported by the driver.
1916 * @param pInterface Pointer to this interface structure.
1917 * @param enmInterface The requested interface identification.
1918 */
1919DECLCALLBACK(void *) Display::drvQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
1920{
1921 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1922 PDRVMAINDISPLAY pDrv = PDMINS2DATA(pDrvIns, PDRVMAINDISPLAY);
1923 switch (enmInterface)
1924 {
1925 case PDMINTERFACE_BASE:
1926 return &pDrvIns->IBase;
1927 case PDMINTERFACE_DISPLAY_CONNECTOR:
1928 return &pDrv->Connector;
1929 default:
1930 return NULL;
1931 }
1932}
1933
1934
1935/**
1936 * Destruct a display driver instance.
1937 *
1938 * @returns VBox status.
1939 * @param pDrvIns The driver instance data.
1940 */
1941DECLCALLBACK(void) Display::drvDestruct(PPDMDRVINS pDrvIns)
1942{
1943 PDRVMAINDISPLAY pData = PDMINS2DATA(pDrvIns, PDRVMAINDISPLAY);
1944 LogFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
1945 if (pData->pDisplay)
1946 {
1947 AutoLock displayLock (pData->pDisplay);
1948 pData->pDisplay->mpDrv = NULL;
1949 pData->pDisplay->mpVMMDev = NULL;
1950 pData->pDisplay->mLastAddress = NULL;
1951 pData->pDisplay->mLastLineSize = 0;
1952 pData->pDisplay->mLastColorDepth = 0,
1953 pData->pDisplay->mLastWidth = 0;
1954 pData->pDisplay->mLastHeight = 0;
1955 }
1956}
1957
1958
1959/**
1960 * Construct a display driver instance.
1961 *
1962 * @returns VBox status.
1963 * @param pDrvIns The driver instance data.
1964 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
1965 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
1966 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
1967 * iInstance it's expected to be used a bit in this function.
1968 */
1969DECLCALLBACK(int) Display::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
1970{
1971 PDRVMAINDISPLAY pData = PDMINS2DATA(pDrvIns, PDRVMAINDISPLAY);
1972 LogFlowFunc (("iInstance=%d\n", pDrvIns->iInstance));
1973
1974 /*
1975 * Validate configuration.
1976 */
1977 if (!CFGMR3AreValuesValid(pCfgHandle, "Object\0"))
1978 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
1979 PPDMIBASE pBaseIgnore;
1980 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBaseIgnore);
1981 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
1982 {
1983 AssertMsgFailed(("Configuration error: Not possible to attach anything to this driver!\n"));
1984 return VERR_PDM_DRVINS_NO_ATTACH;
1985 }
1986
1987 /*
1988 * Init Interfaces.
1989 */
1990 pDrvIns->IBase.pfnQueryInterface = Display::drvQueryInterface;
1991
1992 pData->Connector.pfnResize = Display::displayResizeCallback;
1993 pData->Connector.pfnUpdateRect = Display::displayUpdateCallback;
1994 pData->Connector.pfnRefresh = Display::displayRefreshCallback;
1995 pData->Connector.pfnReset = Display::displayResetCallback;
1996 pData->Connector.pfnLFBModeChange = Display::displayLFBModeChangeCallback;
1997
1998 /*
1999 * Get the IDisplayPort interface of the above driver/device.
2000 */
2001 pData->pUpPort = (PPDMIDISPLAYPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_DISPLAY_PORT);
2002 if (!pData->pUpPort)
2003 {
2004 AssertMsgFailed(("Configuration error: No display port interface above!\n"));
2005 return VERR_PDM_MISSING_INTERFACE_ABOVE;
2006 }
2007
2008 /*
2009 * Get the Display object pointer and update the mpDrv member.
2010 */
2011 void *pv;
2012 rc = CFGMR3QueryPtr(pCfgHandle, "Object", &pv);
2013 if (VBOX_FAILURE(rc))
2014 {
2015 AssertMsgFailed(("Configuration error: No/bad \"Object\" value! rc=%Vrc\n", rc));
2016 return rc;
2017 }
2018 pData->pDisplay = (Display *)pv; /** @todo Check this cast! */
2019 pData->pDisplay->mpDrv = pData;
2020
2021 /*
2022 * Update our display information according to the framebuffer
2023 */
2024 pData->pDisplay->updateDisplayData();
2025
2026 /*
2027 * Start periodic screen refreshes
2028 */
2029 pData->pUpPort->pfnSetRefreshRate(pData->pUpPort, 20);
2030
2031 return VINF_SUCCESS;
2032}
2033
2034
2035/**
2036 * Display driver registration record.
2037 */
2038const PDMDRVREG Display::DrvReg =
2039{
2040 /* u32Version */
2041 PDM_DRVREG_VERSION,
2042 /* szDriverName */
2043 "MainDisplay",
2044 /* pszDescription */
2045 "Main display driver (Main as in the API).",
2046 /* fFlags */
2047 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2048 /* fClass. */
2049 PDM_DRVREG_CLASS_DISPLAY,
2050 /* cMaxInstances */
2051 ~0,
2052 /* cbInstance */
2053 sizeof(DRVMAINDISPLAY),
2054 /* pfnConstruct */
2055 Display::drvConstruct,
2056 /* pfnDestruct */
2057 Display::drvDestruct,
2058 /* pfnIOCtl */
2059 NULL,
2060 /* pfnPowerOn */
2061 NULL,
2062 /* pfnReset */
2063 NULL,
2064 /* pfnSuspend */
2065 NULL,
2066 /* pfnResume */
2067 NULL,
2068 /* pfnDetach */
2069 NULL
2070};
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