summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt12
-rw-r--r--src/client.cpp89
-rw-r--r--src/client.h14
-rw-r--r--src/configuration.cpp5
-rw-r--r--src/defaults.cpp5
-rw-r--r--src/game.cpp4
-rw-r--r--src/graphics.cpp341
-rw-r--r--src/graphics.h114
-rw-r--r--src/gui/gui.cpp4
-rw-r--r--src/gui/gui.h2
-rw-r--r--src/gui/setup_video.cpp340
-rw-r--r--src/gui/setup_video.h18
-rw-r--r--src/openglgraphics.cpp133
-rw-r--r--src/openglgraphics.h22
-rw-r--r--src/resources/ambientlayer.cpp1
-rw-r--r--src/resources/image.h2
-rw-r--r--src/sdlgraphics.cpp304
-rw-r--r--src/sdlgraphics.h73
-rw-r--r--src/video.cpp263
-rw-r--r--src/video.h106
20 files changed, 1068 insertions, 784 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 628fab82..a138578b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -419,14 +419,14 @@ SET(SRCS
avatar.h
being.cpp
being.h
- chatlogger.cpp
- chatlogger.h
- client.cpp
- client.h
channel.cpp
channel.h
channelmanager.cpp
channelmanager.h
+ chatlogger.cpp
+ chatlogger.h
+ client.cpp
+ client.h
commandhandler.cpp
commandhandler.h
compoundsprite.cpp
@@ -495,6 +495,8 @@ SET(SRCS
properties.h
rotationalparticle.cpp
rotationalparticle.h
+ sdlgraphics.cpp
+ sdlgraphics.h
shopitem.cpp
shopitem.h
simpleanimation.cpp
@@ -517,6 +519,8 @@ SET(SRCS
variabledata.h
vector.cpp
vector.h
+ video.cpp
+ video.h
)
SET(SRCS_TMWA
diff --git a/src/client.cpp b/src/client.cpp
index 506ad692..c337420d 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -294,33 +294,19 @@ Client::Client(const Options &options):
if (!useOpenGL && (config.getValue("disableTransparency", 0) == 1))
Image::SDLdisableTransparency();
-#ifdef USE_OPENGL
- // Setup image loading for the right image format
- Image::setLoadAsOpenGL(useOpenGL);
-
- // Create the graphics context
- graphics = useOpenGL ? new OpenGLGraphics : new Graphics;
-#else
- // Create the graphics context
- graphics = new Graphics;
-#endif
+ VideoSettings videoSettings;
+ videoSettings.windowMode = static_cast<WindowMode>(config.getIntValue("windowmode"));
+ videoSettings.width = config.getIntValue("screenwidth");
+ videoSettings.height = config.getIntValue("screenheight");
+ videoSettings.vsync = config.getBoolValue("vsync");
+ videoSettings.openGL = useOpenGL;
- const int width = config.getIntValue("screenwidth");
- const int height = config.getIntValue("screenheight");
- const bool fullscreen = config.getBoolValue("screen");
-
- // Try to set the desired video mode
- if (!graphics->setVideoMode(width, height, fullscreen))
- {
- logger->error(strprintf("Couldn't set %dx%d video mode: %s",
- width, height, SDL_GetError()));
- }
+ // Try to set the desired video mode and create the graphics context
+ graphics = mVideo.initialize(videoSettings);
- SDL_SetWindowTitle(graphics->getTarget(),
+ SDL_SetWindowTitle(mVideo.window(),
branding.getValue("appName", "Mana").c_str());
- Image::setRenderer(graphics->getRenderer());
-
std::string iconFile = branding.getValue("appIcon", "icons/mana");
#ifdef _WIN32
iconFile += ".ico";
@@ -346,7 +332,7 @@ Client::Client(const Options &options):
mIcon = IMG_Load(iconFile.c_str());
if (mIcon)
{
- SDL_SetWindowIcon(graphics->getTarget(), mIcon);
+ SDL_SetWindowIcon(mVideo.window(), mIcon);
}
#endif
@@ -398,11 +384,11 @@ Client::Client(const Options &options):
loginData.registerLogin = false;
if (mCurrentServer.hostname.empty())
- mCurrentServer.hostname = branding.getValue("defaultServer","").c_str();
+ mCurrentServer.hostname = branding.getValue("defaultServer", std::string());
if (mCurrentServer.port == 0)
{
- mCurrentServer.port = (short) branding.getValue("defaultPort",
+ mCurrentServer.port = (unsigned short) branding.getValue("defaultPort",
DEFAULT_PORT);
mCurrentServer.type = ServerInfo::parseType(
branding.getValue("defaultServerType", "tmwathena"));
@@ -451,7 +437,6 @@ Client::~Client()
delete emoteShortcut;
delete gui;
- delete graphics;
// Shutdown libxml
xmlCleanupParser();
@@ -493,20 +478,20 @@ int Client::exec()
{
switch (event.type)
{
- case SDL_QUIT:
- mState = STATE_EXIT;
- break;
+ case SDL_QUIT:
+ mState = STATE_EXIT;
+ break;
- case SDL_KEYDOWN:
- break;
+ case SDL_KEYDOWN:
+ break;
- case SDL_WINDOWEVENT:
- switch (event.window.event) {
- case SDL_WINDOWEVENT_RESIZED:
- handleVideoResize(event.window.data1, event.window.data2);
- break;
- }
+ case SDL_WINDOWEVENT:
+ switch (event.window.event) {
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ videoResized(event.window.data1, event.window.data2);
break;
+ }
+ break;
}
guiInput->pushInput(event);
@@ -1031,7 +1016,6 @@ void Client::event(Event::Channel channel, const Event &event)
if (mLimitFps)
SDL_setFramerate(&mFpsManager, fpsLimit);
}
-
}
void Client::action(const gcn::ActionEvent &event)
@@ -1326,24 +1310,19 @@ void Client::accountLogin(LoginData *loginData)
config.setValue("remember", loginData->remember);
}
-void Client::handleVideoResize(int width, int height)
+void Client::videoResized(int width, int height)
{
- if (graphics->getWidth() == width && graphics->getHeight() == height)
- return;
+ // Store the new size in the configuration.
+ config.setValue("screenwidth", width);
+ config.setValue("screenheight", height);
graphics->videoResized(width, height);
- videoResized(width, height);
+ // Logical size might be different from physical
+ width = graphics->getWidth();
+ height = graphics->getHeight();
- // Since everything appears to have worked out, remember to store the
- // new size in the configuration.
- config.setValue("screenwidth", width);
- config.setValue("screenheight", height);
-}
-
-void Client::videoResized(int width, int height)
-{
- gui->videoResized();
+ gui->videoResized(width, height);
if (mDesktop)
mDesktop->setSize(width, height);
@@ -1357,15 +1336,15 @@ void Client::videoResized(int width, int height)
bool Client::isActive()
{
- return !(SDL_GetWindowFlags(graphics->getTarget()) & SDL_WINDOW_MINIMIZED);
+ return !(SDL_GetWindowFlags(getVideo().window()) & SDL_WINDOW_MINIMIZED);
}
bool Client::hasInputFocus()
{
- return SDL_GetWindowFlags(graphics->getTarget()) & SDL_WINDOW_INPUT_FOCUS;
+ return SDL_GetWindowFlags(getVideo().window()) & SDL_WINDOW_INPUT_FOCUS;
}
bool Client::hasMouseFocus()
{
- return SDL_GetWindowFlags(graphics->getTarget()) & SDL_WINDOW_MOUSE_FOCUS;
+ return SDL_GetWindowFlags(getVideo().window()) & SDL_WINDOW_MOUSE_FOCUS;
}
diff --git a/src/client.h b/src/client.h
index 09233a5c..fe202ccc 100644
--- a/src/client.h
+++ b/src/client.h
@@ -23,6 +23,7 @@
#define CLIENT_H
#include "eventlistener.h"
+#include "video.h"
#include "net/serverinfo.h"
@@ -191,17 +192,15 @@ public:
static const std::string &getScreenshotDirectory()
{ return instance()->mScreenshotDir; }
+ static Video &getVideo()
+ { return instance()->mVideo; }
+
void event(Event::Channel channel, const Event &event) override;
void action(const gcn::ActionEvent &event) override;
/**
- * Should be called after the window has been resized by the user.
- */
- void handleVideoResize(int width, int height);
-
- /**
- * Should be called after a succesful resize or change of resolution, makes
- * sure the GUI and game adapt to the new size.
+ * 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);
@@ -231,6 +230,7 @@ private:
std::string mRootDir;
ServerInfo mCurrentServer;
+ Video mVideo;
Game *mGame;
Window *mCurrentDialog;
diff --git a/src/configuration.cpp b/src/configuration.cpp
index f0972052..f7718bdc 100644
--- a/src/configuration.cpp
+++ b/src/configuration.cpp
@@ -102,10 +102,9 @@ void Configuration::cleanDefaults()
{
if (mDefaultsData)
{
- for (DefaultsData::const_iterator iter = mDefaultsData->begin();
- iter != mDefaultsData->end(); iter++)
+ for (auto &[_, variableData] : *mDefaultsData)
{
- delete iter->second;
+ delete variableData;
}
delete mDefaultsData;
mDefaultsData = nullptr;
diff --git a/src/defaults.cpp b/src/defaults.cpp
index 1c9cb7aa..8cf79590 100644
--- a/src/defaults.cpp
+++ b/src/defaults.cpp
@@ -79,9 +79,10 @@ DefaultsData* getConfigDefaults()
AddDEF(configData, "particleeffects", true);
AddDEF(configData, "logToStandardOut", false);
AddDEF(configData, "opengl", false);
+ AddDEF(configData, "windowmode", static_cast<int>(WindowMode::Windowed));
AddDEF(configData, "screenwidth", defaultScreenWidth);
AddDEF(configData, "screenheight", defaultScreenHeight);
- AddDEF(configData, "screen", false);
+ AddDEF(configData, "vsync", true);
AddDEF(configData, "sound", false);
AddDEF(configData, "sfxVolume", 100);
AddDEF(configData, "notificationsVolume", 100);
@@ -89,7 +90,7 @@ DefaultsData* getConfigDefaults()
AddDEF(configData, "remember", false);
AddDEF(configData, "username", "");
AddDEF(configData, "lastCharacter", "");
- AddDEF(configData, "fpslimit", 60);
+ AddDEF(configData, "fpslimit", 0);
AddDEF(configData, "updatehost", "");
AddDEF(configData, "screenshotDirectory", "");
AddDEF(configData, "useScreenshotDirectorySuffix", true);
diff --git a/src/game.cpp b/src/game.cpp
index ea8cbae3..e27e6f5d 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -396,10 +396,10 @@ void Game::handleInput()
{
bool used = false;
- if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED)
+ 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()->handleVideoResize(event.window.data1, event.window.data2);
+ Client::instance()->videoResized(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 02144cab..b5b30995 100644
--- a/src/graphics.cpp
+++ b/src/graphics.cpp
@@ -20,12 +20,9 @@
*/
#include "graphics.h"
-#include "log.h"
#include "resources/image.h"
-#include "utils/gettext.h"
-
#include <guichan/exception.hpp>
void ImageRect::setAlpha(float alpha)
@@ -36,104 +33,6 @@ void ImageRect::setAlpha(float alpha)
}
}
-
-Graphics::~Graphics()
-{
- _endDraw();
-}
-
-void Graphics::setTarget(SDL_Window *target)
-{
- _endDraw();
-
- mTarget = target;
-
- if (mTarget)
- _beginDraw();
-}
-
-bool Graphics::setVideoMode(int w, int h, bool fs)
-{
- logger->log("Setting video mode %dx%d %s",
- w, h, fs ? "fullscreen" : "windowed");
-
- int windowFlags = SDL_WINDOW_ALLOW_HIGHDPI;
-
- if (fs)
- windowFlags |= SDL_WINDOW_FULLSCREEN;
- else
- windowFlags |= SDL_WINDOW_RESIZABLE;
-
- // TODO_SDL2: Support SDL_WINDOW_FULLSCREEN_DESKTOP
-
- SDL_Window *window = nullptr;
- SDL_Renderer *renderer = nullptr;
- SDL_CreateWindowAndRenderer(w, h, windowFlags, &window, &renderer);
-
- if (!window)
- return false;
-
- SDL_SetWindowMinimumSize(window, 640, 480);
- SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
-
- setTarget(window);
-
- mRenderer = renderer;
- mWidth = w;
- mHeight = h;
- mFullscreen = fs;
-
- if (const char *driver = SDL_GetCurrentVideoDriver())
- logger->log("Using video driver: %s", driver);
- else
- logger->log("Using video driver: not initialized");
-
- SDL_RendererInfo info;
-
- if (SDL_GetRendererInfo(mRenderer, &info) == 0) {
- logger->log("Using renderer: %s", info.name);
-
- logger->log("The renderer is a software fallback: %s",
- (info.flags & SDL_RENDERER_SOFTWARE) ? "yes" : "no");
- logger->log("The renderer is hardware accelerated: %s",
- (info.flags & SDL_RENDERER_ACCELERATED) ? "yes" : "no");
- logger->log("Vsync: %s",
- (info.flags & SDL_RENDERER_PRESENTVSYNC) ? "on" : "off");
- logger->log("Renderer supports rendering to texture: %s",
- (info.flags & SDL_RENDERER_TARGETTEXTURE) ? "yes" : "no");
- logger->log("Max texture size: %dx%d",
- info.max_texture_width, info.max_texture_height);
- }
-
- return true;
-}
-
-bool Graphics::changeVideoMode(int w, int h, bool fs)
-{
- // Just return success if we're already in this mode
- if (mWidth == w &&
- mHeight == h &&
- mFullscreen == fs)
- return true;
-
- _endDraw();
-
- bool success = setVideoMode(w, h, fs);
-
- // If it didn't work, try to restore the previous mode. If that doesn't
- // work either, we're in big trouble and bail out.
- if (!success) {
- if (!setVideoMode(mWidth, mHeight, mFullscreen)) {
- logger->error(_("Failed to change video mode and couldn't "
- "switch back to the previous mode!"));
- }
- }
-
- _beginDraw();
-
- return success;
-}
-
void Graphics::videoResized(int w, int h)
{
mWidth = w;
@@ -155,39 +54,14 @@ bool Graphics::drawImage(Image *image, int x, int y)
if (!image)
return false;
- return drawImage(image, 0, 0, x, y, image->mBounds.w, image->mBounds.h);
-}
-
-bool Graphics::drawRescaledImage(Image *image, int srcX, int srcY,
- int dstX, int dstY,
- int width, int height,
- int desiredWidth, int desiredHeight,
- bool useColor)
-{
- // Check that preconditions for blitting are met.
- if (!mTarget || !image || !image->mTexture)
- return false;
-
- dstX += mClipStack.top().xOffset;
- dstY += mClipStack.top().yOffset;
-
- srcX += image->mBounds.x;
- srcY += image->mBounds.y;
-
- SDL_Rect dstRect;
- SDL_Rect srcRect;
- dstRect.x = dstX; dstRect.y = dstY;
- srcRect.x = srcX; srcRect.y = srcY;
- srcRect.w = width;
- srcRect.h = height;
- dstRect.w = desiredWidth;
- dstRect.h = desiredHeight;
-
- return !(SDL_RenderCopy(mRenderer, image->mTexture, &srcRect, &dstRect) < 0);
+ return drawImage(image, 0, 0, x, y, image->getWidth(), image->getHeight());
}
-bool Graphics::drawImage(Image *image, int srcX, int srcY, int dstX, int dstY,
- int width, int height, bool useColor)
+bool Graphics::drawImage(Image *image,
+ int srcX, int srcY,
+ int dstX, int dstY,
+ int width, int height,
+ bool useColor)
{
if (!image)
return false;
@@ -201,50 +75,11 @@ bool Graphics::drawImage(Image *image, int srcX, int srcY, int dstX, int dstY,
void Graphics::drawImagePattern(Image *image, int x, int y, int w, int h)
{
- // Check that preconditions for blitting are met.
if (!image)
return;
- const int iw = image->getWidth();
- const int ih = image->getHeight();
-
- drawRescaledImagePattern(image, x, y, w, h, iw, ih);
-}
-
-void Graphics::drawRescaledImagePattern(Image *image,
- int x, int y,
- int w, int h,
- int scaledWidth, int scaledHeight)
-{
- // Check that preconditions for blitting are met.
- if (!mTarget || !image || !image->mTexture)
- return;
-
- if (scaledHeight == 0 || scaledWidth == 0)
- return;
-
- for (int py = 0; py < h; py += scaledHeight) // Y position on pattern plane
- {
- int dh = (py + scaledHeight >= h) ? h - py : scaledHeight;
- int srcY = image->mBounds.y;
- int dstY = y + py + mClipStack.top().yOffset;
-
- for (int px = 0; px < w; px += scaledWidth) // X position on pattern plane
- {
- int dw = (px + scaledWidth >= w) ? w - px : scaledWidth;
- int srcX = image->mBounds.x;
- int dstX = x + px + mClipStack.top().xOffset;
-
- SDL_Rect dstRect;
- SDL_Rect srcRect;
- dstRect.x = dstX; dstRect.y = dstY;
- dstRect.w = dw; dstRect.h = dh;
- srcRect.x = srcX; srcRect.y = srcY;
- srcRect.w = dw; srcRect.h = dh;
-
- SDL_RenderCopy(mRenderer, image->mTexture, &srcRect, &dstRect);
- }
- }
+ drawRescaledImagePattern(image, x, y, w, h,
+ image->getWidth(), image->getHeight());
}
void Graphics::drawImageRect(int x, int y, int w, int h,
@@ -298,163 +133,3 @@ 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::updateScreen()
-{
- SDL_RenderPresent(mRenderer);
-}
-
-SDL_Surface *Graphics::getScreenshot()
-{
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- int rmask = 0xff000000;
- int gmask = 0x00ff0000;
- int bmask = 0x0000ff00;
-#else
- int rmask = 0x000000ff;
- int gmask = 0x0000ff00;
- int bmask = 0x00ff0000;
-#endif
- int amask = 0x00000000;
-
- SDL_Surface *screenshot = SDL_CreateRGBSurface(0, mWidth,
- mHeight, 24, rmask, gmask, bmask, amask);
-
- SDL_RenderReadPixels(mRenderer, nullptr,
- screenshot->format->format,
- screenshot->pixels,
- screenshot->pitch);
-
- return screenshot;
-}
-
-bool Graphics::pushClipArea(gcn::Rectangle area)
-{
- bool result = gcn::Graphics::pushClipArea(area);
- updateSDLClipRect();
- return result;
-}
-
-void Graphics::popClipArea()
-{
- gcn::Graphics::popClipArea();
- updateSDLClipRect();
-}
-
-void Graphics::updateSDLClipRect()
-{
- if (mClipStack.empty())
- {
- SDL_RenderSetClipRect(mRenderer, nullptr);
- return;
- }
-
- const gcn::ClipRectangle &carea = mClipStack.top();
- SDL_Rect rect;
- rect.x = carea.x;
- rect.y = carea.y;
- rect.w = carea.width;
- rect.h = carea.height;
-
- SDL_RenderSetClipRect(mRenderer, &rect);
-}
-
-void Graphics::drawPoint(int x, int y)
-{
- if (mClipStack.empty())
- {
- throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
- }
-
- const gcn::ClipRectangle &top = mClipStack.top();
-
- x += top.xOffset;
- y += top.yOffset;
-
- if (!top.isPointInRect(x, y))
- return;
-
- SDL_SetRenderDrawColor(mRenderer,
- (Uint8)(mColor.r),
- (Uint8)(mColor.g),
- (Uint8)(mColor.b),
- (Uint8)(mColor.a));
- SDL_RenderDrawPoint(mRenderer, x, y);
-}
-
-void Graphics::drawLine(int x1, int y1, int x2, int y2)
-{
- if (mClipStack.empty())
- {
- throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
- }
-
- const gcn::ClipRectangle &top = mClipStack.top();
-
- x1 += top.xOffset;
- y1 += top.yOffset;
- x2 += top.xOffset;
- y2 += top.yOffset;
-
- SDL_SetRenderDrawColor(mRenderer,
- (Uint8)(mColor.r),
- (Uint8)(mColor.g),
- (Uint8)(mColor.b),
- (Uint8)(mColor.a));
- SDL_RenderDrawLine(mRenderer, x1, y1, x2, y2);
-}
-
-void Graphics::drawRectangle(const gcn::Rectangle &rectangle)
-{
- if (mClipStack.empty())
- {
- throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
- }
-
- const gcn::ClipRectangle &top = mClipStack.top();
-
- SDL_Rect rect;
- rect.x = rectangle.x + top.xOffset;
- rect.y = rectangle.y + top.yOffset;
- rect.w = rectangle.width;
- rect.h = rectangle.height;
-
- SDL_SetRenderDrawColor(mRenderer,
- (Uint8)(mColor.r),
- (Uint8)(mColor.g),
- (Uint8)(mColor.b),
- (Uint8)(mColor.a));
- SDL_RenderDrawRect(mRenderer, &rect);
-}
-
-void Graphics::fillRectangle(const gcn::Rectangle &rectangle)
-{
- if (mClipStack.empty())
- {
- throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
- }
-
- const gcn::ClipRectangle &top = mClipStack.top();
-
- gcn::Rectangle area = rectangle;
- area.x += top.xOffset;
- area.y += top.yOffset;
-
- if(!area.isIntersecting(top))
- {
- return;
- }
-
- SDL_Rect rect;
- rect.x = area.x;
- rect.y = area.y;
- rect.w = area.width;
- rect.h = area.height;
-
- SDL_SetRenderDrawColor(mRenderer,
- (Uint8)(mColor.r),
- (Uint8)(mColor.g),
- (Uint8)(mColor.b),
- (Uint8)(mColor.a));
- SDL_RenderFillRect(mRenderer, &rect);
-}
diff --git a/src/graphics.h b/src/graphics.h
index 24483078..956e729d 100644
--- a/src/graphics.h
+++ b/src/graphics.h
@@ -28,10 +28,6 @@
#include <guichan/graphics.hpp>
class Image;
-class ImageRect;
-
-static const int defaultScreenWidth = 800;
-static const int defaultScreenHeight = 600;
/**
* 9 images defining a rectangle. 4 corners, 4 sides and a middle area. The
@@ -79,32 +75,15 @@ class Graphics : public gcn::Graphics
public:
Graphics() = default;
- ~Graphics() override;
-
- /**
- * Sets the target SDL_Window to draw to. This funtion also pushes a
- * clip areas corresponding to the dimension of the target.
- *
- * @param target the target to draw to.
- */
- virtual void setTarget(SDL_Window *target);
-
- SDL_Window *getTarget() const { return mTarget; }
- SDL_Renderer *getRenderer() const { return mRenderer; }
-
/**
- * Try to create a window with the given settings.
+ * Sets whether vertical refresh syncing is enabled.
*/
- virtual bool setVideoMode(int w, int h, bool fs);
-
- /**
- * Change the video mode. Can be used for switching to full screen,
- * changing resolution or adapting after window resize.
- */
- bool changeVideoMode(int w, int h, bool fs);
+ virtual void setVSync(bool sync) = 0;
virtual void videoResized(int w, int h);
+ using gcn::Graphics::drawImage;
+
/**
* Blits an image onto the screen.
*
@@ -114,26 +93,13 @@ class Graphics : public gcn::Graphics
bool drawImage(Image *image, int x, int y);
/**
- * Draws a resclaled version of the image
- */
- bool drawRescaledImage(Image *image, int srcX, int srcY,
- int dstX, int dstY,
- int width, int height,
- int desiredWidth, int desiredHeight)
- { return drawRescaledImage(image, srcX, srcY,
- dstX, dstY,
- width, height,
- desiredWidth, desiredHeight,
- false); }
-
- /**
- * Draws a resclaled version of the image
+ * Draws a rescaled version of the image.
*/
virtual bool drawRescaledImage(Image *image, int srcX, int srcY,
- int dstX, int dstY,
- int width, int height,
- int desiredWidth, int desiredHeight,
- bool useColor = false);
+ int dstX, int dstY,
+ int width, int height,
+ int desiredWidth, int desiredHeight,
+ bool useColor = false) = 0;
/**
* Blits an image onto the screen.
@@ -155,28 +121,28 @@ class Graphics : public gcn::Graphics
* Draw a pattern based on a rescaled version of the given image...
*/
virtual void drawRescaledImagePattern(Image *image,
- int x, int y, int w, int h,
- int scaledWidth, int scaledHeight);
+ int x, int y,
+ int w, int h,
+ int scaledWidth,
+ int scaledHeight) = 0;
/**
* Draws a rectangle using images. 4 corner images, 4 side images and 1
* image for the inside.
*/
- void drawImageRect(
- int x, int y, int w, int h,
- Image *topLeft, Image *topRight,
- Image *bottomLeft, Image *bottomRight,
- Image *top, Image *right,
- Image *bottom, Image *left,
- Image *center);
+ void drawImageRect(int x, int y, int w, int h,
+ Image *topLeft, Image *topRight,
+ Image *bottomLeft, Image *bottomRight,
+ Image *top, Image *right,
+ Image *bottom, Image *left,
+ Image *center);
/**
* Draws a rectangle using images. 4 corner images, 4 side images and 1
* image for the inside.
*/
- void drawImageRect(
- int x, int y, int w, int h,
- const ImageRect &imgRect);
+ void drawImageRect(int x, int y, int w, int h,
+ const ImageRect &imgRect);
/**
* Draws a rectangle using images. 4 corner images, 4 side images and 1
@@ -192,7 +158,7 @@ class Graphics : public gcn::Graphics
* Updates the screen. This is done by either copying the buffer to the
* screen or swapping pages.
*/
- virtual void updateScreen();
+ virtual void updateScreen() = 0;
/**
* Returns the width of the screen.
@@ -205,57 +171,31 @@ class Graphics : public gcn::Graphics
int getHeight() const;
/**
- * Returns whether we're in a full screen mode.
- */
- bool getFullscreen() const { return mFullscreen; }
-
- /**
* Takes a screenshot and returns it as SDL surface.
*/
- virtual SDL_Surface *getScreenshot();
+ virtual SDL_Surface *getScreenshot() = 0;
gcn::Font *getFont() const { return mFont; }
- bool pushClipArea(gcn::Rectangle area) override;
-
- void popClipArea() override;
-
void drawImage(const gcn::Image *image,
- int srcX,
- int srcY,
- int dstX,
- int dstY,
- int width,
- int height) override {} // not used
-
- void drawPoint(int x, int y) override;
-
- void drawLine(int x1, int y1, int x2, int y2) override;
-
- void drawRectangle(const gcn::Rectangle &rectangle) override;
-
- void fillRectangle(const gcn::Rectangle &rectangle) override;
+ int srcX, int srcY,
+ int dstX, int dstY,
+ int width, int height) override {} // not used
void setColor(const gcn::Color &color) override
{
mColor = color;
}
- const gcn::Color &getColor() const override
+ const gcn::Color &getColor() const final
{
return mColor;
}
protected:
- void updateSDLClipRect();
-
int mWidth = 0;
int mHeight = 0;
- bool mFullscreen = false;
gcn::Color mColor;
-
- SDL_Window *mTarget = nullptr;
- SDL_Renderer *mRenderer = nullptr;
};
extern Graphics *graphics;
diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp
index fd8f38ea..17f4897d 100644
--- a/src/gui/gui.cpp
+++ b/src/gui/gui.cpp
@@ -220,14 +220,14 @@ void Gui::draw()
mGraphics->popClipArea();
}
-void Gui::videoResized()
+void Gui::videoResized(int width, int height)
{
auto *top = static_cast<WindowContainer*>(getTop());
int oldWidth = top->getWidth();
int oldHeight = top->getHeight();
- top->setSize(graphics->getWidth(), graphics->getHeight());
+ top->setSize(width, height);
top->adjustAfterResize(oldWidth, oldHeight);
}
diff --git a/src/gui/gui.h b/src/gui/gui.h
index c70ed9ad..eea3f23d 100644
--- a/src/gui/gui.h
+++ b/src/gui/gui.h
@@ -67,7 +67,7 @@ class Gui : public gcn::Gui
/**
* Called when the application window has been resized.
*/
- void videoResized();
+ void videoResized(int width, int height);
gcn::FocusHandler *getFocusHandler() const
{ return mFocusHandler; }
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());
}
diff --git a/src/gui/setup_video.h b/src/gui/setup_video.h
index 6c902bb7..d16f6d73 100644
--- a/src/gui/setup_video.h
+++ b/src/gui/setup_video.h
@@ -25,11 +25,12 @@
#include "guichanfwd.h"
#include "gui/widgets/setuptab.h"
+#include "video.h"
#include <guichan/actionlistener.hpp>
#include <guichan/keylistener.hpp>
-class ModeListModel;
+class ResolutionListModel;
class Setup_Video : public SetupTab, public gcn::ActionListener,
public gcn::KeyListener
@@ -43,27 +44,24 @@ class Setup_Video : public SetupTab, public gcn::ActionListener,
void action(const gcn::ActionEvent &event) override;
- static const char *overlayDetailToString(int detail = -1);
-
- static const char *particleDetailToString(int detail = -1);
-
private:
- bool mFullScreenEnabled;
- bool mOpenGLEnabled;
+ VideoSettings mVideoSettings;
bool mCustomCursorEnabled;
bool mParticleEffectsEnabled;
int mFps;
bool mSDLTransparencyDisabled;
- ModeListModel *mModeListModel;
+ std::unique_ptr<gcn::ListModel> mWindowModeListModel;
+ std::unique_ptr<ResolutionListModel> mResolutionListModel;
//gcn::Label *scrollRadiusLabel;
//gcn::Label *scrollLazinessLabel;
gcn::Label *overlayDetailLabel;
gcn::Label *particleDetailLabel;
- gcn::ListBox *mModeList;
- gcn::CheckBox *mFsCheckBox;
+ gcn::DropDown *mWindowModeDropDown;
+ gcn::DropDown *mResolutionDropDown;
+ gcn::CheckBox *mVSyncCheckBox;
gcn::CheckBox *mOpenGLCheckBox;
gcn::CheckBox *mCustomCursorCheckBox;
gcn::CheckBox *mParticleEffectsCheckBox;
diff --git a/src/openglgraphics.cpp b/src/openglgraphics.cpp
index 008f1daa..2f83eea7 100644
--- a/src/openglgraphics.cpp
+++ b/src/openglgraphics.cpp
@@ -42,98 +42,21 @@ const unsigned int vertexBufSize = 500;
GLuint OpenGLGraphics::mLastImage = 0;
-OpenGLGraphics::OpenGLGraphics():
- mAlpha(false), mTexture(false), mColorAlpha(false),
- mSync(false),
- mReduceInputLag(true)
+OpenGLGraphics::OpenGLGraphics(SDL_Window *window, SDL_GLContext glContext)
+ : mWindow(window)
+ , mContext(glContext)
{
+ Image::setLoadAsOpenGL(true);
+
mFloatTexArray = new GLfloat[vertexBufSize * 4];
mIntTexArray = new GLint[vertexBufSize * 4];
mIntVertArray = new GLint[vertexBufSize * 4];
-}
-
-OpenGLGraphics::~OpenGLGraphics()
-{
- delete[] mFloatTexArray;
- delete[] mIntTexArray;
- delete[] mIntVertArray;
-}
-
-void OpenGLGraphics::setSync(bool sync)
-{
- if (mSync == sync)
- return;
-
- mSync = sync;
-
- if (mContext)
- SDL_GL_SetSwapInterval(sync ? 1 : 0);
-}
-
-void OpenGLGraphics::setReduceInputLag(bool reduceInputLag)
-{
- mReduceInputLag = reduceInputLag;
-}
-
-bool OpenGLGraphics::setVideoMode(int w, int h, bool fs)
-{
- logger->log("Setting video mode %dx%d %s",
- w, h, fs ? "fullscreen" : "windowed");
-
- // TODO_SDL2: Support SDL_WINDOW_ALLOW_HIGHDPI, but check handling of clip area
-
- int windowFlags = SDL_WINDOW_OPENGL;
-
- if (fs)
- {
- windowFlags |= SDL_WINDOW_FULLSCREEN;
- }
- else
- {
- // Resizing currently not supported on Windows, where it would require
- // reuploading all textures.
-#if !defined(_WIN32)
- windowFlags |= SDL_WINDOW_RESIZABLE;
-#endif
- }
-
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-
- SDL_Window *window = SDL_CreateWindow("Mana",
- SDL_WINDOWPOS_UNDEFINED,
- SDL_WINDOWPOS_UNDEFINED,
- w, h, windowFlags);
- if (!window) {
- logger->log("Failed to create window: %s", SDL_GetError());
- return false;
- }
- SDL_SetWindowMinimumSize(window, 640, 480);
-
- SDL_GLContext glContext = SDL_GL_CreateContext(window);
- if (!glContext) {
- logger->log("Failed to create OpenGL context: %s", SDL_GetError());
- return false;
- }
-
- mTarget = window;
- mContext = glContext;
- mWidth = w;
- mHeight = h;
- mFullscreen = fs;
-
- if (mSync)
- {
- SDL_GL_SetSwapInterval(1);
- }
+ SDL_GL_GetDrawableSize(mWindow, &mWidth, &mHeight);
// Setup OpenGL
- glViewport(0, 0, w, h);
+ glViewport(0, 0, mWidth, mHeight);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
- int gotDoubleBuffer;
- SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &gotDoubleBuffer);
- logger->log("Using OpenGL %s double buffering.",
- (gotDoubleBuffer ? "with" : "without"));
char const *glExtensions = (char const *)glGetString(GL_EXTENSIONS);
GLint texSize;
@@ -154,18 +77,34 @@ bool OpenGLGraphics::setVideoMode(int w, int h, bool fs)
Image::mTextureSize = texSize;
logger->log("OpenGL texture size: %d pixels%s", Image::mTextureSize,
rectTex ? " (rectangle textures)" : "");
+}
- return true;
+OpenGLGraphics::~OpenGLGraphics()
+{
+ SDL_GL_DeleteContext(mContext);
+
+ delete[] mFloatTexArray;
+ delete[] mIntTexArray;
+ delete[] mIntVertArray;
+}
+
+void OpenGLGraphics::setVSync(bool sync)
+{
+ SDL_GL_SetSwapInterval(sync ? 1 : 0);
+}
+
+void OpenGLGraphics::setReduceInputLag(bool reduceInputLag)
+{
+ mReduceInputLag = reduceInputLag;
}
-void OpenGLGraphics::videoResized(int w, int h)
+void OpenGLGraphics::videoResized(int width, int height)
{
_endDraw();
- mWidth = w;
- mHeight = h;
+ SDL_GL_GetDrawableSize(mWindow, &mWidth, &mHeight);
- glViewport(0, 0, w, h);
+ glViewport(0, 0, mWidth, mHeight);
_beginDraw();
}
@@ -174,7 +113,7 @@ static inline void drawQuad(Image *image,
int srcX, int srcY, int dstX, int dstY,
int width, int height)
{
- if (image->getTextureType() == GL_TEXTURE_2D)
+ if (Image::getTextureType() == GL_TEXTURE_2D)
{
// Find OpenGL normalized texture coordinates.
const float texX1 = static_cast<float>(srcX) /
@@ -236,7 +175,7 @@ static inline void drawRescaledQuad(Image *image,
int width, int height,
int desiredWidth, int desiredHeight)
{
- if (image->getTextureType() == GL_TEXTURE_2D)
+ if (Image::getTextureType() == GL_TEXTURE_2D)
{
// Find OpenGL normalized texture coordinates.
const float texX1 = static_cast<float>(srcX) /
@@ -391,7 +330,7 @@ void OpenGLGraphics::drawImagePattern(Image *image, int x, int y, int w, int h)
unsigned int vp = 0;
const unsigned int vLimit = vertexBufSize * 4;
// Draw a set of textured rectangles
- if (image->getTextureType() == GL_TEXTURE_2D)
+ if (Image::getTextureType() == GL_TEXTURE_2D)
{
float texX1 = static_cast<float>(srcX) / tw;
float texY1 = static_cast<float>(srcY) / th;
@@ -526,7 +465,7 @@ void OpenGLGraphics::drawRescaledImagePattern(Image *image,
const unsigned int vLimit = vertexBufSize * 4;
// Draw a set of textured rectangles
- if (image->getTextureType() == GL_TEXTURE_2D)
+ if (Image::getTextureType() == GL_TEXTURE_2D)
{
const auto tw = static_cast<float>(image->getTextureWidth());
const auto th = static_cast<float>(image->getTextureHeight());
@@ -641,7 +580,7 @@ void OpenGLGraphics::drawRescaledImagePattern(Image *image,
void OpenGLGraphics::updateScreen()
{
- SDL_GL_SwapWindow(mTarget);
+ SDL_GL_SwapWindow(mWindow);
/*
* glFinish flushes all OpenGL commands and makes sure they have been
@@ -685,10 +624,10 @@ void OpenGLGraphics::_endDraw()
popClipArea();
}
-SDL_Surface* OpenGLGraphics::getScreenshot()
+SDL_Surface *OpenGLGraphics::getScreenshot()
{
int w, h;
- SDL_GL_GetDrawableSize(mTarget, &w, &h);
+ SDL_GL_GetDrawableSize(mWindow, &w, &h);
GLint pack = 1;
SDL_Surface *screenshot = SDL_CreateRGBSurface(
@@ -772,7 +711,7 @@ void OpenGLGraphics::popClipArea()
void OpenGLGraphics::setColor(const gcn::Color& color)
{
- mColor = color;
+ Graphics::setColor(color);
glColor4ub(color.r, color.g, color.b, color.a);
mColorAlpha = (color.a != 255);
diff --git a/src/openglgraphics.h b/src/openglgraphics.h
index d300dfd7..0bb07363 100644
--- a/src/openglgraphics.h
+++ b/src/openglgraphics.h
@@ -29,19 +29,14 @@
#include <SDL_opengl.h>
-class OpenGLGraphics : public Graphics
+class OpenGLGraphics final : public Graphics
{
public:
- OpenGLGraphics();
+ OpenGLGraphics(SDL_Window *window, SDL_GLContext glContext);
~OpenGLGraphics() override;
- /**
- * Sets whether vertical refresh syncing is enabled. Takes effect
- * immediately.
- */
- void setSync(bool sync);
- bool getSync() const { return mSync; }
+ void setVSync(bool sync) override;
/**
* Sets whether input lag should be reduced.
@@ -53,8 +48,6 @@ class OpenGLGraphics : public Graphics
void setReduceInputLag(bool reduceInputLag);
bool getReduceInputLag() const { return mReduceInputLag; }
- bool setVideoMode(int w, int h, bool fs) override;
-
void videoResized(int w, int h) override;
bool drawImage(Image *image,
@@ -120,14 +113,15 @@ class OpenGLGraphics : public Graphics
void drawQuadArrayii(int size);
+ SDL_Window *mWindow = nullptr;
SDL_GLContext mContext = nullptr;
GLfloat *mFloatTexArray;
GLint *mIntTexArray;
GLint *mIntVertArray;
- bool mAlpha, mTexture;
- bool mColorAlpha;
- bool mSync;
- bool mReduceInputLag;
+ bool mAlpha = false;
+ bool mTexture = false;
+ bool mColorAlpha = false;
+ bool mReduceInputLag = true;
};
#endif //USE_OPENGL
diff --git a/src/resources/ambientlayer.cpp b/src/resources/ambientlayer.cpp
index b71378cb..2fa522e5 100644
--- a/src/resources/ambientlayer.cpp
+++ b/src/resources/ambientlayer.cpp
@@ -21,6 +21,7 @@
#include "resources/ambientlayer.h"
#include "graphics.h"
+#include "video.h"
#include "resources/image.h"
#include "resources/resourcemanager.h"
diff --git a/src/resources/image.h b/src/resources/image.h
index 662c0393..20a85433 100644
--- a/src/resources/image.h
+++ b/src/resources/image.h
@@ -44,7 +44,7 @@ class Dye;
*/
class Image : public Resource
{
- friend class Graphics;
+ friend class SDLGraphics;
#ifdef USE_OPENGL
friend class OpenGLGraphics;
#endif
diff --git a/src/sdlgraphics.cpp b/src/sdlgraphics.cpp
new file mode 100644
index 00000000..b7f80fd2
--- /dev/null
+++ b/src/sdlgraphics.cpp
@@ -0,0 +1,304 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "sdlgraphics.h"
+
+#include "log.h"
+#include "resources/image.h"
+
+#include <guichan/exception.hpp>
+
+SDLGraphics::SDLGraphics(SDL_Window *window, SDL_Renderer *renderer)
+ : mWindow(window)
+ , mRenderer(renderer)
+{
+ Image::setRenderer(mRenderer);
+
+ SDL_GetRendererOutputSize(mRenderer, &mWidth, &mHeight);
+
+ SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
+
+ if (const char *driver = SDL_GetCurrentVideoDriver())
+ logger->log("Using video driver: %s", driver);
+ else
+ logger->log("Using video driver: not initialized");
+
+ SDL_RendererInfo info;
+
+ if (SDL_GetRendererInfo(renderer, &info) == 0) {
+ logger->log("Using renderer: %s", info.name);
+
+ logger->log("The renderer is a software fallback: %s",
+ (info.flags & SDL_RENDERER_SOFTWARE) ? "yes" : "no");
+ logger->log("The renderer is hardware accelerated: %s",
+ (info.flags & SDL_RENDERER_ACCELERATED) ? "yes" : "no");
+ logger->log("Vsync: %s",
+ (info.flags & SDL_RENDERER_PRESENTVSYNC) ? "on" : "off");
+ logger->log("Renderer supports rendering to texture: %s",
+ (info.flags & SDL_RENDERER_TARGETTEXTURE) ? "yes" : "no");
+ logger->log("Max texture size: %dx%d",
+ info.max_texture_width, info.max_texture_height);
+ }
+}
+
+SDLGraphics::~SDLGraphics()
+{
+ SDL_DestroyRenderer(mRenderer);
+}
+
+void SDLGraphics::setVSync(bool sync)
+{
+#if SDL_VERSION_ATLEAST(2, 0, 18)
+ SDL_RenderSetVSync(mRenderer, sync ? SDL_TRUE : SDL_FALSE);
+#endif
+}
+
+void SDLGraphics::videoResized(int w, int h)
+{
+ SDL_GetRendererOutputSize(mRenderer, &mWidth, &mHeight);
+}
+
+bool SDLGraphics::drawRescaledImage(Image *image,
+ int srcX, int srcY,
+ int dstX, int dstY,
+ int width, int height,
+ int desiredWidth, int desiredHeight,
+ bool useColor)
+{
+ // Check that preconditions for blitting are met.
+ if (!image || !image->mTexture)
+ return false;
+
+ dstX += mClipStack.top().xOffset;
+ dstY += mClipStack.top().yOffset;
+
+ srcX += image->mBounds.x;
+ srcY += image->mBounds.y;
+
+ SDL_Rect dstRect;
+ SDL_Rect srcRect;
+ dstRect.x = dstX; dstRect.y = dstY;
+ srcRect.x = srcX; srcRect.y = srcY;
+ srcRect.w = width;
+ srcRect.h = height;
+ dstRect.w = desiredWidth;
+ dstRect.h = desiredHeight;
+
+ return !(SDL_RenderCopy(mRenderer, image->mTexture, &srcRect, &dstRect) < 0);
+}
+
+void SDLGraphics::drawRescaledImagePattern(Image *image,
+ int x, int y,
+ int w, int h,
+ int scaledWidth,
+ int scaledHeight)
+{
+ // Check that preconditions for blitting are met.
+ if (!image || !image->mTexture)
+ return;
+
+ if (scaledHeight <= 0 || scaledWidth <= 0)
+ return;
+
+ for (int py = 0; py < h; py += scaledHeight) // Y position on pattern plane
+ {
+ int dh = (py + scaledHeight >= h) ? h - py : scaledHeight;
+ int srcY = image->mBounds.y;
+ int dstY = y + py + mClipStack.top().yOffset;
+
+ for (int px = 0; px < w; px += scaledWidth) // X position on pattern plane
+ {
+ int dw = (px + scaledWidth >= w) ? w - px : scaledWidth;
+ int srcX = image->mBounds.x;
+ int dstX = x + px + mClipStack.top().xOffset;
+
+ SDL_Rect dstRect;
+ SDL_Rect srcRect;
+ dstRect.x = dstX; dstRect.y = dstY;
+ dstRect.w = dw; dstRect.h = dh;
+ srcRect.x = srcX; srcRect.y = srcY;
+ srcRect.w = dw; srcRect.h = dh;
+
+ if (SDL_RenderCopy(mRenderer, image->mTexture, &srcRect, &dstRect))
+ return;
+ }
+ }
+}
+
+void SDLGraphics::updateScreen()
+{
+ SDL_RenderPresent(mRenderer);
+}
+
+SDL_Surface *SDLGraphics::getScreenshot()
+{
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ int rmask = 0xff000000;
+ int gmask = 0x00ff0000;
+ int bmask = 0x0000ff00;
+#else
+ int rmask = 0x000000ff;
+ int gmask = 0x0000ff00;
+ int bmask = 0x00ff0000;
+#endif
+ int amask = 0x00000000;
+
+ SDL_Surface *screenshot = SDL_CreateRGBSurface(0, mWidth,
+ mHeight, 24, rmask, gmask, bmask, amask);
+
+ SDL_RenderReadPixels(mRenderer, nullptr,
+ screenshot->format->format,
+ screenshot->pixels,
+ screenshot->pitch);
+
+ return screenshot;
+}
+
+bool SDLGraphics::pushClipArea(gcn::Rectangle area)
+{
+ bool result = Graphics::pushClipArea(area);
+ updateSDLClipRect();
+ return result;
+}
+
+void SDLGraphics::popClipArea()
+{
+ Graphics::popClipArea();
+ updateSDLClipRect();
+}
+
+void SDLGraphics::updateSDLClipRect()
+{
+ if (mClipStack.empty())
+ {
+ SDL_RenderSetClipRect(mRenderer, nullptr);
+ return;
+ }
+
+ const gcn::ClipRectangle &carea = mClipStack.top();
+ SDL_Rect rect;
+ rect.x = carea.x;
+ rect.y = carea.y;
+ rect.w = carea.width;
+ rect.h = carea.height;
+
+ SDL_RenderSetClipRect(mRenderer, &rect);
+}
+
+void SDLGraphics::drawPoint(int x, int y)
+{
+ if (mClipStack.empty())
+ {
+ throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
+ }
+
+ const gcn::ClipRectangle &top = mClipStack.top();
+
+ x += top.xOffset;
+ y += top.yOffset;
+
+ if (!top.isPointInRect(x, y))
+ return;
+
+ SDL_SetRenderDrawColor(mRenderer,
+ (Uint8)(mColor.r),
+ (Uint8)(mColor.g),
+ (Uint8)(mColor.b),
+ (Uint8)(mColor.a));
+ SDL_RenderDrawPoint(mRenderer, x, y);
+}
+
+void SDLGraphics::drawLine(int x1, int y1, int x2, int y2)
+{
+ if (mClipStack.empty())
+ {
+ throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
+ }
+
+ const gcn::ClipRectangle &top = mClipStack.top();
+
+ x1 += top.xOffset;
+ y1 += top.yOffset;
+ x2 += top.xOffset;
+ y2 += top.yOffset;
+
+ SDL_SetRenderDrawColor(mRenderer,
+ (Uint8)(mColor.r),
+ (Uint8)(mColor.g),
+ (Uint8)(mColor.b),
+ (Uint8)(mColor.a));
+ SDL_RenderDrawLine(mRenderer, x1, y1, x2, y2);
+}
+
+void SDLGraphics::drawRectangle(const gcn::Rectangle &rectangle)
+{
+ if (mClipStack.empty())
+ {
+ throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
+ }
+
+ const gcn::ClipRectangle &top = mClipStack.top();
+
+ SDL_Rect rect;
+ rect.x = rectangle.x + top.xOffset;
+ rect.y = rectangle.y + top.yOffset;
+ rect.w = rectangle.width;
+ rect.h = rectangle.height;
+
+ SDL_SetRenderDrawColor(mRenderer,
+ (Uint8)(mColor.r),
+ (Uint8)(mColor.g),
+ (Uint8)(mColor.b),
+ (Uint8)(mColor.a));
+ SDL_RenderDrawRect(mRenderer, &rect);
+}
+
+void SDLGraphics::fillRectangle(const gcn::Rectangle &rectangle)
+{
+ if (mClipStack.empty())
+ {
+ throw GCN_EXCEPTION("Clip stack is empty, perhaps you called a draw funtion outside of _beginDraw() and _endDraw()?");
+ }
+
+ const gcn::ClipRectangle &top = mClipStack.top();
+
+ gcn::Rectangle area = rectangle;
+ area.x += top.xOffset;
+ area.y += top.yOffset;
+
+ if(!area.isIntersecting(top))
+ {
+ return;
+ }
+
+ SDL_Rect rect;
+ rect.x = area.x;
+ rect.y = area.y;
+ rect.w = area.width;
+ rect.h = area.height;
+
+ SDL_SetRenderDrawColor(mRenderer,
+ (Uint8)(mColor.r),
+ (Uint8)(mColor.g),
+ (Uint8)(mColor.b),
+ (Uint8)(mColor.a));
+ SDL_RenderFillRect(mRenderer, &rect);
+}
diff --git a/src/sdlgraphics.h b/src/sdlgraphics.h
new file mode 100644
index 00000000..6685fc83
--- /dev/null
+++ b/src/sdlgraphics.h
@@ -0,0 +1,73 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SDLGRAPHICS_H
+#define SDLGRAPHICS_H
+
+#include "graphics.h"
+
+class SDLGraphics final : public Graphics
+{
+public:
+ SDLGraphics(SDL_Window *window, SDL_Renderer *renderer);
+ ~SDLGraphics() override;
+
+ void setVSync(bool sync) override;
+
+ void videoResized(int w, int h) override;
+
+ bool drawRescaledImage(Image *image,
+ int srcX, int srcY,
+ int dstX, int dstY,
+ int width, int height,
+ int desiredWidth, int desiredHeight,
+ bool useColor) override;
+
+ void drawRescaledImagePattern(Image *image,
+ int x, int y,
+ int w, int h,
+ int scaledWidth,
+ int scaledHeight) override;
+
+ void updateScreen() override;
+
+ SDL_Surface *getScreenshot() override;
+
+ bool pushClipArea(gcn::Rectangle area) override;
+
+ void popClipArea() override;
+
+ void drawPoint(int x, int y) override;
+
+ void drawLine(int x1, int y1, int x2, int y2) override;
+
+ void drawRectangle(const gcn::Rectangle &rectangle) override;
+
+ void fillRectangle(const gcn::Rectangle &rectangle) override;
+
+private:
+ void updateSDLClipRect();
+
+ SDL_Window *mWindow = nullptr;
+ SDL_Renderer *mRenderer = nullptr;
+};
+
+#endif // SDLGRAPHICS_H
diff --git a/src/video.cpp b/src/video.cpp
new file mode 100644
index 00000000..9e69ec27
--- /dev/null
+++ b/src/video.cpp
@@ -0,0 +1,263 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "video.h"
+
+#include "log.h"
+#include "sdlgraphics.h"
+#include "utils/stringutils.h"
+
+#ifdef USE_OPENGL
+#include "openglgraphics.h"
+#endif
+
+#include <algorithm>
+
+Video::~Video()
+{
+ mGraphics.reset(); // reset graphics first
+
+ if (mWindow)
+ SDL_DestroyWindow(mWindow);
+}
+
+Graphics *Video::initialize(const VideoSettings &settings)
+{
+ mSettings = settings;
+
+ if (!initDisplayModes())
+ {
+ logger->log("Failed to initialize display modes: %s", SDL_GetError());
+ }
+
+ SDL_DisplayMode displayMode;
+
+ if (mSettings.windowMode == WindowMode::Fullscreen)
+ {
+ SDL_DisplayMode requestedMode;
+ requestedMode.format = 0;
+ requestedMode.w = mSettings.width;
+ requestedMode.h = mSettings.height;
+ requestedMode.refresh_rate = 0;
+ requestedMode.driverdata = nullptr;
+
+ if (SDL_GetClosestDisplayMode(mSettings.display, &requestedMode, &displayMode) == nullptr)
+ {
+ logger->log("SDL_GetClosestDisplayMode failed: %s, falling back to borderless mode", SDL_GetError());
+ mSettings.windowMode = WindowMode::WindowedFullscreen;
+ }
+ }
+
+ int windowFlags = SDL_WINDOW_RESIZABLE;
+ const char *videoMode = "windowed";
+
+ switch (mSettings.windowMode)
+ {
+ case WindowMode::Windowed:
+ break;
+ case WindowMode::Fullscreen:
+ windowFlags |= SDL_WINDOW_FULLSCREEN;
+ videoMode = "fullscreen";
+ break;
+ case WindowMode::WindowedFullscreen:
+ windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+ videoMode = "windowed fullscreen";
+ break;
+ }
+
+ if (mSettings.openGL)
+ windowFlags |= SDL_WINDOW_OPENGL;
+
+ logger->log("Setting video mode %dx%d %s",
+ mSettings.width,
+ mSettings.height,
+ videoMode);
+
+ mWindow = SDL_CreateWindow("Mana",
+ SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ mSettings.width,
+ mSettings.height,
+ windowFlags);
+
+ if (!mWindow)
+ {
+ logger->error(strprintf("Failed to create window: %s",
+ SDL_GetError()));
+ return nullptr;
+ }
+
+ SDL_SetWindowMinimumSize(mWindow, 640, 480);
+
+ if (mSettings.windowMode == WindowMode::Fullscreen)
+ {
+ if (SDL_SetWindowDisplayMode(mWindow, &displayMode) != 0)
+ {
+ logger->log("SDL_SetWindowDisplayMode failed: %s", SDL_GetError());
+ }
+ }
+
+#ifdef USE_OPENGL
+ if (mSettings.openGL)
+ {
+ SDL_GLContext glContext = SDL_GL_CreateContext(mWindow);
+ if (!glContext)
+ {
+ 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;
+
+ SDL_Renderer *renderer = SDL_CreateRenderer(mWindow, -1, rendererFlags);
+ if (!renderer)
+ {
+ logger->error(strprintf("Failed to create renderer: %s",
+ SDL_GetError()));
+ return nullptr;
+ }
+
+ mGraphics = std::make_unique<SDLGraphics>(mWindow, renderer);
+ return mGraphics.get();
+}
+
+bool Video::apply(const VideoSettings &settings)
+{
+ if (mSettings == settings)
+ return true;
+
+ // When changing to fullscreen mode, we set the display mode first
+ if (settings.windowMode == WindowMode::Fullscreen)
+ {
+ SDL_DisplayMode displayMode;
+ if (SDL_GetWindowDisplayMode(mWindow, &displayMode) != 0)
+ {
+ logger->error(strprintf("SDL_GetCurrentDisplayMode failed: %s", SDL_GetError()));
+ return false;
+ }
+
+ if (displayMode.w != settings.width || displayMode.h != settings.height)
+ {
+#ifdef __APPLE__
+ // Workaround SDL2 issue when switching display modes while already
+ // fullscreen on macOS (tested as of SDL 2.30.0).
+ if (SDL_GetWindowFlags(mWindow) & SDL_WINDOW_FULLSCREEN)
+ SDL_SetWindowFullscreen(mWindow, 0);
+#endif
+
+ displayMode.w = settings.width;
+ displayMode.h = settings.height;
+
+ if (SDL_SetWindowDisplayMode(mWindow, &displayMode) != 0)
+ {
+ logger->error(strprintf("SDL_SetWindowDisplayMode failed: %s", SDL_GetError()));
+ return false;
+ }
+ }
+ }
+
+ int windowFlags = 0;
+ switch (settings.windowMode)
+ {
+ case WindowMode::Windowed:
+ break;
+ case WindowMode::WindowedFullscreen:
+ windowFlags = SDL_WINDOW_FULLSCREEN_DESKTOP;
+ break;
+ case WindowMode::Fullscreen:
+ windowFlags = SDL_WINDOW_FULLSCREEN;
+ break;
+ }
+
+ if (SDL_SetWindowFullscreen(mWindow, windowFlags) != 0)
+ {
+ logger->error(strprintf("SDL_SetWindowFullscreen failed: %s", SDL_GetError()));
+ return false;
+ }
+
+ if (settings.windowMode == WindowMode::Windowed) {
+#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
+ // be distinguished from a maximized window (tested as of SDL 2.30.0).
+ if (!(SDL_GetWindowFlags(mWindow) & SDL_WINDOW_MAXIMIZED))
+#endif
+ 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);
+
+ return true;
+}
+
+bool Video::initDisplayModes()
+{
+ const int displayIndex = mSettings.display;
+ SDL_DisplayMode mode;
+ if (SDL_GetDesktopDisplayMode(displayIndex, &mode) != 0)
+ return false;
+
+ mDesktopDisplayMode.width = mode.w;
+ mDesktopDisplayMode.height = mode.h;
+
+ // Get available fullscreen/hardware modes
+ const int numModes = SDL_GetNumDisplayModes(displayIndex);
+ for (int i = 0; i < numModes; i++)
+ {
+ if (SDL_GetDisplayMode(displayIndex, i, &mode) != 0)
+ return false;
+
+ // Skip the unreasonably small modes
+ if (mode.w < 640 || mode.h < 480)
+ continue;
+
+ // Only list each resolution once
+ // (we currently don't support selecting the refresh rate)
+ if (std::find_if(mDisplayModes.cbegin(),
+ mDisplayModes.cend(),
+ [&mode](const DisplayMode &other) {
+ return mode.w == other.width && mode.h == other.height;
+ }) != mDisplayModes.cend())
+ continue;
+
+ mDisplayModes.push_back(DisplayMode { mode.w, mode.h });
+ }
+
+ return true;
+}
diff --git a/src/video.h b/src/video.h
new file mode 100644
index 00000000..df9cd518
--- /dev/null
+++ b/src/video.h
@@ -0,0 +1,106 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef VIDEO_H
+#define VIDEO_H
+
+#include "graphics.h"
+
+#include <memory>
+#include <vector>
+
+static constexpr int defaultScreenWidth = 1280;
+static constexpr int defaultScreenHeight = 720;
+
+enum class WindowMode
+{
+ Windowed = 0,
+ WindowedFullscreen = 1,
+ Fullscreen = 2,
+};
+
+struct DisplayMode
+{
+ int width = 0;
+ int height = 0;
+};
+
+struct VideoSettings
+{
+ WindowMode windowMode = WindowMode::Windowed;
+ int width = defaultScreenWidth;
+ int height = defaultScreenHeight;
+ int display = 0;
+ bool vsync = true;
+ bool openGL = false;
+
+ bool operator==(const VideoSettings &other) const
+ {
+ return width == other.width &&
+ height == other.height &&
+ windowMode == other.windowMode &&
+ display == other.display &&
+ vsync == other.vsync &&
+ openGL == other.openGL;
+ }
+};
+
+class Video
+{
+public:
+ Video() = default;
+ ~Video();
+
+ const VideoSettings &settings() const { return mSettings; }
+ SDL_Window *window() const { return mWindow; }
+ Graphics *graphics() const { return mGraphics.get(); }
+
+ /**
+ * Try to create a window with the given settings.
+ */
+ Graphics *initialize(const VideoSettings &settings);
+
+ /**
+ * Try to apply the given video settings.
+ */
+ bool apply(const VideoSettings &settings);
+
+ const DisplayMode &desktopDisplayMode() const
+ {
+ return mDesktopDisplayMode;
+ }
+
+ const std::vector<DisplayMode> &displayModes() const
+ {
+ return mDisplayModes;
+ }
+
+private:
+ bool initDisplayModes();
+
+ VideoSettings mSettings;
+ DisplayMode mDesktopDisplayMode;
+ std::vector<DisplayMode> mDisplayModes;
+ std::unique_ptr<Graphics> mGraphics;
+ SDL_Window *mWindow = nullptr;
+};
+
+#endif // VIDEO_H