From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Frontends/VBoxSDL/Framebuffer.cpp | 959 +++++++++++++++++++++++++++++ 1 file changed, 959 insertions(+) create mode 100644 src/VBox/Frontends/VBoxSDL/Framebuffer.cpp (limited to 'src/VBox/Frontends/VBoxSDL/Framebuffer.cpp') diff --git a/src/VBox/Frontends/VBoxSDL/Framebuffer.cpp b/src/VBox/Frontends/VBoxSDL/Framebuffer.cpp new file mode 100644 index 00000000..16dc2826 --- /dev/null +++ b/src/VBox/Frontends/VBoxSDL/Framebuffer.cpp @@ -0,0 +1,959 @@ +/* $Id: Framebuffer.cpp $ */ +/** @file + * VBoxSDL - Implementation of VBoxSDLFB (SDL framebuffer) class + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef RT_OS_OS2 +# undef RT_MAX +// from +# define RT_MAX(Value1, Value2) ((Value1) >= (Value2) ? (Value1) : (Value2)) +#endif + +using namespace com; + +#define LOG_GROUP LOG_GROUP_GUI +#include +#include + +#include "VBoxSDL.h" +#include "Framebuffer.h" +#include "Ico64x01.h" + +#if defined(RT_OS_WINDOWS) || defined(RT_OS_LINUX) +# ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4121) /* warning C4121: 'SDL_SysWMmsg' : alignment of a member was sensitive to packing*/ +# endif +# include /* for SDL_GetWMInfo() */ +# ifdef _MSC_VER +# pragma warning(pop) +# endif +#endif + +#if defined(VBOX_WITH_XPCOM) +NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxSDLFB, IFramebuffer) +NS_DECL_CLASSINFO(VBoxSDLFB) +NS_IMPL_THREADSAFE_ISUPPORTS2_CI(VBoxSDLFBOverlay, IFramebufferOverlay, IFramebuffer) +NS_DECL_CLASSINFO(VBoxSDLFBOverlay) +#endif + +static bool gfSdlInitialized = false; /**< if SDL was initialized */ +static SDL_Surface *gWMIcon = NULL; /**< the application icon */ +static RTNATIVETHREAD gSdlNativeThread = NIL_RTNATIVETHREAD; /**< the SDL thread */ + +// +// Constructor / destructor +// + +VBoxSDLFB::VBoxSDLFB() +{ +} + +HRESULT VBoxSDLFB::FinalConstruct() +{ + return 0; +} + +void VBoxSDLFB::FinalRelease() +{ + return; +} + +/** + * SDL framebuffer constructor. It is called from the main + * (i.e. SDL) thread. Therefore it is safe to use SDL calls + * here. + * @param fFullscreen flag whether we start in fullscreen mode + * @param fResizable flag whether the SDL window should be resizable + * @param fShowSDLConfig flag whether we print out SDL settings + * @param fKeepHostRes flag whether we switch the host screen resolution + * when switching to fullscreen or not + * @param iFixedWidth fixed SDL width (-1 means not set) + * @param iFixedHeight fixed SDL height (-1 means not set) + */ +HRESULT VBoxSDLFB::init(uint32_t uScreenId, + bool fFullscreen, bool fResizable, bool fShowSDLConfig, + bool fKeepHostRes, uint32_t u32FixedWidth, + uint32_t u32FixedHeight, uint32_t u32FixedBPP, + bool fUpdateImage) +{ + LogFlow(("VBoxSDLFB::VBoxSDLFB\n")); + + mScreenId = uScreenId; + mfUpdateImage = fUpdateImage; + mpWindow = NULL; + mpTexture = NULL; + mpRenderer = NULL; + mSurfVRAM = NULL; + mfInitialized = false; + mfFullscreen = fFullscreen; + mfKeepHostRes = fKeepHostRes; + mTopOffset = 0; + mfResizable = fResizable; + mfShowSDLConfig = fShowSDLConfig; + mFixedSDLWidth = u32FixedWidth; + mFixedSDLHeight = u32FixedHeight; + mFixedSDLBPP = u32FixedBPP; + mCenterXOffset = 0; + mCenterYOffset = 0; + /* Start with standard screen dimensions. */ + mGuestXRes = 640; + mGuestYRes = 480; + mPtrVRAM = NULL; + mBitsPerPixel = 0; + mBytesPerLine = 0; + mfSameSizeRequested = false; + mfUpdates = false; + + int rc = RTCritSectInit(&mUpdateLock); + AssertMsg(rc == VINF_SUCCESS, ("Error from RTCritSectInit!\n")); + + resizeGuest(); + mfInitialized = true; +#ifdef RT_OS_WINDOWS + HRESULT hr = CoCreateFreeThreadedMarshaler(this, m_pUnkMarshaler.asOutParam()); + Log(("CoCreateFreeThreadedMarshaler hr %08X\n", hr)); NOREF(hr); +#endif + + rc = SDL_GetRendererInfo(mpRenderer, &mRenderInfo); + if (RT_SUCCESS(rc)) + { + if (fShowSDLConfig) + RTPrintf("Render info:\n" + " Name: %s\n" + " Render flags: 0x%x\n" + " SDL video driver: %s\n", + mRenderInfo.name, + mRenderInfo.flags, + RTEnvGet("SDL_VIDEODRIVER")); + } + + return rc; +} + +VBoxSDLFB::~VBoxSDLFB() +{ + LogFlow(("VBoxSDLFB::~VBoxSDLFB\n")); + if (mSurfVRAM) + { + SDL_FreeSurface(mSurfVRAM); + mSurfVRAM = NULL; + } + RTCritSectDelete(&mUpdateLock); +} + +/* static */ +bool VBoxSDLFB::init(bool fShowSDLConfig) +{ + LogFlow(("VBoxSDLFB::init\n")); + + /* memorize the thread that inited us, that's the SDL thread */ + gSdlNativeThread = RTThreadNativeSelf(); + +#ifdef RT_OS_WINDOWS + /* default to DirectX if nothing else set */ + if (!RTEnvExist("SDL_VIDEODRIVER")) + { + RTEnvSet("SDL_VIDEODRIVER", "directx"); + } +#endif +#ifdef VBOXSDL_WITH_X11 + /* On some X servers the mouse is stuck inside the bottom right corner. + * See http://wiki.clug.org.za/wiki/QEMU_mouse_not_working */ + RTEnvSet("SDL_VIDEO_X11_DGAMOUSE", "0"); +#endif + + int rc = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE); + if (rc != 0) + { + RTPrintf("SDL Error: '%s'\n", SDL_GetError()); + return false; + } + gfSdlInitialized = true; + + RT_NOREF(fShowSDLConfig); + return true; +} + +/** + * Terminate SDL + * + * @remarks must be called from the SDL thread! + */ +void VBoxSDLFB::uninit() +{ + if (gfSdlInitialized) + { + AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n")); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + } +} + +/** + * Returns the current framebuffer width in pixels. + * + * @returns COM status code + * @param width Address of result buffer. + */ +STDMETHODIMP VBoxSDLFB::COMGETTER(Width)(ULONG *width) +{ + LogFlow(("VBoxSDLFB::GetWidth\n")); + if (!width) + return E_INVALIDARG; + *width = mGuestXRes; + return S_OK; +} + +/** + * Returns the current framebuffer height in pixels. + * + * @returns COM status code + * @param height Address of result buffer. + */ +STDMETHODIMP VBoxSDLFB::COMGETTER(Height)(ULONG *height) +{ + LogFlow(("VBoxSDLFB::GetHeight\n")); + if (!height) + return E_INVALIDARG; + *height = mGuestYRes; + return S_OK; +} + +/** + * Return the current framebuffer color depth. + * + * @returns COM status code + * @param bitsPerPixel Address of result variable + */ +STDMETHODIMP VBoxSDLFB::COMGETTER(BitsPerPixel)(ULONG *bitsPerPixel) +{ + LogFlow(("VBoxSDLFB::GetBitsPerPixel\n")); + if (!bitsPerPixel) + return E_INVALIDARG; + /* get the information directly from the surface in use */ + Assert(mSurfVRAM); + *bitsPerPixel = (ULONG)(mSurfVRAM ? mSurfVRAM->format->BitsPerPixel : 0); + return S_OK; +} + +/** + * Return the current framebuffer line size in bytes. + * + * @returns COM status code. + * @param lineSize Address of result variable. + */ +STDMETHODIMP VBoxSDLFB::COMGETTER(BytesPerLine)(ULONG *bytesPerLine) +{ + LogFlow(("VBoxSDLFB::GetBytesPerLine\n")); + if (!bytesPerLine) + return E_INVALIDARG; + /* get the information directly from the surface */ + Assert(mSurfVRAM); + *bytesPerLine = (ULONG)(mSurfVRAM ? mSurfVRAM->pitch : 0); + return S_OK; +} + +STDMETHODIMP VBoxSDLFB::COMGETTER(PixelFormat) (BitmapFormat_T *pixelFormat) +{ + if (!pixelFormat) + return E_POINTER; + *pixelFormat = BitmapFormat_BGR; + return S_OK; +} + +/** + * Returns by how many pixels the guest should shrink its + * video mode height values. + * + * @returns COM status code. + * @param heightReduction Address of result variable. + */ +STDMETHODIMP VBoxSDLFB::COMGETTER(HeightReduction)(ULONG *heightReduction) +{ + if (!heightReduction) + return E_POINTER; + *heightReduction = 0; + return S_OK; +} + +/** + * Returns a pointer to an alpha-blended overlay used for displaying status + * icons above the framebuffer. + * + * @returns COM status code. + * @param aOverlay The overlay framebuffer. + */ +STDMETHODIMP VBoxSDLFB::COMGETTER(Overlay)(IFramebufferOverlay **aOverlay) +{ + if (!aOverlay) + return E_POINTER; + /* Not yet implemented */ + *aOverlay = 0; + return S_OK; +} + +/** + * Returns handle of window where framebuffer context is being drawn + * + * @returns COM status code. + * @param winId Handle of associated window. + */ +STDMETHODIMP VBoxSDLFB::COMGETTER(WinId)(LONG64 *winId) +{ + if (!winId) + return E_POINTER; +#ifdef RT_OS_DARWIN + if (mWinId == NULL) /* (In case it failed the first time.) */ + mWinId = (intptr_t)VBoxSDLGetDarwinWindowId(); +#endif + *winId = mWinId; + return S_OK; +} + +STDMETHODIMP VBoxSDLFB::COMGETTER(Capabilities)(ComSafeArrayOut(FramebufferCapabilities_T, aCapabilities)) +{ + if (ComSafeArrayOutIsNull(aCapabilities)) + return E_POINTER; + + com::SafeArray caps; + + if (mfUpdateImage) + { + caps.resize(2); + caps[0] = FramebufferCapabilities_UpdateImage; + caps[1] = FramebufferCapabilities_RenderCursor; + } + else + { + caps.resize(1); + caps[0] = FramebufferCapabilities_RenderCursor; + } + + caps.detachTo(ComSafeArrayOutArg(aCapabilities)); + return S_OK; +} + +/** + * Notify framebuffer of an update. + * + * @returns COM status code + * @param x Update region upper left corner x value. + * @param y Update region upper left corner y value. + * @param w Update region width in pixels. + * @param h Update region height in pixels. + * @param finished Address of output flag whether the update + * could be fully processed in this call (which + * has to return immediately) or VBox should wait + * for a call to the update complete API before + * continuing with display updates. + */ +STDMETHODIMP VBoxSDLFB::NotifyUpdate(ULONG x, ULONG y, + ULONG w, ULONG h) +{ + /* + * The input values are in guest screen coordinates. + */ + LogFlow(("VBoxSDLFB::NotifyUpdate: x = %d, y = %d, w = %d, h = %d\n", + x, y, w, h)); + +#ifdef VBOXSDL_WITH_X11 + /* + * SDL does not allow us to make this call from any other thread than + * the main SDL thread (which initialized the video mode). So we have + * to send an event to the main SDL thread and process it there. For + * sake of simplicity, we encode all information in the event parameters. + */ + SDL_Event event; + event.type = SDL_USEREVENT; + event.user.code = mScreenId; + event.user.type = SDL_USER_EVENT_UPDATERECT; + // 16 bit is enough for coordinates + event.user.data1 = (void*)(uintptr_t)(x << 16 | y); + event.user.data2 = (void*)(uintptr_t)(w << 16 | h); + PushNotifyUpdateEvent(&event); +#else /* !VBOXSDL_WITH_X11 */ + update(x, y, w, h, true /* fGuestRelative */); +#endif /* !VBOXSDL_WITH_X11 */ + + return S_OK; +} + +STDMETHODIMP VBoxSDLFB::NotifyUpdateImage(ULONG aX, + ULONG aY, + ULONG aWidth, + ULONG aHeight, + ComSafeArrayIn(BYTE, aImage)) +{ + LogFlow(("NotifyUpdateImage: %d,%d %dx%d\n", aX, aY, aWidth, aHeight)); + + com::SafeArray image(ComSafeArrayInArg(aImage)); + + /* Copy to mSurfVRAM. */ + SDL_Rect srcRect; + SDL_Rect dstRect; + srcRect.x = 0; + srcRect.y = 0; + srcRect.w = (uint16_t)aWidth; + srcRect.h = (uint16_t)aHeight; + dstRect.x = (int16_t)aX; + dstRect.y = (int16_t)aY; + dstRect.w = (uint16_t)aWidth; + dstRect.h = (uint16_t)aHeight; + + const uint32_t Rmask = 0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0; + SDL_Surface *surfSrc = SDL_CreateRGBSurfaceFrom(image.raw(), aWidth, aHeight, 32, aWidth * 4, + Rmask, Gmask, Bmask, Amask); + if (surfSrc) + { + RTCritSectEnter(&mUpdateLock); + if (mfUpdates) + SDL_BlitSurface(surfSrc, &srcRect, mSurfVRAM, &dstRect); + RTCritSectLeave(&mUpdateLock); + + SDL_FreeSurface(surfSrc); + } + + return NotifyUpdate(aX, aY, aWidth, aHeight); +} + +extern ComPtr gpDisplay; + +STDMETHODIMP VBoxSDLFB::NotifyChange(ULONG aScreenId, + ULONG aXOrigin, + ULONG aYOrigin, + ULONG aWidth, + ULONG aHeight) +{ + LogRel(("NotifyChange: %d %d,%d %dx%d\n", + aScreenId, aXOrigin, aYOrigin, aWidth, aHeight)); + + ComPtr pSourceBitmap; + if (!mfUpdateImage) + gpDisplay->QuerySourceBitmap(aScreenId, pSourceBitmap.asOutParam()); + + RTCritSectEnter(&mUpdateLock); + + /* Disable screen updates. */ + mfUpdates = false; + + if (mfUpdateImage) + { + mGuestXRes = aWidth; + mGuestYRes = aHeight; + mPtrVRAM = NULL; + mBitsPerPixel = 0; + mBytesPerLine = 0; + } + else + { + /* Save the new bitmap. */ + mpPendingSourceBitmap = pSourceBitmap; + } + + RTCritSectLeave(&mUpdateLock); + + SDL_Event event; + event.type = SDL_USEREVENT; + event.user.type = SDL_USER_EVENT_NOTIFYCHANGE; + event.user.code = mScreenId; + + PushSDLEventForSure(&event); + + RTThreadYield(); + + return S_OK; +} + +/** + * Returns whether we like the given video mode. + * + * @returns COM status code + * @param width video mode width in pixels + * @param height video mode height in pixels + * @param bpp video mode bit depth in bits per pixel + * @param supported pointer to result variable + */ +STDMETHODIMP VBoxSDLFB::VideoModeSupported(ULONG width, ULONG height, ULONG bpp, BOOL *supported) +{ + RT_NOREF(bpp); + + if (!supported) + return E_POINTER; + + /* are constraints set? */ + if ( ( (mMaxScreenWidth != ~(uint32_t)0) + && (width > mMaxScreenWidth)) + || ( (mMaxScreenHeight != ~(uint32_t)0) + && (height > mMaxScreenHeight))) + { + /* nope, we don't want that (but still don't freak out if it is set) */ +#ifdef DEBUG + RTPrintf("VBoxSDL::VideoModeSupported: we refused mode %dx%dx%d\n", width, height, bpp); +#endif + *supported = false; + } + else + { + /* anything will do */ + *supported = true; + } + return S_OK; +} + +STDMETHODIMP VBoxSDLFB::GetVisibleRegion(BYTE *aRectangles, ULONG aCount, + ULONG *aCountCopied) +{ + PRTRECT rects = (PRTRECT)aRectangles; + + if (!rects) + return E_POINTER; + + /// @todo + + NOREF(aCount); + NOREF(aCountCopied); + + return S_OK; +} + +STDMETHODIMP VBoxSDLFB::SetVisibleRegion(BYTE *aRectangles, ULONG aCount) +{ + PRTRECT rects = (PRTRECT)aRectangles; + + if (!rects) + return E_POINTER; + + /// @todo + + NOREF(aCount); + + return S_OK; +} + +STDMETHODIMP VBoxSDLFB::ProcessVHWACommand(BYTE *pCommand, LONG enmCmd, BOOL fGuestCmd) +{ + RT_NOREF(pCommand, enmCmd, fGuestCmd); + return E_NOTIMPL; +} + +STDMETHODIMP VBoxSDLFB::Notify3DEvent(ULONG uType, ComSafeArrayIn(BYTE, aData)) +{ + RT_NOREF(uType); ComSafeArrayNoRef(aData); + return E_NOTIMPL; +} + +// +// Internal public methods +// + +/* This method runs on the main SDL thread. */ +void VBoxSDLFB::notifyChange(ULONG aScreenId) +{ + /* Disable screen updates. */ + RTCritSectEnter(&mUpdateLock); + + if (!mfUpdateImage && mpPendingSourceBitmap.isNull()) + { + /* Do nothing. Change event already processed. */ + RTCritSectLeave(&mUpdateLock); + return; + } + + /* Release the current bitmap and keep the pending one. */ + mpSourceBitmap = mpPendingSourceBitmap; + mpPendingSourceBitmap.setNull(); + + RTCritSectLeave(&mUpdateLock); + + if (mpSourceBitmap.isNull()) + { + mPtrVRAM = NULL; + mBitsPerPixel = 32; + mBytesPerLine = mGuestXRes * 4; + } + else + { + BYTE *pAddress = NULL; + ULONG ulWidth = 0; + ULONG ulHeight = 0; + ULONG ulBitsPerPixel = 0; + ULONG ulBytesPerLine = 0; + BitmapFormat_T bitmapFormat = BitmapFormat_Opaque; + + mpSourceBitmap->QueryBitmapInfo(&pAddress, + &ulWidth, + &ulHeight, + &ulBitsPerPixel, + &ulBytesPerLine, + &bitmapFormat); + + if ( mGuestXRes == ulWidth + && mGuestYRes == ulHeight + && mBitsPerPixel == ulBitsPerPixel + && mBytesPerLine == ulBytesPerLine + && mPtrVRAM == pAddress + ) + { + mfSameSizeRequested = true; + } + else + { + mfSameSizeRequested = false; + } + + mGuestXRes = ulWidth; + mGuestYRes = ulHeight; + mPtrVRAM = pAddress; + mBitsPerPixel = ulBitsPerPixel; + mBytesPerLine = ulBytesPerLine; + } + + resizeGuest(); + + gpDisplay->InvalidateAndUpdateScreen(aScreenId); +} + +/** + * Method that does the actual resize of the guest framebuffer and + * then changes the SDL framebuffer setup. + */ +void VBoxSDLFB::resizeGuest() +{ + LogFlowFunc (("mGuestXRes: %d, mGuestYRes: %d\n", mGuestXRes, mGuestYRes)); + AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), + ("Wrong thread! SDL is not threadsafe!\n")); + + RTCritSectEnter(&mUpdateLock); + + const uint32_t Rmask = 0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0; + + /* first free the current surface */ + if (mSurfVRAM) + { + SDL_FreeSurface(mSurfVRAM); + mSurfVRAM = NULL; + } + + if (mPtrVRAM) + { + /* Create a source surface from the source bitmap. */ + mSurfVRAM = SDL_CreateRGBSurfaceFrom(mPtrVRAM, mGuestXRes, mGuestYRes, mBitsPerPixel, + mBytesPerLine, Rmask, Gmask, Bmask, Amask); + LogFlow(("VBoxSDL:: using the source bitmap\n")); + } + else + { + mSurfVRAM = SDL_CreateRGBSurface(SDL_SWSURFACE, mGuestXRes, mGuestYRes, 32, + Rmask, Gmask, Bmask, Amask); + LogFlow(("VBoxSDL:: using SDL_SWSURFACE\n")); + } + LogFlow(("VBoxSDL:: created VRAM surface %p\n", mSurfVRAM)); + + if (mfSameSizeRequested) + { + mfSameSizeRequested = false; + LogFlow(("VBoxSDL:: the same resolution requested, skipping the resize.\n")); + } + else + { + /* now adjust the SDL resolution */ + resizeSDL(); + } + + /* Enable screen updates. */ + mfUpdates = true; + + RTCritSectLeave(&mUpdateLock); + + repaint(); +} + +/** + * Sets SDL video mode. This is independent from guest video + * mode changes. + * + * @remarks Must be called from the SDL thread! + */ +void VBoxSDLFB::resizeSDL(void) +{ + LogFlow(("VBoxSDL:resizeSDL\n")); + + const int cDisplays = SDL_GetNumVideoDisplays(); + if (cDisplays > 0) + { + for (int d = 0; d < cDisplays; d++) + { + const int cDisplayModes = SDL_GetNumDisplayModes(d); + for (int m = 0; m < cDisplayModes; m++) + { + SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 }; + if (SDL_GetDisplayMode(d, m, &mode) != 0) + { + RTPrintf("Display #%d, mode %d:\t\t%i bpp\t%i x %i", + SDL_BITSPERPIXEL(mode.format), mode.w, mode.h); + } + + if (m == 0) + { + /* + * according to the SDL documentation, the API guarantees that + * the modes are sorted from larger to smaller, so we just + * take the first entry as the maximum. + */ + mMaxScreenWidth = mode.w; + mMaxScreenHeight = mode.h; + } + + /* Keep going. */ + } + } + } + else + AssertFailed(); /** @todo */ + + uint32_t newWidth; + uint32_t newHeight; + + /* reset the centering offsets */ + mCenterXOffset = 0; + mCenterYOffset = 0; + + /* we either have a fixed SDL resolution or we take the guest's */ + if (mFixedSDLWidth != ~(uint32_t)0) + { + newWidth = mFixedSDLWidth; + newHeight = mFixedSDLHeight; + } + else + { + newWidth = RT_MIN(mGuestXRes, mMaxScreenWidth); + newHeight = RT_MIN(mGuestYRes, mMaxScreenHeight); + } + + /* we don't have any extra space by default */ + mTopOffset = 0; + + int sdlWindowFlags = SDL_WINDOW_SHOWN; + if (mfResizable) + sdlWindowFlags |= SDL_WINDOW_RESIZABLE; + if (!mpWindow) + { + SDL_DisplayMode desktop_mode; + int x = 40 + mScreenId * 20; + int y = 40 + mScreenId * 15; + + SDL_GetDesktopDisplayMode(mScreenId, &desktop_mode); + /* create new window */ + + char szTitle[64]; + RTStrPrintf(szTitle, sizeof(szTitle), "SDL window %d", mScreenId); + mpWindow = SDL_CreateWindow(szTitle, x, y, + newWidth, newHeight, sdlWindowFlags); + mpRenderer = SDL_CreateRenderer(mpWindow, -1, 0 /* SDL_RendererFlags */); + if (mpRenderer) + { + SDL_GetRendererInfo(mpRenderer, &mRenderInfo); + + mpTexture = SDL_CreateTexture(mpRenderer, desktop_mode.format, + SDL_TEXTUREACCESS_STREAMING, newWidth, newHeight); + if (!mpTexture) + AssertReleaseFailed(); + } + else + AssertReleaseFailed(); + + if (12320 == g_cbIco64x01) + { + gWMIcon = SDL_CreateRGBSurface(0 /* Flags, must be 0 */, 64, 64, 24, 0xff, 0xff00, 0xff0000, 0); + /** @todo make it as simple as possible. No PNM interpreter here... */ + if (gWMIcon) + { + memcpy(gWMIcon->pixels, g_abIco64x01+32, g_cbIco64x01-32); + SDL_SetWindowIcon(mpWindow, gWMIcon); + } + } + } + else + { + int w, h; + uint32_t format; + int access; + + /* resize current window */ + SDL_GetWindowSize(mpWindow, &w, &h); + + if (w != (int)newWidth || h != (int)newHeight) + SDL_SetWindowSize(mpWindow, newWidth, newHeight); + + SDL_QueryTexture(mpTexture, &format, &access, &w, &h); + SDL_DestroyTexture(mpTexture); + mpTexture = SDL_CreateTexture(mpRenderer, format, access, newWidth, newHeight); + if (!mpTexture) + AssertReleaseFailed(); + } +} + +/** + * Update specified framebuffer area. The coordinates can either be + * relative to the guest framebuffer or relative to the screen. + * + * @remarks Must be called from the SDL thread on Linux! + * @param x left column + * @param y top row + * @param w width in pixels + * @param h height in pixels + * @param fGuestRelative flag whether the above values are guest relative or screen relative; + */ +void VBoxSDLFB::update(int x, int y, int w, int h, bool fGuestRelative) +{ +#ifdef VBOXSDL_WITH_X11 + AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n")); +#endif + RTCritSectEnter(&mUpdateLock); + Log(("Updates %d, %d,%d %dx%d\n", mfUpdates, x, y, w, h)); + // printf("Updates %d, %d,%d %dx%d\n", mfUpdates, x, y, w, h); + if (!mfUpdates) + { + RTCritSectLeave(&mUpdateLock); + return; + } + + Assert(mSurfVRAM); + if (!mSurfVRAM) + { + RTCritSectLeave(&mUpdateLock); + return; + } + + /* the source and destination rectangles */ + SDL_Rect srcRect; + SDL_Rect dstRect; + + /* this is how many pixels we have to cut off from the height for this specific blit */ + int yCutoffGuest = 0; + /** + * If we get a SDL window relative update, we + * just perform a full screen update to keep things simple. + * + * @todo improve + */ + if (!fGuestRelative) + { + x = 0; + w = mGuestXRes; + y = 0; + h = mGuestYRes; + } + + srcRect.x = x; + srcRect.y = y + yCutoffGuest; + srcRect.w = w; + srcRect.h = RT_MAX(0, h - yCutoffGuest); + + /* + * Destination rectangle is just offset by the label height. + * There are two cases though: label height is added to the + * guest resolution (mTopOffset == mLabelHeight; yCutoffGuest == 0) + * or the label cuts off a portion of the guest screen (mTopOffset == 0; + * yCutoffGuest >= 0) + */ + dstRect.x = x + mCenterXOffset; + dstRect.y = y + yCutoffGuest + mTopOffset + mCenterYOffset; + dstRect.w = w; + dstRect.h = RT_MAX(0, h - yCutoffGuest); + + SDL_Texture *pNewTexture = SDL_CreateTextureFromSurface(mpRenderer, mSurfVRAM); + /** @todo Do we need to update the dirty rect for the texture for SDL2 here as well? */ + // SDL_RenderClear(mpRenderer); + //SDL_UpdateTexture(mpTexture, &dstRect, mSurfVRAM->pixels, mSurfVRAM->pitch); + // SDL_RenderCopy(mpRenderer, mpTexture, NULL, NULL); + SDL_RenderCopy(mpRenderer, pNewTexture, &srcRect, &dstRect); + SDL_RenderPresent(mpRenderer); + SDL_DestroyTexture(pNewTexture); + RTCritSectLeave(&mUpdateLock); +} + +/** + * Repaint the whole framebuffer + * + * @remarks Must be called from the SDL thread! + */ +void VBoxSDLFB::repaint() +{ + AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n")); + LogFlow(("VBoxSDLFB::repaint\n")); + int w, h; + uint32_t format; + int access; + SDL_QueryTexture(mpTexture, &format, &access, &w, &h); + update(0, 0, w, h, false /* fGuestRelative */); +} + +/** + * Toggle fullscreen mode + * + * @remarks Must be called from the SDL thread! + */ +void VBoxSDLFB::setFullscreen(bool fFullscreen) +{ + AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n")); + LogFlow(("VBoxSDLFB::SetFullscreen: fullscreen: %d\n", fFullscreen)); + mfFullscreen = fFullscreen; + /* only change the SDL resolution, do not touch the guest framebuffer */ + resizeSDL(); + repaint(); +} + +/** + * Return the geometry of the host. This isn't very well tested but it seems + * to work at least on Linux hosts. + */ +void VBoxSDLFB::getFullscreenGeometry(uint32_t *width, uint32_t *height) +{ + SDL_DisplayMode dm; + int rc = SDL_GetDesktopDisplayMode(0, &dm); /** @BUGBUG Handle multi monitor setups! */ + if (rc == 0) + { + *width = dm.w; + *height = dm.w; + } +} + +int VBoxSDLFB::setWindowTitle(const char *pcszTitle) +{ + SDL_SetWindowTitle(mpWindow, pcszTitle); + + return VINF_SUCCESS; +} -- cgit v1.2.3