From 3a31c708790ba2bb3cf3dab32a17a83659a1acde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 28 Jun 2017 13:56:47 +0200 Subject: [PATCH] Ensure all platforms send geometry events if request was not fulfilled The logic of deciding whether or not to send resize and move events has been centralized in QGuiApplication. This ensures that if a window with geometry 100,100+200x200 is moved and resized to e.g. 0,0+100x100, but the window manager denies the request (because the window would e.g. overlap with system UI), and issues a geometry update with the original geometry, 100,100+200x200, we will still treat that as warrant of a move/resize event to the application, so the application knows that its position and size is as before. [ChangeLog][Qt Gui][QPA] QWindowSystemInterfacePrivate::handleGeometryChange no longer takes the old geometry as an argument. Task-number: QTBUG-57608 Change-Id: I1d471cc7a257fef958bdb1e56184fa95489403a3 Reviewed-by: Simon Hausmann --- src/gui/kernel/qguiapplication.cpp | 43 ++++++++++++------- src/gui/kernel/qwindowsysteminterface.cpp | 15 ++++--- src/gui/kernel/qwindowsysteminterface.h | 2 +- src/gui/kernel/qwindowsysteminterface_p.h | 6 +-- .../platforms/eglfs/api/qeglfswindow.cpp | 23 +++++----- src/plugins/platforms/ios/quiview.mm | 15 ++----- src/plugins/platforms/xcb/qxcbwindow.cpp | 4 -- 7 files changed, 55 insertions(+), 53 deletions(-) diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 51653d9f8a0..a29ddbe44e3 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -2213,7 +2213,7 @@ void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterf } // we may have changed scaling, so trigger resize event if needed if (window->handle()) { - QWindowSystemInterfacePrivate::GeometryChangeEvent gce(window, QHighDpi::fromNativePixels(window->handle()->geometry(), window), QRect()); + QWindowSystemInterfacePrivate::GeometryChangeEvent gce(window, QHighDpi::fromNativePixels(window->handle()->geometry(), window)); processGeometryChangeEvent(&gce); } } @@ -2238,35 +2238,46 @@ void QGuiApplicationPrivate::processGeometryChangeEvent(QWindowSystemInterfacePr if (!window) return; - QRect newRect = e->newGeometry; - QRect oldRect = e->oldGeometry.isNull() ? window->d_func()->geometry : e->oldGeometry; + const QRect lastReportedGeometry = window->d_func()->geometry; + const QRect requestedGeometry = e->requestedGeometry; + const QRect actualGeometry = e->newGeometry; - bool isResize = oldRect.size() != newRect.size(); - bool isMove = oldRect.topLeft() != newRect.topLeft(); + // We send size and move events only if the geometry has changed from + // what was last reported, or if the user tried to set a new geometry, + // but the window manager responded by keeping the old geometry. In the + // latter case we send move/resize events with the same geometry as the + // last reported geometry, to indicate that the window wasn't moved or + // resized. Note that this logic does not apply to the property changes + // of the window, as we don't treat them as part of this request/response + // protocol of QWindow/QPA. + const bool isResize = actualGeometry.size() != lastReportedGeometry.size() + || requestedGeometry.size() != actualGeometry.size(); + const bool isMove = actualGeometry.topLeft() != lastReportedGeometry.topLeft() + || requestedGeometry.topLeft() != actualGeometry.topLeft(); - window->d_func()->geometry = newRect; + window->d_func()->geometry = actualGeometry; if (isResize || window->d_func()->resizeEventPending) { - QResizeEvent e(newRect.size(), oldRect.size()); + QResizeEvent e(actualGeometry.size(), lastReportedGeometry.size()); QGuiApplication::sendSpontaneousEvent(window, &e); window->d_func()->resizeEventPending = false; - if (oldRect.width() != newRect.width()) - window->widthChanged(newRect.width()); - if (oldRect.height() != newRect.height()) - window->heightChanged(newRect.height()); + if (actualGeometry.width() != lastReportedGeometry.width()) + window->widthChanged(actualGeometry.width()); + if (actualGeometry.height() != lastReportedGeometry.height()) + window->heightChanged(actualGeometry.height()); } if (isMove) { //### frame geometry - QMoveEvent e(newRect.topLeft(), oldRect.topLeft()); + QMoveEvent e(actualGeometry.topLeft(), lastReportedGeometry.topLeft()); QGuiApplication::sendSpontaneousEvent(window, &e); - if (oldRect.x() != newRect.x()) - window->xChanged(newRect.x()); - if (oldRect.y() != newRect.y()) - window->yChanged(newRect.y()); + if (actualGeometry.x() != lastReportedGeometry.x()) + window->xChanged(actualGeometry.x()); + if (actualGeometry.y() != lastReportedGeometry.y()) + window->yChanged(actualGeometry.y()); } } diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp index 0be130a40d2..233c27dee74 100644 --- a/src/gui/kernel/qwindowsysteminterface.cpp +++ b/src/gui/kernel/qwindowsysteminterface.cpp @@ -273,13 +273,18 @@ void QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } -/*! - If \a oldRect is null, Qt will use the previously reported geometry instead. - */ -QT_DEFINE_QPA_EVENT_HANDLER(void, handleGeometryChange, QWindow *window, const QRect &newRect, const QRect &oldRect) +QWindowSystemInterfacePrivate::GeometryChangeEvent::GeometryChangeEvent(QWindow *window, const QRect &newGeometry) + : WindowSystemEvent(GeometryChange) + , window(window) + , requestedGeometry(window->handle() ? window->handle()->QPlatformWindow::geometry() : QRect()) + , newGeometry(newGeometry) +{ +} + +QT_DEFINE_QPA_EVENT_HANDLER(void, handleGeometryChange, QWindow *window, const QRect &newRect) { Q_ASSERT(window); - QWindowSystemInterfacePrivate::GeometryChangeEvent *e = new QWindowSystemInterfacePrivate::GeometryChangeEvent(window, QHighDpi::fromNativePixels(newRect, window), QHighDpi::fromNativePixels(oldRect, window)); + QWindowSystemInterfacePrivate::GeometryChangeEvent *e = new QWindowSystemInterfacePrivate::GeometryChangeEvent(window, QHighDpi::fromNativePixels(newRect, window)); if (window->handle()) { // Persist the new geometry so that QWindow::geometry() can be queried in the resize event window->handle()->QPlatformWindow::setGeometry(newRect); diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h index 73ef612a06b..7ea7b072f05 100644 --- a/src/gui/kernel/qwindowsysteminterface.h +++ b/src/gui/kernel/qwindowsysteminterface.h @@ -160,7 +160,7 @@ public: // rect is relative to parent template - static void handleGeometryChange(QWindow *window, const QRect &newRect, const QRect &oldRect = QRect()); + static void handleGeometryChange(QWindow *window, const QRect &newRect); // region is in local coordinates, do not confuse with geometry which is parent-relative template diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h index 55aafc4d155..ef993501f8d 100644 --- a/src/gui/kernel/qwindowsysteminterface_p.h +++ b/src/gui/kernel/qwindowsysteminterface_p.h @@ -131,12 +131,10 @@ public: class GeometryChangeEvent : public WindowSystemEvent { public: - GeometryChangeEvent(QWindow *window, const QRect &newGeometry, const QRect &oldGeometry) - : WindowSystemEvent(GeometryChange), window(window), newGeometry(newGeometry), oldGeometry(oldGeometry) - { } + GeometryChangeEvent(QWindow *window, const QRect &newGeometry); QPointer window; + QRect requestedGeometry; QRect newGeometry; - QRect oldGeometry; }; class EnterEvent : public WindowSystemEvent { diff --git a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp index 69582a91d31..6aaf0d6d6da 100644 --- a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #ifndef QT_NO_OPENGL # include # include @@ -234,21 +235,19 @@ void QEglFSWindow::setVisible(bool visible) void QEglFSWindow::setGeometry(const QRect &r) { - QRect rect; - bool forceFullscreen = m_flags.testFlag(HasNativeWindow); - if (forceFullscreen) + // Persist the requested rect, like a normal setGeometry call + QPlatformWindow::setGeometry(r); + + // Take care of WM behavior, constrain/modify geometry + QRect rect = r; + if (m_flags.testFlag(HasNativeWindow)) rect = screen()->availableGeometry(); - else - rect = r; - const bool changed = rect != QPlatformWindow::geometry(); - QPlatformWindow::setGeometry(rect); + // React to the setGeometry, as if from a WM callback + QRect lastReportedGeometry = qt_window_private(window())->geometry; + QWindowSystemInterface::handleGeometryChange(window(), rect); - // if we corrected the size, trigger a resize event - if (rect != r) - QWindowSystemInterface::handleGeometryChange(window(), rect, r); - - if (changed) + if (rect != lastReportedGeometry) QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size())); } diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm index a1110839ad1..dcdb5ae53f5 100644 --- a/src/plugins/platforms/ios/quiview.mm +++ b/src/plugins/platforms/ios/quiview.mm @@ -151,19 +151,12 @@ qWarning() << m_qioswindow->window() << "is backed by a UIView that has a transform set. This is not supported."; - // The original geometry requested by setGeometry() might be different - // from what we end up with after applying window constraints. - QRect requestedGeometry = m_qioswindow->geometry(); - - QRect actualGeometry = QRectF::fromCGRect(self.frame).toRect(); - - QRect previousGeometry = requestedGeometry != actualGeometry ? - requestedGeometry : qt_window_private(m_qioswindow->window())->geometry; - QWindow *window = m_qioswindow->window(); - QWindowSystemInterface::handleGeometryChange(window, actualGeometry, previousGeometry); + QRect lastReportedGeometry = qt_window_private(window)->geometry; + QRect currentGeometry = QRectF::fromCGRect(self.frame).toRect(); + QWindowSystemInterface::handleGeometryChange(window, currentGeometry); - if (actualGeometry.size() != previousGeometry.size()) { + if (currentGeometry.size() != lastReportedGeometry.size()) { // Trigger expose event on resize [self setNeedsDisplay]; diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index dc258a83f96..74c8f45b366 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -2077,10 +2077,6 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * if (!newScreen) return; - // FIXME: In the case of the requestedGeometry not matching the actualGeometry due - // to e.g. the window manager applying restrictions to the geometry, the application - // will never see a move/resize event if the actualGeometry is the same as the current - // geometry, and may think the requested geometry was fulfilled. QWindowSystemInterface::handleGeometryChange(window(), actualGeometry); // QPlatformScreen::screen() is updated asynchronously, so we can't compare it