From 01322aa4c5ba17b6cf14cc74dfd397e7db618f00 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 6 Jan 2023 21:28:48 +0200 Subject: [PATCH] Do not allow selecting resolution no supported by display --- Mods/vcmi/config/vcmi/english.json | 1 + client/renderSDL/SDL_Extensions.cpp | 8 -- client/renderSDL/SDL_Extensions.h | 3 - client/windows/GUIClasses.cpp | 126 +++++++++++++++++++++++----- client/windows/GUIClasses.h | 9 ++ 5 files changed, 115 insertions(+), 32 deletions(-) diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index 6071cc952..a703ff01f 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -31,6 +31,7 @@ "vcmi.systemOptions.resolutionButton.help" : "{Select resolution}\n\n Change in-game screen resolution. Game restart required to apply new resolution.", "vcmi.systemOptions.resolutionMenu.hover" : "Select resolution", "vcmi.systemOptions.resolutionMenu.help" : "Change in-game screen resolution.", + "vcmi.systemOptions.fullscreenFailed" : "{Fullscreen}\n\n Failed to switch to fullscreen mode! Current resolution is not supported by display!", "vcmi.townHall.missingBase" : "Base building %s must be built first", "vcmi.townHall.noCreaturesToRecruit" : "There are no creatures to recruit!", diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index b5b39ab8e..cad39d812 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -66,14 +66,6 @@ SDL_Color CSDL_Ext::toSDL(const ColorRGBA & color) return result; } -Rect CSDL_Ext::getDisplayBounds() -{ - SDL_Rect displayBounds; - SDL_GetDisplayBounds(std::max(0, SDL_GetWindowDisplayIndex(mainWindow)), &displayBounds); - - return fromSDL(displayBounds); -} - void CSDL_Ext::setColors(SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors) { SDL_SetPaletteColors(surface->format->palette,colors,firstcolor,ncolors); diff --git a/client/renderSDL/SDL_Extensions.h b/client/renderSDL/SDL_Extensions.h index 5df8f96af..483531289 100644 --- a/client/renderSDL/SDL_Extensions.h +++ b/client/renderSDL/SDL_Extensions.h @@ -108,9 +108,6 @@ typedef void (*TColorPutterAlpha)(uint8_t *&ptr, const uint8_t & R, const uint8_ uint32_t colorTouint32_t(const SDL_Color * color); //little endian only SDL_Color makeColor(ui8 r, ui8 g, ui8 b, ui8 a); - /// returns dimensions of display on which VCMI window is located - Rect getDisplayBounds(); - void drawLine(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2); void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const SDL_Color &color, int depth = 1); void drawBorder(SDL_Surface * sur, const Rect &r, const SDL_Color &color, int depth = 1); diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index a78359e6c..63cee346b 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -540,7 +540,7 @@ CSystemOptionsWindow::CSystemOptionsWindow() fullscreen = std::make_shared(Point(246, 215), "sysopchk.def", CButton::tooltipLocalized("vcmi.systemOptions.fullscreenButton"), [&](bool value) { - setBoolSetting("video", "fullscreen", value); + setFullscreenMode(value); }); fullscreen->setSelected(settings["video"]["fullscreen"].Bool()); @@ -552,27 +552,110 @@ CSystemOptionsWindow::CSystemOptionsWindow() gameResLabel = std::make_shared(170, 292, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, resolutionToString(screenRes["width"].Integer(), screenRes["height"].Integer())); } -void CSystemOptionsWindow::selectGameRes() +void CSystemOptionsWindow::setFullscreenMode( bool on) { - std::vector items; + fillSelectableResolutions(); -#ifndef VCMI_IOS - Rect displayBounds = CSDL_Ext::getDisplayBounds(); -#endif + const auto & screenRes = settings["video"]["screenRes"]; + const Point desiredResolution(screenRes["width"].Integer(), screenRes["height"].Integer()); + const Point currentResolution(screen->w, screen->h); + + if (!isResolutionSupported(currentResolution, on)) + { + fullscreen->setSelected(!on); + LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.systemOptions.fullscreenFailed")); + return; + } + + setBoolSetting("video", "fullscreen", on); + + if (!isResolutionSupported(desiredResolution, on)) + { + // user changed his desired resolution and switched to fullscreen + // however resolution he selected before is not available in fullscreen + // so reset it back to currect resolution which is confirmed to be supported earlier + Settings gameRes = settings.write["video"]["screenRes"]; + gameRes["width"].Float() = currentResolution.x; + gameRes["height"].Float() = currentResolution.y; + + gameResLabel->setText(resolutionToString(currentResolution.x, currentResolution.y)); + } +} + +void CSystemOptionsWindow::fillSupportedResolutions() +{ + supportedResolutions.clear(); + + // in game we can only change resolution, display is fixed + int displayID = SDL_GetWindowDisplayIndex(mainWindow); + + int modesCount = SDL_GetNumDisplayModes(displayID); + + for (int i =0; i < modesCount; ++i) + { + SDL_DisplayMode mode; + if (SDL_GetDisplayMode(displayID, i, &mode) != 0) + continue; + + Point resolution(mode.w, mode.h); + + supportedResolutions.push_back(resolution); + } +} + +void CSystemOptionsWindow::fillSelectableResolutions() +{ + fillSupportedResolutions(); + selectableResolutions.clear(); - size_t currentResolutionIndex = 0; - size_t i = 0; for(const auto & it : conf.guiOptions) { - const auto & resolution = it.first; -#ifndef VCMI_IOS - if(displayBounds.w < resolution.first || displayBounds.h < resolution.second) - continue; + const Point dimensions(it.first.first, it.first.second); + + if(isResolutionSupported(dimensions)) + selectableResolutions.push_back(dimensions); + } + + boost::range::sort(selectableResolutions, [](const auto & left, const auto & right) + { + return left.x * left.y < right.x * right.y; + }); +} + +bool CSystemOptionsWindow::isResolutionSupported(const Point & resolution) +{ + return isResolutionSupported( resolution, settings["video"]["fullscreen"].Bool()); +} + +bool CSystemOptionsWindow::isResolutionSupported(const Point & resolution, bool fullscreen) +{ +#ifdef VCMI_IOS + // ios can use any resolution + bool canUseAllResolutions = true; +#else + // in fullscreen only resolutions supported by monitor can be used + bool canUseAllResolutions = (fullscreen == false); #endif - auto resolutionStr = resolutionToString(resolution.first, resolution.second); + if (canUseAllResolutions) + return true; + + return vstd::contains(supportedResolutions, resolution); +} + +void CSystemOptionsWindow::selectGameRes() +{ + fillSelectableResolutions(); + + std::vector items; + size_t currentResolutionIndex = 0; + size_t i = 0; + for(const auto & it : selectableResolutions) + { + auto resolutionStr = resolutionToString(it.x, it.y); if(gameResLabel->getText() == resolutionStr) currentResolutionIndex = i; + items.push_back(std::move(resolutionStr)); ++i; } @@ -586,20 +669,21 @@ void CSystemOptionsWindow::selectGameRes() void CSystemOptionsWindow::setGameRes(int index) { - auto iter = conf.guiOptions.begin(); - std::advance(iter, index); + assert(index >= 0 && index < selectableResolutions.size()); - //do not set resolution to illegal one (0x0) - assert(iter!=conf.guiOptions.end() && iter->first.first > 0 && iter->first.second > 0); + if ( index < 0 || index >= selectableResolutions.size() ) + return; + + Point resolution = selectableResolutions[index]; Settings gameRes = settings.write["video"]["screenRes"]; - gameRes["width"].Float() = iter->first.first; - gameRes["height"].Float() = iter->first.second; + gameRes["width"].Float() = resolution.x; + gameRes["height"].Float() = resolution.y; std::string resText; - resText += boost::lexical_cast(iter->first.first); + resText += std::to_string(resolution.x); resText += "x"; - resText += boost::lexical_cast(iter->first.second); + resText += std::to_string(resolution.y); gameResLabel->setText(resText); } diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index 9e46652c0..fa71e36eb 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -226,6 +226,9 @@ private: SettingsListener onFullscreenChanged; + std::vector supportedResolutions; + std::vector selectableResolutions; + //functions bound to buttons void bloadf(); //load game void bsavef(); //save game @@ -234,6 +237,12 @@ private: void brestartf(); //restart game void bmainmenuf(); //return to main menu + void setFullscreenMode( bool on); + void fillSupportedResolutions(); + void fillSelectableResolutions(); + bool isResolutionSupported(const Point & resolution); + bool isResolutionSupported(const Point & resolution, bool fullscreen); + void selectGameRes(); void setGameRes(int index); void closeAndPushEvent(EUserEvent code);