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