The Battle for Wesnoth  1.19.24+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().maximized()) {
372  window_flags |= SDL_WINDOW_MAXIMIZED;
373  }
374 
375  if(hidden) {
376  LOG_DP << "hiding main window";
377  window_flags |= SDL_WINDOW_HIDDEN;
378  }
379 
380  // Initialize window
381  window.reset(new sdl::window("", w, h, window_flags));
382  if(prefs::get().fullscreen()) {
383  SDL_DisplayMode mode;
384  mode.format = SDL_PIXELFORMAT_UNKNOWN;
385  mode.w = w;
386  mode.h = h;
387  mode.refresh_rate = 0;
388  mode.internal = nullptr;
389 
390  SDL_SetWindowFullscreenMode(*window, &mode);
391  }
392 
393  // It is assumed that this function is only ever called once.
394  // If that is no longer true, then you should clean things up.
395  assert(!render_texture_);
396 
397  PLAIN_LOG << "Setting mode to " << w << "x" << h;
398 
400 
401  const SDL_DisplayMode* currentDisplayMode = SDL_GetCurrentDisplayMode(window->get_display_index());
402  refresh_rate_ = currentDisplayMode->refresh_rate != 0 ? currentDisplayMode->refresh_rate : 60;
403 
404 #ifdef __ANDROID__
405  window->set_size(w, h);
406 #endif
407 
409 }
410 
412 {
413  return bool(window);
414 }
415 
417 {
418  if (testing_) {
419  return test_resolution_;
420  }
421  // As we are rendering via an abstraction, we should never need this.
422  return window->get_output_size();
423 }
424 
426 {
427  if (testing_) {
428  return test_resolution_;
429  }
430  return window->get_size();
431 }
432 
434 {
435  return {0, 0, game_canvas_size_.x, game_canvas_size_.y};
436 }
437 
439 {
440  return game_canvas_size_;
441 }
442 
444 {
445  return current_render_target_.draw_size();
446 }
447 
449 {
450  return {0, 0, current_render_target_.w(), current_render_target_.h()};
451 }
452 
454 {
455  // As we are using SDL_RenderSetIntegerScale, there may be a slight
456  // offset of the drawable area on the render target if the target size
457  // is not perfectly divisble by the scale.
458  // SDL doesn't provide any way of retrieving this offset,
459  // so we just have to base our calculation on the known behaviour.
460  point osize = output_size();
461  point dsize = draw_size();
462  point scale = osize / dsize;
463  return (osize - (scale * dsize)) / 2;
464 }
465 
467 {
468  point p = output_size();
469  return {0, 0, p.x, p.y};
470 }
471 
472 rect to_output(const rect& r)
473 {
474  // Multiply r by integer scale, adding draw_offset to the position.
475  point dsize = current_render_target_.draw_size();
476  point osize = current_render_target_.get_raw_size();
477  point pos = (r.origin() * (osize / dsize)) + draw_offset();
478  point size = r.size() * (osize / dsize);
479  return {pos, size};
480 }
481 
483 {
484  return input_area_;
485 }
486 
488 {
489  return pixel_scale_;
490 }
491 
493 {
494  return max_scale_;
495 }
496 
498 {
499  return refresh_rate_;
500 }
501 
503 {
504  // TODO: this should be more clever, depending on usage
505  if(auto preferred = prefs::get().refresh_rate(); preferred > 0) {
506  return std::min(preferred, refresh_rate_);
507  } else {
508  return refresh_rate_;
509  }
510 }
511 
513 {
514  SDL_DisplayID id = SDL_GetDisplayForWindow(*window);
515  if(id != 0) {
516  return SDL_GetDisplayContentScale(id);
517  }
518  return 0.0f;
519 }
521 {
522  return SDL_GetWindowPixelDensity(*window);
523 }
525 {
526  return SDL_GetWindowDisplayScale(*window);
527 }
528 
530 {
531  if (!SDL_SetRenderTarget(get_renderer(), t)) {
532  ERR_DP << "failed to set render target to "
533  << static_cast<void*>(t.get()) << ' '
534  << t.draw_size() << " / " << t.get_raw_size();
535  ERR_DP << "last SDL error: " << SDL_GetError();
536  throw error("failed to set render target");
537  }
538  current_render_target_ = t;
539 
540  if (testing_) {
541  return;
542  }
543 
544  // The scale factor gets reset when the render target changes,
545  // so make sure it gets set back appropriately.
546  if (!t) {
547  DBG_DP << "rendering to window / screen";
548  window->set_logical_size(game_canvas_size_);
549  } else if (t == render_texture_) {
550  DBG_DP << "rendering to primary buffer";
551  window->set_logical_size(game_canvas_size_);
552  } else {
553  DBG_DP << "rendering to custom target "
554  << static_cast<void*>(t.get()) << ' '
555  << t.draw_size() << " / " << t.get_raw_size();
556  window->set_logical_size(t.w(), t.h());
557  }
558 }
559 
561 {
563 }
564 
566 {
567  force_render_target(render_texture_);
568 }
569 
571 {
572 #ifndef __ANDROID__
573  // This should always be up-to-date, but assert for sanity.
574  assert(current_render_target_ == SDL_GetRenderTarget(get_renderer()));
575 #endif
576  return current_render_target_;
577 }
578 
579 // Note: this is not thread-safe.
580 // Drawing functions should not be called while this is active.
581 // SDL renderer usage is not thread-safe anyway, so this is fine.
583 {
584  if(headless_ || testing_) {
585  // No need to present anything in this case
586  return;
587  }
588 
589  if(!window) {
590  WRN_DP << "trying to render with no window";
591  return;
592  }
593 
594  // This should only ever be called when the main render texture is the
595  // current render target. It could be adapted otherwise... but let's not.
596  if(SDL_GetRenderTarget(*window) != render_texture_) {
597  ERR_DP << "trying to render screen, but current render texture is "
598  << static_cast<void*>(SDL_GetRenderTarget(*window))
599  << " | " << static_cast<void*>(current_render_target_.get())
600  << ". It should be " << static_cast<void*>(render_texture_.get());
601  throw error("tried to render screen from wrong render target");
602  }
603 
604  // Clear the render target so we're drawing to the window.
606 
607  // Use fully transparent black to clear the window backbuffer
608  SDL_SetRenderDrawColor(*window, 0u, 0u, 0u, 0u);
609 
610  // Clear the window backbuffer before rendering the render texture.
611  SDL_RenderClear(*window);
612 
613  // Copy the render texture to the window.
614  SDL_RenderTexture(*window, render_texture_, nullptr, nullptr);
615 
616  // Finalize and display the frame.
617  SDL_RenderPresent(*window);
618 
619  // Reset the render target to the render texture.
621 }
622 
624 {
625  if (!window) {
626  WRN_DP << "trying to read pixels with no window";
627  return surface();
628  }
629 
630  // This should be what we want to read from.
631  texture& target = current_render_target_;
632 
633  // Make doubly sure.
634  if (target != SDL_GetRenderTarget(*window)) {
635  SDL_Texture* t = SDL_GetRenderTarget(*window);
636  ERR_DP << "render target " << static_cast<void*>(target.get())
637  << ' ' << target.draw_size() << " / " << target.get_raw_size()
638  << " doesn't match window render target "
639  << static_cast<void*>(t);
640  throw error("unexpected render target while reading pixels");
641  }
642 
643  // Intersect the draw area with the given rect.
644  rect r_clipped = draw_area();
645  if (r) {
646  r_clipped.clip(*r);
647  if (r_clipped != *r) {
648  DBG_DP << "modifying pixel read area from " << *r
649  << " to " << r_clipped;
650  *r = r_clipped;
651  }
652  }
653 
654  // Convert the rect to output coordinates, if necessary.
655  rect o = to_output(r_clipped);
656 
657  // Create surface and read pixels
658  surface s = SDL_RenderReadPixels(*window, &o);
659  return s;
660 }
661 
663 {
664  if(!window) {
665  WRN_DP << "trying to read pixels with no window";
666  return surface();
667  }
668  surface s = read_pixels(r);
669  if(r) {
670  return scale_surface(s, r->w, r->h);
671  } else {
672  return scale_surface(s, draw_size().x, draw_size().y);
673  }
674 }
675 
676 void set_window_title(const std::string& title)
677 {
678  assert(window);
679  window->set_title(title);
680 }
681 
683 {
684  assert(window);
685  window->set_icon(icon);
686 }
687 
688 SDL_Renderer* get_renderer()
689 {
690  if(window) {
691  return *window;
692  } else {
693  return nullptr;
694  }
695 }
696 
697 SDL_Window* get_window()
698 {
699  if(window) {
700  return *window;
701  } else {
702  return nullptr;
703  }
704 }
705 
706 std::string current_driver()
707 {
708  const char* const drvname = SDL_GetCurrentVideoDriver();
709  return drvname ? drvname : "<not initialized>";
710 }
711 
712 std::vector<std::string> enumerate_drivers()
713 {
714  std::vector<std::string> res;
715  int num_drivers = SDL_GetNumVideoDrivers();
716 
717  for(int n = 0; n < num_drivers; ++n) {
718  const char* drvname = SDL_GetVideoDriver(n);
719  res.emplace_back(drvname ? drvname : "<invalid driver>");
720  }
721 
722  return res;
723 }
724 
725 /**
726  * Tests whether the given flags are currently set on the SDL window.
727  *
728  * @param flags The flags to test, OR'd together.
729  */
730 static bool window_has_flags(uint32_t flags)
731 {
732  return window && (window->get_flags() & flags) != 0;
733 }
734 
736 {
737  return !window_has_flags(SDL_WINDOW_HIDDEN);
738 }
739 
741 {
742  return window_has_flags(SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS);
743 }
744 
746 {
747  return window_has_flags(SDL_WINDOW_MOUSE_FOCUS);
748 }
749 
750 std::vector<point> get_available_resolutions(const bool include_current)
751 {
752  std::vector<point> result;
753 
754 #ifdef __ANDROID__
755  result.push_back(prefs::get().resolution());
756  return result;
757 #endif
758 
759  if(!window) {
760  return result;
761  }
762 
763  const int display_index = window->get_display_index();
764 
765  int modes;
766  SDL_GetFullscreenDisplayModes(display_index, &modes);
767  if(modes <= 0) {
768  PLAIN_LOG << "No modes supported";
769  return result;
770  }
771 
773 
774  // The maximum size to which this window can be set. For some reason this won't
775  // pop up as a display mode of its own.
776  SDL_Rect bounds;
777  SDL_GetDisplayBounds(display_index, &bounds);
778 
779  if(!utils::contains(result, min_res)) {
780  result.push_back(min_res);
781  }
782 
783  if(include_current) {
784  result.push_back(current_resolution());
785  }
786 
787  std::sort(result.begin(), result.end());
788  result.erase(std::unique(result.begin(), result.end()), result.end());
789 
790  return result;
791 }
792 
794 {
795  if (testing_) {
796  return test_resolution_;
797  }
798  return window->get_size();
799 }
800 
801 void set_fullscreen(bool fullscreen)
802 {
803  if (headless_ || testing_) {
804  return;
805  }
806 
807  if (fullscreen) {
808  window->full_screen();
809  } else if (prefs::get().maximized()) {
810  window->to_window();
811  window->maximize();
812  } else {
813  window->to_window();
814  window->restore();
815  }
816 
817  // Update the config value in any case.
818  prefs::get().set_fullscreen(fullscreen);
819 }
820 
822 {
823  set_fullscreen(!prefs::get().fullscreen());
824 }
825 
826 bool set_resolution(const point& resolution)
827 {
828  if(resolution == current_resolution()) {
829  return false;
830  }
831 
832  if(!window) {
833  throw error("tried to set resolution with no window");
834  }
835 
836  if(testing_) {
837  LOG_DP << "resizing test resolution to " << resolution;
838  test_resolution_ = resolution;
839  return update_test_framebuffer();
840  }
841 
842  window->restore();
843  window->set_size(resolution.x, resolution.y);
844  window->center();
845 
846  update_buffers();
847 
848  // Change the saved values in preferences.
849  LOG_DP << "updating resolution to " << resolution;
850  prefs::get().set_resolution(resolution);
851  prefs::get().set_maximized(false);
852 
853  return true;
854 }
855 
856 void update_buffers(bool autoupdate)
857 {
858  if(headless_) {
859  return;
860  }
861 
862  LOG_DP << "updating video buffers";
863  if(update_framebuffer() && autoupdate) {
865  }
866 }
867 
868 std::vector<std::pair<std::string, std::string>> renderer_report()
869 {
870  std::vector<std::pair<std::string, std::string>> res;
871  SDL_Renderer* rnd = get_renderer();
872 
873  if(rnd) {
874  std::string renderer_name = SDL_GetRendererName(rnd) ? SDL_GetRendererName(rnd) : "<unknown>";
875  SDL_PropertiesID props = SDL_GetRendererProperties(rnd);
876 
877  res.emplace_back("Renderer", renderer_name);
878  res.emplace_back("VSync", SDL_GetNumberProperty(props, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 0) != 0 ? "on" : "off");
879  }
880 
881  return res;
882 }
883 
884 } // 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
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:745
float pixel_density()
Definition: video.cpp:520
bool headless()
The game is running headless.
Definition: video.cpp:146
rect draw_area()
The current drawable area.
Definition: video.cpp:448
rect output_area()
{0, 0, output_size().x, output_size().y}
Definition: video.cpp:466
void set_window_title(const std::string &title)
Sets the title of the main window.
Definition: video.cpp:676
static void init_fake()
Definition: video.cpp:156
void clear_render_target()
Reset the render target to the main window / screen.
Definition: video.cpp:560
rect to_output(const rect &r)
Convert coordinates in draw space to coordinates in render space.
Definition: video.cpp:472
void render_screen()
Definition: video.cpp:582
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:868
static bool window_has_flags(uint32_t flags)
Tests whether the given flags are currently set on the SDL window.
Definition: video.cpp:730
static bool update_framebuffer()
Definition: video.cpp:215
void reset_render_target()
Reset the render target to the primary render buffer.
Definition: video.cpp:565
float content_scaling()
Definition: video.cpp:512
point output_size()
Returns the size of the final render target.
Definition: video.cpp:416
std::vector< point > get_available_resolutions(const bool include_current)
Returns the list of available screen resolutions.
Definition: video.cpp:750
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:682
int get_max_pixel_scale()
Definition: video.cpp:492
bool window_is_visible()
True iff the window is not hidden.
Definition: video.cpp:735
void force_render_target(const texture &t)
Set the render target, without any provided way of setting it back.
Definition: video.cpp:529
point game_canvas_size()
The size of the game canvas, in drawing coordinates / game pixels.
Definition: video.cpp:438
SDL_Window * get_window()
Definition: video.cpp:697
bool window_has_focus()
True iff the window has mouse or input focus.
Definition: video.cpp:740
point window_size()
Returns the size of the window in display units / screen coordinates.
Definition: video.cpp:425
bool has_window()
Whether the game has set up a window to render into.
Definition: video.cpp:411
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:712
rect game_canvas()
The game canvas area, in drawing coordinates.
Definition: video.cpp:433
static point draw_offset()
Definition: video.cpp:453
int get_pixel_scale()
Get the current active pixel scale multiplier.
Definition: video.cpp:487
static void init_window(bool hidden=false)
Definition: video.cpp:352
point current_resolution()
The current window size in desktop coordinates.
Definition: video.cpp:793
int native_refresh_rate()
The native refresh rate of display, not taking any user preferences into account.
Definition: video.cpp:497
void init(fake type)
Initialize the video subsystem.
Definition: video.cpp:93
void set_fullscreen(bool fullscreen)
Set the fullscreen state.
Definition: video.cpp:801
void deinit()
Deinitialize the video subsystem.
Definition: video.cpp:122
bool set_resolution(const point &resolution)
Set the window resolution.
Definition: video.cpp:826
int current_refresh_rate()
The refresh rate of the screen.
Definition: video.cpp:502
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:662
std::string current_driver()
The current video driver in use, or else "<not initialized>".
Definition: video.cpp:706
surface read_pixels(rect *r)
Copy back a portion of the render target that is already drawn.
Definition: video.cpp:623
void toggle_fullscreen()
Toggle fullscreen mode.
Definition: video.cpp:821
SDL_Renderer * get_renderer()
Definition: video.cpp:688
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
Definition: video.cpp:856
texture get_render_target()
Get the current render target.
Definition: video.cpp:570
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:443
rect input_area()
Returns the input area of the window, in display coordinates.
Definition: video.cpp:482
float display_scaling()
Definition: video.cpp:524
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
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: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