LCOV - code coverage report
Current view: top level - source/ps - VideoMode.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 38 359 10.6 %
Date: 2022-06-14 00:41:00 Functions: 6 32 18.8 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "VideoMode.h"
      21             : 
      22             : #include "graphics/GameView.h"
      23             : #include "gui/GUIManager.h"
      24             : #include "lib/config2.h"
      25             : #include "lib/external_libraries/libsdl.h"
      26             : #include "lib/tex/tex.h"
      27             : #include "ps/CConsole.h"
      28             : #include "ps/CLogger.h"
      29             : #include "ps/ConfigDB.h"
      30             : #include "ps/CStr.h"
      31             : #include "ps/Filesystem.h"
      32             : #include "ps/Game.h"
      33             : #include "ps/GameSetup/Config.h"
      34             : #include "ps/Pyrogenesis.h"
      35             : #include "renderer/backend/dummy/Device.h"
      36             : #include "renderer/backend/gl/Device.h"
      37             : #include "renderer/Renderer.h"
      38             : 
      39             : namespace
      40             : {
      41             : 
      42             : int DEFAULT_WINDOW_W = 1024;
      43             : int DEFAULT_WINDOW_H = 768;
      44             : 
      45             : int DEFAULT_FULLSCREEN_W = 1024;
      46             : int DEFAULT_FULLSCREEN_H = 768;
      47             : 
      48             : const wchar_t DEFAULT_CURSOR_NAME[] = L"default-arrow";
      49             : 
      50             : } // anonymous namespace
      51             : 
      52             : #if OS_WIN
      53             : // We can't include wutil directly because GL headers conflict with Windows
      54             : // until we use a proper GL loader.
      55             : extern void wutil_SetAppWindow(SDL_Window* window);
      56             : 
      57             : // After a proper HiDPI integration we should switch to manifest until
      58             : // SDL has an implemented HiDPI on Windows.
      59             : extern void wutil_EnableHiDPIOnWindows();
      60             : #endif
      61             : 
      62             : CVideoMode g_VideoMode;
      63             : 
      64             : class CVideoMode::CCursor
      65             : {
      66             : public:
      67             :     enum class CursorBackend
      68             :     {
      69             :         SDL,
      70             :         SYSTEM
      71             :     };
      72             : 
      73             :     CCursor();
      74             :     ~CCursor();
      75             : 
      76             :     void SetCursor(const CStrW& name);
      77             :     void ResetCursor();
      78             : 
      79             : private:
      80             :     CursorBackend m_CursorBackend = CursorBackend::SYSTEM;
      81             :     SDL_Surface* m_CursorSurface = nullptr;
      82             :     SDL_Cursor* m_Cursor = nullptr;
      83             :     CStrW m_CursorName;
      84             : };
      85             : 
      86           0 : CVideoMode::CCursor::CCursor()
      87             : {
      88           0 :     std::string cursorBackend;
      89           0 :     CFG_GET_VAL("cursorbackend", cursorBackend);
      90           0 :     if (cursorBackend == "sdl")
      91           0 :         m_CursorBackend = CursorBackend::SDL;
      92             :     else
      93           0 :         m_CursorBackend = CursorBackend::SYSTEM;
      94             : 
      95           0 :     ResetCursor();
      96           0 : }
      97             : 
      98           0 : CVideoMode::CCursor::~CCursor()
      99             : {
     100           0 :     if (m_Cursor)
     101           0 :         SDL_FreeCursor(m_Cursor);
     102           0 :     if (m_CursorSurface)
     103           0 :         SDL_FreeSurface(m_CursorSurface);
     104           0 : }
     105             : 
     106           0 : void CVideoMode::CCursor::SetCursor(const CStrW& name)
     107             : {
     108           0 :     if (m_CursorBackend == CursorBackend::SYSTEM || m_CursorName == name)
     109           0 :         return;
     110           0 :     m_CursorName = name;
     111             : 
     112           0 :     if (m_Cursor)
     113           0 :         SDL_FreeCursor(m_Cursor);
     114           0 :     if (m_CursorSurface)
     115           0 :         SDL_FreeSurface(m_CursorSurface);
     116             : 
     117           0 :     if (name.empty())
     118             :     {
     119           0 :         SDL_ShowCursor(SDL_DISABLE);
     120           0 :         return;
     121             :     }
     122             : 
     123           0 :     const VfsPath pathBaseName(VfsPath(L"art/textures/cursors") / name);
     124             : 
     125             :     // Read pixel offset of the cursor's hotspot [the bit of it that's
     126             :     // drawn at (g_mouse_x,g_mouse_y)] from file.
     127           0 :     int hotspotX = 0, hotspotY = 0;
     128           0 :     {
     129           0 :         const VfsPath pathHotspotName = pathBaseName.ChangeExtension(L".txt");
     130           0 :         std::shared_ptr<u8> buffer;
     131           0 :         size_t size;
     132           0 :         if (g_VFS->LoadFile(pathHotspotName, buffer, size) != INFO::OK)
     133             :         {
     134           0 :             LOGERROR("Can't load hotspot for cursor: %s", pathHotspotName.string8().c_str());
     135           0 :             return;
     136             :         }
     137           0 :         std::wstringstream s(std::wstring(reinterpret_cast<const wchar_t*>(buffer.get()), size));
     138           0 :         s >> hotspotX >> hotspotY;
     139             :     }
     140             : 
     141           0 :     const VfsPath pathImageName = pathBaseName.ChangeExtension(L".png");
     142             : 
     143           0 :     std::shared_ptr<u8> file;
     144           0 :     size_t fileSize;
     145           0 :     if (g_VFS->LoadFile(pathImageName, file, fileSize) != INFO::OK)
     146             :     {
     147           0 :         LOGERROR("Can't load image for cursor: %s", pathImageName.string8().c_str());
     148           0 :         return;
     149             :     }
     150             : 
     151           0 :     Tex t;
     152           0 :     if (t.decode(file, fileSize) != INFO::OK)
     153             :     {
     154           0 :         LOGERROR("Can't decode image for cursor");
     155           0 :         return;
     156             :     }
     157             : 
     158             :     // Convert to required BGRA format.
     159           0 :     const size_t flags = (t.m_Flags | TEX_BGR) & ~TEX_DXT;
     160           0 :     if (t.transform_to(flags) != INFO::OK)
     161             :     {
     162           0 :         LOGERROR("Can't transform image for cursor");
     163           0 :         return;
     164             :     }
     165           0 :     void* imageBGRA = t.get_data();
     166           0 :     if (!imageBGRA)
     167             :     {
     168           0 :         LOGERROR("Transformed image is empty for cursor");
     169           0 :         return;
     170             :     }
     171             : 
     172           0 :     m_CursorSurface = SDL_CreateRGBSurfaceFrom(imageBGRA,
     173           0 :         static_cast<int>(t.m_Width), static_cast<int>(t.m_Height), 32,
     174           0 :         static_cast<int>(t.m_Width * 4),
     175             :         0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
     176           0 :     if (!m_CursorSurface)
     177             :     {
     178           0 :         LOGERROR("Can't create surface for cursor: %s", SDL_GetError());
     179           0 :         return;
     180             :     }
     181           0 :     const float scale = g_VideoMode.GetScale();
     182           0 :     if (scale != 1.0)
     183             :     {
     184           0 :         SDL_Surface* scaledSurface = SDL_CreateRGBSurface(0,
     185           0 :             m_CursorSurface->w * scale,
     186           0 :             m_CursorSurface->h * scale, 32,
     187             :             0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
     188           0 :         if (!scaledSurface)
     189             :         {
     190           0 :             LOGERROR("Can't create scaled surface forcursor: %s", SDL_GetError());
     191           0 :             return;
     192             :         }
     193           0 :         if (SDL_BlitScaled(m_CursorSurface, nullptr, scaledSurface, nullptr))
     194             :             return;
     195           0 :         SDL_FreeSurface(m_CursorSurface);
     196           0 :         m_CursorSurface = scaledSurface;
     197             :     }
     198           0 :     m_Cursor = SDL_CreateColorCursor(m_CursorSurface, hotspotX, hotspotY);
     199           0 :     if (!m_Cursor)
     200             :     {
     201           0 :         LOGERROR("Can't create cursor: %s", SDL_GetError());
     202           0 :         return;
     203             :     }
     204             : 
     205           0 :     SDL_SetCursor(m_Cursor);
     206             : }
     207             : 
     208           0 : void CVideoMode::CCursor::ResetCursor()
     209             : {
     210           0 :     SetCursor(DEFAULT_CURSOR_NAME);
     211           0 : }
     212             : 
     213           0 : CVideoMode::CVideoMode() :
     214           0 :     m_WindowedW(DEFAULT_WINDOW_W), m_WindowedH(DEFAULT_WINDOW_H), m_WindowedX(0), m_WindowedY(0)
     215             : {
     216           0 : }
     217             : 
     218             : CVideoMode::~CVideoMode() = default;
     219             : 
     220           5 : void CVideoMode::ReadConfig()
     221             : {
     222           5 :     bool windowed = !m_ConfigFullscreen;
     223           5 :     CFG_GET_VAL("windowed", windowed);
     224           5 :     m_ConfigFullscreen = !windowed;
     225             : 
     226           5 :     CFG_GET_VAL("gui.scale", m_Scale);
     227             : 
     228           5 :     CFG_GET_VAL("xres", m_ConfigW);
     229           5 :     CFG_GET_VAL("yres", m_ConfigH);
     230           5 :     CFG_GET_VAL("bpp", m_ConfigBPP);
     231           5 :     CFG_GET_VAL("display", m_ConfigDisplay);
     232           5 :     CFG_GET_VAL("hidpi", m_ConfigEnableHiDPI);
     233           5 :     CFG_GET_VAL("vsync", m_ConfigVSync);
     234             : 
     235          10 :     CStr rendererBackend;
     236           5 :     CFG_GET_VAL("rendererbackend", rendererBackend);
     237           5 :     if (rendererBackend == "glarb")
     238           0 :         m_Backend = Backend::GL_ARB;
     239           5 :     else if (rendererBackend == "dummy")
     240           5 :         m_Backend = Backend::DUMMY;
     241             :     else
     242           0 :         m_Backend = Backend::GL;
     243           5 : }
     244             : 
     245           0 : bool CVideoMode::SetVideoMode(int w, int h, int bpp, bool fullscreen)
     246             : {
     247           0 :     Uint32 flags = 0;
     248           0 :     if (fullscreen)
     249             :     {
     250           0 :         bool borderlessFullscreen = true;
     251           0 :         CFG_GET_VAL("borderless.fullscreen", borderlessFullscreen);
     252           0 :         flags |= borderlessFullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN;
     253             :     }
     254             :     else
     255             :     {
     256           0 :         bool borderlessWindow = false;
     257           0 :         CFG_GET_VAL("borderless.window", borderlessWindow);
     258           0 :         if (borderlessWindow)
     259           0 :             flags |= SDL_WINDOW_BORDERLESS;
     260             :     }
     261             : 
     262           0 :     if (!m_Window)
     263             :     {
     264             : #if OS_WIN
     265             :         if (m_ConfigEnableHiDPI)
     266             :             wutil_EnableHiDPIOnWindows();
     267             : #endif
     268             :         // Note: these flags only take affect in SDL_CreateWindow
     269           0 :         flags |= SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
     270           0 :         if (m_ConfigEnableHiDPI)
     271           0 :             flags |= SDL_WINDOW_ALLOW_HIGHDPI;
     272           0 :         m_WindowedX = m_WindowedY = SDL_WINDOWPOS_CENTERED_DISPLAY(m_ConfigDisplay);
     273             : 
     274           0 :         m_Window = SDL_CreateWindow(main_window_name, m_WindowedX, m_WindowedY, w, h, flags);
     275           0 :         if (!m_Window)
     276             :         {
     277             :             // If fullscreen fails, try windowed mode
     278           0 :             if (fullscreen)
     279             :             {
     280           0 :                 LOGWARNING("Failed to set the video mode to fullscreen for the chosen resolution "
     281             :                     "%dx%d:%d (\"%hs\"), falling back to windowed mode",
     282             :                     w, h, bpp, SDL_GetError());
     283             :                 // Using default size for the window for now, as the attempted setting
     284             :                 // could be as large, or larger than the screen size.
     285           0 :                 return SetVideoMode(DEFAULT_WINDOW_W, DEFAULT_WINDOW_H, bpp, false);
     286             :             }
     287             :             else
     288             :             {
     289           0 :                 LOGERROR("SetVideoMode failed in SDL_CreateWindow: %dx%d:%d %d (\"%s\")",
     290             :                     w, h, bpp, fullscreen ? 1 : 0, SDL_GetError());
     291           0 :                 return false;
     292             :             }
     293             :         }
     294             : 
     295           0 :         if (SDL_SetWindowDisplayMode(m_Window, NULL) < 0)
     296             :         {
     297           0 :             LOGERROR("SetVideoMode failed in SDL_SetWindowDisplayMode: %dx%d:%d %d (\"%s\")",
     298             :                 w, h, bpp, fullscreen ? 1 : 0, SDL_GetError());
     299           0 :             return false;
     300             :         }
     301             : 
     302             : #if OS_WIN
     303             :         // We need to set the window for an error dialog.
     304             :         wutil_SetAppWindow(m_Window);
     305             : #endif
     306             : 
     307           0 :         if (!CreateBackendDevice(true))
     308             :         {
     309           0 :             LOGERROR("SetVideoMode failed in backend device creation: %dx%d:%d %d",
     310             :                 w, h, bpp, fullscreen ? 1 : 0);
     311           0 :             return false;
     312             :         }
     313             :     }
     314             :     else
     315             :     {
     316           0 :         if (m_IsFullscreen != fullscreen)
     317             :         {
     318           0 :             if (!fullscreen)
     319             :             {
     320             :                 // For some reason, when switching from fullscreen to windowed mode,
     321             :                 // we have to set the window size and position before and after switching
     322           0 :                 SDL_SetWindowSize(m_Window, w, h);
     323           0 :                 SDL_SetWindowPosition(m_Window, m_WindowedX, m_WindowedY);
     324             :             }
     325             : 
     326           0 :             if (SDL_SetWindowFullscreen(m_Window, flags) < 0)
     327             :             {
     328           0 :                 LOGERROR("SetVideoMode failed in SDL_SetWindowFullscreen: %dx%d:%d %d (\"%s\")",
     329             :                     w, h, bpp, fullscreen ? 1 : 0, SDL_GetError());
     330           0 :                 return false;
     331             :             }
     332             :         }
     333             : 
     334           0 :         if (!fullscreen)
     335             :         {
     336           0 :             SDL_SetWindowSize(m_Window, w, h);
     337           0 :             SDL_SetWindowPosition(m_Window, m_WindowedX, m_WindowedY);
     338             :         }
     339             :     }
     340             : 
     341             :     // Grab the current video settings
     342           0 :     SDL_GetWindowSize(m_Window, &m_CurrentW, &m_CurrentH);
     343           0 :     m_CurrentBPP = bpp;
     344             : 
     345           0 :     if (fullscreen)
     346           0 :         SDL_SetWindowGrab(m_Window, SDL_TRUE);
     347             :     else
     348           0 :         SDL_SetWindowGrab(m_Window, SDL_FALSE);
     349             : 
     350           0 :     m_IsFullscreen = fullscreen;
     351             : 
     352           0 :     g_xres = m_CurrentW;
     353           0 :     g_yres = m_CurrentH;
     354             : 
     355           0 :     return true;
     356             : }
     357             : 
     358           0 : bool CVideoMode::InitSDL()
     359             : {
     360           0 :     ENSURE(!m_IsInitialised);
     361             : 
     362           0 :     ReadConfig();
     363             : 
     364             :     // preferred video mode = current desktop settings
     365             :     // (command line params may override these)
     366             :     // TODO: handle multi-screen and HiDPI properly.
     367           0 :     SDL_DisplayMode mode;
     368           0 :     if (SDL_GetDesktopDisplayMode(0, &mode) == 0)
     369             :     {
     370           0 :         m_PreferredW = mode.w;
     371           0 :         m_PreferredH = mode.h;
     372           0 :         m_PreferredBPP = SDL_BITSPERPIXEL(mode.format);
     373           0 :         m_PreferredFreq = mode.refresh_rate;
     374             :     }
     375             : 
     376           0 :     int w = m_ConfigW;
     377           0 :     int h = m_ConfigH;
     378             : 
     379           0 :     if (m_ConfigFullscreen)
     380             :     {
     381             :         // If fullscreen and no explicit size set, default to the desktop resolution
     382           0 :         if (w == 0 || h == 0)
     383             :         {
     384           0 :             w = m_PreferredW;
     385           0 :             h = m_PreferredH;
     386             :         }
     387             :     }
     388             : 
     389             :     // If no size determined, default to something sensible
     390           0 :     if (w == 0 || h == 0)
     391             :     {
     392           0 :         w = DEFAULT_WINDOW_W;
     393           0 :         h = DEFAULT_WINDOW_H;
     394             :     }
     395             : 
     396           0 :     if (!m_ConfigFullscreen)
     397             :     {
     398             :         // Limit the window to the screen size (if known)
     399           0 :         if (m_PreferredW)
     400           0 :             w = std::min(w, m_PreferredW);
     401           0 :         if (m_PreferredH)
     402           0 :             h = std::min(h, m_PreferredH);
     403             :     }
     404             : 
     405           0 :     int bpp = GetBestBPP();
     406             : 
     407           0 :     SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
     408           0 :     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
     409           0 :     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
     410           0 :     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
     411             : 
     412           0 :     bool debugContext = false;
     413           0 :     CFG_GET_VAL("renderer.backend.debugcontext", debugContext);
     414           0 :     if (debugContext)
     415           0 :         SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
     416             : 
     417           0 :     bool forceGLVersion = false;
     418           0 :     CFG_GET_VAL("forceglversion", forceGLVersion);
     419           0 :     if (forceGLVersion)
     420             :     {
     421           0 :         CStr forceGLProfile = "compatibility";
     422           0 :         int forceGLMajorVersion = 3;
     423           0 :         int forceGLMinorVersion = 0;
     424           0 :         CFG_GET_VAL("forceglprofile", forceGLProfile);
     425           0 :         CFG_GET_VAL("forceglmajorversion", forceGLMajorVersion);
     426           0 :         CFG_GET_VAL("forceglminorversion", forceGLMinorVersion);
     427             : 
     428           0 :         int profile = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
     429           0 :         if (forceGLProfile == "es")
     430             :             profile = SDL_GL_CONTEXT_PROFILE_ES;
     431           0 :         else if (forceGLProfile == "core")
     432             :             profile = SDL_GL_CONTEXT_PROFILE_CORE;
     433           0 :         else if (forceGLProfile != "compatibility")
     434           0 :             LOGWARNING("Unknown force GL profile '%s', compatibility profile is used", forceGLProfile.c_str());
     435             : 
     436           0 :         if (forceGLMajorVersion < 1 || forceGLMinorVersion < 0)
     437             :         {
     438           0 :             LOGERROR("Unsupported force GL version: %d.%d", forceGLMajorVersion, forceGLMinorVersion);
     439             :         }
     440             :         else
     441             :         {
     442           0 :             SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile);
     443           0 :             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, forceGLMajorVersion);
     444           0 :             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, forceGLMinorVersion);
     445             :         }
     446             :     }
     447             :     else
     448             :     {
     449             : #if CONFIG2_GLES
     450             :         // Require GLES 2.0
     451             :         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
     452             :         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
     453             :         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
     454             : #else
     455             :         // Some macOS and MESA drivers might not create a context even if they can
     456             :         // with the core profile. So disable it for a while until we can guarantee
     457             :         // its creation.
     458             : #if OS_WIN
     459             :         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
     460             : #endif
     461           0 :         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
     462           0 :         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
     463             : #endif
     464             :     }
     465             : 
     466           0 :     if (!SetVideoMode(w, h, bpp, m_ConfigFullscreen))
     467             :     {
     468             :         // Fall back to a smaller depth buffer
     469             :         // (The rendering may be ugly but this helps when running in VMware)
     470           0 :         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
     471             : 
     472           0 :         if (!SetVideoMode(w, h, bpp, m_ConfigFullscreen))
     473             :             return false;
     474             :     }
     475             : 
     476           0 :     SDL_GL_SetSwapInterval(m_ConfigVSync ? 1 : 0);
     477             : 
     478             :     // Work around a bug in the proprietary Linux ATI driver (at least versions 8.16.20 and 8.14.13).
     479             :     // The driver appears to register its own atexit hook on context creation.
     480             :     // If this atexit hook is called before SDL_Quit destroys the OpenGL context,
     481             :     // some kind of double-free problem causes a crash and lockup in the driver.
     482             :     // Calling SDL_Quit twice appears to be harmless, though, and avoids the problem
     483             :     // by destroying the context *before* the driver's atexit hook is called.
     484             :     // (Note that atexit hooks are guaranteed to be called in reverse order of their registration.)
     485           0 :     atexit(SDL_Quit);
     486             :     // End work around.
     487             : 
     488           0 :     m_IsInitialised = true;
     489             : 
     490           0 :     if (!m_ConfigFullscreen)
     491             :     {
     492           0 :         m_WindowedW = w;
     493           0 :         m_WindowedH = h;
     494             :     }
     495             : 
     496           0 :     SetWindowIcon();
     497             : 
     498           0 :     m_Cursor = std::make_unique<CCursor>();
     499             : 
     500           0 :     return true;
     501             : }
     502             : 
     503           5 : bool CVideoMode::InitNonSDL()
     504             : {
     505           5 :     ENSURE(!m_IsInitialised);
     506             : 
     507           5 :     ReadConfig();
     508             : 
     509           5 :     m_IsInitialised = true;
     510             : 
     511           5 :     return true;
     512             : }
     513             : 
     514           5 : void CVideoMode::Shutdown()
     515             : {
     516           5 :     ENSURE(m_IsInitialised);
     517             : 
     518           5 :     m_Cursor.reset();
     519             : 
     520           5 :     m_IsFullscreen = false;
     521           5 :     m_IsInitialised = false;
     522           5 :     m_BackendDevice.reset();
     523           5 :     if (m_Window)
     524             :     {
     525           0 :         SDL_DestroyWindow(m_Window);
     526           0 :         m_Window = nullptr;
     527             :     }
     528           5 : }
     529             : 
     530           5 : bool CVideoMode::CreateBackendDevice(const bool createSDLContext)
     531             : {
     532           5 :     if (m_Backend == Backend::DUMMY)
     533             :     {
     534           5 :         m_BackendDevice = std::make_unique<Renderer::Backend::Dummy::CDevice>();
     535           5 :         return true;
     536             :     }
     537           0 :     m_BackendDevice = Renderer::Backend::GL::CDevice::Create(createSDLContext ? m_Window : nullptr, m_Backend == Backend::GL_ARB);
     538           0 :     if (!m_BackendDevice && m_Backend == Backend::GL)
     539             :     {
     540           0 :         LOGERROR("Unable to create device for GL backend, switching to ARB.", static_cast<int>(m_Backend));
     541           0 :         m_Backend = Backend::GL_ARB;
     542           0 :         return CreateBackendDevice(createSDLContext);
     543             :     }
     544             :     return !!m_BackendDevice;
     545             : }
     546             : 
     547           0 : bool CVideoMode::ResizeWindow(int w, int h)
     548             : {
     549           0 :     ENSURE(m_IsInitialised);
     550             : 
     551             :     // Ignore if not windowed
     552           0 :     if (m_IsFullscreen)
     553             :         return true;
     554             : 
     555             :     // Ignore if the size hasn't changed
     556           0 :     if (w == m_WindowedW && h == m_WindowedH)
     557             :         return true;
     558             : 
     559           0 :     int bpp = GetBestBPP();
     560             : 
     561           0 :     if (!SetVideoMode(w, h, bpp, false))
     562             :         return false;
     563             : 
     564           0 :     m_WindowedW = w;
     565           0 :     m_WindowedH = h;
     566             : 
     567           0 :     UpdateRenderer(w, h);
     568             : 
     569           0 :     return true;
     570             : }
     571             : 
     572           0 : void CVideoMode::Rescale(float scale)
     573             : {
     574           0 :     ENSURE(m_IsInitialised);
     575           0 :     m_Scale = scale;
     576           0 :     UpdateRenderer(m_CurrentW, m_CurrentH);
     577           0 : }
     578             : 
     579           8 : float CVideoMode::GetScale() const
     580             : {
     581           0 :     return m_Scale;
     582             : }
     583             : 
     584           0 : bool CVideoMode::SetFullscreen(bool fullscreen)
     585             : {
     586             :     // This might get called before initialisation by psDisplayError;
     587             :     // if so then silently fail
     588           0 :     if (!m_IsInitialised)
     589             :         return false;
     590             : 
     591             :     // Check whether this is actually a change
     592           0 :     if (fullscreen == m_IsFullscreen)
     593             :         return true;
     594             : 
     595           0 :     if (!m_IsFullscreen)
     596             :     {
     597             :         // Windowed -> fullscreen:
     598             : 
     599           0 :         int w = 0, h = 0;
     600             : 
     601             :         // If a fullscreen size was configured, use that; else use the desktop size; else use a default
     602           0 :         if (m_ConfigFullscreen)
     603             :         {
     604           0 :             w = m_ConfigW;
     605           0 :             h = m_ConfigH;
     606             :         }
     607           0 :         if (w == 0 || h == 0)
     608             :         {
     609           0 :             w = m_PreferredW;
     610           0 :             h = m_PreferredH;
     611             :         }
     612           0 :         if (w == 0 || h == 0)
     613             :         {
     614           0 :             w = DEFAULT_FULLSCREEN_W;
     615           0 :             h = DEFAULT_FULLSCREEN_H;
     616             :         }
     617             : 
     618           0 :         int bpp = GetBestBPP();
     619             : 
     620           0 :         if (!SetVideoMode(w, h, bpp, fullscreen))
     621             :             return false;
     622             : 
     623           0 :         UpdateRenderer(m_CurrentW, m_CurrentH);
     624             : 
     625           0 :         return true;
     626             :     }
     627             :     else
     628             :     {
     629             :         // Fullscreen -> windowed:
     630             : 
     631             :         // Go back to whatever the previous window size was
     632           0 :         int w = m_WindowedW, h = m_WindowedH;
     633             : 
     634           0 :         int bpp = GetBestBPP();
     635             : 
     636           0 :         if (!SetVideoMode(w, h, bpp, fullscreen))
     637             :             return false;
     638             : 
     639           0 :         UpdateRenderer(w, h);
     640             : 
     641           0 :         return true;
     642             :     }
     643             : }
     644             : 
     645           0 : bool CVideoMode::ToggleFullscreen()
     646             : {
     647           0 :     return SetFullscreen(!m_IsFullscreen);
     648             : }
     649             : 
     650           0 : bool CVideoMode::IsInFullscreen() const
     651             : {
     652           0 :     return m_IsFullscreen;
     653             : }
     654             : 
     655           0 : void CVideoMode::UpdatePosition(int x, int y)
     656             : {
     657           0 :     if (!m_IsFullscreen)
     658             :     {
     659           0 :         m_WindowedX = x;
     660           0 :         m_WindowedY = y;
     661             :     }
     662           0 : }
     663             : 
     664           0 : void CVideoMode::UpdateRenderer(int w, int h)
     665             : {
     666           0 :     if (w < 2) w = 2; // avoid GL errors caused by invalid sizes
     667           0 :     if (h < 2) h = 2;
     668             : 
     669           0 :     g_xres = w;
     670           0 :     g_yres = h;
     671             : 
     672           0 :     SViewPort vp = { 0, 0, w, h };
     673             : 
     674           0 :     if (CRenderer::IsInitialised())
     675             :     {
     676           0 :         g_Renderer.SetViewport(vp);
     677           0 :         g_Renderer.Resize(w, h);
     678             :     }
     679             : 
     680           0 :     if (g_GUI)
     681           0 :         g_GUI->UpdateResolution();
     682             : 
     683           0 :     if (g_Console)
     684           0 :         g_Console->UpdateScreenSize(w, h);
     685             : 
     686           0 :     if (g_Game)
     687           0 :         g_Game->GetView()->SetViewport(vp);
     688           0 : }
     689             : 
     690           0 : int CVideoMode::GetBestBPP()
     691             : {
     692           0 :     if (m_ConfigBPP)
     693             :         return m_ConfigBPP;
     694           0 :     if (m_PreferredBPP)
     695           0 :         return m_PreferredBPP;
     696             :     return 32;
     697             : }
     698             : 
     699           0 : int CVideoMode::GetXRes() const
     700             : {
     701           0 :     ENSURE(m_IsInitialised);
     702           0 :     return m_CurrentW;
     703             : }
     704             : 
     705           0 : int CVideoMode::GetYRes() const
     706             : {
     707           0 :     ENSURE(m_IsInitialised);
     708           0 :     return m_CurrentH;
     709             : }
     710             : 
     711           0 : int CVideoMode::GetBPP() const
     712             : {
     713           0 :     ENSURE(m_IsInitialised);
     714           0 :     return m_CurrentBPP;
     715             : }
     716             : 
     717           0 : bool CVideoMode::IsVSyncEnabled() const
     718             : {
     719           0 :     ENSURE(m_IsInitialised);
     720           0 :     return m_ConfigVSync;
     721             : }
     722             : 
     723           0 : int CVideoMode::GetDesktopXRes() const
     724             : {
     725           0 :     ENSURE(m_IsInitialised);
     726           0 :     return m_PreferredW;
     727             : }
     728             : 
     729           0 : int CVideoMode::GetDesktopYRes() const
     730             : {
     731           0 :     ENSURE(m_IsInitialised);
     732           0 :     return m_PreferredH;
     733             : }
     734             : 
     735           0 : int CVideoMode::GetDesktopBPP() const
     736             : {
     737           0 :     ENSURE(m_IsInitialised);
     738           0 :     return m_PreferredBPP;
     739             : }
     740             : 
     741           0 : int CVideoMode::GetDesktopFreq() const
     742             : {
     743           0 :     ENSURE(m_IsInitialised);
     744           0 :     return m_PreferredFreq;
     745             : }
     746             : 
     747           0 : SDL_Window* CVideoMode::GetWindow()
     748             : {
     749           0 :     ENSURE(m_IsInitialised);
     750           0 :     return m_Window;
     751             : }
     752             : 
     753           0 : void CVideoMode::SetWindowIcon()
     754             : {
     755             :     // The window icon should be kept outside of art/textures/, or else it will be converted
     756             :     // to DDS by the archive builder and will become unusable here. Using DDS makes BGRA
     757             :     // conversion needlessly complicated.
     758           0 :     std::shared_ptr<u8> iconFile;
     759           0 :     size_t iconFileSize;
     760           0 :     if (g_VFS->LoadFile("art/icons/window.png", iconFile, iconFileSize) != INFO::OK)
     761             :     {
     762           0 :         LOGWARNING("Window icon not found.");
     763           0 :         return;
     764             :     }
     765             : 
     766           0 :     Tex iconTexture;
     767           0 :     if (iconTexture.decode(iconFile, iconFileSize) != INFO::OK)
     768           0 :         return;
     769             : 
     770             :     // Convert to required BGRA format.
     771           0 :     const size_t iconFlags = (iconTexture.m_Flags | TEX_BGR) & ~TEX_DXT;
     772           0 :     if (iconTexture.transform_to(iconFlags) != INFO::OK)
     773             :         return;
     774             : 
     775           0 :     void* bgra_img = iconTexture.get_data();
     776           0 :     if (!bgra_img)
     777             :         return;
     778             : 
     779           0 :     SDL_Surface *iconSurface = SDL_CreateRGBSurfaceFrom(bgra_img,
     780           0 :         iconTexture.m_Width, iconTexture.m_Height, 32, iconTexture.m_Width * 4,
     781             :         0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
     782           0 :     if (!iconSurface)
     783             :         return;
     784             : 
     785           0 :     SDL_SetWindowIcon(m_Window, iconSurface);
     786           0 :     SDL_FreeSurface(iconSurface);
     787             : }
     788             : 
     789           0 : void CVideoMode::SetCursor(const CStrW& name)
     790             : {
     791           0 :     if (m_Cursor)
     792           0 :         m_Cursor->SetCursor(name);
     793           0 : }
     794             : 
     795           6 : void CVideoMode::ResetCursor()
     796             : {
     797          12 :     if (m_Cursor)
     798           0 :         m_Cursor->ResetCursor();
     799           6 : }

Generated by: LCOV version 1.13