From 1c7b66237de91cc7a8b0a90e9ca0148a0d59f6c6 Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:41:48 +0000 Subject: [PATCH] window: refactored code --- UnleashedRecomp/config.h | 13 ++- UnleashedRecomp/config_detail.h | 36 ++++-- UnleashedRecomp/gpu/video.cpp | 2 +- UnleashedRecomp/ui/window.cpp | 98 ++++++++++------ UnleashedRecomp/ui/window.h | 181 +++++++++++++++++------------ UnleashedRecomp/ui/window_events.h | 28 +++++ 6 files changed, 238 insertions(+), 120 deletions(-) create mode 100644 UnleashedRecomp/ui/window_events.h diff --git a/UnleashedRecomp/config.h b/UnleashedRecomp/config.h index 44970d1..ec5ef76 100644 --- a/UnleashedRecomp/config.h +++ b/UnleashedRecomp/config.h @@ -12,7 +12,7 @@ public: CONFIG_DEFINE_ENUM("System", EScoreBehaviour, ScoreBehaviour, EScoreBehaviour::CheckpointReset); CONFIG_DEFINE("System", bool, UnleashOutOfControlDrain, true); CONFIG_DEFINE("System", bool, WerehogHubTransformVideo, true); - CONFIG_DEFINE("System", bool, LogoSkip, false); + CONFIG_DEFINE_HIDE("System", bool, LogoSkip, false); CONFIG_DEFINE("Controls", bool, CameraXInvert, false); CONFIG_DEFINE("Controls", bool, CameraYInvert, false); @@ -26,10 +26,11 @@ public: CONFIG_DEFINE("Audio", bool, WerehogBattleMusic, true); CONFIG_DEFINE_ENUM("Video", EGraphicsAPI, GraphicsAPI, EGraphicsAPI::D3D12); - CONFIG_DEFINE("Video", int32_t, WindowX, -1); - CONFIG_DEFINE("Video", int32_t, WindowY, -1); - CONFIG_DEFINE("Video", size_t, WindowWidth, 1280); - CONFIG_DEFINE("Video", size_t, WindowHeight, 720); + CONFIG_DEFINE_HIDE("Video", int32_t, WindowX, -1); + CONFIG_DEFINE_HIDE("Video", int32_t, WindowY, -1); + CONFIG_DEFINE("Video", int32_t, WindowWidth, 1280); + CONFIG_DEFINE("Video", int32_t, WindowHeight, 720); + CONFIG_DEFINE_ENUM_HIDE("Video", EWindowState, WindowState, EWindowState::Normal); CONFIG_DEFINE_CALLBACK("Video", float, ResolutionScale, 1.0f, { @@ -42,7 +43,7 @@ public: CONFIG_DEFINE("Video", int32_t, FPS, 60); CONFIG_DEFINE("Video", float, Brightness, 0.5f); CONFIG_DEFINE("Video", size_t, MSAA, 4); - CONFIG_DEFINE("Video", size_t, AnisotropicFiltering, 16); + CONFIG_DEFINE_HIDE("Video", size_t, AnisotropicFiltering, 16); CONFIG_DEFINE_ENUM("Video", EShadowResolution, ShadowResolution, EShadowResolution::x4096); CONFIG_DEFINE_ENUM("Video", EGITextureFiltering, GITextureFiltering, EGITextureFiltering::Bicubic); CONFIG_DEFINE("Video", bool, AlphaToCoverage, true); diff --git a/UnleashedRecomp/config_detail.h b/UnleashedRecomp/config_detail.h index 091bed7..ac91bc0 100644 --- a/UnleashedRecomp/config_detail.h +++ b/UnleashedRecomp/config_detail.h @@ -7,17 +7,23 @@ #define CONFIG_DEFINE(section, type, name, defaultValue) \ inline static ConfigDef name{section, #name, defaultValue}; +#define CONFIG_DEFINE_HIDE(section, type, name, defaultValue) \ + inline static ConfigDef name{section, #name, defaultValue}; + #define CONFIG_DEFINE_ENUM_TEMPLATE(type) \ inline static std::unordered_map g_##type##_template = #define CONFIG_DEFINE_ENUM(section, type, name, defaultValue) \ inline static ConfigDef name{section, #name, defaultValue, g_##type##_template}; +#define CONFIG_DEFINE_ENUM_HIDE(section, type, name, defaultValue) \ + inline static ConfigDef name{section, #name, defaultValue, g_##type##_template}; + #define CONFIG_DEFINE_IMPL(section, type, name, defaultValue, readImpl) \ - inline static ConfigDef name{section, #name, defaultValue, [](ConfigDef* def, const toml::v3::table& table) readImpl}; + inline static ConfigDef name{section, #name, defaultValue, [](ConfigDef* def, const toml::v3::table& table) readImpl}; #define CONFIG_DEFINE_CALLBACK(section, type, name, defaultValue, readCallback) \ - inline static ConfigDef name{section, #name, defaultValue, [](ConfigDef* def) readCallback}; + inline static ConfigDef name{section, #name, defaultValue, [](ConfigDef* def) readCallback}; #define CONFIG_GET_DEFAULT(name) Config::name.DefaultValue #define CONFIG_SET_DEFAULT(name) Config::name.MakeDefault(); @@ -34,9 +40,12 @@ public: virtual std::string ToString() const = 0; }; -template +template class ConfigDef : public ConfigDefBase { +protected: + bool m_isMenuOption{ isMenuOption }; + public: std::string Section{}; std::string Name{}; @@ -44,8 +53,8 @@ public: T Value{ DefaultValue }; std::unordered_map EnumTemplate{}; std::unordered_map EnumTemplateReverse{}; - std::function*, const toml::v3::table&)> ReadImpl; - std::function*)> ReadCallback; + std::function*, const toml::v3::table&)> ReadImpl; + std::function*)> ReadCallback; ConfigDef(std::string section, std::string name, T defaultValue) : Section(section), Name(name), DefaultValue(defaultValue) @@ -62,13 +71,13 @@ public: Config::Definitions.emplace_back(this); } - ConfigDef(std::string section, std::string name, T defaultValue, std::function*)> readCallback) + ConfigDef(std::string section, std::string name, T defaultValue, std::function*)> readCallback) : Section(section), Name(name), DefaultValue(defaultValue), ReadCallback(readCallback) { Config::Definitions.emplace_back(this); } - ConfigDef(std::string section, std::string name, T defaultValue, std::function*, const toml::v3::table&)> readImpl) + ConfigDef(std::string section, std::string name, T defaultValue, std::function*, const toml::v3::table&)> readImpl) : Section(section), Name(name), DefaultValue(defaultValue), ReadImpl(readImpl) { Config::Definitions.emplace_back(this); @@ -234,6 +243,19 @@ CONFIG_DEFINE_ENUM_TEMPLATE(EGraphicsAPI) { "Vulkan", EGraphicsAPI::Vulkan } }; +enum class EWindowState : uint32_t +{ + Normal, + Maximised +}; + +CONFIG_DEFINE_ENUM_TEMPLATE(EWindowState) +{ + { "Normal", EWindowState::Normal }, + { "Maximised", EWindowState::Maximised }, + { "Maximized", EWindowState::Maximised } +}; + enum class EShadowResolution : int32_t { Original = -1, diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index 2ac26bb..1823ca4 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -836,7 +836,7 @@ static void CreateHostDevice() g_copyCommandList = g_device->createCommandList(RenderCommandListType::COPY); g_copyCommandFence = g_device->createCommandFence(); - g_swapChain = g_queue->createSwapChain(Window::s_windowHandle, Config::TripleBuffering ? 3 : 2, RenderFormat::B8G8R8A8_UNORM); + g_swapChain = g_queue->createSwapChain(Window::s_handle, Config::TripleBuffering ? 3 : 2, RenderFormat::B8G8R8A8_UNORM); g_swapChain->setVsyncEnabled(Config::VSync); g_swapChainValid = !g_swapChain->needsResize(); diff --git a/UnleashedRecomp/ui/window.cpp b/UnleashedRecomp/ui/window.cpp index eeb0939..83bbbff 100644 --- a/UnleashedRecomp/ui/window.cpp +++ b/UnleashedRecomp/ui/window.cpp @@ -7,7 +7,6 @@ bool m_isFullscreenKeyReleased = true; int Window_OnSDLEvent(void*, SDL_Event* event) { - // TODO (Hyper): prevent window changes during boot to avoid buffer resize crashes. switch (event->type) { case SDL_QUIT: @@ -25,7 +24,10 @@ int Window_OnSDLEvent(void*, SDL_Event* event) if (!(event->key.keysym.mod & KMOD_ALT) || !m_isFullscreenKeyReleased) break; - Window::SetFullscreen(!Window::IsFullscreen()); + Config::Fullscreen = Window::SetFullscreen(!Window::IsFullscreen()); + + if (!Config::Fullscreen) + Config::WindowState = Window::SetMaximised(Config::WindowState == EWindowState::Maximised); // Block holding ALT+ENTER spamming window changes. m_isFullscreenKeyReleased = false; @@ -35,11 +37,8 @@ int Window_OnSDLEvent(void*, SDL_Event* event) // Restore original window dimensions on F2. case SDLK_F2: - { - Window::SetFullscreen(Config::Fullscreen); - Window::SetWindowDimensions(-1, -1, true); + Window::SetDimensions(SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DEFAULT_WIDTH, DEFAULT_HEIGHT); break; - } } break; @@ -70,16 +69,43 @@ int Window_OnSDLEvent(void*, SDL_Event* event) SDL_ShowCursor(Window::IsFullscreen() ? SDL_DISABLE : SDL_ENABLE); break; + case SDL_WINDOWEVENT_RESTORED: + case SDL_WINDOWEVENT_MAXIMIZED: + { + Config::WindowState = Window::IsMaximised() + ? EWindowState::Maximised + : EWindowState::Normal; + + break; + } + case SDL_WINDOWEVENT_RESIZED: + { Window::s_width = event->window.data1; Window::s_height = event->window.data2; - Window::RaiseResizeEvents(); + + if (!Window::IsFullscreen()) + { + Config::WindowWidth = Window::s_width; + Config::WindowHeight = Window::s_height; + } + break; + } case SDL_WINDOWEVENT_MOVED: - Config::WindowX = event->window.data1; - Config::WindowY = event->window.data2; + { + Window::s_x = event->window.data1; + Window::s_y = event->window.data2; + + if (!Window::IsFullscreen()) + { + Config::WindowX = Window::s_x; + Config::WindowY = Window::s_y; + } + break; + } } break; @@ -91,43 +117,47 @@ int Window_OnSDLEvent(void*, SDL_Event* event) void Window::Init() { - /* TODO: move this since it'll have to change - on soft reboot from the options menu. */ - auto title = Config::Language == ELanguage::Japanese - ? "Sonic World Adventure" - : "SONIC UNLEASHED"; - SDL_InitSubSystem(SDL_INIT_VIDEO); SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); - SDL_AddEventWatch(Window_OnSDLEvent, s_window); - + SDL_AddEventWatch(Window_OnSDLEvent, s_pWindow); SetProcessDPIAware(); - int32_t x = Config::WindowX < 0 ? SDL_WINDOWPOS_CENTERED : Config::WindowX; - int32_t y = Config::WindowY < 0 ? SDL_WINDOWPOS_CENTERED : Config::WindowY; - int32_t width = Config::WindowWidth; - int32_t height = Config::WindowHeight; + s_x = Config::WindowX; + s_y = Config::WindowY; + s_width = Config::WindowWidth; + s_height = Config::WindowHeight; - s_window = SDL_CreateWindow(title, x, y, width, height, SDL_WINDOW_RESIZABLE); + auto isPositionValid = IsPositionValid(); - SDL_GetWindowPosition(s_window, &x, &y); - - Config::WindowX = x; - Config::WindowY = y; - - if (auto icon = GetIconSurface()) + if (!isPositionValid) { - SDL_SetWindowIcon(s_window, icon); - SDL_FreeSurface(icon); + s_x = SDL_WINDOWPOS_CENTERED; + s_y = SDL_WINDOWPOS_CENTERED; + s_width = DEFAULT_WIDTH; + s_height = DEFAULT_HEIGHT; } - SetWindowDimensions(); - SetFullscreen(Config::Fullscreen); + s_pWindow = SDL_CreateWindow("SWA", s_x, s_y, s_width, s_height, GetWindowFlags()); + + if (!isPositionValid) + { + auto rect = Window::GetDimensions(); + + Config::WindowX = rect.x; + Config::WindowY = rect.y; + Config::WindowWidth = rect.w; + Config::WindowHeight = rect.h; + } + + SetIcon(); + SetTitle(); + SDL_SetWindowMinimumSize(s_pWindow, 640, 480); SDL_SysWMinfo info; SDL_VERSION(&info.version); - SDL_GetWindowWMInfo(s_window, &info); - s_windowHandle = info.info.win.window; + SDL_GetWindowWMInfo(s_pWindow, &info); + + s_handle = info.info.win.window; } // CApplication::Update diff --git a/UnleashedRecomp/ui/window.h b/UnleashedRecomp/ui/window.h index b601f23..10c5d51 100644 --- a/UnleashedRecomp/ui/window.h +++ b/UnleashedRecomp/ui/window.h @@ -2,21 +2,25 @@ #include #include "res/icon.h" +#include "ui/window_events.h" #include "config.h" +#define DEFAULT_WIDTH 1280 +#define DEFAULT_HEIGHT 720 + struct Window { - inline static std::vector> ms_resizeEvents; - public: - inline static SDL_Window* s_window; - inline static HWND s_windowHandle; + inline static SDL_Window* s_pWindow; + inline static HWND s_handle; + + inline static int s_x; + inline static int s_y; + inline static int s_width = DEFAULT_WIDTH; + inline static int s_height = DEFAULT_HEIGHT; inline static bool s_isFocused; - inline static int s_width = 1280; - inline static int s_height = 720; - static SDL_Surface* GetIconSurface(void* pIconBmp = nullptr, size_t iconSize = 0) { auto rw = SDL_RWFromMem(pIconBmp ? pIconBmp : (void*)g_icon, pIconBmp ? iconSize : g_icon_size); @@ -28,98 +32,131 @@ public: return surface; } - static bool IsDisplayResolution(int w, int h, bool isExact = true) + static void SetIcon(void* pIconBmp = nullptr, size_t iconSize = 0) { - auto width = w <= 0 ? Config::WindowWidth : w; - auto height = h <= 0 ? Config::WindowHeight : h; - - SDL_Rect displayRect; - if (SDL_GetDisplayBounds(SDL_GetWindowDisplayIndex(s_window), &displayRect) == ERROR_SUCCESS) + if (auto icon = GetIconSurface(pIconBmp, iconSize)) { - if (isExact) - { - if (displayRect.w == width && displayRect.h == height) - return true; - } - else - { - if (displayRect.w <= width && displayRect.h <= height) - return true; - } + SDL_SetWindowIcon(s_pWindow, icon); + SDL_FreeSurface(icon); + } + } + + static void SetTitle(const char* title = nullptr) + { + if (!title) + { + title = Config::Language == ELanguage::Japanese + ? "Sonic World Adventure" + : "SONIC UNLEASHED"; } - return false; + SDL_SetWindowTitle(s_pWindow, title); } static bool IsFullscreen() { - return SDL_GetWindowFlags(s_window) & SDL_WINDOW_FULLSCREEN_DESKTOP; + return SDL_GetWindowFlags(s_pWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP; } static bool SetFullscreen(bool isEnabled) { if (isEnabled) { - SDL_SetWindowFullscreen(s_window, SDL_WINDOW_FULLSCREEN_DESKTOP); + SDL_SetWindowFullscreen(s_pWindow, SDL_WINDOW_FULLSCREEN_DESKTOP); SDL_ShowCursor(SDL_DISABLE); - - return true; } else { - SDL_SetWindowFullscreen(s_window, 0); + SDL_SetWindowFullscreen(s_pWindow, 0); SDL_ShowCursor(SDL_ENABLE); + SetIcon(); + } - SDL_Rect displayRect; - if (SDL_GetDisplayBounds(SDL_GetWindowDisplayIndex(s_window), &displayRect) == ERROR_SUCCESS) - { - // Maximise window if the config resolution is greater than the display. - if (IsDisplayResolution(s_width, s_height, false)) - SDL_MaximizeWindow(s_window); - } + return isEnabled; + } + static bool IsMaximised() + { + return SDL_GetWindowFlags(s_pWindow) & SDL_WINDOW_MAXIMIZED; + } + + static EWindowState SetMaximised(bool isEnabled) + { + if (isEnabled) + { + SDL_MaximizeWindow(s_pWindow); + } + else + { + SDL_RestoreWindow(s_pWindow); + } + + return isEnabled + ? EWindowState::Maximised + : EWindowState::Normal; + } + + static SDL_Rect GetDimensions() + { + SDL_Rect rect{}; + + SDL_GetWindowPosition(s_pWindow, &rect.x, &rect.y); + SDL_GetWindowSize(s_pWindow, &rect.w, &rect.h); + + return rect; + } + + static void SetDimensions(int x, int y, int w, int h) + { + s_x = x; + s_y = y; + s_width = w; + s_height = h; + + SDL_SetWindowSize(s_pWindow, w, h); + SDL_ResizeEvent(s_pWindow, w, h); + + SDL_SetWindowPosition(s_pWindow, x, y); + SDL_MoveEvent(s_pWindow, x, y); + } + + static uint32_t GetWindowFlags() + { + uint32_t flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; + + if (Config::WindowState == EWindowState::Maximised) + flags |= SDL_WINDOW_MAXIMIZED; + + if (Config::Fullscreen) + flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + + return flags; + } + + static bool IsPositionValid() + { + auto displayCount = SDL_GetNumVideoDisplays(); + + if (displayCount <= 0) + { + printf("Failed to validate window position: %s\n", SDL_GetError()); return false; } - } - static void SetWindowDimensions(int w = -1, int h = -1, bool recenter = false) - { - auto width = w <= 0 ? Config::WindowWidth : w; - auto height = h <= 0 ? Config::WindowHeight : h; - auto isPendingMaximise = false; - - if (IsDisplayResolution(width, height)) + for (int i = 0; i < displayCount; i++) { - height -= GetSystemMetrics(31); - isPendingMaximise = true; + SDL_Rect bounds; + if (SDL_GetDisplayBounds(i, &bounds) == 0) + { + if (s_x >= bounds.x && s_x < bounds.x + bounds.w && + s_y >= bounds.y && s_y < bounds.y + bounds.h) + { + return true; + } + } } - int32_t x = recenter ? SDL_WINDOWPOS_CENTERED : Config::WindowX; - int32_t y = recenter ? SDL_WINDOWPOS_CENTERED : Config::WindowY; - - SDL_SetWindowSize(s_window, width, height); - SDL_SetWindowMinimumSize(s_window, 640, 480); - SDL_SetWindowPosition(s_window, x, y); - - s_width = width; - s_height = height; - - if (!isPendingMaximise) - return; - - // Maximise window if the config resolution matches the display. - SDL_MaximizeWindow(s_window); - } - - static void AddResizeEvent(std::function resizeEvent) - { - ms_resizeEvents.push_back(resizeEvent); - } - - static void RaiseResizeEvents() - { - for (const auto& resizeEvent : ms_resizeEvents) - resizeEvent(s_width, s_height); + return false; } static void Init(); diff --git a/UnleashedRecomp/ui/window_events.h b/UnleashedRecomp/ui/window_events.h new file mode 100644 index 0000000..384f167 --- /dev/null +++ b/UnleashedRecomp/ui/window_events.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include "ui/window.h" + +inline static void SDL_ResizeEvent(SDL_Window* pWindow, int width, int height) +{ + SDL_Event event{}; + event.type = SDL_WINDOWEVENT; + event.window.event = SDL_WINDOWEVENT_RESIZED; + event.window.windowID = SDL_GetWindowID(pWindow); + event.window.data1 = width; + event.window.data2 = height; + + SDL_PushEvent(&event); +} + +inline static void SDL_MoveEvent(SDL_Window* pWindow, int x, int y) +{ + SDL_Event event{}; + event.type = SDL_WINDOWEVENT; + event.window.event = SDL_WINDOWEVENT_MOVED; + event.window.windowID = SDL_GetWindowID(pWindow); + event.window.data1 = x; + event.window.data2 = y; + + SDL_PushEvent(&event); +}