From 2d08f989b16dad059d42c94e3a2cdccdbd3c379e Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: 周一, 04 12月 2023 03:38:52 +0800 Subject: [PATCH] Add more comments --- src/core/corewindowagent.cpp | 10 +- src/core/corewindowagent_p.h | 2 src/core/contexts/win32windowcontext_p.h | 11 ++- src/widgets/widgetwindowagent.cpp | 14 ++-- src/core/contexts/win32windowcontext.cpp | 83 ++++++++++++++++++--------- src/quick/quickwindowagent.cpp | 14 ++-- 6 files changed, 82 insertions(+), 52 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index a2f885b..a901cae 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -17,24 +17,6 @@ static WNDPROC g_qtWindowProc = nullptr; // Original Qt window proc function - static bool g_lastMessageHandled = false; - - static LRESULT g_lastMessageResult = false; - - class WindowsNativeEventFilter : public QAbstractNativeEventFilter { - public: - bool nativeEventFilter(const QByteArray &eventType, void *message, - QT_NATIVE_EVENT_RESULT_TYPE *result) override { - if (g_lastMessageHandled) { - *result = static_cast<QT_NATIVE_EVENT_RESULT_TYPE>(g_lastMessageResult); - return true; - } - return false; - } - }; - - static WindowsNativeEventFilter *g_nativeFilter = nullptr; - static inline QPoint fromNativeLocalPosition(const QWindow *window, const QPoint &point) { #if 1 return QHighDpi::fromNativeLocalPosition(point, window); @@ -105,6 +87,44 @@ return true; } + // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1556 + // In QWindowsContext::windowsProc(), the messages will be passed to all global native event + // filters, but because we have already filtered the messages in the hook WndProc function for + // convenience, Qt does not know we may have already process the messages and thus will call + // 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 { + public: + bool nativeEventFilter(const QByteArray &eventType, void *message, + QT_NATIVE_EVENT_RESULT_TYPE *result) override { + if (lastMessageHandled) { + *result = static_cast<QT_NATIVE_EVENT_RESULT_TYPE>(lastMessageResult); + return true; + } + return false; + } + + static bool lastMessageHandled; + static LRESULT lastMessageResult; + static WindowsNativeEventFilter *instance; + + static inline void install() { + instance = new WindowsNativeEventFilter(); + qApp->installNativeEventFilter(instance); + } + + static inline void uninstall() { + qApp->removeNativeEventFilter(instance); + delete instance; + instance = nullptr; + } + }; + + bool WindowsNativeEventFilter::lastMessageHandled = false; + LRESULT WindowsNativeEventFilter::lastMessageResult = 0; + WindowsNativeEventFilter *WindowsNativeEventFilter::instance = nullptr; + // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1025 // We can see from the source code that Qt will filter out some messages first and then send the // unfiltered messages to the event dispatcher. To activate the Snap Layout feature on Windows @@ -171,7 +191,9 @@ } // Try hooked procedure and save result - g_lastMessageHandled = ctx->windowProc(hWnd, message, wParam, lParam, &g_lastMessageResult); + auto &handled = WindowsNativeEventFilter::lastMessageHandled; + auto &result = WindowsNativeEventFilter::lastMessageResult; + handled = ctx->windowProc(hWnd, message, wParam, lParam, &result); // TODO: Determine whether to show system menu // ... @@ -190,11 +212,9 @@ if (auto hWnd = reinterpret_cast<HWND>(windowId); hWnd) { g_wndProcHash->remove(hWnd); - // Remove event filter if the last window is destroyed + // Remove event filter if the all windows has been destroyed if (g_wndProcHash->empty()) { - qApp->removeNativeEventFilter(g_nativeFilter); - delete g_nativeFilter; - g_nativeFilter = nullptr; + WindowsNativeEventFilter::uninstall(); } } } @@ -214,9 +234,8 @@ ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWKHookedWndProc)); // Install global native event filter - if (!g_nativeFilter) { - g_nativeFilter = new WindowsNativeEventFilter(); - qApp->installNativeEventFilter(g_nativeFilter); + if (!WindowsNativeEventFilter::instance) { + WindowsNativeEventFilter::install(); } // Cache window ID @@ -411,7 +430,7 @@ } bool Win32WindowContext::snapLayoutHandler(HWND hWnd, UINT message, WPARAM wParam, - LPARAM lParam, LRESULT *result) { + LPARAM lParam, LRESULT *result) { switch (message) { case WM_MOUSELEAVE: { if (!isTaggedMessage(wParam)) { @@ -429,6 +448,9 @@ GET_Y_LPARAM(dwScreenPos))); auto dummy = CoreWindowAgent::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() + // again, resulting in the client area not responding to any mouse event. mouseLeaveBlocked = true; *result = FALSE; return true; @@ -439,7 +461,10 @@ } case WM_MOUSEMOVE: { - if ((lastHitTestResult != WindowPart::ChromeButton) && mouseLeaveBlocked) { + // At appropriate time, we will call TrackMouseEvent() for Qt. Simultaneously, + // we unset `mouseLeaveBlocked` mark and pretend as if Qt has received + // WM_MOUSELEAVE. + if (lastHitTestResult != WindowPart::ChromeButton && mouseLeaveBlocked) { mouseLeaveBlocked = false; std::ignore = requestForMouseLeaveMessage(hWnd, false); } @@ -479,7 +504,7 @@ // comes, so we reset it when we receive a WM_NCMOUSEMOVE. // If the mouse is entering the client area, there must be a WM_NCHITTEST - // setting it to `Client` before the WM_NCMOUSELEAVE comes; If the mouse is + // setting it to `Client` before the WM_NCMOUSELEAVE comes; if the mouse is // leaving the window, current window part remains as `Outside`. lastHitTestResult = WindowPart::Outside; } diff --git a/src/core/contexts/win32windowcontext_p.h b/src/core/contexts/win32windowcontext_p.h index 52f4433..b99314e 100644 --- a/src/core/contexts/win32windowcontext_p.h +++ b/src/core/contexts/win32windowcontext_p.h @@ -25,17 +25,22 @@ bool setup() override; bool windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); + + // In order to perfectly apply Windows 11 Snap Layout into the Qt window, we need to + // intercept and simulate most of the mouse events, so that the processing logic + // is quite complex. Simultaneously, in order to make the handling code of other + // Windows messages clearer, we have separated them into this function. bool snapLayoutHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); protected: WId windowId = 0; - // Store the last hit test result, it's helpful to handle WM_MOUSEMOVE and WM_NCMOUSELEAVE. + // The last hit test result, helpful to handle WM_MOUSEMOVE and WM_NCMOUSELEAVE. WindowPart lastHitTestResult = WindowPart::Outside; - // True if we blocked a WM_MOUSELEAVE when mouse moves on chrome button, false when a - // WM_MOUSELEAVE comes or we manually call TrackMouseEvent(). + // Whether the last mouse leave message is blocked, mainly for handling the unexpected + // WM_MOUSELEAVE. bool mouseLeaveBlocked = false; }; diff --git a/src/core/corewindowagent.cpp b/src/core/corewindowagent.cpp index abcd6fb..78ed335 100644 --- a/src/core/corewindowagent.cpp +++ b/src/core/corewindowagent.cpp @@ -13,7 +13,7 @@ namespace QWK { - CoreWindowAgentPrivate::CoreWindowAgentPrivate() : q_ptr(nullptr), eventHandler(nullptr) { + CoreWindowAgentPrivate::CoreWindowAgentPrivate() : q_ptr(nullptr), context(nullptr) { } CoreWindowAgentPrivate::~CoreWindowAgentPrivate() = default; @@ -38,7 +38,7 @@ delete handler; return false; } - eventHandler.reset(handler); + context.reset(handler); return true; } @@ -46,12 +46,12 @@ void CoreWindowAgent::showSystemMenu(const QPoint &pos) { Q_D(CoreWindowAgent); - d->eventHandler->showSystemMenu(pos); + d->context->showSystemMenu(pos); } void CoreWindowAgent::startSystemMove(const QPoint &pos) { Q_D(CoreWindowAgent); - auto win = d->eventHandler->window(); + auto win = d->context->window(); if (!win) { return; } @@ -62,7 +62,7 @@ void CoreWindowAgent::startSystemResize(Qt::Edges edges, const QPoint &pos) { Q_D(CoreWindowAgent); - auto win = d->eventHandler->window(); + auto win = d->context->window(); if (!win) { return; } diff --git a/src/core/corewindowagent_p.h b/src/core/corewindowagent_p.h index 013ccbe..5916d89 100644 --- a/src/core/corewindowagent_p.h +++ b/src/core/corewindowagent_p.h @@ -18,7 +18,7 @@ bool setup(QWindow *window, WindowItemDelegate *delegate); - std::unique_ptr<AbstractWindowContext> eventHandler; + std::unique_ptr<AbstractWindowContext> context; Q_DISABLE_COPY_MOVE(CoreWindowAgentPrivate) }; diff --git a/src/quick/quickwindowagent.cpp b/src/quick/quickwindowagent.cpp index 83a7e9b..aae49f7 100644 --- a/src/quick/quickwindowagent.cpp +++ b/src/quick/quickwindowagent.cpp @@ -43,27 +43,27 @@ bool QuickWindowAgent::isHitTestVisible(QQuickItem *item) const { Q_D(const QuickWindowAgent); - return d->eventHandler->isHitTestVisible(item); + return d->context->isHitTestVisible(item); } void QuickWindowAgent::setHitTestVisible(QQuickItem *item, bool visible) { Q_D(QuickWindowAgent); - d->eventHandler->setHitTestVisible(item, visible); + d->context->setHitTestVisible(item, visible); } void QuickWindowAgent::setHitTestVisible(const QRect &rect, bool visible) { Q_D(QuickWindowAgent); - d->eventHandler->setHitTestVisible(rect, visible); + d->context->setHitTestVisible(rect, visible); } QQuickItem *QuickWindowAgent::systemButton(SystemButton button) const { Q_D(const QuickWindowAgent); - return static_cast<QQuickItem *>(d->eventHandler->systemButton(button)); + return static_cast<QQuickItem *>(d->context->systemButton(button)); } void QuickWindowAgent::setSystemButton(SystemButton button, QQuickItem *item) { Q_D(QuickWindowAgent); - if (!d->eventHandler->setSystemButton(button, item)) { + if (!d->context->setSystemButton(button, item)) { return; } Q_EMIT systemButtonChanged(button, item); @@ -71,12 +71,12 @@ QQuickItem *QuickWindowAgent::titleBar() const { Q_D(const QuickWindowAgent); - return static_cast<QQuickItem *>(d->eventHandler->titleBar()); + return static_cast<QQuickItem *>(d->context->titleBar()); } void QuickWindowAgent::setTitleBar(QQuickItem *item) { Q_D(QuickWindowAgent); - if (!d->eventHandler->setTitleBar(item)) { + if (!d->context->setTitleBar(item)) { return; } Q_EMIT titleBarWidgetChanged(item); diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp index 1bf0b4e..252213e 100644 --- a/src/widgets/widgetwindowagent.cpp +++ b/src/widgets/widgetwindowagent.cpp @@ -42,27 +42,27 @@ bool WidgetWindowAgent::isHitTestVisible(QWidget *w) const { Q_D(const WidgetWindowAgent); - return d->eventHandler->isHitTestVisible(w); + return d->context->isHitTestVisible(w); } void WidgetWindowAgent::setHitTestVisible(QWidget *w, bool visible) { Q_D(WidgetWindowAgent); - d->eventHandler->setHitTestVisible(w, visible); + d->context->setHitTestVisible(w, visible); } void WidgetWindowAgent::setHitTestVisible(const QRect &rect, bool visible) { Q_D(WidgetWindowAgent); - d->eventHandler->setHitTestVisible(rect, visible); + d->context->setHitTestVisible(rect, visible); } QWidget *WidgetWindowAgent::systemButton(CoreWindowAgent::SystemButton button) const { Q_D(const WidgetWindowAgent); - return static_cast<QWidget *>(d->eventHandler->systemButton(button)); + return static_cast<QWidget *>(d->context->systemButton(button)); } void WidgetWindowAgent::setSystemButton(CoreWindowAgent::SystemButton button, QWidget *w) { Q_D(WidgetWindowAgent); - if (!d->eventHandler->setSystemButton(button, w)) { + if (!d->context->setSystemButton(button, w)) { return; } Q_EMIT systemButtonChanged(button, w); @@ -70,12 +70,12 @@ QWidget *WidgetWindowAgent::titleBar() const { Q_D(const WidgetWindowAgent); - return static_cast<QWidget *>(d->eventHandler->titleBar()); + return static_cast<QWidget *>(d->context->titleBar()); } void WidgetWindowAgent::setTitleBar(QWidget *w) { Q_D(WidgetWindowAgent); - if (!d->eventHandler->setTitleBar(w)) { + if (!d->context->setTitleBar(w)) { return; } Q_EMIT titleBarWidgetChanged(w); -- Gitblit v1.9.1