Yuhang Zhao
2023-12-19 fe50d30edcc31618905ec3e3dc39c8b78cee49db
src/core/contexts/win32windowcontext.cpp
@@ -79,16 +79,6 @@
    // Original Qt window proc function
    static WNDPROC g_qtWindowProc = nullptr;
    // ### FIXME FIXME FIXME
    // ### FIXME: Tell the user to call in the documentation, instead of automatically
    // calling it directly.
    // ### FIXME FIXME FIXME
    static const struct QWK_Hook {
        QWK_Hook() {
            qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
        }
    } g_hook{};
    struct DynamicApis {
        static const DynamicApis &instance() {
            static const DynamicApis inst{};
@@ -114,6 +104,7 @@
        DYNAMIC_API_DECLARE(DwmIsCompositionEnabled);
        DYNAMIC_API_DECLARE(DwmGetCompositionTimingInfo);
        DYNAMIC_API_DECLARE(DwmGetWindowAttribute);
        DYNAMIC_API_DECLARE(DwmSetWindowAttribute);
        DYNAMIC_API_DECLARE(GetDpiForWindow);
        DYNAMIC_API_DECLARE(GetSystemMetricsForDpi);
        DYNAMIC_API_DECLARE(GetDpiForMonitor);
@@ -140,6 +131,7 @@
            DYNAMIC_API_RESOLVE(dwmapi, DwmIsCompositionEnabled);
            DYNAMIC_API_RESOLVE(dwmapi, DwmGetCompositionTimingInfo);
            DYNAMIC_API_RESOLVE(dwmapi, DwmGetWindowAttribute);
            DYNAMIC_API_RESOLVE(dwmapi, DwmSetWindowAttribute);
            QSystemLibrary winmm(QStringLiteral("winmm"));
            DYNAMIC_API_RESOLVE(winmm, timeGetDevCaps);
@@ -428,8 +420,10 @@
        const auto monitorInfo = getMonitorForWindow(hwnd);
        RECT windowRect{};
        ::GetWindowRect(hwnd, &windowRect);
        const auto newX = monitorInfo.rcMonitor.left + (RECT_WIDTH(monitorInfo.rcMonitor) - RECT_WIDTH(windowRect)) / 2;
        const auto newY = monitorInfo.rcMonitor.top + (RECT_HEIGHT(monitorInfo.rcMonitor) - RECT_HEIGHT(windowRect)) / 2;
        const auto newX = monitorInfo.rcMonitor.left +
                          (RECT_WIDTH(monitorInfo.rcMonitor) - RECT_WIDTH(windowRect)) / 2;
        const auto newY = monitorInfo.rcMonitor.top +
                          (RECT_HEIGHT(monitorInfo.rcMonitor) - RECT_HEIGHT(windowRect)) / 2;
        ::SetWindowPos(hwnd, nullptr, newX, newY, 0, 0,
                       SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
    }
@@ -770,18 +764,39 @@
        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) {
        // 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();
        }
    }
    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));
        }
    }
@@ -792,17 +807,45 @@
    void Win32WindowContext::virtual_hook(int id, void *data) {
        switch (id) {
            case CentralizeHook: {
                const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
                const auto hwnd = reinterpret_cast<HWND>(windowId);
                moveToDesktopCenter(hwnd);
                return;
            }
            case RaiseWindowHook: {
                // FIXME
                return;
            }
            case ShowSystemMenuHook: {
                const auto &pos = *static_cast<const QPoint *>(data);
                auto hWnd = reinterpret_cast<HWND>(m_windowHandle->winId());
                showSystemMenu2(hWnd, qpoint2point(pos), false,
                auto hWnd = reinterpret_cast<HWND>(windowId);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
                const QPoint nativeGlobalPos =
                    QHighDpi::toNativeGlobalPosition(pos, m_windowHandle);
#else
                const QPoint nativeGlobalPos = QHighDpi::toNativePixels(pos, m_windowHandle);
#endif
                showSystemMenu2(hWnd, qpoint2point(nativeGlobalPos), false,
                                m_delegate->isHostSizeFixed(m_host));
                return;
            }
            case WindowAttributeChangedHook: {
                auto args = static_cast<void **>(data);
                const auto &key = *static_cast<const QString *>(args[0]);
                const auto &newVar = *static_cast<const QVariant *>(args[1]);
                const auto &oldVar = *static_cast<const QVariant *>(args[2]);
                if (key == QStringLiteral("no-frame-shadow")) {
                    if (newVar.toBool()) {
                        // TODO: set off
                    } else {
                        // TODO: set on
                    }
                }
                break;
            }
            case DefaultColorsHook: {
@@ -820,7 +863,7 @@
                auto &painter = *static_cast<QPainter *>(args[0]);
                const auto &rect = *static_cast<const QRect *>(args[1]);
                const auto &region = *static_cast<const QRegion *>(args[2]);
                const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId());
                const auto hwnd = reinterpret_cast<HWND>(windowId);
                QPen pen;
                pen.setWidth(getWindowFrameBorderThickness(hwnd) * 2);
@@ -871,32 +914,34 @@
        return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId));
    }
    bool Win32WindowContext::setupHost() {
    void Win32WindowContext::winIdChanged(QWindow *oldWindow) {
        removeManagedWindow(reinterpret_cast<HWND>(windowId));
        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,
             }) {
            const BOOL enable = TRUE;
            DynamicApis::instance().pDwmSetWindowAttribute(hWnd, attr, &enable, sizeof(enable));
        }
#endif
        // 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,
@@ -1157,7 +1202,7 @@
                const WindowPart currentWindowPart = lastHitTestResult;
                if (message == WM_NCMOUSEMOVE) {
                    if (currentWindowPart != WindowPart::ChromeButton) {
                        m_delegate->resetQtGrabbedControl();
                        m_delegate->resetQtGrabbedControl(m_host);
                        if (mouseLeaveBlocked) {
                            emulateClientAreaMessage(hWnd, message, wParam, lParam,
                                                     WM_NCMOUSELEAVE);
@@ -1219,7 +1264,7 @@
                        // window from client area, which means we will get previous window part as
                        // HTCLIENT if the mouse leaves window from client area and enters window
                        // from non-client area, but it has no bad effect.
                        m_delegate->resetQtGrabbedControl();
                        m_delegate->resetQtGrabbedControl(m_host);
                    }
                }
                break;
@@ -1544,9 +1589,10 @@
                // the client area as a whole will shift to the left, which looks very abnormal if
                // we don't repaint it. This exception disappears if we add SWP_NOCOPYBITS flag.
                // But I don't know what caused the problem, or why this would solve it.
                static constexpr const auto kBadWindowPosFlag =
                    SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED;
                const auto windowPos = reinterpret_cast<LPWINDOWPOS>(lParam);
                if (windowPos->flags ==
                    (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED)) {
                if (windowPos->flags == kBadWindowPosFlag) {
                    windowPos->flags |= SWP_NOCOPYBITS;
                }
                break;