VirtualBox

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

Last change on this file since 24405 was 24405, checked in by vboxsync, 15 years ago

Put screenshot to saved state (incomplete and disabled).

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