VirtualBox

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

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

IFramebuffer cleanup next part:

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