summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client.cpp25
-rw-r--r--src/client.h3
-rw-r--r--src/defaults.cpp1
-rw-r--r--src/game.cpp3
-rw-r--r--src/graphics.cpp29
-rw-r--r--src/graphics.h23
-rw-r--r--src/gui/equipmentwindow.cpp3
-rw-r--r--src/gui/gui.cpp21
-rw-r--r--src/gui/sdlinput.cpp25
-rw-r--r--src/gui/setup_video.cpp106
-rw-r--r--src/gui/setup_video.h5
-rw-r--r--src/gui/viewport.cpp19
-rw-r--r--src/openglgraphics.cpp113
-rw-r--r--src/openglgraphics.h18
-rw-r--r--src/sdlgraphics.cpp51
-rw-r--r--src/sdlgraphics.h15
-rw-r--r--src/video.cpp69
-rw-r--r--src/video.h11
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;