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