Zhao Yuhang
2023-12-11 2f6c83c095724bbba0f43b2f2893ba73c17949a6
src/core/contexts/win32windowcontext.cpp
@@ -1,14 +1,15 @@
#include "win32windowcontext_p.h"
#include "qwkcoreglobal_p.h"
#include <optional>
#include <QtCore/QHash>
#include <QtCore/QAbstractNativeEventFilter>
#include <QtCore/QOperatingSystemVersion>
#include <QtCore/QScopeGuard>
#include <QtGui/QGuiApplication>
#include <QtGui/QPainter>
#include <QtGui/QPalette>
#include <QtGui/QStyleHints>
#include <QtCore/private/qwinregistry_p.h>
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtGui/private/qhighdpiscaling_p.h>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@@ -25,18 +26,47 @@
#include <dwmapi.h>
#include <timeapi.h>
#include "nativeeventfilter.h"
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Q_DECLARE_METATYPE(QMargins)
#endif
namespace QWK {
    static constexpr const auto kAutoHideTaskBarThickness =
        quint8{2}; // The thickness of an auto-hide taskbar in pixels.
    using _DWMWINDOWATTRIBUTE = enum _DWMWINDOWATTRIBUTE
    {
        _DWMWA_USE_HOSTBACKDROPBRUSH = 17, // [set] BOOL, Allows the use of host backdrop brushes for the window.
        _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, // Undocumented, the same with DWMWA_USE_IMMERSIVE_DARK_MODE, but available on systems before Win10 20H1.
        _DWMWA_USE_IMMERSIVE_DARK_MODE = 20, // [set] BOOL, Allows a window to either use the accent color, or dark, according to the user Color Mode preferences.
        _DWMWA_WINDOW_CORNER_PREFERENCE = 33, // [set] WINDOW_CORNER_PREFERENCE, Controls the policy that rounds top-level window corners
        _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37, // [get] UINT, width of the visible border around a thick frame window
        _DWMWA_SYSTEMBACKDROP_TYPE = 38, // [get, set] SYSTEMBACKDROP_TYPE, Controls the system-drawn backdrop material of a window, including behind the non-client area.
        _DWMWA_MICA_EFFECT = 1029 // Undocumented, use this value to enable Mica material on Win11 21H2. You should use DWMWA_SYSTEMBACKDROP_TYPE instead on Win11 22H2 and newer.
    };
    using WndProcHash = QHash<HWND, Win32WindowContext *>; // hWnd -> context
    // The thickness of an auto-hide taskbar in pixels.
    static constexpr const auto kAutoHideTaskBarThickness = quint8{2};
    static inline constexpr const auto kFrameBorderActiveColorLight =
        QColor{110, 110, 110};                                                           // #6E6E6E
    static inline constexpr const auto kFrameBorderActiveColorDark = QColor{51, 51, 51}; // #333333
    static inline constexpr const auto kFrameBorderInactiveColorLight =
        QColor{167, 167, 167};                                                           // #A7A7A7
    static inline constexpr const auto kFrameBorderInactiveColorDark =
        QColor{61, 61, 62};                                                              // #3D3D3E
    // hWnd -> context
    using WndProcHash = QHash<HWND, Win32WindowContext *>;
    Q_GLOBAL_STATIC(WndProcHash, g_wndProcHash)
    static WNDPROC g_qtWindowProc = nullptr; // Original Qt window proc function
    // Original Qt window proc function
    static WNDPROC g_qtWindowProc = nullptr;
    // ### FIXME FIXME FIXME
    // ### FIXME: Tell the user to call in the documentation, instead of automatically
    // calling it directly.
    // ### FIXME FIXME FIXME
    static struct QWK_Hook {
        QWK_Hook() {
            qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
@@ -44,47 +74,67 @@
    } g_hook{};
    struct DynamicApis {
        decltype(&::DwmFlush) pDwmFlush = nullptr;
        decltype(&::DwmIsCompositionEnabled) pDwmIsCompositionEnabled = nullptr;
        decltype(&::DwmGetCompositionTimingInfo) pDwmGetCompositionTimingInfo = nullptr;
        decltype(&::GetDpiForWindow) pGetDpiForWindow = nullptr;
        decltype(&::GetSystemMetricsForDpi) pGetSystemMetricsForDpi = nullptr;
        decltype(&::GetDpiForMonitor) pGetDpiForMonitor = nullptr;
        decltype(&::timeGetDevCaps) ptimeGetDevCaps = nullptr;
        decltype(&::timeBeginPeriod) ptimeBeginPeriod = nullptr;
        decltype(&::timeEndPeriod) ptimeEndPeriod = nullptr;
        DynamicApis() {
            QSystemLibrary user32(QStringLiteral("user32.dll"));
            pGetDpiForWindow =
                reinterpret_cast<decltype(pGetDpiForWindow)>(user32.resolve("GetDpiForWindow"));
            pGetSystemMetricsForDpi = reinterpret_cast<decltype(pGetSystemMetricsForDpi)>(
                user32.resolve("GetSystemMetricsForDpi"));
            QSystemLibrary shcore(QStringLiteral("shcore.dll"));
            pGetDpiForMonitor =
                reinterpret_cast<decltype(pGetDpiForMonitor)>(shcore.resolve("GetDpiForMonitor"));
            QSystemLibrary dwmapi(QStringLiteral("dwmapi.dll"));
            pDwmFlush = reinterpret_cast<decltype(pDwmFlush)>(dwmapi.resolve("DwmFlush"));
            pDwmIsCompositionEnabled = reinterpret_cast<decltype(pDwmIsCompositionEnabled)>(
                dwmapi.resolve("DwmIsCompositionEnabled"));
            pDwmGetCompositionTimingInfo = reinterpret_cast<decltype(pDwmGetCompositionTimingInfo)>(dwmapi.resolve("DwmGetCompositionTimingInfo"));
            QSystemLibrary winmm(QStringLiteral("winmm.dll"));
            ptimeGetDevCaps = reinterpret_cast<decltype(ptimeGetDevCaps)>(winmm.resolve("timeGetDevCaps"));
            ptimeBeginPeriod = reinterpret_cast<decltype(ptimeBeginPeriod)>(winmm.resolve("timeBeginPeriod"));
            ptimeEndPeriod = reinterpret_cast<decltype(ptimeEndPeriod)>(winmm.resolve("timeEndPeriod"));
        }
        ~DynamicApis() = default;
        static const DynamicApis &instance() {
            static const DynamicApis inst{};
            return inst;
        }
//        template <typename T>
//        struct DefaultFunc;
//
//        template <typename Return, typename... Args>
//        struct DefaultFunc<Return(QT_WIN_CALLBACK *)(Args...)> {
//            static Return STDAPICALLTYPE func(Args...) {
//                return Return{};
//            }
//        };
//
// #define DYNAMIC_API_DECLARE(NAME) decltype(&::NAME) p##NAME =
// DefaultFunc<decltype(&::NAME)>::func
#define DYNAMIC_API_DECLARE(NAME) decltype(&::NAME) p##NAME = nullptr
        DYNAMIC_API_DECLARE(DwmFlush);
        DYNAMIC_API_DECLARE(DwmIsCompositionEnabled);
        DYNAMIC_API_DECLARE(DwmGetCompositionTimingInfo);
        DYNAMIC_API_DECLARE(DwmGetWindowAttribute);
        DYNAMIC_API_DECLARE(GetDpiForWindow);
        DYNAMIC_API_DECLARE(GetSystemMetricsForDpi);
        DYNAMIC_API_DECLARE(GetDpiForMonitor);
        DYNAMIC_API_DECLARE(timeGetDevCaps);
        DYNAMIC_API_DECLARE(timeBeginPeriod);
        DYNAMIC_API_DECLARE(timeEndPeriod);
#undef DYNAMIC_API_DECLARE
    private:
        DynamicApis() {
#define DYNAMIC_API_RESOLVE(DLL, NAME)                                                             \
    p##NAME = reinterpret_cast<decltype(p##NAME)>(DLL.resolve(#NAME))
            QSystemLibrary user32(QStringLiteral("user32"));
            DYNAMIC_API_RESOLVE(user32, GetDpiForWindow);
            DYNAMIC_API_RESOLVE(user32, GetSystemMetricsForDpi);
            QSystemLibrary shcore(QStringLiteral("shcore"));
            DYNAMIC_API_RESOLVE(shcore, GetDpiForMonitor);
            QSystemLibrary dwmapi(QStringLiteral("dwmapi"));
            DYNAMIC_API_RESOLVE(dwmapi, DwmFlush);
            DYNAMIC_API_RESOLVE(dwmapi, DwmIsCompositionEnabled);
            DYNAMIC_API_RESOLVE(dwmapi, DwmGetCompositionTimingInfo);
            DYNAMIC_API_RESOLVE(dwmapi, DwmGetWindowAttribute);
            QSystemLibrary winmm(QStringLiteral("winmm"));
            DYNAMIC_API_RESOLVE(winmm, timeGetDevCaps);
            DYNAMIC_API_RESOLVE(winmm, timeBeginPeriod);
            DYNAMIC_API_RESOLVE(winmm, timeEndPeriod);
#undef DYNAMIC_API_RESOLVE
        }
        ~DynamicApis() = default;
        Q_DISABLE_COPY_MOVE(DynamicApis)
    };
@@ -169,20 +219,22 @@
    }
    static inline bool isWin8OrGreater() {
        static const bool result =
            QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8;
        static const bool result = IsWindows8OrGreater_Real();
        return result;
    }
    static inline bool isWin8Point1OrGreater() {
        static const bool result =
            QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1;
        static const bool result = IsWindows8Point1OrGreater_Real();
        return result;
    }
    static inline bool isWin10OrGreater() {
        static const bool result =
            QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10;
        static const bool result = IsWindows10OrGreater_Real();
        return result;
    }
    static inline bool isWin11OrGreater() {
        static const bool result = IsWindows11OrGreater_Real();
        return result;
    }
@@ -198,6 +250,69 @@
        return SUCCEEDED(apis.pDwmIsCompositionEnabled(&enabled)) && enabled;
    }
    static inline bool isWindowFrameBorderColorized() {
        const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)");
        if (!registry.isValid()) {
            return false;
        }
        const auto value = registry.dwordValue(L"ColorPrevalence");
        if (!value.second) {
            return false;
        }
        return value.first;
    }
    static inline bool isDarkThemeActive() {
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
        return QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark;
#else
        const QWinRegistryKey registry(
            HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)");
        if (!registry.isValid()) {
            return false;
        }
        const auto value = registry.dwordValue(L"AppsUseLightTheme");
        if (!value.second) {
            return false;
        }
        return !value.first;
#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 {
            return false;
        }
    }
    static 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)");
        if (!registry.isValid()) {
            return {};
        }
        const 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()) {
            return {};
        }
        return QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha());
