Introduce events for Window device pixel ratio changes

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ø <tor.arne.vestbo@qt.io>
This commit is contained in:
David Edmundson 2022-12-05 22:15:01 +00:00
parent b7046ec2ac
commit ab449c205c
13 changed files with 76 additions and 8 deletions

View File

@ -85,6 +85,7 @@ Q_TRACE_POINT(qtcore, QEvent_dtor, QEvent *event, QEvent::Type type);
\value ContextMenu Context popup menu (QContextMenuEvent). \value ContextMenu Context popup menu (QContextMenuEvent).
\value CursorChange The widget's cursor has changed. \value CursorChange The widget's cursor has changed.
\value DeferredDelete The object will be deleted after it has cleaned up (QDeferredDeleteEvent) \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 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 DragLeave The cursor leaves a widget during a drag and drop operation (QDragLeaveEvent).
\value DragMove A drag and drop operation is in progress (QDragMoveEvent). \value DragMove A drag and drop operation is in progress (QDragMoveEvent).

View File

@ -284,6 +284,8 @@ public:
// GraphicsSceneLeave = 220, // GraphicsSceneLeave = 220,
WindowAboutToChangeInternal = 221, // internal for QQuickWidget and texture-based widgets WindowAboutToChangeInternal = 221, // internal for QQuickWidget and texture-based widgets
DevicePixelRatioChange = 222,
// 512 reserved for Qt Jambi's MetaCall event // 512 reserved for Qt Jambi's MetaCall event
// 513 reserved for Qt Jambi's DeleteOnMainThread event // 513 reserved for Qt Jambi's DeleteOnMainThread event

View File

@ -2052,6 +2052,9 @@ void Q_TRACE_INSTRUMENT(qtgui) QGuiApplicationPrivate::processWindowSystemEvent(
case QWindowSystemInterfacePrivate::WindowScreenChanged: case QWindowSystemInterfacePrivate::WindowScreenChanged:
QGuiApplicationPrivate::processWindowScreenChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowScreenChangedEvent *>(e)); QGuiApplicationPrivate::processWindowScreenChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowScreenChangedEvent *>(e));
break; break;
case QWindowSystemInterfacePrivate::WindowDevicePixelRatioChanged:
QGuiApplicationPrivate::processWindowDevicePixelRatioChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *>(e));
break;
case QWindowSystemInterfacePrivate::SafeAreaMarginsChanged: case QWindowSystemInterfacePrivate::SafeAreaMarginsChanged:
QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(static_cast<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *>(e)); QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(static_cast<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *>(e));
break; break;
@ -2555,6 +2558,9 @@ void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterf
if (QWindow *window = wse->window.data()) { if (QWindow *window = wse->window.data()) {
if (window->screen() == wse->screen.data()) if (window->screen() == wse->screen.data())
return; return;
const qreal oldDevicePixelRatio = window->screen() ? window->screen()->devicePixelRatio() : 1.0;
if (QWindow *topLevelWindow = window->d_func()->topLevelWindow(QWindow::ExcludeTransients)) { if (QWindow *topLevelWindow = window->d_func()->topLevelWindow(QWindow::ExcludeTransients)) {
if (QScreen *screen = wse->screen.data()) if (QScreen *screen = wse->screen.data())
topLevelWindow->d_func()->setTopLevelScreen(screen, false /* recreate */); topLevelWindow->d_func()->setTopLevelScreen(screen, false /* recreate */);
@ -2571,7 +2577,22 @@ void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterf
processGeometryChangeEvent(&gce); processGeometryChangeEvent(&gce);
} }
#endif #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) void QGuiApplicationPrivate::processSafeAreaMarginsChangedEvent(QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *wse)

View File

@ -116,6 +116,7 @@ public:
static void processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e); static void processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e);
static void processWindowStateChangedEvent(QWindowSystemInterfacePrivate::WindowStateChangedEvent *e); static void processWindowStateChangedEvent(QWindowSystemInterfacePrivate::WindowStateChangedEvent *e);
static void processWindowScreenChangedEvent(QWindowSystemInterfacePrivate::WindowScreenChangedEvent *e); static void processWindowScreenChangedEvent(QWindowSystemInterfacePrivate::WindowScreenChangedEvent *e);
static void processWindowDevicePixelRatioChangedEvent(QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent *e);
static void processSafeAreaMarginsChangedEvent(QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *e); static void processSafeAreaMarginsChangedEvent(QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent *e);

View File

@ -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 // the platformWindow, if there was one, is now gone, so make this flag reflect reality now
updateRequestPending = false; updateRequestPending = false;
const qreal currentDevicePixelRatio = q->devicePixelRatio();
if (q->parent()) if (q->parent())
q->parent()->create(); q->parent()->create();
@ -550,6 +552,11 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle)
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated); QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
QGuiApplication::sendEvent(q, &e); QGuiApplication::sendEvent(q, &e);
if (!qFuzzyCompare(currentDevicePixelRatio, q->devicePixelRatio())) {
QEvent dprChangeEvent(QEvent::DevicePixelRatioChange);
QGuiApplication::sendEvent(q, &dprChangeEvent);
}
if (needsUpdate) if (needsUpdate)
q->requestUpdate(); q->requestUpdate();
} }

View File

