From e1cbc7a1f19a97c7c00ba32df17b30d4cf32c630 Mon Sep 17 00:00:00 2001
From: Yuhang Zhao <zhaoyuhang@rankyee.com>
Date: 摹曛, 07 12月 2023 17:36:56 +0800
Subject: [PATCH] port sync dwm code

---
 src/core/contexts/win32windowcontext.cpp |   63 ++++++++++++++++++++++++++++++-
 1 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp
index 8259d6f..1201277 100644
--- a/src/core/contexts/win32windowcontext.cpp
+++ b/src/core/contexts/win32windowcontext.cpp
@@ -23,6 +23,7 @@
 
 #include <shellscalingapi.h>
 #include <dwmapi.h>
+#include <timeapi.h>
 
 Q_DECLARE_METATYPE(QMargins)
 
@@ -45,9 +46,13 @@
     struct DynamicApis {
         decltype(&::DwmFlush) pDwmFlush = nullptr;
         decltype(&::DwmIsCompositionEnabled) pDwmIsCompositionEnabled = nullptr;
+        decltype(&::DwmGetCompositionTimingInfo) pDwmGetCompositionTimingInfo = nullptr;
         decltype(&::GetDpiForWindow) pGetDpiForWindow = nullptr;
         decltype(&::GetSystemMetricsForDpi) pGetSystemMetricsForDpi = nullptr;
         decltype(&::GetDpiForMonitor) pGetDpiForMonitor = nullptr;
+        decltype(&::timeGetDevCaps) ptimeGetDevCaps = nullptr;
+        decltype(&::timeBeginPeriod) ptimeBeginPeriod = nullptr;
+        decltype(&::timeEndPeriod) ptimeEndPeriod = nullptr;
 
         DynamicApis() {
             QSystemLibrary user32(QStringLiteral("user32.dll"));
@@ -64,6 +69,12 @@
             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.dll"));
+            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;
@@ -308,6 +319,53 @@
         const auto style = static_cast<DWORD>(::GetWindowLongPtrW(hwnd, GWL_STYLE));
         return (!(style & (WS_MINIMIZE | WS_MAXIMIZE)));
 #endif
+    }
+
+    static inline void syncPaintEventWithDwm() {
+        // No need to sync with DWM if DWM composition is disabled.
+        if (!isDwmCompositionEnabled()) {
+            return;
+        }
+        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));
+        //Q_ASSERT((m > qreal(0)) || qFuzzyIsNull(m));
+        //Q_ASSERT(m < period);
+        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 QPoint fromNativeLocalPosition(const QWindow *window, const QPoint &point) {
@@ -1455,8 +1513,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**:

--
Gitblit v1.9.1