summaryrefslogtreecommitdiff
path: root/src/video.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video.cpp')
-rw-r--r--src/video.cpp263
1 files changed, 263 insertions, 0 deletions
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;
+}