From ca9642b80f500135f5219f14b61e5c7a7de6a433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 3 Jul 2013 11:56:05 +0200 Subject: [PATCH] Watch whether the Wayland socket goes away The Wayland Backend watches the socket it uses for communicating with the Wayland compositor. If the socket is removed we have to perform a kind of emergency stop. The backend tears down all data structures created from the Wayland display and emits a signal that the system compositor died. In addition the Wayland Backend starts to monitor the XDG_RUNTIME_DIR for the socket to be added again. If the socket is created again the backend reinitializes the Wayland connection. This also requires the Compositor to restart. Therefore it connects to the new signals emitted by the Wayland Backend to stop and start compositing. --- composite.cpp | 9 ++++ wayland_backend.cpp | 103 ++++++++++++++++++++++++++++++++++++++++---- wayland_backend.h | 11 +++++ 3 files changed, 114 insertions(+), 9 deletions(-) diff --git a/composite.cpp b/composite.cpp index 56bee8c65a..187d05f58f 100644 --- a/composite.cpp +++ b/composite.cpp @@ -36,6 +36,9 @@ along with this program. If not, see . #include "useractions.h" #include "compositingprefs.h" #include "xcbutils.h" +#if HAVE_WAYLAND +#include "wayland_backend.h" +#endif #include @@ -113,6 +116,12 @@ Compositor::Compositor(QObject* workspace) m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay); m_unusedSupportPropertyTimer.setSingleShot(true); connect(&m_unusedSupportPropertyTimer, SIGNAL(timeout()), SLOT(deleteUnusedSupportProperties())); +#if HAVE_WAYLAND + if (kwinApp()->operationMode() != Application::OperationModeX11) { + connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::systemCompositorDied, this, &Compositor::finish); + connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::backendReady, this, &Compositor::setup); + } +#endif // delay the call to setup by one event cycle // The ctor of this class is invoked from the Workspace ctor, that means before diff --git a/wayland_backend.cpp b/wayland_backend.cpp index 2ee59e979b..4291c40049 100644 --- a/wayland_backend.cpp +++ b/wayland_backend.cpp @@ -26,6 +26,7 @@ along with this program. If not, see . // Qt #include #include +#include #include #include // xcb @@ -627,8 +628,8 @@ WaylandBackend *WaylandBackend::create(QObject *parent) WaylandBackend::WaylandBackend(QObject *parent) : QObject(parent) - , m_display(wl_display_connect(NULL)) - , m_registry(wl_display_get_registry(m_display)) + , m_display(nullptr) + , m_registry(nullptr) , m_compositor(NULL) , m_shell(NULL) , m_surface(NULL) @@ -636,15 +637,17 @@ WaylandBackend::WaylandBackend(QObject *parent) , m_shellSurfaceSize(displayWidth(), displayHeight()) , m_seat() , m_shm() + , m_systemCompositorDied(false) + , m_runtimeDir(qgetenv("XDG_RUNTIME_DIR")) + , m_socketWatcher(nullptr) { - qDebug() << "Created Wayland display"; + m_socketName = qgetenv("WAYLAND_DISPLAY"); + if (m_socketName.isEmpty()) { + m_socketName = QStringLiteral("wayland-0"); + } + + initConnection(); kwinApp()->setOperationMode(Application::OperationModeWaylandAndX11); - // setup the registry - wl_registry_add_listener(m_registry, &s_registryListener, this); - wl_display_dispatch(m_display); - int fd = wl_display_get_fd(m_display); - QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); - connect(notifier, SIGNAL(activated(int)), SLOT(readEvents())); } WaylandBackend::~WaylandBackend() @@ -672,13 +675,94 @@ WaylandBackend::~WaylandBackend() s_self = NULL; } +void WaylandBackend::initConnection() +{ + m_display = wl_display_connect(nullptr); + if (!m_display) { + // TODO: maybe we should now really tear down + qWarning() << "Failed connecting to Wayland display"; + return; + } + m_registry = wl_display_get_registry(m_display); + // setup the registry + wl_registry_add_listener(m_registry, &s_registryListener, this); + wl_display_dispatch(m_display); + int fd = wl_display_get_fd(m_display); + QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(notifier, &QSocketNotifier::activated, this, &WaylandBackend::readEvents); + + if (m_runtimeDir.exists()) { + m_socketWatcher = new QFileSystemWatcher(this); + m_socketWatcher->addPath(m_runtimeDir.absoluteFilePath(m_socketName)); + connect(m_socketWatcher, &QFileSystemWatcher::fileChanged, this, &WaylandBackend::socketFileChanged); + } + qDebug() << "Created Wayland display"; +} + void WaylandBackend::readEvents() { // TODO: this still seems to block + if (m_systemCompositorDied) { + return; + } wl_display_flush(m_display); wl_display_dispatch(m_display); } +void WaylandBackend::socketFileChanged(const QString &socket) +{ + if (!QFile::exists(socket) && !m_systemCompositorDied) { + qDebug() << "We lost the system compositor at:" << socket; + m_systemCompositorDied = true; + emit systemCompositorDied(); + m_seat.reset(); + m_shm.reset(); + if (m_shellSurface) { + free(m_shellSurface); + m_shellSurface = nullptr; + } + if (m_surface) { + free(m_surface); + m_surface = nullptr; + } + if (m_shell) { + free(m_shell); + m_shell = nullptr; + } + if (m_compositor) { + free(m_compositor); + m_compositor = nullptr; + } + if (m_registry) { + free(m_registry); + m_registry = nullptr; + } + if (m_display) { + free(m_display); + m_display = nullptr; + } + // need a new filesystem watcher + delete m_socketWatcher; + m_socketWatcher = new QFileSystemWatcher(this); + m_socketWatcher->addPath(m_runtimeDir.absolutePath()); + connect(m_socketWatcher, &QFileSystemWatcher::directoryChanged, this, &WaylandBackend::socketDirectoryChanged); + } +} + +void WaylandBackend::socketDirectoryChanged() +{ + if (!m_systemCompositorDied) { + return; + } + if (m_runtimeDir.exists(m_socketName)) { + qDebug() << "Socket reappeared"; + delete m_socketWatcher; + m_socketWatcher = nullptr; + initConnection(); + m_systemCompositorDied = false; + } +} + void WaylandBackend::createSeat(uint32_t name) { wl_seat *seat = reinterpret_cast(wl_registry_bind(m_registry, name, &wl_seat_interface, 1)); @@ -704,6 +788,7 @@ void WaylandBackend::createSurface() m_shellSurface = wl_shell_get_shell_surface(m_shell, m_surface); wl_shell_surface_add_listener(m_shellSurface, &s_shellSurfaceListener, this); wl_shell_surface_set_fullscreen(m_shellSurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL); + emit backendReady(); } void WaylandBackend::createShm(uint32_t name) diff --git a/wayland_backend.h b/wayland_backend.h index 21f9b35bce..ad8ddad14d 100644 --- a/wayland_backend.h +++ b/wayland_backend.h @@ -22,6 +22,7 @@ along with this program. If not, see . // KWin #include // Qt +#include #include #include #include @@ -32,6 +33,7 @@ along with this program. If not, see . class QTemporaryFile; class QImage; +class QFileSystemWatcher; struct wl_cursor_theme; struct wl_buffer; struct wl_shm; @@ -188,9 +190,14 @@ public: void dispatchEvents(); Q_SIGNALS: void shellSurfaceSizeChanged(const QSize &size); + void systemCompositorDied(); + void backendReady(); private Q_SLOTS: void readEvents(); + void socketFileChanged(const QString &socket); + void socketDirectoryChanged(); private: + void initConnection(); void createSurface(); wl_display *m_display; wl_registry *m_registry; @@ -201,6 +208,10 @@ private: QSize m_shellSurfaceSize; QScopedPointer m_seat; QScopedPointer m_shm; + bool m_systemCompositorDied; + QString m_socketName; + QDir m_runtimeDir; + QFileSystemWatcher *m_socketWatcher; KWIN_SINGLETON(WaylandBackend) };