VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxSDL/Framebuffer.cpp@ 97364

Last change on this file since 97364 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.2 KB
Line 
1/* $Id: Framebuffer.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * VBoxSDL - Implementation of VBoxSDLFB (SDL framebuffer) class
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <VBox/com/com.h>
29#include <VBox/com/array.h>
30#include <VBox/com/string.h>
31#include <VBox/com/Guid.h>
32#include <VBox/com/ErrorInfo.h>
33#include <VBox/com/VirtualBox.h>
34
35#include <iprt/stream.h>
36#include <iprt/env.h>
37
38#ifdef RT_OS_OS2
39# undef RT_MAX
40// from <iprt/cdefs.h>
41# define RT_MAX(Value1, Value2) ((Value1) >= (Value2) ? (Value1) : (Value2))
42#endif
43
44using namespace com;
45
46#define LOG_GROUP LOG_GROUP_GUI
47#include <VBox/err.h>
48#include <VBox/log.h>
49
50#include "VBoxSDL.h"
51#include "Framebuffer.h"
52#include "Ico64x01.h"
53
54#if defined(RT_OS_WINDOWS) || defined(RT_OS_LINUX)
55# ifdef _MSC_VER
56# pragma warning(push)
57# pragma warning(disable: 4121) /* warning C4121: 'SDL_SysWMmsg' : alignment of a member was sensitive to packing*/
58# endif
59# include <SDL_syswm.h> /* for SDL_GetWMInfo() */
60# ifdef _MSC_VER
61# pragma warning(pop)
62# endif
63#endif
64
65#if defined(VBOX_WITH_XPCOM)
66NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxSDLFB, IFramebuffer)
67NS_DECL_CLASSINFO(VBoxSDLFB)
68NS_IMPL_THREADSAFE_ISUPPORTS2_CI(VBoxSDLFBOverlay, IFramebufferOverlay, IFramebuffer)
69NS_DECL_CLASSINFO(VBoxSDLFBOverlay)
70#endif
71
72#ifdef VBOX_SECURELABEL
73/* function pointers */
74extern "C"
75{
76DECLSPEC int (SDLCALL *pTTF_Init)(void);
77DECLSPEC TTF_Font* (SDLCALL *pTTF_OpenFont)(const char *file, int ptsize);
78DECLSPEC SDL_Surface* (SDLCALL *pTTF_RenderUTF8_Solid)(TTF_Font *font, const char *text, SDL_Color fg);
79DECLSPEC SDL_Surface* (SDLCALL *pTTF_RenderUTF8_Blended)(TTF_Font *font, const char *text, SDL_Color fg);
80DECLSPEC void (SDLCALL *pTTF_CloseFont)(TTF_Font *font);
81DECLSPEC void (SDLCALL *pTTF_Quit)(void);
82}
83#endif /* VBOX_SECURELABEL */
84
85static bool gfSdlInitialized = false; /**< if SDL was initialized */
86static SDL_Surface *gWMIcon = NULL; /**< the application icon */
87static RTNATIVETHREAD gSdlNativeThread = NIL_RTNATIVETHREAD; /**< the SDL thread */
88
89//
90// Constructor / destructor
91//
92
93VBoxSDLFB::VBoxSDLFB()
94{
95}
96
97HRESULT VBoxSDLFB::FinalConstruct()
98{
99 return 0;
100}
101
102void VBoxSDLFB::FinalRelease()
103{
104 return;
105}
106
107/**
108 * SDL framebuffer constructor. It is called from the main
109 * (i.e. SDL) thread. Therefore it is safe to use SDL calls
110 * here.
111 * @param fFullscreen flag whether we start in fullscreen mode
112 * @param fResizable flag whether the SDL window should be resizable
113 * @param fShowSDLConfig flag whether we print out SDL settings
114 * @param fKeepHostRes flag whether we switch the host screen resolution
115 * when switching to fullscreen or not
116 * @param iFixedWidth fixed SDL width (-1 means not set)
117 * @param iFixedHeight fixed SDL height (-1 means not set)
118 */
119HRESULT VBoxSDLFB::init(uint32_t uScreenId,
120 bool fFullscreen, bool fResizable, bool fShowSDLConfig,
121 bool fKeepHostRes, uint32_t u32FixedWidth,
122 uint32_t u32FixedHeight, uint32_t u32FixedBPP,
123 bool fUpdateImage)
124{
125 LogFlow(("VBoxSDLFB::VBoxSDLFB\n"));
126
127 mScreenId = uScreenId;
128 mfUpdateImage = fUpdateImage;
129 mScreen = NULL;
130#ifdef VBOX_WITH_SDL2
131 mpWindow = NULL;
132 mpTexture = NULL;
133 mpRenderer = NULL;
134#endif
135 mSurfVRAM = NULL;
136 mfInitialized = false;
137 mfFullscreen = fFullscreen;
138 mfKeepHostRes = fKeepHostRes;
139 mTopOffset = 0;
140 mfResizable = fResizable;
141 mfShowSDLConfig = fShowSDLConfig;
142 mFixedSDLWidth = u32FixedWidth;
143 mFixedSDLHeight = u32FixedHeight;
144 mFixedSDLBPP = u32FixedBPP;
145 mCenterXOffset = 0;
146 mCenterYOffset = 0;
147 /* Start with standard screen dimensions. */
148 mGuestXRes = 640;
149 mGuestYRes = 480;
150 mPtrVRAM = NULL;
151 mBitsPerPixel = 0;
152 mBytesPerLine = 0;
153 mfSameSizeRequested = false;
154#ifdef VBOX_SECURELABEL
155 mLabelFont = NULL;
156 mLabelHeight = 0;
157 mLabelOffs = 0;
158#endif
159
160 mfUpdates = false;
161
162 int rc = RTCritSectInit(&mUpdateLock);
163 AssertMsg(rc == VINF_SUCCESS, ("Error from RTCritSectInit!\n"));
164
165 resizeGuest();
166 Assert(mScreen);
167 mfInitialized = true;
168#ifdef RT_OS_WINDOWS
169 HRESULT hr = CoCreateFreeThreadedMarshaler(this, m_pUnkMarshaler.asOutParam());
170 Log(("CoCreateFreeThreadedMarshaler hr %08X\n", hr)); NOREF(hr);
171#endif
172
173#ifdef VBOX_WITH_SDL2
174 rc = SDL_GetRendererInfo(mpRenderer, &mRenderInfo);
175 if (RT_SUCCESS(rc))
176 {
177 if (fShowSDLConfig)
178 RTPrintf("Render info:\n"
179 " Name: %s\n"
180 " Render flags: 0x%x\n"
181 " SDL video driver: %s\n",
182 mRenderInfo.name,
183 mRenderInfo.flags,
184 RTEnvGet("SDL_VIDEODRIVER"));
185 }
186#endif
187
188 return rc;
189}
190
191VBoxSDLFB::~VBoxSDLFB()
192{
193 LogFlow(("VBoxSDLFB::~VBoxSDLFB\n"));
194 if (mSurfVRAM)
195 {
196 SDL_FreeSurface(mSurfVRAM);
197 mSurfVRAM = NULL;
198 }
199 mScreen = NULL;
200
201#ifdef VBOX_SECURELABEL
202 if (mLabelFont)
203 pTTF_CloseFont(mLabelFont);
204 if (pTTF_Quit)
205 pTTF_Quit();
206#endif
207
208 RTCritSectDelete(&mUpdateLock);
209}
210
211/* static */
212bool VBoxSDLFB::init(bool fShowSDLConfig)
213{
214 LogFlow(("VBoxSDLFB::init\n"));
215
216 /* memorize the thread that inited us, that's the SDL thread */
217 gSdlNativeThread = RTThreadNativeSelf();
218
219#ifdef RT_OS_WINDOWS
220 /* default to DirectX if nothing else set */
221 if (!RTEnvExist("SDL_VIDEODRIVER"))
222 {
223# ifndef VBOX_WITH_SDL2
224 /* Always select the windib driver by default, as the directx one is known to be broken on newer Windows OSes. */
225 RTEnvSet("SDL_VIDEODRIVER", "windib");
226# else
227 RTEnvSet("SDL_VIDEODRIVER", "directx");
228# endif
229 }
230#endif
231#ifdef VBOXSDL_WITH_X11
232 /* On some X servers the mouse is stuck inside the bottom right corner.
233 * See http://wiki.clug.org.za/wiki/QEMU_mouse_not_working */
234 RTEnvSet("SDL_VIDEO_X11_DGAMOUSE", "0");
235#endif
236
237 int rc = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE);
238 if (rc != 0)
239 {
240 RTPrintf("SDL Error: '%s'\n", SDL_GetError());
241 return false;
242 }
243 gfSdlInitialized = true;
244
245#ifdef VBOX_WITH_SDL2
246 RT_NOREF(fShowSDLConfig);
247#else
248 const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
249 AssertPtr(videoInfo);
250 if (videoInfo)
251 {
252 /* output what SDL is capable of */
253 if (fShowSDLConfig)
254 RTPrintf("SDL capabilities:\n"
255 " Hardware surface support: %s\n"
256 " Window manager available: %s\n"
257 " Screen to screen blits accelerated: %s\n"
258 " Screen to screen colorkey blits accelerated: %s\n"
259 " Screen to screen alpha blits accelerated: %s\n"
260 " Memory to screen blits accelerated: %s\n"
261 " Memory to screen colorkey blits accelerated: %s\n"
262 " Memory to screen alpha blits accelerated: %s\n"
263 " Color fills accelerated: %s\n"
264 " Video memory in kilobytes: %d\n"
265 " Optimal bpp mode: %d\n"
266 "SDL video driver: %s\n",
267 videoInfo->hw_available ? "yes" : "no",
268 videoInfo->wm_available ? "yes" : "no",
269 videoInfo->blit_hw ? "yes" : "no",
270 videoInfo->blit_hw_CC ? "yes" : "no",
271 videoInfo->blit_hw_A ? "yes" : "no",
272 videoInfo->blit_sw ? "yes" : "no",
273 videoInfo->blit_sw_CC ? "yes" : "no",
274 videoInfo->blit_sw_A ? "yes" : "no",
275 videoInfo->blit_fill ? "yes" : "no",
276 videoInfo->video_mem,
277 videoInfo->vfmt->BitsPerPixel,
278 RTEnvGet("SDL_VIDEODRIVER"));
279 }
280#endif /* !VBOX_WITH_SDL2 */
281
282#ifndef VBOX_WITH_SDL2
283 gWMIcon = SDL_AllocSurface(SDL_SWSURFACE, 64, 64, 24, 0xff, 0xff00, 0xff0000, 0);
284 /** @todo make it as simple as possible. No PNM interpreter here... */
285 if (gWMIcon)
286 {
287 memcpy(gWMIcon->pixels, g_abIco64x01+32, g_cbIco64x01-32);
288 SDL_WM_SetIcon(gWMIcon, NULL);
289 }
290#endif
291
292 return true;
293}
294
295/**
296 * Terminate SDL
297 *
298 * @remarks must be called from the SDL thread!
299 */
300void VBoxSDLFB::uninit()
301{
302 if (gfSdlInitialized)
303 {
304 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
305 SDL_QuitSubSystem(SDL_INIT_VIDEO);
306
307#ifndef VBOX_WITH_SDL2
308 if (gWMIcon)
309 {
310 SDL_FreeSurface(gWMIcon);
311 gWMIcon = NULL;
312 }
313#endif
314 }
315}
316
317/**
318 * Returns the current framebuffer width in pixels.
319 *
320 * @returns COM status code
321 * @param width Address of result buffer.
322 */
323STDMETHODIMP VBoxSDLFB::COMGETTER(Width)(ULONG *width)
324{
325 LogFlow(("VBoxSDLFB::GetWidth\n"));
326 if (!width)
327 return E_INVALIDARG;
328 *width = mGuestXRes;
329 return S_OK;
330}
331
332/**
333 * Returns the current framebuffer height in pixels.
334 *
335 * @returns COM status code
336 * @param height Address of result buffer.
337 */
338STDMETHODIMP VBoxSDLFB::COMGETTER(Height)(ULONG *height)
339{
340 LogFlow(("VBoxSDLFB::GetHeight\n"));
341 if (!height)
342 return E_INVALIDARG;
343 *height = mGuestYRes;
344 return S_OK;
345}
346
347/**
348 * Return the current framebuffer color depth.
349 *
350 * @returns COM status code
351 * @param bitsPerPixel Address of result variable
352 */
353STDMETHODIMP VBoxSDLFB::COMGETTER(BitsPerPixel)(ULONG *bitsPerPixel)
354{
355 LogFlow(("VBoxSDLFB::GetBitsPerPixel\n"));
356 if (!bitsPerPixel)
357 return E_INVALIDARG;
358 /* get the information directly from the surface in use */
359 Assert(mSurfVRAM);
360 *bitsPerPixel = (ULONG)(mSurfVRAM ? mSurfVRAM->format->BitsPerPixel : 0);
361 return S_OK;
362}
363
364/**
365 * Return the current framebuffer line size in bytes.
366 *
367 * @returns COM status code.
368 * @param lineSize Address of result variable.
369 */
370STDMETHODIMP VBoxSDLFB::COMGETTER(BytesPerLine)(ULONG *bytesPerLine)
371{
372 LogFlow(("VBoxSDLFB::GetBytesPerLine\n"));
373 if (!bytesPerLine)
374 return E_INVALIDARG;
375 /* get the information directly from the surface */
376 Assert(mSurfVRAM);
377 *bytesPerLine = (ULONG)(mSurfVRAM ? mSurfVRAM->pitch : 0);
378 return S_OK;
379}
380
381STDMETHODIMP VBoxSDLFB::COMGETTER(PixelFormat) (BitmapFormat_T *pixelFormat)
382{
383 if (!pixelFormat)
384 return E_POINTER;
385 *pixelFormat = BitmapFormat_BGR;
386 return S_OK;
387}
388
389/**
390 * Returns by how many pixels the guest should shrink its
391 * video mode height values.
392 *
393 * @returns COM status code.
394 * @param heightReduction Address of result variable.
395 */
396STDMETHODIMP VBoxSDLFB::COMGETTER(HeightReduction)(ULONG *heightReduction)
397{
398 if (!heightReduction)
399 return E_POINTER;
400#ifdef VBOX_SECURELABEL
401 *heightReduction = mLabelHeight;
402#else
403 *heightReduction = 0;
404#endif
405 return S_OK;
406}
407
408/**
409 * Returns a pointer to an alpha-blended overlay used for displaying status
410 * icons above the framebuffer.
411 *
412 * @returns COM status code.
413 * @param aOverlay The overlay framebuffer.
414 */
415STDMETHODIMP VBoxSDLFB::COMGETTER(Overlay)(IFramebufferOverlay **aOverlay)
416{
417 if (!aOverlay)
418 return E_POINTER;
419 /* Not yet implemented */
420 *aOverlay = 0;
421 return S_OK;
422}
423
424/**
425 * Returns handle of window where framebuffer context is being drawn
426 *
427 * @returns COM status code.
428 * @param winId Handle of associated window.
429 */
430STDMETHODIMP VBoxSDLFB::COMGETTER(WinId)(LONG64 *winId)
431{
432 if (!winId)
433 return E_POINTER;
434#ifdef RT_OS_DARWIN
435 if (mWinId == NULL) /* (In case it failed the first time.) */
436 mWinId = (intptr_t)VBoxSDLGetDarwinWindowId();
437#endif
438 *winId = mWinId;
439 return S_OK;
440}
441
442STDMETHODIMP VBoxSDLFB::COMGETTER(Capabilities)(ComSafeArrayOut(FramebufferCapabilities_T, aCapabilities))
443{
444 if (ComSafeArrayOutIsNull(aCapabilities))
445 return E_POINTER;
446
447 com::SafeArray<FramebufferCapabilities_T> caps;
448
449 if (mfUpdateImage)
450 {
451 caps.resize(2);
452 caps[0] = FramebufferCapabilities_UpdateImage;
453 caps[1] = FramebufferCapabilities_RenderCursor;
454 }
455 else
456 {
457 caps.resize(1);
458 caps[0] = FramebufferCapabilities_RenderCursor;
459 }
460
461 caps.detachTo(ComSafeArrayOutArg(aCapabilities));
462 return S_OK;
463}
464
465/**
466 * Notify framebuffer of an update.
467 *
468 * @returns COM status code
469 * @param x Update region upper left corner x value.
470 * @param y Update region upper left corner y value.
471 * @param w Update region width in pixels.
472 * @param h Update region height in pixels.
473 * @param finished Address of output flag whether the update
474 * could be fully processed in this call (which
475 * has to return immediately) or VBox should wait
476 * for a call to the update complete API before
477 * continuing with display updates.
478 */
479STDMETHODIMP VBoxSDLFB::NotifyUpdate(ULONG x, ULONG y,
480 ULONG w, ULONG h)
481{
482 /*
483 * The input values are in guest screen coordinates.
484 */
485 LogFlow(("VBoxSDLFB::NotifyUpdate: x = %d, y = %d, w = %d, h = %d\n",
486 x, y, w, h));
487
488#ifdef VBOXSDL_WITH_X11
489 /*
490 * SDL does not allow us to make this call from any other thread than
491 * the main SDL thread (which initialized the video mode). So we have
492 * to send an event to the main SDL thread and process it there. For
493 * sake of simplicity, we encode all information in the event parameters.
494 */
495 SDL_Event event;
496 event.type = SDL_USEREVENT;
497 event.user.code = mScreenId;
498 event.user.type = SDL_USER_EVENT_UPDATERECT;
499 // 16 bit is enough for coordinates
500 event.user.data1 = (void*)(uintptr_t)(x << 16 | y);
501 event.user.data2 = (void*)(uintptr_t)(w << 16 | h);
502 PushNotifyUpdateEvent(&event);
503#else /* !VBOXSDL_WITH_X11 */
504 update(x, y, w, h, true /* fGuestRelative */);
505#endif /* !VBOXSDL_WITH_X11 */
506
507 return S_OK;
508}
509
510STDMETHODIMP VBoxSDLFB::NotifyUpdateImage(ULONG aX,
511 ULONG aY,
512 ULONG aWidth,
513 ULONG aHeight,
514 ComSafeArrayIn(BYTE, aImage))
515{
516 LogFlow(("NotifyUpdateImage: %d,%d %dx%d\n", aX, aY, aWidth, aHeight));
517
518 com::SafeArray<BYTE> image(ComSafeArrayInArg(aImage));
519
520 /* Copy to mSurfVRAM. */
521 SDL_Rect srcRect;
522 SDL_Rect dstRect;
523 srcRect.x = 0;
524 srcRect.y = 0;
525 srcRect.w = (uint16_t)aWidth;
526 srcRect.h = (uint16_t)aHeight;
527 dstRect.x = (int16_t)aX;
528 dstRect.y = (int16_t)aY;
529 dstRect.w = (uint16_t)aWidth;
530 dstRect.h = (uint16_t)aHeight;
531
532 const uint32_t Rmask = 0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0;
533 SDL_Surface *surfSrc = SDL_CreateRGBSurfaceFrom(image.raw(), aWidth, aHeight, 32, aWidth * 4,
534 Rmask, Gmask, Bmask, Amask);
535 if (surfSrc)
536 {
537 RTCritSectEnter(&mUpdateLock);
538 if (mfUpdates)
539 SDL_BlitSurface(surfSrc, &srcRect, mSurfVRAM, &dstRect);
540 RTCritSectLeave(&mUpdateLock);
541
542 SDL_FreeSurface(surfSrc);
543 }
544
545 return NotifyUpdate(aX, aY, aWidth, aHeight);
546}
547
548extern ComPtr<IDisplay> gpDisplay;
549
550STDMETHODIMP VBoxSDLFB::NotifyChange(ULONG aScreenId,
551 ULONG aXOrigin,
552 ULONG aYOrigin,
553 ULONG aWidth,
554 ULONG aHeight)
555{
556 LogRel(("NotifyChange: %d %d,%d %dx%d\n",
557 aScreenId, aXOrigin, aYOrigin, aWidth, aHeight));
558
559 ComPtr<IDisplaySourceBitmap> pSourceBitmap;
560 if (!mfUpdateImage)
561 gpDisplay->QuerySourceBitmap(aScreenId, pSourceBitmap.asOutParam());
562
563 RTCritSectEnter(&mUpdateLock);
564
565 /* Disable screen updates. */
566 mfUpdates = false;
567
568 if (mfUpdateImage)
569 {
570 mGuestXRes = aWidth;
571 mGuestYRes = aHeight;
572 mPtrVRAM = NULL;
573 mBitsPerPixel = 0;
574 mBytesPerLine = 0;
575 }
576 else
577 {
578 /* Save the new bitmap. */
579 mpPendingSourceBitmap = pSourceBitmap;
580 }
581
582 RTCritSectLeave(&mUpdateLock);
583
584 SDL_Event event;
585 event.type = SDL_USEREVENT;
586 event.user.type = SDL_USER_EVENT_NOTIFYCHANGE;
587 event.user.code = mScreenId;
588
589 PushSDLEventForSure(&event);
590
591 RTThreadYield();
592
593 return S_OK;
594}
595
596/**
597 * Returns whether we like the given video mode.
598 *
599 * @returns COM status code
600 * @param width video mode width in pixels
601 * @param height video mode height in pixels
602 * @param bpp video mode bit depth in bits per pixel
603 * @param supported pointer to result variable
604 */
605STDMETHODIMP VBoxSDLFB::VideoModeSupported(ULONG width, ULONG height, ULONG bpp, BOOL *supported)
606{
607 RT_NOREF(bpp);
608
609 if (!supported)
610 return E_POINTER;
611
612 /* are constraints set? */
613 if ( ( (mMaxScreenWidth != ~(uint32_t)0)
614 && (width > mMaxScreenWidth))
615 || ( (mMaxScreenHeight != ~(uint32_t)0)
616 && (height > mMaxScreenHeight)))
617 {
618 /* nope, we don't want that (but still don't freak out if it is set) */
619#ifdef DEBUG
620 RTPrintf("VBoxSDL::VideoModeSupported: we refused mode %dx%dx%d\n", width, height, bpp);
621#endif
622 *supported = false;
623 }
624 else
625 {
626 /* anything will do */
627 *supported = true;
628 }
629 return S_OK;
630}
631
632STDMETHODIMP VBoxSDLFB::GetVisibleRegion(BYTE *aRectangles, ULONG aCount,
633 ULONG *aCountCopied)
634{
635 PRTRECT rects = (PRTRECT)aRectangles;
636
637 if (!rects)
638 return E_POINTER;
639
640 /// @todo
641
642 NOREF(aCount);
643 NOREF(aCountCopied);
644
645 return S_OK;
646}
647
648STDMETHODIMP VBoxSDLFB::SetVisibleRegion(BYTE *aRectangles, ULONG aCount)
649{
650 PRTRECT rects = (PRTRECT)aRectangles;
651
652 if (!rects)
653 return E_POINTER;
654
655 /// @todo
656
657 NOREF(aCount);
658
659 return S_OK;
660}
661
662STDMETHODIMP VBoxSDLFB::ProcessVHWACommand(BYTE *pCommand, LONG enmCmd, BOOL fGuestCmd)
663{
664 RT_NOREF(pCommand, enmCmd, fGuestCmd);
665 return E_NOTIMPL;
666}
667
668STDMETHODIMP VBoxSDLFB::Notify3DEvent(ULONG uType, ComSafeArrayIn(BYTE, aData))
669{
670 RT_NOREF(uType); ComSafeArrayNoRef(aData);
671 return E_NOTIMPL;
672}
673
674//
675// Internal public methods
676//
677
678/* This method runs on the main SDL thread. */
679void VBoxSDLFB::notifyChange(ULONG aScreenId)
680{
681 /* Disable screen updates. */
682 RTCritSectEnter(&mUpdateLock);
683
684 if (!mfUpdateImage && mpPendingSourceBitmap.isNull())
685 {
686 /* Do nothing. Change event already processed. */
687 RTCritSectLeave(&mUpdateLock);
688 return;
689 }
690
691 /* Release the current bitmap and keep the pending one. */
692 mpSourceBitmap = mpPendingSourceBitmap;
693 mpPendingSourceBitmap.setNull();
694
695 RTCritSectLeave(&mUpdateLock);
696
697 if (mpSourceBitmap.isNull())
698 {
699 mPtrVRAM = NULL;
700 mBitsPerPixel = 32;
701 mBytesPerLine = mGuestXRes * 4;
702 }
703 else
704 {
705 BYTE *pAddress = NULL;
706 ULONG ulWidth = 0;
707 ULONG ulHeight = 0;
708 ULONG ulBitsPerPixel = 0;
709 ULONG ulBytesPerLine = 0;
710 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
711
712 mpSourceBitmap->QueryBitmapInfo(&pAddress,
713 &ulWidth,
714 &ulHeight,
715 &ulBitsPerPixel,
716 &ulBytesPerLine,
717 &bitmapFormat);
718
719 if ( mGuestXRes == ulWidth
720 && mGuestYRes == ulHeight
721 && mBitsPerPixel == ulBitsPerPixel
722 && mBytesPerLine == ulBytesPerLine
723 && mPtrVRAM == pAddress
724 )
725 {
726 mfSameSizeRequested = true;
727 }
728 else
729 {
730 mfSameSizeRequested = false;
731 }
732
733 mGuestXRes = ulWidth;
734 mGuestYRes = ulHeight;
735 mPtrVRAM = pAddress;
736 mBitsPerPixel = ulBitsPerPixel;
737 mBytesPerLine = ulBytesPerLine;
738 }
739
740 resizeGuest();
741
742 gpDisplay->InvalidateAndUpdateScreen(aScreenId);
743}
744
745/**
746 * Method that does the actual resize of the guest framebuffer and
747 * then changes the SDL framebuffer setup.
748 */
749void VBoxSDLFB::resizeGuest()
750{
751 LogFlowFunc (("mGuestXRes: %d, mGuestYRes: %d\n", mGuestXRes, mGuestYRes));
752 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(),
753 ("Wrong thread! SDL is not threadsafe!\n"));
754
755 RTCritSectEnter(&mUpdateLock);
756
757 const uint32_t Rmask = 0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0;
758
759 /* first free the current surface */
760 if (mSurfVRAM)
761 {
762 SDL_FreeSurface(mSurfVRAM);
763 mSurfVRAM = NULL;
764 }
765
766 if (mPtrVRAM)
767 {
768 /* Create a source surface from the source bitmap. */
769 mSurfVRAM = SDL_CreateRGBSurfaceFrom(mPtrVRAM, mGuestXRes, mGuestYRes, mBitsPerPixel,
770 mBytesPerLine, Rmask, Gmask, Bmask, Amask);
771 LogFlow(("VBoxSDL:: using the source bitmap\n"));
772 }
773 else
774 {
775 mSurfVRAM = SDL_CreateRGBSurface(SDL_SWSURFACE, mGuestXRes, mGuestYRes, mBitsPerPixel,
776 Rmask, Gmask, Bmask, Amask);
777 LogFlow(("VBoxSDL:: using SDL_SWSURFACE\n"));
778 }
779 LogFlow(("VBoxSDL:: created VRAM surface %p\n", mSurfVRAM));
780
781 if (mfSameSizeRequested)
782 {
783 mfSameSizeRequested = false;
784 LogFlow(("VBoxSDL:: the same resolution requested, skipping the resize.\n"));
785 }
786 else
787 {
788 /* now adjust the SDL resolution */
789 resizeSDL();
790 }
791
792 /* Enable screen updates. */
793 mfUpdates = true;
794
795 RTCritSectLeave(&mUpdateLock);
796
797 repaint();
798}
799
800/**
801 * Sets SDL video mode. This is independent from guest video
802 * mode changes.
803 *
804 * @remarks Must be called from the SDL thread!
805 */
806void VBoxSDLFB::resizeSDL(void)
807{
808 LogFlow(("VBoxSDL:resizeSDL\n"));
809
810#ifdef VBOX_WITH_SDL2
811 const int cDisplays = SDL_GetNumVideoDisplays();
812 if (cDisplays > 0)
813 {
814 for (int d = 0; d < cDisplays; d++)
815 {
816 const int cDisplayModes = SDL_GetNumDisplayModes(d);
817 for (int m = 0; m < cDisplayModes; m++)
818 {
819 SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
820 if (SDL_GetDisplayMode(d, m, &mode) != 0)
821 {
822 RTPrintf("Display #%d, mode %d:\t\t%i bpp\t%i x %i",
823 SDL_BITSPERPIXEL(mode.format), mode.w, mode.h);
824 }
825
826 if (m == 0)
827 {
828 /*
829 * according to the SDL documentation, the API guarantees that
830 * the modes are sorted from larger to smaller, so we just
831 * take the first entry as the maximum.
832 */
833 mMaxScreenWidth = mode.w;
834 mMaxScreenHeight = mode.h;
835 }
836
837 /* Keep going. */
838 }
839 }
840 }
841 else
842 AssertFailed(); /** @todo */
843#else
844
845 /*
846 * We request a hardware surface from SDL so that we can perform
847 * accelerated system memory to VRAM blits. The way video handling
848 * works it that on the one hand we have the screen surface from SDL
849 * and on the other hand we have a software surface that we create
850 * using guest VRAM memory for linear modes and using SDL allocated
851 * system memory for text and non linear graphics modes. We never
852 * directly write to the screen surface but always use SDL blitting
853 * functions to blit from our system memory surface to the VRAM.
854 * Therefore, SDL can take advantage of hardware acceleration.
855 */
856 int sdlFlags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
857#ifndef RT_OS_OS2 /* doesn't seem to work for some reason... */
858 if (mfResizable)
859 sdlFlags |= SDL_RESIZABLE;
860#endif
861 if (mfFullscreen)
862 sdlFlags |= SDL_FULLSCREEN;
863
864 /*
865 * Now we have to check whether there are video mode restrictions
866 */
867 SDL_Rect **modes;
868 /* Get available fullscreen/hardware modes */
869 modes = SDL_ListModes(NULL, sdlFlags);
870 Assert(modes != NULL);
871 /* -1 means that any mode is possible (usually non fullscreen) */
872 if (modes != (SDL_Rect **)-1)
873 {
874 /*
875 * according to the SDL documentation, the API guarantees that
876 * the modes are sorted from larger to smaller, so we just
877 * take the first entry as the maximum.
878 */
879 mMaxScreenWidth = modes[0]->w;
880 mMaxScreenHeight = modes[0]->h;
881 }
882 else
883 {
884 /* no restriction */
885 mMaxScreenWidth = ~(uint32_t)0;
886 mMaxScreenHeight = ~(uint32_t)0;
887 }
888#endif /* VBOX_WITH_SDL2 */
889
890 uint32_t newWidth;
891 uint32_t newHeight;
892
893 /* reset the centering offsets */
894 mCenterXOffset = 0;
895 mCenterYOffset = 0;
896
897 /* we either have a fixed SDL resolution or we take the guest's */
898 if (mFixedSDLWidth != ~(uint32_t)0)
899 {
900 newWidth = mFixedSDLWidth;
901 newHeight = mFixedSDLHeight;
902 }
903 else
904 {
905 newWidth = RT_MIN(mGuestXRes, mMaxScreenWidth);
906#ifdef VBOX_SECURELABEL
907 newHeight = RT_MIN(mGuestYRes + mLabelHeight, mMaxScreenHeight);
908#else
909 newHeight = RT_MIN(mGuestYRes, mMaxScreenHeight);
910#endif
911 }
912
913 /* we don't have any extra space by default */
914 mTopOffset = 0;
915
916#ifdef VBOX_WITH_SDL2
917 int sdlWindowFlags = SDL_WINDOW_SHOWN;
918 if (mfResizable)
919 sdlWindowFlags |= SDL_WINDOW_RESIZABLE;
920 if (!mpWindow)
921 {
922 SDL_DisplayMode desktop_mode;
923 int x = 40 + mScreenId * 20;
924 int y = 40 + mScreenId * 15;
925
926 SDL_GetDesktopDisplayMode(mScreenId, &desktop_mode);
927 /* create new window */
928
929 char szTitle[64];
930 RTStrPrintf(szTitle, sizeof(szTitle), "SDL window %d", mScreenId);
931 mpWindow = SDL_CreateWindow(szTitle, x, y,
932 newWidth, newHeight, sdlWindowFlags);
933 mpRenderer = SDL_CreateRenderer(mpWindow, -1, 0 /* SDL_RendererFlags */);
934 if (mpRenderer)
935 {
936 SDL_GetRendererInfo(mpRenderer, &mRenderInfo);
937
938 mpTexture = SDL_CreateTexture(mpRenderer, desktop_mode.format,
939 SDL_TEXTUREACCESS_STREAMING, newWidth, newHeight);
940 if (!mpTexture)
941 AssertReleaseFailed();
942 }
943 else
944 AssertReleaseFailed();
945
946 if (12320 == g_cbIco64x01)
947 {
948 gWMIcon = SDL_CreateRGBSurface(0 /* Flags, must be 0 */, 64, 64, 24, 0xff, 0xff00, 0xff0000, 0);
949 /** @todo make it as simple as possible. No PNM interpreter here... */
950 if (gWMIcon)
951 {
952 memcpy(gWMIcon->pixels, g_abIco64x01+32, g_cbIco64x01-32);
953 SDL_SetWindowIcon(mpWindow, gWMIcon);
954 }
955 }
956 }
957 else
958 {
959 int w, h;
960 uint32_t format;
961 int access;
962
963 /* resize current window */
964 SDL_GetWindowSize(mpWindow, &w, &h);
965
966 if (w != (int)newWidth || h != (int)newHeight)
967 SDL_SetWindowSize(mpWindow, newWidth, newHeight);
968
969 SDL_QueryTexture(mpTexture, &format, &access, &w, &h);
970 SDL_DestroyTexture(mpTexture);
971 mpTexture = SDL_CreateTexture(mpRenderer, format, access, newWidth, newHeight);
972 if (!mpTexture)
973 AssertReleaseFailed();
974 }
975
976 void *pixels;
977 int pitch;
978 int w, h, bpp;
979 uint32_t Rmask, Gmask, Bmask, Amask;
980 uint32_t format;
981
982 if (SDL_QueryTexture(mpTexture, &format, NULL, &w, &h) < 0)
983 AssertReleaseFailed();
984
985 if (!SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask))
986 AssertReleaseFailed();
987
988 if (SDL_LockTexture(mpTexture, NULL /* SDL_Rect */, &pixels, &pitch) == 0)
989 {
990 mScreen = SDL_CreateRGBSurfaceFrom(pixels, w, h, bpp, pitch,
991 Rmask, Gmask, Bmask, Amask);
992 SDL_UnlockTexture(mpTexture); /** @BUGBUG See: https://bugzilla.libsdl.org/show_bug.cgi?id=1586 */
993 }
994 else
995 {
996 mScreen = SDL_CreateRGBSurface(0, w, h, bpp, Rmask, Gmask, Bmask, Amask);
997 AssertReleaseFailed();
998 }
999
1000 SDL_SetClipRect(mScreen, NULL);
1001
1002#else
1003 /*
1004 * Now set the screen resolution and get the surface pointer
1005 * @todo BPP is not supported!
1006 */
1007 mScreen = SDL_SetVideoMode(newWidth, newHeight, 0, sdlFlags);
1008
1009 /*
1010 * Set the Window ID. Currently used for OpenGL accelerated guests.
1011 */
1012# if defined (RT_OS_WINDOWS)
1013 SDL_SysWMinfo info;
1014 SDL_VERSION(&info.version);
1015 if (SDL_GetWMInfo(&info))
1016 mWinId = (intptr_t) info.window;
1017# elif defined (RT_OS_LINUX)
1018 SDL_SysWMinfo info;
1019 SDL_VERSION(&info.version);
1020 if (SDL_GetWMInfo(&info))
1021 mWinId = (LONG64) info.info.x11.wmwindow;
1022# elif defined(RT_OS_DARWIN)
1023 mWinId = (intptr_t)VBoxSDLGetDarwinWindowId();
1024# else
1025 /* XXX ignore this for other architectures */
1026# endif
1027#endif /* VBOX_WITH_SDL2 */
1028#ifdef VBOX_SECURELABEL
1029 /*
1030 * For non fixed SDL resolution, the above call tried to add the label height
1031 * to the guest height. If it worked, we have an offset. If it didn't the below
1032 * code will try again with the original guest resolution.
1033 */
1034 if (mFixedSDLWidth == ~(uint32_t)0)
1035 {
1036 /* if it didn't work, then we have to go for the original resolution and paint over the guest */
1037 if (!mScreen)
1038 {
1039 mScreen = SDL_SetVideoMode(newWidth, newHeight - mLabelHeight, 0, sdlFlags);
1040 }
1041 else
1042 {
1043 /* we now have some extra space */
1044 mTopOffset = mLabelHeight;
1045 }
1046 }
1047 else
1048 {
1049 /* in case the guest resolution is small enough, we do have a top offset */
1050 if (mFixedSDLHeight - mGuestYRes >= mLabelHeight)
1051 mTopOffset = mLabelHeight;
1052
1053 /* we also might have to center the guest picture */
1054 if (mFixedSDLWidth > mGuestXRes)
1055 mCenterXOffset = (mFixedSDLWidth - mGuestXRes) / 2;
1056 if (mFixedSDLHeight > mGuestYRes + mLabelHeight)
1057 mCenterYOffset = (mFixedSDLHeight - (mGuestYRes + mLabelHeight)) / 2;
1058 }
1059#endif /* VBOX_SECURELABEL */
1060
1061 AssertMsg(mScreen, ("Error: SDL_SetVideoMode failed!\n"));
1062 if (mScreen)
1063 {
1064#ifdef VBOX_WIN32_UI
1065 /* inform the UI code */
1066 resizeUI(mScreen->w, mScreen->h);
1067#endif
1068 if (mfShowSDLConfig)
1069 RTPrintf("Resized to %dx%d\n", mScreen->w, mScreen->h);
1070 }
1071}
1072
1073/**
1074 * Update specified framebuffer area. The coordinates can either be
1075 * relative to the guest framebuffer or relative to the screen.
1076 *
1077 * @remarks Must be called from the SDL thread on Linux!
1078 * @param x left column
1079 * @param y top row
1080 * @param w width in pixels
1081 * @param h height in pixels
1082 * @param fGuestRelative flag whether the above values are guest relative or screen relative;
1083 */
1084void VBoxSDLFB::update(int x, int y, int w, int h, bool fGuestRelative)
1085{
1086#ifdef VBOXSDL_WITH_X11
1087 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
1088#endif
1089 RTCritSectEnter(&mUpdateLock);
1090 Log(("Updates %d, %d,%d %dx%d\n", mfUpdates, x, y, w, h));
1091 if (!mfUpdates)
1092 {
1093 RTCritSectLeave(&mUpdateLock);
1094 return;
1095 }
1096
1097 Assert(mScreen);
1098 Assert(mSurfVRAM);
1099 if (!mScreen || !mSurfVRAM)
1100 {
1101 RTCritSectLeave(&mUpdateLock);
1102 return;
1103 }
1104
1105 /* the source and destination rectangles */
1106 SDL_Rect srcRect;
1107 SDL_Rect dstRect;
1108
1109 /* this is how many pixels we have to cut off from the height for this specific blit */
1110 int yCutoffGuest = 0;
1111
1112#ifdef VBOX_SECURELABEL
1113 bool fPaintLabel = false;
1114 /* if we have a label and no space for it, we have to cut off a bit */
1115 if (mLabelHeight && !mTopOffset)
1116 {
1117 if (y < (int)mLabelHeight)
1118 yCutoffGuest = mLabelHeight - y;
1119 }
1120#endif
1121
1122 /**
1123 * If we get a SDL window relative update, we
1124 * just perform a full screen update to keep things simple.
1125 *
1126 * @todo improve
1127 */
1128 if (!fGuestRelative)
1129 {
1130#ifdef VBOX_SECURELABEL
1131 /* repaint the label if necessary */
1132 if (y < (int)mLabelHeight)
1133 fPaintLabel = true;
1134#endif
1135 x = 0;
1136 w = mGuestXRes;
1137 y = 0;
1138 h = mGuestYRes;
1139 }
1140
1141 srcRect.x = x;
1142 srcRect.y = y + yCutoffGuest;
1143 srcRect.w = w;
1144 srcRect.h = RT_MAX(0, h - yCutoffGuest);
1145
1146 /*
1147 * Destination rectangle is just offset by the label height.
1148 * There are two cases though: label height is added to the
1149 * guest resolution (mTopOffset == mLabelHeight; yCutoffGuest == 0)
1150 * or the label cuts off a portion of the guest screen (mTopOffset == 0;
1151 * yCutoffGuest >= 0)
1152 */
1153 dstRect.x = x + mCenterXOffset;
1154#ifdef VBOX_SECURELABEL
1155 dstRect.y = RT_MAX(mLabelHeight, y + yCutoffGuest + mTopOffset) + mCenterYOffset;
1156#else
1157 dstRect.y = y + yCutoffGuest + mTopOffset + mCenterYOffset;
1158#endif
1159 dstRect.w = w;
1160 dstRect.h = RT_MAX(0, h - yCutoffGuest);
1161
1162 /*
1163 * Now we just blit
1164 */
1165 SDL_BlitSurface(mSurfVRAM, &srcRect, mScreen, &dstRect);
1166 /* hardware surfaces don't need update notifications */
1167#if defined(VBOX_WITH_SDL2)
1168 AssertRelease(mScreen->flags & SDL_PREALLOC);
1169 /** @todo Do we need to update the dirty rect for the texture for SDL2 here as well? */
1170 SDL_RenderClear(mpRenderer);
1171 SDL_RenderCopy(mpRenderer, mpTexture, &dstRect, &dstRect);
1172 SDL_RenderPresent(mpRenderer);
1173#else
1174 if ((mScreen->flags & SDL_HWSURFACE) == 0)
1175 SDL_UpdateRect(mScreen, dstRect.x, dstRect.y, dstRect.w, dstRect.h);
1176#endif
1177
1178#ifdef VBOX_SECURELABEL
1179 if (fPaintLabel)
1180 paintSecureLabel(0, 0, 0, 0, false);
1181#endif
1182 RTCritSectLeave(&mUpdateLock);
1183}
1184
1185/**
1186 * Repaint the whole framebuffer
1187 *
1188 * @remarks Must be called from the SDL thread!
1189 */
1190void VBoxSDLFB::repaint()
1191{
1192 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
1193 LogFlow(("VBoxSDLFB::repaint\n"));
1194 update(0, 0, mScreen->w, mScreen->h, false /* fGuestRelative */);
1195}
1196
1197/**
1198 * Toggle fullscreen mode
1199 *
1200 * @remarks Must be called from the SDL thread!
1201 */
1202void VBoxSDLFB::setFullscreen(bool fFullscreen)
1203{
1204 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
1205 LogFlow(("VBoxSDLFB::SetFullscreen: fullscreen: %d\n", fFullscreen));
1206 mfFullscreen = fFullscreen;
1207 /* only change the SDL resolution, do not touch the guest framebuffer */
1208 resizeSDL();
1209 repaint();
1210}
1211
1212/**
1213 * Return the geometry of the host. This isn't very well tested but it seems
1214 * to work at least on Linux hosts.
1215 */
1216void VBoxSDLFB::getFullscreenGeometry(uint32_t *width, uint32_t *height)
1217{
1218#ifndef VBOX_WITH_SDL2
1219 SDL_Rect **modes;
1220
1221 /* Get available fullscreen/hardware modes */
1222 modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
1223 Assert(modes != NULL);
1224 /* -1 means that any mode is possible (usually non fullscreen) */
1225 if (modes != (SDL_Rect **)-1)
1226 {
1227 /*
1228 * According to the SDL documentation, the API guarantees that the modes
1229 * are sorted from larger to smaller, so we just take the first entry as
1230 * the maximum.
1231 *
1232 * XXX Crude Xinerama hack :-/
1233 */
1234 if ( modes[0]->w > (16*modes[0]->h/9)
1235 && modes[1]
1236 && modes[1]->h == modes[0]->h)
1237 {
1238 *width = modes[1]->w;
1239 *height = modes[1]->h;
1240 }
1241 else
1242 {
1243 *width = modes[0]->w;
1244 *height = modes[0]->w;
1245 }
1246 }
1247#else
1248 SDL_DisplayMode dm;
1249 int rc = SDL_GetDesktopDisplayMode(0, &dm); /** @BUGBUG Handle multi monitor setups! */
1250 if (rc == 0)
1251 {
1252 *width = dm.w;
1253 *height = dm.w;
1254 }
1255#endif
1256}
1257
1258#ifdef VBOX_WITH_SDL2
1259int VBoxSDLFB::setWindowTitle(const char *pcszTitle)
1260{
1261 SDL_SetWindowTitle(mpWindow, pcszTitle);
1262
1263 return VINF_SUCCESS;
1264}
1265#endif
1266
1267#ifdef VBOX_SECURELABEL
1268
1269/**
1270 * Setup the secure labeling parameters
1271 *
1272 * @returns VBox status code
1273 * @param height height of the secure label area in pixels
1274 * @param font file path fo the TrueType font file
1275 * @param pointsize font size in points
1276 */
1277int VBoxSDLFB::initSecureLabel(uint32_t height, char *font, uint32_t pointsize, uint32_t labeloffs)
1278{
1279 LogFlow(("VBoxSDLFB:initSecureLabel: new offset: %d pixels, new font: %s, new pointsize: %d\n",
1280 height, font, pointsize));
1281 mLabelHeight = height;
1282 mLabelOffs = labeloffs;
1283 Assert(font);
1284 pTTF_Init();
1285 mLabelFont = pTTF_OpenFont(font, pointsize);
1286 if (!mLabelFont)
1287 {
1288 AssertMsgFailed(("Failed to open TTF font file %s\n", font));
1289 return VERR_OPEN_FAILED;
1290 }
1291 mSecureLabelColorFG = 0x0000FF00;
1292 mSecureLabelColorBG = 0x00FFFF00;
1293 repaint();
1294 return VINF_SUCCESS;
1295}
1296
1297/**
1298 * Set the secure label text and repaint the label
1299 *
1300 * @param text UTF-8 string of new label
1301 * @remarks must be called from the SDL thread!
1302 */
1303void VBoxSDLFB::setSecureLabelText(const char *text)
1304{
1305 mSecureLabelText = text;
1306 paintSecureLabel(0, 0, 0, 0, true);
1307}
1308
1309/**
1310 * Sets the secure label background color.
1311 *
1312 * @param colorFG encoded RGB value for text
1313 * @param colorBG encored RGB value for background
1314 * @remarks must be called from the SDL thread!
1315 */
1316void VBoxSDLFB::setSecureLabelColor(uint32_t colorFG, uint32_t colorBG)
1317{
1318 mSecureLabelColorFG = colorFG;
1319 mSecureLabelColorBG = colorBG;
1320 paintSecureLabel(0, 0, 0, 0, true);
1321}
1322
1323/**
1324 * Paint the secure label if required
1325 *
1326 * @param fForce Force the repaint
1327 * @remarks must be called from the SDL thread!
1328 */
1329void VBoxSDLFB::paintSecureLabel(int x, int y, int w, int h, bool fForce)
1330{
1331 RT_NOREF(x, w, h);
1332# ifdef VBOXSDL_WITH_X11
1333 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
1334# endif
1335 /* only when the function is present */
1336 if (!pTTF_RenderUTF8_Solid)
1337 return;
1338 /* check if we can skip the paint */
1339 if (!fForce && ((uint32_t)y > mLabelHeight))
1340 {
1341 return;
1342 }
1343 /* first fill the background */
1344 SDL_Rect rect = {0, 0, (Uint16)mScreen->w, (Uint16)mLabelHeight};
1345 SDL_FillRect(mScreen, &rect, SDL_MapRGB(mScreen->format,
1346 (mSecureLabelColorBG & 0x00FF0000) >> 16, /* red */
1347 (mSecureLabelColorBG & 0x0000FF00) >> 8, /* green */
1348 mSecureLabelColorBG & 0x000000FF)); /* blue */
1349
1350 /* now the text */
1351 if ( mLabelFont != NULL
1352 && !mSecureLabelText.isEmpty()
1353 )
1354 {
1355 SDL_Color clrFg = {(uint8_t)((mSecureLabelColorFG & 0x00FF0000) >> 16),
1356 (uint8_t)((mSecureLabelColorFG & 0x0000FF00) >> 8),
1357 (uint8_t)( mSecureLabelColorFG & 0x000000FF ), 0};
1358 SDL_Surface *sText = (pTTF_RenderUTF8_Blended != NULL)
1359 ? pTTF_RenderUTF8_Blended(mLabelFont, mSecureLabelText.c_str(), clrFg)
1360 : pTTF_RenderUTF8_Solid(mLabelFont, mSecureLabelText.c_str(), clrFg);
1361 rect.x = 10;
1362 rect.y = mLabelOffs;
1363 SDL_BlitSurface(sText, NULL, mScreen, &rect);
1364 SDL_FreeSurface(sText);
1365 }
1366 /* make sure to update the screen */
1367 SDL_UpdateRect(mScreen, 0, 0, mScreen->w, mLabelHeight);
1368}
1369
1370#endif /* VBOX_SECURELABEL */
1371
1372// IFramebufferOverlay
1373///////////////////////////////////////////////////////////////////////////////////
1374
1375/**
1376 * Constructor for the VBoxSDLFBOverlay class (IFramebufferOverlay implementation)
1377 *
1378 * @param x Initial X offset for the overlay
1379 * @param y Initial Y offset for the overlay
1380 * @param width Initial width for the overlay
1381 * @param height Initial height for the overlay
1382 * @param visible Whether the overlay is initially visible
1383 * @param alpha Initial alpha channel value for the overlay
1384 */
1385VBoxSDLFBOverlay::VBoxSDLFBOverlay(ULONG x, ULONG y, ULONG width, ULONG height,
1386 BOOL visible, VBoxSDLFB *aParent) :
1387 mOverlayX(x), mOverlayY(y), mOverlayWidth(width),
1388 mOverlayHeight(height), mOverlayVisible(visible),
1389 mParent(aParent)
1390{}
1391
1392/**
1393 * Destructor for the VBoxSDLFBOverlay class.
1394 */
1395VBoxSDLFBOverlay::~VBoxSDLFBOverlay()
1396{
1397 SDL_FreeSurface(mBlendedBits);
1398 SDL_FreeSurface(mOverlayBits);
1399}
1400
1401/**
1402 * Perform any initialisation of the overlay that can potentially fail
1403 *
1404 * @returns S_OK on success or the reason for the failure
1405 */
1406HRESULT VBoxSDLFBOverlay::init()
1407{
1408#ifndef VBOX_WITH_SDL2
1409 Uint32 fFlags = SDL_ANYFORMAT;
1410#else
1411 Uint32 fFlags = 0;
1412#endif
1413
1414 mBlendedBits = SDL_CreateRGBSurface(fFlags, mOverlayWidth, mOverlayHeight, 32,
1415 0x00ff0000, 0x0000ff00, 0x000000ff, 0);
1416 AssertMsgReturn(mBlendedBits != NULL, ("Failed to create an SDL surface\n"),
1417 E_OUTOFMEMORY);
1418
1419#ifndef VBOX_WITH_SDL2
1420 fFlags = SDL_SWSURFACE | SDL_SRCALPHA, mOverlayWidth;
1421#else
1422 fFlags = 0;
1423#endif
1424
1425 mOverlayBits = SDL_CreateRGBSurface(fFlags,
1426 mOverlayHeight, 32, 0x00ff0000, 0x0000ff00,
1427 0x000000ff, 0xff000000, 0);
1428 AssertMsgReturn(mOverlayBits != NULL, ("Failed to create an SDL surface\n"),
1429 E_OUTOFMEMORY);
1430 return S_OK;
1431}
1432
1433/**
1434 * Returns the current overlay X offset in pixels.
1435 *
1436 * @returns COM status code
1437 * @param x Address of result buffer.
1438 */
1439STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(X)(ULONG *x)
1440{
1441 LogFlow(("VBoxSDLFBOverlay::GetX\n"));
1442 if (!x)
1443 return E_INVALIDARG;
1444 *x = mOverlayX;
1445 return S_OK;
1446}
1447
1448/**
1449 * Returns the current overlay height in pixels.
1450 *
1451 * @returns COM status code
1452 * @param height Address of result buffer.
1453 */
1454STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(Y)(ULONG *y)
1455{
1456 LogFlow(("VBoxSDLFBOverlay::GetY\n"));
1457 if (!y)
1458 return E_INVALIDARG;
1459 *y = mOverlayY;
1460 return S_OK;
1461}
1462
1463/**
1464 * Returns the current overlay width in pixels. In fact, this returns the line size.
1465 *
1466 * @returns COM status code
1467 * @param width Address of result buffer.
1468 */
1469STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(Width)(ULONG *width)
1470{
1471 LogFlow(("VBoxSDLFBOverlay::GetWidth\n"));
1472 if (!width)
1473 return E_INVALIDARG;
1474 *width = mOverlayBits->pitch;
1475 return S_OK;
1476}
1477
1478/**
1479 * Returns the current overlay line size in pixels.
1480 *
1481 * @returns COM status code
1482 * @param lineSize Address of result buffer.
1483 */
1484STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(BytesPerLine)(ULONG *bytesPerLine)
1485{
1486 LogFlow(("VBoxSDLFBOverlay::GetBytesPerLine\n"));
1487 if (!bytesPerLine)
1488 return E_INVALIDARG;
1489 *bytesPerLine = mOverlayBits->pitch;
1490 return S_OK;
1491}
1492
1493/**
1494 * Returns the current overlay height in pixels.
1495 *
1496 * @returns COM status code
1497 * @param height Address of result buffer.
1498 */
1499STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(Height)(ULONG *height)
1500{
1501 LogFlow(("VBoxSDLFBOverlay::GetHeight\n"));
1502 if (!height)
1503 return E_INVALIDARG;
1504 *height = mOverlayHeight;
1505 return S_OK;
1506}
1507
1508/**
1509 * Returns whether the overlay is currently visible.
1510 *
1511 * @returns COM status code
1512 * @param visible Address of result buffer.
1513 */
1514STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(Visible)(BOOL *visible)
1515{
1516 LogFlow(("VBoxSDLFBOverlay::GetVisible\n"));
1517 if (!visible)
1518 return E_INVALIDARG;
1519 *visible = mOverlayVisible;
1520 return S_OK;
1521}
1522
1523/**
1524 * Sets whether the overlay is currently visible.
1525 *
1526 * @returns COM status code
1527 * @param visible New value.
1528 */
1529STDMETHODIMP VBoxSDLFBOverlay::COMSETTER(Visible)(BOOL visible)
1530{
1531 LogFlow(("VBoxSDLFBOverlay::SetVisible\n"));
1532 mOverlayVisible = visible;
1533 return S_OK;
1534}
1535
1536/**
1537 * Returns the value of the global alpha channel.
1538 *
1539 * @returns COM status code
1540 * @param alpha Address of result buffer.
1541 */
1542STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(Alpha)(ULONG *alpha)
1543{
1544 RT_NOREF(alpha);
1545 LogFlow(("VBoxSDLFBOverlay::GetAlpha\n"));
1546 return E_NOTIMPL;
1547}
1548
1549/**
1550 * Sets whether the overlay is currently visible.
1551 *
1552 * @returns COM status code
1553 * @param alpha new value.
1554 */
1555STDMETHODIMP VBoxSDLFBOverlay::COMSETTER(Alpha)(ULONG alpha)
1556{
1557 RT_NOREF(alpha);
1558 LogFlow(("VBoxSDLFBOverlay::SetAlpha\n"));
1559 return E_NOTIMPL;
1560}
1561
1562/**
1563 * Returns the current colour depth. In fact, this is always 32bpp.
1564 *
1565 * @returns COM status code
1566 * @param bitsPerPixel Address of result buffer.
1567 */
1568STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(BitsPerPixel)(ULONG *bitsPerPixel)
1569{
1570 LogFlow(("VBoxSDLFBOverlay::GetBitsPerPixel\n"));
1571 if (!bitsPerPixel)
1572 return E_INVALIDARG;
1573 *bitsPerPixel = 32;
1574 return S_OK;
1575}
1576
1577/**
1578 * Returns the current pixel format. In fact, this is always RGB.
1579 *
1580 * @returns COM status code
1581 * @param pixelFormat Address of result buffer.
1582 */
1583STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(PixelFormat)(ULONG *pixelFormat)
1584{
1585 LogFlow(("VBoxSDLFBOverlay::GetPixelFormat\n"));
1586 if (!pixelFormat)
1587 return E_INVALIDARG;
1588 *pixelFormat = BitmapFormat_BGR;
1589 return S_OK;
1590}
1591
1592/**
1593 * Returns whether the guest VRAM is used directly. In fact, this is always FALSE.
1594 *
1595 * @returns COM status code
1596 * @param usesGuestVRAM Address of result buffer.
1597 */
1598STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(UsesGuestVRAM)(BOOL *usesGuestVRAM)
1599{
1600 LogFlow(("VBoxSDLFBOverlay::GetUsesGuestVRAM\n"));
1601 if (!usesGuestVRAM)
1602 return E_INVALIDARG;
1603 *usesGuestVRAM = FALSE;
1604 return S_OK;
1605}
1606
1607/**
1608 * Returns the height reduction. In fact, this is always 0.
1609 *
1610 * @returns COM status code
1611 * @param heightReduction Address of result buffer.
1612 */
1613STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(HeightReduction)(ULONG *heightReduction)
1614{
1615 LogFlow(("VBoxSDLFBOverlay::GetHeightReduction\n"));
1616 if (!heightReduction)
1617 return E_INVALIDARG;
1618 *heightReduction = 0;
1619 return S_OK;
1620}
1621
1622/**
1623 * Returns the overlay for this framebuffer. Obviously, we return NULL here.
1624 *
1625 * @returns COM status code
1626 * @param overlay Address of result buffer.
1627 */
1628STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(Overlay)(IFramebufferOverlay **aOverlay)
1629{
1630 LogFlow(("VBoxSDLFBOverlay::GetOverlay\n"));
1631 if (!aOverlay)
1632 return E_INVALIDARG;
1633 *aOverlay = 0;
1634 return S_OK;
1635}
1636
1637/**
1638 * Returns associated window handle. We return NULL here.
1639 *
1640 * @returns COM status code
1641 * @param winId Address of result buffer.
1642 */
1643STDMETHODIMP VBoxSDLFBOverlay::COMGETTER(WinId)(LONG64 *winId)
1644{
1645 LogFlow(("VBoxSDLFBOverlay::GetWinId\n"));
1646 if (!winId)
1647 return E_INVALIDARG;
1648 *winId = 0;
1649 return S_OK;
1650}
1651
1652
1653/**
1654 * Lock the overlay. This should not be used - lock the parent IFramebuffer instead.
1655 *
1656 * @returns COM status code
1657 */
1658STDMETHODIMP VBoxSDLFBOverlay::Lock()
1659{
1660 LogFlow(("VBoxSDLFBOverlay::Lock\n"));
1661 AssertMsgFailed(("You should not attempt to lock an IFramebufferOverlay object -\n"
1662 "lock the parent IFramebuffer object instead.\n"));
1663 return E_NOTIMPL;
1664}
1665
1666/**
1667 * Unlock the overlay.
1668 *
1669 * @returns COM status code
1670 */
1671STDMETHODIMP VBoxSDLFBOverlay::Unlock()
1672{
1673 LogFlow(("VBoxSDLFBOverlay::Unlock\n"));
1674 AssertMsgFailed(("You should not attempt to lock an IFramebufferOverlay object -\n"
1675 "lock the parent IFramebuffer object instead.\n"));
1676 return E_NOTIMPL;
1677}
1678
1679/**
1680 * Change the X and Y co-ordinates of the overlay area.
1681 *
1682 * @returns COM status code
1683 * @param x New X co-ordinate.
1684 * @param y New Y co-ordinate.
1685 */
1686STDMETHODIMP VBoxSDLFBOverlay::Move(ULONG x, ULONG y)
1687{
1688 mOverlayX = x;
1689 mOverlayY = y;
1690 return S_OK;
1691}
1692
1693/**
1694 * Notify the overlay that a section of the framebuffer has been redrawn.
1695 *
1696 * @returns COM status code
1697 * @param x X co-ordinate of upper left corner of modified area.
1698 * @param y Y co-ordinate of upper left corner of modified area.
1699 * @param w Width of modified area.
1700 * @param h Height of modified area.
1701 * @retval finished Set if the operation has completed.
1702 *
1703 * All we do here is to send a request to the parent to update the affected area,
1704 * translating between our co-ordinate system and the parent's. It would be have
1705 * been better to call the parent directly, but such is life. We leave bounds
1706 * checking to the parent.
1707 */
1708STDMETHODIMP VBoxSDLFBOverlay::NotifyUpdate(ULONG x, ULONG y,
1709 ULONG w, ULONG h)
1710{
1711 return mParent->NotifyUpdate(x + mOverlayX, y + mOverlayY, w, h);
1712}
1713
1714/**
1715 * Change the dimensions of the overlay.
1716 *
1717 * @returns COM status code
1718 * @param pixelFormat Must be BitmapFormat_BGR.
1719 * @param vram Must be NULL.
1720 * @param lineSize Ignored.
1721 * @param w New overlay width.
1722 * @param h New overlay height.
1723 * @retval finished Set if the operation has completed.
1724 */
1725STDMETHODIMP VBoxSDLFBOverlay::RequestResize(ULONG aScreenId, ULONG pixelFormat, ULONG vram,
1726 ULONG bitsPerPixel, ULONG bytesPerLine,
1727 ULONG w, ULONG h, BOOL *finished)
1728{
1729 RT_NOREF(aScreenId, bytesPerLine, finished);
1730 AssertReturn(pixelFormat == BitmapFormat_BGR, E_INVALIDARG);
1731 AssertReturn(vram == 0, E_INVALIDARG);
1732 AssertReturn(bitsPerPixel == 32, E_INVALIDARG);
1733 mOverlayWidth = w;
1734 mOverlayHeight = h;
1735 SDL_FreeSurface(mOverlayBits);
1736
1737#ifndef VBOX_WITH_SDL2
1738 Uint32 fFlags = SDL_ANYFORMAT;
1739#else
1740 Uint32 fFlags = 0;
1741#endif
1742
1743 mBlendedBits = SDL_CreateRGBSurface(fFlags, mOverlayWidth, mOverlayHeight, 32,
1744 0x00ff0000, 0x0000ff00, 0x000000ff, 0);
1745 AssertMsgReturn(mBlendedBits != NULL, ("Failed to create an SDL surface\n"),
1746 E_OUTOFMEMORY);
1747
1748#ifndef VBOX_WITH_SDL2
1749 fFlags = SDL_SWSURFACE | SDL_SRCALPHA;
1750#else
1751 fFlags = 0;
1752#endif
1753
1754 mOverlayBits = SDL_CreateRGBSurface(fFlags, mOverlayWidth,
1755 mOverlayHeight, 32, 0x00ff0000, 0x0000ff00,
1756 0x000000ff, 0xff000000);
1757 AssertMsgReturn(mOverlayBits != NULL, ("Failed to create an SDL surface\n"),
1758 E_OUTOFMEMORY);
1759 return S_OK;
1760}
1761
1762/**
1763 * Returns whether we like the given video mode.
1764 *
1765 * @returns COM status code
1766 * @param width video mode width in pixels
1767 * @param height video mode height in pixels
1768 * @param bpp video mode bit depth in bits per pixel
1769 * @retval supported pointer to result variable
1770 *
1771 * Basically, we support anything with 32bpp.
1772 */
1773STDMETHODIMP VBoxSDLFBOverlay::VideoModeSupported(ULONG width, ULONG height, ULONG bpp, BOOL *supported)
1774{
1775 RT_NOREF(width, height);
1776 if (!supported)
1777 return E_POINTER;
1778 if (bpp == 32)
1779 *supported = true;
1780 else
1781 *supported = false;
1782 return S_OK;
1783}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use