From 8b72eabae325c34d8eab1544203993015cc91741 Mon Sep 17 00:00:00 2001
From: Sine Striker <trueful@163.com>
Date: 周一, 18 12月 2023 00:54:51 +0800
Subject: [PATCH] Add win32 winIdChange workaround

---
 src/core/contexts/abstractwindowcontext.cpp |   30 ++++-----
 src/widgets/widgetwindowagent_win.cpp       |    1 
 src/core/contexts/cocoawindowcontext.mm     |    5 +
 src/core/contexts/abstractwindowcontext_p.h |    7 +
 src/core/contexts/cocoawindowcontext_p.h    |    2 
 src/core/windowagentbase.cpp                |   11 +--
 src/core/contexts/qtwindowcontext_p.h       |    2 
 src/core/contexts/win32windowcontext.cpp    |   77 ++++++++++++++++---------
 src/widgets/widgetwindowagent_mac.cpp       |    3 
 src/core/contexts/win32windowcontext_p.h    |    2 
 src/widgets/widgetwindowagent.cpp           |   27 ++++++++-
 src/core/contexts/qtwindowcontext.cpp       |    5 +
 src/core/windowagentbase_p.h                |    2 
 src/quick/quickwindowagent.cpp              |    4 -
 14 files changed, 110 insertions(+), 68 deletions(-)

diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp
index 7c40c89..d69ab75 100644
--- a/src/core/contexts/abstractwindowcontext.cpp
+++ b/src/core/contexts/abstractwindowcontext.cpp
@@ -11,27 +11,17 @@
 
     AbstractWindowContext::~AbstractWindowContext() = default;
 
