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 <oliver.wolff@qt.io>
Reviewed-by: Andre de la Rocha <andre.rocha@qt.io>
Friedemann Kleint 2019-05-08 13:36:39 +02:00
parent d2fd9b1b98
commit 7eed1e40d4
3 changed files with 44 additions and 23 deletions

View File

@ -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<RECT *>(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<RECT *>(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: {

View File

@ -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.

View File

@ -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);