#endif
    }
    static inline void triggerFrameChange(HWND hwnd) {
        ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
                       SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER |
@@ -210,16 +325,28 @@
            return apis.pGetDpiForWindow(hwnd);
        } else if (apis.pGetDpiForMonitor) { // Win8.1
            HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
            UINT dpiX{USER_DEFAULT_SCREEN_DPI};
            UINT dpiY{USER_DEFAULT_SCREEN_DPI};
            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);
            //const int dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY);
            ::ReleaseDC(nullptr, hdc);
            return quint32(dpiX);
        }
    }
    static inline quint32 getWindowFrameBorderThickness(HWND hwnd) {
        UINT result{ 0 };
        const DynamicApis &apis = DynamicApis::instance();
        if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &result, sizeof(result)))) {
            return result;
        } else {
            const quint32 dpi = getDpiForWindow(hwnd);
            result = quint32(std::round(qreal(1) * qreal(dpi) / qreal(USER_DEFAULT_SCREEN_DPI)));
            return result;
        }
    }
@@ -249,11 +376,11 @@
    static inline void updateInternalWindowFrameMargins(HWND hwnd, QWindow *window) {
        const auto margins = [hwnd]() -> QMargins {
            const int titleBarHeight = getTitleBarHeight(hwnd);
            const auto titleBarHeight = int(getTitleBarHeight(hwnd));
            if (isWin10OrGreater()) {
                return {0, -titleBarHeight, 0, 0};
            } else {
                const int frameSize = getResizeBorderThickness(hwnd);
                const auto frameSize = int(getResizeBorderThickness(hwnd));
                return {-frameSize, -titleBarHeight, -frameSize, -frameSize};
            }
        }();
@@ -262,7 +389,7 @@
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
        if (QPlatformWindow *platformWindow = window->handle()) {
            if (const auto ni = QGuiApplication::platformNativeInterface()) {
                ni->setWindowProperty(platformWindow, QStringLiteral("_q_windowsCustomMargins"),
                ni->setWindowProperty(platformWindow, QStringLiteral("WindowsCustomMargins"),
                                      marginsVar);
            }
        }
@@ -358,6 +485,73 @@
        apis.ptimeEndPeriod(ms_granularity);
    }
    static inline void showSystemMenu2(HWND hWnd, const POINT &pos, const bool selectFirstEntry,
                                       const bool fixedSize) {
        const HMENU hMenu = ::GetSystemMenu(hWnd, FALSE);
        if (!hMenu) {
            // The corresponding window doesn't have a system menu, most likely due to the
            // lack of the "WS_SYSMENU" window style. This situation should not be treated
            // as an error so just ignore it and return early.
            return;
        }
        const bool maxOrFull = IsMaximized(hWnd) || isFullScreen(hWnd);
        ::EnableMenuItem(hMenu, SC_CLOSE, (MF_BYCOMMAND | MFS_ENABLED));
        ::EnableMenuItem(hMenu, SC_MAXIMIZE,
                         (MF_BYCOMMAND | ((maxOrFull || fixedSize) ? MFS_DISABLED : MFS_ENABLED)));
        ::EnableMenuItem(hMenu, SC_RESTORE,
                         (MF_BYCOMMAND | ((maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_DISABLED)));
        // The first menu item should be selected by default if the menu is brought
        // up by keyboard. I don't know how to pre-select a menu item but it seems
        // highlight can do the job. However, there's an annoying issue if we do
        // this manually: the highlighted menu item is really only highlighted,
        // not selected, so even if the mouse cursor hovers on other menu items
        // or the user navigates to other menu items through keyboard, the original
        // highlight bar will not move accordingly, the OS will generate another
        // highlight bar to indicate the current selected menu item, which will make
        // the menu look kind of weird. Currently I don't know how to fix this issue.
        ::HiliteMenuItem(hWnd, hMenu, SC_RESTORE,
                         (MF_BYCOMMAND | (selectFirstEntry ? MFS_HILITE : MFS_UNHILITE)));
        ::EnableMenuItem(hMenu, SC_MINIMIZE, (MF_BYCOMMAND | MFS_ENABLED));
        ::EnableMenuItem(hMenu, SC_SIZE,
                         (MF_BYCOMMAND | ((maxOrFull || fixedSize) ? MFS_DISABLED : MFS_ENABLED)));
        ::EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | (maxOrFull ? MFS_DISABLED : MFS_ENABLED)));
        // The default menu item will appear in bold font. There can only be one default
        // menu item per menu at most. Set the item ID to "UINT_MAX" (or simply "-1")
        // can clear the default item for the given menu.
        UINT defaultItemId = UINT_MAX;
        if (isWin11OrGreater()) {
            if (maxOrFull) {
                defaultItemId = SC_RESTORE;
            } else {
                defaultItemId = SC_MAXIMIZE;
            }
        }
        if (defaultItemId == UINT_MAX) {
            defaultItemId = SC_CLOSE;
        }
        ::SetMenuDefaultItem(hMenu, defaultItemId, FALSE);
        // Popup the system menu at the required position.
        const auto result = ::TrackPopupMenu(
            hMenu,
            (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)),
            pos.x, pos.y, 0, hWnd, nullptr);
        // Unhighlight the first menu item after the popup menu is closed, otherwise it will keep
        // highlighting until we unhighlight it manually.
        ::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | MFS_UNHILITE));
        if (!result) {
            // The user canceled the menu, no need to continue.
            return;
        }
        // Send the command that the user chooses to the corresponding window.
        ::PostMessageW(hWnd, WM_SYSCOMMAND, result, 0);
    }
    static inline Win32WindowContext::WindowPart getHitWindowPart(int hitTestResult) {
        switch (hitTestResult) {
            case HTCLIENT:
@@ -382,7 +576,8 @@
            case HTBORDER:
                return Win32WindowContext::FixedBorder;
            default:
                break; // unreachable
                // unreachable
                break;
        }
        return Win32WindowContext::Outside;
    }