-    bool AbstractWindowContext::setup(QObject *host, WindowItemDelegate *delegate) {
-        if (!host || !delegate) {
-            return false;
+    void AbstractWindowContext::setup(QObject *host, WindowItemDelegate *delegate) {
+        if (m_host || !host || !delegate) {
+            return;
         }
-
-        auto windowHandle = delegate->hostWindow(host);
-        if (!windowHandle) {
-            return false;
-        }
-
         m_host = host;
         m_delegate.reset(delegate);
-        m_windowHandle = windowHandle;
-
-        if (!setupHost()) {
-            m_host = nullptr;
-            m_delegate.reset();
-            m_windowHandle = nullptr;
-            return false;
+        m_windowHandle = m_delegate->hostWindow(m_host);
+        if (m_windowHandle) {
+            m_windowHandleGuard = m_windowHandle;
+            winIdChanged(nullptr, false);
         }
-        return true;
     }
 
     bool AbstractWindowContext::setHitTestVisible(const QObject *obj, bool visible) {
@@ -183,4 +173,10 @@
         virtual_hook(ShowSystemMenuHook, &const_cast<QPoint &>(pos));
     }
 
+    void AbstractWindowContext::notifyWinIdChange() {
+        auto oldWindow = m_windowHandle;
+        m_windowHandle = m_delegate->window(m_host);
+        winIdChanged(oldWindow, oldWindow && m_windowHandleGuard.isNull());
+    }
+
 }
\ No newline at end of file
diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h
index a86ddd8..29f6749 100644
--- a/src/core/contexts/abstractwindowcontext_p.h
+++ b/src/core/contexts/abstractwindowcontext_p.h
@@ -5,6 +5,7 @@
 #include <memory>
 
 #include <QtCore/QSet>
+#include <QtCore/QPointer>
 #include <QtGui/QRegion>
 #include <QtGui/QWindow>
 
@@ -21,7 +22,7 @@
         ~AbstractWindowContext() override;
 
     public:
-        bool setup(QObject *host, WindowItemDelegate *delegate);
+        void setup(QObject *host, WindowItemDelegate *delegate);
 
         inline QObject *host() const;
         inline QWindow *window() const;
@@ -56,14 +57,16 @@
         virtual void virtual_hook(int id, void *data);
 
         void showSystemMenu(const QPoint &pos);
+        void notifyWinIdChange();
 
     protected:
-        virtual bool setupHost() = 0;
+        virtual void winIdChanged(QWindow *oldWindow, bool destroyed) = 0;
 
     protected:
         QObject *m_host{};
         std::unique_ptr<WindowItemDelegate> m_delegate;
         QWindow *m_windowHandle{};
+        QPointer<QWindow> m_windowHandleGuard;
 
         QSet<const QObject *> m_hitTestVisibleItems;
 #ifdef Q_OS_MAC
diff --git a/src/core/contexts/cocoawindowcontext.mm b/src/core/contexts/cocoawindowcontext.mm
index 1845599..a12be53 100644
--- a/src/core/contexts/cocoawindowcontext.mm
+++ b/src/core/contexts/cocoawindowcontext.mm
@@ -259,7 +259,8 @@
 
     CocoaWindowEventFilter::~CocoaWindowEventFilter() = default;
 
-    bool CocoaWindowEventFilter::eventFilter(QObject *object, QEvent *event) {
+    bool CocoaWindowEventFilter::eventFilter(QObject *obj, QEvent *event) {
+        Q_UNUSED(obj)
         auto type = event->type();
         if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) {
             return false;
@@ -389,7 +390,7 @@
         AbstractWindowContext::virtual_hook(id, data);
     }
 
-    bool CocoaWindowContext::setupHost() {
+    bool CocoaWindowContext::winIdChanged(QWindow *oldWindow) {
         windowId = m_windowHandle->winId();
         ensureWindowProxy(windowId)->setSystemTitleBarVisible(false);
         std::ignore = new CocoaWindowEventFilter(this, this);
diff --git a/src/core/contexts/cocoawindowcontext_p.h b/src/core/contexts/cocoawindowcontext_p.h
index 4729c3f..c257adf 100644
--- a/src/core/contexts/cocoawindowcontext_p.h
+++ b/src/core/contexts/cocoawindowcontext_p.h
@@ -15,7 +15,7 @@
         void virtual_hook(int id, void *data) override;
 
     protected:
-        bool setupHost() override;
+        void winIdChanged(QWindow *oldWindow) override;
 
     protected:
         WId windowId = 0;
diff --git a/src/core/contexts/qtwindowcontext.cpp b/src/core/contexts/qtwindowcontext.cpp
index ad982c6..45aed07 100644
--- a/src/core/contexts/qtwindowcontext.cpp
+++ b/src/core/contexts/qtwindowcontext.cpp
@@ -103,7 +103,8 @@
 
     QtWindowEventFilter::~QtWindowEventFilter() = default;
 
-    bool QtWindowEventFilter::eventFilter(QObject *object, QEvent *event) {
+    bool QtWindowEventFilter::eventFilter(QObject *obj, QEvent *event) {
+        Q_UNUSED(obj)
         auto type = event->type();
         if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) {
             return false;
@@ -252,7 +253,7 @@
         AbstractWindowContext::virtual_hook(id, data);
     }
 
-    bool QtWindowContext::setupHost() {
+    bool QtWindowContext::winIdChanged() {
         m_delegate->setWindowFlags(m_host, Qt::FramelessWindowHint);
         std::ignore = new QtWindowEventFilter(this, this);
         return true;
diff --git a/src/core/contexts/qtwindowcontext_p.h b/src/core/contexts/qtwindowcontext_p.h
index 107353a..ff62b4a 100644
--- a/src/core/contexts/qtwindowcontext_p.h
+++ b/src/core/contexts/qtwindowcontext_p.h
@@ -15,7 +15,7 @@
         void virtual_hook(int id, void *data) override;
 
     protected:
-        bool setupHost() override;
+        bool winIdChanged() override;
     };
 
 }
diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 95f6e1a..2cfd8b1 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -764,18 +764,44 @@
         return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
     }
 
+    static inline void addManagedWindow(HWND hWnd, Win32WindowContext *ctx) {
+        // Store original window proc
+        if (!g_qtWindowProc) {
+            g_qtWindowProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hWnd, GWLP_WNDPROC));
+        }
+
+        // Hook window proc
+        ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWKHookedWndProc));
+
+        // Install global native event filter
+        WindowsNativeEventFilter::install();
+
+        // Save window handle mapping
+        g_wndProcHash->insert(hWnd, ctx);
+    }
+
+    static inline void removeManagedWindow(HWND hWnd, bool restore) {
+        // Remove window handle mapping
+        if (!g_wndProcHash->remove(hWnd))
+            return;
+
+        // Remove event filter if the all windows has been destroyed
+        if (g_wndProcHash->empty()) {
+            WindowsNativeEventFilter::uninstall();
+        }
+
+        // Restore window proc
+        if (restore) {
+            ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(g_qtWindowProc));
+        }
+    }
+
     Win32WindowContext::Win32WindowContext() : AbstractWindowContext() {
     }
 
     Win32WindowContext::~Win32WindowContext() {
-        // Remove window handle mapping
-        if (auto hWnd = reinterpret_cast<HWND>(windowId); hWnd) {
-            g_wndProcHash->remove(hWnd);
-
-            // Remove event filter if the all windows has been destroyed
-            if (g_wndProcHash->empty()) {
-                WindowsNativeEventFilter::uninstall();
-            }
+        if (windowId) {
+            removeManagedWindow(reinterpret_cast<HWND>(windowId), false);
         }
     }
 
@@ -871,14 +897,24 @@
         return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId));
     }
 
