Fix unintented newline in rP20809.
[0ad.git] / source / ps / VideoMode.cpp
blobe3b095b35f30149533c82cfefcd500e89d3c0149
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"
26 #include "lib/ogl.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"
34 #include "ps/Game.h"
35 #include "ps/GameSetup/Config.h"
36 #include "renderer/Renderer.h"
38 #if OS_MACOSX
39 # include "lib/sysdep/os/osx/osx_sys_version.h"
40 #endif
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)
76 Uint32 flags = 0;
77 if (fullscreen)
78 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
80 if (!m_Window)
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);
87 if (!m_Window)
89 // If fullscreen fails, try windowed mode
90 if (fullscreen)
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);
99 else
101 LOGERROR("SetVideoMode failed in SDL_CreateWindow: %dx%d:%d %d (\"%s\")",
102 w, h, bpp, fullscreen ? 1 : 0, SDL_GetError());
103 return false;
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());
111 return false;
114 SDL_GLContext context = SDL_GL_CreateContext(m_Window);
115 if (!context)
117 LOGERROR("SetVideoMode failed in SDL_GL_CreateContext: %dx%d:%d %d (\"%s\")",
118 w, h, bpp, fullscreen ? 1 : 0, SDL_GetError());
119 return false;
122 else
124 if (m_IsFullscreen != fullscreen)
126 if (!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());
138 return false;
142 if (!fullscreen)
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);
151 m_CurrentBPP = bpp;
153 if (fullscreen)
154 SDL_SetWindowGrab(m_Window, SDL_TRUE);
155 else
156 SDL_SetWindowGrab(m_Window, SDL_FALSE);
158 m_IsFullscreen = fullscreen;
160 g_xres = m_CurrentW;
161 g_yres = m_CurrentH;
163 return true;
166 bool CVideoMode::InitSDL()
168 ENSURE(!m_IsInitialised);
170 ReadConfig();
172 EnableS3TC();
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);
178 int w = m_ConfigW;
179 int h = m_ConfigH;
181 if (m_ConfigFullscreen)
183 // If fullscreen and no explicit size set, default to the desktop resolution
184 if (w == 0 || h == 0)
186 w = m_PreferredW;
187 h = m_PreferredH;
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)
201 if (m_PreferredW)
202 w = std::min(w, m_PreferredW);
203 if (m_PreferredH)
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);
213 #if CONFIG2_GLES
214 // Require GLES 2.0
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);
218 #endif
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))
227 return false;
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.)
239 atexit(SDL_Quit);
240 // End work around.
242 ogl_Init(); // required after each mode change
243 // (TODO: does that mean we need to call this when toggling fullscreen later?)
245 #if !OS_ANDROID
246 u16 ramp[256];
247 SDL_CalculateGammaRamp(g_Gamma, ramp);
248 if (SDL_SetWindowGammaRamp(m_Window, ramp, ramp, ramp) < 0)
249 LOGWARNING("SDL_SetWindowGammaRamp failed");
250 #endif
252 m_IsInitialised = true;
254 if (!m_ConfigFullscreen)
256 m_WindowedW = w;
257 m_WindowedH = h;
260 SetWindowIcon();
262 return true;
265 bool CVideoMode::InitNonSDL()
267 ENSURE(!m_IsInitialised);
269 ReadConfig();
271 EnableS3TC();
273 m_IsInitialised = true;
275 return true;
278 void CVideoMode::Shutdown()
280 ENSURE(m_IsInitialised);
282 m_IsFullscreen = false;
283 m_IsInitialised = false;
284 if (m_Window)
286 SDL_DestroyWindow(m_Window);
287 m_Window = NULL;
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);
310 #endif
313 bool CVideoMode::ResizeWindow(int w, int h)
315 ENSURE(m_IsInitialised);
317 // Ignore if not windowed
318 if (m_IsFullscreen)
319 return true;
321 // Ignore if the size hasn't changed
322 if (w == m_WindowedW && h == m_WindowedH)
323 return true;
325 int bpp = GetBestBPP();
327 if (!SetVideoMode(w, h, bpp, false))
328 return false;
330 m_WindowedW = w;
331 m_WindowedH = h;
333 UpdateRenderer(w, h);
335 return true;
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)
343 return false;
345 // Check whether this is actually a change
346 if (fullscreen == m_IsFullscreen)
347 return true;
349 if (!m_IsFullscreen)
351 // Windowed -> fullscreen:
353 int w = 0, h = 0;
355 // If a fullscreen size was configured, use that; else use the desktop size; else use a default
356 if (m_ConfigFullscreen)
358 w = m_ConfigW;
359 h = m_ConfigH;
361 if (w == 0 || h == 0)
363 w = m_PreferredW;
364 h = m_PreferredH;
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))
375 return false;
377 UpdateRenderer(m_CurrentW, m_CurrentH);
379 return true;
381 else
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))
391 return false;
393 UpdateRenderer(w, h);
395 return true;
399 bool CVideoMode::ToggleFullscreen()
401 return SetFullscreen(!m_IsFullscreen);
404 void CVideoMode::UpdatePosition(int x, int y)
406 if (!m_IsFullscreen)
408 m_WindowedX = x;
409 m_WindowedY = y;
413 void CVideoMode::UpdateRenderer(int w, int h)
415 if (w < 2) w = 2; // avoid GL errors caused by invalid sizes
416 if (h < 2) h = 2;
418 g_xres = w;
419 g_yres = h;
421 SViewPort vp = { 0, 0, w, h };
423 if (CRenderer::IsInitialised())
425 g_Renderer.SetViewport(vp);
426 g_Renderer.Resize(w, h);
429 if (g_GUI)
430 g_GUI->UpdateResolution();
432 if (g_Console)
433 g_Console->UpdateScreenSize(w, h);
435 if (g_Game)
436 g_Game->GetView()->SetViewport(vp);
439 int CVideoMode::GetBestBPP()
441 if (m_ConfigBPP)
442 return m_ConfigBPP;
443 if (m_PreferredBPP)
444 return m_PreferredBPP;
445 return 32;
448 int CVideoMode::GetXRes()
450 ENSURE(m_IsInitialised);
451 return m_CurrentW;
454 int CVideoMode::GetYRes()
456 ENSURE(m_IsInitialised);
457 return m_CurrentH;
460 int CVideoMode::GetBPP()
462 ENSURE(m_IsInitialised);
463 return m_CurrentBPP;
466 int CVideoMode::GetDesktopXRes()
468 ENSURE(m_IsInitialised);
469 return m_PreferredW;
472 int CVideoMode::GetDesktopYRes()
474 ENSURE(m_IsInitialised);
475 return m_PreferredH;
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);
493 return m_Window;
496 void CVideoMode::SetWindowIcon()
498 std::shared_ptr<u8> iconFile;
499 size_t iconFileSize;
500 if (g_VFS->LoadFile("art/textures/icons/window.png", iconFile, iconFileSize) != INFO::OK)
502 LOGWARNING("Window icon not found.");
503 return;
506 Tex iconTexture;
507 if (iconTexture.decode(iconFile, iconFileSize) != INFO::OK)
508 return;
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)
513 return;
515 void* bgra_img = iconTexture.get_data();
516 if (!bgra_img)
517 return;
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);
522 if (!iconSurface)
523 return;
525 SDL_SetWindowIcon(m_Window, iconSurface);
526 SDL_FreeSurface(iconSurface);