VirtualBox

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

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

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