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