浏览代码

Merge pull request #1386 from IvanSavenko/resolutions_filter

Do not allow selecting resolutions not supported by display
Ivan Savenko 2 年之前
父节点
当前提交
c8c38ac922

+ 1 - 0
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!",

+ 35 - 8
client/renderSDL/SDL_Extensions.cpp

@@ -49,14 +49,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);
@@ -878,7 +870,42 @@ void CSDL_Ext::getClipRect(SDL_Surface * src, Rect & other)
 	other = CSDL_Ext::fromSDL(rect);
 }
 
+bool CSDL_Ext::isResolutionSupported(const std::vector<Point> & resolutions, const Point toTest )
+{
+#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
+	// ios can use any resolution
+	// presumably, same goes for Android
+	return true;
+#else
+	// in fullscreen only resolutions supported by monitor can be used
+	return vstd::contains(resolutions, toTest);
+#endif
+}
+
+std::vector<Point> CSDL_Ext::getSupportedResolutions()
+{
+	int displayID = SDL_GetWindowDisplayIndex(mainWindow);
+	return getSupportedResolutions(displayID);
+}
+
+std::vector<Point> CSDL_Ext::getSupportedResolutions( int displayIndex)
+{
+	std::vector<Point> result;
+
+	int modesCount = SDL_GetNumDisplayModes(displayIndex);
+
+	for (int i =0; i < modesCount; ++i)
+	{
+		SDL_DisplayMode mode;
+		if (SDL_GetDisplayMode(displayIndex, i, &mode) != 0)
+			continue;
 
+		Point resolution(mode.w, mode.h);
+
+		result.push_back(resolution);
+	}
+	return result;
+}
 
 template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<2>(int, int);
 template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<3>(int, int);

+ 5 - 3
client/renderSDL/SDL_Extensions.h

@@ -81,9 +81,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);
@@ -107,6 +104,11 @@ typedef void (*TColorPutterAlpha)(uint8_t *&ptr, const uint8_t & R, const uint8_
 	void applyEffectBpp( SDL_Surface * surf, const Rect & rect, int mode );
 	void applyEffect(SDL_Surface * surf, const Rect & rect, int mode); //mode: 0 - sepia, 1 - grayscale
 
+	bool isResolutionSupported(const std::vector<Point> & resolutions, const Point toTest );
+
+	std::vector<Point> getSupportedResolutions();
+	std::vector<Point> getSupportedResolutions( int displayIndex);
+
 	void setColorKey(SDL_Surface * surface, SDL_Color color);
 
 	///set key-color to 0,255,255

+ 80 - 22
client/windows/GUIClasses.cpp

@@ -63,6 +63,8 @@
 #include "../lib/NetPacksBase.h"
 #include "../lib/StartInfo.h"
 
+#include <SDL_surface.h>
+
 CRecruitmentWindow::CCreatureCard::CCreatureCard(CRecruitmentWindow * window, const CCreature * crea, int totalAmount)
 	: CIntObject(LCLICK | RCLICK),
 	parent(window),
@@ -540,7 +542,7 @@ CSystemOptionsWindow::CSystemOptionsWindow()
 
 	fullscreen = std::make_shared<CToggleButton>(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 +554,82 @@ CSystemOptionsWindow::CSystemOptionsWindow()
 	gameResLabel = std::make_shared<CLabel>(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<std::string> 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::fillSelectableResolutions()
+{
+	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;
-#endif
+		const Point dimensions(it.first.first, it.first.second);
 
-		auto resolutionStr = resolutionToString(resolution.first, resolution.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)
+{
+	if (!fullscreen)
+		return true;
+
+	auto supportedList = CSDL_Ext::getSupportedResolutions();
+
+	return CSDL_Ext::isResolutionSupported(supportedList, resolution);
+}
+
+void CSystemOptionsWindow::selectGameRes()
+{
+	fillSelectableResolutions();
+
+	std::vector<std::string> 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 +643,21 @@ void CSystemOptionsWindow::selectGameRes()
 
 void CSystemOptionsWindow::setGameRes(int index)
 {
-	auto iter = conf.guiOptions.begin();
-	std::advance(iter, index);
+	assert(index >= 0 && index < selectableResolutions.size());
+
+	if ( index < 0 || index >= selectableResolutions.size() )
+		return;
 
-	//do not set resolution to illegal one (0x0)
-	assert(iter!=conf.guiOptions.end() && iter->first.first > 0 && iter->first.second > 0);
+	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<std::string>(iter->first.first);
+	resText += std::to_string(resolution.x);
 	resText += "x";
-	resText += boost::lexical_cast<std::string>(iter->first.second);
+	resText += std::to_string(resolution.y);
 	gameResLabel->setText(resText);
 }
 

+ 8 - 0
client/windows/GUIClasses.h

@@ -226,6 +226,9 @@ private:
 
 	SettingsListener onFullscreenChanged;
 
+	std::vector<Point> supportedResolutions;
+	std::vector<Point> selectableResolutions;
+
 	//functions bound to buttons
 	void bloadf(); //load game
 	void bsavef(); //save game
@@ -234,6 +237,11 @@ private:
 	void brestartf(); //restart game
 	void bmainmenuf(); //return to main menu
 
+	void setFullscreenMode( bool on);
+	void fillSelectableResolutions();
+	bool isResolutionSupported(const Point & resolution);
+	bool isResolutionSupported(const Point & resolution, bool fullscreen);
+
 	void selectGameRes();
 	void setGameRes(int index);
 	void closeAndPushEvent(EUserEvent code);