diff --git a/client/renderSDL/ScreenHandler.cpp b/client/renderSDL/ScreenHandler.cpp index 2cf37c1b2..9b2add2ad 100644 --- a/client/renderSDL/ScreenHandler.cpp +++ b/client/renderSDL/ScreenHandler.cpp @@ -25,7 +25,7 @@ #include // TODO: should be made into a private members of ScreenHandler -SDL_Window * mainWindow = nullptr; +static SDL_Window * mainWindow = nullptr; SDL_Renderer * mainRenderer = nullptr; SDL_Texture * screenTexture = nullptr; SDL_Surface * screen = nullptr; //main screen surface @@ -42,9 +42,12 @@ std::tuple ScreenHandler::getSupportedScalingRange() const // arbitrary limit on *downscaling*. Allow some downscaling, if requested by user. Should be generally limited to 100+ for all but few devices static const double minimalScaling = 50; - Point renderResolution = getPreferredRenderingResolution(); - double maximalScalingWidth = 100.0 * renderResolution.x / minResolution.x; - double maximalScalingHeight = 100.0 * renderResolution.y / minResolution.y; + Point renderResolution = getActualRenderResolution(); + double reservedAreaWidth = settings["video"]["reservedWidth"].Float(); + Point availableResolution = Point(renderResolution.x * (1 - reservedAreaWidth), renderResolution.y); + + double maximalScalingWidth = 100.0 * availableResolution.x / minResolution.x; + double maximalScalingHeight = 100.0 * availableResolution.y / minResolution.y; double maximalScaling = std::min(maximalScalingWidth, maximalScalingHeight); return { minimalScaling, maximalScaling }; @@ -78,18 +81,31 @@ Rect ScreenHandler::convertLogicalPointsToWindow(const Rect & input) const Point ScreenHandler::getPreferredLogicalResolution() const { - Point renderResolution = getPreferredRenderingResolution(); + Point renderResolution = getActualRenderResolution(); + double reservedAreaWidth = settings["video"]["reservedWidth"].Float(); + Point availableResolution = Point(renderResolution.x * (1 - reservedAreaWidth), renderResolution.y); + auto [minimalScaling, maximalScaling] = getSupportedScalingRange(); int userScaling = settings["video"]["resolution"]["scaling"].Integer(); int scaling = std::clamp(userScaling, minimalScaling, maximalScaling); - Point logicalResolution = renderResolution * 100.0 / scaling; + Point logicalResolution = availableResolution * 100.0 / scaling; return logicalResolution; } -Point ScreenHandler::getPreferredRenderingResolution() const +Point ScreenHandler::getActualRenderResolution() const +{ + assert(mainRenderer != nullptr); + + Point result; + SDL_GetRendererOutputSize(mainRenderer, &result.x, &result.y); + + return result; +} + +Point ScreenHandler::getPreferredWindowResolution() const { if (getPreferredWindowMode() == EWindowMode::FULLSCREEN_BORDERLESS_WINDOWED) { @@ -208,7 +224,7 @@ void ScreenHandler::updateWindowState() SDL_DisplayMode mode; SDL_GetDesktopDisplayMode(displayIndex, &mode); - Point resolution = getPreferredRenderingResolution(); + Point resolution = getPreferredWindowResolution(); mode.w = resolution.x; mode.h = resolution.y; @@ -226,7 +242,7 @@ void ScreenHandler::updateWindowState() } case EWindowMode::WINDOWED: { - Point resolution = getPreferredRenderingResolution(); + Point resolution = getPreferredWindowResolution(); SDL_SetWindowFullscreen(mainWindow, 0); SDL_SetWindowSize(mainWindow, resolution.x, resolution.y); SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex)); @@ -316,7 +332,7 @@ SDL_Window * ScreenHandler::createWindowImpl(Point dimensions, int flags, bool c SDL_Window * ScreenHandler::createWindow() { #ifndef VCMI_MOBILE - Point dimensions = getPreferredRenderingResolution(); + Point dimensions = getPreferredWindowResolution(); switch(getPreferredWindowMode()) { @@ -376,7 +392,7 @@ void ScreenHandler::validateSettings() { //we only check that our desired window size fits on screen int displayIndex = getPreferredDisplayIndex(); - Point resolution = getPreferredRenderingResolution(); + Point resolution = getPreferredWindowResolution(); SDL_DisplayMode mode; @@ -394,7 +410,7 @@ void ScreenHandler::validateSettings() if (getPreferredWindowMode() == EWindowMode::FULLSCREEN_EXCLUSIVE) { auto legalOptions = getSupportedResolutions(); - Point selectedResolution = getPreferredRenderingResolution(); + Point selectedResolution = getPreferredWindowResolution(); if(!vstd::contains(legalOptions, selectedResolution)) { diff --git a/client/renderSDL/ScreenHandler.h b/client/renderSDL/ScreenHandler.h index 79ae0e68e..5bd5ae30b 100644 --- a/client/renderSDL/ScreenHandler.h +++ b/client/renderSDL/ScreenHandler.h @@ -30,14 +30,17 @@ enum class EWindowMode }; /// This class is responsible for management of game window and its main rendering surface -class ScreenHandler : public IScreenHandler +class ScreenHandler final : public IScreenHandler { /// Dimensions of target surfaces/textures, this value is what game logic views as screen size Point getPreferredLogicalResolution() const; /// Dimensions of output window, if different from logicalResolution SDL will perform scaling /// This value is what player views as window size - Point getPreferredRenderingResolution() const; + Point getPreferredWindowResolution() const; + + /// Dimensions of render output, usually same as window size except for high-DPI screens on macOS / iOS + Point getActualRenderResolution() const; EWindowMode getPreferredWindowMode() const; diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 71832ae99..9311387a5 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -109,13 +109,14 @@ "additionalProperties" : false, "default" : {}, "required" : [ - "resolution", - "fullscreen", - "realFullscreen", - "cursor", - "showIntro", - "spellbookAnimation", - "driver", + "resolution", + "reservedWidth", + "fullscreen", + "realFullscreen", + "cursor", + "showIntro", + "spellbookAnimation", + "driver", "displayIndex", "showfps", "targetfps" @@ -134,6 +135,11 @@ "defaultAndroid" : {"width" : 800, "height" : 600, "scaling" : 200 }, "default" : {"width" : 800, "height" : 600, "scaling" : 100 } }, + "reservedWidth" : { + "type" : "number", + "defaultIOS" : 0.1, // iOS camera cutout / notch is excluded from available area by SDL + "default" : 0 + }, "fullscreen" : { "type" : "boolean", "default" : false