From 3e942c3dc8955be577079fbc028ce216e1c594b2 Mon Sep 17 00:00:00 2001
From: SineStriker <55847490+SineStriker@users.noreply.github.com>
Date: 周二, 11 2月 2025 19:07:53 +0800
Subject: [PATCH] Fix numerous bugs (#162)

---
 src/core/shared/qwkwindowsextra_p.h |  247 +++++++++++++++++++++++++++++--------------------
 1 files changed, 145 insertions(+), 102 deletions(-)

diff --git a/src/core/shared/qwkwindowsextra_p.h b/src/core/shared/qwkwindowsextra_p.h
index df97727..8fdc86e 100644
--- a/src/core/shared/qwkwindowsextra_p.h
+++ b/src/core/shared/qwkwindowsextra_p.h
@@ -1,3 +1,7 @@
+// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
+// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
+// SPDX-License-Identifier: Apache-2.0
+
 #ifndef QWKWINDOWSEXTRA_P_H
 #define QWKWINDOWSEXTRA_P_H
 
@@ -16,12 +20,12 @@
 
 #include <QWKCore/qwindowkit_windows.h>
 
-#include <QtCore/private/qsystemlibrary_p.h>
-#include <QtCore/private/qwinregistry_p.h>
-
+#include <QtCore/QtMath>
 #include <QtGui/QGuiApplication>
 #include <QtGui/QStyleHints>
 #include <QtGui/QPalette>
+
+#include <QtCore/private/qsystemlibrary_p.h>
 
 // Don't include this header in any header files.
 
@@ -132,12 +136,14 @@
     };
     using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *;
 
-    enum PREFERRED_APP_MODE
-    {
-        PAM_DEFAULT = 0, // Default behavior on systems before Win10 1809. It indicates the application doesn't support dark mode at all.
-        PAM_AUTO = 1, // Available since Win10 1809, let system decide whether to enable dark mode or not.
+    enum PREFERRED_APP_MODE {
+        PAM_DEFAULT = 0, // Default behavior on systems before Win10 1809. It indicates the
+                         // application doesn't support dark mode at all.
+        PAM_AUTO =
+            1, // Available since Win10 1809, let system decide whether to enable dark mode or not.
         PAM_DARK = 2, // Available since Win10 1903, force dark mode regardless of the system theme.
-        PAM_LIGHT = 3, // Available since Win10 1903, force light mode regardless of the system theme.
+        PAM_LIGHT =
+            3, // Available since Win10 1903, force light mode regardless of the system theme.
         PAM_MAX = 4
     };
 
@@ -145,9 +151,9 @@
 
     // Win10 1809 (10.0.17763)
     using RefreshImmersiveColorPolicyStatePtr = VOID(WINAPI *)(VOID); // Ordinal 104
-    using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL); // Ordinal 133
-    using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL); // Ordinal 135
-    using FlushMenuThemesPtr = VOID(WINAPI *)(VOID); // Ordinal 136
+    using AllowDarkModeForWindowPtr = BOOL(WINAPI *)(HWND, BOOL);     // Ordinal 133
+    using AllowDarkModeForAppPtr = BOOL(WINAPI *)(BOOL);              // Ordinal 135
+    using FlushMenuThemesPtr = VOID(WINAPI *)(VOID);                  // Ordinal 136
     // Win10 1903 (10.0.18362)
     using SetPreferredAppModePtr = PREFERRED_APP_MODE(WINAPI *)(PREFERRED_APP_MODE); // Ordinal 135
 
