38 #include <SDL2/SDL_render.h>
44 #define LOG_DP LOG_STREAM(info, log_display)
45 #define ERR_DP LOG_STREAM(err, log_display)
46 #define WRN_DP LOG_STREAM(warn, log_display)
47 #define DBG_DP LOG_STREAM(debug, log_display)
52 std::unique_ptr<sdl::window> window;
58 texture current_render_target_ = {};
60 bool headless_ =
false;
61 bool testing_ =
false;
62 point test_resolution_ = {1024, 768};
63 int refresh_rate_ = 0;
64 point game_canvas_size_ = {0, 0};
66 rect input_area_ = {};
88 LOG_DP <<
"initializing video";
89 if(SDL_WasInit(SDL_INIT_VIDEO)) {
90 throw error(
"video subsystem already initialized");
92 if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
93 ERR_DP <<
"Could not initialize SDL_video: " << SDL_GetError();
94 throw error(
"Video initialization failed");
108 throw error(
"unrecognized fake type passed to video::init");
114 LOG_DP <<
"deinitializing video";
118 assert(SDL_WasInit(SDL_INIT_TIMER));
124 render_texture_.reset();
125 current_render_target_.reset();
132 if(SDL_WasInit(SDL_INIT_VIDEO)) {
133 LOG_DP <<
"quitting SDL video subsystem";
134 SDL_QuitSubSystem(SDL_INIT_VIDEO);
136 if(SDL_WasInit(SDL_INIT_VIDEO)) {
138 throw error(
"video subsystem still initialized after deinit");
156 game_canvas_size_ = {800,600};
170 throw(
"trying to update test framebuffer with no window");
173 bool changed =
false;
177 if (render_texture_) {
179 SDL_QueryTexture(render_texture_,
nullptr,
nullptr, &
w, &
h);
180 if (
w != test_resolution_.x ||
h != test_resolution_.y) {
182 LOG_DP <<
"destroying old render texture";
183 render_texture_.reset();
186 if (!render_texture_) {
187 LOG_DP <<
"creating offscreen render texture";
188 render_texture_.assign(SDL_CreateTexture(
191 SDL_TEXTUREACCESS_TARGET,
192 test_resolution_.x, test_resolution_.y
194 LOG_DP <<
"updated render target to " << test_resolution_.x
195 <<
"x" << test_resolution_.y;
200 game_canvas_size_ = test_resolution_;
201 input_area_ = {{}, test_resolution_};
212 throw error(
"trying to update framebuffer with no window");
219 bool changed =
false;
222 SDL_SetRenderTarget(*
window,
nullptr);
227 SDL_RenderSetIntegerScale(*
window, SDL_TRUE);
231 int max_scale = std::min(
240 int def_scale = std::min(
243 scale = std::min(max_scale, def_scale);
245 int min_scale = std::min(
253 if (pixel_scale_ !=
scale) {
254 pixel_scale_ =
scale;
261 if (lsize.x != osize.x /
scale || lsize.y != osize.y /
scale) {
263 LOG_DP <<
"reducing pixel scale from desired "
268 LOG_DP <<
"overriding logical size";
269 LOG_DP <<
" old lsize: " << lsize;
270 LOG_DP <<
" old wsize: " << wsize;
271 LOG_DP <<
" old osize: " << osize;
273 lsize =
window->get_logical_size();
274 wsize =
window->get_size();
275 osize =
window->get_output_size();
276 LOG_DP <<
" new lsize: " << lsize;
277 LOG_DP <<
" new wsize: " << wsize;
278 LOG_DP <<
" new osize: " << osize;
280 SDL_RenderGetScale(*
window, &sx, &sy);
281 LOG_DP <<
" render scale: " << sx <<
", " << sy;
284 game_canvas_size_ = lsize;
287 if (render_texture_) {
289 SDL_QueryTexture(render_texture_,
nullptr,
nullptr, &
w, &
h);
290 if (
w != osize.x ||
h != osize.y) {
292 LOG_DP <<
"destroying old render texture";
293 render_texture_.reset();
296 if (!render_texture_) {
297 LOG_DP <<
"creating offscreen render texture";
298 render_texture_.assign(SDL_CreateTexture(
301 SDL_TEXTUREACCESS_TARGET,
305 render_texture_.set_draw_size(lsize);
313 input_area_ = {{}, wsize};
316 if (active_area.
size() != osize) {
317 LOG_DP <<
"render target offset: LT " << active_area.
pos() <<
" RB "
318 << osize - active_area.
size() - active_area.
pos();
321 (active_area.
pos() * wsize) / osize,
322 (active_area.
size() * wsize) / osize
324 LOG_DP <<
"input area: " << input_area_;
332 LOG_DP <<
"creating test window " << test_resolution_.x
333 <<
"x" << test_resolution_.y;
335 uint32_t window_flags = 0;
336 window_flags |= SDL_WINDOW_HIDDEN;
339 uint32_t renderer_flags = 0;
340 renderer_flags |= SDL_RENDERER_TARGETTEXTURE;
344 "", 0, 0, test_resolution_.x, test_resolution_.y,
345 window_flags, renderer_flags
362 uint32_t window_flags = 0;
365 window_flags |= SDL_WINDOW_RESIZABLE;
366 window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
369 window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
371 window_flags |= SDL_WINDOW_MAXIMIZED;
374 uint32_t renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
378 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
386 assert(!render_texture_);
392 SDL_DisplayMode currentDisplayMode;
393 SDL_GetCurrentDisplayMode(
window->get_display_index(), ¤tDisplayMode);
394 refresh_rate_ = currentDisplayMode.refresh_rate != 0 ? currentDisplayMode.refresh_rate : 60;
407 return test_resolution_;
410 return window->get_output_size();
416 return test_resolution_;
418 return window->get_size();
423 return {0, 0, game_canvas_size_.x, game_canvas_size_.y};
428 return game_canvas_size_;
433 return current_render_target_.draw_size();
438 return {0, 0, current_render_target_.w(), current_render_target_.h()};
451 return (osize - (
scale * dsize)) / 2;
457 return {0, 0,
p.x,
p.y};
463 point dsize = current_render_target_.draw_size();
464 point osize = current_render_target_.get_raw_size();
483 return refresh_rate_;
489 ERR_DP <<
"failed to set render target to "
490 <<
static_cast<void*
>(
t.get()) <<
' '
491 <<
t.draw_size() <<
" / " <<
t.get_raw_size();
492 ERR_DP <<
"last SDL error: " << SDL_GetError();
493 throw error(
"failed to set render target");
495 current_render_target_ =
t;
504 DBG_DP <<
"rendering to window / screen";
505 window->set_logical_size(game_canvas_size_);
506 }
else if (
t == render_texture_) {
507 DBG_DP <<
"rendering to primary buffer";
508 window->set_logical_size(game_canvas_size_);
510 DBG_DP <<
"rendering to custom target "
511 <<
static_cast<void*
>(
t.get()) <<
' '
512 <<
t.draw_size() <<
" / " <<
t.get_raw_size();
513 window->set_logical_size(
t.w(),
t.h());
525 assert(current_render_target_ == SDL_GetRenderTarget(
get_renderer()));
526 return current_render_target_;
534 if(headless_ || testing_) {
540 WRN_DP <<
"trying to render with no window";
546 if(SDL_GetRenderTarget(*
window) != render_texture_) {
547 ERR_DP <<
"trying to render screen, but current render texture is "
548 <<
static_cast<void*
>(SDL_GetRenderTarget(*
window))
549 <<
" | " <<
static_cast<void*
>(current_render_target_.get())
550 <<
". It should be " <<
static_cast<void*
>(render_texture_.get());
551 throw error(
"tried to render screen from wrong render target");
558 SDL_RenderCopy(*
window, render_texture_,
nullptr,
nullptr);
561 SDL_RenderPresent(*
window);
570 WRN_DP <<
"trying to read pixels with no window";
575 texture& target = current_render_target_;
578 if (target != SDL_GetRenderTarget(*
window)) {
579 SDL_Texture*
t = SDL_GetRenderTarget(*
window);
580 ERR_DP <<
"render target " <<
static_cast<void*
>(target.
get())
582 <<
" doesn't match window render target "
583 <<
static_cast<void*
>(
t);
584 throw error(
"unexpected render target while reading pixels");
591 if (r_clipped != *r) {
592 DBG_DP <<
"modifying pixel read area from " << *r
593 <<
" to " << r_clipped;
603 SDL_RenderReadPixels(*
window, &o,
s->format->format,
s->pixels,
s->pitch);
610 WRN_DP <<
"trying to read pixels with no window";
649 const char*
const drvname = SDL_GetCurrentVideoDriver();
650 return drvname ? drvname :
"<not initialized>";
655 std::vector<std::string> res;
656 int num_drivers = SDL_GetNumVideoDrivers();
658 for(
int n = 0;
n < num_drivers; ++
n) {
659 const char* drvname = SDL_GetVideoDriver(
n);
660 res.emplace_back(drvname ? drvname :
"<invalid driver>");
693 std::vector<point> result;
699 const int display_index =
window->get_display_index();
701 const int modes = SDL_GetNumDisplayModes(display_index);
712 SDL_GetDisplayBounds(display_index, &bounds);
714 SDL_DisplayMode mode;
716 for(
int i = 0;
i < modes; ++
i) {
717 if(SDL_GetDisplayMode(display_index,
i, &mode) == 0) {
719 if(mode.w > bounds.w && mode.h > bounds.h) {
723 if(mode.w >= min_res.x && mode.h >= min_res.y) {
724 result.emplace_back(mode.w, mode.h);
729 if(std::find(result.begin(), result.end(), min_res) == result.end()) {
730 result.push_back(min_res);
733 if(include_current) {
737 std::sort(result.begin(), result.end());
738 result.erase(std::unique(result.begin(), result.end()), result.end());
746 return test_resolution_;
756 return (
window->get_flags() & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
761 if (headless_ || testing_) {
795 throw error(
"tried to set resolution with no window");
824 LOG_DP <<
"updating video buffers";
832 float hdpi = 0.0f, vdpi = 0.0f;
833 if(
window && SDL_GetDisplayDPI(
window->get_display_index(),
nullptr, &hdpi, &vdpi) == 0) {
842 hdpi /= scale_factor;
843 vdpi /= scale_factor;
847 return { hdpi, vdpi };
852 std::vector<std::pair<std::string, std::string>> res;
856 if(
window && (rnd = *
window) && SDL_GetRendererInfo(rnd, &ri) == 0) {
857 std::string renderer_name = ri.name ? ri.name :
"<unknown>";
859 if(ri.flags & SDL_RENDERER_SOFTWARE) {
860 renderer_name +=
" (sw)";
863 if(ri.flags & SDL_RENDERER_ACCELERATED) {
864 renderer_name +=
" (hw)";
867 std::string renderer_max = std::to_string(ri.max_texture_width) +
869 std::to_string(ri.max_texture_height);
871 res.emplace_back(
"Renderer", renderer_name);
872 res.emplace_back(
"Maximum texture size", renderer_max);
873 res.emplace_back(
"VSync", ri.flags & SDL_RENDERER_PRESENTVSYNC ?
"on" :
"off");
The wrapper class for the SDL_Window class.
Wrapper class to encapsulate creation and management of an SDL_Texture.
point draw_size() const
The size of the texture in draw-space.
point get_raw_size() const
The raw internal texture size.
SDL_Texture * get() const
Represents version numbers.
Interfaces for manipulating version numbers of engine, add-ons, etc.
Standard logging facilities (interface).
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.
void flush_texture_cache()
Flush the rendered text cache.
void flush_cache()
Purges all image caches.
const int max_window_height
const int def_window_width
const int max_window_width
const int def_window_height
void _set_fullscreen(bool ison)
void _set_maximized(bool ison)
const int max_pixel_scale
const int min_window_height
const int min_window_width
void _set_resolution(const point &res)
version_info get_version()
Returns the runtime SDL version.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
bool window_has_mouse_focus()
True iff the window has mouse focus.
bool headless()
The game is running headless.
rect draw_area()
The current drawable area.
rect output_area()
{0, 0, output_size().x, output_size().y}
void set_window_title(const std::string &title)
Sets the title of the main window.
void clear_render_target()
Reset the render target to the main window / screen.
rect to_output(const rect &r)
Convert coordinates in draw space to coordinates in render space.
std::vector< std::pair< std::string, std::string > > renderer_report()
Provides diagnostic information about the current renderer for the build_info API.
static bool window_has_flags(uint32_t flags)
Tests whether the given flags are currently set on the SDL window.
static bool update_framebuffer()
point output_size()
Returns the size of the final render target.
std::vector< point > get_available_resolutions(const bool include_current)
Returns the list of available screen resolutions.
static bool update_test_framebuffer()
Returns true if the buffer was changed.
void set_window_icon(surface &icon)
Sets the icon of the main window.
bool window_is_visible()
True iff the window is not hidden.
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...
surface read_pixels(SDL_Rect *r)
Copy back a portion of the render target that is already drawn.
void force_render_target(const texture &t)
Set the render target, without any provided way of setting it back.
point game_canvas_size()
The size of the game canvas, in drawing coordinates / game pixels.
SDL_Window * get_window()
bool window_has_focus()
True iff the window has mouse or input focus.
point window_size()
Returns the size of the window in display units / screen coordinates.
bool has_window()
Whether the game has set up a window to render into.
bool testing()
The game is running unit tests.
bool is_fullscreen()
Whether we are currently in fullscreen mode.
std::vector< std::string > enumerate_drivers()
A list of available video drivers.
static void init_window()
rect game_canvas()
The game canvas area, in drawing coordinates.
std::pair< float, float > get_dpi()
Retrieves the current game screen DPI for the build_info API.
static point draw_offset()
int get_pixel_scale()
Get the current active pixel scale multiplier.
point current_resolution()
The current window size in desktop coordinates.
void init(fake type)
Initialize the video subsystem.
void set_fullscreen(bool fullscreen)
Set the fullscreen state.
void deinit()
Deinitialize the video subsystem.
bool set_resolution(const point &resolution)
Set the window resolution.
int current_refresh_rate()
The refresh rate of the screen.
std::string current_driver()
The current video driver in use, or else "<not initialized>".
void toggle_fullscreen()
Toggle fullscreen mode.
SDL_Renderer * get_renderer()
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
texture get_render_target()
Get the current render target.
fake
For describing the type of faked display, if any.
static void init_test_window()
point draw_size()
The size of the current render target in drawing coordinates.
rect input_area()
Returns the input area of the window, in display coordinates.
void scale(size_t factor, const uint32_t *src, uint32_t *trg, int srcWidth, int srcHeight, const ScalerCfg &cfg=ScalerCfg(), int yFirst=0, int yLast=std::numeric_limits< int >::max())
Contains a wrapper class for the SDL_Window class.
Transitional API for porting SDL_ttf-based code to Pango.
An abstract description of a rectangle with integer coordinates.
void clip(const SDL_Rect &r)
Clip this rectangle by the given rectangle.
constexpr point size() const
constexpr point pos() const
An error specifically indicating video subsystem problems.
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...
static lg::log_domain log_display("display")