-    bool Win32WindowContext::setupHost() {
+    void Win32WindowContext::winIdChanged(QWindow *oldWindow, bool destroyed) {
+        if (oldWindow) {
+            removeManagedWindow(reinterpret_cast<HWND>(windowId), !destroyed);
+        }
+
+        if (!m_windowHandle) {
+            return;
+        }
+
         // Install window hook
         auto winId = m_windowHandle->winId();
         auto hWnd = reinterpret_cast<HWND>(winId);
 
 #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
-        for (const auto attr :
-             {_DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, _DWMWA_USE_IMMERSIVE_DARK_MODE}) {
+        for (const auto attr : {
+                 _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
+                 _DWMWA_USE_IMMERSIVE_DARK_MODE,
+             }) {
             const BOOL enable = TRUE;
             DynamicApis::instance().pDwmSetWindowAttribute(hWnd, attr, &enable, sizeof(enable));
         }
@@ -887,24 +923,11 @@
         // Inform Qt we want and have set custom margins
         updateInternalWindowFrameMargins(hWnd, m_windowHandle);
 
-        // Store original window proc
-        if (!g_qtWindowProc) {
-            g_qtWindowProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hWnd, GWLP_WNDPROC));
-        }
+        // Add managed window
+        addManagedWindow(hWnd, this);
 
-        // Hook window proc
-        ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWKHookedWndProc));
-
-        // Install global native event filter
-        WindowsNativeEventFilter::install();
-
-        // Cache window ID
+        // Cache win id
         windowId = winId;
-
-        // Save window handle mapping
-        g_wndProcHash->insert(hWnd, this);
-
-        return true;
     }
 
     bool Win32WindowContext::windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
diff --git a/src/core/contexts/win32windowcontext_p.h b/src/core/contexts/win32windowcontext_p.h
index 9303938..3d2649e 100644
--- a/src/core/contexts/win32windowcontext_p.h
+++ b/src/core/contexts/win32windowcontext_p.h
@@ -30,7 +30,7 @@
         int borderThickness() const;
 
     protected:
-        bool setupHost() override;
+        void winIdChanged(QWindow *oldWindow, bool destroyed) override;
 
     public:
         bool windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result);
diff --git a/src/core/windowagentbase.cpp b/src/core/windowagentbase.cpp
index f38fb5d..d2b9e1e 100644
--- a/src/core/windowagentbase.cpp
+++ b/src/core/windowagentbase.cpp
@@ -42,13 +42,10 @@
 #endif
     }
 