@@ -155,7 +161,7 @@
 
         struct DynamicApis {
             static const DynamicApis &instance() {
-                static const DynamicApis inst{};
+                static const DynamicApis inst;
                 return inst;
             }
 
@@ -163,17 +169,14 @@
 
             DYNAMIC_API_DECLARE(DwmFlush);
             DYNAMIC_API_DECLARE(DwmIsCompositionEnabled);
-            DYNAMIC_API_DECLARE(DwmGetCompositionTimingInfo);
             DYNAMIC_API_DECLARE(DwmGetWindowAttribute);
             DYNAMIC_API_DECLARE(DwmSetWindowAttribute);
             DYNAMIC_API_DECLARE(DwmExtendFrameIntoClientArea);
             DYNAMIC_API_DECLARE(DwmEnableBlurBehindWindow);
             DYNAMIC_API_DECLARE(GetDpiForWindow);
             DYNAMIC_API_DECLARE(GetSystemMetricsForDpi);
+            DYNAMIC_API_DECLARE(AdjustWindowRectExForDpi);
             DYNAMIC_API_DECLARE(GetDpiForMonitor);
-            DYNAMIC_API_DECLARE(timeGetDevCaps);
-            DYNAMIC_API_DECLARE(timeBeginPeriod);
-            DYNAMIC_API_DECLARE(timeEndPeriod);
 
 #undef DYNAMIC_API_DECLARE
 
@@ -193,6 +196,7 @@
                 DYNAMIC_API_RESOLVE(user32, GetDpiForWindow);
                 DYNAMIC_API_RESOLVE(user32, GetSystemMetricsForDpi);
                 DYNAMIC_API_RESOLVE(user32, SetWindowCompositionAttribute);
+                DYNAMIC_API_RESOLVE(user32, AdjustWindowRectExForDpi);
 
                 QSystemLibrary shcore(QStringLiteral("shcore"));
                 DYNAMIC_API_RESOLVE(shcore, GetDpiForMonitor);
@@ -200,20 +204,14 @@
                 QSystemLibrary dwmapi(QStringLiteral("dwmapi"));
                 DYNAMIC_API_RESOLVE(dwmapi, DwmFlush);
                 DYNAMIC_API_RESOLVE(dwmapi, DwmIsCompositionEnabled);
-                DYNAMIC_API_RESOLVE(dwmapi, DwmGetCompositionTimingInfo);
                 DYNAMIC_API_RESOLVE(dwmapi, DwmGetWindowAttribute);
                 DYNAMIC_API_RESOLVE(dwmapi, DwmSetWindowAttribute);
                 DYNAMIC_API_RESOLVE(dwmapi, DwmExtendFrameIntoClientArea);
                 DYNAMIC_API_RESOLVE(dwmapi, DwmEnableBlurBehindWindow);
 
-                QSystemLibrary winmm(QStringLiteral("winmm"));
-                DYNAMIC_API_RESOLVE(winmm, timeGetDevCaps);
-                DYNAMIC_API_RESOLVE(winmm, timeBeginPeriod);
-                DYNAMIC_API_RESOLVE(winmm, timeEndPeriod);
-
 #undef DYNAMIC_API_RESOLVE
 
-#define UNDOC_API_RESOLVE(DLL, NAME, ORDINAL)                                                             \
+#define UNDOC_API_RESOLVE(DLL, NAME, ORDINAL)                                                      \
     p##NAME = reinterpret_cast<decltype(p##NAME)>(DLL.resolve(MAKEINTRESOURCEA(ORDINAL)))
 
                 QSystemLibrary uxtheme(QStringLiteral("uxtheme"));
@@ -228,127 +226,101 @@
 
             ~DynamicApis() = default;
 
-            Q_DISABLE_COPY_MOVE(DynamicApis)
+            Q_DISABLE_COPY(DynamicApis)
         };
 
     }
 
