Sine Striker
2023-12-06 a0bede6ff6a700a6eea9702c49d378f07de22f63
src/core/contexts/win32windowcontext.cpp
@@ -1,20 +1,31 @@
#include "win32windowcontext_p.h"
#include "qwkcoreglobal_p.h"
#include <optional>
#include <QtCore/QHash>
#include <QtCore/QAbstractNativeEventFilter>
#include <QtCore/QCoreApplication>
#include <QtCore/QOperatingSystemVersion>
#include <QtCore/QScopeGuard>
#include <QtCore/QTimer>
#include <QtGui/QGuiApplication>
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtGui/private/qhighdpiscaling_p.h>
#include "qwkcoreglobal_p.h"
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#  include <QtGui/private/qguiapplication_p.h>
#endif
#include <QtGui/qpa/qplatformwindow.h>
#if QT_VERSION < QT_VERSION_CHECK(6, 2, 0)
#  include <QtGui/qpa/qplatformnativeinterface.h>
#else
#  include <QtGui/qpa/qplatformwindow_p.h>
#endif
#include <shellscalingapi.h>
#include <dwmapi.h>
Q_DECLARE_METATYPE(QMargins)
namespace QWK {
@@ -25,6 +36,12 @@
    Q_GLOBAL_STATIC(WndProcHash, g_wndProcHash)
    static WNDPROC g_qtWindowProc = nullptr; // Original Qt window proc function
    static struct QWK_Hook {
        QWK_Hook() {
            qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
        }
    } g_hook{};
    struct DynamicApis {
        decltype(&::DwmFlush) pDwmFlush = nullptr;
@@ -46,7 +63,8 @@
            QSystemLibrary dwmapi(QStringLiteral("dwmapi.dll"));
            pDwmFlush = reinterpret_cast<decltype(pDwmFlush)>(dwmapi.resolve("DwmFlush"));
            pDwmIsCompositionEnabled = reinterpret_cast<decltype(pDwmIsCompositionEnabled)>(dwmapi.resolve("DwmIsCompositionEnabled"));
            pDwmIsCompositionEnabled = reinterpret_cast<decltype(pDwmIsCompositionEnabled)>(
                dwmapi.resolve("DwmIsCompositionEnabled"));
        }
        ~DynamicApis() = default;
@@ -175,13 +193,15 @@
        if (!hwnd) {
            return;
        }
        ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
        ::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) {
        Q_ASSERT(hwnd);
        if (!hwnd) {
            return USER_DEFAULT_SCREEN_DPI;
            return 0;
        }
        const DynamicApis &apis = DynamicApis::instance();
        if (apis.pGetDpiForWindow) {         // Win10
@@ -214,6 +234,55 @@
        } else {
            return ::GetSystemMetrics(SM_CXSIZEFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER);
        }
    }
    static inline quint32 getTitleBarHeight(HWND hwnd) {
        Q_ASSERT(hwnd);
        if (!hwnd) {
            return 0;
        }
        const auto captionHeight = [hwnd]() -> int {
            const DynamicApis &apis = DynamicApis::instance();
            if (apis.pGetSystemMetricsForDpi) {
                const quint32 dpi = getDpiForWindow(hwnd);
                return apis.pGetSystemMetricsForDpi(SM_CYCAPTION, dpi);
            } else {
                return ::GetSystemMetrics(SM_CYCAPTION);
            }
        }();
        return captionHeight + getResizeBorderThickness(hwnd);
    }
    static inline void updateInternalWindowFrameMargins(HWND hwnd, QWindow *window) {
        Q_ASSERT(hwnd);
        Q_ASSERT(window);
        if (!hwnd || !window) {
            return;
        }
        const auto margins = [hwnd]() -> QMargins {
            const int titleBarHeight = getTitleBarHeight(hwnd);
            if (isWin10OrGreater()) {
                return {0, -titleBarHeight, 0, 0};
            } else {
                const int frameSize = getResizeBorderThickness(hwnd);
                return {-frameSize, -titleBarHeight, -frameSize, -frameSize};
            }
        }();
        const QVariant marginsVar = QVariant::fromValue(margins);
        window->setProperty("_q_windowsCustomMargins", marginsVar);
#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"),
                                      marginsVar);
            }
        }
#else
        if (const auto platformWindow =
                dynamic_cast<QNativeInterface::Private::QWindowsWindow *>(window->handle())) {
            platformWindow->setCustomMargins(margins);
        }
