1 /* Copyright (C) 2017 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/Camera.h"
23 #include "graphics/GameView.h"
24 #include "gui/GUIManager.h"
25 #include "lib/config2.h"
27 #include "lib/external_libraries/libsdl.h"
28 #include "lib/sysdep/gfx.h"
29 #include "lib/tex/tex.h"
30 #include "ps/CConsole.h"
31 #include "ps/CLogger.h"
32 #include "ps/ConfigDB.h"
33 #include "ps/Filesystem.h"
35 #include "ps/GameSetup/Config.h"
36 #include "renderer/Renderer.h"
39 # include "lib/sysdep/os/osx/osx_sys_version.h"
43 static int DEFAULT_WINDOW_W
= 1024;
44 static int DEFAULT_WINDOW_H
= 768;
46 static int DEFAULT_FULLSCREEN_W
= 1024;
47 static int DEFAULT_FULLSCREEN_H
= 768;
49 CVideoMode g_VideoMode
;
51 CVideoMode::CVideoMode() :
52 m_IsFullscreen(false), m_IsInitialised(false), m_Window(NULL
),
53 m_PreferredW(0), m_PreferredH(0), m_PreferredBPP(0), m_PreferredFreq(0),
54 m_ConfigW(0), m_ConfigH(0), m_ConfigBPP(0), m_ConfigFullscreen(false), m_ConfigForceS3TCEnable(true),
55 m_WindowedW(DEFAULT_WINDOW_W
), m_WindowedH(DEFAULT_WINDOW_H
), m_WindowedX(0), m_WindowedY(0)
57 // (m_ConfigFullscreen defaults to false, so users don't get stuck if
58 // e.g. half the filesystem is missing and the config files aren't loaded)
61 void CVideoMode::ReadConfig()
63 bool windowed
= !m_ConfigFullscreen
;
64 CFG_GET_VAL("windowed", windowed
);
65 m_ConfigFullscreen
= !windowed
;
67 CFG_GET_VAL("xres", m_ConfigW
);
68 CFG_GET_VAL("yres", m_ConfigH
);
69 CFG_GET_VAL("bpp", m_ConfigBPP
);
70 CFG_GET_VAL("display", m_ConfigDisplay
);
71 CFG_GET_VAL("force_s3tc_enable", m_ConfigForceS3TCEnable
);
74 bool CVideoMode::SetVideoMode(int w
, int h
, int bpp
, bool fullscreen
)
78 flags
|= SDL_WINDOW_FULLSCREEN_DESKTOP
;
82 // Note: these flags only take affect in SDL_CreateWindow
83 flags
|= SDL_WINDOW_OPENGL
| SDL_WINDOW_SHOWN
| SDL_WINDOW_RESIZABLE
;
84 m_WindowedX
= m_WindowedY
= SDL_WINDOWPOS_CENTERED_DISPLAY(m_ConfigDisplay
);
86 m_Window
= SDL_CreateWindow("0 A.D.", m_WindowedX
, m_WindowedY
, w
, h
, flags
);
89 // If fullscreen fails, try windowed mode
92 LOGWARNING("Failed to set the video mode to fullscreen for the chosen resolution "
93 "%dx%d:%d (\"%hs\"), falling back to windowed mode",
94 w
, h
, bpp
, SDL_GetError());
95 // Using default size for the window for now, as the attempted setting
96 // could be as large, or larger than the screen size.
97 return SetVideoMode(DEFAULT_WINDOW_W
, DEFAULT_WINDOW_H
, bpp
, false);
101 LOGERROR("SetVideoMode failed in SDL_CreateWindow: %dx%d:%d %d (\"%s\")",
102 w
, h
, bpp
, fullscreen
? 1 : 0, SDL_GetError());
107 if (SDL_SetWindowDisplayMode(m_Window
, NULL
) < 0)
109 LOGERROR("SetVideoMode failed in SDL_SetWindowDisplayMode: %dx%d:%d %d (\"%s\")",
110 w
, h
, bpp
, fullscreen
? 1 : 0, SDL_GetError());
114 SDL_GLContext context
= SDL_GL_CreateContext(m_Window
);
117 LOGERROR("SetVideoMode failed in SDL_GL_CreateContext: %dx%d:%d %d (\"%s\")",
118 w
, h
, bpp
, fullscreen
? 1 : 0, SDL_GetError());
124 if (m_IsFullscreen
!= fullscreen
)
128 // For some reason, when switching from fullscreen to windowed mode,
129 // we have to set the window size and position before and after switching
130 SDL_SetWindowSize(m_Window
, w
, h
);
131 SDL_SetWindowPosition(m_Window
, m_WindowedX
, m_WindowedY
);
134 if (SDL_SetWindowFullscreen(m_Window
, flags
) < 0)
136 LOGERROR("SetVideoMode failed in SDL_SetWindowFullscreen: %dx%d:%d %d (\"%s\")",
137 w
, h
, bpp
, fullscreen
? 1 : 0, SDL_GetError());
144 SDL_SetWindowSize(m_Window
, w
, h
);
145 SDL_SetWindowPosition(m_Window
, m_WindowedX
, m_WindowedY
);
149 // Grab the current video settings
150 SDL_GetWindowSize(m_Window
, &m_CurrentW
, &m_CurrentH
);
154 SDL_SetWindowGrab(m_Window
, SDL_TRUE
);
156 SDL_SetWindowGrab(m_Window
, SDL_FALSE
);
158 m_IsFullscreen
= fullscreen
;
166 bool CVideoMode::InitSDL()
168 ENSURE(!m_IsInitialised
);
174 // preferred video mode = current desktop settings
175 // (command line params may override these)
176 gfx::GetVideoMode(&m_PreferredW
, &m_PreferredH
, &m_PreferredBPP
, &m_PreferredFreq
);
181 if (m_ConfigFullscreen
)
183 // If fullscreen and no explicit size set, default to the desktop resolution
184 if (w
== 0 || h
== 0)
191 // If no size determined, default to something sensible
192 if (w
== 0 || h
== 0)
194 w
= DEFAULT_WINDOW_W
;
195 h
= DEFAULT_WINDOW_H
;
198 if (!m_ConfigFullscreen
)
200 // Limit the window to the screen size (if known)
202 w
= std::min(w
, m_PreferredW
);
204 h
= std::min(h
, m_PreferredH
);
207 int bpp
= GetBestBPP();
209 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE
, 24);
210 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE
, 8);
211 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER
, 1);
215 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK
, SDL_GL_CONTEXT_PROFILE_ES
);
216 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION
, 2);
217 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION
, 0);
220 if (!SetVideoMode(w
, h
, bpp
, m_ConfigFullscreen
))
222 // Fall back to a smaller depth buffer
223 // (The rendering may be ugly but this helps when running in VMware)
224 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE
, 16);
226 if (!SetVideoMode(w
, h
, bpp
, m_ConfigFullscreen
))
230 SDL_GL_SetSwapInterval(g_VSync
? 1 : 0);
232 // Work around a bug in the proprietary Linux ATI driver (at least versions 8.16.20 and 8.14.13).
233 // The driver appears to register its own atexit hook on context creation.
234 // If this atexit hook is called before SDL_Quit destroys the OpenGL context,
235 // some kind of double-free problem causes a crash and lockup in the driver.
236 // Calling SDL_Quit twice appears to be harmless, though, and avoids the problem
237 // by destroying the context *before* the driver's atexit hook is called.
238 // (Note that atexit hooks are guaranteed to be called in reverse order of their registration.)
242 ogl_Init(); // required after each mode change
243 // (TODO: does that mean we need to call this when toggling fullscreen later?)
247 SDL_CalculateGammaRamp(g_Gamma
, ramp
);
248 if (SDL_SetWindowGammaRamp(m_Window
, ramp
, ramp
, ramp
) < 0)
249 LOGWARNING("SDL_SetWindowGammaRamp failed");
252 m_IsInitialised
= true;
254 if (!m_ConfigFullscreen
)
265 bool CVideoMode::InitNonSDL()
267 ENSURE(!m_IsInitialised
);
273 m_IsInitialised
= true;
278 void CVideoMode::Shutdown()
280 ENSURE(m_IsInitialised
);
282 m_IsFullscreen
= false;
283 m_IsInitialised
= false;
286 SDL_DestroyWindow(m_Window
);
291 void CVideoMode::EnableS3TC()
293 // On Linux we have to try hard to get S3TC compressed texture support.
294 // If the extension is already provided by default, that's fine.
295 // Otherwise we should enable the 'force_s3tc_enable' environment variable
296 // and (re)initialise the video system, so that Mesa provides the extension
297 // (if the driver at least supports decompression).
298 // (This overrides the force_s3tc_enable specified via driconf files.)
299 // Otherwise we should complain to the user, and stop using compressed textures.
301 // Setting the environment variable causes Mesa to print an ugly message to stderr
302 // ("ATTENTION: default value of option force_s3tc_enable overridden by environment."),
303 // so it'd be nicer to skip that if S3TC will be supported by default,
304 // but reinitialising video is a pain (and it might do weird things when fullscreen)
305 // so we just unconditionally set it (unless our config file explicitly disables it).
307 #if !(OS_WIN || OS_MACOSX) // (assume Mesa is used for all non-Windows non-Mac platforms)
308 if (m_ConfigForceS3TCEnable
)
309 setenv("force_s3tc_enable", "true", 0);
313 bool CVideoMode::ResizeWindow(int w
, int h
)
315 ENSURE(m_IsInitialised
);
317 // Ignore if not windowed
321 // Ignore if the size hasn't changed
322 if (w
== m_WindowedW
&& h
== m_WindowedH
)
325 int bpp
= GetBestBPP();
327 if (!SetVideoMode(w
, h
, bpp
, false))
333 UpdateRenderer(w
, h
);
338 bool CVideoMode::SetFullscreen(bool fullscreen
)
340 // This might get called before initialisation by psDisplayError;
341 // if so then silently fail
342 if (!m_IsInitialised
)
345 // Check whether this is actually a change
346 if (fullscreen
== m_IsFullscreen
)
351 // Windowed -> fullscreen:
355 // If a fullscreen size was configured, use that; else use the desktop size; else use a default
356 if (m_ConfigFullscreen
)
361 if (w
== 0 || h
== 0)
366 if (w
== 0 || h
== 0)
368 w
= DEFAULT_FULLSCREEN_W
;
369 h
= DEFAULT_FULLSCREEN_H
;
372 int bpp
= GetBestBPP();
374 if (!SetVideoMode(w
, h
, bpp
, fullscreen
))
377 UpdateRenderer(m_CurrentW
, m_CurrentH
);
383 // Fullscreen -> windowed:
385 // Go back to whatever the previous window size was
386 int w
= m_WindowedW
, h
= m_WindowedH
;
388 int bpp
= GetBestBPP();
390 if (!SetVideoMode(w
, h
, bpp
, fullscreen
))
393 UpdateRenderer(w
, h
);
399 bool CVideoMode::ToggleFullscreen()
401 return SetFullscreen(!m_IsFullscreen
);
404 void CVideoMode::UpdatePosition(int x
, int y
)
413 void CVideoMode::UpdateRenderer(int w
, int h
)
415 if (w
< 2) w
= 2; // avoid GL errors caused by invalid sizes
421 SViewPort vp
= { 0, 0, w
, h
};
423 if (CRenderer::IsInitialised())
425 g_Renderer
.SetViewport(vp
);
426 g_Renderer
.Resize(w
, h
);
430 g_GUI
->UpdateResolution();
433 g_Console
->UpdateScreenSize(w
, h
);
436 g_Game
->GetView()->SetViewport(vp
);
439 int CVideoMode::GetBestBPP()
444 return m_PreferredBPP
;
448 int CVideoMode::GetXRes()
450 ENSURE(m_IsInitialised
);
454 int CVideoMode::GetYRes()
456 ENSURE(m_IsInitialised
);
460 int CVideoMode::GetBPP()
462 ENSURE(m_IsInitialised
);
466 int CVideoMode::GetDesktopXRes()
468 ENSURE(m_IsInitialised
);
472 int CVideoMode::GetDesktopYRes()
474 ENSURE(m_IsInitialised
);
478 int CVideoMode::GetDesktopBPP()
480 ENSURE(m_IsInitialised
);
481 return m_PreferredBPP
;
484 int CVideoMode::GetDesktopFreq()
486 ENSURE(m_IsInitialised
);
487 return m_PreferredFreq
;
490 SDL_Window
* CVideoMode::GetWindow()
492 ENSURE(m_IsInitialised
);
496 void CVideoMode::SetWindowIcon()
498 std::shared_ptr
<u8
> iconFile
;
500 if (g_VFS
->LoadFile("art/textures/icons/window.png", iconFile
, iconFileSize
) != INFO::OK
)
502 LOGWARNING("Window icon not found.");
507 if (iconTexture
.decode(iconFile
, iconFileSize
) != INFO::OK
)
510 // Convert to required BGRA format.
511 const size_t iconFlags
= (iconTexture
.m_Flags
| TEX_BGR
) & ~TEX_DXT
;
512 if (iconTexture
.transform_to(iconFlags
) != INFO::OK
)
515 void* bgra_img
= iconTexture
.get_data();
519 SDL_Surface
*iconSurface
= SDL_CreateRGBSurfaceFrom(bgra_img
,
520 iconTexture
.m_Width
, iconTexture
.m_Height
, 32, iconTexture
.m_Width
* 4,
521 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
525 SDL_SetWindowIcon(m_Window
, iconSurface
);
526 SDL_FreeSurface(iconSurface
);