-    static inline constexpr bool operator==(const POINT &lhs, const POINT &rhs) noexcept {
+    inline constexpr bool operator==(const POINT &lhs, const POINT &rhs) noexcept {
         return ((lhs.x == rhs.x) && (lhs.y == rhs.y));
     }
 
-    static inline constexpr bool operator!=(const POINT &lhs, const POINT &rhs) noexcept {
+    inline constexpr bool operator!=(const POINT &lhs, const POINT &rhs) noexcept {
         return !operator==(lhs, rhs);
     }
 
-    static inline constexpr bool operator==(const SIZE &lhs, const SIZE &rhs) noexcept {
+    inline constexpr bool operator==(const SIZE &lhs, const SIZE &rhs) noexcept {
         return ((lhs.cx == rhs.cx) && (lhs.cy == rhs.cy));
     }
 
-    static inline constexpr bool operator!=(const SIZE &lhs, const SIZE &rhs) noexcept {
+    inline constexpr bool operator!=(const SIZE &lhs, const SIZE &rhs) noexcept {
         return !operator==(lhs, rhs);
     }
 
-    static inline constexpr bool operator>(const SIZE &lhs, const SIZE &rhs) noexcept {
+    inline constexpr bool operator>(const SIZE &lhs, const SIZE &rhs) noexcept {
         return ((lhs.cx * lhs.cy) > (rhs.cx * rhs.cy));
     }
 
-    static inline constexpr bool operator>=(const SIZE &lhs, const SIZE &rhs) noexcept {
+    inline constexpr bool operator>=(const SIZE &lhs, const SIZE &rhs) noexcept {
         return (operator>(lhs, rhs) || operator==(lhs, rhs));
     }
 
-    static inline constexpr bool operator<(const SIZE &lhs, const SIZE &rhs) noexcept {
+    inline constexpr bool operator<(const SIZE &lhs, const SIZE &rhs) noexcept {
         return (operator!=(lhs, rhs) && !operator>(lhs, rhs));
     }
 
-    static inline constexpr bool operator<=(const SIZE &lhs, const SIZE &rhs) noexcept {
+    inline constexpr bool operator<=(const SIZE &lhs, const SIZE &rhs) noexcept {
         return (operator<(lhs, rhs) || operator==(lhs, rhs));
     }
 
-    static inline constexpr bool operator==(const RECT &lhs, const RECT &rhs) noexcept {
+    inline constexpr bool operator==(const RECT &lhs, const RECT &rhs) noexcept {
         return ((lhs.left == rhs.left) && (lhs.top == rhs.top) && (lhs.right == rhs.right) &&
                 (lhs.bottom == rhs.bottom));
     }
 
-    static inline constexpr bool operator!=(const RECT &lhs, const RECT &rhs) noexcept {
+    inline constexpr bool operator!=(const RECT &lhs, const RECT &rhs) noexcept {
         return !operator==(lhs, rhs);
     }
 
-    static inline constexpr QPoint point2qpoint(const POINT &point) {
+    inline constexpr QPoint point2qpoint(const POINT &point) {
         return QPoint{int(point.x), int(point.y)};
     }
 
-    static inline constexpr POINT qpoint2point(const QPoint &point) {
+    inline constexpr POINT qpoint2point(const QPoint &point) {
         return POINT{LONG(point.x()), LONG(point.y())};
     }
 
-    static inline constexpr QSize size2qsize(const SIZE &size) {
+    inline constexpr QSize size2qsize(const SIZE &size) {
         return QSize{int(size.cx), int(size.cy)};
     }
 
-    static inline constexpr SIZE qsize2size(const QSize &size) {
+    inline constexpr SIZE qsize2size(const QSize &size) {
         return SIZE{LONG(size.width()), LONG(size.height())};
     }
 
-    static inline constexpr QRect rect2qrect(const RECT &rect) {
+    inline constexpr QRect rect2qrect(const RECT &rect) {
         return QRect{
             QPoint{int(rect.left),        int(rect.top)         },
             QSize{int(RECT_WIDTH(rect)), int(RECT_HEIGHT(rect))}
         };
     }
 
-    static inline constexpr RECT qrect2rect(const QRect &qrect) {
+    inline constexpr RECT qrect2rect(const QRect &qrect) {
         return RECT{LONG(qrect.left()), LONG(qrect.top()), LONG(qrect.right()),
                     LONG(qrect.bottom())};
     }
 
-    static inline /*constexpr*/ QString hwnd2str(const WId windowId) {
+    inline constexpr QMargins margins2qmargins(const MARGINS &margins) {
+        return {margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth,
+                margins.cyBottomHeight};
+    }
+
+    inline constexpr MARGINS qmargins2margins(const QMargins &qmargins) {
+        return {qmargins.left(), qmargins.right(), qmargins.top(), qmargins.bottom()};
+    }
+
+    inline /*constexpr*/ QString hwnd2str(const WId windowId) {
         // NULL handle is allowed here.
         return QLatin1String("0x") +
                QString::number(windowId, 16).toUpper().rightJustified(8, u'0');
     }
 
-    static inline /*constexpr*/ QString hwnd2str(HWND hwnd) {
+    inline /*constexpr*/ QString hwnd2str(HWND hwnd) {
         // NULL handle is allowed here.
         return hwnd2str(reinterpret_cast<WId>(hwnd));
     }
 
-    static inline bool isWin8OrGreater() {
-        static const bool result = IsWindows8OrGreater_Real();
-        return result;
-    }
-
-    static inline bool isWin8Point1OrGreater() {
-        static const bool result = IsWindows8Point1OrGreater_Real();
-        return result;
-    }
-
-    static inline bool isWin10OrGreater() {
-        static const bool result = IsWindows10OrGreater_Real();
-        return result;
-    }
-
-    static inline bool isWin101809OrGreater() {
-        static const bool result = IsWindows101809OrGreater_Real();
-        return result;
-    }
-
-    static inline bool isWin101903OrGreater() {
-        static const bool result = IsWindows101903OrGreater_Real();
-        return result;
-    }
-
-    static inline bool isWin11OrGreater() {
-        static const bool result = IsWindows11OrGreater_Real();
-        return result;
-    }
-
-    static inline bool isWin1122H2OrGreater() {
-        static const bool result = IsWindows1122H2OrGreater_Real();
-        return result;
-    }
-
-    static inline bool isDwmCompositionEnabled() {
+    inline bool isDwmCompositionEnabled() {
         if (isWin8OrGreater()) {
             return true;
         }
@@ -360,35 +332,38 @@
         return SUCCEEDED(apis.pDwmIsCompositionEnabled(&enabled)) && enabled;
     }
 
-    static inline bool isWindowFrameBorderColorized() {
-        const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)");
+    inline bool isWindowFrameBorderColorized() {
+        WindowsRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)");
         if (!registry.isValid()) {
             return false;
         }
-        const auto value = registry.dwordValue(L"ColorPrevalence");
+        auto value = registry.dwordValue(L"ColorPrevalence");
         if (!value.second) {
             return false;
         }
         return value.first;
     }
 
-    static inline bool isHighContrastModeEnabled() {
+    inline bool isHighContrastModeEnabled() {
         HIGHCONTRASTW hc{};
         hc.cbSize = sizeof(hc);
         ::SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, FALSE);
         return (hc.dwFlags & HCF_HIGHCONTRASTON);
     }
 
-    static inline bool isDarkThemeActive() {
+    inline bool isDarkThemeActive() {
+        if (!isWin101809OrGreater()) {
+            return false;
+        }
 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
         return QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark;
 #else
-        const QWinRegistryKey registry(
+        WindowsRegistryKey registry(
             HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
         if (!registry.isValid()) {
             return false;
         }
-        const auto value = registry.dwordValue(L"AppsUseLightTheme");
+        auto value = registry.dwordValue(L"AppsUseLightTheme");
         if (!value.second) {
             return false;
         }
@@ -396,43 +371,111 @@
 #endif
     }
 
-    static inline bool isDarkWindowFrameEnabled(HWND hwnd) {
-        BOOL enabled = FALSE;
-        const DynamicApis &apis = DynamicApis::instance();
-        if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, _DWMWA_USE_IMMERSIVE_DARK_MODE, &enabled,
-                                                  sizeof(enabled)))) {
-            return enabled;
-        } else if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd,
-                                                         _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
-                                                         &enabled, sizeof(enabled)))) {
-            return enabled;
-        } else {
+    inline bool isDarkWindowFrameEnabled(HWND hwnd) {
+        if (!isWin101809OrGreater()) {
             return false;
         }
+        BOOL enabled = FALSE;
+        const DynamicApis &apis = DynamicApis::instance();
+        const auto attr = isWin1020H1OrGreater() ? _DWMWA_USE_IMMERSIVE_DARK_MODE
+                                                 : _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
+        return SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, attr, &enabled, sizeof(enabled))) &&
+               enabled;
     }
 
-    static inline QColor getAccentColor() {
+    inline QColor getAccentColor() {
 #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
         return QGuiApplication::palette().color(QPalette::Accent);
 #else
-        const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)");
+        WindowsRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)");
         if (!registry.isValid()) {
             return {};
         }
-        const auto value = registry.dwordValue(L"AccentColor");
+        auto value = registry.dwordValue(L"AccentColor");
         if (!value.second) {
             return {};
         }
         // The retrieved value is in the #AABBGGRR format, we need to
         // convert it to the #AARRGGBB format which Qt expects.
-        const QColor abgr = QColor::fromRgba(value.first);
-        if (!abgr.isValid()) {
+        QColor color = QColor::fromRgba(value.first);
+        if (!color.isValid()) {
             return {};
         }
-        return QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha());
+        return QColor::fromRgb(color.blue(), color.green(), color.red(), color.alpha());
 #endif
     }
 
+    inline quint32 getDpiForWindow(HWND hwnd) {
+        const DynamicApis &apis = DynamicApis::instance();
+        if (apis.pGetDpiForWindow) { // Win10
+            return apis.pGetDpiForWindow(hwnd);
+        } else if (apis.pGetDpiForMonitor) { // Win8.1
+            HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+            UINT dpiX{0};
+            UINT dpiY{0};
+            apis.pGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
+            return dpiX;
+        } else { // Win2K
+            HDC hdc = ::GetDC(nullptr);
+            const int dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX);
+            // const int dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY);
+            ::ReleaseDC(nullptr, hdc);
+            return quint32(dpiX);
+        }
+    }
+
+    inline quint32 getSystemMetricsForDpi(int index, quint32 dpi) {
+        const DynamicApis &apis = DynamicApis::instance();
+        if (apis.pGetSystemMetricsForDpi) {
+            return apis.pGetSystemMetricsForDpi(index, dpi);
+        }
+        const int result = ::GetSystemMetrics(index);
+        // GetSystemMetrics() always give you scaled value.
+        if (dpi != USER_DEFAULT_SCREEN_DPI) {
+            return result;
+        }
+        const qreal dpr = qreal(dpi) / qreal(USER_DEFAULT_SCREEN_DPI);
+        // ### Not sure how Windows itself rounds non-integer value.
+        return qFloor(qreal(result) / dpr);
+    }
+
+    inline quint32 getWindowFrameBorderThickness(HWND hwnd) {
+        const DynamicApis &apis = DynamicApis::instance();
+        if (isWin11OrGreater()) {
+            UINT result = 0;
+            if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
+                                                      &result, sizeof(result)))) {
+                return result;
+            }
+        }
+        if (isWin10OrGreater()) {
+            const quint32 dpi = getDpiForWindow(hwnd);
+            // When DPI is 96, it should be 1px.
+            return getSystemMetricsForDpi(SM_CXBORDER, dpi);
+        }
+        // There's no such thing (a visible frame border line) before Win10.
+        return 0;
+    }
+
+    inline quint32 getResizeBorderThickness(HWND hwnd) {
+        const quint32 dpi = getDpiForWindow(hwnd);
+        // When DPI is 96, SM_CXSIZEFRAME is 4px, SM_CXPADDEDBORDER is also 4px,
+        // so the result should be 8px. This result won't be affected by OS version,
+        // it's 8px in Win7, and so in Win11.
+        return getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) +
+               getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
+    }
+
+    inline quint32 getTitleBarHeight(HWND hwnd) {
+        const quint32 dpi = getDpiForWindow(hwnd);
+        // When DPI is 96, SM_CYCAPTION is 23px, so the result should be 31px.
+        // However, according to latest MS design manual, the title bar height
+        // should be 32px, maybe there's some rounding issue.
+        return getSystemMetricsForDpi(SM_CYCAPTION, dpi) +
+               getSystemMetricsForDpi(SM_CYSIZEFRAME, dpi) +
+               getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
+    }
+
 }
 
 #endif // QWKWINDOWSEXTRA_P_H

--
Gitblit v1.9.1