VirtualBox

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

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

Saved state screenshot API (xTracker 4364).

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