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