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/contexts/win32windowcontext.cpp | 83 +++++++++++++++++++++++++++-------------- 1 files changed, 54 insertions(+), 29 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; } -- Gitblit v1.9.1