From f3a834a8014920e4ebac34ec9662e71ab947e932 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Wed, 17 Jul 2024 21:00:30 +0200 Subject: [PATCH 1/7] fix: Crash due to use of invalid style pointers after restart --- plugins/builtin/source/content/themes.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/builtin/source/content/themes.cpp b/plugins/builtin/source/content/themes.cpp index 09cb03313d57d..c305556a0de08 100644 --- a/plugins/builtin/source/content/themes.cpp +++ b/plugins/builtin/source/content/themes.cpp @@ -258,7 +258,8 @@ namespace hex::plugin::builtin { RequestInitThemeHandlers::subscribe([] { { auto &style = ImGui::GetStyle(); - const static ThemeManager::StyleMap ImGuiStyleMap = { + static ThemeManager::StyleMap ImGuiStyleMap; + ImGuiStyleMap = { { "alpha", { &style.Alpha, 0.1F, 1.0F, false } }, { "disabled-alpha", { &style.DisabledAlpha, 0.0F, 1.0F, false } }, { "window-padding", { &style.WindowPadding, 0.0F, 20.0F, true } }, @@ -294,7 +295,8 @@ namespace hex::plugin::builtin { { auto &style = ImPlot::GetStyle(); - const static ThemeManager::StyleMap ImPlotStyleMap = { + static ThemeManager::StyleMap ImPlotStyleMap; + ImPlotStyleMap = { { "line-weight", { &style.LineWeight, 0.0F, 5.0F, true } }, { "marker-size", { &style.MarkerSize, 2.0F, 10.0F, true } }, { "marker-weight", { &style.MarkerWeight, 0.0F, 5.0F, true } }, @@ -328,7 +330,8 @@ namespace hex::plugin::builtin { { auto &style = ImNodes::GetStyle(); - const static ThemeManager::StyleMap ImNodesStyleMap = { + static ThemeManager::StyleMap ImNodesStyleMap; + ImNodesStyleMap = { { "grid-spacing", { &style.GridSpacing, 0.0F, 100.0F, true } }, { "node-corner-rounding", { &style.NodeCornerRounding, 0.0F, 12.0F, true } }, { "node-padding", { &style.NodePadding, 0.0F, 20.0F, true } }, @@ -351,8 +354,9 @@ namespace hex::plugin::builtin { { auto &style = ImGuiExt::GetCustomStyle(); - const static ThemeManager::StyleMap ImHexStyleMap = { - { "window-blur", { &style.WindowBlur, 0.0F, 1.0F, true } }, + static ThemeManager::StyleMap ImHexStyleMap; + ImHexStyleMap = { + { "window-blur", { &style.WindowBlur, 0.0F, 1.0F, true } }, { "popup-alpha", { &style.PopupWindowAlpha, 0.0F, 1.0F, false } }, }; From a408e65a23574a5bde0f470c0ebeb9733764c901 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Sun, 14 Jul 2024 15:50:32 -0500 Subject: [PATCH 2/7] feat: Implement cross-platform DPI scaling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This replaces the Windows-only code with code that should work on all the platforms. Tested on Linux/Wayland. The “Scaling” option is now just treated like a normal UI zoom control for users who want a larger UI. This means “Native” scaling option is removed now since that does not mean anything. --- lib/libimhex/CMakeLists.txt | 1 + .../include/hex/api/event_manager.hpp | 2 +- lib/libimhex/include/hex/api/imhex_api.hpp | 19 +- .../include/hex/helpers/utils_macos.hpp | 1 - lib/libimhex/include/hex/ui/glfw_di.h | 42 ++++ lib/libimhex/source/api/imhex_api.cpp | 22 +- lib/libimhex/source/helpers/utils.cpp | 8 +- lib/libimhex/source/helpers/utils_macos.m | 4 - lib/libimhex/source/ui/glfw_di.cpp | 223 ++++++++++++++++++ .../source/ui/imgui_imhex_extensions.cpp | 10 +- .../imgui/custom/source/imgui_impl_glfw.cpp | 44 +++- main/gui/source/init/splash_window.cpp | 49 ++-- main/gui/source/window/linux_window.cpp | 11 +- main/gui/source/window/macos_window.cpp | 7 +- main/gui/source/window/web_window.cpp | 3 +- main/gui/source/window/win_window.cpp | 39 +-- main/gui/source/window/window.cpp | 97 ++++---- plugins/builtin/romfs/lang/de_DE.json | 1 - plugins/builtin/romfs/lang/en_US.json | 1 - plugins/builtin/romfs/lang/es_ES.json | 1 - plugins/builtin/romfs/lang/hu_HU.json | 1 - plugins/builtin/romfs/lang/it_IT.json | 1 - plugins/builtin/romfs/lang/ja_JP.json | 1 - plugins/builtin/romfs/lang/ko_KR.json | 1 - plugins/builtin/romfs/lang/pt_BR.json | 1 - plugins/builtin/romfs/lang/zh_CN.json | 1 - plugins/builtin/romfs/lang/zh_TW.json | 1 - plugins/builtin/source/content/events.cpp | 5 +- plugins/builtin/source/content/init_tasks.cpp | 29 +-- .../source/content/main_menu_items.cpp | 12 +- .../source/content/out_of_box_experience.cpp | 6 +- .../source/content/settings_entries.cpp | 17 +- .../builtin/source/content/welcome_screen.cpp | 2 +- plugins/fonts/source/font_loader.cpp | 89 +++---- 34 files changed, 491 insertions(+), 261 deletions(-) create mode 100644 lib/libimhex/include/hex/ui/glfw_di.h create mode 100644 lib/libimhex/source/ui/glfw_di.cpp diff --git a/lib/libimhex/CMakeLists.txt b/lib/libimhex/CMakeLists.txt index abcd9677faa54..9752d1aab0ace 100644 --- a/lib/libimhex/CMakeLists.txt +++ b/lib/libimhex/CMakeLists.txt @@ -45,6 +45,7 @@ set(LIBIMHEX_SOURCES source/providers/memory_provider.cpp source/providers/undo/stack.cpp + source/ui/glfw_di.cpp source/ui/imgui_imhex_extensions.cpp source/ui/view.cpp source/ui/popup.cpp diff --git a/lib/libimhex/include/hex/api/event_manager.hpp b/lib/libimhex/include/hex/api/event_manager.hpp index 43da0a12be902..63e7955aebddc 100644 --- a/lib/libimhex/include/hex/api/event_manager.hpp +++ b/lib/libimhex/include/hex/api/event_manager.hpp @@ -226,7 +226,7 @@ namespace hex { EVENT_DEF(EventAbnormalTermination, int); EVENT_DEF(EventThemeChanged); EVENT_DEF(EventOSThemeChanged); - EVENT_DEF(EventDPIChanged, float, float); + EVENT_DEF(EventDPIChanged, float); EVENT_DEF(EventWindowFocused, bool); /** diff --git a/lib/libimhex/include/hex/api/imhex_api.hpp b/lib/libimhex/include/hex/api/imhex_api.hpp index 2ecd25c1b0394..6d10ce60479bf 100644 --- a/lib/libimhex/include/hex/api/imhex_api.hpp +++ b/lib/libimhex/include/hex/api/imhex_api.hpp @@ -426,8 +426,8 @@ namespace hex { void setMainDockSpaceId(ImGuiID id); void setMainWindowHandle(GLFWwindow *window); - void setGlobalScale(float scale); - void setNativeScale(float scale); + void setContentScale(float scale); + void setUserScale(float scale); void setBorderlessWindowMode(bool enabled); void setMultiWindowMode(bool enabled); @@ -481,17 +481,22 @@ namespace hex { /** - * @brief Gets the current global scale - * @return The current global scale + * @brief Gets the combined window content and user scale + * @return The combined window content and user scale */ float getGlobalScale(); /** - * @brief Gets the current native scale - * @return The current native scale + * @brief Gets the window content scale + * @return The window content scale */ - float getNativeScale(); + float getContentScale(); + /** + * @brief Gets the user requested scale + * @return The user requested scale + */ + float getUserScale(); /** * @brief Gets the current main window position diff --git a/lib/libimhex/include/hex/helpers/utils_macos.hpp b/lib/libimhex/include/hex/helpers/utils_macos.hpp index 5c025356e99af..533a8a1cf8fa5 100644 --- a/lib/libimhex/include/hex/helpers/utils_macos.hpp +++ b/lib/libimhex/include/hex/helpers/utils_macos.hpp @@ -10,7 +10,6 @@ void openWebpageMacos(const char *url); bool isMacosSystemDarkModeEnabled(); bool isMacosFullScreenModeEnabled(GLFWwindow *window); - float getBackingScaleFactor(); void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode); diff --git a/lib/libimhex/include/hex/ui/glfw_di.h b/lib/libimhex/include/hex/ui/glfw_di.h new file mode 100644 index 0000000000000..824ffded3bb3c --- /dev/null +++ b/lib/libimhex/include/hex/ui/glfw_di.h @@ -0,0 +1,42 @@ +#pragma once + +struct GLFWwindow; +struct GLFWmonitor; +typedef void (* GLFWcursorposfun)(GLFWwindow* window, double diX, double diY); +typedef void (* GLFWframebuffersizefun)(GLFWwindow* window, int width, int height); +typedef void (* GLFWwindowcontentscalefun)(GLFWwindow* window, float xscale, float yscale); +typedef void (* GLFWwindowposfun)(GLFWwindow* window, int diX, int diY); +typedef void (* GLFWwindowsizefun)(GLFWwindow* window, int diWidth, int diHeight); + +namespace hex { + /** + * These functions translate GLFW coordinates to/from a device-independent + * coordinate system so application code does not need to perform any + * platform-specific scaling transformations itself. + * + * This ought to be handled by GLFW, but GLFW <=3.4 leaks the platform’s + * coordinate system instead of abstracting it. Some platforms use a + * device-independent coordinate system (Wayland, macOS, Web) where others + * do not (X11, Win32), and this detail should not be leaking into + * application code. + */ + namespace glfw { + GLFWwindow *CreateWindow(int diWidth, int diHeight, const char* title, GLFWmonitor* monitor, GLFWwindow* share); + void DestroyWindow(GLFWwindow *window); + void *GetWindowUserPointer(GLFWwindow *window); + void *SetWindowUserPointer(GLFWwindow *window, void *pointer); + void GetMonitorPos(GLFWmonitor *monitor, int *diX, int *diY); + bool GetMonitorSize(GLFWmonitor *monitor, int *diWidth, int *diHeight); + void SetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int diXpos, int diYpos, int diWidth, int diHeight, int refreshRate); + void GetWindowPos(GLFWwindow *window, int *diX, int *diY); + void SetWindowPos(GLFWwindow *window, int diX, int diY); + void GetWindowSize(GLFWwindow *window, int *diWidth, int *diHeight); + void SetWindowSize(GLFWwindow *window, int diWidth, int diHeight); + void SetWindowSizeLimits(GLFWwindow *window, int diMinWidth, int diMinHeight, int diMaxWidth, int diMaxHeight); + GLFWcursorposfun SetCursorPosCallback(GLFWwindow *window, GLFWcursorposfun callback); + GLFWwindowposfun SetFramebufferSizeCallback(GLFWwindow *window, GLFWframebuffersizefun callback); + GLFWwindowcontentscalefun SetWindowContentScaleCallback(GLFWwindow *window, GLFWwindowcontentscalefun callback); + GLFWwindowposfun SetWindowPosCallback(GLFWwindow *window, GLFWwindowposfun callback); + GLFWwindowsizefun SetWindowSizeCallback(GLFWwindow *window, GLFWwindowsizefun callback); + } +} diff --git a/lib/libimhex/source/api/imhex_api.cpp b/lib/libimhex/source/api/imhex_api.cpp index 4d48152c21a99..fd992b162f881 100644 --- a/lib/libimhex/source/api/imhex_api.cpp +++ b/lib/libimhex/source/api/imhex_api.cpp @@ -501,17 +501,16 @@ namespace hex { } - static float s_globalScale = 1.0; - void setGlobalScale(float scale) { - s_globalScale = scale; + static float s_contentScale = 1.0; + void setContentScale(float scale) { + s_contentScale = scale; } - static float s_nativeScale = 1.0; - void setNativeScale(float scale) { - s_nativeScale = scale; + static float s_userScale = 1.0; + void setUserScale(float scale) { + s_userScale = scale; } - static bool s_borderlessWindowMode; void setBorderlessWindowMode(bool enabled) { s_borderlessWindowMode = enabled; @@ -598,13 +597,16 @@ namespace hex { } float getGlobalScale() { - return impl::s_globalScale; + return getUserScale() * getContentScale(); } - float getNativeScale() { - return impl::s_nativeScale; + float getContentScale() { + return impl::s_contentScale; } + float getUserScale() { + return impl::s_userScale; + } ImVec2 getMainWindowPosition() { if ((ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) != ImGuiConfigFlags_None) diff --git a/lib/libimhex/source/helpers/utils.cpp b/lib/libimhex/source/helpers/utils.cpp index b8a71e9e50e3d..44a9cabf03722 100644 --- a/lib/libimhex/source/helpers/utils.cpp +++ b/lib/libimhex/source/helpers/utils.cpp @@ -29,19 +29,19 @@ namespace hex { float operator""_scaled(long double value) { - return value * ImHexApi::System::getGlobalScale(); + return value * ImHexApi::System::getUserScale(); } float operator""_scaled(unsigned long long value) { - return value * ImHexApi::System::getGlobalScale(); + return value * ImHexApi::System::getUserScale(); } ImVec2 scaled(const ImVec2 &vector) { - return vector * ImHexApi::System::getGlobalScale(); + return vector * ImHexApi::System::getUserScale(); } ImVec2 scaled(float x, float y) { - return ImVec2(x, y) * ImHexApi::System::getGlobalScale(); + return ImVec2(x, y) * ImHexApi::System::getUserScale(); } std::string to_string(u128 value) { diff --git a/lib/libimhex/source/helpers/utils_macos.m b/lib/libimhex/source/helpers/utils_macos.m index 4436232b98e6a..6fe739cf8c9c6 100644 --- a/lib/libimhex/source/helpers/utils_macos.m +++ b/lib/libimhex/source/helpers/utils_macos.m @@ -42,10 +42,6 @@ bool isMacosSystemDarkModeEnabled(void) { } } - float getBackingScaleFactor(void) { - return [[NSScreen mainScreen] backingScaleFactor]; - } - void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode) { NSWindow* cocoaWindow = glfwGetCocoaWindow(window); diff --git a/lib/libimhex/source/ui/glfw_di.cpp b/lib/libimhex/source/ui/glfw_di.cpp new file mode 100644 index 0000000000000..bf77330577115 --- /dev/null +++ b/lib/libimhex/source/ui/glfw_di.cpp @@ -0,0 +1,223 @@ +#include + +#include + +#include +#include + +namespace hex::glfw { + struct GlfwData { + void *userPointer = nullptr; + GLFWcursorposfun userCursorPosFn = nullptr; + GLFWframebuffersizefun userFramebufferSizeFn = nullptr; + GLFWwindowcontentscalefun userWindowContentScaleFn = nullptr; + GLFWwindowposfun userWindowPosFn = nullptr; + GLFWwindowsizefun userWindowSizeFn = nullptr; + float scaleX = 1.0F; + float scaleY = 1.0F; + }; + + static void RecalculateScale(GLFWwindow *window, GlfwData *data) { + int width, height, framebufferWidth, framebufferHeight; + float scaleX, scaleY; + glfwGetWindowSize(window, &width, &height); + glfwGetFramebufferSize(window, &framebufferWidth, &framebufferHeight); + glfwGetWindowContentScale(window, &scaleX, &scaleY); + data->scaleX = scaleX / (float(framebufferWidth) / width); + data->scaleY = scaleY / (float(framebufferHeight) / height); + } + + static GlfwData* GetWindowData(GLFWwindow *window) { + if (!window) + return nullptr; + + return static_cast(glfwGetWindowUserPointer(window)); + } + + template + void FromGLFW(GLFWwindow *window, T *x, T *y) { + auto data = GetWindowData(window); + if (!data) + throw std::runtime_error("Missing window data; did you use glfwCreateWindow directly instead of hex::glfw::CreateWindow?"); + if (x) + *x /= data->scaleX; + if (y) + *y /= data->scaleY; + } + + template + static void FromGLFW(GLFWmonitor *monitor, T *x, T *y) { + float xScale, yScale; + glfwGetMonitorContentScale(monitor, &xScale, &yScale); + if (x) + *x /= xScale; + if (y) + *y /= yScale; + } + + template + void ToGLFW(GLFWwindow *window, T *x, T *y) { + auto data = GetWindowData(window); + if (!data) + throw std::runtime_error("Missing window data; did you use glfwCreateWindow directly instead of hex::glfw::CreateWindow?"); + if (x) + *x *= data->scaleX; + if (y) + *y *= data->scaleY; + } + + template + static void ToGLFW(GLFWmonitor *monitor, T *x, T *y) { + float xScale, yScale; + glfwGetMonitorContentScale(monitor, &xScale, &yScale); + if (x) + *x *= xScale; + if (y) + *y *= yScale; + } + + GLFWwindow* CreateWindow(int diWidth, int diHeight, const char* title, GLFWmonitor* monitor, GLFWwindow* share) { + auto window = glfwCreateWindow(1, 1, title, monitor, share); + if (!window) + return nullptr; + + auto *data = new (std::nothrow) GlfwData; + if (!data) { + glfwDestroyWindow(window); + return nullptr; + } + + glfwSetWindowUserPointer(window, data); + RecalculateScale(window, data); + glfwSetWindowSize(window, diWidth * data->scaleX, diHeight * data->scaleY); + glfwSetFramebufferSizeCallback(window, [](GLFWwindow *window, int width, int height) { + auto data = GetWindowData(window); + RecalculateScale(window, data); + if (data->userFramebufferSizeFn) + (data->userFramebufferSizeFn)(window, width, height); + }); + glfwSetWindowContentScaleCallback(window, [](GLFWwindow *window, float scaleX, float scaleY) { + auto data = GetWindowData(window); + RecalculateScale(window, data); + if (data->userWindowContentScaleFn) + (data->userWindowContentScaleFn)(window, scaleX, scaleY); + }); + + return window; + } + + void DestroyWindow(GLFWwindow *window) { + if (!window) + return; + + delete GetWindowData(window); + glfwDestroyWindow(window); + } + + void *GetWindowUserPointer(GLFWwindow *window) { + if (!window) + return nullptr; + + return GetWindowData(window)->userPointer; + } + + void *SetWindowUserPointer(GLFWwindow *window, void *pointer) { + if (!window) + return nullptr; + + auto data = GetWindowData(window); + auto prev = data->userPointer; + data->userPointer = pointer; + return prev; + } + + bool GetMonitorSize(GLFWmonitor *monitor, int *diWidth, int *diHeight) { + auto mode = glfwGetVideoMode(monitor); + if (!mode) + return false; + + if (diWidth) + *diWidth = mode->width; + if (diHeight) + *diHeight = mode->height; + FromGLFW(monitor, diWidth, diHeight); + + return true; + } + + void GetMonitorPos(GLFWmonitor *monitor, int *diX, int *diY) { + glfwGetMonitorPos(monitor, diX, diY); + FromGLFW(monitor, diX, diY); + } + + void SetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int diXpos, int diYpos, int diWidth, int diHeight, int refreshRate) { + if (monitor) + ToGLFW(monitor, &diXpos, &diYpos); + else + ToGLFW(window, &diXpos, &diYpos); + + ToGLFW(window, &diWidth, &diHeight); + glfwSetWindowMonitor(window, monitor, diXpos, diYpos, diWidth, diHeight, refreshRate); + } + + void GetWindowPos(GLFWwindow *window, int *diX, int *diY) { + glfwGetWindowPos(window, diX, diY); + FromGLFW(window, diX, diY); + } + + void SetWindowPos(GLFWwindow *window, int diX, int diY) { + ToGLFW(window, &diX, &diY); + glfwSetWindowPos(window, diX, diY); + } + + void GetWindowSize(GLFWwindow *window, int *diWidth, int *diHeight) { + glfwGetWindowSize(window, diWidth, diHeight); + FromGLFW(window, diWidth, diHeight); + } + + void SetWindowSize(GLFWwindow *window, int diWidth, int diHeight) { + ToGLFW(window, &diWidth, &diHeight); + glfwSetWindowSize(window, diWidth, diHeight); + } + + void SetWindowSizeLimits(GLFWwindow *window, int diMinWidth, int diMinHeight, int diMaxWidth, int diMaxHeight) { + ToGLFW(window, + diMinWidth == GLFW_DONT_CARE ? nullptr : &diMinWidth, + diMinHeight == GLFW_DONT_CARE ? nullptr : &diMinHeight); + ToGLFW(window, + diMaxWidth == GLFW_DONT_CARE ? nullptr : &diMaxWidth, + diMaxHeight == GLFW_DONT_CARE ? nullptr : &diMaxHeight); + glfwSetWindowSizeLimits(window, diMinWidth, diMinHeight, diMaxWidth, diMaxHeight); + } + + #define IMHEX_WINDOW_CALLBACK_SCALE(fnName, cbType, paramType) \ + static void fnName##Wrapper(GLFWwindow *window, paramType x, paramType y) { \ + FromGLFW(window, &x, &y); \ + (GetWindowData(window)->user##fnName##Fn)(window, x, y); \ + } \ + \ + cbType Set##fnName##Callback(GLFWwindow *window, cbType callback) { \ + auto data = GetWindowData(window); \ + auto previous = data->user##fnName##Fn; \ + data->user##fnName##Fn = callback; \ + glfwSet##fnName##Callback(window, callback ? fnName##Wrapper : nullptr);\ + return previous; \ + } + + IMHEX_WINDOW_CALLBACK_SCALE(CursorPos, GLFWcursorposfun, double) + IMHEX_WINDOW_CALLBACK_SCALE(WindowPos, GLFWwindowposfun, int) + IMHEX_WINDOW_CALLBACK_SCALE(WindowSize, GLFWwindowsizefun, int) + #undef IMHEX_WINDOW_CALLBACK_SCALE + + #define IMHEX_WINDOW_CALLBACK_RECALC(fnName, cbType) \ + cbType Set##fnName##Callback(GLFWwindow *window, cbType callback) { \ + auto data = GetWindowData(window); \ + auto previous = data->user##fnName##Fn; \ + data->user##fnName##Fn = callback; \ + return previous; \ + } + + IMHEX_WINDOW_CALLBACK_RECALC(FramebufferSize, GLFWframebuffersizefun) + IMHEX_WINDOW_CALLBACK_RECALC(WindowContentScale, GLFWwindowcontentscalefun) + #undef IMHEX_WINDOW_CALLBACK_RECALC +} diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp index 5d4d6a589d484..58c1f5a9770db 100644 --- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp +++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp @@ -23,11 +23,13 @@ #include #include #include +#include namespace ImGuiExt { using namespace ImGui; + using hex::operator""_scaled; namespace { @@ -500,8 +502,8 @@ namespace ImGuiExt { RenderTextClipped(bb.Min + style.FramePadding * 2 + ImVec2(style.FramePadding.x * 2, label_size.y), bb.Max - style.FramePadding, description, nullptr, &text_size, style.ButtonTextAlign, &clipBb); PopStyleColor(); - RenderFrame(ImVec2(bb.Min.x, bb.Max.y - 5 * hex::ImHexApi::System::getGlobalScale()), bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), false, style.FrameRounding); - RenderFrame(ImVec2(bb.Min.x, bb.Max.y - 5 * hex::ImHexApi::System::getGlobalScale()), ImVec2(bb.Min.x + fraction * bb.GetSize().x, bb.Max.y), GetColorU32(ImGuiCol_Button), false, style.FrameRounding); + RenderFrame(ImVec2(bb.Min.x, bb.Max.y - 5_scaled), bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), false, style.FrameRounding); + RenderFrame(ImVec2(bb.Min.x, bb.Max.y - 5_scaled), ImVec2(bb.Min.x + fraction * bb.GetSize().x, bb.Max.y), GetColorU32(ImGuiCol_Button), false, style.FrameRounding); RenderFrame(bb.Min, bb.Max, 0x00, true, style.FrameRounding); PopStyleVar(2); @@ -579,7 +581,7 @@ namespace ImGuiExt { if (!std::string_view(text).empty()) { const auto textWidth = CalcTextSize(text).x; - const auto maxWidth = 300 * hex::ImHexApi::System::getGlobalScale(); + const auto maxWidth = 300_scaled; const bool wrapping = textWidth > maxWidth; if (wrapping) @@ -912,7 +914,7 @@ namespace ImGuiExt { const ImGuiStyle &style = g.Style; ImVec2 pos = window->DC.CursorPos + ImVec2(0, yOffset); - ImVec2 size = CalcItemSize(ImVec2(100, 5) * hex::ImHexApi::System::getGlobalScale(), 100, g.FontSize + style.FramePadding.y * 2.0F); + ImVec2 size = CalcItemSize(ImVec2(100_scaled, 5_scaled), 100, g.FontSize + style.FramePadding.y * 2.0F); ImRect bb(pos, pos + size); ItemSize(size, 0); if (!ItemAdd(bb, 0)) diff --git a/lib/third_party/imgui/custom/source/imgui_impl_glfw.cpp b/lib/third_party/imgui/custom/source/imgui_impl_glfw.cpp index b78f4429ef07d..e13a64a9852c2 100644 --- a/lib/third_party/imgui/custom/source/imgui_impl_glfw.cpp +++ b/lib/third_party/imgui/custom/source/imgui_impl_glfw.cpp @@ -77,6 +77,7 @@ #include "imgui.h" #ifndef IMGUI_DISABLE #include "imgui_impl_glfw.h" +#include // Clang warnings with -Weverything #if defined(__clang__) @@ -164,6 +165,15 @@ struct ImGui_ImplGlfw_Data bool InstalledCallbacks; bool CallbacksChainForAllWindows; bool WantUpdateMonitors; + // GLFW’s (<=3.4) coordinate space is a leaky abstraction of the platform’s + // coordinate system. Some platforms are already device-independent + // (Wayland, macOS, Web); others are not (X11, Win32). ScaleCoordinates is + // the value required to convert between DI coordinates and GLFW coordinates + // so that the application only sees DI coordinates. Notably, this is NOT + // the same as GLFW’s content scale; content scale describes the pixel + // aspect ratio of the framebuffer and is the same across all platforms + // regardless of coordinate system. + ImVec2 ScaleCoordinates; #ifdef __EMSCRIPTEN__ const char* CanvasSelector; #endif @@ -473,6 +483,8 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) x += window_x; y += window_y; } + x /= bd->ScaleCoordinates.x; + y /= bd->ScaleCoordinates.y; io.AddMousePosEvent((float)x, (float)y); bd->LastValidMousePos = ImVec2((float)x, (float)y); } @@ -611,6 +623,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw bd->Window = window; bd->Time = 0.0; bd->WantUpdateMonitors = true; + bd->ScaleCoordinates = ImVec2(1.0F, 1.0F); // IMHEX PATCH BEGIN #ifdef __EMSCRIPTEN__ @@ -776,6 +789,8 @@ static void ImGui_ImplGlfw_UpdateMouseData() mouse_x += window_x; mouse_y += window_y; } + mouse_x /= bd->ScaleCoordinates.x; + mouse_y /= bd->ScaleCoordinates.y; bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y); io.AddMousePosEvent((float)mouse_x, (float)mouse_y); } @@ -929,7 +944,6 @@ static void ImGui_ImplGlfw_UpdateMonitors() // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. float x_scale, y_scale; glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale); - monitor.DpiScale = x_scale; // IMHEX PATCH BEGIN // REASON: Prevent occasional crash when a monitor connection status is changed @@ -951,11 +965,33 @@ void ImGui_ImplGlfw_NewFrame() // Setup display size (every frame to accommodate for window resizing) int w, h; int display_w, display_h; + float scale_w, scale_h; glfwGetWindowSize(bd->Window, &w, &h); glfwGetFramebufferSize(bd->Window, &display_w, &display_h); + glfwGetWindowContentScale(bd->Window, &scale_w, &scale_h); io.DisplaySize = ImVec2((float)w, (float)h); if (w > 0 && h > 0) io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); + + // Since `ImGui_ImplGlfw_NewFrame` updates `DisplaySize` and + // `DisplayFramebufferScale` immediately before calling + // `ImGui_ImplGlfw_UpdateMouseData` (which needs `ScaleCoordinates` to be + // accurate to translate mouse events into DI space), it seems necessary to + // calculate `ScaleCoordinates` here. + // GLFW (<=3.4) also does not update content scale and framebuffer size + // atomically on at least Wayland, and there is no documented order for the + // `glfwSetFramebufferSizeCallback` and `glfwSetContentScaleCallback` + // callback functions, so it also seems they could not be used reliably even + // if this function were modified to stop setting `DisplaySize` and + // `DisplayFramebufferScale` on every frame. + bd->ScaleCoordinates = ImVec2(scale_w / io.DisplayFramebufferScale.x, scale_h / io.DisplayFramebufferScale.y); + if (std::abs(bd->ScaleCoordinates.x - 1.0F) < .001F && std::abs(bd->ScaleCoordinates.y - 1.0F) < .001F) { + bd->ScaleCoordinates = ImVec2(1.0F, 1.0F); + } else { + io.DisplaySize /= bd->ScaleCoordinates; + io.DisplayFramebufferScale = bd->ScaleCoordinates; + } + if (bd->WantUpdateMonitors) ImGui_ImplGlfw_UpdateMonitors(); @@ -1189,10 +1225,11 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport) { + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; int x = 0, y = 0; glfwGetWindowPos(vd->Window, &x, &y); - return ImVec2((float)x, (float)y); + return ImVec2((float)x, (float)y) / bd->ScaleCoordinates; } static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) @@ -1207,7 +1244,7 @@ static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; int w = 0, h = 0; glfwGetWindowSize(vd->Window, &w, &h); - return ImVec2((float)w, (float)h); + return ImVec2((float)w, (float)h) / ImGui_ImplGlfw_GetBackendData()->ScaleCoordinates; } static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) @@ -1224,6 +1261,7 @@ static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) glfwSetWindowPos(vd->Window, x, y - height + size.y); #endif vd->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount(); + size *= ImGui_ImplGlfw_GetBackendData()->ScaleCoordinates; glfwSetWindowSize(vd->Window, (int)size.x, (int)size.y); } diff --git a/main/gui/source/init/splash_window.cpp b/main/gui/source/init/splash_window.cpp index 5cb2d396030fc..69004573f6cbd 100644 --- a/main/gui/source/init/splash_window.cpp +++ b/main/gui/source/init/splash_window.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -77,20 +78,20 @@ namespace hex::init { return; // Get information about the monitor - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - if (!mode) + int monitorWidth, monitorHeight; + if (!hex::glfw::GetMonitorSize(monitor, &monitorWidth, &monitorHeight)) return; // Get the position of the monitor's viewport on the virtual screen int monitorX, monitorY; - glfwGetMonitorPos(monitor, &monitorX, &monitorY); + hex::glfw::GetMonitorPos(monitor, &monitorX, &monitorY); // Get the window size int windowWidth, windowHeight; - glfwGetWindowSize(window, &windowWidth, &windowHeight); + hex::glfw::GetWindowSize(window, &windowWidth, &windowHeight); // Center the splash screen on the monitor - glfwSetWindowPos(window, monitorX + (mode->width - windowWidth) / 2, monitorY + (mode->height - windowHeight) / 2); + hex::glfw::SetWindowPos(window, monitorX + (monitorWidth - windowWidth) / 2, monitorY + (monitorHeight - windowHeight) / 2); } static ImColor getHighlightColor(u32 index) { @@ -251,7 +252,7 @@ namespace hex::init { FrameResult WindowSplash::fullFrame() { - glfwSetWindowSize(m_window, 640, 400); + hex::glfw::SetWindowSize(m_window, 640, 400); centerWindow(m_window); glfwPollEvents(); @@ -426,7 +427,6 @@ namespace hex::init { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, GLFW_TRUE); #else glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); @@ -445,8 +445,7 @@ namespace hex::init { glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); - // Create the splash screen window - m_window = glfwCreateWindow(1, 1, "Starting ImHex...", nullptr, nullptr); + m_window = hex::glfw::CreateWindow(1, 1, "Starting ImHex...", nullptr, nullptr); if (m_window == nullptr) { hex::nativeErrorMessage(hex::format( "Failed to create GLFW window: [{}] {}.\n" @@ -460,27 +459,6 @@ namespace hex::init { // Force window to be fully opaque by default glfwSetWindowOpacity(m_window, 1.0F); - // Calculate native scale factor for hidpi displays - { - float xScale = 0, yScale = 0; - glfwGetWindowContentScale(m_window, &xScale, &yScale); - - auto meanScale = std::midpoint(xScale, yScale); - if (meanScale <= 0.0F) - meanScale = 1.0F; - - #if defined(OS_MACOS) - meanScale /= getBackingScaleFactor(); - #elif defined(OS_WEB) - meanScale = 1.0F; - #endif - - ImHexApi::System::impl::setGlobalScale(meanScale); - ImHexApi::System::impl::setNativeScale(meanScale); - - log::info("Native scaling set to: {:.1f}", meanScale); - } - glfwMakeContextCurrent(m_window); glfwSwapInterval(1); } @@ -502,17 +480,20 @@ namespace hex::init { ImGui_ImplOpenGL3_Init("#version 130"); #endif - auto &io = ImGui::GetIO(); + float contentScale; + glfwGetWindowContentScale(m_window, &contentScale, nullptr); - ImGui::GetStyle().ScaleAllSizes(ImHexApi::System::getGlobalScale()); + auto &io = ImGui::GetIO(); // Load fonts necessary for the splash screen { io.Fonts->Clear(); ImFontConfig cfg; - cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true; + cfg.OversampleH = cfg.OversampleV = 1; + cfg.PixelSnapH = true; cfg.SizePixels = ImHexApi::Fonts::DefaultFontSize; + cfg.RasterizerDensity = contentScale; io.Fonts->AddFontDefault(&cfg); std::uint8_t *px; @@ -574,7 +555,7 @@ namespace hex::init { } void WindowSplash::exitGLFW() const { - glfwDestroyWindow(m_window); + hex::glfw::DestroyWindow(m_window); glfwTerminate(); } diff --git a/main/gui/source/window/linux_window.cpp b/main/gui/source/window/linux_window.cpp index 60fbbdd8e7e3b..309e6a609a474 100644 --- a/main/gui/source/window/linux_window.cpp +++ b/main/gui/source/window/linux_window.cpp @@ -11,6 +11,8 @@ #include #include + #include + #include #include @@ -18,7 +20,6 @@ #include #include - #include #include #include @@ -74,10 +75,6 @@ namespace hex { } void Window::configureGLFW() { - #if defined(GLFW_SCALE_FRAMEBUFFER) - glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE); - #endif - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_DECORATED, ImHexApi::System::isBorderlessWindowModeEnabled() ? GL_FALSE : GL_TRUE); @@ -140,7 +137,7 @@ namespace hex { }); glfwSetWindowRefreshCallback(m_window, [](GLFWwindow *window) { - auto win = static_cast(glfwGetWindowUserPointer(window)); + auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); win->fullFrame(); }); @@ -156,4 +153,4 @@ namespace hex { } -#endif \ No newline at end of file +#endif diff --git a/main/gui/source/window/macos_window.cpp b/main/gui/source/window/macos_window.cpp index 50d1b4c4d4dd1..ca766188cdbe8 100644 --- a/main/gui/source/window/macos_window.cpp +++ b/main/gui/source/window/macos_window.cpp @@ -11,11 +11,11 @@ #include #include + #include + #include #include - #include - namespace hex { void nativeErrorMessage(const std::string &message) { @@ -27,7 +27,6 @@ namespace hex { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, GLFW_TRUE); glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); } @@ -99,7 +98,7 @@ namespace hex { setupMacosWindowStyle(m_window, ImHexApi::System::isBorderlessWindowModeEnabled()); glfwSetWindowRefreshCallback(m_window, [](GLFWwindow *window) { - auto win = static_cast(glfwGetWindowUserPointer(window)); + auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); win->fullFrame(); }); } diff --git a/main/gui/source/window/web_window.cpp b/main/gui/source/window/web_window.cpp index 827cf72f0b17a..0d5a44cc264fa 100644 --- a/main/gui/source/window/web_window.cpp +++ b/main/gui/source/window/web_window.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -119,7 +120,7 @@ namespace hex { }); glfwSetWindowRefreshCallback(m_window, [](GLFWwindow *window) { - auto win = static_cast(glfwGetWindowUserPointer(window)); + auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); resizeCanvas(); win->fullFrame(); }); diff --git a/main/gui/source/window/win_window.cpp b/main/gui/source/window/win_window.cpp index 4ff534fd3e682..d56caa369e51d 100644 --- a/main/gui/source/window/win_window.cpp +++ b/main/gui/source/window/win_window.cpp @@ -1,5 +1,4 @@ #include -#include #include "window.hpp" @@ -11,6 +10,7 @@ #include #include #include + #include #include #include @@ -48,22 +48,6 @@ namespace hex { // Custom Window procedure for receiving OS events static LRESULT commonWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { - case WM_DPICHANGED: { - int interfaceScaleSetting = int(hex::ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling_factor", 0.0F) * 10.0F); - if (interfaceScaleSetting != 0) - break; - - const auto newScale = LOWORD(wParam) / 96.0F; - const auto oldScale = ImHexApi::System::getNativeScale(); - - EventDPIChanged::post(oldScale, newScale); - ImHexApi::System::impl::setNativeScale(newScale); - - ThemeManager::reapplyCurrentTheme(); - ImGui::GetStyle().ScaleAllSizes(newScale); - - return TRUE; - } case WM_COPYDATA: { // Handle opening files in existing instance @@ -390,18 +374,6 @@ namespace hex { void Window::initNative() { - // Setup DPI Awareness - { - using SetProcessDpiAwarenessContextFunc = HRESULT(WINAPI *)(DPI_AWARENESS_CONTEXT); - - SetProcessDpiAwarenessContextFunc SetProcessDpiAwarenessContext = - (SetProcessDpiAwarenessContextFunc)(void*)GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetProcessDpiAwarenessContext"); - - if (SetProcessDpiAwarenessContext != nullptr) { - SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - } - } - if (ImHexApi::System::isDebugBuild()) { // If the application is running in debug mode, ImHex runs under the CONSOLE subsystem, // so we don't need to do anything besides enabling ANSI colors @@ -629,12 +601,9 @@ namespace hex { ImGui::GetIO().ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent(); - glfwSetFramebufferSizeCallback(m_window, [](GLFWwindow* window, int width, int height) { - auto *win = static_cast(glfwGetWindowUserPointer(window)); + hex::glfw::SetFramebufferSizeCallback(m_window, [](GLFWwindow* window, int, int) { + auto *win = static_cast(hex::glfw::GetWindowUserPointer(window)); win->m_unlockFrameRate = true; - - glViewport(0, 0, width, height); - ImHexApi::System::impl::setMainWindowSize(width, height); }); DwmEnableMMCSS(TRUE); @@ -649,7 +618,7 @@ namespace hex { } glfwSetWindowRefreshCallback(m_window, [](GLFWwindow *window) { - auto win = static_cast(glfwGetWindowUserPointer(window)); + auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); win->fullFrame(); DwmFlush(); diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index 5e27102a858d9..ef01b79f17251 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -15,6 +16,7 @@ #include #include +#include #include #include @@ -115,28 +117,13 @@ namespace hex { m_popupsToOpen.push_back(name); }); - EventDPIChanged::subscribe([this](float oldScaling, float newScaling) { - if (oldScaling == newScaling || oldScaling == 0 || newScaling == 0) - return; - - int width, height; - glfwGetWindowSize(m_window, &width, &height); - - width = float(width) * newScaling / oldScaling; - height = float(height) * newScaling / oldScaling; - - ImHexApi::System::impl::setMainWindowSize(width, height); - glfwSetWindowSize(m_window, width, height); - }); - - LayoutManager::registerLoadCallback([this](std::string_view line) { int width = 0, height = 0; sscanf(line.data(), "MainWindowSize=%d,%d", &width, &height); if (width > 0 && height > 0) { TaskManager::doLater([width, height, this]{ - glfwSetWindowSize(m_window, width, height); + hex::glfw::SetWindowSize(m_window, width, height); }); } }); @@ -218,8 +205,8 @@ namespace hex { { int x = 0, y = 0; int width = 0, height = 0; - glfwGetWindowPos(m_window, &x, &y); - glfwGetWindowSize(m_window, &width, &height); + hex::glfw::GetWindowPos(m_window, &x, &y); + hex::glfw::GetWindowSize(m_window, &width, &height); ImHexApi::System::impl::setMainWindowPosition(x, y); ImHexApi::System::impl::setMainWindowSize(width, height); @@ -265,10 +252,10 @@ namespace hex { static ImVec2 lastWindowSize = ImHexApi::System::getMainWindowSize(); if (ImHexApi::System::impl::isWindowResizable()) { - glfwSetWindowSizeLimits(m_window, 480_scaled, 360_scaled, GLFW_DONT_CARE, GLFW_DONT_CARE); + hex::glfw::SetWindowSizeLimits(m_window, 480, 360, GLFW_DONT_CARE, GLFW_DONT_CARE); lastWindowSize = ImHexApi::System::getMainWindowSize(); } else { - glfwSetWindowSizeLimits(m_window, lastWindowSize.x, lastWindowSize.y, lastWindowSize.x, lastWindowSize.y); + hex::glfw::SetWindowSizeLimits(m_window, lastWindowSize.x, lastWindowSize.y, lastWindowSize.x, lastWindowSize.y); } this->fullFrame(); @@ -779,11 +766,7 @@ namespace hex { // Create window m_windowTitle = "ImHex"; - m_window = glfwCreateWindow(1280_scaled, 720_scaled, m_windowTitle.c_str(), nullptr, nullptr); - - ImHexApi::System::impl::setMainWindowHandle(m_window); - - glfwSetWindowUserPointer(m_window, this); + m_window = hex::glfw::CreateWindow(1280, 720, m_windowTitle.c_str(), nullptr, nullptr); if (m_window == nullptr) { log::fatal("Failed to create window!"); @@ -793,6 +776,9 @@ namespace hex { // Force window to be fully opaque by default glfwSetWindowOpacity(m_window, 1.0F); + ImHexApi::System::impl::setMainWindowHandle(m_window); + hex::glfw::SetWindowUserPointer(m_window, this); + glfwMakeContextCurrent(m_window); // Disable VSync. Not like any graphics driver actually cares @@ -801,58 +787,66 @@ namespace hex { // Center window GLFWmonitor *monitor = glfwGetPrimaryMonitor(); if (monitor != nullptr) { - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - if (mode != nullptr) { + int monitorWidth, monitorHeight; + if (hex::glfw::GetMonitorSize(monitor, &monitorWidth, &monitorHeight)) { int monitorX, monitorY; - glfwGetMonitorPos(monitor, &monitorX, &monitorY); + hex::glfw::GetMonitorPos(monitor, &monitorX, &monitorY); int windowWidth, windowHeight; - glfwGetWindowSize(m_window, &windowWidth, &windowHeight); + hex::glfw::GetWindowSize(m_window, &windowWidth, &windowHeight); - glfwSetWindowPos(m_window, monitorX + (mode->width - windowWidth) / 2, monitorY + (mode->height - windowHeight) / 2); + hex::glfw::SetWindowPos(m_window, monitorX + (monitorWidth - windowWidth) / 2, monitorY + (monitorHeight - windowHeight) / 2); } } // Set up initial window position { int x = 0, y = 0; - glfwGetWindowPos(m_window, &x, &y); if (initialWindowProperties.has_value()) { x = initialWindowProperties->x; y = initialWindowProperties->y; + } else { + hex::glfw::GetWindowPos(m_window, &x, &y); } ImHexApi::System::impl::setMainWindowPosition(x, y); - glfwSetWindowPos(m_window, x, y); + hex::glfw::SetWindowPos(m_window, x, y); } // Set up initial window size { int width = 0, height = 0; - glfwGetWindowSize(m_window, &width, &height); if (initialWindowProperties.has_value()) { width = initialWindowProperties->width; height = initialWindowProperties->height; + } else { + hex::glfw::GetWindowSize(m_window, &width, &height); } ImHexApi::System::impl::setMainWindowSize(width, height); - glfwSetWindowSize(m_window, width, height); + hex::glfw::SetWindowSize(m_window, width, height); + } + + { + float contentScale; + glfwGetWindowContentScale(m_window, &contentScale, nullptr); + ImHexApi::System::impl::setContentScale(contentScale); } // Register window move callback - glfwSetWindowPosCallback(m_window, [](GLFWwindow *window, int x, int y) { + hex::glfw::SetWindowPosCallback(m_window, [](GLFWwindow *window, int x, int y) { ImHexApi::System::impl::setMainWindowPosition(x, y); - auto win = static_cast(glfwGetWindowUserPointer(window)); + auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); win->m_unlockFrameRate = true; win->fullFrame(); }); // Register window resize callback - glfwSetWindowSizeCallback(m_window, [](GLFWwindow *window, [[maybe_unused]] int width, [[maybe_unused]] int height) { - auto win = static_cast(glfwGetWindowUserPointer(window)); + hex::glfw::SetWindowSizeCallback(m_window, [](GLFWwindow *window, [[maybe_unused]] int width, [[maybe_unused]] int height) { + auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); win->m_unlockFrameRate = true; #if !defined(OS_WINDOWS) @@ -870,8 +864,8 @@ namespace hex { #endif }); - glfwSetCursorPosCallback(m_window, [](GLFWwindow *window, double, double) { - auto win = static_cast(glfwGetWindowUserPointer(window)); + hex::glfw::SetCursorPosCallback(m_window, [](GLFWwindow *window, double, double) { + auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); win->m_unlockFrameRate = true; }); @@ -909,7 +903,7 @@ namespace hex { key != GLFW_KEY_LEFT_SHIFT && key != GLFW_KEY_RIGHT_SHIFT && key != GLFW_KEY_LEFT_SUPER && key != GLFW_KEY_RIGHT_SUPER ) { - auto win = static_cast(glfwGetWindowUserPointer(window)); + auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); win->m_unlockFrameRate = true; if (!(mods & GLFW_MOD_NUM_LOCK)) { @@ -935,11 +929,20 @@ namespace hex { EventWindowClosing::post(window); }); - glfwSetWindowSizeLimits(m_window, 480_scaled, 360_scaled, GLFW_DONT_CARE, GLFW_DONT_CARE); + hex::glfw::SetWindowContentScaleCallback(m_window, [](GLFWwindow *, float newScale, float) { + auto oldScale = ImHexApi::System::getContentScale(); + if (newScale == 0 || oldScale == newScale) + return; + + ImHexApi::System::impl::setContentScale(newScale); + EventDPIChanged::post(newScale); + }); + + hex::glfw::SetWindowSizeLimits(m_window, 480, 360, GLFW_DONT_CARE, GLFW_DONT_CARE); } void Window::resize(i32 width, i32 height) { - glfwSetWindowSize(m_window, width, height); + hex::glfw::SetWindowSize(m_window, width, height); } void Window::initImGui() { @@ -966,7 +969,6 @@ namespace hex { io.ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_NavEnableKeyboard; io.ConfigWindowsMoveFromTitleBarOnly = true; - io.FontGlobalScale = 1.0F; if (glfwGetPrimaryMonitor() != nullptr) { if (ImHexApi::System::isMutliWindowModeEnabled()) @@ -985,10 +987,6 @@ namespace hex { } io.UserData = &m_imguiCustomData; - - auto scale = ImHexApi::System::getGlobalScale(); - style.ScaleAllSizes(scale); - io.DisplayFramebufferScale = ImVec2(scale, scale); io.Fonts->SetTexID(fonts->TexID); style.WindowMenuButtonPosition = ImGuiDir_None; @@ -1041,10 +1039,11 @@ namespace hex { plugin.setImGuiContext(ImGui::GetCurrentContext()); RequestInitThemeHandlers::post(); + EventDPIChanged::post(ImHexApi::System::getContentScale()); } void Window::exitGLFW() { - glfwDestroyWindow(m_window); + hex::glfw::DestroyWindow(m_window); glfwTerminate(); m_window = nullptr; diff --git a/plugins/builtin/romfs/lang/de_DE.json b/plugins/builtin/romfs/lang/de_DE.json index 9308dd843c4e4..7babcdbdb4040 100644 --- a/plugins/builtin/romfs/lang/de_DE.json +++ b/plugins/builtin/romfs/lang/de_DE.json @@ -492,7 +492,6 @@ "hex.builtin.setting.interface.multi_windows": "Multi-Window-Unterstützung aktivieren", "hex.builtin.setting.interface.pattern_data_row_bg": "Aktiviere farbige Patternhintergründe", "hex.builtin.setting.interface.restore_window_pos": "Fensterposition und Grösse wiederherstellen", - "hex.builtin.setting.interface.scaling.native": "Nativ", "hex.builtin.setting.interface.scaling_factor": "Skalierung", "hex.builtin.setting.interface.show_header_command_palette": "Befehlspalette in Titelbar anzeigen", "hex.builtin.setting.interface.style": "Stil", diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index fbffa3bba9f32..95eb6bf97602b 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -501,7 +501,6 @@ "hex.builtin.setting.interface.language": "Language", "hex.builtin.setting.interface.multi_windows": "Enable Multi Window support", "hex.builtin.setting.interface.scaling_factor": "Scaling", - "hex.builtin.setting.interface.scaling.native": "Native", "hex.builtin.setting.interface.scaling.fractional_warning": "The default font does not support fractional scaling. For better results, select a custom font in the 'Font' tab.", "hex.builtin.setting.interface.show_header_command_palette": "Show Command Palette in Window Header", "hex.builtin.setting.interface.style": "Styling", diff --git a/plugins/builtin/romfs/lang/es_ES.json b/plugins/builtin/romfs/lang/es_ES.json index 09dee4ea72264..e4fe6238613de 100644 --- a/plugins/builtin/romfs/lang/es_ES.json +++ b/plugins/builtin/romfs/lang/es_ES.json @@ -489,7 +489,6 @@ "hex.builtin.setting.interface.multi_windows": "Activar soporte de ventanas múltiples", "hex.builtin.setting.interface.pattern_data_row_bg": "", "hex.builtin.setting.interface.restore_window_pos": "", - "hex.builtin.setting.interface.scaling.native": "Nativo", "hex.builtin.setting.interface.scaling_factor": "Escalado", "hex.builtin.setting.interface.style": "", "hex.builtin.setting.interface.wiki_explain_language": "Idioma de Wikipedia", diff --git a/plugins/builtin/romfs/lang/hu_HU.json b/plugins/builtin/romfs/lang/hu_HU.json index 644552553664d..d3ca464703dce 100644 --- a/plugins/builtin/romfs/lang/hu_HU.json +++ b/plugins/builtin/romfs/lang/hu_HU.json @@ -485,7 +485,6 @@ "hex.builtin.setting.interface.language": "Nyelv", "hex.builtin.setting.interface.multi_windows": "Több ablakos mód engedélyezése", "hex.builtin.setting.interface.scaling_factor": "Méretezés", - "hex.builtin.setting.interface.scaling.native": "Natív", "hex.builtin.setting.interface.show_header_command_palette": "Parancspaletta mutatása az ablak fejlécben", "hex.builtin.setting.interface.style": "Stílusok", "hex.builtin.setting.interface.window": "Ablak", diff --git a/plugins/builtin/romfs/lang/it_IT.json b/plugins/builtin/romfs/lang/it_IT.json index 55777ebe2d30c..4710e88b4c1cb 100644 --- a/plugins/builtin/romfs/lang/it_IT.json +++ b/plugins/builtin/romfs/lang/it_IT.json @@ -489,7 +489,6 @@ "hex.builtin.setting.interface.multi_windows": "", "hex.builtin.setting.interface.pattern_data_row_bg": "", "hex.builtin.setting.interface.restore_window_pos": "", - "hex.builtin.setting.interface.scaling.native": "Nativo", "hex.builtin.setting.interface.scaling_factor": "Scale", "hex.builtin.setting.interface.style": "", "hex.builtin.setting.interface.wiki_explain_language": "", diff --git a/plugins/builtin/romfs/lang/ja_JP.json b/plugins/builtin/romfs/lang/ja_JP.json index 92c6722ddd4b7..71c08b9d9d677 100644 --- a/plugins/builtin/romfs/lang/ja_JP.json +++ b/plugins/builtin/romfs/lang/ja_JP.json @@ -489,7 +489,6 @@ "hex.builtin.setting.interface.multi_windows": "", "hex.builtin.setting.interface.pattern_data_row_bg": "", "hex.builtin.setting.interface.restore_window_pos": "", - "hex.builtin.setting.interface.scaling.native": "ネイティブ", "hex.builtin.setting.interface.scaling_factor": "スケーリング", "hex.builtin.setting.interface.style": "", "hex.builtin.setting.interface.wiki_explain_language": "", diff --git a/plugins/builtin/romfs/lang/ko_KR.json b/plugins/builtin/romfs/lang/ko_KR.json index c201ab4a52884..8de596e545c49 100644 --- a/plugins/builtin/romfs/lang/ko_KR.json +++ b/plugins/builtin/romfs/lang/ko_KR.json @@ -489,7 +489,6 @@ "hex.builtin.setting.interface.multi_windows": "다중 창 지원 사용", "hex.builtin.setting.interface.pattern_data_row_bg": "색상 패턴 배경 사용", "hex.builtin.setting.interface.restore_window_pos": "창 위치 복원", - "hex.builtin.setting.interface.scaling.native": "기본", "hex.builtin.setting.interface.scaling_factor": "배율", "hex.builtin.setting.interface.style": "스타일", "hex.builtin.setting.interface.wiki_explain_language": "위키백과 언어", diff --git a/plugins/builtin/romfs/lang/pt_BR.json b/plugins/builtin/romfs/lang/pt_BR.json index ce60ad38f3c11..edbe96baada4f 100644 --- a/plugins/builtin/romfs/lang/pt_BR.json +++ b/plugins/builtin/romfs/lang/pt_BR.json @@ -489,7 +489,6 @@ "hex.builtin.setting.interface.multi_windows": "", "hex.builtin.setting.interface.pattern_data_row_bg": "", "hex.builtin.setting.interface.restore_window_pos": "", - "hex.builtin.setting.interface.scaling.native": "Nativo", "hex.builtin.setting.interface.scaling_factor": "Scaling", "hex.builtin.setting.interface.style": "", "hex.builtin.setting.interface.wiki_explain_language": "Idioma do Wikipedia", diff --git a/plugins/builtin/romfs/lang/zh_CN.json b/plugins/builtin/romfs/lang/zh_CN.json index af847367f038d..7c47ffca3cdeb 100644 --- a/plugins/builtin/romfs/lang/zh_CN.json +++ b/plugins/builtin/romfs/lang/zh_CN.json @@ -530,7 +530,6 @@ "hex.builtin.setting.interface.native_window_decorations": "使用操作系统窗口装饰", "hex.builtin.setting.interface.pattern_data_row_bg": "启用彩色图案背景", "hex.builtin.setting.interface.restore_window_pos": "恢复窗口位置", - "hex.builtin.setting.interface.scaling.native": "本地默认", "hex.builtin.setting.interface.scaling_factor": "缩放", "hex.builtin.setting.interface.show_header_command_palette": "在窗口标题中显示命令面板", "hex.builtin.setting.interface.style": "风格", diff --git a/plugins/builtin/romfs/lang/zh_TW.json b/plugins/builtin/romfs/lang/zh_TW.json index e80271fa4bc58..878f48018a709 100644 --- a/plugins/builtin/romfs/lang/zh_TW.json +++ b/plugins/builtin/romfs/lang/zh_TW.json @@ -489,7 +489,6 @@ "hex.builtin.setting.interface.multi_windows": "啟用多視窗支援", "hex.builtin.setting.interface.pattern_data_row_bg": "", "hex.builtin.setting.interface.restore_window_pos": "Restore window position", - "hex.builtin.setting.interface.scaling.native": "原生", "hex.builtin.setting.interface.scaling_factor": "縮放", "hex.builtin.setting.interface.style": "", "hex.builtin.setting.interface.wiki_explain_language": "維基百科語言", diff --git a/plugins/builtin/source/content/events.cpp b/plugins/builtin/source/content/events.cpp index 05e942b7eb340..1652b734cf2d3 100644 --- a/plugins/builtin/source/content/events.cpp +++ b/plugins/builtin/source/content/events.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -240,8 +241,8 @@ namespace hex::plugin::builtin { { int x = 0, y = 0, width = 0, height = 0, maximized = 0; - glfwGetWindowPos(window, &x, &y); - glfwGetWindowSize(window, &width, &height); + hex::glfw::GetWindowPos(window, &x, &y); + hex::glfw::GetWindowSize(window, &width, &height); maximized = glfwGetWindowAttrib(window, GLFW_MAXIMIZED); ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.window.x", x); diff --git a/plugins/builtin/source/content/init_tasks.cpp b/plugins/builtin/source/content/init_tasks.cpp index 55524504ae64e..0e03c5b8262d9 100644 --- a/plugins/builtin/source/content/init_tasks.cpp +++ b/plugins/builtin/source/content/init_tasks.cpp @@ -99,20 +99,21 @@ namespace hex::plugin::builtin { } bool configureUIScale() { - EventDPIChanged::subscribe([](float, float newScaling) { - int interfaceScaleSetting = int(ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling_factor", 0.0F) * 10.0F); - - float interfaceScaling; - if (interfaceScaleSetting == 0) - interfaceScaling = newScaling; - else - interfaceScaling = interfaceScaleSetting / 10.0F; - - ImHexApi::System::impl::setGlobalScale(interfaceScaling); - }); - - const auto nativeScale = ImHexApi::System::getNativeScale(); - EventDPIChanged::post(nativeScale, nativeScale); + int interfaceScaleSetting = int(ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling_factor", 1.0F) * 10.0F); + + float interfaceScaling; + // 0 used to mean 'native' but with DPI awareness in GLFW this just + // means no UI scaling + if (interfaceScaleSetting == 0) { + interfaceScaling = 1.0F; + ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling_factor", 1.0F); + } else + interfaceScaling = interfaceScaleSetting / 10.0F; + + // An `EventScaleChanged` event should be posted when the main + // window loads to communicate the content scale, so there should be + // no need to send one here too + ImHexApi::System::impl::setUserScale(interfaceScaling); return true; } diff --git a/plugins/builtin/source/content/main_menu_items.cpp b/plugins/builtin/source/content/main_menu_items.cpp index d917e31e1280b..caaf4050d7476 100644 --- a/plugins/builtin/source/content/main_menu_items.cpp +++ b/plugins/builtin/source/content/main_menu_items.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -536,11 +537,14 @@ namespace hex::plugin::builtin { size = ImHexApi::System::getMainWindowSize(); const auto monitor = glfwGetPrimaryMonitor(); - const auto videoMode = glfwGetVideoMode(monitor); - - glfwSetWindowMonitor(window, monitor, 0, 0, videoMode->width, videoMode->height, videoMode->refreshRate); + auto videoMode = glfwGetVideoMode(monitor); + if (videoMode) { + int monitorWidth, monitorHeight; + hex::glfw::GetMonitorSize(monitor, &monitorWidth, &monitorHeight); + hex::glfw::SetWindowMonitor(window, monitor, 0, 0, monitorWidth, monitorHeight, videoMode->refreshRate); + } } else { - glfwSetWindowMonitor(window, nullptr, position.x, position.y, size.x, size.y, 0); + hex::glfw::SetWindowMonitor(window, nullptr, position.x, position.y, size.x, size.y, 0); } }, []{ return true; }, []{ return glfwGetWindowMonitor(ImHexApi::System::getMainWindowHandle()) != nullptr; }); diff --git a/plugins/builtin/source/content/out_of_box_experience.cpp b/plugins/builtin/source/content/out_of_box_experience.cpp index f57e43ba2e5e9..b7a34d37ea938 100644 --- a/plugins/builtin/source/content/out_of_box_experience.cpp +++ b/plugins/builtin/source/content/out_of_box_experience.cpp @@ -100,7 +100,7 @@ namespace hex::plugin::builtin { ImGui::EndChild(); if (!s_screenshots.empty()) { - const auto imageSize = s_screenshots.front().second.getSize() * ImHexApi::System::getGlobalScale(); + const auto imageSize = scaled(s_screenshots.front().second.getSize()); const auto padding = ImGui::GetStyle().CellPadding.x; const auto stride = imageSize.x + padding * 2; static bool imageHovered = false; @@ -221,7 +221,7 @@ namespace hex::plugin::builtin { currLanguage = languages.begin(); // Draw globe image - const auto imageSize = s_compassTexture.getSize() / (1.5F * (1.0F / ImHexApi::System::getGlobalScale())); + const auto imageSize = scaled(s_compassTexture.getSize() / 1.5F); ImGui::SetCursorPos((ImGui::GetWindowSize() / 2 - imageSize / 2) - ImVec2(0, 50_scaled)); ImGui::Image(s_globeTexture, imageSize); @@ -370,7 +370,7 @@ namespace hex::plugin::builtin { ImGui::NewLine(); // Draw compass image - const auto imageSize = s_compassTexture.getSize() / (1.5F * (1.0F / ImHexApi::System::getGlobalScale())); + const auto imageSize = scaled(s_compassTexture.getSize() / 1.5F); ImGui::SetCursorPos((ImGui::GetWindowSize() / 2 - imageSize / 2) - ImVec2(0, 50_scaled)); ImGui::Image(s_compassTexture, imageSize); diff --git a/plugins/builtin/source/content/settings_entries.cpp b/plugins/builtin/source/content/settings_entries.cpp index 47500811a8bba..0064fd7dba037 100644 --- a/plugins/builtin/source/content/settings_entries.cpp +++ b/plugins/builtin/source/content/settings_entries.cpp @@ -172,19 +172,12 @@ namespace hex::plugin::builtin { class ScalingWidget : public ContentRegistry::Settings::Widgets::Widget { public: bool draw(const std::string &name) override { - auto format = [this] -> std::string { - if (m_value == 0) - return "hex.builtin.setting.interface.scaling.native"_lang + hex::format(" (x{:.1f})", ImHexApi::System::getNativeScale()); - else - return "x%.1f"; - }(); - - bool changed = ImGui::SliderFloat(name.data(), &m_value, 0, 4, format.c_str()); + bool changed = ImGui::SliderFloat(name.data(), &m_value, 0.1, 4, "x%.1f"); - if (m_value < 0) - m_value = 0; - else if (m_value > 10) - m_value = 10; + if (m_value < 0.1) + m_value = 0.1; + else if (m_value > 4) + m_value = 4; if (s_showScalingWarning && (u32(m_value * 10) % 10) != 0) { ImGui::SameLine(); diff --git a/plugins/builtin/source/content/welcome_screen.cpp b/plugins/builtin/source/content/welcome_screen.cpp index 3a2ff1f1e82d2..320c13d156d29 100644 --- a/plugins/builtin/source/content/welcome_screen.cpp +++ b/plugins/builtin/source/content/welcome_screen.cpp @@ -557,7 +557,7 @@ namespace hex::plugin::builtin { for (const auto &path : paths::Config.read()) { if (auto crashFilePath = std::fs::path(path) / CrashFileName; wolv::io::fs::exists(crashFilePath)) { hasCrashed = true; - + log::info("Found crash.json file at {}", wolv::util::toUTF8String(crashFilePath)); wolv::io::File crashFile(crashFilePath, wolv::io::File::Mode::Read); nlohmann::json crashFileData; diff --git a/plugins/fonts/source/font_loader.cpp b/plugins/fonts/source/font_loader.cpp index 2ac7efb5e0bb7..7fc1ad947d3c4 100644 --- a/plugins/fonts/source/font_loader.cpp +++ b/plugins/fonts/source/font_loader.cpp @@ -49,23 +49,33 @@ namespace hex::fonts { enableUnicodeCharacters(false); // Set the default configuration for the font atlas - m_config.OversampleH = m_config.OversampleV = 1; - m_config.PixelSnapH = true; m_config.MergeMode = false; - // Make sure the font atlas doesn't get too large, otherwise weaker GPUs might reject it - m_fontAtlas->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight; - m_fontAtlas->TexDesiredWidth = 4096; + EventDPIChanged::subscribe(this, [this](float) { + if (scaleAndBuild()) { + ImGui_ImplOpenGL3_DestroyFontsTexture(); + ImGui_ImplOpenGL3_CreateFontsTexture(); + ImHexApi::Fonts::impl::setFontAtlas(getAtlas()); + } + }); } ~FontAtlas() { IM_DELETE(m_fontAtlas); + EventDPIChanged::unsubscribe(this); + } + + bool scaleAndBuild() { + updateFontScaling(ImHexApi::System::getUserScale(), ImHexApi::System::getContentScale()); + return build(); } Font addDefaultFont() { ImFontConfig config = m_config; config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; - config.SizePixels = std::floor(ImHexApi::System::getGlobalScale()) * 13.0F; + config.SizePixels = ImHexApi::Fonts::DefaultFontSize; + config.OversampleH = m_config.OversampleV = 1; + config.PixelSnapH = true; auto font = m_fontAtlas->AddFontDefault(&config); m_fontSizes.emplace_back(false, config.SizePixels); @@ -159,24 +169,21 @@ namespace hex::fonts { } [[nodiscard]] ImFontAtlas* getAtlas() { - auto result = m_fontAtlas; - - return result; + return m_fontAtlas; } - float calculateFontDescend(const ImHexApi::Fonts::Font &font, float fontSize) const { + float calculateFontDescent(const ImHexApi::Fonts::Font &font, float fontSize) const { auto atlas = std::make_unique(); auto cfg = m_config; // Calculate the expected font size - auto size = fontSize; if (font.defaultSize.has_value()) - size = font.defaultSize.value() * std::max(1.0F, std::floor(ImHexApi::Fonts::getFontSize() / ImHexApi::Fonts::DefaultFontSize)); + fontSize = font.defaultSize.value() * std::max(1.0F, std::floor(ImHexApi::Fonts::getFontSize() / ImHexApi::Fonts::DefaultFontSize)); else - size = std::max(1.0F, std::floor(size / ImHexApi::Fonts::DefaultFontSize)) * ImHexApi::Fonts::DefaultFontSize; + fontSize = std::max(1.0F, std::floor(fontSize / ImHexApi::Fonts::DefaultFontSize)) * ImHexApi::Fonts::DefaultFontSize; cfg.MergeMode = false; - cfg.SizePixels = size; + cfg.SizePixels = fontSize; cfg.FontDataOwnedByAtlas = false; // Construct a range that only contains the first glyph of the font @@ -203,16 +210,18 @@ namespace hex::fonts { m_config.MergeMode = false; } - void updateFontScaling(float newScaling) { + void updateFontScaling(float scale, float density) { for (int i = 0; i < m_fontAtlas->ConfigData.size(); i += 1) { const auto &[scalable, fontSize] = m_fontSizes[i]; auto &configData = m_fontAtlas->ConfigData[i]; if (!scalable) { - configData.SizePixels = fontSize * std::floor(newScaling); + configData.SizePixels = fontSize * std::max(1.0F, std::floor(scale)); } else { - configData.SizePixels = fontSize * newScaling; + configData.SizePixels = fontSize * scale; } + + configData.RasterizerDensity = density; } } @@ -254,25 +263,16 @@ namespace hex::fonts { } float getFontSize() { - float fontSize = ImHexApi::Fonts::DefaultFontSize; - - if (auto scaling = ImHexApi::System::getGlobalScale(); u32(scaling) * 10 == u32(scaling * 10)) - fontSize *= scaling; - else - fontSize *= scaling * 0.75F; + float fontSize = 0.0F; + if (!ImHexApi::Fonts::getCustomFontPath().empty()) { + fontSize = float(ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_size", ImHexApi::Fonts::DefaultFontSize)); + } - // Fall back to the default font if the global scale is 0 if (fontSize == 0.0F) fontSize = ImHexApi::Fonts::DefaultFontSize; - // If a custom font is used, adjust the font size - if (!ImHexApi::Fonts::getCustomFontPath().empty()) { - fontSize = float(ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_size", 13)) * ImHexApi::System::getGlobalScale(); - } - return fontSize; } - } bool buildFontAtlasImpl(bool loadUnicodeCharacters) { @@ -282,11 +282,9 @@ namespace hex::fonts { // Check if Unicode support is enabled in the settings and that the user doesn't use the No GPU version on Windows // The Mesa3D software renderer on Windows identifies itself as "VMware, Inc." bool shouldLoadUnicode = - ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.load_all_unicode_chars", false) && - ImHexApi::System::getGPUVendor() != "VMware, Inc."; - - if (!loadUnicodeCharacters) - shouldLoadUnicode = false; + loadUnicodeCharacters && + ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.load_all_unicode_chars", false) && + ImHexApi::System::getGPUVendor() != "VMware, Inc."; fontAtlas.enableUnicodeCharacters(shouldLoadUnicode); @@ -298,10 +296,9 @@ namespace hex::fonts { ImHexApi::Fonts::impl::setCustomFontPath(findCustomFontPath()); } - ImHexApi::Fonts::impl::setFontSize(getFontSize()); - - const auto fontSize = ImHexApi::Fonts::getFontSize(); + const auto fontSize = getFontSize(); + ImHexApi::Fonts::impl::setFontSize(fontSize); const auto &customFontPath = ImHexApi::Fonts::getCustomFontPath(); // Try to load the custom font if one was set @@ -348,25 +345,15 @@ namespace hex::fonts { glyphRanges.push_back(glyphRange); // Calculate the glyph offset for the font - ImVec2 offset = { font.offset.x, font.offset.y - (defaultFont->getDescent() - fontAtlas.calculateFontDescend(font, fontSize)) }; + ImVec2 offset = { font.offset.x, font.offset.y - (defaultFont->getDescent() - fontAtlas.calculateFontDescent(font, fontSize)) }; // Load the font fontAtlas.addFontFromMemory(font.fontData, font.defaultSize.value_or(fontSize), !font.defaultSize.has_value(), offset, glyphRanges.back()); } } - EventDPIChanged::subscribe([](float, float newScaling) { - fontAtlas.updateFontScaling(newScaling); - - if (fontAtlas.build()) { - ImGui_ImplOpenGL3_DestroyFontsTexture(); - ImGui_ImplOpenGL3_CreateFontsTexture(); - ImHexApi::Fonts::impl::setFontAtlas(fontAtlas.getAtlas()); - } - }); - // Build the font atlas - const bool result = fontAtlas.build(); + const bool result = fontAtlas.scaleAndBuild(); if (result) { // Set the font atlas if the build was successful ImHexApi::Fonts::impl::setFontAtlas(fontAtlas.getAtlas()); @@ -380,7 +367,7 @@ namespace hex::fonts { ContentRegistry::Settings::write("hex.builtin.setting.font", "hex.builtin.setting.font.load_all_unicode_chars", false); ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling_factor", 1.0F); - ImHexApi::System::impl::setGlobalScale(1.0F); + ImHexApi::System::impl::setUserScale(1.0F); return false; } else { From 4493ffff41b1527b9529ac61fa59acb16f25c778 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Mon, 15 Jul 2024 00:49:47 -0500 Subject: [PATCH 3/7] feat: Render SVGs at native resolution --- .../include/hex/ui/imgui_imhex_extensions.h | 9 +++-- .../source/ui/imgui_imhex_extensions.cpp | 39 +++++++++++++++++-- .../source/content/out_of_box_experience.cpp | 3 +- .../builtin/source/content/welcome_screen.cpp | 4 +- 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h index 304b45c21b823..42f0fa94075e5 100644 --- a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h +++ b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h @@ -86,9 +86,9 @@ namespace ImGuiExt { static Texture fromGLTexture(unsigned int texture, int width, int height); static Texture fromBitmap(const ImU8 *buffer, int size, int width, int height, Filter filter = Filter::Nearest); static Texture fromBitmap(std::span buffer, int width, int height, Filter filter = Filter::Nearest); - static Texture fromSVG(const char *path, int width = 0, int height = 0, Filter filter = Filter::Nearest); - static Texture fromSVG(const std::fs::path &path, int width = 0, int height = 0, Filter filter = Filter::Nearest); - static Texture fromSVG(std::span buffer, int width = 0, int height = 0, Filter filter = Filter::Nearest); + static Texture fromSVG(const char *path, int width = 0, int height = 0, float scale = 1.0f, Filter filter = Filter::Nearest); + static Texture fromSVG(const std::fs::path &path, int width = 0, int height = 0, float scale = 1.0f, Filter filter = Filter::Nearest); + static Texture fromSVG(std::span buffer, int width = 0, int height = 0, float scale = 1.0f, Filter filter = Filter::Nearest); ~Texture(); @@ -109,7 +109,7 @@ namespace ImGuiExt { } [[nodiscard]] auto getSize() const noexcept { - return ImVec2(m_width, m_height); + return ImVec2(float(m_width) / m_scale, float(m_height) / m_scale); } [[nodiscard]] constexpr auto getAspectRatio() const noexcept { @@ -121,6 +121,7 @@ namespace ImGuiExt { private: ImTextureID m_textureId = nullptr; int m_width = 0, m_height = 0; + float m_scale = 1.0f; }; float GetTextWrapPos(); diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp index 58c1f5a9770db..2a6fafae88fdb 100644 --- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp +++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp @@ -33,6 +33,23 @@ namespace ImGuiExt { namespace { + void adjustSVGScale(const lunasvg::Document *document, int &width, int &height, int scale) { + if (document->width() == 0 || document->height() == 0) + return; + + if (width == 0 && height == 0) { + width = document->width(); + height = document->height(); + } else if (width != 0 && height == 0) { + height = std::ceil(double(width) * document->height() / document->width()); + } else if (height != 0 && width == 0) { + width = std::ceil(double(height) * document->width() / document->height()); + } + + width *= scale; + height *= scale; + } + bool isOpenGLExtensionSupported(const char *name) { static std::set extensions; @@ -223,8 +240,13 @@ namespace ImGuiExt { return result; } - Texture Texture::fromSVG(const char *path, int width, int height, Filter filter) { + Texture Texture::fromSVG(const char *path, int width, int height, float scale, Filter filter) { auto document = lunasvg::Document::loadFromFile(path); + if (!document) + return {}; + + adjustSVGScale(document.get(), width, height, scale); + auto bitmap = document->renderToBitmap(width, height); auto texture = createMultisampleTextureFromRGBA8Array(bitmap.data(), bitmap.width(), bitmap.height(), filter); @@ -232,17 +254,23 @@ namespace ImGuiExt { Texture result; result.m_width = bitmap.width(); result.m_height = bitmap.height(); + result.m_scale = scale; result.m_textureId = reinterpret_cast(static_cast(texture)); return result; } - Texture Texture::fromSVG(const std::fs::path &path, int width, int height, Filter filter) { - return Texture::fromSVG(wolv::util::toUTF8String(path).c_str(), width, height, filter); + Texture Texture::fromSVG(const std::fs::path &path, int width, int height, float scale, Filter filter) { + return Texture::fromSVG(wolv::util::toUTF8String(path).c_str(), width, height, scale, filter); } - Texture Texture::fromSVG(std::span buffer, int width, int height, Filter filter) { + Texture Texture::fromSVG(std::span buffer, int width, int height, float scale, Filter filter) { auto document = lunasvg::Document::loadFromData(reinterpret_cast(buffer.data()), buffer.size()); + if (!document) + return {}; + + adjustSVGScale(document.get(), width, height, scale); + auto bitmap = document->renderToBitmap(width, height); bitmap.convertToRGBA(); @@ -251,6 +279,7 @@ namespace ImGuiExt { Texture result; result.m_width = bitmap.width(); result.m_height = bitmap.height(); + result.m_scale = scale; result.m_textureId = reinterpret_cast(static_cast(texture)); return result; @@ -263,6 +292,7 @@ namespace ImGuiExt { m_textureId = other.m_textureId; m_width = other.m_width; m_height = other.m_height; + m_scale = other.m_scale; other.m_textureId = nullptr; } @@ -274,6 +304,7 @@ namespace ImGuiExt { m_textureId = other.m_textureId; m_width = other.m_width; m_height = other.m_height; + m_scale = other.m_scale; other.m_textureId = nullptr; diff --git a/plugins/builtin/source/content/out_of_box_experience.cpp b/plugins/builtin/source/content/out_of_box_experience.cpp index b7a34d37ea938..d63c2dcdae3d1 100644 --- a/plugins/builtin/source/content/out_of_box_experience.cpp +++ b/plugins/builtin/source/content/out_of_box_experience.cpp @@ -442,7 +442,8 @@ namespace hex::plugin::builtin { ImHexApi::System::setWindowResizable(false); const auto imageTheme = ThemeManager::getImageTheme(); - s_imhexBanner = ImGuiExt::Texture::fromSVG(romfs::get(hex::format("assets/{}/banner.svg", imageTheme)).span()); + const auto scale = ImHexApi::System::getContentScale(); + s_imhexBanner = ImGuiExt::Texture::fromSVG(romfs::get(hex::format("assets/{}/banner.svg", imageTheme)).span(), 0, 0, scale); s_compassTexture = ImGuiExt::Texture::fromImage(romfs::get("assets/common/compass.png").span()); s_globeTexture = ImGuiExt::Texture::fromImage(romfs::get("assets/common/globe.png").span()); s_screenshotDescriptions = nlohmann::json::parse(romfs::get("assets/screenshot_descriptions.json").string()); diff --git a/plugins/builtin/source/content/welcome_screen.cpp b/plugins/builtin/source/content/welcome_screen.cpp index 320c13d156d29..48e01986c8772 100644 --- a/plugins/builtin/source/content/welcome_screen.cpp +++ b/plugins/builtin/source/content/welcome_screen.cpp @@ -524,7 +524,9 @@ namespace hex::plugin::builtin { }; auto changeTextureSvg = [&](const std::string &path, float width) { - return ImGuiExt::Texture::fromSVG(romfs::get(path).span(), width, 0, ImGuiExt::Texture::Filter::Linear); + // UI scaling is already baked into the width + const auto scale = ImHexApi::System::getContentScale(); + return ImGuiExt::Texture::fromSVG(romfs::get(path).span(), width, 0, scale, ImGuiExt::Texture::Filter::Linear); }; ThemeManager::changeTheme(theme); From a83e2a50f72ed7776157994f6b7c76235a877e73 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Mon, 15 Jul 2024 01:15:17 -0500 Subject: [PATCH 4/7] feat: Improve appearance of OOB banner Rendering at a non-native resolution and then scaling the bitmap looks bad. The scaling seems to try to just get it to end up the same size as the welcome screen, so just use the same size for both. --- plugins/builtin/source/content/out_of_box_experience.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/builtin/source/content/out_of_box_experience.cpp b/plugins/builtin/source/content/out_of_box_experience.cpp index d63c2dcdae3d1..102f0a020aba6 100644 --- a/plugins/builtin/source/content/out_of_box_experience.cpp +++ b/plugins/builtin/source/content/out_of_box_experience.cpp @@ -76,7 +76,7 @@ namespace hex::plugin::builtin { // Draw banner ImGui::SetCursorPos(scaled({ 25 * bannerSlideIn, 25 })); - const auto bannerSize = s_imhexBanner.getSize() / (3.0F * (1.0F / ImHexApi::System::getGlobalScale())); + const auto bannerSize = s_imhexBanner.getSize(); ImGui::Image( s_imhexBanner, bannerSize, @@ -443,7 +443,7 @@ namespace hex::plugin::builtin { const auto imageTheme = ThemeManager::getImageTheme(); const auto scale = ImHexApi::System::getContentScale(); - s_imhexBanner = ImGuiExt::Texture::fromSVG(romfs::get(hex::format("assets/{}/banner.svg", imageTheme)).span(), 0, 0, scale); + s_imhexBanner = ImGuiExt::Texture::fromSVG(romfs::get(hex::format("assets/{}/banner.svg", imageTheme)).span(), 300_scaled, 0, scale); s_compassTexture = ImGuiExt::Texture::fromImage(romfs::get("assets/common/compass.png").span()); s_globeTexture = ImGuiExt::Texture::fromImage(romfs::get("assets/common/globe.png").span()); s_screenshotDescriptions = nlohmann::json::parse(romfs::get("assets/screenshot_descriptions.json").string()); From 3cc56fe27dde9ec9926aa61267c4769e3761d2b0 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Wed, 17 Jul 2024 20:57:01 +0200 Subject: [PATCH 5/7] impr: Make DPI changes not cause window flickering --- main/gui/source/window/window.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index ef01b79f17251..5ac9a721a35e0 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -849,10 +849,8 @@ namespace hex { auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); win->m_unlockFrameRate = true; - #if !defined(OS_WINDOWS) - if (!glfwGetWindowAttrib(window, GLFW_ICONIFIED)) - ImHexApi::System::impl::setMainWindowSize(width, height); - #endif + if (!glfwGetWindowAttrib(window, GLFW_ICONIFIED)) + ImHexApi::System::impl::setMainWindowSize(width, height); #if defined(OS_MACOS) // Stop widgets registering hover effects while the window is being resized @@ -929,13 +927,16 @@ namespace hex { EventWindowClosing::post(window); }); - hex::glfw::SetWindowContentScaleCallback(m_window, [](GLFWwindow *, float newScale, float) { + hex::glfw::SetWindowContentScaleCallback(m_window, [](GLFWwindow *window, float newScale, float) { auto oldScale = ImHexApi::System::getContentScale(); if (newScale == 0 || oldScale == newScale) return; ImHexApi::System::impl::setContentScale(newScale); EventDPIChanged::post(newScale); + + auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); + win->fullFrame(); }); hex::glfw::SetWindowSizeLimits(m_window, 480, 360, GLFW_DONT_CARE, GLFW_DONT_CARE); From a02dcaff6095bd745c983ecad164cd7e6bbd12eb Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Wed, 17 Jul 2024 22:40:50 -0500 Subject: [PATCH 6/7] impr: Balance font rendering quality with atlas memory usage --- plugins/fonts/source/font_loader.cpp | 43 +++++++++++++++------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/plugins/fonts/source/font_loader.cpp b/plugins/fonts/source/font_loader.cpp index 7fc1ad947d3c4..a06c2e5a9ead5 100644 --- a/plugins/fonts/source/font_loader.cpp +++ b/plugins/fonts/source/font_loader.cpp @@ -48,7 +48,7 @@ namespace hex::fonts { FontAtlas() : m_fontAtlas(IM_NEW(ImFontAtlas)) { enableUnicodeCharacters(false); - // Set the default configuration for the font atlas + m_config.SizePixels = ImHexApi::Fonts::DefaultFontSize; m_config.MergeMode = false; EventDPIChanged::subscribe(this, [this](float) { @@ -71,17 +71,10 @@ namespace hex::fonts { } Font addDefaultFont() { - ImFontConfig config = m_config; - config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; - config.SizePixels = ImHexApi::Fonts::DefaultFontSize; - config.OversampleH = m_config.OversampleV = 1; - config.PixelSnapH = true; - - auto font = m_fontAtlas->AddFontDefault(&config); - m_fontSizes.emplace_back(false, config.SizePixels); - + setAntiAliasing(false); + auto font = m_fontAtlas->AddFontDefault(&m_config); + m_fontSizes.emplace_back(false, m_config.SizePixels); m_config.MergeMode = true; - return Font(font); } @@ -127,10 +120,13 @@ namespace hex::fonts { } void setAntiAliasing(bool enabled) { - if (enabled) + if (enabled) { + m_config.PixelSnapH = false; m_config.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; - else + } else { + m_config.PixelSnapH = true; m_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; + } } void enableUnicodeCharacters(bool enabled) { @@ -165,6 +161,9 @@ namespace hex::fonts { } bool build() const { + // FIXME: Large font size + Unicode enabled + fonts with lots of + // glyphs = unrecoverable `pack_rect.was_packed` assertion + // failure in `ImFontAtlasBuildWithFreeTypeEx` return m_fontAtlas->Build(); } @@ -211,16 +210,20 @@ namespace hex::fonts { } void updateFontScaling(float scale, float density) { - for (int i = 0; i < m_fontAtlas->ConfigData.size(); i += 1) { + // Using only `RasterizerDensity` in combination with + // `ImGuiIO::FontGlobalScale` does not work; when the combined + // density and scale go above a certain amount, something + // silently breaks and the window is rendered all black except + // for rasterised images which are drawn on top of the black + for (auto i = 0; i < m_fontAtlas->ConfigData.size(); ++i) { const auto &[scalable, fontSize] = m_fontSizes[i]; auto &configData = m_fontAtlas->ConfigData[i]; - if (!scalable) { - configData.SizePixels = fontSize * std::max(1.0F, std::floor(scale)); - } else { - configData.SizePixels = fontSize * scale; - } - + configData.SizePixels = fontSize * std::max(1.0F, scalable ? scale : std::floor(scale)); + // The oversample limit is chosen arbitrarily, based loosely + // on the experience of having UI scaling above 3.0 black + // out during testing as described in the previous comment + configData.OversampleH = scalable && configData.SizePixels <= 18 ? 2 : 1; configData.RasterizerDensity = density; } } From a94fbe49c5a6bb38331d0317efa9c56045fcd159 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 18 Jul 2024 00:09:00 -0500 Subject: [PATCH 7/7] impr: Re-render SVG textures on scaling changes This change also future-proofs the scaling event so it can be fired when users change the scaling mode in the settings, since both user scaling and window content scaling changes require re-rasterisation. --- .../include/hex/api/event_manager.hpp | 2 +- .../source/ui/imgui_imhex_extensions.cpp | 2 +- main/gui/source/window/window.cpp | 4 +-- .../source/content/out_of_box_experience.cpp | 8 ++++-- .../builtin/source/content/welcome_screen.cpp | 25 +++++++++++++------ plugins/fonts/source/font_loader.cpp | 4 +-- 6 files changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/libimhex/include/hex/api/event_manager.hpp b/lib/libimhex/include/hex/api/event_manager.hpp index 63e7955aebddc..f9efef20dab18 100644 --- a/lib/libimhex/include/hex/api/event_manager.hpp +++ b/lib/libimhex/include/hex/api/event_manager.hpp @@ -226,7 +226,7 @@ namespace hex { EVENT_DEF(EventAbnormalTermination, int); EVENT_DEF(EventThemeChanged); EVENT_DEF(EventOSThemeChanged); - EVENT_DEF(EventDPIChanged, float); + EVENT_DEF(EventScaleChanged); EVENT_DEF(EventWindowFocused, bool); /** diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp index 2a6fafae88fdb..bc571cdeff869 100644 --- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp +++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp @@ -33,7 +33,7 @@ namespace ImGuiExt { namespace { - void adjustSVGScale(const lunasvg::Document *document, int &width, int &height, int scale) { + void adjustSVGScale(const lunasvg::Document *document, int &width, int &height, float scale) { if (document->width() == 0 || document->height() == 0) return; diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index 5ac9a721a35e0..d26273cd840e5 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -933,7 +933,7 @@ namespace hex { return; ImHexApi::System::impl::setContentScale(newScale); - EventDPIChanged::post(newScale); + EventScaleChanged::post(); auto win = static_cast(hex::glfw::GetWindowUserPointer(window)); win->fullFrame(); @@ -1040,7 +1040,7 @@ namespace hex { plugin.setImGuiContext(ImGui::GetCurrentContext()); RequestInitThemeHandlers::post(); - EventDPIChanged::post(ImHexApi::System::getContentScale()); + EventScaleChanged::post(); } void Window::exitGLFW() { diff --git a/plugins/builtin/source/content/out_of_box_experience.cpp b/plugins/builtin/source/content/out_of_box_experience.cpp index 102f0a020aba6..ef3e42cfcd065 100644 --- a/plugins/builtin/source/content/out_of_box_experience.cpp +++ b/plugins/builtin/source/content/out_of_box_experience.cpp @@ -442,8 +442,12 @@ namespace hex::plugin::builtin { ImHexApi::System::setWindowResizable(false); const auto imageTheme = ThemeManager::getImageTheme(); - const auto scale = ImHexApi::System::getContentScale(); - s_imhexBanner = ImGuiExt::Texture::fromSVG(romfs::get(hex::format("assets/{}/banner.svg", imageTheme)).span(), 300_scaled, 0, scale); + auto loadBanner = [=] { + const auto scale = ImHexApi::System::getContentScale(); + s_imhexBanner = ImGuiExt::Texture::fromSVG(romfs::get(hex::format("assets/{}/banner.svg", imageTheme)).span(), 300_scaled, 0, scale); + }; + EventScaleChanged::subscribe(loadBanner); + loadBanner(); s_compassTexture = ImGuiExt::Texture::fromImage(romfs::get("assets/common/compass.png").span()); s_globeTexture = ImGuiExt::Texture::fromImage(romfs::get("assets/common/globe.png").span()); s_screenshotDescriptions = nlohmann::json::parse(romfs::get("assets/screenshot_descriptions.json").string()); diff --git a/plugins/builtin/source/content/welcome_screen.cpp b/plugins/builtin/source/content/welcome_screen.cpp index 48e01986c8772..72a20fb5d048a 100644 --- a/plugins/builtin/source/content/welcome_screen.cpp +++ b/plugins/builtin/source/content/welcome_screen.cpp @@ -479,6 +479,15 @@ namespace hex::plugin::builtin { } } + static auto changeTextureSvg(const std::string &path, float width) { + const auto scale = ImHexApi::System::getContentScale(); + return ImGuiExt::Texture::fromSVG(romfs::get(path).span(), width, 0, scale, ImGuiExt::Texture::Filter::Linear); + }; + + static void loadBannerTexture() { + s_bannerTexture = changeTextureSvg(hex::format("assets/{}/banner.svg", ThemeManager::getImageTheme()), 300_scaled); + } + /** * @brief Registers the event handlers related to the welcome screen * should only be called once, at startup @@ -518,19 +527,21 @@ namespace hex::plugin::builtin { ImHexApi::System::setTargetFPS(static_cast(value.get(14))); }); + EventScaleChanged::subscribe([]() { + // FIXME: The condition is a hack to wait until the + // RequestChangeTheme event has fired because it is too hard to me + // to do appropriate state management with way the event manager is + // designed and with global variables everywhere + if (s_bannerTexture) + loadBannerTexture(); + }); RequestChangeTheme::subscribe([](const std::string &theme) { auto changeTexture = [&](const std::string &path) { return ImGuiExt::Texture::fromImage(romfs::get(path).span(), ImGuiExt::Texture::Filter::Nearest); }; - auto changeTextureSvg = [&](const std::string &path, float width) { - // UI scaling is already baked into the width - const auto scale = ImHexApi::System::getContentScale(); - return ImGuiExt::Texture::fromSVG(romfs::get(path).span(), width, 0, scale, ImGuiExt::Texture::Filter::Linear); - }; - ThemeManager::changeTheme(theme); - s_bannerTexture = changeTextureSvg(hex::format("assets/{}/banner.svg", ThemeManager::getImageTheme()), 300_scaled); + loadBannerTexture(); s_nightlyTexture = changeTextureSvg(hex::format("assets/{}/nightly.svg", "common"), 35_scaled); s_backdropTexture = changeTexture(hex::format("assets/{}/backdrop.png", ThemeManager::getImageTheme())); diff --git a/plugins/fonts/source/font_loader.cpp b/plugins/fonts/source/font_loader.cpp index a06c2e5a9ead5..2305439a3c236 100644 --- a/plugins/fonts/source/font_loader.cpp +++ b/plugins/fonts/source/font_loader.cpp @@ -51,7 +51,7 @@ namespace hex::fonts { m_config.SizePixels = ImHexApi::Fonts::DefaultFontSize; m_config.MergeMode = false; - EventDPIChanged::subscribe(this, [this](float) { + EventScaleChanged::subscribe(this, [this]() { if (scaleAndBuild()) { ImGui_ImplOpenGL3_DestroyFontsTexture(); ImGui_ImplOpenGL3_CreateFontsTexture(); @@ -62,7 +62,7 @@ namespace hex::fonts { ~FontAtlas() { IM_DELETE(m_fontAtlas); - EventDPIChanged::unsubscribe(this); + EventScaleChanged::unsubscribe(this); } bool scaleAndBuild() {