Sine Striker
2023-12-26 c8a5a82994e513acb00ba0e1f0b882ccbb18a6db
src/core/contexts/win32windowcontext.cpp
@@ -5,10 +5,10 @@
#include <QtCore/QHash>
#include <QtCore/QScopeGuard>
#include <QtCore/QTimer>
#include <QtCore/QDateTime>
#include <QtGui/QGuiApplication>
#include <QtGui/QPainter>
#include <QtGui/QPalette>
#include <QtGui/QStyleHints>
#include <QtGui/private/qhighdpiscaling_p.h>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@@ -25,10 +25,6 @@
#include "qwkglobal_p.h"
#include "qwkwindowsextra_p.h"
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Q_DECLARE_METATYPE(QMargins)
#endif
namespace QWK {
@@ -68,55 +64,6 @@
        ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
                       SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER |
                           SWP_FRAMECHANGED);
    }
    static 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);
        }
    }
    static inline quint32 getSystemMetricsForDpi(int index, quint32 dpi) {
        const DynamicApis &apis = DynamicApis::instance();
        if (apis.pGetSystemMetricsForDpi) {
            return ::GetSystemMetricsForDpi(index, dpi);
        }
        return ::GetSystemMetrics(index);
    }
    static inline quint32 getWindowFrameBorderThickness(HWND hwnd) {
        const DynamicApis &apis = DynamicApis::instance();
        if (UINT result = 0; SUCCEEDED(apis.pDwmGetWindowAttribute(
                hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &result, sizeof(result)))) {
            return result;
        }
        return getSystemMetricsForDpi(SM_CXBORDER, getDpiForWindow(hwnd));
    }
    static inline quint32 getResizeBorderThickness(HWND hwnd) {
        const quint32 dpi = getDpiForWindow(hwnd);
        return getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) +
               getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
    }
    static inline quint32 getTitleBarHeight(HWND hwnd) {
        const quint32 dpi = getDpiForWindow(hwnd);
        return getSystemMetricsForDpi(SM_CYCAPTION, dpi) +
               getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) +
               getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
    }
    static void setInternalWindowFrameMargins(QWindow *window, const QMargins &margins) {
@@ -658,6 +605,7 @@
            }
            case DrawWindows10BorderHook: {
#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER)
                if (!windowId)
                    return;
@@ -668,7 +616,7 @@
                const auto hwnd = reinterpret_cast<HWND>(windowId);
                QPen pen;
                pen.setWidth(getWindowFrameBorderThickness(hwnd) * 2);
                pen.setWidth(int(getWindowFrameBorderThickness(hwnd)) * 2);
                const bool dark = isDarkThemeActive() && isDarkWindowFrameEnabled(hwnd);
                if (m_delegate->isWindowActive(m_host)) {
@@ -688,7 +636,7 @@
                }
                painter.save();
                // We needs anti-aliasing to give us better result.
                // We need antialiasing to give us better result.
                painter.setRenderHint(QPainter::Antialiasing);
                painter.setPen(pen);
@@ -697,6 +645,33 @@
                    QPoint{m_windowHandle->width(), 0}
                });
                painter.restore();
#endif
                return;
            }
            case DrawWindows10BorderHook2: {
#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER)
                if (!m_windowHandle)
                    return;
                // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L1025
                // https://docs.microsoft.com/en-us/windows/win32/dwm/customframe#extending-the-client-frame
                // Draw a black rectangle to make Windows native top border show
                auto hWnd = reinterpret_cast<HWND>(windowId);
                HDC hdc = ::GetDC(hWnd);
                RECT windowRect{};
                ::GetClientRect(hWnd, &windowRect);
                RECT rcTopBorder = {
                    0,
                    0,
                    RECT_WIDTH(windowRect),
                    int(getWindowFrameBorderThickness(hWnd)),
                };
                ::FillRect(hdc, &rcTopBorder,
                           reinterpret_cast<HBRUSH>(::GetStockObject(BLACK_BRUSH)));
                ::ReleaseDC(hWnd, hdc);
#endif
                return;
            }
