Patch-Source: https://github.com/qt/qtbase/commit/ab449c205cb656b32e8d1b8e036783f18704ff8e https://codereview.qt-project.org/c/qt/qtbase/+/447074 https://bugreports.qt.io/browse/QTBUG-93380 -- From ab449c205cb656b32e8d1b8e036783f18704ff8e Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Mon, 5 Dec 2022 22:15:01 +0000 Subject: [PATCH] Introduce events for Window device pixel ratio changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a mix between screen device pixel ratio. Currently we store the property on a per-window basis, but the change notifications are still on a per screen basis which can fall apart on edge cases. On wayland we are getting per window DPR changes without as useful screen change events so it's important to fix. It also has potential to clean up the Windows backend in the future where the backend is currently papering over the two concepts. This patch introduces two new events: A QWindowSystemInterface to trigger a window DPR change independently of a screen change. An event to notify windows the new DPR rather than needing to track signals on the screen. This happens either when the window dpr changes or implicitly through a screen change. This can deprecate an existing event ScreenChangeInternal so the value is reused and renamed for clarity. Change-Id: I637a07fd4520ba3184ccc2c987c29d8d23a65ad3 Reviewed-by: Tor Arne Vestbø --- src/corelib/kernel/qcoreevent.cpp | 1 + src/corelib/kernel/qcoreevent.h | 2 ++ src/gui/kernel/qguiapplication.cpp | 21 +++++++++++++++++++++ src/gui/kernel/qguiapplication_p.h | 1 + src/gui/kernel/qwindow.cpp | 7 +++++++ src/gui/kernel/qwindowsysteminterface.cpp | 6 ++++++ src/gui/kernel/qwindowsysteminterface.h | 3 +++ src/gui/kernel/qwindowsysteminterface_p.h | 12 +++++++++++- src/openglwidgets/qopenglwidget.cpp | 2 +- src/widgets/kernel/qwidget.cpp | 2 ++ src/widgets/kernel/qwidgetwindow.cpp | 22 ++++++++++++++++++---- src/widgets/kernel/qwidgetwindow_p.h | 3 ++- src/widgets/widgets/qdockwidget.cpp | 2 +- 13 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index 0f4af06363d..b01f1c2a699 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -85,6 +85,7 @@ Q_TRACE_POINT(qtcore, QEvent_dtor, QEvent *event, QEvent::Type type); \value ContextMenu Context popup menu (QContextMenuEvent). \value CursorChange The widget's cursor has changed. \value DeferredDelete The object will be deleted after it has cleaned up (QDeferredDeleteEvent) + \value DevicePixelRatioChange The devicePixelRatio has changed for this widget's or window's underlying backing store \value DragEnter The cursor enters a widget during a drag and drop operation (QDragEnterEvent). \value DragLeave The cursor leaves a widget during a drag and drop operation (QDragLeaveEvent). \value DragMove A drag and drop operation is in progress (QDragMoveEvent). diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index 536c5ab5582..0c83919bc50 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -284,6 +284,8 @@ class Q_CORE_EXPORT QEvent // event base class // GraphicsSceneLeave = 220, WindowAboutToChangeInternal = 221, // internal for QQuickWidget and texture-based widgets + DevicePixelRatioChange = 222, + // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index f37095053e4..ca2d2155415 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -2052,6 +2052,9 @@ void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::processWindowSystemEvent( case QWindowSystemInterfacePrivate::WindowScreenChanged: QGuiApplicationPrivate::processWindowScreenChangedEvent(static_cast(e)); break; + case QWindowSystemInterfacePrivate::WindowDevicePixelRatioChanged: + QGuiApplicationPrivate::processWindowDevicePixelRatioChangedEvent(static_cast(e)); + break; case QWindowSystemInterfacePrivate::SafeAreaMarginsChanged: QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(static_cast(e)); break; @@ -2555,6 +2558,9 @@ void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterf if (QWindow *window = wse->window.data()) { if (window->screen() == wse->screen.data()) return; + + const qreal oldDevicePixelRatio = window->screen() ? window->screen()->devicePixelRatio() : 1.0; + if (QWindow *topLevelWindow = window->d_func()->topLevelWindow(QWindow::ExcludeTransients)) { if (QScreen *screen = wse->screen.data()) topLevelWindow->d_func()->setTopLevelScreen(screen, false /* recreate */); @@ -2571,9 +2577,24 @@ void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterf processGeometryChangeEvent(&gce); } #endif + + const qreal newDevicePixelRatio = window->screen() ? window->screen()->devicePixelRatio() : 1.0; + if (!qFuzzyCompare(oldDevicePixelRatio, newDevicePixelRatio)) { + QEvent dprChangeEvent(QEvent::DevicePixelRatioChange); + QGuiApplication::sendSpontaneousEvent(window, &dprChangeEvent); + } } } +void QGuiApplicationPrivate::processWindowDevicePixelRatioChangedEvent(QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *wde) +{ + if (wde->window.isNull()) + return; + + QEvent dprChangeEvent(QEvent::DevicePixelRatioChange); + QGuiApplication::sendSpontaneousEvent(wde->window, &dprChangeEvent); +} + void QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *wse) { if (wse->window.isNull()) diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 47228a0738b..433cf1a0886 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -116,6 +116,7 @@ class Q_GUI_EXPORT QGuiApplicationPrivate : public QCoreApplicationPrivate static void processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e); static void processWindowStateChangedEvent(QWindowSystemInterfacePrivate::WindowStateChangedEvent *e); static void processWindowScreenChangedEvent(QWindowSystemInterfacePrivate::WindowScreenChangedEvent *e); + static void processWindowDevicePixelRatioChangedEvent(QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *e); static void processSafeAreaMarginsChangedEvent(QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *e); diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 1e368914f36..5a0fc79f30a 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -505,6 +505,8 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle) // the platformWindow, if there was one, is now gone, so make this flag reflect reality now updateRequestPending = false; + const qreal currentDevicePixelRatio = q->devicePixelRatio(); + if (q->parent()) q->parent()->create(); @@ -550,6 +552,11 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle) QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated); QGuiApplication::sendEvent(q, &e); + if (!qFuzzyCompare(currentDevicePixelRatio, q->devicePixelRatio())) { + QEvent dprChangeEvent(QEvent::DevicePixelRatioChange); + QGuiApplication::sendEvent(q, &dprChangeEvent); + } + if (needsUpdate) q->requestUpdate(); } diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp index ab07002191a..00343fef665 100644 --- a/src/gui/kernel/qwindowsysteminterface.cpp +++ b/src/gui/kernel/qwindowsysteminterface.cpp @@ -259,6 +259,12 @@ QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowScreenChanged, QWindow *window, QS handleWindowSystemEvent(window, screen); } +QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowDevicePixelRatioChanged, QWindow *window) +{ + handleWindowSystemEvent(window); +} + + QT_DEFINE_QPA_EVENT_HANDLER(void, handleSafeAreaMarginsChanged, QWindow *window) { handleWindowSystemEvent(window); diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h index 004019ae2de..37b06b0b040 100644 --- a/src/gui/kernel/qwindowsysteminterface.h +++ b/src/gui/kernel/qwindowsysteminterface.h @@ -190,6 +190,9 @@ class Q_GUI_EXPORT QWindowSystemInterface template static void handleWindowScreenChanged(QWindow *window, QScreen *newScreen); + template + static void handleWindowDevicePixelRatioChanged(QWindow *window); + template static void handleSafeAreaMarginsChanged(QWindow *window); diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h index 27aa7dbc570..3569667325e 100644 --- a/src/gui/kernel/qwindowsysteminterface_p.h +++ b/src/gui/kernel/qwindowsysteminterface_p.h @@ -68,7 +68,8 @@ class Q_GUI_EXPORT QWindowSystemInterfacePrivate { WindowScreenChanged = 0x21, SafeAreaMarginsChanged = 0x22, ApplicationTermination = 0x23, - Paint = 0x24 + Paint = 0x24, + WindowDevicePixelRatioChanged = 0x25, }; class WindowSystemEvent { @@ -154,6 +155,15 @@ class Q_GUI_EXPORT QWindowSystemInterfacePrivate { QPointer screen; }; + class WindowDevicePixelRatioChangedEvent : public WindowSystemEvent { + public: + WindowDevicePixelRatioChangedEvent(QWindow *w) + : WindowSystemEvent(WindowDevicePixelRatioChanged), window(w) + { } + + QPointer window; + }; + class SafeAreaMarginsChangedEvent : public WindowSystemEvent { public: SafeAreaMarginsChangedEvent(QWindow *w) diff --git a/src/openglwidgets/qopenglwidget.cpp b/src/openglwidgets/qopenglwidget.cpp index 4d8c181c226..68fabc7f14c 100644 --- a/src/openglwidgets/qopenglwidget.cpp +++ b/src/openglwidgets/qopenglwidget.cpp @@ -1690,7 +1690,7 @@ bool QOpenGLWidget::event(QEvent *e) } } break; - case QEvent::ScreenChangeInternal: + case QEvent::DevicePixelRatioChange: if (d->initialized && d->paintDevice->devicePixelRatio() != devicePixelRatio()) d->recreateFbos(); break; diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 4da80073a82..3ca70d56e5a 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -9318,6 +9318,8 @@ bool QWidget::event(QEvent *event) const QWindow *win = te->window; d->setWinId((win && win->handle()) ? win->handle()->winId() : 0); } + break; + case QEvent::DevicePixelRatioChange: if (d->data.fnt.d->dpi != logicalDpiY()) d->updateFont(d->data.fnt); d->renderToTextureReallyDirty = 1; diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 7a3c38bb8d1..b6a6acfc927 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -329,6 +329,10 @@ bool QWidgetWindow::event(QEvent *event) m_widget->repaint(); return true; + case QEvent::DevicePixelRatioChange: + handleDevicePixelRatioChange(); + break; + default: break; } @@ -695,22 +699,32 @@ void QWidgetWindow::updateMargins() m_widget->data->fstrut_dirty = false; } -static void sendScreenChangeRecursively(QWidget *widget) +static void sendChangeRecursively(QWidget *widget, QEvent::Type type) { - QEvent e(QEvent::ScreenChangeInternal); + QEvent e(type); QCoreApplication::sendEvent(widget, &e); QWidgetPrivate *d = QWidgetPrivate::get(widget); for (int i = 0; i < d->children.size(); ++i) { QWidget *w = qobject_cast(d->children.at(i)); if (w) - sendScreenChangeRecursively(w); + sendChangeRecursively(w, type); } } void QWidgetWindow::handleScreenChange() { // Send an event recursively to the widget and its children. - sendScreenChangeRecursively(m_widget); + sendChangeRecursively(m_widget, QEvent::ScreenChangeInternal); + + // Invalidate the backing store buffer and repaint immediately. + if (screen()) + repaintWindow(); +} + +void QWidgetWindow::handleDevicePixelRatioChange() +{ + // Send an event recursively to the widget and its children. + sendChangeRecursively(m_widget, QEvent::DevicePixelRatioChange); // Invalidate the backing store buffer and repaint immediately. if (screen()) diff --git a/src/widgets/kernel/qwidgetwindow_p.h b/src/widgets/kernel/qwidgetwindow_p.h index 92f1a8b6d47..0eee3cd900e 100644 --- a/src/widgets/kernel/qwidgetwindow_p.h +++ b/src/widgets/kernel/qwidgetwindow_p.h @@ -81,9 +81,10 @@ class QWidgetWindow : public QWindow private slots: void updateObjectName(); - void handleScreenChange(); private: + void handleScreenChange(); + void handleDevicePixelRatioChange(); void repaintWindow(); bool updateSize(); void updateMargins(); diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index 67bb0994464..375bd3daaee 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -119,7 +119,7 @@ bool QDockWidgetTitleButton::event(QEvent *event) { switch (event->type()) { case QEvent::StyleChange: - case QEvent::ScreenChangeInternal: + case QEvent::DevicePixelRatioChange: m_iconSize = -1; break; default: