1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "VideoMode.h"
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/sysdep/os.h"
27 #include "lib/tex/tex.h"
28 #include "ps/CConsole.h"
29 #include "ps/CLogger.h"
30 #include "ps/ConfigDB.h"
32 #if OS_MACOSX && SDL_VERSION_ATLEAST(2, 0, 6)
33 #include "ps/DllLoader.h"
35 #include "ps/Filesystem.h"
37 #include "ps/GameSetup/Config.h"
38 #include "ps/Pyrogenesis.h"
39 #include "renderer/backend/dummy/DeviceForward.h"
40 #include "renderer/backend/gl/DeviceForward.h"
41 #include "renderer/backend/IDevice.h"
42 #include "renderer/backend/vulkan/DeviceForward.h"
43 #include "renderer/Renderer.h"
45 #include <string_view>
47 #if OS_MACOSX && SDL_VERSION_ATLEAST(2, 0, 6)
48 #include <SDL_vulkan.h>
55 int DEFAULT_WINDOW_W
= 1024;
56 int DEFAULT_WINDOW_H
= 768;
58 int DEFAULT_FULLSCREEN_W
= 1024;
59 int DEFAULT_FULLSCREEN_H
= 768;
61 const wchar_t DEFAULT_CURSOR_NAME
[] = L
"default-arrow";
63 Renderer::Backend::Backend
GetFallbackBackend(const Renderer::Backend::Backend backend
)
65 Renderer::Backend::Backend fallback
= Renderer::Backend::Backend::DUMMY
;
66 // We use a switch instead of a list to have compile-time checks for missed
67 // values and because a linear priority list doesn't work for general case.
70 case Renderer::Backend::Backend::GL
:
71 fallback
= Renderer::Backend::Backend::GL_ARB
;
73 case Renderer::Backend::Backend::GL_ARB
:
74 fallback
= Renderer::Backend::Backend::DUMMY
;
76 case Renderer::Backend::Backend::DUMMY
:
78 case Renderer::Backend::Backend::VULKAN
:
79 fallback
= Renderer::Backend::Backend::GL
;
85 std::string_view
GetBackendName(const Renderer::Backend::Backend backend
)
87 std::string_view name
{"Unknown"};
90 case Renderer::Backend::Backend::GL
:
93 case Renderer::Backend::Backend::GL_ARB
:
96 case Renderer::Backend::Backend::DUMMY
:
99 case Renderer::Backend::Backend::VULKAN
:
106 } // anonymous namespace
109 // We can't include wutil directly because GL headers conflict with Windows
110 // until we use a proper GL loader.
111 extern void wutil_SetAppWindow(SDL_Window
* window
);
113 // After a proper HiDPI integration we should switch to manifest until
114 // SDL has an implemented HiDPI on Windows.
115 extern void wutil_EnableHiDPIOnWindows();
118 CVideoMode g_VideoMode
;
120 class CVideoMode::CCursor
123 enum class CursorBackend
132 void SetCursor(const CStrW
& name
);
136 CursorBackend m_CursorBackend
= CursorBackend::SYSTEM
;
137 SDL_Surface
* m_CursorSurface
= nullptr;
138 SDL_Cursor
* m_Cursor
= nullptr;
142 CVideoMode::CCursor::CCursor()
144 std::string cursorBackend
;
145 CFG_GET_VAL("cursorbackend", cursorBackend
);
146 if (cursorBackend
== "sdl")
147 m_CursorBackend
= CursorBackend::SDL
;
149 m_CursorBackend
= CursorBackend::SYSTEM
;
154 CVideoMode::CCursor::~CCursor()
157 SDL_FreeCursor(m_Cursor
);
159 SDL_FreeSurface(m_CursorSurface
);
162 void CVideoMode::CCursor::SetCursor(const CStrW
& name
)
164 if (m_CursorBackend
== CursorBackend::SYSTEM
|| m_CursorName
== name
)
169 SDL_FreeCursor(m_Cursor
);
171 SDL_FreeSurface(m_CursorSurface
);
175 SDL_ShowCursor(SDL_DISABLE
);
179 const VfsPath
pathBaseName(VfsPath(L
"art/textures/cursors") / name
);
181 // Read pixel offset of the cursor's hotspot [the bit of it that's
182 // drawn at (g_mouse_x,g_mouse_y)] from file.
183 int hotspotX
= 0, hotspotY
= 0;
185 const VfsPath pathHotspotName
= pathBaseName
.ChangeExtension(L
".txt");
186 std::shared_ptr
<u8
> buffer
;
188 if (g_VFS
->LoadFile(pathHotspotName
, buffer
, size
) != INFO::OK
)
190 LOGERROR("Can't load hotspot for cursor: %s", pathHotspotName
.string8().c_str());
193 std::wstringstream
s(std::wstring(reinterpret_cast<const wchar_t*>(buffer
.get()), size
));
194 s
>> hotspotX
>> hotspotY
;
197 const VfsPath pathImageName
= pathBaseName
.ChangeExtension(L
".png");
199 std::shared_ptr
<u8
> file
;
201 if (g_VFS
->LoadFile(pathImageName
, file
, fileSize
) != INFO::OK
)
203 LOGERROR("Can't load image for cursor: %s", pathImageName
.string8().c_str());
208 if (t
.decode(file
, fileSize
) != INFO::OK
)
210 LOGERROR("Can't decode image for cursor");
214 // Convert to required BGRA format.
215 const size_t flags
= (t
.m_Flags
| TEX_BGR
) & ~TEX_DXT
;
216 if (t
.transform_to(flags
) != INFO::OK
)
218 LOGERROR("Can't transform image for cursor");
221 void* imageBGRA
= t
.get_data();
224 LOGERROR("Transformed image is empty for cursor");
228 m_CursorSurface
= SDL_CreateRGBSurfaceFrom(imageBGRA
,
229 static_cast<int>(t
.m_Width
), static_cast<int>(t
.m_Height
), 32,
230 static_cast<int>(t
.m_Width
* 4),
231 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
232 if (!m_CursorSurface
)
234 LOGERROR("Can't create surface for cursor: %s", SDL_GetError());
237 const float scale
= g_VideoMode
.GetScale();
240 SDL_Surface
* scaledSurface
= SDL_CreateRGBSurface(0,
241 m_CursorSurface
->w
* scale
,
242 m_CursorSurface
->h
* scale
, 32,
243 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
246 LOGERROR("Can't create scaled surface forcursor: %s", SDL_GetError());
249 if (SDL_BlitScaled(m_CursorSurface
, nullptr, scaledSurface
, nullptr))
251 SDL_FreeSurface(m_CursorSurface
);
252 m_CursorSurface
= scaledSurface
;
254 m_Cursor
= SDL_CreateColorCursor(m_CursorSurface
, hotspotX
, hotspotY
);
257 LOGERROR("Can't create cursor: %s", SDL_GetError());
261 SDL_SetCursor(m_Cursor
);
264 void CVideoMode::CCursor::ResetCursor()
266 SetCursor(DEFAULT_CURSOR_NAME
);
269 CVideoMode::CVideoMode() :
270 m_WindowedW(DEFAULT_WINDOW_W
), m_WindowedH(DEFAULT_WINDOW_H
), m_WindowedX(0), m_WindowedY(0)
274 CVideoMode::~CVideoMode() = default;
276 void CVideoMode::ReadConfig()
278 bool windowed
= !m_ConfigFullscreen
;
279 CFG_GET_VAL("windowed", windowed
);
280 m_ConfigFullscreen
= !windowed
;
282 CFG_GET_VAL("gui.scale", m_Scale
);
284 CFG_GET_VAL("xres", m_ConfigW
);
285 CFG_GET_VAL("yres", m_ConfigH
);
286 CFG_GET_VAL("bpp", m_ConfigBPP
);
287 CFG_GET_VAL("display", m_ConfigDisplay
);
288 CFG_GET_VAL("hidpi", m_ConfigEnableHiDPI
);
289 CFG_GET_VAL("vsync", m_ConfigVSync
);
291 CStr rendererBackend
;
292 CFG_GET_VAL("rendererbackend", rendererBackend
);
293 if (rendererBackend
== "glarb")
294 m_Backend
= Renderer::Backend::Backend::GL_ARB
;
295 else if (rendererBackend
== "dummy")
296 m_Backend
= Renderer::Backend::Backend::DUMMY
;
297 else if (rendererBackend
== "vulkan")
298 m_Backend
= Renderer::Backend::Backend::VULKAN
;
300 m_Backend
= Renderer::Backend::Backend::GL
;
303 if (m_ConfigEnableHiDPI
)
304 wutil_EnableHiDPIOnWindows();
308 bool CVideoMode::SetVideoMode(int w
, int h
, int bpp
, bool fullscreen
)
313 bool borderlessFullscreen
= true;
314 CFG_GET_VAL("borderless.fullscreen", borderlessFullscreen
);
315 flags
|= borderlessFullscreen
? SDL_WINDOW_FULLSCREEN_DESKTOP
: SDL_WINDOW_FULLSCREEN
;
319 bool borderlessWindow
= false;
320 CFG_GET_VAL("borderless.window", borderlessWindow
);
321 if (borderlessWindow
)
322 flags
|= SDL_WINDOW_BORDERLESS
;
327 const bool isGLBackend
=
328 m_Backend
== Renderer::Backend::Backend::GL
||
329 m_Backend
== Renderer::Backend::Backend::GL_ARB
;
332 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL
, 1);
333 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE
, 24);
334 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE
, 8);
335 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER
, 1);
337 bool debugContext
= false;
338 CFG_GET_VAL("renderer.backend.debugcontext", debugContext
);
340 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS
, SDL_GL_CONTEXT_DEBUG_FLAG
);
342 bool forceGLVersion
= false;
343 CFG_GET_VAL("forceglversion", forceGLVersion
);
346 CStr forceGLProfile
= "compatibility";
347 int forceGLMajorVersion
= 3;
348 int forceGLMinorVersion
= 0;
349 CFG_GET_VAL("forceglprofile", forceGLProfile
);
350 CFG_GET_VAL("forceglmajorversion", forceGLMajorVersion
);
351 CFG_GET_VAL("forceglminorversion", forceGLMinorVersion
);
353 int profile
= SDL_GL_CONTEXT_PROFILE_COMPATIBILITY
;
354 if (forceGLProfile
== "es")
355 profile
= SDL_GL_CONTEXT_PROFILE_ES
;
356 else if (forceGLProfile
== "core")
357 profile
= SDL_GL_CONTEXT_PROFILE_CORE
;
358 else if (forceGLProfile
!= "compatibility")
359 LOGWARNING("Unknown force GL profile '%s', compatibility profile is used", forceGLProfile
.c_str());
361 if (forceGLMajorVersion
< 1 || forceGLMinorVersion
< 0)
363 LOGERROR("Unsupported force GL version: %d.%d", forceGLMajorVersion
, forceGLMinorVersion
);
367 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK
, profile
);
368 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION
, forceGLMajorVersion
);
369 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION
, forceGLMinorVersion
);
376 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK
, SDL_GL_CONTEXT_PROFILE_ES
);
377 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION
, 2);
378 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION
, 0);
380 // Some macOS and MESA drivers might not create a context even if they can
381 // with the core profile. So disable it for a while until we can guarantee
384 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK
, SDL_GL_CONTEXT_PROFILE_CORE
);
386 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION
, 2);
387 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION
, 1);
392 // Note: these flags only take affect in SDL_CreateWindow
393 flags
|= SDL_WINDOW_SHOWN
| SDL_WINDOW_RESIZABLE
;
394 if (m_ConfigEnableHiDPI
)
395 flags
|= SDL_WINDOW_ALLOW_HIGHDPI
;
397 flags
|= SDL_WINDOW_OPENGL
;
398 else if (m_Backend
== Renderer::Backend::Backend::VULKAN
)
399 flags
|= SDL_WINDOW_VULKAN
;
400 m_WindowedX
= m_WindowedY
= SDL_WINDOWPOS_CENTERED_DISPLAY(m_ConfigDisplay
);
402 #if OS_MACOSX && SDL_VERSION_ATLEAST(2, 0, 6)
403 if (m_Backend
== Renderer::Backend::Backend::VULKAN
)
405 // MoltenVK - enable full component swizzling support.
406 setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
407 CStr fullPathToVulkanLibrary
= DllLoader::GenerateFilename("MoltenVK", "", ".dylib");
408 // MoltenVK - only print warnings and errors.
409 setenv("MVK_CONFIG_LOG_LEVEL", "2", 1);
410 if (SDL_Vulkan_LoadLibrary(fullPathToVulkanLibrary
.c_str()) != 0)
412 LOGWARNING("Failed to load %s.", fullPathToVulkanLibrary
.c_str());
413 DowngradeBackendSettingAfterCreationFailure();
414 return SetVideoMode(w
, h
, bpp
, fullscreen
);
417 LOGMESSAGE("Loaded %s.", fullPathToVulkanLibrary
.c_str());
421 m_Window
= SDL_CreateWindow(main_window_name
, m_WindowedX
, m_WindowedY
, w
, h
, flags
);
424 // SDL might fail to create a window in case of missing a Vulkan driver.
425 if (m_Backend
== Renderer::Backend::Backend::VULKAN
)
427 LOGWARNING("Failed to create a Vulkan window: %s", SDL_GetError());
428 DowngradeBackendSettingAfterCreationFailure();
429 return SetVideoMode(w
, h
, bpp
, fullscreen
);
432 // If fullscreen fails, try windowed mode
435 LOGWARNING("Failed to set the video mode to fullscreen for the chosen resolution "
436 "%dx%d:%d (\"%hs\"), falling back to windowed mode",
437 w
, h
, bpp
, SDL_GetError());
438 // Using default size for the window for now, as the attempted setting
439 // could be as large, or larger than the screen size.
440 return SetVideoMode(DEFAULT_WINDOW_W
, DEFAULT_WINDOW_H
, bpp
, false);
447 SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE
, &depthSize
);
450 // Fall back to a smaller depth buffer
451 // (The rendering may be ugly but this helps when running in VMware)
452 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE
, 16);
454 return SetVideoMode(w
, h
, bpp
, fullscreen
);
458 LOGERROR("SetVideoMode failed in SDL_CreateWindow: %dx%d:%d %d (\"%s\")",
459 w
, h
, bpp
, fullscreen
? 1 : 0, SDL_GetError());
464 if (SDL_SetWindowDisplayMode(m_Window
, NULL
) < 0)
466 LOGERROR("SetVideoMode failed in SDL_SetWindowDisplayMode: %dx%d:%d %d (\"%s\")",
467 w
, h
, bpp
, fullscreen
? 1 : 0, SDL_GetError());
472 // We need to set the window for an error dialog.
473 wutil_SetAppWindow(m_Window
);
476 if (!TryCreateBackendDevice(m_Window
))
478 DowngradeBackendSettingAfterCreationFailure();
479 SDL_DestroyWindow(m_Window
);
481 return SetVideoMode(w
, h
, bpp
, fullscreen
);
485 SDL_GL_SetSwapInterval(m_ConfigVSync
? 1 : 0);
489 if (m_IsFullscreen
!= fullscreen
)
493 // For some reason, when switching from fullscreen to windowed mode,
494 // we have to set the window size and position before and after switching
495 SDL_SetWindowSize(m_Window
, w
, h
);
496 SDL_SetWindowPosition(m_Window
, m_WindowedX
, m_WindowedY
);
499 if (SDL_SetWindowFullscreen(m_Window
, flags
) < 0)
501 LOGERROR("SetVideoMode failed in SDL_SetWindowFullscreen: %dx%d:%d %d (\"%s\")",
502 w
, h
, bpp
, fullscreen
? 1 : 0, SDL_GetError());
509 SDL_SetWindowSize(m_Window
, w
, h
);
510 SDL_SetWindowPosition(m_Window
, m_WindowedX
, m_WindowedY
);
514 // Grab the current video settings
515 SDL_GetWindowSize(m_Window
, &m_CurrentW
, &m_CurrentH
);
518 // #545: we need to constrain the window in fullscreen mode to avoid mouse
519 // "falling out" of the window in case of multiple displays.
520 bool mouseGrabInFullscreen
= true;
521 CFG_GET_VAL("window.mousegrabinfullscreen", mouseGrabInFullscreen
);
522 bool mouseGrabInWindowMode
= false;
523 CFG_GET_VAL("window.mousegrabinwindowmode", mouseGrabInWindowMode
);
525 if (fullscreen
? mouseGrabInFullscreen
: mouseGrabInWindowMode
)
526 SDL_SetWindowGrab(m_Window
, SDL_TRUE
);
528 SDL_SetWindowGrab(m_Window
, SDL_FALSE
);
530 m_IsFullscreen
= fullscreen
;
538 bool CVideoMode::InitSDL()
540 ENSURE(!m_IsInitialised
);
544 // preferred video mode = current desktop settings
545 // (command line params may override these)
546 // TODO: handle multi-screen and HiDPI properly.
547 SDL_DisplayMode mode
;
548 if (SDL_GetDesktopDisplayMode(0, &mode
) == 0)
550 m_PreferredW
= mode
.w
;
551 m_PreferredH
= mode
.h
;
552 m_PreferredBPP
= SDL_BITSPERPIXEL(mode
.format
);
553 m_PreferredFreq
= mode
.refresh_rate
;
559 if (m_ConfigFullscreen
)
561 // If fullscreen and no explicit size set, default to the desktop resolution
562 if (w
== 0 || h
== 0)
569 // If no size determined, default to something sensible
570 if (w
== 0 || h
== 0)
572 w
= DEFAULT_WINDOW_W
;
573 h
= DEFAULT_WINDOW_H
;
576 if (!m_ConfigFullscreen
)
578 // Limit the window to the screen size (if known)
580 w
= std::min(w
, m_PreferredW
);
582 h
= std::min(h
, m_PreferredH
);
585 const int bpp
= GetBestBPP();
586 if (!SetVideoMode(w
, h
, bpp
, m_ConfigFullscreen
))
589 // Work around a bug in the proprietary Linux ATI driver (at least versions 8.16.20 and 8.14.13).
590 // The driver appears to register its own atexit hook on context creation.
591 // If this atexit hook is called before SDL_Quit destroys the OpenGL context,
592 // some kind of double-free problem causes a crash and lockup in the driver.
593 // Calling SDL_Quit twice appears to be harmless, though, and avoids the problem
594 // by destroying the context *before* the driver's atexit hook is called.
595 // (Note that atexit hooks are guaranteed to be called in reverse order of their registration.)
599 m_IsInitialised
= true;
601 if (!m_ConfigFullscreen
)
609 m_Cursor
= std::make_unique
<CCursor
>();
614 bool CVideoMode::InitNonSDL()
616 ENSURE(!m_IsInitialised
);
620 m_IsInitialised
= true;
625 void CVideoMode::Shutdown()
627 ENSURE(m_IsInitialised
);
631 m_IsFullscreen
= false;
632 m_IsInitialised
= false;
633 m_BackendDevice
.reset();
636 SDL_DestroyWindow(m_Window
);
641 bool CVideoMode::CreateBackendDevice(const bool createSDLContext
)
643 if (!createSDLContext
&& m_Backend
== Renderer::Backend::Backend::VULKAN
)
644 m_Backend
= Renderer::Backend::Backend::GL
;
645 SDL_Window
* window
= createSDLContext
? m_Window
: nullptr;
646 while (m_Backend
!= Renderer::Backend::Backend::DUMMY
)
648 if (TryCreateBackendDevice(window
))
650 DowngradeBackendSettingAfterCreationFailure();
652 return TryCreateBackendDevice(window
);
655 bool CVideoMode::TryCreateBackendDevice(SDL_Window
* window
)
659 case Renderer::Backend::Backend::GL
:
660 m_BackendDevice
= Renderer::Backend::GL::CreateDevice(window
, false);
662 case Renderer::Backend::Backend::GL_ARB
:
663 m_BackendDevice
= Renderer::Backend::GL::CreateDevice(window
, true);
665 case Renderer::Backend::Backend::DUMMY
:
666 m_BackendDevice
= Renderer::Backend::Dummy::CreateDevice(window
);
667 ENSURE(m_BackendDevice
);
669 case Renderer::Backend::Backend::VULKAN
:
670 m_BackendDevice
= Renderer::Backend::Vulkan::CreateDevice(window
);
673 return static_cast<bool>(m_BackendDevice
);
676 void CVideoMode::DowngradeBackendSettingAfterCreationFailure()
678 const Renderer::Backend::Backend fallback
= GetFallbackBackend(m_Backend
);
679 LOGERROR("Unable to create device for %s backend, switching to %s.",
680 GetBackendName(m_Backend
), GetBackendName(fallback
));
681 m_Backend
= fallback
;
684 bool CVideoMode::ResizeWindow(int w
, int h
)
686 ENSURE(m_IsInitialised
);
688 // Ignore if not windowed
692 // Ignore if the size hasn't changed
693 if (w
== m_WindowedW
&& h
== m_WindowedH
)
696 int bpp
= GetBestBPP();
698 if (!SetVideoMode(w
, h
, bpp
, false))
704 UpdateRenderer(w
, h
);
709 void CVideoMode::Rescale(float scale
)
711 ENSURE(m_IsInitialised
);
713 UpdateRenderer(m_CurrentW
, m_CurrentH
);
716 float CVideoMode::GetScale() const
721 bool CVideoMode::SetFullscreen(bool fullscreen
)
723 // This might get called before initialisation by psDisplayError;
724 // if so then silently fail
725 if (!m_IsInitialised
)
728 // Check whether this is actually a change
729 if (fullscreen
== m_IsFullscreen
)
734 // Windowed -> fullscreen:
738 // If a fullscreen size was configured, use that; else use the desktop size; else use a default
739 if (m_ConfigFullscreen
)
744 if (w
== 0 || h
== 0)
749 if (w
== 0 || h
== 0)
751 w
= DEFAULT_FULLSCREEN_W
;
752 h
= DEFAULT_FULLSCREEN_H
;
755 int bpp
= GetBestBPP();
757 if (!SetVideoMode(w
, h
, bpp
, fullscreen
))
760 UpdateRenderer(m_CurrentW
, m_CurrentH
);
766 // Fullscreen -> windowed:
768 // Go back to whatever the previous window size was
769 int w
= m_WindowedW
, h
= m_WindowedH
;
771 int bpp
= GetBestBPP();
773 if (!SetVideoMode(w
, h
, bpp
, fullscreen
))
776 UpdateRenderer(w
, h
);
782 bool CVideoMode::ToggleFullscreen()
784 return SetFullscreen(!m_IsFullscreen
);
787 bool CVideoMode::IsInFullscreen() const
789 return m_IsFullscreen
;
792 void CVideoMode::UpdatePosition(int x
, int y
)
801 void CVideoMode::UpdateRenderer(int w
, int h
)
803 if (w
< 2) w
= 2; // avoid GL errors caused by invalid sizes
809 SViewPort vp
= { 0, 0, w
, h
};
811 if (g_VideoMode
.GetBackendDevice())
812 g_VideoMode
.GetBackendDevice()->OnWindowResize(w
, h
);
814 if (CRenderer::IsInitialised())
815 g_Renderer
.Resize(w
, h
);
818 g_GUI
->UpdateResolution();
821 g_Console
->UpdateScreenSize(w
, h
);
824 g_Game
->GetView()->SetViewport(vp
);
827 int CVideoMode::GetBestBPP()
832 return m_PreferredBPP
;
836 int CVideoMode::GetXRes() const
838 ENSURE(m_IsInitialised
);
842 int CVideoMode::GetYRes() const
844 ENSURE(m_IsInitialised
);
848 int CVideoMode::GetBPP() const
850 ENSURE(m_IsInitialised
);
854 bool CVideoMode::IsVSyncEnabled() const
856 ENSURE(m_IsInitialised
);
857 return m_ConfigVSync
;
860 int CVideoMode::GetDesktopXRes() const
862 ENSURE(m_IsInitialised
);
866 int CVideoMode::GetDesktopYRes() const
868 ENSURE(m_IsInitialised
);
872 int CVideoMode::GetDesktopBPP() const
874 ENSURE(m_IsInitialised
);
875 return m_PreferredBPP
;
878 int CVideoMode::GetDesktopFreq() const
880 ENSURE(m_IsInitialised
);
881 return m_PreferredFreq
;
884 SDL_Window
* CVideoMode::GetWindow()
886 ENSURE(m_IsInitialised
);
890 void CVideoMode::SetWindowIcon()
892 // The window icon should be kept outside of art/textures/, or else it will be converted
893 // to DDS by the archive builder and will become unusable here. Using DDS makes BGRA
894 // conversion needlessly complicated.
895 std::shared_ptr
<u8
> iconFile
;
897 if (g_VFS
->LoadFile("art/icons/window.png", iconFile
, iconFileSize
) != INFO::OK
)
899 LOGWARNING("Window icon not found.");
904 if (iconTexture
.decode(iconFile
, iconFileSize
) != INFO::OK
)
907 // Convert to required BGRA format.
908 const size_t iconFlags
= (iconTexture
.m_Flags
| TEX_BGR
) & ~TEX_DXT
;
909 if (iconTexture
.transform_to(iconFlags
) != INFO::OK
)
912 void* bgra_img
= iconTexture
.get_data();
916 SDL_Surface
*iconSurface
= SDL_CreateRGBSurfaceFrom(bgra_img
,
917 iconTexture
.m_Width
, iconTexture
.m_Height
, 32, iconTexture
.m_Width
* 4,
918 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
922 SDL_SetWindowIcon(m_Window
, iconSurface
);
923 SDL_FreeSurface(iconSurface
);
926 void CVideoMode::SetCursor(const CStrW
& name
)
929 m_Cursor
->SetCursor(name
);
932 void CVideoMode::ResetCursor()
935 m_Cursor
->ResetCursor();