diff --git a/framelesshelper_win32.cpp b/framelesshelper_win32.cpp index 48096cb..c617664 100644 --- a/framelesshelper_win32.cpp +++ b/framelesshelper_win32.cpp @@ -381,76 +381,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me // is not correct. It confuses QPA's internal logic. clientRect->bottom += 1; #endif - if (Utilities::isDwmCompositionAvailable()) { - QSystemLibrary winmmLib(QStringLiteral("winmm")); - static const auto ptimeGetDevCaps = - reinterpret_cast(winmmLib.resolve("timeGetDevCaps")); - static const auto ptimeBeginPeriod = - reinterpret_cast(winmmLib.resolve("timeBeginPeriod")); - static const auto ptimeEndPeriod = - reinterpret_cast(winmmLib.resolve("timeEndPeriod")); - static const auto pDwmGetCompositionTimingInfo = - reinterpret_cast(QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetCompositionTimingInfo")); - if (ptimeGetDevCaps && ptimeBeginPeriod && ptimeEndPeriod && pDwmGetCompositionTimingInfo) { - // Dirty hack to workaround the resize flicker caused by DWM. - LARGE_INTEGER freq = {}; - if (QueryPerformanceFrequency(&freq) == FALSE) { - qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceFrequency")); - break; - } - flh_TIMECAPS tc = {}; - if (ptimeGetDevCaps(&tc, sizeof(tc)) != /*MMSYSERR_NOERROR*/0) { - qWarning() << "timeGetDevCaps() failed."; - break; - } - const UINT ms_granularity = tc.wPeriodMin; - if (ptimeBeginPeriod(ms_granularity) != /*TIMERR_NOERROR*/0) { - qWarning() << "timeBeginPeriod() failed."; - break; - } - LARGE_INTEGER now0 = {}; - if (QueryPerformanceCounter(&now0) == FALSE) { - qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceCounter")); - break; - } - // ask DWM where the vertical blank falls - DWM_TIMING_INFO dti; - SecureZeroMemory(&dti, sizeof(dti)); - dti.cbSize = sizeof(dti); - const HRESULT hr = pDwmGetCompositionTimingInfo(nullptr, &dti); - if (FAILED(hr)) { - qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("DwmGetCompositionTimingInfo")); - break; - } - LARGE_INTEGER now1 = {}; - if (QueryPerformanceCounter(&now1) == FALSE) { - qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceCounter")); - break; - } - // - DWM told us about SOME vertical blank - // - past or future, possibly many frames away - // - convert that into the NEXT vertical blank - const LONGLONG period = dti.qpcRefreshPeriod; - const LONGLONG dt = dti.qpcVBlank - now1.QuadPart; - LONGLONG w = 0, m = 0; - if (dt >= 0) { - w = dt / period; - } else { - // reach back to previous period - // - so m represents consistent position within phase - w = -1 + dt / period; - } - m = dt - (period * w); - Q_ASSERT(m >= 0); - Q_ASSERT(m < period); - const qreal m_ms = 1000.0 * static_cast(m) / static_cast(freq.QuadPart); - Sleep(static_cast(qRound(m_ms))); - if (ptimeEndPeriod(ms_granularity) != /*TIMERR_NOERROR*/0) { - qWarning() << "timeEndPeriod() failed."; - break; - } - } - } + Utilities::syncWmPaintWithDwm(); // We cannot return WVR_REDRAW otherwise Windows exhibits bugs where // client pixels and child windows are mispositioned by the width/height // of the upper-left nonclient area. @@ -681,6 +612,9 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me Utilities::updateQtFrameMargins(const_cast(window), true); Utilities::triggerFrameChange(reinterpret_cast(msg->hwnd)); } break; + case WM_DWMCOMPOSITIONCHANGED: { + Utilities::updateFrameMargins(reinterpret_cast(msg->hwnd), false); + } break; default: break; } diff --git a/framelesshelper_windows.h b/framelesshelper_windows.h index a32e725..b907314 100644 --- a/framelesshelper_windows.h +++ b/framelesshelper_windows.h @@ -86,6 +86,10 @@ #define WM_NCUAHDRAWFRAME (0x00AF) #endif +#ifndef WM_DWMCOMPOSITIONCHANGED +#define WM_DWMCOMPOSITIONCHANGED (0x031E) +#endif + #ifndef WM_DWMCOLORIZATIONCOLORCHANGED #define WM_DWMCOLORIZATIONCOLORCHANGED (0x0320) #endif diff --git a/utilities.h b/utilities.h index 5cf4ed8..bba7d20 100644 --- a/utilities.h +++ b/utilities.h @@ -57,6 +57,7 @@ FRAMELESSHELPER_API void updateQtFrameMargins(QWindow *window, const bool enable [[nodiscard]] FRAMELESSHELPER_API QString getSystemErrorMessage(const QString &function); [[nodiscard]] FRAMELESSHELPER_API bool isFullScreen(const WId winId); [[nodiscard]] FRAMELESSHELPER_API bool isWindowNoState(const WId winId); +FRAMELESSHELPER_API void syncWmPaintWithDwm(); #endif } diff --git a/utilities_win32.cpp b/utilities_win32.cpp index aa3b7e4..096f46e 100644 --- a/utilities_win32.cpp +++ b/utilities_win32.cpp @@ -593,4 +593,80 @@ bool Utilities::isWindowNoState(const WId winId) return ((wp.showCmd == SW_NORMAL) || (wp.showCmd == SW_RESTORE)); } +void Utilities::syncWmPaintWithDwm() +{ + // No need to sync with DWM if DWM composition is disabled. + if (!isDwmCompositionAvailable()) { + return; + } + QSystemLibrary winmmLib(QStringLiteral("winmm")); + static const auto ptimeGetDevCaps = + reinterpret_cast(winmmLib.resolve("timeGetDevCaps")); + static const auto ptimeBeginPeriod = + reinterpret_cast(winmmLib.resolve("timeBeginPeriod")); + static const auto ptimeEndPeriod = + reinterpret_cast(winmmLib.resolve("timeEndPeriod")); + static const auto pDwmGetCompositionTimingInfo = + reinterpret_cast(QSystemLibrary::resolve(QStringLiteral("dwmapi"), "DwmGetCompositionTimingInfo")); + if (!ptimeGetDevCaps || !ptimeBeginPeriod || !ptimeEndPeriod || !pDwmGetCompositionTimingInfo) { + return; + } + // Dirty hack to workaround the resize flicker caused by DWM. + LARGE_INTEGER freq = {}; + if (QueryPerformanceFrequency(&freq) == FALSE) { + qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceFrequency")); + return; + } + flh_TIMECAPS tc = {}; + if (ptimeGetDevCaps(&tc, sizeof(tc)) != /*MMSYSERR_NOERROR*/0) { + qWarning() << "timeGetDevCaps() failed."; + return; + } + const UINT ms_granularity = tc.wPeriodMin; + if (ptimeBeginPeriod(ms_granularity) != /*TIMERR_NOERROR*/0) { + qWarning() << "timeBeginPeriod() failed."; + return; + } + LARGE_INTEGER now0 = {}; + if (QueryPerformanceCounter(&now0) == FALSE) { + qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceCounter")); + return; + } + // ask DWM where the vertical blank falls + DWM_TIMING_INFO dti; + SecureZeroMemory(&dti, sizeof(dti)); + dti.cbSize = sizeof(dti); + const HRESULT hr = pDwmGetCompositionTimingInfo(nullptr, &dti); + if (FAILED(hr)) { + qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("DwmGetCompositionTimingInfo")); + return; + } + LARGE_INTEGER now1 = {}; + if (QueryPerformanceCounter(&now1) == FALSE) { + qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceCounter")); + return; + } + // - DWM told us about SOME vertical blank + // - past or future, possibly many frames away + // - convert that into the NEXT vertical blank + const LONGLONG period = dti.qpcRefreshPeriod; + const LONGLONG dt = dti.qpcVBlank - now1.QuadPart; + LONGLONG w = 0, m = 0; + if (dt >= 0) { + w = dt / period; + } else { + // reach back to previous period + // - so m represents consistent position within phase + w = -1 + dt / period; + } + m = dt - (period * w); + Q_ASSERT(m >= 0); + Q_ASSERT(m < period); + const qreal m_ms = 1000.0 * static_cast(m) / static_cast(freq.QuadPart); + Sleep(static_cast(qRound(m_ms))); + if (ptimeEndPeriod(ms_granularity) != /*TIMERR_NOERROR*/0) { + qWarning() << "timeEndPeriod() failed."; + } +} + FRAMELESSHELPER_END_NAMESPACE