@ -259,6 +259,12 @@ QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowScreenChanged, QWindow *window, QS
handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowScreenChangedEvent, Delivery>(window, screen); handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowScreenChangedEvent, Delivery>(window, screen);
} }
QT_DEFINE_QPA_EVENT_HANDLER(void, handleWindowDevicePixelRatioChanged, QWindow *window)
{
handleWindowSystemEvent<QWindowSystemInterfacePrivate::WindowDevicePixelRatioChangedEvent, Delivery>(window);
}
QT_DEFINE_QPA_EVENT_HANDLER(void, handleSafeAreaMarginsChanged, QWindow *window) QT_DEFINE_QPA_EVENT_HANDLER(void, handleSafeAreaMarginsChanged, QWindow *window)
{ {
handleWindowSystemEvent<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent, Delivery>(window); handleWindowSystemEvent<QWindowSystemInterfacePrivate::SafeAreaMarginsChangedEvent, Delivery>(window);

View File

@ -190,6 +190,9 @@ public:
template<typename Delivery = QWindowSystemInterface::DefaultDelivery> template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleWindowScreenChanged(QWindow *window, QScreen *newScreen); static void handleWindowScreenChanged(QWindow *window, QScreen *newScreen);
template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleWindowDevicePixelRatioChanged(QWindow *window);
template<typename Delivery = QWindowSystemInterface::DefaultDelivery> template<typename Delivery = QWindowSystemInterface::DefaultDelivery>
static void handleSafeAreaMarginsChanged(QWindow *window); static void handleSafeAreaMarginsChanged(QWindow *window);

View File

@ -68,7 +68,8 @@ public:
WindowScreenChanged = 0x21, WindowScreenChanged = 0x21,
SafeAreaMarginsChanged = 0x22, SafeAreaMarginsChanged = 0x22,
ApplicationTermination = 0x23, ApplicationTermination = 0x23,
Paint = 0x24 Paint = 0x24,
WindowDevicePixelRatioChanged = 0x25,
}; };
class WindowSystemEvent { class WindowSystemEvent {
@ -154,6 +155,15 @@ public:
QPointer<QScreen> screen; QPointer<QScreen> screen;
}; };
class WindowDevicePixelRatioChangedEvent : public WindowSystemEvent {
public:
WindowDevicePixelRatioChangedEvent(QWindow *w)
: WindowSystemEvent(WindowDevicePixelRatioChanged), window(w)
{ }
QPointer<QWindow> window;
};
class SafeAreaMarginsChangedEvent : public WindowSystemEvent { class SafeAreaMarginsChangedEvent : public WindowSystemEvent {
public: public:
SafeAreaMarginsChangedEvent(QWindow *w) SafeAreaMarginsChangedEvent(QWindow *w)

View File

@ -1690,7 +1690,7 @@ bool QOpenGLWidget::event(QEvent *e)
} }
} }
break; break;
case QEvent::ScreenChangeInternal: case QEvent::DevicePixelRatioChange:
if (d->initialized && d->paintDevice->devicePixelRatio() != devicePixelRatio()) if (d->initialized && d->paintDevice->devicePixelRatio() != devicePixelRatio())
d->recreateFbos(); d->recreateFbos();
break; break;

View File

@ -9318,6 +9318,8 @@ bool QWidget::event(QEvent *event)
const QWindow *win = te->window; const QWindow *win = te->window;
d->setWinId((win && win->handle()) ? win->handle()->winId() : 0); d->setWinId((win && win->handle()) ? win->handle()->winId() : 0);
} }
break;
case QEvent::DevicePixelRatioChange:
if (d->data.fnt.d->dpi != logicalDpiY()) if (d->data.fnt.d->dpi != logicalDpiY())
d->updateFont(d->data.fnt); d->updateFont(d->data.fnt);
d->renderToTextureReallyDirty = 1; d->renderToTextureReallyDirty = 1;

View File

@ -329,6 +329,10 @@ bool QWidgetWindow::event(QEvent *event)
m_widget->repaint(); m_widget->repaint();
return true; return true;
case QEvent::DevicePixelRatioChange:
handleDevicePixelRatioChange();
break;
default: default:
break; break;
} }
@ -695,22 +699,32 @@ void QWidgetWindow::updateMargins()
m_widget->data->fstrut_dirty = false; 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); QCoreApplication::sendEvent(widget, &e);
QWidgetPrivate *d = QWidgetPrivate::get(widget); QWidgetPrivate *d = QWidgetPrivate::get(widget);
for (int i = 0; i < d->children.size(); ++i) { for (int i = 0; i < d->children.size(); ++i) {
QWidget *w = qobject_cast<QWidget *>(d->children.at(i)); QWidget *w = qobject_cast<QWidget *>(d->children.at(i));
if (w) if (w)
sendScreenChangeRecursively(w); sendChangeRecursively(w, type);
} }
} }
void QWidgetWindow::handleScreenChange() void QWidgetWindow::handleScreenChange()
{ {
// Send an event recursively to the widget and its children. // 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. // Invalidate the backing store buffer and repaint immediately.
if (screen()) if (screen())

View File

@ -81,9 +81,10 @@ protected:
private slots: private slots:
void updateObjectName(); void updateObjectName();
void handleScreenChange();
private: private:
void handleScreenChange();
void handleDevicePixelRatioChange();
void repaintWindow(); void repaintWindow();
bool updateSize(); bool updateSize();
void updateMargins(); void updateMargins();

View File

@ -119,7 +119,7 @@ bool QDockWidgetTitleButton::event(QEvent *event)
{ {
switch (event->type()) { switch (event->type()) {
case QEvent::StyleChange: case QEvent::StyleChange:
case QEvent::ScreenChangeInternal: case QEvent::DevicePixelRatioChange:
m_iconSize = -1; m_iconSize = -1;
break; break;
default: default: