From cbec0f6e008c45b957949b973a4e5638ef48335e Mon Sep 17 00:00:00 2001 From: lampy Date: Fri, 10 Jan 2025 16:20:29 +0600 Subject: [PATCH 1/5] Scale window decorations with screen DPI on Windows --- os/win/winapi.cpp | 6 ++++++ os/win/winapi.h | 14 ++++++++++++++ os/win/window.cpp | 39 +++++++++++++++++++++++++++++++++------ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/os/win/winapi.cpp b/os/win/winapi.cpp index e78376d11..3a736d89a 100644 --- a/os/win/winapi.cpp +++ b/os/win/winapi.cpp @@ -25,6 +25,12 @@ WinAPI::WinAPI() GET_PROC(m_user32, GetPointerInfo); GET_PROC(m_user32, GetPointerPenInfo); GET_PROC(m_user32, SetProcessDpiAwarenessContext); + GET_PROC(m_user32, GetWindowDpiAwarenessContext); + GET_PROC(m_user32, AreDpiAwarenessContextsEqual); + GET_PROC(m_user32, EnableNonClientDpiScaling); + GET_PROC(m_user32, GetDpiForWindow); + GET_PROC(m_user32, GetSystemMetricsForDpi); + GET_PROC(m_user32, AdjustWindowRectExForDpi); } if (m_ninput) { GET_PROC(m_ninput, CreateInteractionContext); diff --git a/os/win/winapi.h b/os/win/winapi.h index b80b9adb6..caac9b6cb 100644 --- a/os/win/winapi.h +++ b/os/win/winapi.h @@ -47,6 +47,12 @@ typedef HRESULT(WINAPI* ProcessPointerFramesInteractionContext_Func)( const POINTER_INFO* pointerInfo); typedef BOOL(WINAPI* SetProcessDpiAwarenessContext_Func)(DPI_AWARENESS_CONTEXT value); +typedef DPI_AWARENESS_CONTEXT(WINAPI* GetWindowDpiAwarenessContext_Func)(HWND hwnd); +typedef BOOL(WINAPI* AreDpiAwarenessContextsEqual_Func)(DPI_AWARENESS_CONTEXT dpiContextA, DPI_AWARENESS_CONTEXT dpiContextB); +typedef BOOL(WINAPI* EnableNonClientDpiScaling_Func)(HWND hwnd); +typedef UINT(WINAPI* GetDpiForWindow_Func)(HWND hwnd); +typedef int(WINAPI* GetSystemMetricsForDpi_Func)(int nIndex, UINT dpi); +typedef BOOL(WINAPI* AdjustWindowRectExForDpi_Func)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi); class WinAPI { public: @@ -71,6 +77,14 @@ class WinAPI { SetPropertyInteractionContext_Func SetPropertyInteractionContext = nullptr; ProcessPointerFramesInteractionContext_Func ProcessPointerFramesInteractionContext = nullptr; + // Functions introduced on Windows 10 version 1607 + GetWindowDpiAwarenessContext_Func GetWindowDpiAwarenessContext = nullptr; + AreDpiAwarenessContextsEqual_Func AreDpiAwarenessContextsEqual = nullptr; + EnableNonClientDpiScaling_Func EnableNonClientDpiScaling = nullptr; + GetDpiForWindow_Func GetDpiForWindow = nullptr; + GetSystemMetricsForDpi_Func GetSystemMetricsForDpi = nullptr; + AdjustWindowRectExForDpi_Func AdjustWindowRectExForDpi = nullptr; + // Functions introduced on Windows 10 version 1703 SetProcessDpiAwarenessContext_Func SetProcessDpiAwarenessContext = nullptr; diff --git a/os/win/window.cpp b/os/win/window.cpp index f3f923169..957b67721 100644 --- a/os/win/window.cpp +++ b/os/win/window.cpp @@ -1787,8 +1787,16 @@ LRESULT WindowWin::wndProc(UINT msg, WPARAM wparam, LPARAM lparam) // scrollbars. In this way they are not visible, but we still // get their messages. NCCALCSIZE_PARAMS* cs = reinterpret_cast(lparam); - cs->rgrc[0].right += GetSystemMetrics(SM_CYVSCROLL); - cs->rgrc[0].bottom += GetSystemMetrics(SM_CYHSCROLL); + + auto& winApi = system()->winApi(); + if (winApi.GetWindowDpiAwarenessContext) { + UINT dpi = winApi.GetDpiForWindow(m_hwnd); + cs->rgrc[0].right += winApi.GetSystemMetricsForDpi(SM_CYVSCROLL, dpi); + cs->rgrc[0].bottom += winApi.GetSystemMetricsForDpi(SM_CYHSCROLL, dpi); + } else { + cs->rgrc[0].right += GetSystemMetrics(SM_CYVSCROLL); + cs->rgrc[0].bottom += GetSystemMetrics(SM_CYHSCROLL); + } } } break; @@ -2490,10 +2498,21 @@ HWND WindowWin::createHwnd(WindowWin* self, const WindowSpec& spec) rc.w = spec.contentRect().w; rc.h = spec.contentRect().h; RECT ncrc = { 0, 0, rc.w, rc.h }; - AdjustWindowRectEx(&ncrc, - style, - false, // Add a field to WindowSpec to add native menu bars - exStyle); + + auto& winApi = system()->winApi(); + if (winApi.GetWindowDpiAwarenessContext) { + UINT dpi = winApi.GetDpiForWindow(self->m_hwnd); + winApi.AdjustWindowRectExForDpi(&ncrc, + style, + false, // Add a field to WindowSpec to add native menu bars + exStyle, + dpi); + } else { + AdjustWindowRectEx(&ncrc, + style, + false, // Add a field to WindowSpec to add native menu bars + exStyle); + } if (rc.x != CW_USEDEFAULT) rc.x += ncrc.left; @@ -2541,6 +2560,14 @@ LRESULT CALLBACK WindowWin::staticWndProc(HWND hwnd, UINT msg, WPARAM wparam, LP if (wnd && wnd->m_hwnd == nullptr) wnd->m_hwnd = hwnd; + + // Enable scaling for titlebar with legacy per-monitor dpi awareness steeting + auto& winApi = system()->winApi(); + if (winApi.GetWindowDpiAwarenessContext) { + DPI_AWARENESS_CONTEXT dpiContext = winApi.GetWindowDpiAwarenessContext(hwnd); + if (!winApi.AreDpiAwarenessContextsEqual(dpiContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) + winApi.EnableNonClientDpiScaling(hwnd); + } } else { wnd = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); From 0f638dbd01948621344133d757dbdd6ea4cd4329 Mon Sep 17 00:00:00 2001 From: lampy Date: Sat, 11 Jan 2025 15:50:26 +0600 Subject: [PATCH 2/5] Scale window content with screen DPI on Windows --- os/win/window.cpp | 26 +++++++++++++++++++++++++- os/win/window.h | 2 ++ os/window.h | 4 ++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/os/win/window.cpp b/os/win/window.cpp index 957b67721..96bbd7e9b 100644 --- a/os/win/window.cpp +++ b/os/win/window.cpp @@ -158,6 +158,7 @@ WindowWin::Touch::Touch() : fingers(0), canBeMouse(false), asMouse(false), timer WindowWin::WindowWin(const WindowSpec& spec) : m_clientSize(1, 1) , m_scale(spec.scale()) + , m_baseScale(spec.scale()) , m_isCreated(false) , m_adjustShadow(true) , m_translateDeadKeys(false) @@ -263,6 +264,11 @@ WindowWin::WindowWin(const WindowSpec& spec) SetWindowLongPtr(m_hwnd, GWLP_USERDATA, reinterpret_cast(this)); + if (winApi.GetWindowDpiAwarenessContext) { + double scaleFactor = winApi.GetDpiForWindow(m_hwnd) / (double) USER_DEFAULT_SCREEN_DPI; + m_scale = std::max(1, (int)(m_baseScale * scaleFactor + 0.51)); + } + // This flag is used to avoid calling T::resizeImpl() when we // add the scrollbars to the window. (As the T type could not be // fully initialized yet.) @@ -361,7 +367,10 @@ os::ColorSpaceRef WindowWin::colorSpace() const void WindowWin::setScale(int scale) { - m_scale = scale; + double scaleFactor = m_scale / (double) m_baseScale; + m_baseScale = scale; + // round to nearest integer, rounding 0.5 up + m_scale = std::max(1, (int)(scale * scaleFactor + 0.51)); // Align window size to new scale { @@ -966,6 +975,21 @@ LRESULT WindowWin::wndProc(UINT msg, WPARAM wparam, LPARAM lparam) } break; + case WM_DPICHANGED: { + double scaleFactor = HIWORD(wparam) / (double) USER_DEFAULT_SCREEN_DPI; + m_scale = std::max(1, (int)(m_baseScale * scaleFactor + 0.51)); + + RECT* prcNewWindow = (RECT*)lparam; + SetWindowPos(m_hwnd, + nullptr, + prcNewWindow ->left, + prcNewWindow ->top, + prcNewWindow->right - prcNewWindow->left, + prcNewWindow->bottom - prcNewWindow->top, + SWP_NOZORDER | SWP_NOACTIVATE); + break; + } + case WM_SETCURSOR: // We set our custom cursor if we are in the client area, or in // the case of windows with custom frames (borderless), we diff --git a/os/win/window.h b/os/win/window.h index 33bd66e7f..a50263e0b 100644 --- a/os/win/window.h +++ b/os/win/window.h @@ -39,6 +39,7 @@ class WindowWin : public Window { os::ScreenRef screen() const override; os::ColorSpaceRef colorSpace() const override; int scale() const override { return m_scale; } + int baseScale() const override { return m_baseScale; } void setScale(int scale) override; bool isVisible() const override; void setVisible(bool visible) override; @@ -144,6 +145,7 @@ class WindowWin : public Window { std::unique_ptr m_dragTargetAdapter = nullptr; int m_scale; + int m_baseScale; bool m_isCreated; // Since Windows Vista, it looks like Microsoft decided to change // the meaning of the window position to the shadow position (when diff --git a/os/window.h b/os/window.h index b66736ff2..c5f1c069a 100644 --- a/os/window.h +++ b/os/window.h @@ -103,6 +103,10 @@ class Window : public RefCount { // The window size will be a multiple of the scale. virtual void setScale(int scale) = 0; + // Returns the current window scale for default DPI value. + // It is constant across different displays. + virtual int baseScale() const { return scale(); } + // Returns true if the window is visible in the screen. virtual bool isVisible() const = 0; From 511a113bff6d23d0c3ea357d1ca91e30340fddf2 Mon Sep 17 00:00:00 2001 From: lampysprites <49268426+lampysprites@users.noreply.github.com> Date: Sun, 30 Mar 2025 08:24:12 +0600 Subject: [PATCH 3/5] Update os/win/window.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martín Capello --- os/win/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/win/window.cpp b/os/win/window.cpp index 96bbd7e9b..17908ce7f 100644 --- a/os/win/window.cpp +++ b/os/win/window.cpp @@ -266,7 +266,7 @@ WindowWin::WindowWin(const WindowSpec& spec) if (winApi.GetWindowDpiAwarenessContext) { double scaleFactor = winApi.GetDpiForWindow(m_hwnd) / (double) USER_DEFAULT_SCREEN_DPI; - m_scale = std::max(1, (int)(m_baseScale * scaleFactor + 0.51)); + m_scale = std::ceil(m_baseScale * scaleFactor); } // This flag is used to avoid calling T::resizeImpl() when we From 7f9faf65b4e7d51623dbf4929d803c2cacb47d8b Mon Sep 17 00:00:00 2001 From: lampysprites <49268426+lampysprites@users.noreply.github.com> Date: Sun, 30 Mar 2025 08:24:25 +0600 Subject: [PATCH 4/5] Update os/win/window.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martín Capello --- os/win/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/win/window.cpp b/os/win/window.cpp index 17908ce7f..e59e6e974 100644 --- a/os/win/window.cpp +++ b/os/win/window.cpp @@ -370,7 +370,7 @@ void WindowWin::setScale(int scale) double scaleFactor = m_scale / (double) m_baseScale; m_baseScale = scale; // round to nearest integer, rounding 0.5 up - m_scale = std::max(1, (int)(scale * scaleFactor + 0.51)); + m_scale = std::ceil(m_baseScale * scaleFactor); // Align window size to new scale { From c8dd1a971cf3a53822d6154ae589690697ea220d Mon Sep 17 00:00:00 2001 From: lampysprites <49268426+lampysprites@users.noreply.github.com> Date: Sun, 30 Mar 2025 08:24:35 +0600 Subject: [PATCH 5/5] Update os/win/window.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martín Capello --- os/win/window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/win/window.cpp b/os/win/window.cpp index e59e6e974..c22e102bd 100644 --- a/os/win/window.cpp +++ b/os/win/window.cpp @@ -977,7 +977,7 @@ LRESULT WindowWin::wndProc(UINT msg, WPARAM wparam, LPARAM lparam) case WM_DPICHANGED: { double scaleFactor = HIWORD(wparam) / (double) USER_DEFAULT_SCREEN_DPI; - m_scale = std::max(1, (int)(m_baseScale * scaleFactor + 0.51)); + m_scale = std::ceil(m_baseScale * scaleFactor); RECT* prcNewWindow = (RECT*)lparam; SetWindowPos(m_hwnd,