The Battle for Wesnoth  1.19.5+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  // TODO: this should be more clever, depending on usage
489  if(auto preferred = prefs::get().refresh_rate(); preferred > 0) {
490  return std::min(preferred, refresh_rate_);
491  } else {
492  return refresh_rate_;
493  }
494 }
495 
497 {
498  if (SDL_SetRenderTarget(get_renderer(), t)) {
499  ERR_DP << "failed to set render target to "
500  << static_cast<void*>(t.get()) << ' '
501  << t.draw_size() << " / " << t.get_raw_size();
502  ERR_DP << "last SDL error: " << SDL_GetError();
503  throw error("failed to set render target");
504  }
505  current_render_target_ = t;
506 
507  if (testing_) {
508  return;
509  }
510 
511  // The scale factor gets reset when the render target changes,
512  // so make sure it gets set back appropriately.
513  if (!t) {
514  DBG_DP << "rendering to window / screen";
515  window->set_logical_size(game_canvas_size_);
516  } else if (t == render_texture_) {
517  DBG_DP << "rendering to primary buffer";
518  window->set_logical_size(game_canvas_size_);
519  } else {
520  DBG_DP << "rendering to custom target "
521  << static_cast<void*>(t.get()) << ' '
522  << t.draw_size() << " / " << t.get_raw_size();
523  window->set_logical_size(t.w(), t.h());
524  }
525 }
526 
528 {
530 }
531 
533 {
534  force_render_target(render_texture_);
535 }
536 
538 {
539  // This should always be up-to-date, but assert for sanity.
540  assert(current_render_target_ == SDL_GetRenderTarget(get_renderer()));
541  return current_render_target_;
542 }
543 
544 // Note: this is not thread-safe.
545 // Drawing functions should not be called while this is active.
546 // SDL renderer usage is not thread-safe anyway, so this is fine.
548 {
549  if(headless_ || testing_) {
550  // No need to present anything in this case
551  return;
552  }
553 
554  if(!window) {
555  WRN_DP << "trying to render with no window";
556  return;
557  }
558 
559  // This should only ever be called when the main render texture is the
560  // current render target. It could be adapted otherwise... but let's not.
561  if(SDL_GetRenderTarget(*window) != render_texture_) {
562  ERR_DP << "trying to render screen, but current render texture is "
563  << static_cast<void*>(SDL_GetRenderTarget(*window))
564  << " | " << static_cast<void*>(current_render_target_.get())
565  << ". It should be " << static_cast<void*>(render_texture_.get());
566  throw error("tried to render screen from wrong render target");
567  }
568 
569  // Clear the render target so we're drawing to the window.
571 
572  // Copy the render texture to the window.
573  SDL_RenderCopy(*window, render_texture_, nullptr, nullptr);
574 
575  // Finalize and display the frame.
576  SDL_RenderPresent(*window);
577 
578  // Reset the render target to the render texture.
580 }
581 
582 surface read_pixels(SDL_Rect* r)
583 {
584  if (!window) {
585  WRN_DP << "trying to read pixels with no window";
586  return surface();
587  }
588 
589  // This should be what we want to read from.
590  texture& target = current_render_target_;
591 
592  // Make doubly sure.
593  if (target != SDL_GetRenderTarget(*window)) {
594  SDL_Texture* t = SDL_GetRenderTarget(*window);
595  ERR_DP << "render target " << static_cast<void*>(target.get())
596  << ' ' << target.draw_size() << " / " << target.get_raw_size()
597  << " doesn't match window render target "
598  << static_cast<void*>(t);
599  throw error("unexpected render target while reading pixels");
600  }
601 
602  // Intersect the draw area with the given rect.
603  rect r_clipped = draw_area();
604  if (r) {
605  r_clipped.clip(*r);
606  if (r_clipped != *r) {
607  DBG_DP << "modifying pixel read area from " << *r
608  << " to " << r_clipped;
609  *r = r_clipped;
610  }
611  }
612 
613  // Convert the rect to output coordinates, if necessary.
614  rect o = to_output(r_clipped);
615 
616  // Create surface and read pixels
617  surface s(o.w, o.h);
618  SDL_RenderReadPixels(*window, &o, s->format->format, s->pixels, s->pitch);
619  return s;
620 }
621 
623 {
624  if(!window) {
625  WRN_DP << "trying to read pixels with no window";
626  return surface();
627  }
628  surface s = read_pixels(r);
629  if(r) {
630  return scale_surface(s, r->w, r->h);
631  } else {
632  return scale_surface(s, draw_size().x, draw_size().y);
633  }
634 }
635 
636 void set_window_title(const std::string& title)
637 {
638  assert(window);
639  window->set_title(title);
640 }
641 
643 {
644  assert(window);
645  window->set_icon(icon);
646 }
647 
648 SDL_Renderer* get_renderer()
649 {
650  if(window) {
651  return *window;
652  } else {
653  return nullptr;
654  }
655 }
656 
657 SDL_Window* get_window()
658 {
659  return *window;
660 }
661 
662 std::string current_driver()
663 {
664  const char* const drvname = SDL_GetCurrentVideoDriver();
665  return drvname ? drvname : "<not initialized>";
666 }
667 
668 std::vector<std::string> enumerate_drivers()
669 {
670  std::vector<std::string> res;
671  int num_drivers = SDL_GetNumVideoDrivers();
672 
673  for(int n = 0; n < num_drivers; ++n) {
674  const char* drvname = SDL_GetVideoDriver(n);
675  res.emplace_back(drvname ? drvname : "<invalid driver>");
676  }
677 
678  return res;
679 }
680 
681 /**
682  * Tests whether the given flags are currently set on the SDL window.
683  *
684  * @param flags The flags to test, OR'd together.
685  */
686 static bool window_has_flags(uint32_t flags)
687 {
688  return window && (window->get_flags() & flags) != 0;
689 }
690 
692 {
693  return window_has_flags(SDL_WINDOW_SHOWN);
694 }
695 
697 {
698  return window_has_flags(SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_INPUT_FOCUS);
699 }
700 
702 {
703  return window_has_flags(SDL_WINDOW_MOUSE_FOCUS);
704 }
705 
706 std::vector<point> get_available_resolutions(const bool include_current)
707 {
708  std::vector<point> result;
709 
710  if(!window) {
711  return result;
712  }
713 
714  const int display_index = window->get_display_index();
715 
716  const int modes = SDL_GetNumDisplayModes(display_index);
717  if(modes <= 0) {
718  PLAIN_LOG << "No modes supported";
719  return result;
720  }
721 
723 
724  // The maximum size to which this window can be set. For some reason this won't
725  // pop up as a display mode of its own.
726  SDL_Rect bounds;
727  SDL_GetDisplayBounds(display_index, &bounds);
728 
729  SDL_DisplayMode mode;
730 
731  for(int i = 0; i < modes; ++i) {
732  if(SDL_GetDisplayMode(display_index, i, &mode) == 0) {
733  // Exclude any results outside the range of the current DPI.
734  if(mode.w > bounds.w && mode.h > bounds.h) {
735  continue;
736  }
737 
738  if(mode.w >= min_res.x && mode.h >= min_res.y) {
739  result.emplace_back(mode.w, mode.h);
740  }
741  }
742  }
743 
744  if(std::find(result.begin(), result.end(), min_res) == result.end()) {
745  result.push_back(min_res);
746  }
747 
748  if(include_current) {
749  result.push_back(current_resolution());
750  }
751 
752  std::sort(result.begin(), result.end());
753  result.erase(std::unique(result.begin(), result.end()), result.end());
754 
755  return result;
756 }
757 
759 {
760  if (testing_) {
761  return test_resolution_;
762  }
763  return point(window->get_size()); // Convert from plain SDL_Point
764 }
765 
767 {
768  if (testing_) {
769  return true;
770  }
771  return (window->get_flags() & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
772 }
773 
774 void set_fullscreen(bool fullscreen)
775 {
776  if (headless_ || testing_) {
777  return;
778  }
779 
780  // Only do anything if the current value differs from the desired value
781  if (window && is_fullscreen() != fullscreen) {
782  if (fullscreen) {
783  window->full_screen();
784  } else if (prefs::get().maximized()) {
785  window->to_window();
786  window->maximize();
787  } else {
788  window->to_window();
789  window->restore();
790  }
791  update_buffers();
792  }
793 
794  // Update the config value in any case.
795  prefs::get().set_fullscreen(fullscreen);
796 }
797 
799 {
800  set_fullscreen(!prefs::get().fullscreen());
801 }
802 
803 bool set_resolution(const point& resolution)
804 {
805  if(resolution == current_resolution()) {
806  return false;
807  }
808 
809  if(!window) {
810  throw error("tried to set resolution with no window");
811  }
812 
813  if(testing_) {
814  LOG_DP << "resizing test resolution to " << resolution;
815  test_resolution_ = resolution;
816  return update_test_framebuffer();
817  }
818 
819  window->restore();
820  window->set_size(resolution.x, resolution.y);
821  window->center();
822 
823  update_buffers();
824 
825  // Change the saved values in preferences.
826  LOG_DP << "updating resolution to " << resolution;
827  prefs::get().set_resolution(resolution);
828  prefs::get().set_maximized(false);
829 
830  return true;
831 }
832 
833 void update_buffers(bool autoupdate)
834 {
835  if(headless_) {
836  return;
837  }
838 
839  LOG_DP << "updating video buffers";
840  if(update_framebuffer() && autoupdate) {
842  }
843 }
844 
845 std::pair<float, float> get_dpi()
846 {
847  float hdpi = 0.0f, vdpi = 0.0f;
848  if(window && SDL_GetDisplayDPI(window->get_display_index(), nullptr, &hdpi, &vdpi) == 0) {
849 #ifdef TARGET_OS_OSX
850  // SDL 2.0.12 changes SDL_GetDisplayDPI. Function now returns DPI
851  // multiplied by screen's scale factor. This part of code reverts
852  // this multiplication.
853  //
854  // For more info see issue: https://github.com/wesnoth/wesnoth/issues/5019
855  if(sdl::get_version() >= version_info{2, 0, 12}) {
856  float scale_factor = desktop::apple::get_scale_factor(window->get_display_index());
857  hdpi /= scale_factor;
858  vdpi /= scale_factor;
859  }
860 #endif
861  }
862  return { hdpi, vdpi };
863 }
864 
865 std::vector<std::pair<std::string, std::string>> renderer_report()
866 {
867  std::vector<std::pair<std::string, std::string>> res;
868  SDL_Renderer* rnd;
869  SDL_RendererInfo ri;
870 
871  if(window && (rnd = *window) && SDL_GetRendererInfo(rnd, &ri) == 0) {
872  std::string renderer_name = ri.name ? ri.name : "<unknown>";
873 
874  if(ri.flags & SDL_RENDERER_SOFTWARE) {
875  renderer_name += " (sw)";
876  }
877 
878  if(ri.flags & SDL_RENDERER_ACCELERATED) {
879  renderer_name += " (hw)";
880  }
881 
882  std::string renderer_max = std::to_string(ri.max_texture_width) +
883  'x' +
884  std::to_string(ri.max_texture_height);
885 
886  res.emplace_back("Renderer", renderer_name);
887  res.emplace_back("Maximum texture size", renderer_max);
888  res.emplace_back("VSync", ri.flags & SDL_RENDERER_PRESENTVSYNC ? "on" : "off");
889  }
890 
891  return res;
892 }
893 
894 } // 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:1028
int w
Interfaces for manipulating version numbers of engine, add-ons, etc.
Standard logging facilities (interface).
#define PLAIN_LOG
Definition: log.hpp:299
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(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
bool window_has_mouse_focus()
True iff the window has mouse focus.
Definition: video.cpp:701
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:636
static void init_fake()
Definition: video.cpp:149
void clear_render_target()
Reset the render target to the main window / screen.
Definition: video.cpp:527
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:547
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:865
static bool window_has_flags(uint32_t flags)
Tests whether the given flags are currently set on the SDL window.
Definition: video.cpp:686
static bool update_framebuffer()
Definition: video.cpp:207
void reset_render_target()
Reset the render target to the primary render buffer.
Definition: video.cpp:532
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:706
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:642
bool window_is_visible()
True iff the window is not hidden.
Definition: video.cpp:691
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:622
surface read_pixels(SDL_Rect *r)
Copy back a portion of the render target that is already drawn.
Definition: video.cpp:582
void force_render_target(const texture &t)
Set the render target, without any provided way of setting it back.
Definition: video.cpp:496
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:657
bool window_has_focus()
True iff the window has mouse or input focus.
Definition: video.cpp:696
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:766
std::vector< std::string > enumerate_drivers()
A list of available video drivers.
Definition: video.cpp:668
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:845
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:758
void init(fake type)
Initialize the video subsystem.
Definition: video.cpp:81
void set_fullscreen(bool fullscreen)
Set the fullscreen state.
Definition: video.cpp:774
void deinit()
Deinitialize the video subsystem.
Definition: video.cpp:110
bool set_resolution(const point &resolution)
Set the window resolution.
Definition: video.cpp:803
int current_refresh_rate()
The refresh rate of the screen.
Definition: video.cpp:486
std::string current_driver()
The current video driver in use, or else "<not initialized>".
Definition: video.cpp:662
void toggle_fullscreen()
Toggle fullscreen mode.
Definition: video.cpp:798
SDL_Renderer * get_renderer()
Definition: video.cpp:648
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
Definition: video.cpp:833
texture get_render_target()
Get the current render target.
Definition: video.cpp:537
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:313
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