@@ -426,7 +621,7 @@
    // DefWindowProc(). Consequently, we have to add a global native filter that forwards the result
    // of the hook function, telling Qt whether we have filtered the events before. Since Qt only
    // handles Windows window messages in the main thread, it is safe to do so.
    class WindowsNativeEventFilter : public QAbstractNativeEventFilter {
    class WindowsNativeEventFilter : public NativeEventFilter {
    public:
        bool nativeEventFilter(const QByteArray &eventType, void *message,
                               QT_NATIVE_EVENT_RESULT_TYPE *result) override {
@@ -440,7 +635,7 @@
            // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1546
            // Qt needs to refer to the WM_NCCALCSIZE message data that hasn't been processed, so we
            // have to process it after Qt acquired the initial data.
            // have to process it after Qt acquires the initial data.
            auto msg = static_cast<const MSG *>(message);
            if (msg->message == WM_NCCALCSIZE && lastMessageContext) {
                LRESULT res;
@@ -461,14 +656,12 @@
                return;
            }
            instance = new WindowsNativeEventFilter();
            installNativeEventFilter(instance);
        }
        static inline void uninstall() {
            if (!instance) {
                return;
            }
            removeNativeEventFilter(instance);
            delete instance;
            instance = nullptr;
        }
@@ -553,12 +746,7 @@
        // Try hooked procedure and save result
        LRESULT result;
        bool handled = ctx->windowProc(hWnd, message, wParam, lParam, &result);
        // TODO: Determine whether to show system menu
        // ...
        if (handled) {
        if (ctx->windowProc(hWnd, message, wParam, lParam, &result)) {
            return result;
        }
@@ -579,6 +767,85 @@
                WindowsNativeEventFilter::uninstall();
            }
        }
    }
    QString Win32WindowContext::key() const {
        return QStringLiteral("win32");
    }
    void Win32WindowContext::virtual_hook(int id, void *data) {
        switch (id) {
            case CentralizeHook: {
                const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
                moveToDesktopCenter(hwnd);
                return;
            }
            case ShowSystemMenuHook: {
                const auto &pos = *static_cast<const QPoint *>(data);
                auto hWnd = reinterpret_cast<HWND>(m_windowHandle->winId());
                showSystemMenu2(hWnd, qpoint2point(pos), false,
                                m_delegate->isHostSizeFixed(m_host));
                return;
            }
            case NeedsDrawBordersHook: {
                auto &result = *static_cast<bool *>(data);
                result = isWin10OrGreater() && !isWin11OrGreater();
                return;
            }
            case DrawBordersHook: {
                auto args = static_cast<void **>(data);
                auto &painter = *static_cast<QPainter *>(args[0]);
                const auto &rect = *static_cast<const QRect *>(args[1]);
                const auto &region = *static_cast<const QRegion *>(args[2]);
                const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
                QPen pen{};
                const auto borderThickness = int(QHighDpi::fromNativePixels(getWindowFrameBorderThickness(hwnd), m_windowHandle));
                pen.setWidth(borderThickness * 2);
                const bool active = m_host->isWidgetType() ? m_host->property("isActiveWindow").toBool() : m_host->property("active").toBool();
                const bool dark = isDarkThemeActive() && isDarkWindowFrameEnabled(hwnd);
                if (active) {
                    if (isWindowFrameBorderColorized()) {
                        pen.setColor(getAccentColor());
                    } else {
                        if (dark) {
                            pen.setColor(kFrameBorderActiveColorDark);
                        } else {
                            pen.setColor(kFrameBorderActiveColorLight);
                        }
                    }
                } else {
                    if (dark) {
                        pen.setColor(kFrameBorderInactiveColorDark);
                    } else {
                        pen.setColor(kFrameBorderInactiveColorLight);
                    }
                }
                painter.save();
                painter.setRenderHint(QPainter::Antialiasing); // ### TODO: do we need to enable or disable it?
                painter.setPen(pen);
                painter.drawLine(QLine{ QPoint{ 0, 0 }, QPoint{ rect.width(), 0 } });
                painter.restore();
                return;
            }
            case QueryBorderThicknessHook: {
                auto args = static_cast<void **>(data);
                const bool requireNative = *static_cast<const bool *>(args[0]);
                quint32 &thickness = *static_cast<quint32 *>(args[1]);
                const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
                const auto nativeThickness = getWindowFrameBorderThickness(hwnd);
                if (requireNative) {
                    thickness = nativeThickness;
                } else {
                    thickness = QHighDpi::fromNativePixels(nativeThickness, m_windowHandle);
                }
                return;
            }
            default: {
                // unreachable
                break;
            }
        }
        AbstractWindowContext::virtual_hook(id, data);
    }
    bool Win32WindowContext::setupHost() {
@@ -641,14 +908,17 @@
            return true;
        }
        if (systemMenuHandler(hWnd, message, wParam, lParam, result)) {
            return true;
        }
        return false; // Not handled
    }
    static constexpr const auto kMessageTag = WPARAM(0x97CCEA99);
    static inline constexpr bool isTaggedMessage(WPARAM wParam) {
        return (wParam == kMessageTag);
    }
    static constexpr const struct {
        const WPARAM wParam = 0xF1C9ADD4;
        const LPARAM lParam = 0xAFB6F4C6;
    } kMessageTag;
    static inline quint64 getKeyState() {
        quint64 result = 0;
@@ -688,7 +958,7 @@
                // wParam is always ignored in mouse leave messages, but here we
                // give them a special tag to be able to distinguish which messages
                // are sent by ourselves.
                return kMessageTag;
                return kMessageTag.wParam;
            }
            const quint64 keyState = getKeyState();
            if ((myMsg >= WM_NCXBUTTONDOWN) && (myMsg <= WM_NCXBUTTONDBLCLK)) {
@@ -766,6 +1036,7 @@
                SEND_MESSAGE(hWnd, WM_MOUSELEAVE, wParamNew, lParamNew);
                break;
            default:
                // unreachable
                break;
        }
