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