VirtualBox

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

Last change on this file since 94623 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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