The Battle for Wesnoth  1.19.25+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.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::min(max_scale_, 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  // Use fully transparent black to clear the window backbuffer
601  SDL_SetRenderDrawColor(*window, 0u, 0u, 0u, 0u);
602 
603  // Clear the window backbuffer before rendering the render texture.
604  SDL_RenderClear(*window);
605 
606  // Copy the render texture to the window.
607  SDL_RenderTexture(*window, render_texture_, nullptr, nullptr);
608 
609  // Finalize and display the frame.
610  SDL_RenderPresent(*window);
611 
612  // Reset the render target to the render texture.
614 }
615 
617 {
618  if (!window) {
619  WRN_DP << "trying to read pixels with no window";
620  return surface();
621  }
622 
623  // This should be what we want to read from.
624  texture& target = current_render_target_;
625 
626  // Make doubly sure.
627  if (target != SDL_GetRenderTarget(*window)) {
628  SDL_Texture* t = SDL_GetRenderTarget(*window);
629  ERR_DP << "render target " << static_cast<void*>(target.get())
630  << ' ' << target.draw_size() << " / " << target.get_raw_size()
631  << " doesn't match window render target "
632  << static_cast<void*>(t);
633  throw error("unexpected render target while reading pixels");
634  }
635 
636  // Intersect the draw area with the given rect.
637  rect r_clipped = draw_area();
638  if (r) {
639  r_clipped.clip(*r);
640  if (r_clipped != *r) {
641  DBG_DP << "modifying pixel read area from " << *r
642  << " to " << r_clipped;
643  *r = r_clipped;
644  }
645  }
646 
647  // Convert the rect to output coordinates, if necessary.
648  rect o = to_output(r_clipped);
649 
650  // Create surface and read pixels
651  surface s = SDL_RenderReadPixels(*window, &o);
652  return s;
653 }
654 
656 {
657  if(!window) {
658  WRN_DP << "trying to read pixels with no window";
659  return surface();
660  }
661  surface s = read_pixels(r);
662  if(r) {
663  return scale_surface(s, r->w, r->h);
664  } else {
665  return scale_surface(s, draw_size().x, draw_size().y);
666  }
667 }
668 
669 void set_window_title(const std::string& title)
670 {
671  assert(window);
672  window->set_title(title);
673 }
674 
676 {
677  assert(window);
678  window->set_icon(icon);
679 }
680 
681 SDL_Renderer* get_renderer()
682 {
683  if(window) {
684  return *window;
685  } else {
686  return nullptr;
687  }
688 }
689 
690 SDL_Window* get_window()
691 {
692  if(window) {
693  return *window;
694  } else {
695  return nullptr;
696  }
697 }
698 
699 std::string current_driver()
700 {
701  const char* const drvname = SDL_GetCurrentVideoDriver();
702  return drvname ? drvname : "<not initialized>";
703 }
704 
705 std::vector<std::string> enumerate_drivers()
706 {
707  std::vector<std::string> res;
708  int num_drivers = SDL_GetNumVideoDrivers();
709 
710  for(int n = 0; n < num_drivers; ++n) {
711  const char* drvname = SDL_GetVideoDriver(n);
712  res.emplace_back(drvname ? drvname : "<invalid driver>");
713  }
714 
715  return res;
716 }
717 
718 /**
719  * Tests whether the given flags are currently set on the SDL window.
720  *
721  * @param flags The flags to test, OR'd together.
722  */
723 static bool window_has_flags(uint32_t flags)
724 {
725  return window && (window->get_flags() & flags) != 0;
726 }
727 
729 {
730  return !window_has_flags(SDL_WINDOW_HIDDEN);
731 }
732 
734 {
735  return window_has_flags(SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS);
736 }
737 
739 {
740  return window_has_flags(SDL_WINDOW_MOUSE_FOCUS);
741 }
742 
743 std::vector<point> get_available_resolutions(const bool include_current)
744 {
745  std::vector<point> result;
746 
747 #ifdef __ANDROID__
748  result.push_back(prefs::get().resolution());
749  return result;
750 #endif
751 
752  if(!window) {
753  return result;
754  }
755 
756  const int display_index = window->get_display_index();
757 
758  int mode_count = 0;
759  SDL_DisplayMode** modes = SDL_GetFullscreenDisplayModes(display_index, &mode_count);
760  if(mode_count <= 0 || !modes) {
761  PLAIN_LOG << "No modes supported";
762  return result;
763  }
764 
766 
767  // The maximum size to which this window can be set. For some reason this won't
768  // pop up as a display mode of its own.
769  rect bounds;
770  SDL_GetDisplayBounds(display_index, &bounds);
771 
772  for(SDL_DisplayMode* mode : utils::span(modes, mode_count)) {
773  assert(mode);
774  point size{mode->w, mode->h};
775 
776  if(min_res <= size && size <= bounds.size()) {
777  result.push_back(size);
778  }
779  }
780 
781  SDL_free(modes);
782 
783  if(!utils::contains(result, min_res)) {
784  result.push_back(min_res);
785  }
786 
787  if(include_current) {
788  result.push_back(current_resolution());
789  }
790 
791  std::sort(result.begin(), result.end());
792  result.erase(std::unique(result.begin(), result.end()), result.end());
793 
794  return result;
795 }
796 
798 {
799  if (testing_) {
800  return test_resolution_;
801  }
802  return window->get_size();
803 }
804 
805 void set_fullscreen(bool fullscreen)
806 {
807  if (headless_ || testing_) {
808  return;
809  }
810 
811  if (fullscreen) {
812  window->full_screen();
813  } else if (prefs::get().maximized()) {
814  window->to_window();
815  window->maximize();
816  } else {
817  window->to_window();
818  window->restore();
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::vector<std::string> get_available_renderers()
873 {
874  std::vector<std::string> renderers;
875 
876  int renderer_count = SDL_GetNumRenderDrivers();
877  for(int i = 0; i < renderer_count; i++) {
878  renderers.emplace_back(SDL_GetRenderDriver(i));
879  }
880 
881  return renderers;
882 }
883 
884 std::vector<std::pair<std::string, std::string>> renderer_report()
885 {
886  std::vector<std::pair<std::string, std::string>> res;
887  SDL_Renderer* rnd = get_renderer();
888 
889  if(rnd) {
890  std::string renderer_name = SDL_GetRendererName(rnd) ? SDL_GetRendererName(rnd) : "<unknown>";
891  SDL_PropertiesID props = SDL_GetRendererProperties(rnd);
892 
893  res.emplace_back("Renderer", renderer_name);
894  res.emplace_back("VSync", SDL_GetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0) != 0 ? "on" : "off");
895  }
896 
897  return res;
898 }
899 
900 } // 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:87
bool window_has_mouse_focus()
True iff the window has mouse focus.
Definition: video.cpp:738
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:669
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:884
static bool window_has_flags(uint32_t flags)
Tests whether the given flags are currently set on the SDL window.
Definition: video.cpp:723
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:743
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:675
int get_max_pixel_scale()
Definition: video.cpp:485
bool window_is_visible()
True iff the window is not hidden.
Definition: video.cpp:728
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:690
bool window_has_focus()
True iff the window has mouse or input focus.
Definition: video.cpp:733
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:705
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:872
point current_resolution()
The current window size in desktop coordinates.
Definition: video.cpp:797
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:805
void deinit()
Deinitialize the video subsystem.
Definition: video.cpp:122
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: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:655
std::string current_driver()
The current video driver in use, or else "<not initialized>".
Definition: video.cpp:699
surface read_pixels(rect *r)
Copy back a portion of the render target that is already drawn.
Definition: video.cpp:616
void toggle_fullscreen()
Toggle fullscreen mode.
Definition: video.cpp:825
SDL_Renderer * get_renderer()
Definition: video.cpp:681
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: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:98
#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