-    bool WindowAgentBasePrivate::setup(QObject *host, WindowItemDelegate *delegate) {
-        std::unique_ptr<AbstractWindowContext> ctx(createContext());
-        if (!ctx->setup(host, delegate)) {
-            return false;
-        }
-        context = std::move(ctx);
-        return true;
+    void WindowAgentBasePrivate::setup(QObject *host, WindowItemDelegate *delegate) {
+        auto ctx = createContext();
+        ctx->setup(host, delegate);
+        context.reset(ctx);
     }
 
     WindowAgentBase::~WindowAgentBase() = default;
diff --git a/src/core/windowagentbase_p.h b/src/core/windowagentbase_p.h
index f0e4a2d..032167e 100644
--- a/src/core/windowagentbase_p.h
+++ b/src/core/windowagentbase_p.h
@@ -18,7 +18,7 @@
 
         virtual AbstractWindowContext *createContext() const;
 
-        bool setup(QObject *host, WindowItemDelegate *delegate);
+        void setup(QObject *host, WindowItemDelegate *delegate);
 
         std::unique_ptr<AbstractWindowContext> context;
 
diff --git a/src/quick/quickwindowagent.cpp b/src/quick/quickwindowagent.cpp
index 2657126..4e07a3d 100644
--- a/src/quick/quickwindowagent.cpp
+++ b/src/quick/quickwindowagent.cpp
@@ -32,9 +32,7 @@
             return false;
         }
 
-        if (!d->setup(window, new QuickItemDelegate())) {
-            return false;
-        }
+        d->setup(window, new QuickItemDelegate());
         d->hostWindow = window;
 
 #ifdef Q_OS_WINDOWS
diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp
index 8b6988e..a15a7ed 100644
--- a/src/widgets/widgetwindowagent.cpp
+++ b/src/widgets/widgetwindowagent.cpp
@@ -9,6 +9,27 @@
 
 namespace QWK {
 
+    class WidgetWinIdChangeEventFilter : public QObject {
+    public:
+        explicit WidgetWinIdChangeEventFilter(QWidget *widget, AbstractWindowContext *ctx)
+            : QObject(ctx), widget(widget), ctx(ctx) {
+            widget->installEventFilter(this);
+        }
+
+    protected:
+        bool eventFilter(QObject *obj, QEvent *event) override {
+            Q_UNUSED(obj)
+            if (event->type() == QEvent::WinIdChange) {
+                ctx->notifyWinIdChange();
+            }
+            return false;
+        }
+
+    protected:
+        QWidget *widget;
+        AbstractWindowContext *ctx;
+    };
+
     WidgetWindowAgentPrivate::WidgetWindowAgentPrivate() = default;
 
     WidgetWindowAgentPrivate::~WidgetWindowAgentPrivate() = default;
@@ -36,14 +57,14 @@
         w->setAttribute(Qt::WA_DontCreateNativeAncestors);
         w->setAttribute(Qt::WA_NativeWindow);
 
-        if (!d->setup(w, new WidgetItemDelegate())) {
-            return false;
-        }
+        d->setup(w, new WidgetItemDelegate());
         d->hostWidget = w;
 
 #ifdef Q_OS_WINDOWS
         d->setupWindows10BorderWorkaround();
 #endif
+        std::ignore = new WidgetWinIdChangeEventFilter(w, d->context.get());
+
         return true;
     }
 
diff --git a/src/widgets/widgetwindowagent_mac.cpp b/src/widgets/widgetwindowagent_mac.cpp
index c21327c..912b03f 100644
--- a/src/widgets/widgetwindowagent_mac.cpp
+++ b/src/widgets/widgetwindowagent_mac.cpp
@@ -15,6 +15,7 @@
 
     protected:
         bool eventFilter(QObject *obj, QEvent *event) override {
+            Q_UNUSED(obj)
             switch (event->type()) {
                 case QEvent::Move:
                 case QEvent::Resize: {
@@ -25,7 +26,7 @@
                 default:
                     break;
             }
-            return QObject::eventFilter(obj, event);
+            return false;
         }
 
     protected:
diff --git a/src/widgets/widgetwindowagent_win.cpp b/src/widgets/widgetwindowagent_win.cpp
index 3eb9f1e..9197dca 100644
--- a/src/widgets/widgetwindowagent_win.cpp
+++ b/src/widgets/widgetwindowagent_win.cpp
@@ -64,6 +64,7 @@
         }
 
         bool eventFilter(QObject *obj, QEvent *event) override {
+            Q_UNUSED(obj)
             switch (event->type()) {
                 case QEvent::Paint: {
                     if (widget->windowState() & (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen))

--
Gitblit v1.9.1