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