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