39 #include <SDL2/SDL_render.h> 45 #define LOG_DP LOG_STREAM(info, log_display) 46 #define ERR_DP LOG_STREAM(err, log_display) 47 #define WRN_DP LOG_STREAM(warn, log_display) 48 #define DBG_DP LOG_STREAM(debug, log_display) 53 std::unique_ptr<sdl::window> window;
59 texture current_render_target_ = {};
61 bool headless_ =
false;
62 bool testing_ =
false;
63 point test_resolution_ = {1024, 768};
64 int refresh_rate_ = 0;
65 point game_canvas_size_ = {0, 0};
67 rect input_area_ = {};
89 LOG_DP <<
"initializing video";
90 if(SDL_WasInit(SDL_INIT_VIDEO)) {
91 throw error(
"video subsystem already initialized");
93 if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
94 ERR_DP <<
"Could not initialize SDL_video: " << SDL_GetError();
95 throw error(
"Video initialization failed");
109 throw error(
"unrecognized fake type passed to video::init");
115 LOG_DP <<
"deinitializing video";
119 assert(SDL_WasInit(SDL_INIT_TIMER));
125 render_texture_.reset();
126 current_render_target_.reset();
133 if(SDL_WasInit(SDL_INIT_VIDEO)) {
134 LOG_DP <<
"quitting SDL video subsystem";
135 SDL_QuitSubSystem(SDL_INIT_VIDEO);
137 if(SDL_WasInit(SDL_INIT_VIDEO)) {
139 throw error(
"video subsystem still initialized after deinit");
157 game_canvas_size_ = {800,600};
171 throw(
"trying to update test framebuffer with no window");
174 bool changed =
false;
178 if (render_texture_) {
180 SDL_QueryTexture(render_texture_,
nullptr,
nullptr, &w, &h);
181 if (w != test_resolution_.x || h != test_resolution_.y) {
183 LOG_DP <<
"destroying old render texture";
184 render_texture_.reset();
187 if (!render_texture_) {
188 LOG_DP <<
"creating offscreen render texture";
189 render_texture_.assign(SDL_CreateTexture(
192 SDL_TEXTUREACCESS_TARGET,
193 test_resolution_.x, test_resolution_.y
195 LOG_DP <<
"updated render target to " << test_resolution_.x
196 <<
"x" << test_resolution_.y;
201 game_canvas_size_ = test_resolution_;
202 input_area_ = {{}, test_resolution_};
213 throw error(
"trying to update framebuffer with no window");
220 bool changed =
false;
223 SDL_SetRenderTarget(*
window,
nullptr);
228 SDL_RenderSetIntegerScale(*
window, SDL_TRUE);
232 int max_scale = std::min(
241 int def_scale = std::min(
244 scale = std::min(max_scale, def_scale);
246 int min_scale = std::min(
249 scale = std::max(scale, min_scale);
254 if (pixel_scale_ != scale) {
255 pixel_scale_ =
scale;
262 if (lsize.x != osize.x / scale || lsize.y != osize.y / scale) {
264 LOG_DP <<
"reducing pixel scale from desired " 269 LOG_DP <<
"overriding logical size";
270 LOG_DP <<
" old lsize: " << lsize;
271 LOG_DP <<
" old wsize: " << wsize;
272 LOG_DP <<
" old osize: " << osize;
273 window->set_logical_size(osize.x / scale, osize.y / scale);
274 lsize =
window->get_logical_size();
275 wsize =
window->get_size();
276 osize =
window->get_output_size();
277 LOG_DP <<
" new lsize: " << lsize;
278 LOG_DP <<
" new wsize: " << wsize;
279 LOG_DP <<
" new osize: " << osize;
281 SDL_RenderGetScale(*
window, &sx, &sy);
282 LOG_DP <<
" render scale: " << sx <<
", " << sy;
285 game_canvas_size_ = lsize;
288 if (render_texture_) {
290 SDL_QueryTexture(render_texture_,
nullptr,
nullptr, &w, &h);
291 if (w != osize.x || h != osize.y) {
293 LOG_DP <<
"destroying old render texture";
294 render_texture_.reset();
297 if (!render_texture_) {
298 LOG_DP <<
"creating offscreen render texture";
299 render_texture_.assign(SDL_CreateTexture(
302 SDL_TEXTUREACCESS_TARGET,
306 render_texture_.set_draw_size(lsize);
314 input_area_ = {{}, wsize};
317 if (active_area.
size() != osize) {
318 LOG_DP <<
"render target offset: LT " << active_area.
pos() <<
" RB " 319 << osize - active_area.
size() - active_area.
pos();
322 (active_area.
pos() * wsize) / osize,
323 (active_area.
size() * wsize) / osize
325 LOG_DP <<
"input area: " << input_area_;
333 LOG_DP <<
"creating test window " << test_resolution_.x
334 <<
"x" << test_resolution_.y;
336 uint32_t window_flags = 0;
337 window_flags |= SDL_WINDOW_HIDDEN;
340 uint32_t renderer_flags = 0;
341 renderer_flags |= SDL_RENDERER_TARGETTEXTURE;
345 "", 0, 0, test_resolution_.x, test_resolution_.y,
346 window_flags, renderer_flags
363 uint32_t window_flags = 0;
366 window_flags |= SDL_WINDOW_RESIZABLE;
367 window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
370 window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
372 window_flags |= SDL_WINDOW_MAXIMIZED;
375 uint32_t renderer_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
379 renderer_flags |= SDL_RENDERER_PRESENTVSYNC;
387 assert(!render_texture_);
389 PLAIN_LOG <<
"Setting mode to " << w <<
"x" <<
h;
393 SDL_DisplayMode currentDisplayMode;
394 SDL_GetCurrentDisplayMode(
window->get_display_index(), ¤tDisplayMode);
395 refresh_rate_ = currentDisplayMode.refresh_rate != 0 ? currentDisplayMode.refresh_rate : 60;
408 return test_resolution_;
411 return window->get_output_size();
417 return test_resolution_;
419 return window->get_size();
424 return {0, 0, game_canvas_size_.x, game_canvas_size_.y};
429 return game_canvas_size_;
434 return current_render_target_.draw_size();
439 return {0, 0, current_render_target_.w(), current_render_target_.h()};
452 return (osize - (scale * dsize)) / 2;
458 return {0, 0, p.x, p.y};
464 point dsize = current_render_target_.draw_size();
465 point osize = current_render_target_.get_raw_size();
484 return refresh_rate_;
490 ERR_DP <<
"failed to set render target to " 491 <<
static_cast<void*
>(t.
get()) <<
' ' 493 ERR_DP <<
"last SDL error: " << SDL_GetError();
494 throw error(
"failed to set render target");
496 current_render_target_ =
t;
505 DBG_DP <<
"rendering to window / screen";
506 window->set_logical_size(game_canvas_size_);
507 }
else if (t == render_texture_) {
508 DBG_DP <<
"rendering to primary buffer";
509 window->set_logical_size(game_canvas_size_);
511 DBG_DP <<
"rendering to custom target " 512 <<
static_cast<void*
>(t.
get()) <<
' ' 514 window->set_logical_size(t.
w(), t.
h());
526 assert(current_render_target_ == SDL_GetRenderTarget(
get_renderer()));
527 return current_render_target_;
535 if(headless_ || testing_) {
541 WRN_DP <<
"trying to render with no window";
547 if(SDL_GetRenderTarget(*
window) != render_texture_) {
548 ERR_DP <<
"trying to render screen, but current render texture is " 549 <<
static_cast<void*
>(SDL_GetRenderTarget(*
window))
550 <<
" | " << static_cast<void*>(current_render_target_.get())
551 <<
". It should be " << static_cast<void*>(render_texture_.get());
552 throw error(
"tried to render screen from wrong render target");
559 SDL_RenderCopy(*
window, render_texture_,
nullptr,
nullptr);
562 SDL_RenderPresent(*
window);
571 WRN_DP <<
"trying to read pixels with no window";
576 texture& target = current_render_target_;
579 if (target != SDL_GetRenderTarget(*
window)) {
580 SDL_Texture*
t = SDL_GetRenderTarget(*
window);
581 ERR_DP <<
"render target " <<
static_cast<void*
>(target.
get())
583 <<
" doesn't match window render target " 584 <<
static_cast<void*
>(
t);
585 throw error(
"unexpected render target while reading pixels");
592 if (r_clipped != *r) {
593 DBG_DP <<
"modifying pixel read area from " << *r
594 <<
" to " << r_clipped;
604 SDL_RenderReadPixels(*
window, &o, s->format->format, s->pixels, s->pitch);
611 WRN_DP <<
"trying to read pixels with no window";
650 const char*
const drvname = SDL_GetCurrentVideoDriver();
651 return drvname ? drvname :
"<not initialized>";
656 std::vector<std::string> res;
657 int num_drivers = SDL_GetNumVideoDrivers();
659 for(
int n = 0;
n < num_drivers; ++
n) {
660 const char* drvname = SDL_GetVideoDriver(
n);
661 res.emplace_back(drvname ? drvname :
"<invalid driver>");
694 std::vector<point> result;
700 const int display_index =
window->get_display_index();
702 const int modes = SDL_GetNumDisplayModes(display_index);
713 SDL_GetDisplayBounds(display_index, &bounds);
715 SDL_DisplayMode mode;
717 for(
int i = 0;
i < modes; ++
i) {
718 if(SDL_GetDisplayMode(display_index,
i, &mode) == 0) {
720 if(mode.w > bounds.w && mode.h > bounds.h) {
724 if(mode.w >= min_res.x && mode.h >= min_res.y) {
725 result.emplace_back(mode.w, mode.h);
730 if(std::find(result.begin(), result.end(), min_res) == result.end()) {
731 result.push_back(min_res);
734 if(include_current) {
738 std::sort(result.begin(), result.end());
739 result.erase(std::unique(result.begin(), result.end()), result.end());
747 return test_resolution_;
757 return (
window->get_flags() & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
762 if (headless_ || testing_) {
796 throw error(
"tried to set resolution with no window");
806 window->set_size(resolution.x, resolution.y);
825 LOG_DP <<
"updating video buffers";
833 float hdpi = 0.0f, vdpi = 0.0f;
834 if(
window && SDL_GetDisplayDPI(
window->get_display_index(),
nullptr, &hdpi, &vdpi) == 0) {
843 hdpi /= scale_factor;
844 vdpi /= scale_factor;
848 return { hdpi, vdpi };
853 std::vector<std::pair<std::string, std::string>> res;
857 if(
window && (rnd = *
window) && SDL_GetRendererInfo(rnd, &ri) == 0) {
858 std::string renderer_name = ri.name ? ri.name :
"<unknown>";
860 if(ri.flags & SDL_RENDERER_SOFTWARE) {
861 renderer_name +=
" (sw)";
864 if(ri.flags & SDL_RENDERER_ACCELERATED) {
865 renderer_name +=
" (hw)";
868 std::string renderer_max = std::to_string(ri.max_texture_width) +
870 std::to_string(ri.max_texture_height);
872 res.emplace_back(
"Renderer", renderer_name);
873 res.emplace_back(
"Maximum texture size", renderer_max);
874 res.emplace_back(
"VSync", ri.flags & SDL_RENDERER_PRESENTVSYNC ?
"on" :
"off");
void init(fake type)
Initialize the video subsystem.
void _set_fullscreen(bool ison)
const int min_window_height
std::vector< point > get_available_resolutions(const bool include_current)
Returns the list of available screen resolutions.
surface read_pixels(SDL_Rect *r)
Copy back a portion of the render target that is already drawn.
int get_pixel_scale()
Get the current active pixel scale multiplier.
SDL_Renderer * get_renderer()
void _set_maximized(bool ison)
Interfaces for manipulating version numbers of engine, add-ons, etc.
int w() const
The draw-space width of the texture, in pixels.
std::string current_driver()
The current video driver in use, or else "<not initialized>".
An error specifically indicating video subsystem problems.
void set_window_title(const std::string &title)
Sets the title of the main window.
rect output_area()
{0, 0, output_size().x, output_size().y}
bool is_fullscreen()
Whether we are currently in fullscreen mode.
bool window_has_focus()
True iff the window has mouse or input focus.
static bool update_test_framebuffer()
Returns true if the buffer was changed.
const int min_window_width
int h() const
The draw-space height of the texture, in pixels.
point get_raw_size() const
The raw internal texture size.
const int max_window_width
rect to_output(const rect &r)
Convert coordinates in draw space to coordinates in render space.
int current_refresh_rate()
The refresh rate of the screen.
void _set_resolution(const point &res)
void deinit()
Deinitialize the video subsystem.
static void init_window()
void set_fullscreen(bool fullscreen)
Set the fullscreen state.
std::vector< std::pair< std::string, std::string > > renderer_report()
Provides diagnostic information about the current renderer for the build_info API.
constexpr point pos() const
bool testing()
The game is running unit tests.
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 point draw_offset()
texture get_render_target()
Get the current render target.
static bool window_has_flags(uint32_t flags)
Tests whether the given flags are currently set on the SDL window.
std::vector< std::string > enumerate_drivers()
A list of available video drivers.
static bool update_framebuffer()
Wrapper class to encapsulate creation and management of an SDL_Texture.
void invalidate_all()
Mark the entire screen as requiring redraw.
bool set_resolution(const point &resolution)
Set the window resolution.
void flush_cache()
Purges all image caches.
void update_buffers(bool autoupdate)
Update buffers to match current resolution and pixel scale settings.
point current_resolution()
The current window size in desktop coordinates.
rect game_canvas()
The game canvas area, in drawing coordinates.
point draw_size() const
The size of the texture in draw-space.
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.
The wrapper class for the SDL_Window class.
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())
point output_size()
Returns the size of the final render target.
static lg::log_domain log_display("display")
void clip(const SDL_Rect &r)
Clip this rectangle by the given rectangle.
rect draw_area()
The current drawable area.
bool headless()
The game is running headless.
map_display and display: classes which take care of displaying the map and game-data on the screen...
const int max_pixel_scale
constexpr point size() const
void set_window_icon(surface &icon)
Sets the icon of the main window.
std::pair< float, float > get_dpi()
Retrieves the current game screen DPI for the build_info API.
CGFloat get_scale_factor(int display_index)
const int def_window_width
static map_location::DIRECTION s
void force_render_target(const texture &t)
Set the render target, without any provided way of setting it back.
fake
For describing the type of faked display, if any.
point game_canvas_size()
The size of the game canvas, in drawing coordinates / game pixels.
An abstract description of a rectangle with integer coordinates.
static void init_test_window()
void flush_texture_cache()
Flush the rendered text cache.
bool has_window()
Whether the game has set up a window to render into.
bool window_has_mouse_focus()
True iff the window has mouse focus.
Represents version numbers.
point draw_size()
The size of the current render target in drawing coordinates.
bool window_is_visible()
True iff the window is not hidden.
void toggle_fullscreen()
Toggle fullscreen mode.
const int def_window_height
SDL_Texture * get() const
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...
SDL_Window * get_window()
Standard logging facilities (interface).
point window_size()
Returns the size of the window in display units / screen coordinates.
rect input_area()
Returns the input area of the window, in display coordinates.
Contains a wrapper class for the SDL_Window class.
void point(int x, int y)
Draw a single point.
static map_location::DIRECTION n
Transitional API for porting SDL_ttf-based code to Pango.
const int max_window_height
void clear_render_target()
Reset the render target to the main window / screen.