From 7eed1e40d4d3b6a066bac52995eed7e75d17de2d Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 8 May 2019 13:36:39 +0200 Subject: Windows QPA: Fix resize loops when moving fixed size windows between screens Postpone the screen change until the DPI changed event in case a move between screens with different DPI is detected. Task-number: QTBUG-65580 Change-Id: I356f144b243d7d1ce7feabf0434c3f534b903965 Reviewed-by: Oliver Wolff Reviewed-by: Andre de la Rocha --- src/plugins/platforms/windows/qwindowscontext.cpp | 29 ++++++++++++------- src/plugins/platforms/windows/qwindowswindow.cpp | 35 ++++++++++++++++------- src/plugins/platforms/windows/qwindowswindow.h | 3 +- 3 files changed, 44 insertions(+), 23 deletions(-) (limited to 'src/plugins/platforms/windows') diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 8d1ef9f34a..281f18af5b 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -1322,17 +1322,24 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, #endif } break; case QtWindows::DpiChangedEvent: { - if (!resizeOnDpiChanged(platformWindow->window())) - return false; - platformWindow->setFlag(QWindowsWindow::WithinDpiChanged); - const RECT *prcNewWindow = reinterpret_cast(lParam); - qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED" - << platformWindow->window() << *prcNewWindow; - SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, - prcNewWindow->right - prcNewWindow->left, - prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); - platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged); - return true; + // Try to apply the suggested size first and then notify ScreenChanged + // so that the resize event sent from QGuiApplication incorporates it + // WM_DPICHANGED is sent with a size that avoids resize loops (by + // snapping back to the previous screen, see QTBUG-65580). + const bool doResize = resizeOnDpiChanged(platformWindow->window()); + if (doResize) { + platformWindow->setFlag(QWindowsWindow::WithinDpiChanged); + platformWindow->updateFullFrameMargins(); + const auto prcNewWindow = reinterpret_cast(lParam); + qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED" + << platformWindow->window() << *prcNewWindow; + SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, + prcNewWindow->right - prcNewWindow->left, + prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); + platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged); + } + platformWindow->checkForScreenChanged(QWindowsWindow::FromDpiChange); + return doResize; } #if QT_CONFIG(sessionmanager) case QtWindows::QueryEndSessionApplicationEvent: { diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 841d3dccdc..3bf424d0ac 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1861,28 +1861,41 @@ void QWindowsWindow::handleResized(int wParam) } } -void QWindowsWindow::checkForScreenChanged() +static inline bool equalDpi(const QDpi &d1, const QDpi &d2) { - if (parent()) + return qFuzzyCompare(d1.first, d2.first) && qFuzzyCompare(d1.second, d2.second); +} + +void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode) +{ + if (parent() || QWindowsScreenManager::isSingleScreen()) return; QPlatformScreen *currentScreen = screen(); - const auto &screenManager = QWindowsContext::instance()->screenManager(); - const QWindowsScreen *newScreen = screenManager.screenForHwnd(m_data.hwnd); - if (newScreen != nullptr && newScreen != currentScreen) { - qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ - << ' ' << window() << " \"" << currentScreen->name() - << "\"->\"" << newScreen->name() << '"'; - updateFullFrameMargins(); - setFlag(SynchronousGeometryChangeEvent); - QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); + const QWindowsScreen *newScreen = + QWindowsContext::instance()->screenManager().screenForHwnd(m_data.hwnd); + if (newScreen == nullptr || newScreen == currentScreen) + return; + // For screens with different DPI: postpone until WM_DPICHANGE + if (mode == FromGeometryChange + && !equalDpi(currentScreen->logicalDpi(), newScreen->logicalDpi())) { + return; } + qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ + << ' ' << window() << " \"" << currentScreen->name() + << "\"->\"" << newScreen->name() << '"'; + if (mode == FromGeometryChange) + setFlag(SynchronousGeometryChangeEvent); + updateFullFrameMargins(); + QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); } void QWindowsWindow::handleGeometryChange() { const QRect previousGeometry = m_data.geometry; m_data.geometry = geometry_sys(); + if (testFlag(WithinDpiChanged)) + return; // QGuiApplication will send resize QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive // expose events when shrinking, synthesize. diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 5e44511e5d..ce67e46df3 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -334,7 +334,8 @@ public: void alertWindow(int durationMs = 0); void stopAlertWindow(); - void checkForScreenChanged(); + enum ScreenChangeMode { FromGeometryChange, FromDpiChange }; + void checkForScreenChanged(ScreenChangeMode mode = FromGeometryChange); static void setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes); void registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes = QWindowsWindowFunctions::NormalTouch); -- cgit v1.2.3