@@ -706,13 +681,29 @@
        AbstractWindowContext::virtual_hook(id, data);
    }
    bool Win32WindowContext::needBorderPainter() const {
        Q_UNUSED(this)
        return isSystemBorderEnabled() && !isWin11OrGreater();
    }
    QVariant Win32WindowContext::windowAttribute(const QString &key) const {
        if (key == QStringLiteral("title-bar-rect")) {
            if (!m_windowHandle)
                return {};
    int Win32WindowContext::borderThickness() const {
        return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId));
            auto hwnd = reinterpret_cast<HWND>(windowId);
            RECT frame{};
            ::AdjustWindowRectExForDpi(&frame, ::GetWindowLongPtrW(hwnd, GWL_STYLE), FALSE, 0,
                                       getDpiForWindow(hwnd));
            return QVariant::fromValue(rect2qrect(frame));
        }
        if (key == QStringLiteral("win10-border-needed")) {
            return isSystemBorderEnabled() && !isWin11OrGreater();
        }
        if (key == QStringLiteral("border-thickness")) {
            return m_windowHandle
                       ? int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)))
                       : 0;
        }
        return AbstractWindowContext::windowAttribute(key);
    }
    void Win32WindowContext::winIdChanged() {
@@ -731,8 +722,8 @@
        auto hWnd = reinterpret_cast<HWND>(winId);
        if (!isSystemBorderEnabled()) {
            static constexpr const MARGINS margins = {1, 1, 1, 1};
            DynamicApis::instance().pDwmExtendFrameIntoClientArea(hWnd, &margins);
            static auto margins = QVariant::fromValue(QMargins(1, 1, 1, 1));
            setWindowAttribute(QStringLiteral("extra-margins"), margins);
        }
        {
@@ -797,7 +788,7 @@
            msg.wParam = wParam;
            msg.lParam = lParam;
            QT_NATIVE_EVENT_RESULT_TYPE res = 0;
            if (dispatch(QByteArrayLiteral("windows_generic_MSG"), &msg, &res)) {
            if (nativeDispatch(QByteArrayLiteral("windows_generic_MSG"), &msg, &res)) {
                *result = LRESULT(res);
                return true;
            }
@@ -807,11 +798,46 @@
    bool Win32WindowContext::windowAttributeChanged(const QString &key, const QVariant &attribute,
                                                    const QVariant &oldAttribute) {
        Q_UNUSED(oldAttribute)
        const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
        const DynamicApis &apis = DynamicApis::instance();
        static constexpr const MARGINS extendMargins = {-1, -1, -1, -1};
        static const auto defaultMargins =
            isSystemBorderEnabled() ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1};
        static constexpr const MARGINS extendedMargins = {-1, -1, -1, -1};
        const auto &restoreMargins = [this, &apis, hwnd]() {
            auto margins = qmargins2margins(
                m_windowAttributes.value(QStringLiteral("extra-margins")).value<QMargins>());
            apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
        };
        if (key == QStringLiteral("extra-margins")) {
            auto margins = qmargins2margins(attribute.value<QMargins>());
            DynamicApis::instance().pDwmExtendFrameIntoClientArea(hwnd, &margins);
            return true;
        }
        if (key == QStringLiteral("dark-mode")) {
            if (!isWin101809OrGreater()) {
                return false;
            }
            BOOL enable = attribute.toBool();
            if (isWin101903OrGreater()) {
                apis.pSetPreferredAppMode(enable ? PAM_AUTO : PAM_DEFAULT);
            } else {
                apis.pAllowDarkModeForApp(enable);
            }
            for (const auto attr : {
                     _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
                     _DWMWA_USE_IMMERSIVE_DARK_MODE,
                 }) {
                apis.pDwmSetWindowAttribute(hwnd, attr, &enable, sizeof(enable));
            }
            apis.pFlushMenuThemes();
            return true;
        }
        // For Win11 or later
        if (key == QStringLiteral("mica")) {
            if (!isWin11OrGreater()) {
                return false;
@@ -819,7 +845,7 @@
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins);
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
                if (isWin1122H2OrGreater()) {
                    // Use official DWM API to enable Mica, available since Windows 11 22H2
                    // (10.0.22621).
@@ -841,17 +867,19 @@
                    const BOOL enable = FALSE;
                    apis.pDwmSetWindowAttribute(hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
                }
                apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins);
                restoreMargins();
            }
            return true;
        } else if (key == QStringLiteral("mica-alt")) {
        }
        if (key == QStringLiteral("mica-alt")) {
            if (!isWin1122H2OrGreater()) {
                return false;
            }
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins);
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
                // Use official DWM API to enable Mica Alt, available since Windows 11 22H2
                // (10.0.22621).
                const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TABBEDWINDOW;
@@ -861,17 +889,19 @@
                const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO;
                apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
                                            sizeof(backdropType));
                apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins);
                restoreMargins();
            }
            return true;
        } else if (key == QStringLiteral("acrylic-material")) {
        }
        if (key == QStringLiteral("acrylic-material")) {
            if (!isWin11OrGreater()) {
                return false;
            }
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins);
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
                const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TRANSIENTWINDOW;
                apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
@@ -906,16 +936,18 @@
                //     wcad.cbData = sizeof(policy);
                //     apis.pSetWindowCompositionAttribute(hwnd, &wcad);
                apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins);
                restoreMargins();
            }
            return true;
        } else if (key == QStringLiteral("dwm-blur")) {
        }
        if (key == QStringLiteral("dwm-blur")) {
            // TODO: Optimize
            // Currently not available!!!
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins);
                apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
                if (isWin8OrGreater()) {
                    ACCENT_POLICY policy{};
                    policy.dwAccentState = ACCENT_ENABLE_BLURBEHIND;
@@ -947,31 +979,8 @@
                    bb.dwFlags = DWM_BB_ENABLE;
                    apis.pDwmEnableBlurBehindWindow(hwnd, &bb);
                }
                apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins);
                restoreMargins();
            }
            return true;
        } else if (key == QStringLiteral("dark-mode")) {
            if (!isWin101809OrGreater()) {
                return false;
            }
            BOOL enable = attribute.toBool();
            if (isWin101903OrGreater()) {
                apis.pSetPreferredAppMode(enable ? PAM_AUTO : PAM_DEFAULT);
            } else {
                apis.pAllowDarkModeForApp(enable);
            }
            for (const auto attr : {
                     _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
                     _DWMWA_USE_IMMERSIVE_DARK_MODE,
                 }) {
                apis.pDwmSetWindowAttribute(hwnd, attr, &enable, sizeof(enable));
            }
            apis.pFlushMenuThemes();
            return true;
        }
        return false;
@@ -1309,12 +1318,12 @@
                                                 LPARAM lParam, LRESULT *result) {
        switch (message) {
            case WM_SHOWWINDOW: {
                if (!centered) {
                if (!initialCentered) {
                    // If wParam is TRUE, the window is being shown.
                    // If lParam is zero, the message was sent because of a call to the ShowWindow
                    // function.
                    if (wParam && !lParam) {
                        centered = true;
                        initialCentered = true;
                        moveWindowToDesktopCenter(hWnd);
                    }
                }