VirtualBox

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

Last change on this file since 11162 was 10760, checked in by vboxsync, 16 years ago

Fixed a bunch of changeFramebuffer invocations on EMT (VMR3ReqCall) that has had the wrong parameter count since r22071 (multi monitor support). Showed up now with Qt4 on the mac, hitting some assertion on the screen id at line 1907.

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