From eb266da10552eeefcb0f7dfe514d4385aec563f8 Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: 周日, 03 12月 2023 05:05:23 +0800 Subject: [PATCH] Add windows context implementation --- src/core/corewindowagent.cpp | 10 +- src/core/corewindowagent_p.h | 2 src/core/qwkcoreglobal.h | 12 -- src/core/windowitemdelegate.h | 2 src/core/contexts/abstractwindowcontext.cpp | 4 src/core/contexts/abstractwindowcontext_p.h | 3 src/core/CMakeLists.txt | 1 src/core/qwindowkit_windows.h | 18 ++++ src/core/qwkcoreglobal_p.h | 36 +++++++++ src/core/contexts/win32windowcontext.cpp | 125 +++++++++++++++++++++++++++---- 10 files changed, 173 insertions(+), 40 deletions(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8444ad6..2ad4a0c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -5,6 +5,7 @@ set(_src qwkcoreglobal.h + qwkcoreglobal_p.h corewindowagent.h corewindowagent_p.h corewindowagent.cpp diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp index a6d4aeb..920e210 100644 --- a/src/core/contexts/abstractwindowcontext.cpp +++ b/src/core/contexts/abstractwindowcontext.cpp @@ -2,9 +2,7 @@ namespace QWK { - AbstractWindowContext::~AbstractWindowContext() { - delete m_delegate; - } + AbstractWindowContext::~AbstractWindowContext() = default; void AbstractWindowContext::setupWindow(QWindow *window) { Q_ASSERT(window); diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h index 4c04734..c9b1374 100644 --- a/src/core/contexts/abstractwindowcontext_p.h +++ b/src/core/contexts/abstractwindowcontext_p.h @@ -2,6 +2,7 @@ #define ABSTRACTWINDOWCONTEXT_P_H #include <array> +#include <memory> #include <QtCore/QSet> #include <QtGui/QWindow> @@ -42,7 +43,7 @@ protected: QWindow *m_windowHandle; - WindowItemDelegate *m_delegate; + std::unique_ptr<WindowItemDelegate> m_delegate; QSet<QObject *> m_hitTestVisibleItems; QList<QRect> m_hitTestVisibleRects; diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index a0d5779..73a8eb2 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -1,6 +1,10 @@ #include "win32windowcontext_p.h" #include <QtCore/QHash> +#include <QtCore/QAbstractNativeEventFilter> +#include <QtCore/QCoreApplication> + +#include "qwkcoreglobal_p.h" namespace QWK { @@ -9,8 +13,78 @@ static WNDPROC g_qtWindowProc = nullptr; // Original Qt window proc function - extern "C" LRESULT QT_WIN_CALLBACK QWK_WindowsWndProc(HWND hWnd, UINT message, WPARAM wParam, - LPARAM lParam) { + 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; + + // 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 + // 11, we must process some non-client area messages ourselves, but unfortunately these messages + // have been filtered out already in that line, and thus we'll never have the chance to process + // them ourselves. This is Qt's low level platform specific code, so we don't have any official + // ways to change this behavior. But luckily we can replace the window procedure function of + // Qt's windows, and in this hooked window procedure function, we finally have the chance to + // process window messages before Qt touches them. So we reconstruct the MSG structure and send + // it to our own custom native event filter to do all the magic works. But since the system menu + // feature doesn't necessarily belong to the native implementation, we seperate the handling + // code and always process the system menu part in this function for both implementations. + // + // Original event flow: + // [Entry] Windows Message Queue + // | + // [Qt Window Proc] qwindowscontext.cpp#L1547: qWindowsWndProc() + // ``` + // const bool handled = QWindowsContext::instance()->windowsProc + // (hwnd, message, et, wParam, lParam, &result, + // &platformWindow); + // ``` + // | + // [Non-Input Filter] qwindowscontext.cpp#L1025: QWindowsContext::windowsProc() + // ``` + // if (!isInputMessage(msg.message) && + // filterNativeEvent(&msg, result)) + // return true; + // ``` + // | + // [User Filter] qwindowscontext.cpp#L1588: QWindowsContext::windowsProc() + // ``` + // QAbstractEventDispatcher *dispatcher = + // QAbstractEventDispatcher::instance(); + // qintptr filterResult = 0; + // if (dispatcher && + // dispatcher->filterNativeEvent(nativeEventType(), msg, + // &filterResult)) { + // *result = LRESULT(filterResult); + // return true; + // } + // ``` + // | + // [Extra work] The rest of QWindowsContext::windowsProc() and qWindowsWndProc() + // + // Notice: Only non-input messages will be processed by the user-defined global native event + // filter!!! These events are then passed to the widget class's own overridden + // QWidget::nativeEvent() as a local filter, where all native events can be handled, but we must + // create a new class derived from QWidget which we don't intend to. Therefore, we don't expect + // to process events from the global native event filter, but instead hook Qt's window + // procedure. + + extern "C" LRESULT QT_WIN_CALLBACK QWKHookedWndProc(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam) { Q_ASSERT(hWnd); if (!hWnd) { return FALSE; @@ -22,13 +96,14 @@ return ::DefWindowProcW(hWnd, message, wParam, lParam); } - // Try hooked procedure - LRESULT result; - if (ctx->windowProc(hWnd, message, wParam, lParam, &result)) { - return result; - } + // Try hooked procedure and save result + g_lastMessageHandled = ctx->windowProc(hWnd, message, wParam, lParam, &g_lastMessageResult); - // Fallback to Qt's procedure + // TODO: Determine whether to show system menu + // ... + + // Since Qt does the necessary processing of the message afterward, we still need to + // continue dispatching it. return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); } @@ -38,8 +113,16 @@ Win32WindowContext::~Win32WindowContext() { // Remove window handle mapping - auto hWnd = reinterpret_cast<HWND>(windowId); - g_wndProcHash->remove(hWnd); + if (auto hWnd = reinterpret_cast<HWND>(windowId); hWnd) { + g_wndProcHash->remove(hWnd); + + // Remove event filter if the last window is destroyed + if (g_wndProcHash->empty()) { + qApp->removeNativeEventFilter(g_nativeFilter); + delete g_nativeFilter; + g_nativeFilter = nullptr; + } + } } bool Win32WindowContext::setup() { @@ -47,15 +130,23 @@ // Install window hook auto hWnd = reinterpret_cast<HWND>(winId); - auto qtWindowProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hWnd, GWLP_WNDPROC)); - ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWK_WindowsWndProc)); - - windowId = winId; // Store original window proc if (!g_qtWindowProc) { - g_qtWindowProc = 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 + if (!g_nativeFilter) { + g_nativeFilter = new WindowsNativeEventFilter(); + qApp->installNativeEventFilter(g_nativeFilter); + } + + // Cache window ID + windowId = winId; // Save window handle mapping g_wndProcHash->insert(hWnd, this); @@ -67,10 +158,10 @@ LRESULT *result) { *result = FALSE; + Q_UNUSED(windowId) + // TODO: Implement // ... - - Q_UNUSED(windowId) return false; // Not handled } diff --git a/src/core/corewindowagent.cpp b/src/core/corewindowagent.cpp index c843273..abcd6fb 100644 --- a/src/core/corewindowagent.cpp +++ b/src/core/corewindowagent.cpp @@ -1,6 +1,8 @@ #include "corewindowagent.h" #include "corewindowagent_p.h" +#include "qwkcoreglobal_p.h" + #ifdef Q_OS_WINDOWS # include "win32windowcontext_p.h" #else @@ -11,12 +13,10 @@ namespace QWK { - CoreWindowAgentPrivate::CoreWindowAgentPrivate() : eventHandler(nullptr) { + CoreWindowAgentPrivate::CoreWindowAgentPrivate() : q_ptr(nullptr), eventHandler(nullptr) { } - CoreWindowAgentPrivate::~CoreWindowAgentPrivate() { - delete eventHandler; - } + CoreWindowAgentPrivate::~CoreWindowAgentPrivate() = default; void CoreWindowAgentPrivate::init() { } @@ -38,7 +38,7 @@ delete handler; return false; } - eventHandler = handler; + eventHandler.reset(handler); return true; } diff --git a/src/core/corewindowagent_p.h b/src/core/corewindowagent_p.h index 0c9be23..013ccbe 100644 --- a/src/core/corewindowagent_p.h +++ b/src/core/corewindowagent_p.h @@ -18,7 +18,7 @@ bool setup(QWindow *window, WindowItemDelegate *delegate); - AbstractWindowContext *eventHandler; + std::unique_ptr<AbstractWindowContext> eventHandler; Q_DISABLE_COPY_MOVE(CoreWindowAgentPrivate) }; diff --git a/src/core/qwindowkit_windows.h b/src/core/qwindowkit_windows.h index c7726b9..ff7ff2b 100644 --- a/src/core/qwindowkit_windows.h +++ b/src/core/qwindowkit_windows.h @@ -2,5 +2,23 @@ #define QWINDOWKIT_WINDOWS_H #include <QtCore/qt_windows.h> +#include <QtCore/qglobal.h> + +// MOC can't handle C++ attributes before 5.15. +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) +# define Q_NODISCARD [[nodiscard]] +# define Q_MAYBE_UNUSED [[maybe_unused]] +#else +# define Q_NODISCARD +# define Q_MAYBE_UNUSED +#endif + +#ifndef GET_X_LPARAM +# define GET_X_LPARAM(lp) (static_cast<int>(static_cast<short>(LOWORD(lp)))) +#endif + +#ifndef GET_Y_LPARAM +# define GET_Y_LPARAM(lp) (static_cast<int>(static_cast<short>(HIWORD(lp)))) +#endif #endif // QWINDOWKIT_WINDOWS_H diff --git a/src/core/qwkcoreglobal.h b/src/core/qwkcoreglobal.h index 517d91c..94982e6 100644 --- a/src/core/qwkcoreglobal.h +++ b/src/core/qwkcoreglobal.h @@ -1,7 +1,7 @@ #ifndef QWKCOREGLOBAL_H #define QWKCOREGLOBAL_H -#include <QtCore/QLoggingCategory> +#include <QtCore/QtGlobal> #ifndef QWK_CORE_EXPORT # ifdef QWK_CORE_STATIC @@ -13,16 +13,6 @@ # define QWK_CORE_EXPORT Q_DECL_IMPORT # endif # endif -#endif - -QWK_CORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(qWindowKitLog) - -#define QWK_INFO qCInfo(qWindowKitLog) -#define QWK_DEBUG qCDebug(qWindowKitLog) -#define QWK_WARNING qCWarning(qWindowKitLog) -#define QWK_CRITICAL qCCritical(qWindowKitLog) -#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) -# define QWK_FATAL qCFatal(qWindowKitLog) #endif #endif // QWKCOREGLOBAL_H diff --git a/src/core/qwkcoreglobal_p.h b/src/core/qwkcoreglobal_p.h new file mode 100644 index 0000000..5f76437 --- /dev/null +++ b/src/core/qwkcoreglobal_p.h @@ -0,0 +1,36 @@ +#ifndef QWKCOREGLOBAL_P_H +#define QWKCOREGLOBAL_P_H + +#include <QtCore/QEvent> +#include <QtCore/QLoggingCategory> + +#include <QWKCore/qwkcoreglobal.h> + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +using QT_NATIVE_EVENT_RESULT_TYPE = qintptr; +using QT_ENTER_EVENT_TYPE = QEnterEvent; +#else +using QT_NATIVE_EVENT_RESULT_TYPE = long; +using QT_ENTER_EVENT_TYPE = QEvent; +#endif + +QWK_CORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(qWindowKitLog) + +#define QWK_INFO qCInfo(qWindowKitLog) +#define QWK_DEBUG qCDebug(qWindowKitLog) +#define QWK_WARNING qCWarning(qWindowKitLog) +#define QWK_CRITICAL qCCritical(qWindowKitLog) +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) +# define QWK_FATAL qCFatal(qWindowKitLog) +#endif + +// MOC can't handle C++ attributes before 5.15. +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) +# define Q_NODISCARD [[nodiscard]] +# define Q_MAYBE_UNUSED [[maybe_unused]] +#else +# define Q_NODISCARD +# define Q_MAYBE_UNUSED +#endif + +#endif // QWKCOREGLOBAL_P_H diff --git a/src/core/windowitemdelegate.h b/src/core/windowitemdelegate.h index 985dedc..fe9a252 100644 --- a/src/core/windowitemdelegate.h +++ b/src/core/windowitemdelegate.h @@ -1,8 +1,6 @@ #ifndef WINDOWITEMDELEGATE_H #define WINDOWITEMDELEGATE_H -#include <memory> - #include <QtCore/QObject> #include <QtGui/QWindow> -- Gitblit v1.9.1