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