VirtualBox

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

Last change on this file since 14894 was 14772, checked in by vboxsync, 16 years ago

Added vim modelines to aid following coding guidelines, like no tabs,
similar to what is already in the xidl file.

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

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