diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.cpp b/src/plugins/platforms/wayland/qwaylanddisplay.cpp index 02169e77..42d2aa0f 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay.cpp +++ b/src/plugins/platforms/wayland/qwaylanddisplay.cpp @@ -603,6 +603,11 @@ QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const return nullptr; } +bool QWaylandDisplay::isScreenAlive(QWaylandScreen *screen) const +{ + return mScreens.contains(screen) || mWaitingScreens.contains(screen); +} + void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen) { if (!mWaitingScreens.removeOne(screen)) diff --git a/src/plugins/platforms/wayland/qwaylanddisplay_p.h b/src/plugins/platforms/wayland/qwaylanddisplay_p.h index 29952886..379403ee 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay_p.h +++ b/src/plugins/platforms/wayland/qwaylanddisplay_p.h @@ -116,6 +116,7 @@ public: QPlatformPlaceholderScreen *placeholderScreen() const { return mPlaceholderScreen; } void ensureScreen(); + bool isScreenAlive(QWaylandScreen *screen) const; QWaylandScreen *screenForOutput(struct wl_output *output) const; void handleScreenInitialized(QWaylandScreen *screen); diff --git a/src/plugins/platforms/wayland/qwaylandsurface.cpp b/src/plugins/platforms/wayland/qwaylandsurface.cpp index 274fdda8..f630cec3 100644 --- a/src/plugins/platforms/wayland/qwaylandsurface.cpp +++ b/src/plugins/platforms/wayland/qwaylandsurface.cpp @@ -13,6 +13,7 @@ namespace QtWaylandClient { QWaylandSurface::QWaylandSurface(QWaylandDisplay *display) : wl_surface(display->createSurface(this)) + , m_display(display) { connect(qApp, &QGuiApplication::screenRemoved, this, &QWaylandSurface::handleScreenRemoved); connect(qApp, &QGuiApplication::screenAdded, this, &QWaylandSurface::screensChanged); @@ -25,6 +26,13 @@ QWaylandSurface::~QWaylandSurface() QWaylandScreen *QWaylandSurface::oldestEnteredScreen() { + // Prune stale screen pointers before iterating. Entries can go stale + // when a wl_output is removed while a waiting-state QWaylandScreen + // referencing it was in m_screens (direct deletion, no screenRemoved signal). + m_screens.removeIf([this](QWaylandScreen *s) { + return !m_display->isScreenAlive(s); + }); + for (auto *screen : std::as_const(m_screens)) { // only report valid screens // we can have some ouptuts waiting for xdg output information @@ -60,6 +68,14 @@ void QWaylandSurface::surface_enter(wl_output *output) if (!addedScreen) return; + // libwayland resolves wl_output proxy pointers at demarshal time. If a + // preceding event in the same dispatch batch destroyed the output's proxy + // (e.g. via wl_registry.global_remove), fromWlOutput may return a stale + // QWaylandScreen pointer from freed proxy memory. Validate against the + // display's live screen lists before dereferencing. + if (!m_display->isScreenAlive(addedScreen)) + return; + if (m_screens.contains(addedScreen)) { qCWarning(lcQpaWayland) << "Ignoring unexpected wl_surface.enter received for output with id:" @@ -80,6 +96,10 @@ void QWaylandSurface::surface_leave(wl_output *output) if (!removedScreen) return; + // See comment in surface_enter: the proxy may reference a dead screen. + if (!m_display->isScreenAlive(removedScreen)) + return; + bool wasRemoved = m_screens.removeOne(removedScreen); if (!wasRemoved) { qCWarning(lcQpaWayland) diff --git a/src/plugins/platforms/wayland/qwaylandsurface_p.h b/src/plugins/platforms/wayland/qwaylandsurface_p.h index 41860297..af59f0a3 100644 --- a/src/plugins/platforms/wayland/qwaylandsurface_p.h +++ b/src/plugins/platforms/wayland/qwaylandsurface_p.h @@ -57,6 +57,7 @@ protected: QList m_screens; //As seen by wl_surface.enter/leave events. Chronological order. QWaylandWindow *m_window = nullptr; + QWaylandDisplay *m_display = nullptr; std::optional m_preferredBufferScale; std::optional m_preferredBufferTransform;