/* * The Mana Client * Copyright (C) 2004-2009 The Mana World Development Team * Copyright (C) 2009-2012 The Mana Developers * * This file is part of The Mana Client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gui/setup_video.h" #include "client.h" #include "configuration.h" #include "game.h" #include "graphics.h" #include "gui/widgets/dropdown.h" #include "localplayer.h" #include "particle.h" #include "gui/okdialog.h" #include "gui/widgets/checkbox.h" #include "gui/widgets/label.h" #include "gui/widgets/layout.h" #include "gui/widgets/slider.h" #include "gui/widgets/spacer.h" #include "utils/gettext.h" #include "utils/stringutils.h" #include #include #include #include #include #include /** * A list model for a given list of strings. * * \ingroup Interface */ class StringListModel : public gcn::ListModel { public: StringListModel(std::vector strings) : mStrings(std::move(strings)) {} int getNumberOfElements() override { return mStrings.size(); } std::string getElementAt(int i) override { return mStrings[i]; } private: const std::vector mStrings; }; /** * The list model for mode list. * * \ingroup Interface */ class ResolutionListModel : public gcn::ListModel { public: ResolutionListModel() { mDisplayModes = Client::getVideo().displayModes(); // Add a dummy mode for "current window size" mDisplayModes.insert(mDisplayModes.begin(), DisplayMode()); } int getNumberOfElements() override { return mDisplayModes.size(); } 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; } const DisplayMode &getModeAt(int i) const { return mDisplayModes.at(i); } /** * Returns the index corresponding to the given video resolution * or 0 ("Custom") 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 mDisplayModes; }; /** * The list model for choosing the scale. * * \ingroup Interface */ class ScaleListModel : public gcn::ListModel { public: ScaleListModel(const VideoSettings &videoSettings) : mVideoSettings(videoSettings) {} void setVideoSettings(const VideoSettings &videoSettings) { mVideoSettings = videoSettings; } int getNumberOfElements() override { return mVideoSettings.maxScale() + 1; } std::string getElementAt(int i) override { if (i == 0) return strprintf(_("Auto (%dx)"), mVideoSettings.autoScale()); return strprintf(_("%dx"), i); } private: VideoSettings mVideoSettings; }; static const char *overlayDetailToString(int detail) { if (detail == -1) detail = config.getIntValue("OverlayDetail"); switch (detail) { case 0: return _("off"); case 1: return _("low"); case 2: return _("high"); } return ""; } static const char *particleDetailToString(int detail) { if (detail == -1) detail = 3 - config.getIntValue("particleEmitterSkip"); switch (detail) { case 0: return _("low"); case 1: return _("medium"); case 2: return _("high"); case 3: return _("max"); } return ""; } Setup_Video::Setup_Video(): mVideoSettings(Client::getVideo().settings()), mCustomCursorEnabled(config.getBoolValue("customcursor")), mParticleEffectsEnabled(config.getBoolValue("particleeffects")), mFps(config.getIntValue("fpslimit")), mSDLTransparencyDisabled(config.getBoolValue("disableTransparency")), mWindowModeListModel(new StringListModel({ _("Windowed"), _("Windowed Fullscreen"), _("Fullscreen") })), mResolutionListModel(new ResolutionListModel), mScaleListModel(new ScaleListModel(mVideoSettings)), mWindowModeDropDown(new DropDown(mWindowModeListModel.get())), mResolutionDropDown(new DropDown(mResolutionListModel.get())), mScaleDropDown(new DropDown(mScaleListModel.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), mOverlayDetail(config.getIntValue("OverlayDetail")), mOverlayDetailSlider(new Slider(0, 2)), mOverlayDetailField(new Label), mParticleDetail(3 - config.getIntValue("particleEmitterSkip")), mParticleDetailSlider(new Slider(0, 3)), mParticleDetailField(new Label), mDisableSDLTransparencyCheckBox( new CheckBox(_("Disable transparency (Low CPU mode)"), mSDLTransparencyDisabled)) { setName(_("Video")); overlayDetailLabel = new Label(_("Ambient FX:")); particleDetailLabel = new Label(_("Particle detail:")); #ifndef USE_OPENGL mOpenGLCheckBox->setEnabled(false); #endif mFpsLabel->setCaption(mFps > 0 ? toString(mFps) : _("None")); mFpsLabel->setWidth(60); mFpsSlider->setValue(mFps); mFpsSlider->setEnabled(mFps > 0); mFpsCheckBox->setSelected(mFps > 0); overlayDetailLabel->setAlignment(Graphics::RIGHT); particleDetailLabel->setAlignment(Graphics::RIGHT); // If the openGL Mode is enabled, disabling the transaprency // is irrelevant. mDisableSDLTransparencyCheckBox->setEnabled(!mVideoSettings.openGL); // Pre-select the current video mode. mWindowModeDropDown->setSelected(static_cast(mVideoSettings.windowMode)); mResolutionDropDown->setSelected(mResolutionListModel->getIndexOf(mVideoSettings.width, mVideoSettings.height)); mResolutionDropDown->setEnabled(mVideoSettings.windowMode != WindowMode::WindowedFullscreen); mScaleDropDown->setSelected(mVideoSettings.userScale); // Set actions mWindowModeDropDown->setActionEventId("windowmode"); mResolutionDropDown->setActionEventId("resolution"); mCustomCursorCheckBox->setActionEventId("customcursor"); mParticleEffectsCheckBox->setActionEventId("particleeffects"); mDisableSDLTransparencyCheckBox->setActionEventId("disableTransparency"); mFpsCheckBox->setActionEventId("fpslimitcheckbox"); mFpsSlider->setActionEventId("fpslimitslider"); mOverlayDetailSlider->setActionEventId("overlaydetailslider"); mOverlayDetailField->setActionEventId("overlaydetailfield"); mOpenGLCheckBox->setActionEventId("opengl"); mParticleDetailSlider->setActionEventId("particledetailslider"); mParticleDetailField->setActionEventId("particledetailfield"); // Set listeners mWindowModeDropDown->addActionListener(this); mResolutionDropDown->addActionListener(this); mCustomCursorCheckBox->addActionListener(this); mOpenGLCheckBox->addActionListener(this); mParticleEffectsCheckBox->addActionListener(this); mDisableSDLTransparencyCheckBox->addActionListener(this); mFpsCheckBox->addActionListener(this); mFpsSlider->addActionListener(this); mOverlayDetailSlider->addActionListener(this); mOverlayDetailField->addKeyListener(this); mParticleDetailSlider->addActionListener(this); mParticleDetailField->addKeyListener(this); mOverlayDetailField->setCaption(overlayDetailToString(mOverlayDetail)); mOverlayDetailSlider->setValue(mOverlayDetail); mParticleDetailField->setCaption(particleDetailToString(mParticleDetail)); mParticleDetailSlider->setValue(mParticleDetail); // Do the layout ContainerPlacer place = getPlacer(0, 0); place.getCell().setHAlign(LayoutCell::FILL); place(0, 0, new Label(_("Window mode:"))); place(1, 0, mWindowModeDropDown, 2).setPadding(2); place(0, 1, new Label(_("Resolution:"))); place(1, 1, mResolutionDropDown, 2).setPadding(2); place(0, 2, new Label(_("Scale:"))); place(1, 2, mScaleDropDown, 2).setPadding(2); place(0, 3, mVSyncCheckBox, 4); place(0, 4, mOpenGLCheckBox, 4); place = getPlacer(0, 1); place.getCell().setHAlign(LayoutCell::FILL); place(0, 0, new Spacer(), 4); place(0, 1, mCustomCursorCheckBox, 4); place(0, 2, mDisableSDLTransparencyCheckBox, 4); place(0, 3, mFpsCheckBox); place(1, 3, mFpsSlider, 2); place(3, 3, mFpsLabel); place(0, 4, mParticleEffectsCheckBox, 4); place(0, 5, particleDetailLabel); place(1, 5, mParticleDetailSlider, 2); place(3, 5, mParticleDetailField); place(0, 6, overlayDetailLabel); place(1, 6, mOverlayDetailSlider, 2); place(3, 6, mOverlayDetailField); } Setup_Video::~Setup_Video() = default; void Setup_Video::apply() { // Video mode changes auto &video = Client::getVideo(); mVideoSettings = video.settings(); mVideoSettings.windowMode = static_cast(mWindowModeDropDown->getSelected()); if (mResolutionDropDown->getSelected() > 0) { const auto &mode = mResolutionListModel->getModeAt(mResolutionDropDown->getSelected()); mVideoSettings.width = mode.width; mVideoSettings.height = mode.height; } mVideoSettings.userScale = std::max(0, mScaleDropDown->getSelected()); mVideoSettings.vsync = mVSyncCheckBox->isSelected(); if (video.apply(mVideoSettings)) { config.setValue("windowmode", static_cast(mVideoSettings.windowMode)); config.setValue("scale", mVideoSettings.userScale); config.setValue("vsync", mVideoSettings.vsync); config.setValue("screenwidth", mVideoSettings.width); config.setValue("screenheight", mVideoSettings.height); Client::instance()->checkGraphicsSize(); } else { new OkDialog(_("Error"), _("Failed to change video mode.")); } // OpenGL change if (mOpenGLCheckBox->isSelected() != mVideoSettings.openGL) { config.setValue("opengl", mOpenGLCheckBox->isSelected()); // OpenGL can currently only be changed by restarting, notify user. if (mOpenGLCheckBox->isSelected()) { new OkDialog(_("Changing to OpenGL"), _("Applying change to OpenGL requires restart.\n\n" "In case OpenGL messes up your game graphics, " "restart the game with the command line option " "\"--no-opengl\".")); } else { new OkDialog(_("Deactivating OpenGL"), _("Applying change to OpenGL requires restart.")); } } // If LowCPU is enabled from a disabled state we warn the user else if (mDisableSDLTransparencyCheckBox->isSelected()) { if (config.getValue("disableTransparency", true) == false) { new OkDialog(_("Transparency disabled"), _("You must restart to apply changes.")); } } else { if (config.getValue("disableTransparency", true) == true) { new OkDialog(_("Transparency enabled"), _("You must restart to apply changes.")); } } config.setValue("disableTransparency", mDisableSDLTransparencyCheckBox->isSelected()); mFps = mFpsCheckBox->isSelected() ? (int) mFpsSlider->getValue() : 0; mFpsSlider->setEnabled(mFps > 0); // FPS change config.setValue("fpslimit", mFps); // We sync old and new values at apply time mVideoSettings.windowMode = static_cast(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"); mSDLTransparencyDisabled = config.getBoolValue("disableTransparency"); } void Setup_Video::cancel() { // Set back to the current video mode. mVideoSettings = Client::getVideo().settings(); mWindowModeDropDown->setSelected(static_cast(mVideoSettings.windowMode)); mResolutionDropDown->setSelected(mResolutionListModel->getIndexOf(mVideoSettings.width, mVideoSettings.height)); mScaleDropDown->setSelected(mVideoSettings.userScale); 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); mParticleDetailSlider->setValue(mParticleDetail); std::string text = mFpsCheckBox->isSelected() ? toString(mFps) : _("None"); mFpsLabel->setCaption(text); mDisableSDLTransparencyCheckBox->setSelected(mSDLTransparencyDisabled); mDisableSDLTransparencyCheckBox->setEnabled(!mVideoSettings.openGL); config.setValue("windowmode", static_cast(mVideoSettings.windowMode)); config.setValue("customcursor", mCustomCursorEnabled); config.setValue("particleeffects", mParticleEffectsEnabled); config.setValue("opengl", mVideoSettings.openGL); config.setValue("disableTransparency", mSDLTransparencyDisabled); } void Setup_Video::action(const gcn::ActionEvent &event) { const std::string &id = event.getId(); if (id == "windowmode" || id == "resolution") { auto windowMode = static_cast(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); } refreshScaleList(); } else if (id == "resolution") { refreshScaleList(); } else if (id == "customcursor") { config.setValue("customcursor", mCustomCursorCheckBox->isSelected()); } else if (id == "particleeffects") { config.setValue("particleeffects", mParticleEffectsCheckBox->isSelected()); Particle::enabled = mParticleEffectsCheckBox->isSelected(); if (Game::instance()) { new OkDialog(_("Particle Effect Settings Changed."), _("Changes will take effect on map change.")); } } else if (id == "overlaydetailslider") { int val = (int) mOverlayDetailSlider->getValue(); mOverlayDetailField->setCaption(overlayDetailToString(val)); config.setValue("OverlayDetail", val); } else if (id == "particledetailslider") { int val = (int) mParticleDetailSlider->getValue(); mParticleDetailField->setCaption(particleDetailToString(val)); config.setValue("particleEmitterSkip", 3 - val); Particle::emitterSkip = 4 - val; } else if (id == "fpslimitcheckbox" || id == "fpslimitslider") { int fps = (int) mFpsSlider->getValue(); fps = fps > 0 ? fps : mFpsSlider->getScaleStart(); mFps = mFpsCheckBox->isSelected() ? fps : 0; const std::string text = mFps > 0 ? toString(mFps) : _("None"); mFpsLabel->setCaption(text); mFpsSlider->setValue(mFps); mFpsSlider->setEnabled(mFps > 0); } else if (id == "opengl" || id == "disableTransparency") { // Disable transparency disabling when in OpenGL. if (mOpenGLCheckBox->isSelected()) { mDisableSDLTransparencyCheckBox->setSelected(false); mDisableSDLTransparencyCheckBox->setEnabled(false); } else { mDisableSDLTransparencyCheckBox->setEnabled(true); } } } void Setup_Video::refreshScaleList() { if (mResolutionDropDown->getSelected() > 0) { const auto &mode = mResolutionListModel->getModeAt(mResolutionDropDown->getSelected()); mVideoSettings.width = mode.width; mVideoSettings.height = mode.height; } else { auto &videoSettings = Client::getVideo().settings(); mVideoSettings.width = videoSettings.width; mVideoSettings.height = videoSettings.height; } mScaleListModel->setVideoSettings(mVideoSettings); mScaleDropDown->setListModel(mScaleListModel.get()); mScaleDropDown->setSelected(mVideoSettings.userScale); }