Sine Striker
2023-12-03 eb266da10552eeefcb0f7dfe514d4385aec563f8
Add windows context implementation
9个文件已修改
1个文件已添加
213 ■■■■ 已修改文件
src/core/CMakeLists.txt 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext.cpp 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext_p.h 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext.cpp 125 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/corewindowagent.cpp 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/corewindowagent_p.h 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/qwindowkit_windows.h 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/qwkcoreglobal.h 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/qwkcoreglobal_p.h 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/windowitemdelegate.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/CMakeLists.txt
@@ -5,6 +5,7 @@
set(_src
    qwkcoreglobal.h
    qwkcoreglobal_p.h
    corewindowagent.h
    corewindowagent_p.h
    corewindowagent.cpp
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);
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;
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
    }
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;
    }
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)
    };
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
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
src/core/qwkcoreglobal_p.h
New file
@@ -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
src/core/windowitemdelegate.h
@@ -1,8 +1,6 @@
#ifndef WINDOWITEMDELEGATE_H
#define WINDOWITEMDELEGATE_H
#include <memory>
#include <QtCore/QObject>
#include <QtGui/QWindow>