diff options
-rw-r--r-- | src/client.cpp | 25 | ||||
-rw-r--r-- | src/client.h | 3 | ||||
-rw-r--r-- | src/defaults.cpp | 1 | ||||
-rw-r--r-- | src/game.cpp | 3 | ||||
-rw-r--r-- | src/graphics.cpp | 29 | ||||
-rw-r--r-- | src/graphics.h | 23 | ||||
-rw-r--r-- | src/gui/equipmentwindow.cpp | 3 | ||||
-rw-r--r-- | src/gui/gui.cpp | 21 | ||||
-rw-r--r-- | src/gui/sdlinput.cpp | 25 | ||||
-rw-r--r-- | src/gui/setup_video.cpp | 106 | ||||
-rw-r--r-- | src/gui/setup_video.h | 5 | ||||
-rw-r--r-- | src/gui/viewport.cpp | 19 | ||||
-rw-r--r-- | src/openglgraphics.cpp | 113 | ||||
-rw-r--r-- | src/openglgraphics.h | 18 | ||||
-rw-r--r-- | src/sdlgraphics.cpp | 51 | ||||
-rw-r--r-- | src/sdlgraphics.h | 15 | ||||
-rw-r--r-- | src/video.cpp | 69 | ||||
-rw-r--r-- | src/video.h | 11 |
18 files changed, 386 insertions, 154 deletions
diff --git a/src/client.cpp b/src/client.cpp index 1348112d..eac55c83 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -310,6 +310,7 @@ Client::Client(const Options &options): videoSettings.windowMode = static_cast<WindowMode>(config.getIntValue("windowmode")); videoSettings.width = config.getIntValue("screenwidth"); videoSettings.height = config.getIntValue("screenheight"); + videoSettings.userScale = config.getIntValue("scale"); videoSettings.vsync = config.getBoolValue("vsync"); videoSettings.openGL = useOpenGL; @@ -348,9 +349,6 @@ Client::Client(const Options &options): } #endif - // Initialize for drawing - graphics->_beginDraw(); - Theme::prepareThemePath(); // Initialize the item and emote shortcuts. @@ -508,7 +506,8 @@ int Client::exec() case SDL_WINDOWEVENT: switch (event.window.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: - videoResized(event.window.data1, event.window.data2); + handleWindowSizeChanged(event.window.data1, + event.window.data2); break; } break; @@ -1322,17 +1321,25 @@ void Client::accountLogin(LoginData *loginData) config.setValue("remember", loginData->remember); } -void Client::videoResized(int width, int height) +void Client::handleWindowSizeChanged(int width, int height) { // Store the new size in the configuration. config.setValue("screenwidth", width); config.setValue("screenheight", height); - graphics->videoResized(width, height); + mVideo.windowSizeChanged(width, height); + + checkGraphicsSize(); +} + +void Client::checkGraphicsSize() +{ + const int width = graphics->getWidth(); + const int height = graphics->getHeight(); - // Logical size might be different from physical - width = graphics->getWidth(); - height = graphics->getHeight(); + const auto guiTop = gui->getTop(); + if (guiTop->getWidth() == width && guiTop->getHeight() == height) + return; gui->videoResized(width, height); diff --git a/src/client.h b/src/client.h index be591979..49780a38 100644 --- a/src/client.h +++ b/src/client.h @@ -213,7 +213,8 @@ public: * Should be called after the window has been resized. Makes sure the GUI * and game adapt to the new size. */ - void videoResized(int width, int height); + void handleWindowSizeChanged(int width, int height); + void checkGraphicsSize(); static bool isActive(); static bool hasInputFocus(); diff --git a/src/defaults.cpp b/src/defaults.cpp index 1830d394..3c03355a 100644 --- a/src/defaults.cpp +++ b/src/defaults.cpp @@ -80,6 +80,7 @@ DefaultsData* getConfigDefaults() AddDEF(configData, "windowmode", static_cast<int>(WindowMode::Windowed)); AddDEF(configData, "screenwidth", defaultScreenWidth); AddDEF(configData, "screenheight", defaultScreenHeight); + AddDEF(configData, "scale", 0); AddDEF(configData, "vsync", true); AddDEF(configData, "sound", false); AddDEF(configData, "sfxVolume", 100); diff --git a/src/game.cpp b/src/game.cpp index b7cc02fe..acf2d5a2 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -397,7 +397,8 @@ void Game::handleInput() if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { // Let the client deal with this one (it'll pass down from there) - Client::instance()->videoResized(event.window.data1, event.window.data2); + Client::instance()->handleWindowSizeChanged(event.window.data1, + event.window.data2); } // Keyboard events (for discontinuous keys) else if (event.type == SDL_KEYDOWN) diff --git a/src/graphics.cpp b/src/graphics.cpp index b5b30995..19f87e0c 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -33,20 +33,10 @@ void ImageRect::setAlpha(float alpha) } } -void Graphics::videoResized(int w, int h) +void Graphics::updateSize(int width, int height, float /*scale*/) { - mWidth = w; - mHeight = h; -} - -int Graphics::getWidth() const -{ - return mWidth; -} - -int Graphics::getHeight() const -{ - return mHeight; + mWidth = width; + mHeight = height; } bool Graphics::drawImage(Image *image, int x, int y) @@ -63,9 +53,6 @@ bool Graphics::drawImage(Image *image, int width, int height, bool useColor) { - if (!image) - return false; - return drawRescaledImage(image, srcX, srcY, dstX, dstY, @@ -133,3 +120,13 @@ void Graphics::drawImageRect(int x, int y, int w, int h, imgRect.grid[1], imgRect.grid[5], imgRect.grid[7], imgRect.grid[3], imgRect.grid[4]); } + +void Graphics::_beginDraw() +{ + pushClipArea(gcn::Rectangle(0, 0, mWidth, mHeight)); +} + +void Graphics::_endDraw() +{ + popClipArea(); +} diff --git a/src/graphics.h b/src/graphics.h index 956e729d..91bb01cf 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -80,7 +80,10 @@ class Graphics : public gcn::Graphics */ virtual void setVSync(bool sync) = 0; - virtual void videoResized(int w, int h); + /** + * Called when the window size or scale has changed. + */ + virtual void updateSize(int width, int height, float scale); using gcn::Graphics::drawImage; @@ -161,14 +164,24 @@ class Graphics : public gcn::Graphics virtual void updateScreen() = 0; /** - * Returns the width of the screen. + * Returns the logical width of the screen. + */ + int getWidth() const { return mWidth; } + + /** + * Returns the logical height of the screen. */ - int getWidth() const; + int getHeight() const { return mHeight; } /** - * Returns the height of the screen. + * Converts a window coordinate to a logical coordinate. Used for + * converting mouse coordinates. */ - int getHeight() const; + virtual void windowToLogical(int windowX, int windowY, + float &logicalX, float &logicalY) const = 0; + + void _beginDraw() override; + void _endDraw() override; /** * Takes a screenshot and returns it as SDL surface. diff --git a/src/gui/equipmentwindow.cpp b/src/gui/equipmentwindow.cpp index 45f3dcd1..e6230aed 100644 --- a/src/gui/equipmentwindow.cpp +++ b/src/gui/equipmentwindow.cpp @@ -246,9 +246,6 @@ void EquipmentWindow::mouseMoved(gcn::MouseEvent &event) const int x = event.getX(); const int y = event.getY(); - int mouseX, mouseY; - SDL_GetMouseState(&mouseX, &mouseY); - // Show ItemTooltip std::string slotName = getSlotName(x, y); if (!slotName.empty()) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 2d470a3e..62350f99 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -193,11 +193,18 @@ void Gui::logic() void Gui::draw() { - mGraphics->pushClipArea(getTop()->getDimension()); - getTop()->draw(mGraphics); + mGraphics->_beginDraw(); - int mouseX, mouseY; + mGraphics->pushClipArea(mTop->getDimension()); + mTop->draw(mGraphics); + mGraphics->popClipArea(); + + int mouseX; + int mouseY; Uint8 button = SDL_GetMouseState(&mouseX, &mouseY); + float logicalX; + float logicalY; + graphics->windowToLogical(mouseX, mouseY, logicalX, logicalY); if ((Client::hasMouseFocus() || button & SDL_BUTTON(1)) && mCustomCursor @@ -208,11 +215,11 @@ void Gui::draw() static_cast<Graphics*>(mGraphics)->drawImage( mouseCursor, - mouseX - 15, - mouseY - 17); + logicalX - 15, + logicalY - 17); } - mGraphics->popClipArea(); + mGraphics->_endDraw(); } void Gui::videoResized(int width, int height) @@ -221,6 +228,8 @@ void Gui::videoResized(int width, int height) int oldWidth = top->getWidth(); int oldHeight = top->getHeight(); + if (oldWidth == width && oldHeight == height) + return; top->setSize(width, height); top->adjustAfterResize(oldWidth, oldHeight); diff --git a/src/gui/sdlinput.cpp b/src/gui/sdlinput.cpp index 8396418c..3c8e3ecc 100644 --- a/src/gui/sdlinput.cpp +++ b/src/gui/sdlinput.cpp @@ -57,6 +57,7 @@ */ #include "sdlinput.h" +#include "graphics.h" #include <guichan/exception.hpp> @@ -119,6 +120,15 @@ TextInput SDLInput::dequeueTextInput() return textInput; } +static void setMouseCoordinates(gcn::MouseInput &mouseInput, int x, int y) +{ + float logicalX; + float logicalY; + graphics->windowToLogical(x, y, logicalX, logicalY); + mouseInput.setX(static_cast<int>(logicalX)); + mouseInput.setY(static_cast<int>(logicalY)); +} + void SDLInput::pushInput(SDL_Event event) { gcn::KeyInput keyInput; @@ -154,8 +164,7 @@ void SDLInput::pushInput(SDL_Event event) case SDL_MOUSEBUTTONDOWN: mMouseDown = true; - mouseInput.setX(event.button.x); - mouseInput.setY(event.button.y); + setMouseCoordinates(mouseInput, event.button.x, event.button.y); mouseInput.setButton(convertMouseButton(event.button.button)); mouseInput.setType(gcn::MouseInput::PRESSED); mouseInput.setTimeStamp(SDL_GetTicks()); @@ -164,8 +173,7 @@ void SDLInput::pushInput(SDL_Event event) case SDL_MOUSEBUTTONUP: mMouseDown = false; - mouseInput.setX(event.button.x); - mouseInput.setY(event.button.y); + setMouseCoordinates(mouseInput, event.button.x, event.button.y); mouseInput.setButton(convertMouseButton(event.button.button)); mouseInput.setType(gcn::MouseInput::RELEASED); mouseInput.setTimeStamp(SDL_GetTicks()); @@ -173,8 +181,7 @@ void SDLInput::pushInput(SDL_Event event) break; case SDL_MOUSEMOTION: - mouseInput.setX(event.button.x); - mouseInput.setY(event.button.y); + setMouseCoordinates(mouseInput, event.button.x, event.button.y); mouseInput.setButton(gcn::MouseInput::EMPTY); mouseInput.setType(gcn::MouseInput::MOVED); mouseInput.setTimeStamp(SDL_GetTicks()); @@ -184,13 +191,11 @@ void SDLInput::pushInput(SDL_Event event) case SDL_MOUSEWHEEL: if (event.wheel.y) { #if SDL_VERSION_ATLEAST(2, 26, 0) - mouseInput.setX(event.wheel.mouseX); - mouseInput.setY(event.wheel.mouseY); + setMouseCoordinates(mouseInput, event.wheel.mouseX, event.wheel.mouseY); #else int x, y; SDL_GetMouseState(&x, &y); - mouseInput.setX(x); - mouseInput.setY(y); + setMouseCoordinates(mouseInput, x, y); #endif mouseInput.setButton(gcn::MouseInput::EMPTY); mouseInput.setType(event.wheel.y > 0 ? gcn::MouseInput::WHEEL_MOVED_UP diff --git a/src/gui/setup_video.cpp b/src/gui/setup_video.cpp index 6f2f0d26..21f1935a 100644 --- a/src/gui/setup_video.cpp +++ b/src/gui/setup_video.cpp @@ -148,6 +148,40 @@ private: std::vector<DisplayMode> 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) { @@ -186,8 +220,10 @@ Setup_Video::Setup_Video(): 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)), @@ -232,9 +268,11 @@ Setup_Video::Setup_Video(): 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"); @@ -248,6 +286,7 @@ Setup_Video::Setup_Video(): // Set listeners mWindowModeDropDown->addActionListener(this); + mResolutionDropDown->addActionListener(this); mCustomCursorCheckBox->addActionListener(this); mOpenGLCheckBox->addActionListener(this); mParticleEffectsCheckBox->addActionListener(this); @@ -270,11 +309,13 @@ Setup_Video::Setup_Video(): place.getCell().setHAlign(LayoutCell::FILL); place(0, 0, new Label(_("Window mode:"))); - place(1, 0, mWindowModeDropDown, 2); + place(1, 0, mWindowModeDropDown, 2).setPadding(2); place(0, 1, new Label(_("Resolution:"))); - place(1, 1, mResolutionDropDown, 2); - place(0, 2, mVSyncCheckBox, 4); - place(0, 3, mOpenGLCheckBox, 4); + 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); @@ -304,24 +345,29 @@ void Setup_Video::apply() { // Video mode changes auto &video = Client::getVideo(); - auto videoSettings = video.settings(); + mVideoSettings = video.settings(); + + mVideoSettings.windowMode = static_cast<WindowMode>(mWindowModeDropDown->getSelected()); if (mResolutionDropDown->getSelected() > 0) { const auto &mode = mResolutionListModel->getModeAt(mResolutionDropDown->getSelected()); - videoSettings.width = mode.width; - videoSettings.height = mode.height; + mVideoSettings.width = mode.width; + mVideoSettings.height = mode.height; } - videoSettings.windowMode = static_cast<WindowMode>(mWindowModeDropDown->getSelected()); - videoSettings.vsync = mVSyncCheckBox->isSelected(); + mVideoSettings.userScale = std::max(0, mScaleDropDown->getSelected()); + mVideoSettings.vsync = mVSyncCheckBox->isSelected(); - if (video.apply(videoSettings)) + if (video.apply(mVideoSettings)) { - config.setValue("windowmode", static_cast<int>(videoSettings.windowMode)); - config.setValue("vsync", videoSettings.vsync); - config.setValue("screenwidth", videoSettings.width); - config.setValue("screenheight", videoSettings.height); + config.setValue("windowmode", static_cast<int>(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 { @@ -387,9 +433,11 @@ void Setup_Video::apply() void Setup_Video::cancel() { // Set back to the current video mode. + mVideoSettings = Client::getVideo().settings(); + mWindowModeDropDown->setSelected(static_cast<int>(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); @@ -416,7 +464,7 @@ void Setup_Video::action(const gcn::ActionEvent &event) { const std::string &id = event.getId(); - if (id == "windowmode") + if (id == "windowmode" || id == "resolution") { auto windowMode = static_cast<WindowMode>(mWindowModeDropDown->getSelected()); @@ -433,6 +481,12 @@ void Setup_Video::action(const gcn::ActionEvent &event) { mResolutionDropDown->setEnabled(true); } + + refreshScaleList(); + } + else if (id == "resolution") + { + refreshScaleList(); } else if (id == "customcursor") { @@ -488,3 +542,23 @@ void Setup_Video::action(const gcn::ActionEvent &event) } } } + +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); +} diff --git a/src/gui/setup_video.h b/src/gui/setup_video.h index d16f6d73..0914f6b1 100644 --- a/src/gui/setup_video.h +++ b/src/gui/setup_video.h @@ -31,6 +31,7 @@ #include <guichan/keylistener.hpp> class ResolutionListModel; +class ScaleListModel; class Setup_Video : public SetupTab, public gcn::ActionListener, public gcn::KeyListener @@ -45,6 +46,8 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, void action(const gcn::ActionEvent &event) override; private: + void refreshScaleList(); + VideoSettings mVideoSettings; bool mCustomCursorEnabled; bool mParticleEffectsEnabled; @@ -53,6 +56,7 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, std::unique_ptr<gcn::ListModel> mWindowModeListModel; std::unique_ptr<ResolutionListModel> mResolutionListModel; + std::unique_ptr<ScaleListModel> mScaleListModel; //gcn::Label *scrollRadiusLabel; //gcn::Label *scrollLazinessLabel; @@ -61,6 +65,7 @@ class Setup_Video : public SetupTab, public gcn::ActionListener, gcn::DropDown *mWindowModeDropDown; gcn::DropDown *mResolutionDropDown; + gcn::DropDown *mScaleDropDown; gcn::CheckBox *mVSyncCheckBox; gcn::CheckBox *mOpenGLCheckBox; gcn::CheckBox *mCustomCursorCheckBox; diff --git a/src/gui/viewport.cpp b/src/gui/viewport.cpp index a31a65f6..b71e6530 100644 --- a/src/gui/viewport.cpp +++ b/src/gui/viewport.cpp @@ -39,14 +39,10 @@ #include "net/net.h" #include "net/playerhandler.h" -#include "resources/resourcemanager.h" - #include "utils/stringutils.h" #include <cmath> -extern volatile int tick_time; - Viewport::Viewport() { setOpaque(false); @@ -81,8 +77,6 @@ void Viewport::setMap(Map *map) mMap = map; } -extern MiniStatusWindow *miniStatusWindow; - void Viewport::draw(gcn::Graphics *gcnGraphics) { static int lastTick = tick_time; @@ -291,7 +285,13 @@ void Viewport::logic() void Viewport::_followMouse() { - Uint8 button = SDL_GetMouseState(&mMouseX, &mMouseY); + const Uint8 button = SDL_GetMouseState(&mMouseX, &mMouseY); + float logicalX; + float logicalY; + graphics->windowToLogical(mMouseX, mMouseY, logicalX, logicalY); + mMouseX = static_cast<int>(logicalX); + mMouseY = static_cast<int>(logicalY); + // If the left button is dragged if (mPlayerFollowMouse && button & SDL_BUTTON(1)) { @@ -459,7 +459,8 @@ void Viewport::mousePressed(gcn::MouseEvent &event) mPopupMenu->showPopup(event.getX(), event.getY(), mHoverBeing); return; } - else if (mHoverItem) + + if (mHoverItem) { mPopupMenu->showPopup(event.getX(), event.getY(), mHoverItem); return; @@ -541,7 +542,7 @@ void Viewport::mouseDragged(gcn::MouseEvent &event) { mLocalWalkTime = tick_time; local_player->setDestination(event.getX() + (int) mPixelViewX, - event.getY() + (int) mPixelViewY); + event.getY() + (int) mPixelViewY); local_player->pathSetByMouse(); } } diff --git a/src/openglgraphics.cpp b/src/openglgraphics.cpp index 3cc1b9e3..ea37bdaa 100644 --- a/src/openglgraphics.cpp +++ b/src/openglgraphics.cpp @@ -19,14 +19,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#ifdef USE_OPENGL + #include "openglgraphics.h" #include "log.h" +#include "video.h" #include "resources/image.h" -#ifdef USE_OPENGL - #ifdef __APPLE__ #include <OpenGL/OpenGL.h> #endif @@ -42,6 +43,19 @@ const unsigned int vertexBufSize = 500; GLuint OpenGLGraphics::mLastImage = 0; +std::unique_ptr<OpenGLGraphics> OpenGLGraphics::create(SDL_Window *window, + const VideoSettings &settings) +{ + SDL_GLContext glContext = SDL_GL_CreateContext(window); + if (!glContext) + return {}; + + if (settings.vsync) + SDL_GL_SetSwapInterval(1); + + return std::make_unique<OpenGLGraphics>(window, glContext); +} + OpenGLGraphics::OpenGLGraphics(SDL_Window *window, SDL_GLContext glContext) : mWindow(window) , mContext(glContext) @@ -77,6 +91,23 @@ OpenGLGraphics::OpenGLGraphics(SDL_Window *window, SDL_GLContext glContext) Image::mTextureSize = texSize; logger->log("OpenGL texture size: %d pixels%s", Image::mTextureSize, rectTex ? " (rectangle textures)" : ""); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, (double)mWidth, (double)mHeight, 0.0, -1.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glEnable(GL_SCISSOR_TEST); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); } OpenGLGraphics::~OpenGLGraphics() @@ -98,15 +129,28 @@ void OpenGLGraphics::setReduceInputLag(bool reduceInputLag) mReduceInputLag = reduceInputLag; } -void OpenGLGraphics::videoResized(int width, int height) +void OpenGLGraphics::updateSize(int windowWidth, int windowHeight, float scale) { - _endDraw(); + mScale = scale; - SDL_GL_GetDrawableSize(mWindow, &mWidth, &mHeight); + int drawableWidth; + int drawableHeight; + SDL_GL_GetDrawableSize(mWindow, &drawableWidth, &drawableHeight); - glViewport(0, 0, mWidth, mHeight); + glViewport(0, 0, drawableWidth, drawableHeight); + + float displayScaleX = windowWidth > 0 ? static_cast<float>(drawableWidth) / windowWidth : 1.0f; + float displayScaleY = windowHeight > 0 ? static_cast<float>(drawableHeight) / windowHeight : 1.0f; + + mScaleX = mScale * displayScaleX; + mScaleY = mScale * displayScaleY; + + mWidth = std::ceil(drawableWidth / mScaleX); + mHeight = std::ceil(drawableHeight / mScaleY); - _beginDraw(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, (double)mWidth, (double)mHeight, 0.0, -1.0, 1.0); } static inline void drawQuad(Image *image, @@ -596,32 +640,11 @@ void OpenGLGraphics::updateScreen() glFinish(); } -void OpenGLGraphics::_beginDraw() -{ - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - glOrtho(0.0, (double)mWidth, (double)mHeight, 0.0, -1.0, 1.0); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glEnable(GL_SCISSOR_TEST); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - pushClipArea(gcn::Rectangle(0, 0, mWidth, mHeight)); -} - -void OpenGLGraphics::_endDraw() +void OpenGLGraphics::windowToLogical(int windowX, int windowY, + float &logicalX, float &logicalY) const { - popClipArea(); + logicalX = windowX / mScale; + logicalY = windowY / mScale; } SDL_Surface *OpenGLGraphics::getScreenshot() @@ -685,10 +708,14 @@ bool OpenGLGraphics::pushClipArea(gcn::Rectangle area) glPushMatrix(); glTranslatef(transX, transY, 0); - glScissor(mClipStack.top().x, - mHeight - mClipStack.top().y - mClipStack.top().height, - mClipStack.top().width, - mClipStack.top().height); + + int x = (int) (mClipStack.top().x * mScaleX); + int y = (int) ((mHeight - mClipStack.top().y - + mClipStack.top().height) * mScaleY); + int width = (int) (mClipStack.top().width * mScaleX); + int height = (int) (mClipStack.top().height * mScaleY); + + glScissor(x, y, width, height); return result; } @@ -697,14 +724,18 @@ void OpenGLGraphics::popClipArea() { Graphics::popClipArea(); + glPopMatrix(); + if (mClipStack.empty()) return; - glPopMatrix(); - glScissor(mClipStack.top().x, - mHeight - mClipStack.top().y - mClipStack.top().height, - mClipStack.top().width, - mClipStack.top().height); + int x = (int) (mClipStack.top().x * mScaleX); + int y = (int) ((mHeight - mClipStack.top().y - + mClipStack.top().height) * mScaleY); + int width = (int) (mClipStack.top().width * mScaleX); + int height = (int) (mClipStack.top().height * mScaleY); + + glScissor(x, y, width, height); } void OpenGLGraphics::setColor(const gcn::Color& color) diff --git a/src/openglgraphics.h b/src/openglgraphics.h index 0bb07363..ab08d075 100644 --- a/src/openglgraphics.h +++ b/src/openglgraphics.h @@ -22,16 +22,23 @@ #ifndef OPENGLGRAPHICS_H #define OPENGLGRAPHICS_H +#ifdef USE_OPENGL #include "graphics.h" -#ifdef USE_OPENGL #define NO_SDL_GLEXT #include <SDL_opengl.h> +#include <memory> + +class VideoSettings; + class OpenGLGraphics final : public Graphics { public: + static std::unique_ptr<OpenGLGraphics> create(SDL_Window *window, + const VideoSettings &settings); + OpenGLGraphics(SDL_Window *window, SDL_GLContext glContext); ~OpenGLGraphics() override; @@ -48,7 +55,7 @@ class OpenGLGraphics final : public Graphics void setReduceInputLag(bool reduceInputLag); bool getReduceInputLag() const { return mReduceInputLag; } - void videoResized(int w, int h) override; + void updateSize(int windowWidth, int windowHeight, float scale) override; bool drawImage(Image *image, int srcX, int srcY, @@ -78,8 +85,8 @@ class OpenGLGraphics final : public Graphics void updateScreen() override; - void _beginDraw() override; - void _endDraw() override; + void windowToLogical(int windowX, int windowY, + float &logicalX, float &logicalY) const override; bool pushClipArea(gcn::Rectangle area) override; void popClipArea() override; @@ -118,6 +125,9 @@ class OpenGLGraphics final : public Graphics GLfloat *mFloatTexArray; GLint *mIntTexArray; GLint *mIntVertArray; + float mScale = 1.0f; + float mScaleX = 1.0f; + float mScaleY = 1.0f; bool mAlpha = false; bool mTexture = false; bool mColorAlpha = false; diff --git a/src/sdlgraphics.cpp b/src/sdlgraphics.cpp index 3f6e809b..8e77a64f 100644 --- a/src/sdlgraphics.cpp +++ b/src/sdlgraphics.cpp @@ -23,12 +23,30 @@ #include "log.h" #include "resources/image.h" +#include "utils/stringutils.h" +#include "video.h" #include <guichan/exception.hpp> -SDLGraphics::SDLGraphics(SDL_Window *window, SDL_Renderer *renderer) - : mWindow(window) - , mRenderer(renderer) +std::unique_ptr<Graphics> SDLGraphics::create(SDL_Window *window, const VideoSettings &settings) +{ + int rendererFlags = 0; + if (settings.vsync) + rendererFlags |= SDL_RENDERER_PRESENTVSYNC; + + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, rendererFlags); + if (!renderer) + { + logger->error(strprintf("Failed to create renderer: %s", + SDL_GetError())); + return {}; + } + + return std::make_unique<SDLGraphics>(renderer); +} + +SDLGraphics::SDLGraphics(SDL_Renderer *renderer) + : mRenderer(renderer) { Image::setRenderer(mRenderer); @@ -71,9 +89,20 @@ void SDLGraphics::setVSync(bool sync) #endif } -void SDLGraphics::videoResized(int w, int h) +void SDLGraphics::updateSize(int windowWidth, int windowHeight, float scale) { SDL_GetRendererOutputSize(mRenderer, &mWidth, &mHeight); + + float displayScaleX = windowWidth > 0 ? static_cast<float>(mWidth) / windowWidth : 1.0f; + float displayScaleY = windowHeight > 0 ? static_cast<float>(mHeight) / windowHeight : 1.0f; + + float scaleX = scale * displayScaleX; + float scaleY = scale * displayScaleY; + + mWidth = std::ceil(mWidth / scaleX); + mHeight = std::ceil(mHeight / scaleY); + + SDL_RenderSetScale(mRenderer, scaleX, scaleY); } bool SDLGraphics::drawRescaledImage(Image *image, @@ -148,6 +177,20 @@ void SDLGraphics::updateScreen() SDL_RenderPresent(mRenderer); } +void SDLGraphics::windowToLogical(int windowX, int windowY, + float &logicalX, float &logicalY) const +{ +#if SDL_VERSION_ATLEAST(2, 0, 18) + SDL_RenderWindowToLogical(mRenderer, windowX, windowY, &logicalX, &logicalY); +#else + float scaleX; + float scaleY; + SDL_RenderGetScale(mRenderer, &scaleX, &scaleY); + logicalX = windowX / scaleX; + logicalY = windowY / scaleY; +#endif +} + SDL_Surface *SDLGraphics::getScreenshot() { #if SDL_BYTEORDER == SDL_BIG_ENDIAN diff --git a/src/sdlgraphics.h b/src/sdlgraphics.h index 6685fc83..ab6407f0 100644 --- a/src/sdlgraphics.h +++ b/src/sdlgraphics.h @@ -24,15 +24,22 @@ #include "graphics.h" +#include <memory> + +class VideoSettings; + class SDLGraphics final : public Graphics { public: - SDLGraphics(SDL_Window *window, SDL_Renderer *renderer); + static std::unique_ptr<Graphics> create(SDL_Window *window, + const VideoSettings &settings); + + SDLGraphics(SDL_Renderer *renderer); ~SDLGraphics() override; void setVSync(bool sync) override; - void videoResized(int w, int h) override; + void updateSize(int windowWidth, int windowHeight, float scale) override; bool drawRescaledImage(Image *image, int srcX, int srcY, @@ -49,6 +56,9 @@ public: void updateScreen() override; + void windowToLogical(int windowX, int windowY, + float &logicalX, float &logicalY) const override; + SDL_Surface *getScreenshot() override; bool pushClipArea(gcn::Rectangle area) override; @@ -66,7 +76,6 @@ public: private: void updateSDLClipRect(); - SDL_Window *mWindow = nullptr; SDL_Renderer *mRenderer = nullptr; }; diff --git a/src/video.cpp b/src/video.cpp index 9e69ec27..7ab21d12 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -31,6 +31,26 @@ #include <algorithm> +int VideoSettings::scale() const +{ + if (userScale == 0) + return autoScale(); + + return std::clamp(userScale, 1, maxScale()); +} + +int VideoSettings::autoScale() const +{ + // Automatic scaling factor based on at least 800x600 logical resolution + return std::max(1, std::min(width / 800, height / 600)); +} + +int VideoSettings::maxScale() const +{ + // Logical resolution needs to stay at least 640x480 + return std::max(1, std::min(width / 640, height / 480)); +} + Video::~Video() { mGraphics.reset(); // reset graphics first @@ -66,7 +86,7 @@ Graphics *Video::initialize(const VideoSettings &settings) } } - int windowFlags = SDL_WINDOW_RESIZABLE; + int windowFlags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; const char *videoMode = "windowed"; switch (mSettings.windowMode) @@ -115,40 +135,27 @@ Graphics *Video::initialize(const VideoSettings &settings) } } + // Make sure the resolution is reflected in current settings + SDL_GetWindowSize(mWindow, &mSettings.width, &mSettings.height); + #ifdef USE_OPENGL if (mSettings.openGL) { - SDL_GLContext glContext = SDL_GL_CreateContext(mWindow); - if (!glContext) + mGraphics = OpenGLGraphics::create(mWindow, mSettings); + if (!mGraphics) { logger->log("Failed to create OpenGL context, falling back to SDL renderer: %s", SDL_GetError()); mSettings.openGL = false; } - else - { - if (mSettings.vsync) - SDL_GL_SetSwapInterval(1); - - mGraphics = std::make_unique<OpenGLGraphics>(mWindow, glContext); - return mGraphics.get(); - } } #endif - int rendererFlags = 0; - if (settings.vsync) - rendererFlags |= SDL_RENDERER_PRESENTVSYNC; + if (!mGraphics) + mGraphics = SDLGraphics::create(mWindow, mSettings); - SDL_Renderer *renderer = SDL_CreateRenderer(mWindow, -1, rendererFlags); - if (!renderer) - { - logger->error(strprintf("Failed to create renderer: %s", - SDL_GetError())); - return nullptr; - } + mGraphics->updateSize(mSettings.width, mSettings.height, mSettings.scale()); - mGraphics = std::make_unique<SDLGraphics>(mWindow, renderer); return mGraphics.get(); } @@ -206,7 +213,9 @@ bool Video::apply(const VideoSettings &settings) return false; } - if (settings.windowMode == WindowMode::Windowed) { + if (settings.windowMode == WindowMode::Windowed && + (settings.width != mSettings.width || + settings.height != mSettings.height)) { #ifdef __APPLE__ // Workaround SDL2 issue when setting the window size on a window // which the user has put in fullscreen. Unfortunately, this mode can't @@ -216,16 +225,24 @@ bool Video::apply(const VideoSettings &settings) SDL_SetWindowSize(mWindow, settings.width, settings.height); } - mGraphics->setVSync(settings.vsync); - mSettings = settings; - // Make sure the resolution is reflected in current settings SDL_GetWindowSize(mWindow, &mSettings.width, &mSettings.height); + mGraphics->setVSync(mSettings.vsync); + mGraphics->updateSize(mSettings.width, mSettings.height, mSettings.scale()); + return true; } +void Video::windowSizeChanged(int width, int height) +{ + mSettings.width = width; + mSettings.height = height; + + mGraphics->updateSize(width, height, mSettings.scale()); +} + bool Video::initDisplayModes() { const int displayIndex = mSettings.display; diff --git a/src/video.h b/src/video.h index df9cd518..c98434e6 100644 --- a/src/video.h +++ b/src/video.h @@ -49,15 +49,21 @@ struct VideoSettings int width = defaultScreenWidth; int height = defaultScreenHeight; int display = 0; + int userScale = 0; bool vsync = true; bool openGL = false; + int scale() const; + int autoScale() const; + int maxScale() const; + bool operator==(const VideoSettings &other) const { return width == other.width && height == other.height && windowMode == other.windowMode && display == other.display && + userScale == other.userScale && vsync == other.vsync && openGL == other.openGL; } @@ -83,6 +89,11 @@ public: */ bool apply(const VideoSettings &settings); + /** + * Handle a change in window size, possibly adjusting the scale. + */ + void windowSizeChanged(int width, int height); + const DisplayMode &desktopDisplayMode() const { return mDesktopDisplayMode; |