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