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