The Battle for Wesnoth  1.15.0-dev
video.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "video.hpp"
16 
17 #include "game_config.hpp"
18 #include "log.hpp"
19 #include "ogl/utils.hpp"
20 #include "preferences/general.hpp"
21 #include "sdl/point.hpp"
22 #include "sdl/render_utils.hpp"
23 #include "sdl/texture.hpp"
24 #include "sdl/userevent.hpp"
25 #include "sdl/utils.hpp"
26 #include "sdl/window.hpp"
27 
28 #include <cassert>
29 #include <vector>
30 
31 static lg::log_domain log_display("display");
32 #define LOG_DP LOG_STREAM(info, log_display)
33 #define ERR_DP LOG_STREAM(err, log_display)
34 
35 #define MAGIC_DPI_SCALE_NUMBER 96
36 
37 CVideo* CVideo::singleton_ = nullptr;
38 
39 namespace
40 {
41 bool interactive = true;
42 }
43 
45  : window()
46 #ifdef USE_GL_RENDERING
47  , gl_context()
48 #endif
49  , fake_screen_(false)
50  , fake_size_(0u, 0u)
51  , updated_locked_(0)
52  , flip_locked_(0)
53 {
54  assert(!singleton_);
55  singleton_ = this;
56 
57  initSDL();
58 
59  switch(type) {
60  case NO_FAKE:
61  break;
62  case FAKE:
63  make_fake();
64  break;
65  case FAKE_TEST:
67  break;
68  }
69 }
70 
72 {
73  const int res = SDL_InitSubSystem(SDL_INIT_VIDEO);
74 
75  if(res < 0) {
76  ERR_DP << "Could not initialize SDL_video: " << SDL_GetError() << std::endl;
77  throw CVideo::error();
78  }
79 }
80 
82 {
83  LOG_DP << "calling SDL_Quit()\n";
84  SDL_Quit();
85  assert(singleton_);
86  singleton_ = nullptr;
87  LOG_DP << "called SDL_Quit()\n";
88 }
89 
91 {
92  return !interactive || window == nullptr;
93 }
94 
96 {
97  if(event.type == SDL_WINDOWEVENT) {
98  switch(event.window.event) {
99  case SDL_WINDOWEVENT_RESIZED:
100  case SDL_WINDOWEVENT_RESTORED:
101  case SDL_WINDOWEVENT_SHOWN:
102  case SDL_WINDOWEVENT_EXPOSED:
103  break;
104  }
105  }
106 }
107 
109 {
110  fake_screen_ = true;
111  fake_size_ = { 1024, 768 };
112 }
113 
114 void CVideo::make_test_fake(const unsigned width, const unsigned height)
115 {
116  interactive = false;
117  fake_size_.first = width;
118  fake_size_.second = height;
119 }
120 
122 {
123  // Position
124  const int x = preferences::fullscreen() ? SDL_WINDOWPOS_UNDEFINED : SDL_WINDOWPOS_CENTERED;
125  const int y = preferences::fullscreen() ? SDL_WINDOWPOS_UNDEFINED : SDL_WINDOWPOS_CENTERED;
126 
127  // Dimensions
128  const point res = preferences::resolution();
129  const int w = res.x;
130  const int h = res.y;
131 
132  uint32_t window_flags = 0;
133 
134  // Add any more default flags here
135  window_flags |= SDL_WINDOW_RESIZABLE;
136 #ifdef __APPLE__
137  window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
138 #endif
139 #ifdef USE_GL_RENDERING
140  video_flags |= SDL_WINDOW_OPENGL;
141 #endif
142 
144  window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
145  } else if(preferences::maximized()) {
146  window_flags |= SDL_WINDOW_MAXIMIZED;
147  }
148 
149  // Initialize window
150  window.reset(new sdl::window("", x, y, w, h, window_flags, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE));
151 
152  std::cerr << "Setting mode to " << w << "x" << h << std::endl;
153 
155 
156 #ifdef USE_GL_RENDERING
157  // Initialize an OpenGL context for the window.
158  gl_context.reset(new gl::context(window.get()));
159 
160  gl::clear_screen();
161  render_screen();
162 #endif
163 
165 }
166 
168 {
169  assert(window);
170  if(fake_screen_) {
171  return;
172  }
173 
174  switch(mode) {
175  case TO_FULLSCREEN:
176  window->full_screen();
177  break;
178 
179  case TO_WINDOWED:
180  window->to_window();
181  window->restore();
182  break;
183 
184  case TO_MAXIMIZED_WINDOW:
185  window->to_window();
186  window->maximize();
187  break;
188 
189  case TO_RES:
190  window->restore();
191  window->set_size(size.x, size.y);
192  window->center();
193  break;
194  }
195 }
196 
197 SDL_Rect CVideo::screen_area(bool as_pixels) const
198 {
199  if(!window) {
200  return {0, 0,
201  static_cast<int>(fake_size_.first),
202  static_cast<int>(fake_size_.second)
203  };
204  }
205 
206  // First, get the renderer size in pixels.
207  SDL_Point size = window->get_output_size();
208 
209  // Then convert the dimensions into screen coordinates, if applicable.
210  if(!as_pixels) {
211 #ifdef __APPLE__
212  size = window->get_size();
213 #else
214  float scale_x, scale_y;
215  std::tie(scale_x, scale_y) = get_dpi_scale_factor();
216 
217  size.x /= scale_x;
218  size.y /= scale_y;
219 #endif
220  }
221 
222  return {0, 0, size.x, size.y};
223 }
224 
225 int CVideo::get_width(bool as_pixels) const
226 {
227  return screen_area(as_pixels).w;
228 }
229 
230 int CVideo::get_height(bool as_pixels) const
231 {
232  return screen_area(as_pixels).h;
233 }
234 
235 void CVideo::delay(unsigned int milliseconds)
236 {
237  if(!game_config::no_delay) {
238  SDL_Delay(milliseconds);
239  }
240 }
241 
243 {
244  if(fake_screen_ || flip_locked_ > 0) {
245  return;
246  }
247 
248  if(window) {
249  window->render();
250  }
251 }
252 
254  const texture& txt, SDL_Rect* src_rect, SDL_Rect* dst_rect, const bool flip_h, const bool flip_v)
255 {
256  if(!window || txt.null()) {
257  return;
258  }
259 
260  // If no additional data was provided, render immediately.
261  if(!flip_h && !flip_v) {
262  SDL_RenderCopy(*window, txt, src_rect, dst_rect);
263  return;
264  }
265 
266  // Calculate flipping mode.
267  int fmode = SDL_FLIP_NONE;
268 
269  if(flip_h && flip_v) {
270  fmode = SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL;
271  } else if(flip_h) {
272  fmode = SDL_FLIP_HORIZONTAL;
273  } else if(flip_v) {
274  fmode = SDL_FLIP_VERTICAL;
275  }
276 
277  SDL_RendererFlip flip_mode = static_cast<SDL_RendererFlip>(fmode);
278 
279  // TODO: add handling of rotations.
280  static const double rotate_angle = 0;
281  static const SDL_Point* center = nullptr;
282 
283  SDL_RenderCopyEx(*window, txt, src_rect, dst_rect, rotate_angle, center, flip_mode);
284 }
285 
286 void CVideo::lock_updates(bool value)
287 {
288  if(value == true) {
289  ++updated_locked_;
290  } else {
291  --updated_locked_;
292  }
293 }
294 
296 {
297  return updated_locked_ > 0;
298 }
299 
300 void CVideo::set_window_title(const std::string& title)
301 {
302  assert(window);
303  window->set_title(title);
304 }
305 
307 {
308  assert(window);
309  window->set_icon(icon);
310 }
311 
313 {
314  if(!window) {
315  return;
316  }
317 
318  window->fill(0, 0, 0, 255);
319 }
320 
322 {
323  return window.get();
324 }
325 
326 SDL_Renderer* CVideo::get_renderer()
327 {
328  if(!window) {
329  return nullptr;
330  }
331 
332  return *window;
333 }
334 
335 bool CVideo::window_has_flags(uint32_t flags) const
336 {
337  if(!window) {
338  return false;
339  }
340 
341  return (window->get_flags() & flags) != 0;
342 }
343 
344 std::pair<float, float> CVideo::get_dpi_scale_factor() const
345 {
346  std::pair<float, float> result{1.0f, 1.0f};
347 
348  if(!window) {
349  return result;
350  }
351 
352  float hdpi, vdpi;
353  SDL_GetDisplayDPI(window->get_display_index(), nullptr, &hdpi, &vdpi);
354 
355  result.first = hdpi / MAGIC_DPI_SCALE_NUMBER;
356  result.second = vdpi / MAGIC_DPI_SCALE_NUMBER;
357 
358  return result;
359 }
360 
361 std::vector<point> CVideo::get_available_resolutions(const bool include_current)
362 {
363  std::vector<point> result;
364 
365  if(!window) {
366  return result;
367  }
368 
369  const int display_index = window->get_display_index();
370 
371  const int modes = SDL_GetNumDisplayModes(display_index);
372  if(modes <= 0) {
373  std::cerr << "No modes supported\n";
374  return result;
375  }
376 
378 
379 #if 0
380  // DPI scale factor.
381  float scale_h, scale_v;
382  std::tie(scale_h, scale_v) = get_dpi_scale_factor();
383 #endif
384 
385  // The maximum size to which this window can be set. For some reason this won't
386  // pop up as a display mode of its own.
387  SDL_Rect bounds;
388  SDL_GetDisplayBounds(display_index, &bounds);
389 
390  SDL_DisplayMode mode;
391 
392  for(int i = 0; i < modes; ++i) {
393  if(SDL_GetDisplayMode(display_index, i, &mode) == 0) {
394  // Exclude any results outside the range of the current DPI.
395  if(mode.w > bounds.w && mode.h > bounds.h) {
396  continue;
397  }
398 
399  if(mode.w >= min_res.x && mode.h >= min_res.y) {
400  result.emplace_back(mode.w, mode.h);
401  }
402  }
403  }
404 
405  if(std::find(result.begin(), result.end(), min_res) == result.end()) {
406  result.push_back(min_res);
407  }
408 
409  if(include_current) {
410  result.push_back(current_resolution());
411  }
412 
413  std::sort(result.begin(), result.end());
414  result.erase(std::unique(result.begin(), result.end()), result.end());
415 
416  return result;
417 }
418 
420 {
421  return point(window->get_size()); // Convert from plain SDL_Point
422 }
423 
425 {
426  return (window->get_flags() & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
427 }
428 
429 void CVideo::set_fullscreen(bool ison)
430 {
431  if(window && is_fullscreen() != ison) {
432  const point& res = preferences::resolution();
433 
434  MODE_EVENT mode;
435 
436  if(ison) {
437  mode = TO_FULLSCREEN;
438  } else {
440  }
441 
442  set_window_mode(mode, res);
443  }
444 
445  // Change the config value.
447 }
448 
450 {
452 }
453 
454 bool CVideo::set_resolution(const unsigned width, const unsigned height)
455 {
456  return set_resolution(point(width, height));
457 }
458 
460 {
461  if(resolution == current_resolution()) {
462  return false;
463  }
464 
465  set_window_mode(TO_RES, resolution);
466 
467  // Change the saved values in preferences.
468  preferences::_set_resolution(resolution);
470 
471  // Push a window-resized event to the queue. This is necessary so various areas
472  // of the game (like GUI2) update properly with the new size.
474 
475  return true;
476 }
477 
478 void CVideo::lock_flips(bool lock)
479 {
480  if(lock) {
481  ++flip_locked_;
482  } else {
483  --flip_locked_;
484  }
485 }
void raise_resize_event()
Definition: events.cpp:653
void set_window_icon(surface &icon)
Sets the icon of the main window.
Definition: video.cpp:306
void _set_fullscreen(bool ison)
Definition: general.cpp:412
bool update_locked() const
Whether the screen has been &#39;locked&#39; or not.
Definition: video.cpp:295
point current_resolution()
Definition: video.cpp:419
const int min_window_height
Definition: general.cpp:68
void _set_maximized(bool ison)
Definition: general.cpp:407
FAKE_TYPES
Definition: video.hpp:42
std::pair< float, float > get_dpi_scale_factor() const
The current scale factor on High-DPI screens.
Definition: video.cpp:344
#define ERR_DP
Definition: video.cpp:33
#define LOG_DP
Definition: video.cpp:32
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
const int min_window_width
Definition: general.cpp:67
Definition: video.hpp:36
void lock_updates(bool value)
Stop the screen being redrawn.
Definition: video.cpp:286
void _set_resolution(const point &res)
Definition: general.cpp:401
static CVideo * singleton_
Definition: video.hpp:215
MODE_EVENT
Definition: video.hpp:84
bool non_interactive() const
Definition: video.cpp:90
int flip_locked_
Definition: video.hpp:252
#define h
int updated_locked_
Definition: video.hpp:251
-file util.hpp
~CVideo()
Definition: video.cpp:81
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition: texture.hpp:26
void render_screen()
Renders the screen.
Definition: video.cpp:242
bool maximized()
Definition: general.cpp:391
int x
x coordinate.
Definition: point.hpp:44
bool set_resolution(const unsigned width, const unsigned height)
Definition: video.cpp:454
void set_window_title(const std::string &title)
Sets the title of the main window.
Definition: video.cpp:300
void make_fake()
Definition: video.cpp:108
void initSDL()
Initializes the SDL video subsystem.
Definition: video.cpp:71
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
The wrapper class for the SDL_Window class.
Definition: window.hpp:45
bool null() const
Definition: texture.hpp:79
void make_test_fake(const unsigned width=1024, const unsigned height=768)
Creates a fake frame buffer for the unit tests.
Definition: video.cpp:114
void lock_flips(bool)
Definition: video.cpp:478
bool fullscreen()
Definition: general.cpp:396
static lg::log_domain log_display("display")
sdl::window * get_window()
Returns a pointer to the underlying SDL window.
Definition: video.cpp:321
int get_width(bool as_pixels=true) const
Returns the window renderer width in pixels or screen coordinates.
Definition: video.cpp:225
virtual void handle_window_event(const SDL_Event &event)
Definition: video.cpp:95
std::size_t i
Definition: function.cpp:933
std::pair< unsigned, unsigned > fake_size_
Definition: video.hpp:231
void init_window()
Initializes a new SDL window instance, taking into account any preiously saved states.
Definition: video.cpp:121
void toggle_fullscreen()
Definition: video.cpp:449
void set_fullscreen(bool ison)
Definition: video.cpp:429
Holds a 2D point.
Definition: point.hpp:23
int w
int get_height(bool as_pixels=true) const
Returns the window renderer height in pixels or in screen coordinates.
Definition: video.cpp:230
static int sort(lua_State *L)
Definition: ltablib.cpp:411
#define MAGIC_DPI_SCALE_NUMBER
Definition: video.cpp:35
bool is_fullscreen() const
Definition: video.cpp:424
Standard logging facilities (interface).
video_event_handler event_handler_
Definition: video.hpp:249
static void delay(unsigned int milliseconds)
Waits a given number of milliseconds before returning.
Definition: video.cpp:235
void set_window_mode(const MODE_EVENT mode, const point &size)
Sets the window&#39;s mode - ie, changing it to fullscreen, maximizing, etc.
Definition: video.cpp:167
Contains a wrapper class for the SDL_Window class.
bool fake_screen_
Definition: video.hpp:229
point resolution()
Definition: general.cpp:375
CVideo(const CVideo &)=delete
SDL_Rect screen_area(bool as_pixels=true) const
Returns the current window renderer area, either in pixels or screen coordinates. ...
Definition: video.cpp:197
void clear_screen()
Clear the screen contents.
Definition: video.cpp:312
int y
y coordinate.
Definition: point.hpp:47
SDL_Renderer * get_renderer()
Returns a pointer to the underlying window&#39;s renderer.
Definition: video.cpp:326
std::vector< point > get_available_resolutions(const bool include_current=false)
Returns the list of available screen resolutions.
Definition: video.cpp:361
bool window_has_flags(uint32_t flags) const
Tests whether the given flags are currently set on the SDL window.
Definition: video.cpp:335
virtual void join_global()
Definition: events.cpp:320
std::unique_ptr< sdl::window > window
The SDL window object.
Definition: video.hpp:218
void render_copy(const texture &txt, SDL_Rect *src_rect=nullptr, SDL_Rect *dst_rect=nullptr, const bool flip_h=false, const bool flip_v=false)
Definition: video.cpp:253