Mana
Loading...
Searching...
No Matches
video.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "video.h"
23
24#include "log.h"
25#include "sdlgraphics.h"
26#include "utils/stringutils.h"
27
28#ifdef USE_OPENGL
29#include "openglgraphics.h"
30#endif
31
32#include <algorithm>
33
34static constexpr int WINDOW_MIN_WIDTH = 640;
35static constexpr int WINDOW_MIN_HEIGHT = 400;
36
38{
39 if (userScale == 0)
40 return autoScale();
41
42 return std::clamp(userScale, 1, maxScale());
43}
44
46{
47 // Automatic scaling factor based on at least 800x600 logical resolution
48 return std::max(1, std::min(width / 800, height / 600));
49}
50
52{
53 // Logical resolution needs to stay at least the minimum size
54 return std::max(1, std::min(width / WINDOW_MIN_WIDTH,
55 height / WINDOW_MIN_HEIGHT));
56}
57
59{
60 mGraphics.reset(); // reset graphics first
61
62 if (mWindow)
63 SDL_DestroyWindow(mWindow);
64}
65
67{
69
70 if (!initDisplayModes())
71 {
72 Log::info("Failed to initialize display modes: %s", SDL_GetError());
73 }
74
75 SDL_DisplayMode displayMode;
76
78 {
79 SDL_DisplayMode requestedMode;
80 requestedMode.format = 0;
81 requestedMode.w = mSettings.width;
82 requestedMode.h = mSettings.height;
83 requestedMode.refresh_rate = 0;
84 requestedMode.driverdata = nullptr;
85
86 if (SDL_GetClosestDisplayMode(mSettings.display, &requestedMode, &displayMode) == nullptr)
87 {
88 Log::info("SDL_GetClosestDisplayMode failed: %s, falling back to borderless mode", SDL_GetError());
90 }
91 }
92
93 int windowFlags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
94 const char *videoMode = "windowed";
95
96 switch (mSettings.windowMode)
97 {
99 // In windowed mode, the window is initially created hidden, we'll show
100 // it once we have finished setting it up and done an initial paint to
101 // avoid startup flicker on X11 and Windows.
102 windowFlags |= SDL_WINDOW_HIDDEN;
103 break;
105 windowFlags |= SDL_WINDOW_FULLSCREEN;
106 videoMode = "fullscreen";
107 break;
109 windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
110 // On Windows, fullscreen desktop with OpenGL actually flickers worse
111 // when the window is initially hidden.
112 if (!mSettings.openGL)
113 windowFlags |= SDL_WINDOW_HIDDEN;
114 videoMode = "windowed fullscreen";
115 break;
116 }
117
118 if (mSettings.openGL)
119 windowFlags |= SDL_WINDOW_OPENGL;
120
121 Log::info("Setting video mode %dx%d %s",
124 videoMode);
125
126 mWindowShown = !(windowFlags & SDL_WINDOW_HIDDEN);
127 mWindow = SDL_CreateWindow("Mana",
128 SDL_WINDOWPOS_UNDEFINED,
129 SDL_WINDOWPOS_UNDEFINED,
132 windowFlags);
133
134 if (!mWindow)
135 {
136 Log::critical(strprintf("Failed to create window: %s", SDL_GetError()));
137 return nullptr;
138 }
139
140 SDL_SetWindowMinimumSize(mWindow, WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT);
141
143 {
144 if (SDL_SetWindowDisplayMode(mWindow, &displayMode) != 0)
145 {
146 Log::info("SDL_SetWindowDisplayMode failed: %s", SDL_GetError());
147 }
148 }
149
150 // Make sure the resolution is reflected in current settings
151 SDL_GetWindowSize(mWindow, &mSettings.width, &mSettings.height);
152
153#ifdef USE_OPENGL
154 if (mSettings.openGL)
155 {
156 mGraphics = OpenGLGraphics::create(mWindow, mSettings);
157 if (!mGraphics)
158 {
159 Log::info("Failed to create OpenGL context, falling back to SDL renderer: %s",
160 SDL_GetError());
161 mSettings.openGL = false;
162 }
163 }
164#endif
165
166 if (!mGraphics)
168
170
171 return mGraphics.get();
172}
173
174bool Video::apply(const VideoSettings &settings)
175{
176 if (mSettings == settings)
177 return true;
178
179 // When changing to fullscreen mode, we set the display mode first
181 {
182 SDL_DisplayMode displayMode;
183 if (SDL_GetWindowDisplayMode(mWindow, &displayMode) != 0)
184 {
185 Log::critical(strprintf("SDL_GetCurrentDisplayMode failed: %s", SDL_GetError()));
186 return false;
187 }
188
189 if (displayMode.w != settings.width || displayMode.h != settings.height)
190 {
191#ifdef __APPLE__
192 // Workaround SDL2 issue when switching display modes while already
193 // fullscreen on macOS (tested as of SDL 2.30.0).
194 if (SDL_GetWindowFlags(mWindow) & SDL_WINDOW_FULLSCREEN)
195 SDL_SetWindowFullscreen(mWindow, 0);
196#endif
197
198 displayMode.w = settings.width;
199 displayMode.h = settings.height;
200
201 if (SDL_SetWindowDisplayMode(mWindow, &displayMode) != 0)
202 {
203 Log::critical(strprintf("SDL_SetWindowDisplayMode failed: %s", SDL_GetError()));
204 return false;
205 }
206 }
207 }
208
209 int windowFlags = 0;
210 switch (settings.windowMode)
211 {
213 break;
215 windowFlags = SDL_WINDOW_FULLSCREEN_DESKTOP;
216 break;
218 windowFlags = SDL_WINDOW_FULLSCREEN;
219 break;
220 }
221
222 if (SDL_SetWindowFullscreen(mWindow, windowFlags) != 0)
223 {
224 Log::critical(strprintf("SDL_SetWindowFullscreen failed: %s", SDL_GetError()));
225 return false;
226 }
227
231#ifdef __APPLE__
232 // Workaround SDL2 issue when setting the window size on a window
233 // which the user has put in fullscreen. Unfortunately, this mode can't
234 // be distinguished from a maximized window (tested as of SDL 2.30.0).
235 if (!(SDL_GetWindowFlags(mWindow) & SDL_WINDOW_MAXIMIZED))
236#endif
237 SDL_SetWindowSize(mWindow, settings.width, settings.height);
238 }
239
241
242 SDL_GetWindowSize(mWindow, &mSettings.width, &mSettings.height);
243
244 mGraphics->setVSync(mSettings.vsync);
246
247 return true;
248}
249
251{
252 SDL_GetWindowSize(mWindow, &mSettings.width, &mSettings.height);
253 mGraphics->updateSize(mSettings.width,
255 mSettings.scale());
256}
257
259{
260 mGraphics->updateScreen();
261
262 if (!mWindowShown)
263 {
264 SDL_ShowWindow(mWindow);
265 mWindowShown = true;
266 }
267}
268
270{
271 const int displayIndex = mSettings.display;
272 SDL_DisplayMode mode;
273 if (SDL_GetDesktopDisplayMode(displayIndex, &mode) != 0)
274 return false;
275
276 mDesktopDisplayMode.width = mode.w;
278
279 // Get available fullscreen/hardware modes
280 const int numModes = SDL_GetNumDisplayModes(displayIndex);
281 for (int i = 0; i < numModes; i++)
282 {
283 if (SDL_GetDisplayMode(displayIndex, i, &mode) != 0)
284 return false;
285
286 // Skip the unreasonably small modes
287 if (mode.w < WINDOW_MIN_WIDTH || mode.h < WINDOW_MIN_HEIGHT)
288 continue;
289
290 // Only list each resolution once
291 // (we currently don't support selecting the refresh rate)
292 if (std::find_if(mDisplayModes.cbegin(),
293 mDisplayModes.cend(),
294 [&mode](const DisplayMode &other) {
295 return mode.w == other.width && mode.h == other.height;
296 }) != mDisplayModes.cend())
297 continue;
298
299 mDisplayModes.push_back(DisplayMode { mode.w, mode.h });
300 }
301
302 return true;
303}
A central point of control for graphics.
Definition graphics.h:78
static std::unique_ptr< Graphics > create(SDL_Window *window, const VideoSettings &settings)
VideoSettings mSettings
Definition video.h:114
void updateWindowSize()
Handles a change in window size, possibly adjusting the scale.
Definition video.cpp:250
bool apply(const VideoSettings &settings)
Try to apply the given video settings.
Definition video.cpp:174
void present()
Present the next frame.
Definition video.cpp:258
SDL_Window * mWindow
Definition video.h:118
DisplayMode mDesktopDisplayMode
Definition video.h:115
const VideoSettings & settings() const
Definition video.h:77
Graphics * initialize(const VideoSettings &settings)
Try to create a window with the given settings.
Definition video.cpp:66
std::vector< DisplayMode > mDisplayModes
Definition video.h:116
std::unique_ptr< Graphics > mGraphics
Definition video.h:117
bool initDisplayModes()
Definition video.cpp:269
bool mWindowShown
Definition video.h:119
~Video()
Definition video.cpp:58
void info(const char *log_text,...) LOG_PRINTF_ATTR
std::string strprintf(char const *format,...)
A safe version of sprintf that returns a std::string of the result.
int height
Definition video.h:42
int width
Definition video.h:41
WindowMode windowMode
Definition video.h:47
bool vsync
Definition video.h:52
int height
Definition video.h:49
int scale() const
Definition video.cpp:37
bool openGL
Definition video.h:53
int maxScale() const
Definition video.cpp:51
int width
Definition video.h:48
int userScale
Definition video.h:51
int display
Definition video.h:50
int autoScale() const
Definition video.cpp:45
@ WindowedFullscreen