VirtualBox

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

Last change on this file since 2258 was 2219, checked in by vboxsync, 18 years ago

Repaint display when framebuffer completed an asynchronous resize.

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