From 988674c5cab26797b6ce77ac3df064e36070e7f3 Mon Sep 17 00:00:00 2001
From: Sine Striker <trueful@163.com>
Date: 周五, 08 12月 2023 01:55:36 +0800
Subject: [PATCH] Fix neglectful code

---
 src/core/contexts/win32windowcontext.cpp |  179 +++++++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 124 insertions(+), 55 deletions(-)

diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 27e5347..eb4f40d 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -23,19 +23,26 @@
 
 #include <shellscalingapi.h>
 #include <dwmapi.h>
+#include <timeapi.h>
 
 Q_DECLARE_METATYPE(QMargins)
 
 namespace QWK {
 
-    static constexpr const auto kAutoHideTaskBarThickness =
-        quint8{2}; // The thickness of an auto-hide taskbar in pixels.
+    // The thickness of an auto-hide taskbar in pixels.
+    static constexpr const auto kAutoHideTaskBarThickness = quint8{2};
 
-    using WndProcHash = QHash<HWND, Win32WindowContext *>; // hWnd -> context
+    // hWnd -> context
+    using WndProcHash = QHash<HWND, Win32WindowContext *>;
     Q_GLOBAL_STATIC(WndProcHash, g_wndProcHash)
 
-    static WNDPROC g_qtWindowProc = nullptr; // Original Qt window proc function
+    // 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 struct QWK_Hook {
         QWK_Hook() {
             qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
@@ -43,27 +50,56 @@
     } g_hook{};
 
     struct DynamicApis {
-        decltype(&::DwmFlush) pDwmFlush = nullptr;
-        decltype(&::DwmIsCompositionEnabled) pDwmIsCompositionEnabled = nullptr;
-        decltype(&::GetDpiForWindow) pGetDpiForWindow = nullptr;
-        decltype(&::GetSystemMetricsForDpi) pGetSystemMetricsForDpi = nullptr;
-        decltype(&::GetDpiForMonitor) pGetDpiForMonitor = nullptr;
+//        template <typename T>
+//        struct DefaultFunc;
+//
+//        template <typename Return, typename... Args>
+//        struct DefaultFunc<Return(QT_WIN_CALLBACK *)(Args...)> {
+//            static Return STDAPICALLTYPE func(Args...) {
+//                return Return{};
+//            }
+//        };
+//
+// #define DWM_API_DECLARE(NAME) decltype(&::NAME) p##NAME = DefaultFunc<decltype(&::NAME)>::func
+#define DWM_API_DECLARE(NAME) decltype(&::NAME) p##NAME = nullptr
+
+        DWM_API_DECLARE(DwmFlush);
+        DWM_API_DECLARE(DwmIsCompositionEnabled);
+        DWM_API_DECLARE(DwmGetCompositionTimingInfo);
+        DWM_API_DECLARE(GetDpiForWindow);
+        DWM_API_DECLARE(GetSystemMetricsForDpi);
+        DWM_API_DECLARE(GetDpiForMonitor);
+        DWM_API_DECLARE(timeGetDevCaps);
+        DWM_API_DECLARE(timeBeginPeriod);
+        DWM_API_DECLARE(timeEndPeriod);
+
+#undef DWM_API_DECLARE
 
         DynamicApis() {
-            QSystemLibrary user32(QStringLiteral("user32.dll"));
+            QSystemLibrary user32(QStringLiteral("user32"));
             pGetDpiForWindow =
                 reinterpret_cast<decltype(pGetDpiForWindow)>(user32.resolve("GetDpiForWindow"));
             pGetSystemMetricsForDpi = reinterpret_cast<decltype(pGetSystemMetricsForDpi)>(
                 user32.resolve("GetSystemMetricsForDpi"));
 
-            QSystemLibrary shcore(QStringLiteral("shcore.dll"));
+            QSystemLibrary shcore(QStringLiteral("shcore"));
             pGetDpiForMonitor =
                 reinterpret_cast<decltype(pGetDpiForMonitor)>(shcore.resolve("GetDpiForMonitor"));
 
-            QSystemLibrary dwmapi(QStringLiteral("dwmapi.dll"));
+            QSystemLibrary dwmapi(QStringLiteral("dwmapi"));
             pDwmFlush = reinterpret_cast<decltype(pDwmFlush)>(dwmapi.resolve("DwmFlush"));
             pDwmIsCompositionEnabled = reinterpret_cast<decltype(pDwmIsCompositionEnabled)>(
                 dwmapi.resolve("DwmIsCompositionEnabled"));
+            pDwmGetCompositionTimingInfo = reinterpret_cast<decltype(pDwmGetCompositionTimingInfo)>(
+                dwmapi.resolve("DwmGetCompositionTimingInfo"));
+
+            QSystemLibrary winmm(QStringLiteral("winmm"));
+            ptimeGetDevCaps =
+                reinterpret_cast<decltype(ptimeGetDevCaps)>(winmm.resolve("timeGetDevCaps"));
+            ptimeBeginPeriod =
+                reinterpret_cast<decltype(ptimeBeginPeriod)>(winmm.resolve("timeBeginPeriod"));
+            ptimeEndPeriod =
+                reinterpret_cast<decltype(ptimeEndPeriod)>(winmm.resolve("timeEndPeriod"));
         }
 
         ~DynamicApis() = default;
@@ -284,10 +320,6 @@
     }
 
     static inline bool isFullScreen(HWND hwnd) {
-        Q_ASSERT(hwnd);
-        if (!hwnd) {
-            return false;
-        }
         RECT windowRect{};
         ::GetWindowRect(hwnd, &windowRect);
         // Compare to the full area of the screen, not the work area.
@@ -295,10 +327,6 @@
     }
 
     static inline bool isWindowNoState(HWND hwnd) {
-        Q_ASSERT(hwnd);
-        if (!hwnd) {
-            return false;
-        }
 #if 0
         WINDOWPLACEMENT wp{};
         wp.length = sizeof(wp);
@@ -310,16 +338,49 @@
 #endif
     }
 
-    static inline QPoint fromNativeLocalPosition(const QWindow *window, const QPoint &point) {
-        Q_ASSERT(window);
-        if (!window) {
-            return point;
+    static inline void syncPaintEventWithDwm() {
+        // No need to sync with DWM if DWM composition is disabled.
+        if (!isDwmCompositionEnabled()) {
+            return;
         }
-#if 1
-        return QHighDpi::fromNativeLocalPosition(point, window);
-#else
-        return QPointF(QPointF(point) / window->devicePixelRatio()).toPoint();
-#endif
+        const DynamicApis &apis = DynamicApis::instance();
+        // Dirty hack to workaround the resize flicker caused by DWM.
+        LARGE_INTEGER freq{};
+        ::QueryPerformanceFrequency(&freq);
+        TIMECAPS tc{};
+        apis.ptimeGetDevCaps(&tc, sizeof(tc));
+        const UINT ms_granularity = tc.wPeriodMin;
+        apis.ptimeBeginPeriod(ms_granularity);
+        LARGE_INTEGER now0{};
+        ::QueryPerformanceCounter(&now0);
+        // ask DWM where the vertical blank falls
+        DWM_TIMING_INFO dti{};
+        dti.cbSize = sizeof(dti);
+        apis.pDwmGetCompositionTimingInfo(nullptr, &dti);
+        LARGE_INTEGER now1{};
+        ::QueryPerformanceCounter(&now1);
+        // - DWM told us about SOME vertical blank
+        //   - past or future, possibly many frames away
+        // - convert that into the NEXT vertical blank
+        const auto period = qreal(dti.qpcRefreshPeriod);
+        const auto dt = qreal(dti.qpcVBlank - now1.QuadPart);
+        const qreal ratio = (dt / period);
+        auto w = qreal(0);
+        auto m = qreal(0);
+        if ((dt > qreal(0)) || qFuzzyIsNull(dt)) {
+            w = ratio;
+        } else {
+            // reach back to previous period
+            // - so m represents consistent position within phase
+            w = (ratio - qreal(1));
+        }
+        m = (dt - (period * w));
+        if ((m < qreal(0)) || qFuzzyCompare(m, period) || (m > period)) {
+            return;
+        }
+        const qreal m_ms = (qreal(1000) * m / qreal(freq.QuadPart));
+        ::Sleep(static_cast<DWORD>(std::round(m_ms)));
+        apis.ptimeEndPeriod(ms_granularity);
     }
 
     static inline Win32WindowContext::WindowPart getHitWindowPart(int hitTestResult) {
@@ -396,9 +457,6 @@
                                QT_NATIVE_EVENT_RESULT_TYPE *result) override {
             Q_UNUSED(eventType)
 
-            auto orgLastMessageContext = lastMessageContext;
-            lastMessageContext = nullptr;
-
             // It has been observed that the pointer that Qt gives us is sometimes null on some
             // machines. We need to guard against it in such scenarios.
             if (!result) {
@@ -407,11 +465,11 @@
 
             // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1546
             // Qt needs to refer to the WM_NCCALCSIZE message data that hasn't been processed, so we
-            // have to process it after Qt acquired the initial data.
-            auto msg = reinterpret_cast<const MSG *>(message);
-            if (msg->message == WM_NCCALCSIZE && orgLastMessageContext) {
+            // have to process it after Qt acquires the initial data.
+            auto msg = static_cast<const MSG *>(message);
+            if (msg->message == WM_NCCALCSIZE && lastMessageContext) {
                 LRESULT res;
-                if (Win32WindowContext::nonClientCalcSizeHandler(msg->hwnd, msg->message,
+                if (lastMessageContext->nonClientCalcSizeHandler(msg->hwnd, msg->message,
                                                                  msg->wParam, msg->lParam, &res)) {
                     *result = decltype(*result)(res);
                     return true;
@@ -424,11 +482,17 @@
         static Win32WindowContext *lastMessageContext;
 
         static inline void install() {
+            if (instance) {
+                return;
+            }
             instance = new WindowsNativeEventFilter();
             installNativeEventFilter(instance);
         }
 
         static inline void uninstall() {
+            if (!instance) {
+                return;
+            }
             removeNativeEventFilter(instance);
             delete instance;
             instance = nullptr;
@@ -507,22 +571,18 @@
         // forward it right away and process it in our native event filter.
         if (message == WM_NCCALCSIZE) {
             WindowsNativeEventFilter::lastMessageContext = ctx;
-            return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
+            LRESULT result = ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
+            WindowsNativeEventFilter::lastMessageContext = nullptr;
+            return result;
         }
 
         // Try hooked procedure and save result
         LRESULT result;
-        bool handled = ctx->windowProc(hWnd, message, wParam, lParam, &result);
-
-        // TODO: Determine whether to show system menu
-        // ...
-
-        if (handled) {
+        if (ctx->windowProc(hWnd, message, wParam, lParam, &result)) {
             return result;
         }
 
         // Continue dispatching.
-        WindowsNativeEventFilter::lastMessageContext = ctx;
         return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
     }
 
@@ -558,9 +618,7 @@
         ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWKHookedWndProc));
 
         // Install global native event filter
-        if (!WindowsNativeEventFilter::instance) {
-            WindowsNativeEventFilter::install();
-        }
+        WindowsNativeEventFilter::install();
 
         // Cache window ID
         windowId = winId;
@@ -591,6 +649,10 @@
 
         if (!isValidWindow(hWnd, false, true)) {
             return false;
+        }
+
+        if (systemMenuHandler(hWnd, message, wParam, lParam, result)) {
+            return true;
         }
 
         // Test snap layout
@@ -762,8 +824,8 @@
                     DWORD dwScreenPos = ::GetMessagePos();
                     POINT screenPoint{GET_X_LPARAM(dwScreenPos), GET_Y_LPARAM(dwScreenPos)};
                     ::ScreenToClient(hWnd, &screenPoint);
-                    QPoint qtScenePos =
-                        fromNativeLocalPosition(m_windowHandle, {screenPoint.x, screenPoint.y});
+                    QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(
+                        QPoint{screenPoint.x, screenPoint.y}, m_windowHandle);
                     auto dummy = CoreWindowAgent::Unknown;
                     if (isInSystemButtons(qtScenePos, &dummy)) {
                         // We must record whether the last WM_MOUSELEAVE was filtered, because if
@@ -811,7 +873,7 @@
                 const WindowPart currentWindowPart = lastHitTestResult;
                 if (message == WM_NCMOUSEMOVE) {
                     if (currentWindowPart != WindowPart::ChromeButton) {
-                        std::ignore = m_delegate->resetQtGrabbedControl();
+                        m_delegate->resetQtGrabbedControl();
                         if (mouseLeaveBlocked) {
                             emulateClientAreaMessage(hWnd, message, wParam, lParam,
                                                      WM_NCMOUSELEAVE);
@@ -873,7 +935,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.
-                        std::ignore = m_delegate->resetQtGrabbedControl();
+                        m_delegate->resetQtGrabbedControl();
                     }
                 }
                 break;
@@ -995,8 +1057,8 @@
                 auto clientWidth = RECT_WIDTH(clientRect);
                 auto clientHeight = RECT_HEIGHT(clientRect);
 
-                QPoint qtScenePos = fromNativeLocalPosition(
-                    m_windowHandle, QPoint(nativeLocalPos.x, nativeLocalPos.y));
+                QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(
+                    QPoint(nativeLocalPos.x, nativeLocalPos.y), m_windowHandle);
 
                 bool isFixedSize = m_delegate->isHostSizeFixed(m_host);
                 bool isTitleBar = isInTitleBarDraggableArea(qtScenePos);
@@ -1247,6 +1309,7 @@
     bool Win32WindowContext::nonClientCalcSizeHandler(HWND hWnd, UINT message, WPARAM wParam,
                                                       LPARAM lParam, LRESULT *result) {
         Q_UNUSED(message)
+        Q_UNUSED(this)
 
         // Windows鏄牴鎹繖涓秷鎭殑杩斿洖鍊兼潵璁剧疆绐楀彛鐨勫鎴峰尯锛堢獥鍙d腑鐪熸鏄剧ず鐨勫唴瀹癸級
         // 鍜岄潪瀹㈡埛鍖猴紙鏍囬鏍忋�佺獥鍙h竟妗嗐�佽彍鍗曟爮鍜岀姸鎬佹爮绛塛indows绯荤粺鑷鎻愪緵鐨勯儴鍒�
@@ -1455,8 +1518,9 @@
                 }
             }
         }
-        // ### TODO: std::ignore = Utils::syncWmPaintWithDwm(); // This should be executed
-        // at the very last. By returning WVR_REDRAW we can make the window resizing look
+        // We should call this function only before the function returns.
+        syncPaintEventWithDwm();
+        // By returning WVR_REDRAW we can make the window resizing look
         // less broken. But we must return 0 if wParam is FALSE, according to Microsoft
         // Docs.
         // **IMPORTANT NOTE**:
@@ -1470,4 +1534,9 @@
         return true;
     }
 
+    bool Win32WindowContext::systemMenuHandler(HWND hWnd, UINT message, WPARAM wParam,
+                                               LPARAM lParam, LRESULT *result) {
+        return false;
+    }
+
 }

--
Gitblit v1.9.1