diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.cpp b/src/plugins/platforms/wayland/qwaylanddisplay.cpp index 02169e77..43ab00fa 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay.cpp +++ b/src/plugins/platforms/wayland/qwaylanddisplay.cpp @@ -423,8 +423,10 @@ void QWaylandDisplay::reconnect() m_eventThread->wait(); m_frameEventQueueThread->wait(); - qDeleteAll(mWaitingScreens); - mWaitingScreens.clear(); + for (auto *screen : std::exchange(mWaitingScreens, {})) { + forgetScreenForSurfaces(screen); + delete screen; + } while (!mScreens.isEmpty()) { auto screen = mScreens.takeLast(); @@ -603,6 +605,27 @@ QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const return nullptr; } +void QWaylandDisplay::registerSurface(QWaylandSurface *surface) +{ + mSurfaces.append(surface); +} + +void QWaylandDisplay::unregisterSurface(QWaylandSurface *surface) +{ + mSurfaces.removeOne(surface); +} + +bool QWaylandDisplay::isScreenAlive(QWaylandScreen *screen) const +{ + return mScreens.contains(screen) || mWaitingScreens.contains(screen); +} + +void QWaylandDisplay::forgetScreenForSurfaces(QWaylandScreen *screen) +{ + for (auto *surface : std::as_const(mSurfaces)) + surface->m_screens.removeAll(screen); +} + void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen) { if (!mWaitingScreens.removeOne(screen)) @@ -823,6 +846,7 @@ void QWaylandDisplay::registry_global_remove(uint32_t id) for (auto *screen : mWaitingScreens) { if (screen->outputId() == id) { mWaitingScreens.removeOne(screen); + forgetScreenForSurfaces(screen); delete screen; break; } @@ -831,6 +855,7 @@ void QWaylandDisplay::registry_global_remove(uint32_t id) for (QWaylandScreen *screen : std::as_const(mScreens)) { if (screen->outputId() == id) { mScreens.removeOne(screen); + forgetScreenForSurfaces(screen); // If this is the last screen, we have to add a fake screen, or Qt will break. ensureScreen(); QWindowSystemInterface::handleScreenRemoved(screen); diff --git a/src/plugins/platforms/wayland/qwaylanddisplay_p.h b/src/plugins/platforms/wayland/qwaylanddisplay_p.h index 29952886..88b57945 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay_p.h +++ b/src/plugins/platforms/wayland/qwaylanddisplay_p.h @@ -116,6 +116,10 @@ public: QPlatformPlaceholderScreen *placeholderScreen() const { return mPlaceholderScreen; } void ensureScreen(); + void registerSurface(QWaylandSurface *surface); + void unregisterSurface(QWaylandSurface *surface); + bool isScreenAlive(QWaylandScreen *screen) const; + QWaylandScreen *screenForOutput(struct wl_output *output) const; void handleScreenInitialized(QWaylandScreen *screen); @@ -289,6 +293,7 @@ private: void checkWaylandError(); void reconnect(); void setupConnection(); + void forgetScreenForSurfaces(QWaylandScreen *screen); void handleWaylandSync(); void requestWaylandSync(); @@ -311,6 +316,7 @@ private: QList mScreens; QPlatformPlaceholderScreen *mPlaceholderScreen = nullptr; QList mInputDevices; + QList mSurfaces; QList mRegistryListeners; QWaylandIntegration *mWaylandIntegration = nullptr; #if QT_CONFIG(cursor) diff --git a/src/plugins/platforms/wayland/qwaylandsurface.cpp b/src/plugins/platforms/wayland/qwaylandsurface.cpp index 274fdda8..fd098066 100644 --- a/src/plugins/platforms/wayland/qwaylandsurface.cpp +++ b/src/plugins/platforms/wayland/qwaylandsurface.cpp @@ -13,18 +13,31 @@ namespace QtWaylandClient { QWaylandSurface::QWaylandSurface(QWaylandDisplay *display) : wl_surface(display->createSurface(this)) + , m_display(display) { + display->registerSurface(this); connect(qApp, &QGuiApplication::screenRemoved, this, &QWaylandSurface::handleScreenRemoved); connect(qApp, &QGuiApplication::screenAdded, this, &QWaylandSurface::screensChanged); } QWaylandSurface::~QWaylandSurface() { + m_display->unregisterSurface(this); destroy(); } QWaylandScreen *QWaylandSurface::oldestEnteredScreen() { + // Prune any screen pointers that the display no longer knows about. + // This guards against every possible stale-pointer path: waiting-screen + // deletion, signal-ordering gaps during ensureScreen(), and any future + // code that removes a screen without notifying surfaces. + if (m_display) { + 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 diff --git a/src/plugins/platforms/wayland/qwaylandsurface_p.h b/src/plugins/platforms/wayland/qwaylandsurface_p.h index 41860297..ddb63b04 100644 --- a/src/plugins/platforms/wayland/qwaylandsurface_p.h +++ b/src/plugins/platforms/wayland/qwaylandsurface_p.h @@ -57,10 +57,12 @@ 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; friend class QWaylandWindow; // TODO: shouldn't need to be friends + friend class QWaylandDisplay; }; } // namespace QtWaylandClient