The Battle for Wesnoth  1.19.13+dev
video.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2025
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "video.hpp"
17 
18 #include "draw_manager.hpp"
19 #include "font/text.hpp"
20 #include "log.hpp"
21 #include "picture.hpp"
23 #include "sdl/point.hpp"
24 #include "sdl/texture.hpp"
25 #include "sdl/utils.hpp"
26 #include "sdl/window.hpp"
27 
28 #ifdef TARGET_OS_OSX
29 #include "desktop/apple_video.hpp"
30 #include "game_version.hpp"
31 #endif
32 
33 #include <SDL2/SDL.h>
34 #include <SDL2/SDL_render.h> // SDL_Texture
35 
36 #include <cassert>
37 #include <vector>
38 
39 #ifdef __ANDROID__
40 extern "C" {
41  SDL_Surface* Android_AP_getFrameBuffer();
42 }
43 #endif
44 
45 static lg::log_domain log_display("display");
46 #define LOG_DP LOG_STREAM(info, log_display)
47 #define ERR_DP LOG_STREAM(err, log_display)
48 #define WRN_DP LOG_STREAM(warn, log_display)
49 #define DBG_DP LOG_STREAM(debug, log_display)
50 
51 namespace
52 {
53 /** The SDL window object. Will be null only if headless_. */
54 std::unique_ptr<sdl::window> window;
55 
56 /** The main offscreen render target. */
57 texture render_texture_ = {};
58 
59 /** The current offscreen render target. */
60 texture current_render_target_ = {};
61 
62 bool headless_ = false; /**< running with no window at all */
63 bool testing_ = false; /**< running unit tests */
64 point test_resolution_ = {1024, 768}; /**< resolution for unit tests */
65 int refresh_rate_ = 0;
66 point game_canvas_size_ = {0, 0};
67 int pixel_scale_ = 1;
68 rect input_area_ = {};
69 
70 } // anon namespace
71 
72 namespace video
73 {
74 
75 // Non-public interface
76 void render_screen(); // exposed and used only in draw_manager.cpp
77 
78 // Internal functions
79 static void init_window(bool hidden=false);
80 static void init_test_window();
81 static void init_fake();
82 static void init_test();
83 static bool update_framebuffer();
84 static bool update_test_framebuffer();
85 static point draw_offset();
86 
87 
88 void init(fake type)
89 {
90  LOG_DP << "initializing video";
91  if(SDL_WasInit(SDL_INIT_VIDEO)) {
92  throw error("video subsystem already initialized");
93  }
94  if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
95  ERR_DP << "Could not initialize SDL_video: " << SDL_GetError();
96  throw error("Video initialization failed");
97  }
98 
99  switch(type) {
100  case fake::none:
101  init_window();
102  break;
103  case fake::no_window:
104  init_fake();
105  break;
106  case fake::no_draw:
107  init_test();
108  break;
109  case fake::hide_window:
110  init_window(true);
111  break;
112  default:
113  throw error("unrecognized fake type passed to video::init");
114  }
115 }
116 
117 void deinit()
118 {
119  LOG_DP << "deinitializing video";
120 
121  // SDL_INIT_TIMER is always initialized at program start.
122  // If it is not initialized here, there is a problem.
123  assert(SDL_WasInit(SDL_INIT_TIMER));
124 
125  // Clear any static texture caches,
126  // lest they try to delete textures after SDL_Quit.
128  render_texture_.reset();
129  current_render_target_.reset();
130 
131  // Destroy the window, and thus also the renderer.
132  window.reset();
133 
134  // Close the video subsystem.
135  if(SDL_WasInit(SDL_INIT_VIDEO)) {
136  LOG_DP << "quitting SDL video subsystem";
137  SDL_QuitSubSystem(SDL_INIT_VIDEO);
138  }
139  if(SDL_WasInit(SDL_INIT_VIDEO)) {
140  // This should not have been initialized multiple times
141  throw error("video subsystem still initialized after deinit");
142  }
143 }
144 
145 bool headless()
146 {
147  return headless_;
148 }
149 
150 bool testing()
151 {
152  return testing_;
153 }
154 
155 void init_fake()
156 {
157  LOG_DP << "running headless";
158  headless_ = true;
159  refresh_rate_ = 1;
160  game_canvas_size_ = {800,600};
161 }
162 
163 void init_test()
164 {
165  testing_ = true;
166  refresh_rate_ = 1;
168 }
169 
170 /** Returns true if the buffer was changed */
172 {
173  if (!window) {
174  throw("trying to update test framebuffer with no window");
175  }
176 
177  bool changed = false;
178 
179  // TODO: code unduplication
180  // Build or update the current render texture.
181  if (render_texture_) {
182  int w, h;
183  SDL_QueryTexture(render_texture_, nullptr, nullptr, &w, &h);
184  if (w != test_resolution_.x || h != test_resolution_.y) {
185  // Delete it and let it be recreated.
186  LOG_DP << "destroying old render texture";
187  render_texture_.reset();
188  }
189  }
190  if (!render_texture_) {
191  LOG_DP << "creating offscreen render texture";
192  render_texture_.assign(SDL_CreateTexture(
193  *window,
194  window->pixel_format(),
195  SDL_TEXTUREACCESS_TARGET,
196  test_resolution_.x, test_resolution_.y
197  ));
198  LOG_DP << "updated render target to " << test_resolution_.x
199  << "x" << test_resolution_.y;
200  changed = true;
201  }
202 
203  pixel_scale_ = 1;
204  game_canvas_size_ = test_resolution_;
205  input_area_ = {{}, test_resolution_};
206 
207  // The render texture is always the render target in this case.
208  force_render_target(render_texture_);
209 
210  return changed;
211 }
212 
214 {
215  if (!window) {
216  throw error("trying to update framebuffer with no window");
217  }
218 
219  if (testing_) {
220  return update_test_framebuffer();
221  }
222 
223  bool changed = false;
224 
225  // Make sure we're getting values from the native window.
226  SDL_SetRenderTarget(*window, nullptr);
227 
228  // Non-integer scales are not currently supported.
229  // This option makes things neater when window size is not a perfect
230  // multiple of logical size, which can happen when manually resizing.
231  SDL_RenderSetIntegerScale(*window, SDL_TRUE);
232 
233  // Find max valid pixel scale at current output size.
234  point osize(window->get_output_size());
235  int max_scale = std::min(
238  max_scale = std::min(max_scale, pref_constants::max_pixel_scale);
239 
240  // Determine best pixel scale according to preference and window size
241  int scale = 1;
242  if (prefs::get().auto_pixel_scale()) {
243  // Try to match the default size (1280x720) but do not reduce below
244  int def_scale = std::min(
247  scale = std::min(max_scale, def_scale);
248  // Otherwise reduce to keep below the max window size (1920x1080).
249  int min_scale = std::min(
250  osize.x / (pref_constants::max_window_width+1) + 1,
251  osize.y / (pref_constants::max_window_height+1) + 1);
252  scale = std::max(scale, min_scale);
253  } else {
254  scale = std::min(max_scale, prefs::get().pixel_scale());
255  }
256  // Cache it for easy access.
257  if (pixel_scale_ != scale) {
258  pixel_scale_ = scale;
259  changed = true;
260  }
261 
262  // Update logical size if it doesn't match the current resolution and scale.
263  point lsize(window->get_logical_size());
264  point wsize(window->get_size());
265  if (lsize.x != osize.x / scale || lsize.y != osize.y / scale) {
266  if (!prefs::get().auto_pixel_scale() && scale < prefs::get().pixel_scale()) {
267  LOG_DP << "reducing pixel scale from desired "
268  << prefs::get().pixel_scale() << " to maximum allowable "
269  << scale;
270  }
271  LOG_DP << "pixel scale: " << scale;
272  LOG_DP << "overriding logical size";
273  LOG_DP << " old lsize: " << lsize;
274  LOG_DP << " old wsize: " << wsize;
275  LOG_DP << " old osize: " << osize;
276  window->set_logical_size(osize.x / scale, osize.y / scale);
277  lsize = window->get_logical_size();
278  wsize = window->get_size();
279  osize = window->get_output_size();
280  LOG_DP << " new lsize: " << lsize;
281  LOG_DP << " new wsize: " << wsize;
282  LOG_DP << " new osize: " << osize;
283  float sx, sy;
284  SDL_RenderGetScale(*window, &sx, &sy);
285  LOG_DP << " render scale: " << sx << ", " << sy;
286  }
287  // Cache it for easy access
288  game_canvas_size_ = lsize;
289 
290  // Build or update the current render texture.
291  if (render_texture_) {
292  int w, h;
293  SDL_QueryTexture(render_texture_, nullptr, nullptr, &w, &h);
294  if (w != osize.x || h != osize.y) {
295  // Delete it and let it be recreated.
296  LOG_DP << "destroying old render texture";
297  render_texture_.reset();
298  } else {
299  // This isn't currently used, but ensure it's accurate anyway.
300  render_texture_.set_draw_size(lsize);
301  }
302  }
303  if (!render_texture_) {
304  LOG_DP << "creating offscreen render texture";
305  render_texture_.assign(SDL_CreateTexture(
306  *window,
307  window->pixel_format(),
308  SDL_TEXTUREACCESS_TARGET,
309  osize.x, osize.y
310  ));
311  // This isn't really necessary, but might be nice to have attached
312  render_texture_.set_draw_size(lsize);
313  changed = true;
314  }
315 
316  // Assign the render texture now. It will be used for all drawing.
317  force_render_target(render_texture_);
318 
319  // By default input area is the same as the window area.
320  input_area_ = {{}, wsize};
321 
322  rect active_area = to_output(draw_area());
323  if (active_area.size() != osize) {
324  LOG_DP << "render target offset: LT " << active_area.origin() << " RB "
325  << osize - active_area.size() - active_area.origin();
326  // Translate active_area into display coordinates as input_area_
327  input_area_ = {
328  (active_area.origin() * wsize) / osize,
329  (active_area.origin() * wsize) / osize
330  };
331  LOG_DP << "input area: " << input_area_;
332  }
333 
334  return changed;
335 }
336 
338 {
339  LOG_DP << "creating test window " << test_resolution_.x
340  << "x" << test_resolution_.y;
341 
342  uint32_t window_flags = 0;
343  window_flags |= SDL_WINDOW_HIDDEN;
344  // The actual window won't be used, as we'll be rendering to texture.
345 
346  uint32_t renderer_flags = 0;
347  renderer_flags |= SDL_RENDERER_TARGETTEXTURE;
348  // All we need is to be able to render to texture.
349 
350  window.reset(new sdl::window(
351  "", 0, 0, test_resolution_.x, test_resolution_.y,
352  window_flags, renderer_flags
353  ));
354 
356 }
357 
358 void init_window(bool hidden)
359 {
360  // Position
361  const int x = prefs::get().fullscreen() ? SDL_WINDOWPOS_UNDEFINED : SDL_WINDOWPOS_CENTERED;
362  const int y = prefs::get().fullscreen() ? SDL_WINDOWPOS_UNDEFINED : SDL_WINDOWPOS_CENTERED;
363 
364  // Dimensions
365  const point res = prefs::get().resolution();
366  const int w = res.x;
367  const int h = res.y;
368 
369  uint32_t window_flags = 0;
370 
371  // Add any more default flags here
372  window_flags |= SDL_WINDOW_RESIZABLE;
373  window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
374 
375  if(prefs::get().fullscreen()) {
376  window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
377  } else if(prefs::get().maximized()) {
378  window_flags |= SDL_WINDOW_MAXIMIZED;
379  }
380 
381 #ifdef __ANDROID__
382  SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles");
383 #endif
384 
385  if(hidden) {
386  LOG_DP << "hiding main window";
387  window_flags |= SDL_WINDOW_HIDDEN;
388  }
389 
390  uint32_t renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
391 
392  if(prefs::get().vsync()) {
393  LOG_DP << "VSYNC on";
394  renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
395  }
396 
397  // Initialize window
398  window.reset(new sdl::window("", x, y, w, h, window_flags, renderer_flags));
399 
400  // It is assumed that this function is only ever called once.
401  // If that is no longer true, then you should clean things up.
402  assert(!render_texture_);
403 
404  PLAIN_LOG << "Setting mode to " << w << "x" << h;
405 
407 
408  SDL_DisplayMode currentDisplayMode;
409  SDL_GetCurrentDisplayMode(window->get_display_index(), &currentDisplayMode);
410  refresh_rate_ = currentDisplayMode.refresh_rate != 0 ? currentDisplayMode.refresh_rate : 60;
411 
412 #ifdef __ANDROID__
413  window->set_size(w, h);
414 #endif
415 
417 }
418 
420 {
421  return bool(window);
422 }
423 
425 {
426  if (testing_) {
427  return test_resolution_;
428  }
429  // As we are rendering via an abstraction, we should never need this.
430  return window->get_output_size();
431 }
432 
434 {
435  if (testing_) {
436  return test_resolution_;
437  }
438  return window->get_size();
439 }
440 
442 {
443  return {0, 0, game_canvas_size_.x, game_canvas_size_.y};
444 }
445 
447 {
448  return game_canvas_size_;
449 }
450 
452 {
453  return current_render_target_.draw_size();
454 }
455 
457 {
458  return {0, 0, current_render_target_.w(), current_render_target_.h()};
459 }
460 
462 {
463  // As we are using SDL_RenderSetIntegerScale, there may be a slight
464  // offset of the drawable area on the render target if the target size
465  // is not perfectly divisble by the scale.
466  // SDL doesn't provide any way of retrieving this offset,
467  // so we just have to base our calculation on the known behaviour.
468  point osize = output_size();
469  point dsize = draw_size();
470  point scale = osize / dsize;
471  return (osize - (scale * dsize)) / 2;
472 }
473 
475 {
476  point p = output_size();
477  return {0, 0, p.x, p.y};
478 }
479 
480 rect to_output(const rect& r)
481 {
482  // Multiply r by integer scale, adding draw_offset to the position.
483  point dsize = current_render_target_.draw_size();
484  point osize = current_render_target_.get_raw_size();
485  point pos = (r.origin() * (osize / dsize)) + draw_offset();
486  point size = r.size() * (osize / dsize);
487  return {pos, size};
488 }
489 
491 {
492  return input_area_;
493 }
494 
496 {
497  return pixel_scale_;
498 }
499 
501 {
502  return refresh_rate_;
503 }
504 
506 {
507  // TODO: this should be more clever, depending on usage
508  if(auto preferred = prefs::get().refresh_rate(); preferred > 0) {
509  return std::min(preferred, refresh_rate_);
510  } else {
511  return refresh_rate_;
512  }
513 }
514 
516 {
517  if (SDL_SetRenderTarget(get_renderer(), t)) {
518  ERR_DP << "failed to set render target to "
519  << static_cast<void*>(t.get()) << ' '
520  << t.draw_size() << " / " << t.get_raw_size();
521  ERR_DP << "last SDL error: " << SDL_GetError();
522  throw error("failed to set render target");
523  }
524  current_render_target_ = t;
525 
526  if (testing_) {
527  return;
528  }
529 
530  // The scale factor gets reset when the render target changes,
531  // so make sure it gets set back appropriately.
532  if (!t) {
533  DBG_DP << "rendering to window / screen";
534  window->set_logical_size(game_canvas_size_);
535  } else if (t == render_texture_) {
536  DBG_DP << "rendering to primary buffer";
537  window->set_logical_size(game_canvas_size_);
538  } else {
539  DBG_DP << "rendering to custom target "
540  << static_cast<void*>(t.get()) << ' '
541  << t.draw_size() << " / " << t.get_raw_size();
542  window->set_logical_size(t.w(), t.h());
543  }
544 }
545 
547 {
549 }
550 
552 {
553  force_render_target(render_texture_);
554 }
555 
557 {
558 #ifndef __ANDROID__
559  // This should always be up-to-date, but assert for sanity.
560  assert(current_render_target_ == SDL_GetRenderTarget(get_renderer()));
561 #endif
562  return current_render_target_;
563 }
564 
565 // Note: this is not thread-safe.
566 // Drawing functions should not be called while this is active.
567 // SDL renderer usage is not thread-safe anyway, so this is fine.
569 {
570  if(headless_ || testing_) {
571  // No need to present anything in this case
572  return;
573  }
574 
575  if(!window) {
576  WRN_DP << "trying to render with no window";
577  return;
578  }
579 
580  // This should only ever be called when the main render texture is the
581  // current render target. It could be adapted otherwise... but let's not.
582  if(SDL_GetRenderTarget(*window) != render_texture_) {
583  ERR_DP << "trying to render screen, but current render texture is "
584  << static_cast<void*>(SDL_GetRenderTarget(*window))
585  << " | " << static_cast<void*>(current_render_target_.get())
586  << ". It should be " << static_cast<void*>(render_texture_.get());
587  throw error("tried to render screen from wrong render target");
588  }
589 
590  // Clear the render target so we're drawing to the window.
592 
593  // Use fully transparent black to clear the window backbuffer
594  SDL_SetRenderDrawColor(*window, 0u, 0u, 0u, 0u);
595 
596  // Clear the window backbuffer before rendering the render texture.
597  SDL_RenderClear(*window);
598 
599  // Copy the render texture to the window.
600  SDL_RenderCopy(*window, render_texture_, nullptr, nullptr);
601 
602  // Finalize and display the frame.
603  SDL_RenderPresent(*window);
604 
605  // Reset the render target to the render texture.
607 }
608 
609 surface read_pixels(SDL_Rect* r)
610 {
611  if (!window) {
612  WRN_DP << "trying to read pixels with no window";
613  return surface();
614  }
615 
616  // This should be what we want to read from.
617  texture& target = current_render_target_;
618 
619  // Make doubly sure.
620  if (target != SDL_GetRenderTarget(*window)) {
621  SDL_Texture* t = SDL_GetRenderTarget(*window);
622  ERR_DP << "render target " << static_cast<void*>(target.get())
623  << ' ' << target.draw_size() << " / " << target.get_raw_size()
624  << " doesn't match window render target "
625  << static_cast<void*>(t);
626  throw error("unexpected render target while reading pixels");
627  }
628 
629  // Intersect the draw area with the given rect.
630  rect r_clipped = draw_area();
631  if (r) {
632  r_clipped.clip(*r);
633  if (r_clipped != *r) {
634  DBG_DP << "modifying pixel read area from " << *r
635  << " to " << r_clipped;
636  *r = r_clipped;
637  }
638  }
639 
640  // Convert the rect to output coordinates, if necessary.
641  rect o = to_output(r_clipped);
642 
643  // Create surface and read pixels
644  surface s(o.w, o.h);
645  SDL_RenderReadPixels(*window, &o, s->format->format, s->pixels, s->pitch);
646  return s;
647 }
648 
650 {
651  if(!window) {
652  WRN_DP << "trying to read pixels with no window";
653  return surface();
654  }
655  surface s = read_pixels(r);
656  if(r) {
657  return scale_surface(s, r->w, r->h);
658  } else {
659  return scale_surface(s, draw_size().x, draw_size().y);
660  }
661 }
662 
663 void set_window_title(const std::string& title)
664 {
665  assert(window);
666  window->set_title(title);
667 }
668 
670 {
671  assert(window);
672  window->set_icon(icon);
673 }
674 
675 SDL_Renderer* get_renderer()
676 {
677  if(window) {
678  return *window;
679  } else {
680  return nullptr;
681  }
682 }
683 
684 SDL_Window* get_window()
685 {
686  return *window;
687 }
688 
689 std::string current_driver()
690 {
691  const char* const drvname = SDL_GetCurrentVideoDriver();
692  return drvname ? drvname : "<not initialized>";
693 }
694 
695 std::vector<std::string> enumerate_drivers()
696 {
697  std::vector<std::string> res;
698  int num_drivers = SDL_GetNumVideoDrivers();
699 
700  for(int n = 0; n < num_drivers; ++n) {
701  const char* drvname = SDL_GetVideoDriver(n);
702  res.emplace_back(drvname ? drvname : "<invalid driver>");
703  }
704 
705  return res;
706 }
707 
708 /**
709  * Tests whether the given flags are currently set on the SDL window.
710  *
711  * @param flags The flags to test, OR'd together.
712  */
713 static bool window_has_flags(uint32_t flags)
714 {
715  return window && (window->get_flags() & flags) != 0;
716 }
717 
719 {
720  return window_has_flags(SDL_WINDOW_SHOWN);
721 }
722 
724 {
725  return window_has_flags(SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS);
726 }
727 
729 {
730  return window_has_flags(SDL_WINDOW_MOUSE_FOCUS);
731 }
732 
733 std::vector<point> get_available_resolutions(const bool include_current)
734 {
735  std::vector<point> result;
736 
737  if(!window) {
738  return result;
739  }
740 
741  const int display_index = window->get_display_index();
742 
743  const int modes = SDL_GetNumDisplayModes(display_index);
744  if(modes <= 0) {
745  PLAIN_LOG << "No modes supported";
746  return result;
747  }
748 
750 
751  // The maximum size to which this window can be set. For some reason this won't
752  // pop up as a display mode of its own.
753  SDL_Rect bounds;
754  SDL_GetDisplayBounds(display_index, &bounds);
755 
756  SDL_DisplayMode mode;
757 
758  for(int i = 0; i < modes; ++i) {
759  if(SDL_GetDisplayMode(display_index, i, &mode) == 0) {
760  // Exclude any results outside the range of the current DPI.
761  if(mode.w > bounds.w && mode.h > bounds.h) {
762  continue;
763  }
764 
765  if(mode.w >= min_res.x && mode.h >= min_res.y) {
766  result.emplace_back(mode.w, mode.h);
767  }
768  }
769  }
770 
771  if(std::find(result.begin(), result.end(), min_res) == result.end()) {
772  result.push_back(min_res);
773  }
774 
775  if(include_current) {
776  result.push_back(current_resolution());
777  }
778 
779  std::sort(result.begin(), result.end());
780  result.erase(std::unique(result.begin(), result.end()), result.end());
781 
782  return result;
783 }
784 
786 {
787  if (testing_) {
788  return test_resolution_;
789  }
790  return point(window->get_size()); // Convert from plain SDL_Point
791 }
792 
794 {
795  if (testing_) {
796  return true;
797  }
798  return (window->get_flags() & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
799 }
800 
801 void set_fullscreen(bool fullscreen)
802 {
803  if (headless_ || testing_) {
804  return;
805  }
806 
807  // Only do anything if the current value differs from the desired value
808  if (window && is_fullscreen() != fullscreen) {
809  if (fullscreen) {
810  window->full_screen();
811  } else if (prefs::get().maximized()) {
812  window->to_window();
813  window->maximize();
814  } else {
815  window->to_window();
816  window->restore();
817  }
818  update_buffers();
819  }
820 
821  // Update the config value in any case.
822  prefs::get().set_fullscreen(fullscreen);
823 }
824 
826 {
827  set_fullscreen(!prefs::get().fullscreen());
828 }
829 
830 bool set_resolution(const point& resolution)
831 {
832  if(resolution == current_resolution()) {
833  return false;
834  }
835 
836  if(!window) {
837  throw error("tried to set resolution with no window");
838  }
839 
840  if(testing_) {
841  LOG_DP << "resizing test resolution to " << resolution;
842  test_resolution_ = resolution;
843  return update_test_framebuffer();
844  }
845 
846  window->restore();
847  window->set_size(resolution.x, resolution.y);
848  window->center();
849 
850  update_buffers();
851 
852  // Change the saved values in preferences.
853  LOG_DP << "updating resolution to " << resolution;
854  prefs::get().set_resolution(resolution);
855  prefs::get().set_maximized(false);
856 
857  return true;
858 }
859 
860 void update_buffers(bool autoupdate)
861 {
862  if(headless_) {
863  return;
864  }
865 
866  LOG_DP << "updating video buffers";
867  if(update_framebuffer() && autoupdate) {
869  }
870 }
871 
872 std::pair<float, float> get_dpi()
873 {
874  float hdpi = 0.0f, vdpi = 0.0f;
875  if(window && SDL_GetDisplayDPI(window->get_display_index(), nullptr, &hdpi, &vdpi) == 0) {
876 #ifdef TARGET_OS_OSX
877  // SDL 2.0.12 changes SDL_GetDisplayDPI. Function now returns DPI
878  // multiplied by screen's scale factor. This part of code reverts
879  // this multiplication.
880  //
881  // For more info see issue: https://github.com/wesnoth/wesnoth/issues/5019
882  if(sdl::get_version() >= version_info{2, 0, 12}) {
883  float scale_factor = desktop::apple::get_scale_factor(window->get_display_index());
884  hdpi /= scale_factor;
885  vdpi /= scale_factor;
886  }
887 #endif
888  }
889  return { hdpi, vdpi };
890 }
891 
892 std::vector<std::pair<std::string, std::string>> renderer_report()
893 {
894  std::vector<std::pair<std::string, std::string>> res;
895  SDL_Renderer* rnd;
896  SDL_RendererInfo ri;
897 
898  if(window && (rnd = *window) && SDL_GetRendererInfo(rnd, &ri) == 0) {
899  std::string renderer_name = ri.name ? ri.name : "<unknown>";
900 
901  if(ri.flags & SDL_RENDERER_SOFTWARE) {
902  renderer_name += " (sw)";
903  }
904 
905  if(ri.flags & SDL_RENDERER_ACCELERATED) {
906  renderer_name += " (hw)";
907  }
908 
909  std::string renderer_max = std::to_string(ri.max_texture_width) +
910  'x' +
911  std::to_string(ri.max_texture_height);
912 
913  res.emplace_back("Renderer", renderer_name);
914  res.emplace_back("Maximum texture size", renderer_max);
915  res.emplace_back("VSync", ri.flags & SDL_RENDERER_PRESENTVSYNC ? "on" : "off");
916  }
917 
918  return res;
919 }
920 
921 } // namespace video
double t
Definition: astarsearch.cpp:63
static prefs & get()
int pixel_scale()
void set_resolution(const point &res)
point resolution()
The wrapper class for the SDL_Window class.
Definition: window.hpp:48
Wrapper class to encapsulate creation and management of an SDL_Texture.
Definition: texture.hpp:33
point draw_size() const
The size of the texture in draw-space.
Definition: texture.hpp:120
point get_raw_size() const
The raw internal texture size.
Definition: texture.cpp:99
SDL_Texture * get() const
Definition: texture.hpp:194
Represents version numbers.
std::size_t i
Definition: function.cpp:1032
int w
Interfaces for manipulating version numbers of engine, add-ons, etc.
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:296
CGFloat get_scale_factor(int display_index)
void invalidate_all()
Mark the entire screen as requiring redraw.
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:211
void flush_cache()
Purges all image caches.
Definition: picture.cpp:210
const int min_window_height
Definition: preferences.hpp:37
const int max_pixel_scale
Definition: preferences.hpp:49
const int def_window_width
Definition: preferences.hpp:39
const int min_window_width
Definition: preferences.hpp:36
const int max_window_height
Definition: preferences.hpp:43
const int max_window_width
Definition: preferences.hpp:42
const int def_window_height
Definition: preferences.hpp:40
version_info get_version()
Returns the runtime SDL version.
Definition: utils.cpp:40
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:140
bool window_has_mouse_focus()
True iff the window has mouse focus.
Definition: video.cpp:728
bool headless()
The game is running headless.
Definition: video.cpp:145
rect draw_area()
The current drawable area.
Definition: video.cpp:456
rect output_area()
{0, 0, output_size().x, output_size().y}
Definition: video.cpp:474
void set_window_title(const std::string &title)
Sets the title of the main window.
Definition: video.cpp:663
static void init_fake()
Definition: video.cpp:155
void clear_render_target()
Reset the render target to the main window / screen.
Definition: video.cpp:546
rect to_output(const rect &r)
Convert coordinates in draw space to coordinates in render space.
Definition: video.cpp:480
void render_screen()
Definition: video.cpp:568
std::vector< std::pair< std::string, std::string > > renderer_report()
Provides diagnostic information about the current renderer for the build_info API.
Definition: video.cpp:892
static bool window_has_flags(uint32_t flags)
Tests whether the given flags are currently set on the SDL window.
Definition: video.cpp:713
static bool update_framebuffer()
Definition: video.cpp:213
void reset_render_target()
Reset the render target to the primary render buffer.
Definition: video.cpp:551
point output_size()
Returns the size of the final render target.
Definition: video.cpp:424
std::vector< point > get_available_resolutions(const bool include_current)
Returns the list of available screen resolutions.
Definition: video.cpp:733
static bool update_test_framebuffer()
Returns true if the buffer was changed.
Definition: video.cpp:171
void set_window_icon(surface &icon)
Sets the icon of the main window.
Definition: video.cpp:669
bool window_is_visible()
True iff the window is not hidden.
Definition: video.cpp:718
surface read_pixels_low_res(SDL_Rect *r)
The same as read_pixels, but returns a low-resolution surface suitable for use with the old drawing s...
Definition: video.cpp:649
surface read_pixels(SDL_Rect *r)
Copy back a portion of the render target that is already drawn.
Definition: video.cpp:609
void force_render_target(const texture &t)
Set the render target, without any provided way of setting it back.
Definition: video.cpp:515
point game_canvas_size()
The size of the game canvas, in drawing coordinates / game pixels.
Definition: video.cpp:446
SDL_Window * get_window()
Definition: video.cpp:684
bool window_has_focus()
True iff the window has mouse or input focus.
Definition: video.cpp:723
point window_size()
Returns the size of the window in display units / screen coordinates.
Definition: video.cpp:433
bool has_window()
Whether the game has set up a window to render into.
Definition: video.cpp:419
bool testing()
The game is running unit tests.
Definition: video.cpp:150
bool is_fullscreen()
Whether we are currently in fullscreen mode.
Definition: video.cpp:793
std::vector< std::string > enumerate_drivers()
A list of available video drivers.
Definition: video.cpp:695
rect game_canvas()
The game canvas area, in drawing coordinates.
Definition: video.cpp:441
std::pair< float, float > get_dpi()
Retrieves the current game screen DPI for the build_info API.
Definition: video.cpp:872
static point draw_offset()
Definition: video.cpp:461
int get_pixel_scale()
Get the current active pixel scale multiplier.
Definition: video.cpp:495
static void init_window(bool hidden=false)
Definition: video.cpp:358
point current_resolution()
The current window size in desktop coordinates.
Definition: video.cpp:785
int native_refresh_rate()
The native refresh rate of display, not taking any user preferences into account.
Definition: video.cpp:500
void init(fake type)
Initialize the video subsystem.
Definition: video.cpp:88
void set_fullscreen(bool fullscreen)
Set the fullscreen state.
Definition: video.cpp:801
void deinit()
Deinitialize the video subsystem.
Definition: video.cpp:117
bool set_resolution(const point &resolution)
Set the window resolution.
Definition: video.cpp:830
int current_refresh_rate()
The refresh rate of the screen.
Definition: video.cpp:505
std::string current_driver()
The current video driver in use, or else "<not initialized>".
Definition: video.cpp:689
void toggle_fullscreen()
Toggle fullscreen mode.
Definition: video.cpp:825
SDL_Renderer * get_renderer()
Definition: video.cpp:675
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
Definition: video.cpp:860
texture get_render_target()
Get the current render target.
Definition: video.cpp:556
static void init_test()
Definition: video.cpp:163
fake
For describing the type of faked display, if any.
Definition: video.hpp:44
static void init_test_window()
Definition: video.cpp:337
point draw_size()
The size of the current render target in drawing coordinates.
Definition: video.cpp:451
rect input_area()
Returns the input area of the window, in display coordinates.
Definition: video.cpp:490
void scale(size_t factor, const uint32_t *src, uint32_t *trg, int srcWidth, int srcHeight, ColorFormat colFmt, const ScalerCfg &cfg=ScalerCfg(), int yFirst=0, int yLast=std::numeric_limits< int >::max())
Definition: xbrz.cpp:1175
Contains a wrapper class for the SDL_Window class.
Holds a 2D point.
Definition: point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:49
constexpr point origin() const
Definition: rect.hpp:66
void clip(const SDL_Rect &r)
Clip this rectangle by the given rectangle.
Definition: rect.cpp:101
constexpr point size() const
Definition: rect.hpp:67
An error specifically indicating video subsystem problems.
Definition: video.hpp:316
mock_party p
static map_location::direction n
static map_location::direction s
surface scale_surface(const surface &surf, int w, int h)
Scale a surface using alpha-weighted modified bilinear filtering Note: causes artifacts with alpha gr...
Definition: utils.cpp:99
#define LOG_DP
Definition: video.cpp:46
#define WRN_DP
Definition: video.cpp:48
static lg::log_domain log_display("display")
#define ERR_DP
Definition: video.cpp:47
#define DBG_DP
Definition: video.cpp:49
#define h