summaryrefslogtreecommitdiff
path: root/src/gui/setup_video.cpp
diff options
context:
space:
mode:
authorThorbjørn Lindeijer <bjorn@lindeijer.nl>2024-02-27 10:51:33 +0000
committerThorbjørn Lindeijer <bjorn@lindeijer.nl>2024-02-27 10:51:33 +0000
commit254c3db4a39aa35274fd7cd4ec2cccde5b92d71b (patch)
tree01c1b5ad71794c02b03d9c45f2c4b5043bc03163 /src/gui/setup_video.cpp
parent81e4f170d8ba4ccbcfa1e6c07bd0522dfc3b6e08 (diff)
downloadmana-254c3db4a39aa35274fd7cd4ec2cccde5b92d71b.tar.gz
mana-254c3db4a39aa35274fd7cd4ec2cccde5b92d71b.tar.bz2
mana-254c3db4a39aa35274fd7cd4ec2cccde5b92d71b.tar.xz
mana-254c3db4a39aa35274fd7cd4ec2cccde5b92d71b.zip
Added VSync and windowed fullscreen options
The configuration and setup UI were adjusted to the new options. This also fixes issues in applying new video settings. Default resolution was changed from 800x600 to 1280x720. VSync is enabled by default while FPS limit was disabled. Display aspect ratio for the resolution options. I had to work around some macOS issues: * Don't change window size when it appears to be "maximized", since it just changes the rendering area while leaving the window maximized. * Unset fullscreen display mode temporarily to allow changing resolutions, otherwise the rendering area no longer matches the screen and mouse input is also off. * Removed SDL_WINDOW_ALLOW_HIGHDPI for now because it causes issues on macOS, since we're not actually handling the scaling factor. A Video class and an SDLGraphics subclass were split off from Graphics. This setup has Less duplication and leaves the OpenGLGraphics and SDLGraphics better separated. Fixes #57 Fixes #58
Diffstat (limited to 'src/gui/setup_video.cpp')
-rw-r--r--src/gui/setup_video.cpp340
1 files changed, 174 insertions, 166 deletions
diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp
index 2d4835ec..de32fcb1 100644
--- a/src/gui/setup_video.cpp
+++ b/src/gui/setup_video.cpp
@@ -25,6 +25,7 @@
#include "configuration.h"
#include "game.h"
#include "graphics.h"
+#include "gui/widgets/dropdown.h"
#include "localplayer.h"
#include "particle.h"
@@ -33,8 +34,6 @@
#include "gui/widgets/checkbox.h"
#include "gui/widgets/label.h"
#include "gui/widgets/layout.h"
-#include "gui/widgets/listbox.h"
-#include "gui/widgets/scrollarea.h"
#include "gui/widgets/slider.h"
#include "gui/widgets/spacer.h"
@@ -46,75 +45,111 @@
#include <SDL.h>
+#include <numeric>
#include <string>
#include <vector>
-extern Graphics *graphics;
-
/**
- * The list model for mode list.
+ * A list model for a given list of strings.
*
* \ingroup Interface
*/
-class ModeListModel : public gcn::ListModel
+class StringListModel : public gcn::ListModel
{
- public:
- ModeListModel();
-
- ~ModeListModel() override { }
-
- /**
- * Returns the number of elements in container.
- */
- int getNumberOfElements() override { return mVideoModes.size(); }
-
- /**
- * Returns element from container.
- */
- std::string getElementAt(int i) override { return mVideoModes[i]; }
-
- /**
- * Returns the index corresponding to the given video mode.
- * E.g.: "800x600".
- * or -1 if not found.
- */
- int getIndexOf(const std::string &widthXHeightMode);
-
- private:
- std::vector<std::string> mVideoModes;
+public:
+ StringListModel(std::vector<std::string> strings)
+ : mStrings(std::move(strings))
+ {}
+
+ int getNumberOfElements() override
+ {
+ return mStrings.size();
+ }
+
+ std::string getElementAt(int i) override
+ {
+ return mStrings[i];
+ }
+
+private:
+ const std::vector<std::string> mStrings;
};
-ModeListModel::ModeListModel()
+/**
+ * The list model for mode list.
+ *
+ * \ingroup Interface
+ */
+class ResolutionListModel : public gcn::ListModel
{
- /* Get available fullscreen/hardware modes */
- const int numModes = SDL_GetNumDisplayModes(0);
- for (int i = 0; i < numModes; i++)
+public:
+ ResolutionListModel()
{
- SDL_DisplayMode mode;
- if (SDL_GetDisplayMode(0, i, &mode) != 0)
- continue;
+ mDisplayModes = Client::getVideo().displayModes();
- // Skip the unreasonably small modes
- if (mode.w < 640 || mode.h < 480)
- continue;
+ // Add a dummy mode for "current window size"
+ mDisplayModes.insert(mDisplayModes.begin(), DisplayMode());
+ }
- // TODO_SDL2: Modes now dinstinguish between pixel format and refresh rate as well
- // TODO_SDL2: Fullscreen mode needs display selection
+ int getNumberOfElements() override
+ {
+ return mDisplayModes.size();
+ }
- mVideoModes.push_back(toString(mode.w) + "x" + toString(mode.h));
+ std::string getElementAt(int i) override
+ {
+ if (i == 0)
+ return _("Custom");
+
+ const auto &mode = getModeAt(i);
+ auto result = toString(mode.width) + "x" + toString(mode.height);
+
+ // Append the aspect ratio
+ const int gcd = std::gcd(mode.width, mode.height);
+ int aspectWidth = mode.width / gcd;
+ int aspectHeight = mode.height / gcd;
+ if (aspectWidth == 8 && aspectHeight == 5)
+ {
+ aspectWidth = 16;
+ aspectHeight = 10;
+ }
+ if (aspectWidth == 7 && aspectHeight == 3)
+ {
+ aspectWidth = 21;
+ aspectHeight = 9;
+ }
+ if (aspectWidth <= 32)
+ result += " (" + toString(aspectWidth) + ":" + toString(aspectHeight) + ")";
+
+ return result;
}
-}
-int ModeListModel::getIndexOf(const std::string &widthXHeightMode)
-{
- for (unsigned i = 0; i < mVideoModes.size(); i++)
- if (mVideoModes.at(i) == widthXHeightMode)
- return i;
+ const DisplayMode &getModeAt(int i) const
+ {
+ return mDisplayModes.at(i);
+ }
- return -1;
-}
+ /**
+ * Returns the index corresponding to the given video resolution
+ * or -1 if not found.
+ */
+ int getIndexOf(int width, int height) const
+ {
+ for (unsigned i = 1; i < mDisplayModes.size(); i++) {
+ const auto &mode = mDisplayModes[i];
+ if (mode.width == width && mode.height == height)
+ return i;
+ }
+
+ return 0;
+ }
+
+private:
+ std::vector<DisplayMode> mDisplayModes;
+};
-const char *Setup_Video::overlayDetailToString(int detail)
+
+static const char *overlayDetailToString(int detail)
{
if (detail == -1)
detail = config.getIntValue("OverlayDetail");
@@ -128,7 +163,7 @@ const char *Setup_Video::overlayDetailToString(int detail)
return "";
}
-const char *Setup_Video::particleDetailToString(int detail)
+static const char *particleDetailToString(int detail)
{
if (detail == -1)
detail = 3 - config.getIntValue("particleEmitterSkip");
@@ -144,20 +179,19 @@ const char *Setup_Video::particleDetailToString(int detail)
}
Setup_Video::Setup_Video():
- mFullScreenEnabled(config.getBoolValue("screen")),
- mOpenGLEnabled(config.getBoolValue("opengl")),
+ mVideoSettings(Client::getVideo().settings()),
mCustomCursorEnabled(config.getBoolValue("customcursor")),
mParticleEffectsEnabled(config.getBoolValue("particleeffects")),
mFps(config.getIntValue("fpslimit")),
mSDLTransparencyDisabled(config.getBoolValue("disableTransparency")),
- mModeListModel(new ModeListModel),
- mModeList(new ListBox(mModeListModel)),
- mFsCheckBox(new CheckBox(_("Full screen"), mFullScreenEnabled)),
- mOpenGLCheckBox(new CheckBox(_("OpenGL"), mOpenGLEnabled)),
- mCustomCursorCheckBox(new CheckBox(_("Custom cursor"),
- mCustomCursorEnabled)),
- mParticleEffectsCheckBox(new CheckBox(_("Particle effects"),
- mParticleEffectsEnabled)),
+ mWindowModeListModel(new StringListModel({ _("Windowed"), _("Windowed Fullscreen"), _("Fullscreen") })),
+ mResolutionListModel(new ResolutionListModel),
+ mWindowModeDropDown(new DropDown(mWindowModeListModel.get())),
+ mResolutionDropDown(new DropDown(mResolutionListModel.get())),
+ mVSyncCheckBox(new CheckBox(_("VSync"), mVideoSettings.vsync)),
+ mOpenGLCheckBox(new CheckBox(_("OpenGL (Legacy)"), mVideoSettings.openGL)),
+ mCustomCursorCheckBox(new CheckBox(_("Custom cursor"), mCustomCursorEnabled)),
+ mParticleEffectsCheckBox(new CheckBox(_("Particle effects"), mParticleEffectsEnabled)),
mFpsCheckBox(new CheckBox(_("FPS limit:"))),
mFpsSlider(new Slider(10, 120)),
mFpsLabel(new Label),
@@ -173,17 +207,9 @@ Setup_Video::Setup_Video():
{
setName(_("Video"));
- auto *space = new Spacer(0,10);
-
- auto *scrollArea = new ScrollArea(mModeList);
- scrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
- scrollArea->setSize(100, 200);
-
overlayDetailLabel = new Label(_("Ambient FX:"));
particleDetailLabel = new Label(_("Particle detail:"));
- mModeList->setEnabled(true);
-
#ifndef USE_OPENGL
mOpenGLCheckBox->setEnabled(false);
#endif
@@ -199,15 +225,16 @@ Setup_Video::Setup_Video():
// If the openGL Mode is enabled, disabling the transaprency
// is irrelevant.
- mDisableSDLTransparencyCheckBox->setEnabled(!mOpenGLEnabled);
+ mDisableSDLTransparencyCheckBox->setEnabled(!mVideoSettings.openGL);
// Pre-select the current video mode.
- std::string videoMode = toString(graphics->getWidth()) + "x"
- + toString(graphics->getHeight());
- mModeList->setSelected(mModeListModel->getIndexOf(videoMode));
+ mWindowModeDropDown->setSelected(static_cast<int>(mVideoSettings.windowMode));
+ mResolutionDropDown->setSelected(mResolutionListModel->getIndexOf(mVideoSettings.width,
+ mVideoSettings.height));
+ mResolutionDropDown->setEnabled(mVideoSettings.windowMode != WindowMode::WindowedFullscreen);
// Set actions
- mModeList->setActionEventId("videomode");
+ mWindowModeDropDown->setActionEventId("windowmode");
mCustomCursorCheckBox->setActionEventId("customcursor");
mParticleEffectsCheckBox->setActionEventId("particleeffects");
mDisableSDLTransparencyCheckBox->setActionEventId("disableTransparency");
@@ -220,7 +247,7 @@ Setup_Video::Setup_Video():
mParticleDetailField->setActionEventId("particledetailfield");
// Set listeners
- mModeList->addActionListener(this);
+ mWindowModeDropDown->addActionListener(this);
mCustomCursorCheckBox->addActionListener(this);
mOpenGLCheckBox->addActionListener(this);
mParticleEffectsCheckBox->addActionListener(this);
@@ -242,104 +269,67 @@ Setup_Video::Setup_Video():
ContainerPlacer place = getPlacer(0, 0);
place.getCell().setHAlign(LayoutCell::FILL);
- place(0, 0, scrollArea, 1, 4).setPadding(2).setHAlign(LayoutCell::FILL);
- place(1, 0, space, 1, 4);
- place(2, 0, mFsCheckBox);
- place(2, 1, mOpenGLCheckBox);
- place(2, 2, mCustomCursorCheckBox);
+ place(0, 0, new Label(_("Window mode:")));
+ place(1, 0, mWindowModeDropDown, 2);
+ place(0, 1, new Label(_("Resolution:")));
+ place(1, 1, mResolutionDropDown, 2);
+ place(0, 2, mVSyncCheckBox, 4);
+ place(0, 3, mOpenGLCheckBox, 4);
place = getPlacer(0, 1);
place.getCell().setHAlign(LayoutCell::FILL);
- place(0, 0, space, 3);
- place(0, 1, mDisableSDLTransparencyCheckBox, 4);
+ place(0, 0, new Spacer(), 4);
+ place(0, 1, mCustomCursorCheckBox, 4);
+ place(0, 2, mDisableSDLTransparencyCheckBox, 4);
- place(0, 2, mFpsCheckBox);
- place(1, 2, mFpsSlider, 2);
- place(3, 2, mFpsLabel);
+ place(0, 3, mFpsCheckBox);
+ place(1, 3, mFpsSlider, 2);
+ place(3, 3, mFpsLabel);
- place(0, 3, mParticleEffectsCheckBox, 4);
+ place(0, 4, mParticleEffectsCheckBox, 4);
- place(0, 4, particleDetailLabel);
- place(1, 4, mParticleDetailSlider, 2);
- place(3, 4, mParticleDetailField);
+ place(0, 5, particleDetailLabel);
+ place(1, 5, mParticleDetailSlider, 2);
+ place(3, 5, mParticleDetailField);
- place(0, 5, overlayDetailLabel);
- place(1, 5, mOverlayDetailSlider, 2);
- place(3, 5, mOverlayDetailField);
+ place(0, 6, overlayDetailLabel);
+ place(1, 6, mOverlayDetailSlider, 2);
+ place(3, 6, mOverlayDetailField);
}
-Setup_Video::~Setup_Video()
-{
- delete mModeListModel;
- delete mModeList;
-}
+Setup_Video::~Setup_Video() = default;
void Setup_Video::apply()
{
// Video mode changes
- int screenWidth = graphics->getWidth();
- int screenHeight = graphics->getHeight();
+ auto &video = Client::getVideo();
+ auto videoSettings = video.settings();
- if (mModeList->getSelected() > -1)
+ if (mResolutionDropDown->getSelected() > 0)
{
- std::string mode = mModeListModel->getElementAt(mModeList->getSelected());
- screenWidth = atoi(mode.substr(0, mode.find("x")).c_str());
- screenHeight = atoi(mode.substr(mode.find("x") + 1).c_str());
+ const auto &mode = mResolutionListModel->getModeAt(mResolutionDropDown->getSelected());
+ videoSettings.width = mode.width;
+ videoSettings.height = mode.height;
}
- bool fullscreen = mFsCheckBox->isSelected();
+ videoSettings.windowMode = static_cast<WindowMode>(mWindowModeDropDown->getSelected());
+ videoSettings.vsync = mVSyncCheckBox->isSelected();
- if (fullscreen != graphics->getFullscreen() ||
- screenWidth != graphics->getWidth() ||
- screenHeight != graphics->getHeight())
+ if (video.apply(videoSettings))
{
- /* The OpenGL test is only necessary on Windows, since switching
- * to/from full screen works fine on Linux. On Windows we'd have to
- * reinitialize the OpenGL state and reload all textures.
- *
- * See http://libsdl.org/cgi/docwiki.cgi/SDL_SetVideoMode
- */
-
-#if defined(_WIN32) || defined(__APPLE__)
- // checks for opengl usage
- if (config.getBoolValue("opengl"))
- {
- new OkDialog(_("Changing Video Mode"),
- _("Restart needed for changes to take effect."));
-
- config.setValue("screen", fullscreen);
- config.setValue("screenwidth", screenWidth);
- config.setValue("screenheight", screenHeight);
- }
- else
-#endif
- {
- if (!graphics->changeVideoMode(screenWidth,
- screenHeight,
- fullscreen))
- {
- std::stringstream errorMessage;
- if (fullscreen)
- errorMessage << _("Failed to switch to fullscreen mode.");
- else
- errorMessage << _("Failed to switch to windowed mode.");
-
- new OkDialog(_("Error"), errorMessage.str());
- }
- else
- {
- Client::instance()->videoResized(screenWidth, screenHeight);
-
- config.setValue("screen", fullscreen);
- config.setValue("screenwidth", screenWidth);
- config.setValue("screenheight", screenHeight);
- }
- }
+ config.setValue("windowmode", static_cast<int>(videoSettings.windowMode));
+ config.setValue("vsync", videoSettings.vsync);
+ config.setValue("screenwidth", videoSettings.width);
+ config.setValue("screenheight", videoSettings.height);
+ }
+ else
+ {
+ new OkDialog(_("Error"), _("Failed to change video mode."));
}
// OpenGL change
- if (mOpenGLCheckBox->isSelected() != mOpenGLEnabled)
+ if (mOpenGLCheckBox->isSelected() != mVideoSettings.openGL)
{
config.setValue("opengl", mOpenGLCheckBox->isSelected());
@@ -385,21 +375,26 @@ void Setup_Video::apply()
config.setValue("fpslimit", mFps);
// We sync old and new values at apply time
- mFullScreenEnabled = config.getBoolValue("screen");
+ mVideoSettings.windowMode = static_cast<WindowMode>(config.getIntValue("windowmode"));
+ mVideoSettings.vsync = config.getBoolValue("vsync");
+ mVideoSettings.openGL = config.getBoolValue("opengl");
mCustomCursorEnabled = config.getBoolValue("customcursor");
mParticleEffectsEnabled = config.getBoolValue("particleeffects");
mOverlayDetail = config.getIntValue("OverlayDetail");
- mOpenGLEnabled = config.getBoolValue("opengl");
mSDLTransparencyDisabled = config.getBoolValue("disableTransparency");
}
void Setup_Video::cancel()
{
- mFpsCheckBox->setSelected(mFps > 0);
- mFsCheckBox->setSelected(mFullScreenEnabled);
- mOpenGLCheckBox->setSelected(mOpenGLEnabled);
+ // Set back to the current video mode.
+ mResolutionDropDown->setSelected(mResolutionListModel->getIndexOf(mVideoSettings.width,
+ mVideoSettings.height));
+
+ mVSyncCheckBox->setSelected(mVideoSettings.vsync);
+ mOpenGLCheckBox->setSelected(mVideoSettings.openGL);
mCustomCursorCheckBox->setSelected(mCustomCursorEnabled);
mParticleEffectsCheckBox->setSelected(mParticleEffectsEnabled);
+ mFpsCheckBox->setSelected(mFps > 0);
mFpsSlider->setValue(mFps);
mFpsSlider->setEnabled(mFps > 0);
mOverlayDetailSlider->setValue(mOverlayDetail);
@@ -407,18 +402,13 @@ void Setup_Video::cancel()
std::string text = mFpsCheckBox->isSelected() ? toString(mFps) : _("None");
mFpsLabel->setCaption(text);
mDisableSDLTransparencyCheckBox->setSelected(mSDLTransparencyDisabled);
- mDisableSDLTransparencyCheckBox->setEnabled(!mOpenGLEnabled);
+ mDisableSDLTransparencyCheckBox->setEnabled(!mVideoSettings.openGL);
- config.setValue("screen", mFullScreenEnabled);
-
- // Set back to the current video mode.
- std::string videoMode = toString(graphics->getWidth()) + "x"
- + toString(graphics->getHeight());
- mModeList->setSelected(mModeListModel->getIndexOf(videoMode));
+ config.setValue("windowmode", static_cast<int>(mVideoSettings.windowMode));
config.setValue("customcursor", mCustomCursorEnabled);
config.setValue("particleeffects", mParticleEffectsEnabled);
- config.setValue("opengl", mOpenGLEnabled);
+ config.setValue("opengl", mVideoSettings.openGL);
config.setValue("disableTransparency", mSDLTransparencyDisabled);
}
@@ -426,7 +416,25 @@ void Setup_Video::action(const gcn::ActionEvent &event)
{
const std::string &id = event.getId();
- if (id == "customcursor")
+ if (id == "windowmode")
+ {
+ auto windowMode = static_cast<WindowMode>(mWindowModeDropDown->getSelected());
+
+ // When the window mode is "windowed fullscreen" we should select the
+ // desktop resolution and disable the option to change it
+ if (windowMode == WindowMode::WindowedFullscreen)
+ {
+ const auto &desktop = Client::getVideo().desktopDisplayMode();
+ mResolutionDropDown->setSelected(
+ mResolutionListModel->getIndexOf(desktop.width, desktop.height));
+ mResolutionDropDown->setEnabled(false);
+ }
+ else
+ {
+ mResolutionDropDown->setEnabled(true);
+ }
+ }
+ else if (id == "customcursor")
{
config.setValue("customcursor", mCustomCursorCheckBox->isSelected());
}