@@ -788,7 +1059,7 @@
                                               LPARAM lParam, LRESULT *result) {
        switch (message) {
            case WM_MOUSELEAVE: {
                if (!isTaggedMessage(wParam)) {
                if (wParam == kMessageTag.wParam) {
                    // Qt will call TrackMouseEvent() to get the WM_MOUSELEAVE message when it
                    // receives WM_MOUSEMOVE messages, and since we are converting every
                    // WM_NCMOUSEMOVE message to WM_MOUSEMOVE message and send it back to the window
@@ -800,9 +1071,9 @@
                    DWORD dwScreenPos = ::GetMessagePos();
                    POINT screenPoint{GET_X_LPARAM(dwScreenPos), GET_Y_LPARAM(dwScreenPos)};
                    ::ScreenToClient(hWnd, &screenPoint);
                    QPoint qtScenePos =
                        QHighDpi::fromNativeLocalPosition(QPoint{screenPoint.x, screenPoint.y}, m_windowHandle);
                    auto dummy = CoreWindowAgent::Unknown;
                    QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(point2qpoint(screenPoint),
                                                                          m_windowHandle);
                    auto dummy = WindowAgentBase::Unknown;
                    if (isInSystemButtons(qtScenePos, &dummy)) {
                        // We must record whether the last WM_MOUSELEAVE was filtered, because if
                        // Qt does not receive this message it will not call TrackMouseEvent()
@@ -1033,14 +1304,14 @@
                auto clientWidth = RECT_WIDTH(clientRect);
                auto clientHeight = RECT_HEIGHT(clientRect);
                QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(
                    QPoint(nativeLocalPos.x, nativeLocalPos.y), m_windowHandle);
                QPoint qtScenePos =
                    QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), m_windowHandle);
                bool isFixedSize = m_delegate->isHostSizeFixed(m_host);
                bool isTitleBar = isInTitleBarDraggableArea(qtScenePos);
                bool dontOverrideCursor = false; // ### TODO
                CoreWindowAgent::SystemButton sysButtonType = CoreWindowAgent::Unknown;
                WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown;
                if (!isFixedSize && isInSystemButtons(qtScenePos, &sysButtonType)) {
                    // Firstly, we set the hit test result to a default value to be able to detect
                    // whether we have changed it or not afterwards.
@@ -1050,22 +1321,31 @@
                    // this is also the normal behavior of a native Win32 window (but only when the
                    // window is not maximized/fullscreen/minimized, of course).
                    if (isWindowNoState(hWnd)) {
                        static constexpr const int kBorderSize = 2;
                        static constexpr const auto kBorderSize = quint8{2};
                        bool isTop = (nativeLocalPos.y <= kBorderSize);
                        bool isLeft = nativeLocalPos.x <= kBorderSize;
                        bool isRight = (nativeLocalPos.x >= (clientWidth - kBorderSize));
                        if (isTop || isRight) {
                        if (isTop || isLeft || isRight) {
                            if (dontOverrideCursor) {
                                // The user doesn't want the window to be resized, so we tell
                                // Windows we are in the client area so that the controls beneath
                                // the mouse cursor can still be hovered or clicked.
                                *result = (isTitleBar ? HTCAPTION : HTCLIENT);
                            } else {
                                if (isTop && isRight) {
                                    *result = HTTOPRIGHT;
                                } else if (isTop) {
                                    *result = HTTOP;
                                if (isTop) {
                                    if (isLeft) {
                                        *result = HTTOPLEFT;
                                    } else if (isRight) {
                                        *result = HTTOPRIGHT;
                                    } else {
                                        *result = HTTOP;
                                    }
                                } else {
                                    *result = HTRIGHT;
                                    if (isLeft) {
                                        *result = HTLEFT;
                                    } else {
                                        *result = HTRIGHT;
                                    }
                                }
                            }
                        }
@@ -1075,23 +1355,24 @@
                        // exact role of our button. The Snap Layout feature introduced in Windows
                        // 11 won't work without this.
                        switch (sysButtonType) {
                            case CoreWindowAgent::WindowIcon:
                            case WindowAgentBase::WindowIcon:
                                *result = HTSYSMENU;
                                break;
                            case CoreWindowAgent::Help:
                            case WindowAgentBase::Help:
                                *result = HTHELP;
                                break;
                            case CoreWindowAgent::Minimize:
                            case WindowAgentBase::Minimize:
                                *result = HTREDUCE;
                                break;
                            case CoreWindowAgent::Maximize:
                            case WindowAgentBase::Maximize:
                                *result = HTZOOM;
                                break;
                            case CoreWindowAgent::Close:
                            case WindowAgentBase::Close:
                                *result = HTCLOSE;
                                break;
                            default:
                                break; // unreachable
                                // unreachable
                                break;
                        }
                    }
                    if (*result == HTNOWHERE) {
@@ -1165,9 +1446,9 @@
                        const bool isBottom = (nativeLocalPos.y >= (clientHeight - frameSize));
                        // Make the border a little wider to let the user easy to resize on corners.
                        const auto scaleFactor = ((isTop || isBottom) ? qreal(2) : qreal(1));
                        const int scaledFrameSizeX = std::round(qreal(frameSize) * scaleFactor);
                        const bool isLeft = (nativeLocalPos.x < scaledFrameSizeX);
                        const bool isRight = (nativeLocalPos.x >= (clientWidth - scaledFrameSizeX));
                        const int scaledFrameSize = std::round(qreal(frameSize) * scaleFactor);
                        const bool isLeft = (nativeLocalPos.x < scaledFrameSize);
                        const bool isRight = (nativeLocalPos.x >= (clientWidth - scaledFrameSize));
                        if (dontOverrideCursor && (isTop || isBottom || isLeft || isRight)) {
                            // Return HTCLIENT instead of HTBORDER here, because the mouse is
                            // inside the window now, return HTCLIENT to let the controls
@@ -1285,6 +1566,7 @@
    bool Win32WindowContext::nonClientCalcSizeHandler(HWND hWnd, UINT message, WPARAM wParam,
                                                      LPARAM lParam, LRESULT *result) {
        Q_UNUSED(message)
        Q_UNUSED(this)
        // Windows是根据这个消息的返回值来设置窗口的客户区(窗口中真正显示的内容)
        // 和非客户区(标题栏、窗口边框、菜单栏和状态栏等Windows系统自行提供的部分
@@ -1511,6 +1793,89 @@
    bool Win32WindowContext::systemMenuHandler(HWND hWnd, UINT message, WPARAM wParam,
                                               LPARAM lParam, LRESULT *result) {
        const auto getNativePosFromMouse = [lParam]() -> POINT {
            return {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
        };
        const auto getNativeGlobalPosFromKeyboard = [hWnd]() -> POINT {
            const bool maxOrFull = IsMaximized(hWnd) || isFullScreen(hWnd);
            const quint32 frameSize = getResizeBorderThickness(hWnd);
            const quint32 horizontalOffset = ((maxOrFull || !isWin10OrGreater()) ? 0 : frameSize);
            const auto verticalOffset = [hWnd, maxOrFull, frameSize]() -> quint32 {
                const quint32 titleBarHeight = getTitleBarHeight(hWnd);
                if (!isWin10OrGreater()) {
                    return titleBarHeight;
                }
                if (isWin11OrGreater()) {
                    if (maxOrFull) {
                        return (titleBarHeight + frameSize);
                    }
                    return titleBarHeight;
                }
                if (maxOrFull) {
                    return titleBarHeight;
                }
                return titleBarHeight - frameSize;
            }();
            RECT windowPos{};
            ::GetWindowRect(hWnd, &windowPos);
            return {static_cast<LONG>(windowPos.left + horizontalOffset),
                    static_cast<LONG>(windowPos.top + verticalOffset)};
        };
        bool shouldShowSystemMenu = false;
        bool broughtByKeyboard = false;
        POINT nativeGlobalPos{};
        switch (message) {
            case WM_RBUTTONUP: {
                const POINT nativeLocalPos = getNativePosFromMouse();
                const QPoint qtScenePos =
                    QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), m_windowHandle);
                if (isInTitleBarDraggableArea(qtScenePos)) {
                    shouldShowSystemMenu = true;
                    nativeGlobalPos = nativeLocalPos;
                    ::ClientToScreen(hWnd, &nativeGlobalPos);
                }
                break;
            }
            case WM_NCRBUTTONUP: {
                if (wParam == HTCAPTION) {
                    shouldShowSystemMenu = true;
                    nativeGlobalPos = getNativePosFromMouse();
                }
                break;
            }
            case WM_SYSCOMMAND: {
                const WPARAM filteredWParam = (wParam & 0xFFF0);
                if ((filteredWParam == SC_KEYMENU) && (lParam == VK_SPACE)) {
                    shouldShowSystemMenu = true;
                    broughtByKeyboard = true;
                    nativeGlobalPos = getNativeGlobalPosFromKeyboard();
                }
                break;
            }
            case WM_KEYDOWN:
            case WM_SYSKEYDOWN: {
                const bool altPressed = ((wParam == VK_MENU) || (::GetKeyState(VK_MENU) < 0));
                const bool spacePressed = ((wParam == VK_SPACE) || (::GetKeyState(VK_SPACE) < 0));
                if (altPressed && spacePressed) {
                    shouldShowSystemMenu = true;
                    broughtByKeyboard = true;
                    nativeGlobalPos = getNativeGlobalPosFromKeyboard();
                }
                break;
            }
            default:
                break;
        }
        if (shouldShowSystemMenu) {
            showSystemMenu2(hWnd, nativeGlobalPos, broughtByKeyboard,
                            m_delegate->isHostSizeFixed(m_host));
            // QPA's internal code will handle system menu events separately, and its
            // behavior is not what we would want to see because it doesn't know our
            // window doesn't have any window frame now, so return early here to avoid
            // entering Qt's own handling logic.
            *result = FALSE;
            return true;
        }
        return false;
    }