The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 "display.hpp"
18 #include "floating_label.hpp"
19 #include "image.hpp"
20 #include "log.hpp"
21 #include "preferences/general.hpp"
22 #include "sdl/point.hpp"
23 #include "sdl/userevent.hpp"
24 #include "sdl/utils.hpp"
25 #include "sdl/window.hpp"
26 
27 #include <cassert>
28 #include <vector>
29 
30 static lg::log_domain log_display("display");
31 #define LOG_DP LOG_STREAM(info, log_display)
32 #define ERR_DP LOG_STREAM(err, log_display)
33 
34 #define MAGIC_DPI_SCALE_NUMBER 96
35 
36 CVideo* CVideo::singleton_ = nullptr;
37 
38 namespace
39 {
40 surface frameBuffer = nullptr;
41 bool fake_interactive = false;
42 }
43 
44 namespace video2
45 {
46 std::list<events::sdl_handler*> draw_layers;
47 
48 draw_layering::draw_layering(const bool auto_join)
49  : sdl_handler(auto_join)
50 {
51  draw_layers.push_back(this);
52 }
53 
55 {
56  draw_layers.remove(this);
57 
59 }
60 
62 {
63  SDL_Event event;
64  event.type = SDL_WINDOWEVENT;
65  event.window.event = SDL_WINDOWEVENT_RESIZED;
66  event.window.data1 = (*frameBuffer).h;
67  event.window.data2 = (*frameBuffer).w;
68 
69  for(const auto& layer : draw_layers) {
70  layer->handle_window_event(event);
71  }
72 
73  SDL_Event drawEvent;
75 
76  drawEvent.type = DRAW_ALL_EVENT;
77  drawEvent.user = data;
78  SDL_FlushEvent(DRAW_ALL_EVENT);
79  SDL_PushEvent(&drawEvent);
80 }
81 
82 } // video2
83 
85  : window()
86  , fake_screen_(false)
87  , help_string_(0)
88  , updated_locked_(0)
89  , flip_locked_(0)
90  , refresh_rate_(0)
91 {
92  assert(!singleton_);
93  singleton_ = this;
94 
95  initSDL();
96 
97  switch(type) {
98  case NO_FAKE:
99  break;
100  case FAKE:
101  make_fake();
102  break;
103  case FAKE_TEST:
104  make_test_fake();
105  break;
106  }
107 }
108 
110 {
111  const int res = SDL_InitSubSystem(SDL_INIT_VIDEO);
112 
113  if(res < 0) {
114  ERR_DP << "Could not initialize SDL_video: " << SDL_GetError() << std::endl;
115  throw CVideo::error();
116  }
117 }
118 
120 {
121  if(sdl_get_version() >= version_info(2, 0, 6)) {
122  // Because SDL will free the framebuffer,
123  // ensure that we won't attempt to free it.
124  frameBuffer.clear_without_free();
125  }
126 
127  LOG_DP << "calling SDL_Quit()\n";
128  SDL_Quit();
129  assert(singleton_);
130  singleton_ = nullptr;
131  LOG_DP << "called SDL_Quit()\n";
132 }
133 
135 {
136  return fake_interactive ? false : (window == nullptr);
137 }
138 
140 {
141  if(event.type == SDL_WINDOWEVENT) {
142  switch(event.window.event) {
143  case SDL_WINDOWEVENT_RESIZED:
144  case SDL_WINDOWEVENT_RESTORED:
145  case SDL_WINDOWEVENT_SHOWN:
146  case SDL_WINDOWEVENT_EXPOSED:
147  // if(display::get_singleton())
148  // display::get_singleton()->redraw_everything();
149  SDL_Event drawEvent;
151 
152  drawEvent.type = DRAW_ALL_EVENT;
153  drawEvent.user = data;
154 
155  SDL_FlushEvent(DRAW_ALL_EVENT);
156  SDL_PushEvent(&drawEvent);
157  break;
158  }
159  }
160 }
161 
162 void CVideo::blit_surface(int x, int y, surface surf, SDL_Rect* srcrect, SDL_Rect* clip_rect)
163 {
164  surface& target(getSurface());
165  SDL_Rect dst{x, y, 0, 0};
166 
167  const clip_rect_setter clip_setter(target, clip_rect, clip_rect != nullptr);
168  sdl_blit(surf, srcrect, target, &dst);
169 }
170 
172 {
173  fake_screen_ = true;
174  refresh_rate_ = 1;
175 
176 #if SDL_VERSION_ATLEAST(2, 0, 6)
177  frameBuffer = SDL_CreateRGBSurfaceWithFormat(0, 16, 16, 24, SDL_PIXELFORMAT_BGR888);
178 #else
179  frameBuffer = SDL_CreateRGBSurface(0, 16, 16, 24, 0xFF0000, 0xFF00, 0xFF, 0);
180 #endif
181 
182  image::set_pixel_format(frameBuffer->format);
183 }
184 
185 void CVideo::make_test_fake(const unsigned width, const unsigned height)
186 {
187 #if SDL_VERSION_ATLEAST(2, 0, 6)
188  frameBuffer = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_BGR888);
189 #else
190  frameBuffer = SDL_CreateRGBSurface(0, width, height, 32, 0xFF0000, 0xFF00, 0xFF, 0);
191 #endif
192 
193  image::set_pixel_format(frameBuffer->format);
194 
195  fake_interactive = true;
196  refresh_rate_ = 1;
197 }
198 
200 {
201  if(!window) {
202  return;
203  }
204 
205  surface fb = SDL_GetWindowSurface(*window);
206  if(!frameBuffer) {
207  frameBuffer = fb;
208  } else {
209  if(sdl_get_version() >= version_info(2, 0, 6)) {
210  // Because SDL has already freed the old framebuffer,
211  // ensure that we won't attempt to free it.
212  frameBuffer.clear_without_free();
213  }
214 
215  frameBuffer.assign(fb);
216  }
217 }
218 
220 {
221  // Position
222  const int x = preferences::fullscreen() ? SDL_WINDOWPOS_UNDEFINED : SDL_WINDOWPOS_CENTERED;
223  const int y = preferences::fullscreen() ? SDL_WINDOWPOS_UNDEFINED : SDL_WINDOWPOS_CENTERED;
224 
225  // Dimensions
226  const point res = preferences::resolution();
227  const int w = res.x;
228  const int h = res.y;
229 
230  uint32_t window_flags = 0;
231 
232  // Add any more default flags here
233  window_flags |= SDL_WINDOW_RESIZABLE;
234 
236  window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
237  } else if(preferences::maximized()) {
238  window_flags |= SDL_WINDOW_MAXIMIZED;
239  }
240 
241  // Initialize window
242  window.reset(new sdl::window("", x, y, w, h, window_flags, SDL_RENDERER_SOFTWARE));
243 
244  std::cerr << "Setting mode to " << w << "x" << h << std::endl;
245 
247 
248  SDL_DisplayMode currentDisplayMode;
249  SDL_GetCurrentDisplayMode(window->get_display_index(), &currentDisplayMode);
250  refresh_rate_ = currentDisplayMode.refresh_rate != 0 ? currentDisplayMode.refresh_rate : 60;
251 
253 
255  if(frameBuffer) {
256  image::set_pixel_format(frameBuffer->format);
257  }
258 }
259 
261 {
262  assert(window);
263  if(fake_screen_) {
264  return;
265  }
266 
267  switch(mode) {
268  case TO_FULLSCREEN:
269  window->full_screen();
270  break;
271 
272  case TO_WINDOWED:
273  window->to_window();
274  window->restore();
275  break;
276 
277  case TO_MAXIMIZED_WINDOW:
278  window->to_window();
279  window->maximize();
280  break;
281 
282  case TO_RES:
283  window->restore();
284  window->set_size(size.x, size.y);
285  window->center();
286  break;
287  }
288 
290  if(frameBuffer) {
291  image::set_pixel_format(frameBuffer->format);
292  }
293 }
294 
295 SDL_Rect CVideo::screen_area(bool as_pixels) const
296 {
297  if(!window) {
298  return {0, 0, frameBuffer->w, frameBuffer->h};
299  }
300 
301  // First, get the renderer size in pixels.
302  SDL_Point size = window->get_output_size();
303 
304  // Then convert the dimensions into screen coordinates, if applicable.
305  if(!as_pixels) {
306  float scale_x, scale_y;
307  std::tie(scale_x, scale_y) = get_dpi_scale_factor();
308 
309  size.x /= scale_x;
310  size.y /= scale_y;
311  }
312 
313  return {0, 0, size.x, size.y};
314 }
315 
316 int CVideo::get_width(bool as_pixels) const
317 {
318  return screen_area(as_pixels).w;
319 }
320 
321 int CVideo::get_height(bool as_pixels) const
322 {
323  return screen_area(as_pixels).h;
324 }
325 
326 void CVideo::delay(unsigned int milliseconds)
327 {
328  if(!game_config::no_delay) {
329  SDL_Delay(milliseconds);
330  }
331 }
332 
334 {
335  if(fake_screen_ || flip_locked_ > 0) {
336  return;
337  }
338 
339  if(window) {
340  window->render();
341  }
342 }
343 
344 void CVideo::lock_updates(bool value)
345 {
346  if(value == true) {
347  ++updated_locked_;
348  } else {
349  --updated_locked_;
350  }
351 }
352 
354 {
355  return updated_locked_ > 0;
356 }
357 
359 {
360  assert(window);
361  window->set_title(title);
362 }
363 
365 {
366  assert(window);
367  window->set_icon(icon);
368 }
369 
371 {
372  if(!window) {
373  return;
374  }
375 
376  window->fill(0, 0, 0, 255);
377 }
378 
380 {
381  return window.get();
382 }
383 
384 bool CVideo::window_has_flags(uint32_t flags) const
385 {
386  if(!window) {
387  return false;
388  }
389 
390  return (window->get_flags() & flags) != 0;
391 }
392 
393 std::pair<float, float> CVideo::get_dpi_scale_factor() const
394 {
395  std::pair<float, float> result{1.0f, 1.0f};
396 
397  if(!window) {
398  return result;
399  }
400 
401  float hdpi, vdpi;
402  SDL_GetDisplayDPI(window->get_display_index(), nullptr, &hdpi, &vdpi);
403 
404  result.first = hdpi / MAGIC_DPI_SCALE_NUMBER;
405  result.second = vdpi / MAGIC_DPI_SCALE_NUMBER;
406 
407  return result;
408 }
409 
410 std::vector<point> CVideo::get_available_resolutions(const bool include_current)
411 {
412  std::vector<point> result;
413 
414  if(!window) {
415  return result;
416  }
417 
418  const int display_index = window->get_display_index();
419 
420  const int modes = SDL_GetNumDisplayModes(display_index);
421  if(modes <= 0) {
422  std::cerr << "No modes supported\n";
423  return result;
424  }
425 
427 
428 #if 0
429  // DPI scale factor.
430  float scale_h, scale_v;
431  std::tie(scale_h, scale_v) = get_dpi_scale_factor();
432 #endif
433 
434  // The maximum size to which this window can be set. For some reason this won't
435  // pop up as a display mode of its own.
436  SDL_Rect bounds;
437  SDL_GetDisplayBounds(display_index, &bounds);
438 
439  SDL_DisplayMode mode;
440 
441  for(int i = 0; i < modes; ++i) {
442  if(SDL_GetDisplayMode(display_index, i, &mode) == 0) {
443  // Exclude any results outside the range of the current DPI.
444  if(mode.w > bounds.w && mode.h > bounds.h) {
445  continue;
446  }
447 
448  if(mode.w >= min_res.x && mode.h >= min_res.y) {
449  result.emplace_back(mode.w, mode.h);
450  }
451  }
452  }
453 
454  if(std::find(result.begin(), result.end(), min_res) == result.end()) {
455  result.push_back(min_res);
456  }
457 
458  if(include_current) {
459  result.push_back(current_resolution());
460  }
461 
462  std::sort(result.begin(), result.end());
463  result.erase(std::unique(result.begin(), result.end()), result.end());
464 
465  return result;
466 }
467 
469 {
470  return frameBuffer;
471 }
472 
474 {
475  return point(window->get_size()); // Convert from plain SDL_Point
476 }
477 
479 {
480  return (window->get_flags() & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
481 }
482 
484 {
486 
487  const color_t color{0, 0, 0, 0xbb};
488 
489  int size = font::SIZE_LARGE;
490 
491  while(size > 0) {
492  if(font::line_width(str, size) > get_width()) {
493  size--;
494  } else {
495  break;
496  }
497  }
498 
499  const int border = 5;
500 
501  font::floating_label flabel(str);
502  flabel.set_font_size(size);
503  flabel.set_position(get_width() / 2, get_height());
504  flabel.set_bg_color(color);
505  flabel.set_border_size(border);
506 
508 
509  const SDL_Rect& rect = font::get_floating_label_rect(help_string_);
510  font::move_floating_label(help_string_, 0.0, -double(rect.h));
511 
512  return help_string_;
513 }
514 
516 {
517  if(handle == help_string_) {
519  help_string_ = 0;
520  }
521 }
522 
524 {
526 }
527 
528 void CVideo::set_fullscreen(bool ison)
529 {
530  if(window && is_fullscreen() != ison) {
531  const point& res = preferences::resolution();
532 
533  MODE_EVENT mode;
534 
535  if(ison) {
536  mode = TO_FULLSCREEN;
537  } else {
539  }
540 
541  set_window_mode(mode, res);
542 
543  if(display* d = display::get_singleton()) {
544  d->redraw_everything();
545  }
546  }
547 
548  // Change the config value.
550 }
551 
553 {
555 }
556 
557 bool CVideo::set_resolution(const unsigned width, const unsigned height)
558 {
559  return set_resolution(point(width, height));
560 }
561 
563 {
564  if(resolution == current_resolution()) {
565  return false;
566  }
567 
568  set_window_mode(TO_RES, resolution);
569 
570  if(display* d = display::get_singleton()) {
571  d->redraw_everything();
572  }
573 
574  // Change the saved values in preferences.
575  preferences::_set_resolution(resolution);
577 
578  // Push a window-resized event to the queue. This is necessary so various areas
579  // of the game (like GUI2) update properly with the new size.
581 
582  return true;
583 }
584 
585 void CVideo::lock_flips(bool lock)
586 {
587  if(lock) {
588  ++flip_locked_;
589  } else {
590  --flip_locked_;
591  }
592 }
void raise_resize_event()
Definition: events.cpp:638
void set_window_icon(surface &icon)
Sets the icon of the main window.
Definition: video.cpp:364
void _set_fullscreen(bool ison)
Definition: general.cpp:412
draw_layering(const bool auto_join=true)
Definition: video.cpp:48
std::vector< char_t > string
std::pair< float, float > get_dpi_scale_factor() const
The current scale factor on High-DPI screens.
Definition: video.cpp:393
point current_resolution()
Definition: video.cpp:473
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:87
const int min_window_height
Definition: general.cpp:68
std::list< events::sdl_handler * > draw_layers
Definition: video.cpp:46
void set_pixel_format(SDL_PixelFormat *format)
sets the pixel format used by the images.
Definition: image.cpp:712
void _set_maximized(bool ison)
Definition: general.cpp:407
FAKE_TYPES
Definition: video.hpp:37
#define ERR_DP
Definition: video.cpp:32
#define LOG_DP
Definition: video.cpp:31
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
const int min_window_width
Definition: general.cpp:67
int help_string_
Curent ID of the help string.
Definition: video.hpp:271
Definition: video.hpp:31
void flip()
Renders the screen.
Definition: video.cpp:333
void lock_updates(bool value)
Stop the screen being redrawn.
Definition: video.cpp:344
void remove_floating_label(int handle)
removes the floating label given by 'handle' from the screen
void _set_resolution(const point &res)
Definition: general.cpp:401
bool is_fullscreen() const
Definition: video.cpp:478
static CVideo * singleton_
Definition: video.hpp:241
MODE_EVENT
Definition: video.hpp:76
int refresh_rate_
Definition: video.hpp:275
int flip_locked_
Definition: video.hpp:274
#define h
void set_font_size(int font_size)
void blit_surface(int x, int y, surface surf, SDL_Rect *srcrect=nullptr, SDL_Rect *clip_rect=nullptr)
Draws a surface directly onto the screen framebuffer.
Definition: video.cpp:162
#define d
int updated_locked_
Definition: video.hpp:273
-file util.hpp
~CVideo()
Definition: video.cpp:119
surface & getSurface()
Returns a reference to the framebuffer.
Definition: video.cpp:468
bool maximized()
Definition: general.cpp:391
void move_floating_label(int handle, double xmove, double ymove)
moves the floating label given by 'handle' by (xmove,ymove)
int x
x coordinate.
Definition: point.hpp:44
virtual ~draw_layering()
Definition: video.cpp:54
bool set_resolution(const unsigned width, const unsigned height)
Definition: video.cpp:557
void set_window_title(const std::string &title)
Sets the title of the main window.
Definition: video.cpp:358
void make_fake()
Definition: video.cpp:171
void initSDL()
Initializes the SDL video subsystem.
Definition: video.cpp:109
The wrapper class for the SDL_Window class.
Definition: window.hpp:44
void make_test_fake(const unsigned width=1024, const unsigned height=768)
Creates a fake frame buffer for the unit tests.
Definition: video.cpp:185
void lock_flips(bool)
Definition: video.cpp:585
bool window_has_flags(uint32_t flags) const
Tests whether the given flags are currently set on the SDL window.
Definition: video.cpp:384
bool fullscreen()
Definition: general.cpp:396
int get_width(bool as_pixels=true) const
Returns the window renderer width in pixels or screen coordinates.
Definition: video.cpp:316
static lg::log_domain log_display("display")
void set_bg_color(const color_t &bg_color)
void set_position(double xpos, double ypos)
map_display and display: classes which take care of displaying the map and game-data on the screen...
sdl::window * get_window()
Returns a pointer to the underlying SDL window.
Definition: video.cpp:379
#define DRAW_ALL_EVENT
Definition: events.hpp:29
size_t size(const utf8::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
Definition: video.cpp:44
virtual void handle_window_event(const SDL_Event &event)
Definition: video.cpp:139
bool update_locked() const
Whether the screen has been 'locked' or not.
Definition: video.cpp:353
void init_window()
Initializes a new SDL window instance, taking into account any preiously saved states.
Definition: video.cpp:219
void toggle_fullscreen()
Definition: video.cpp:552
size_t i
Definition: function.cpp:933
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
void set_fullscreen(bool ison)
Definition: video.cpp:528
Holds a 2D point.
Definition: point.hpp:23
SDL_Rect get_floating_label_rect(int handle)
int w
static int sort(lua_State *L)
Definition: ltablib.cpp:411
version_info sdl_get_version()
Definition: utils.cpp:33
void set_border_size(int border)
Represents version numbers.
Definition: version.hpp:43
int set_help_string(const std::string &str)
Displays a help string with the given text.
Definition: video.cpp:483
const int SIZE_LARGE
Definition: constants.cpp:27
#define MAGIC_DPI_SCALE_NUMBER
Definition: video.cpp:34
void clear_without_free()
Definition: surface.hpp:71
int get_height(bool as_pixels=true) const
Returns the window renderer height in pixels or in screen coordinates.
Definition: video.cpp:321
bool find(E event, F functor)
Tests whether an event handler is available.
void trigger_full_redraw()
Definition: video.cpp:61
Standard logging facilities (interface).
video_event_handler event_handler_
Definition: video.hpp:268
void clear_all_help_strings()
Removes all help strings.
Definition: video.cpp:523
static void delay(unsigned int milliseconds)
Waits a given number of milliseconds before returning.
Definition: video.cpp:326
bool non_interactive() const
Definition: video.cpp:134
void set_window_mode(const MODE_EVENT mode, const point &size)
Sets the window's mode - ie, changing it to fullscreen, maximizing, etc.
Definition: video.cpp:260
Contains a wrapper class for the SDL_Window class.
SDL_Rect screen_area(bool as_pixels=true) const
Returns the current window renderer area, either in pixels or screen coordinates. ...
Definition: video.cpp:295
bool fake_screen_
Definition: video.hpp:250
point resolution()
Definition: general.cpp:375
void update_framebuffer()
Updates and ensures the framebuffer surface is valid.
Definition: video.cpp:199
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:33
CVideo(const CVideo &)=delete
void clear_screen()
Clear the screen contents.
Definition: video.cpp:370
std::shared_ptr< halo_record > handle
Definition: halo.hpp:31
int y
y coordinate.
Definition: point.hpp:47
std::vector< point > get_available_resolutions(const bool include_current=false)
Returns the list of available screen resolutions.
Definition: video.cpp:410
virtual void join_global()
Definition: events.cpp:299
int line_width(const std::string &line, int font_size, int style)
Determine the width of a line of text given a certain font size.
Definition: sdl_ttf.cpp:416
std::unique_ptr< sdl::window > window
The SDL window object.
Definition: video.hpp:244
void clear_help_string(int handle)
Removes the help string with the given handle.
Definition: video.cpp:515