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 http://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 fake_interactive = false;
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 fake_interactive ? false : (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  fake_interactive = true;
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 USE_GL_RENDERING
137  video_flags |= SDL_WINDOW_OPENGL;
138 #endif
139 
141  window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
142  } else if(preferences::maximized()) {
143  window_flags |= SDL_WINDOW_MAXIMIZED;
144  }
145 
146  // Initialize window
147  window.reset(new sdl::window("", x, y, w, h, window_flags, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE));
148 
149  std::cerr << "Setting mode to " << w << "x" << h << std::endl;
150 
152 
153 #ifdef USE_GL_RENDERING
154  // Initialize an OpenGL context for the window.
155  gl_context.reset(new gl::context(window.get()));
156 
157  gl::clear_screen();
158  render_screen();
159 #endif
160 
162 }
163 
165 {
166  assert(window);
167  if(fake_screen_) {
168  return;
169  }
170 
171  switch(mode) {
172  case TO_FULLSCREEN:
173  window->full_screen();
174  break;
175 
176  case TO_WINDOWED:
177  window->to_window();
178  window->restore();
179  break;
180 
181  case TO_MAXIMIZED_WINDOW:
182  window->to_window();
183  window->maximize();
184  break;
185 
186  case TO_RES:
187  window->restore();
188  window->set_size(size.x, size.y);
189  window->center();
190  break;
191  }
192 }
193 
194 SDL_Rect CVideo::screen_area(bool as_pixels) const
195 {
196  if(!window) {
197  return {0, 0,
198  static_cast<int>(fake_size_.first),
199  static_cast<int>(fake_size_.second)
200  };
201  }
202 
203  // First, get the renderer size in pixels.
204  SDL_Point size = window->get_output_size();
205 
206  // Then convert the dimensions into screen coordinates, if applicable.
207  if(!as_pixels) {
208 #ifdef __APPLE__
209  size = window->get_size();
210 #else
211  float scale_x, scale_y;
212  std::tie(scale_x, scale_y) = get_dpi_scale_factor();
213 
214  size.x /= scale_x;
215  size.y /= scale_y;
216 #endif
217  }
218 
219  return {0, 0, size.x, size.y};
220 }
221 
222 int CVideo::get_width(bool as_pixels) const
223 {
224  return screen_area(as_pixels).w;
225 }
226 
227 int CVideo::get_height(bool as_pixels) const
228 {
229  return screen_area(as_pixels).h;
230 }
231 
232 void CVideo::delay(unsigned int milliseconds)
233 {
234  if(!game_config::no_delay) {
235  SDL_Delay(milliseconds);
236  }
237 }
238 
240 {
241  if(fake_screen_ || flip_locked_ > 0) {
242  return;
243  }
244 
245  if(window) {
246  window->render();
247  }
248 }
249 
251  const texture& txt, SDL_Rect* src_rect, SDL_Rect* dst_rect, const bool flip_h, const bool flip_v)
252 {
253  if(!window || txt.null()) {
254  return;
255  }
256 
257  // If no additional data was provided, render immediately.
258  if(!flip_h && !flip_v) {
259  SDL_RenderCopy(*window, txt, src_rect, dst_rect);
260  return;
261  }
262 
263  // Calculate flipping mode.
264  int fmode = SDL_FLIP_NONE;
265 
266  if(flip_h && flip_v) {
267  fmode = SDL_FLIP_HORIZONTAL | SDL_FLIP_VERTICAL;
268  } else if(flip_h) {
269  fmode = SDL_FLIP_HORIZONTAL;
270  } else if(flip_v) {
271  fmode = SDL_FLIP_VERTICAL;
272  }
273 
274  SDL_RendererFlip flip_mode = static_cast<SDL_RendererFlip>(fmode);
275 
276  // TODO: add handling of rotations.
277  static const double rotate_angle = 0;
278  static const SDL_Point* center = nullptr;
279 
280  SDL_RenderCopyEx(*window, txt, src_rect, dst_rect, rotate_angle, center, flip_mode);
281 }
282 
283 void CVideo::lock_updates(bool value)
284 {
285  if(value == true) {
286  ++updated_locked_;
287  } else {
288  --updated_locked_;
289  }
290 }
291 
293 {
294  return updated_locked_ > 0;
295 }
296 
297 void CVideo::set_window_title(const std::string& title)
298 {
299  assert(window);
300  window->set_title(title);
301 }
302 
304 {
305  assert(window);
306  window->set_icon(icon);
307 }
308 
310 {
311  if(!window) {
312  return;
313  }
314 
315  window->fill(0, 0, 0, 255);
316 }
317 
319 {
320  return window.get();
321 }
322 
323 SDL_Renderer* CVideo::get_renderer()
324 {
325  if(!window) {
326  return nullptr;
327  }
328 
329  return *window;
330 }
331 
332 bool CVideo::window_has_flags(uint32_t flags) const
333 {
334  if(!window) {
335  return false;
336  }
337 
338  return (window->get_flags() & flags) != 0;
339 }
340 
341 std::pair<float, float> CVideo::get_dpi_scale_factor() const
342 {
343  std::pair<float, float> result{1.0f, 1.0f};
344 
345  if(!window) {
346  return result;
347  }
348 
349  float hdpi, vdpi;
350  SDL_GetDisplayDPI(window->get_display_index(), nullptr, &hdpi, &vdpi);
351 
352  result.first = hdpi / MAGIC_DPI_SCALE_NUMBER;
353  result.second = vdpi / MAGIC_DPI_SCALE_NUMBER;
354 
355  return result;
356 }
357 
358 std::vector<point> CVideo::get_available_resolutions(const bool include_current)
359 {
360  std::vector<point> result;
361 
362  if(!window) {
363  return result;
364  }
365 
366  const int display_index = window->get_display_index();
367 
368  const int modes = SDL_GetNumDisplayModes(display_index);
369  if(modes <= 0) {
370  std::cerr << "No modes supported\n";
371  return result;
372  }
373 
375 
376 #if 0
377  // DPI scale factor.
378  float scale_h, scale_v;
379  std::tie(scale_h, scale_v) = get_dpi_scale_factor();
380 #endif
381 
382  // The maximum size to which this window can be set. For some reason this won't
383  // pop up as a display mode of its own.
384  SDL_Rect bounds;
385  SDL_GetDisplayBounds(display_index, &bounds);
386 
387  SDL_DisplayMode mode;
388 
389  for(int i = 0; i < modes; ++i) {
390  if(SDL_GetDisplayMode(display_index, i, &mode) == 0) {
391  // Exclude any results outside the range of the current DPI.
392  if(mode.w > bounds.w && mode.h > bounds.h) {
393  continue;
394  }
395 
396  if(mode.w >= min_res.x && mode.h >= min_res.y) {
397  result.emplace_back(mode.w, mode.h);
398  }
399  }
400  }
401 
402  if(std::find(result.begin(), result.end(), min_res) == result.end()) {
403  result.push_back(min_res);
404  }
405 
406  if(include_current) {
407  result.push_back(current_resolution());
408  }
409 
410  std::sort(result.begin(), result.end());
411  result.erase(std::unique(result.begin(), result.end()), result.end());
412 
413  return result;
414 }
415 
417 {
418  return point(window->get_size()); // Convert from plain SDL_Point
419 }
420 
422 {
423  return (window->get_flags() & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
424 }
425 
426 void CVideo::set_fullscreen(bool ison)
427 {
428  if(window && is_fullscreen() != ison) {
429  const point& res = preferences::resolution();
430 
431  MODE_EVENT mode;
432 
433  if(ison) {
434  mode = TO_FULLSCREEN;
435  } else {
437  }
438 
439  set_window_mode(mode, res);
440  }
441 
442  // Change the config value.
444 }
445 
447 {
449 }
450 
451 bool CVideo::set_resolution(const unsigned width, const unsigned height)
452 {
453  return set_resolution(point(width, height));
454 }
455 
457 {
458  if(resolution == current_resolution()) {
459  return false;
460  }
461 
462  set_window_mode(TO_RES, resolution);
463 
464  // Change the saved values in preferences.
465  preferences::_set_resolution(resolution);
467 
468  // Push a window-resized event to the queue. This is necessary so various areas
469  // of the game (like GUI2) update properly with the new size.
471 
472  return true;
473 }
474 
475 void CVideo::lock_flips(bool lock)
476 {
477  if(lock) {
478  ++flip_locked_;
479  } else {
480  --flip_locked_;
481  }
482 }
void raise_resize_event()
Definition: events.cpp:640
void set_window_icon(surface &icon)
Sets the icon of the main window.
Definition: video.cpp:303
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:292
point current_resolution()
Definition: video.cpp:416
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:341
#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:283
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:239
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:451
void set_window_title(const std::string &title)
Sets the title of the main window.
Definition: video.cpp:297
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:475
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:318
int get_width(bool as_pixels=true) const
Returns the window renderer width in pixels or screen coordinates.
Definition: video.cpp:222
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:446
void set_fullscreen(bool ison)
Definition: video.cpp:426
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:227
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:421
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:232
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:164
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:194
void clear_screen()
Clear the screen contents.
Definition: video.cpp:309
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:323
std::vector< point > get_available_resolutions(const bool include_current=false)
Returns the list of available screen resolutions.
Definition: video.cpp:358
bool window_has_flags(uint32_t flags) const
Tests whether the given flags are currently set on the SDL window.
Definition: video.cpp:332
virtual void join_global()
Definition: events.cpp:304
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:250