#endif
    }
    static inline std::optional<MONITORINFOEXW> getMonitorForWindow(HWND hwnd) {
@@ -342,11 +411,14 @@
    public:
        bool nativeEventFilter(const QByteArray &eventType, void *message,
                               QT_NATIVE_EVENT_RESULT_TYPE *result) override {
            Q_UNUSED(eventType);
            // It has been observed that the pointer that Qt gives us is sometimes null on some
            // machines. We need to guard against it in such scenarios.
            if (!result) {
                return false;
            }
            if (lastMessageHandled) {
                *result = static_cast<QT_NATIVE_EVENT_RESULT_TYPE>(lastMessageResult);
                return true;
@@ -447,13 +519,16 @@
        // TODO: Determine whether to show system menu
        // ...
        // Since Qt does the necessary processing of the message afterward, we still need to
        // Since Qt does the necessary processing of the WM_NCCALCSIZE afterward, we still need to
        // continue dispatching it.
        if (message != WM_NCCALCSIZE && handled) {
            return result;
        }
        return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
    }
    Win32WindowContext::Win32WindowContext(QWindow *window, WindowItemDelegate *delegate)
        : AbstractWindowContext(window, delegate) {
    Win32WindowContext::Win32WindowContext(QObject *host, WindowItemDelegate *delegate)
        : AbstractWindowContext(host, delegate) {
    }
    Win32WindowContext::~Win32WindowContext() {
@@ -469,10 +544,16 @@
    }
    bool Win32WindowContext::setup() {
        auto winId = m_windowHandle->winId();
        if (!m_windowHandle) {
            return false;
        }
        // Install window hook
        auto winId = m_windowHandle->winId();
        auto hWnd = reinterpret_cast<HWND>(winId);
        // Inform Qt we want and have set custom margins
        updateInternalWindowFrameMargins(hWnd, m_windowHandle);
        // Store original window proc
        if (!g_qtWindowProc) {
@@ -492,6 +573,8 @@
        // Save window handle mapping
        g_wndProcHash->insert(hWnd, this);
        QTimer::singleShot(0, m_windowHandle, [hWnd]() { triggerFrameChange(hWnd); });
        return true;
    }
@@ -685,9 +768,10 @@
                    // actually lost the hover state. So we filter out these superfluous mouse leave
                    // events here to avoid this issue.
                    DWORD dwScreenPos = ::GetMessagePos();
                    POINT screenPoint{GET_X_LPARAM(dwScreenPos), GET_Y_LPARAM(dwScreenPos)};
                    ::ScreenToClient(hWnd, &screenPoint);
                    QPoint qtScenePos =
                        fromNativeLocalPosition(m_windowHandle, QPoint(GET_X_LPARAM(dwScreenPos),
                                                                       GET_Y_LPARAM(dwScreenPos)));
                        fromNativeLocalPosition(m_windowHandle, {screenPoint.x, screenPoint.y});
                    auto dummy = CoreWindowAgent::Unknown;
                    if (isInSystemButtons(qtScenePos, &dummy)) {
                        // We must record whether the last WM_MOUSELEAVE was filtered, because if
@@ -1137,9 +1221,9 @@
                QPoint qtScenePos = fromNativeLocalPosition(
                    m_windowHandle, QPoint(nativeLocalPos.x, nativeLocalPos.y));
                bool isFixedSize = /*isWindowFixedSize()*/ false; // ### FIXME
                bool isFixedSize = m_delegate->isHostSizeFixed(m_host);
                bool isTitleBar = isInTitleBarDraggableArea(qtScenePos);
                bool dontOverrideCursor = false;                  // ### TODO
                bool dontOverrideCursor = false; // ### TODO
                CoreWindowAgent::SystemButton sysButtonType = CoreWindowAgent::Unknown;
                if (!isFixedSize && isInSystemButtons(qtScenePos, &sysButtonType)) {
@@ -1254,6 +1338,7 @@
                        return true;
                    }
                    *result = HTCLIENT;
                    return true;
                } else {
                    if (full) {
                        *result = HTCLIENT;
@@ -1315,8 +1400,8 @@
                        return true;
                    }
                    *result = HTCLIENT;
                    return true;
                }
                return true;
            }
            default:
                break;
@@ -1384,4 +1469,4 @@
        return false;
    }
}
}