From 3185530ed6f61440862545cb652d6b14d3917120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 13 Aug 2014 12:54:02 +0200 Subject: [PATCH] [kwin-wayland] Create dedicated thread for wayland connection The Wayland event queue is moved into a dedicated thread and a new class is created for just creating the connection and listening for events. The WaylandBackend creates the thread and uses an event queue for the main thread. REVIEW: 119761 --- CMakeLists.txt | 5 +- autotests/CMakeLists.txt | 5 + autotests/wayland_client/CMakeLists.txt | 8 + .../test_wayland_connection_thread.cpp | 222 ++++++++++++++++++ composite.cpp | 4 +- main_wayland.cpp | 10 +- wayland_backend.cpp | 175 ++++++-------- wayland_backend.h | 15 +- wayland_client/connection_thread.cpp | 146 ++++++++++++ wayland_client/connection_thread.h | 109 +++++++++ 10 files changed, 581 insertions(+), 118 deletions(-) create mode 100644 autotests/wayland_client/CMakeLists.txt create mode 100644 autotests/wayland_client/test_wayland_connection_thread.cpp create mode 100644 wayland_client/connection_thread.cpp create mode 100644 wayland_client/connection_thread.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cf349d18b..e33aa9f981 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -397,7 +397,10 @@ if(KWIN_HAVE_EGL) endif() if(Wayland_Client_FOUND AND XKB_FOUND) - set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} wayland_backend.cpp) + set(kwin_KDEINIT_SRCS + ${kwin_KDEINIT_SRCS} + wayland_backend.cpp + wayland_client/connection_thread.cpp) if(KWIN_HAVE_EGL AND Wayland_Egl_FOUND) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} egl_wayland_backend.cpp) endif() diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index c10ea6ef92..4bf1cac8a5 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,4 +1,9 @@ add_definitions(-DKWIN_UNIT_TEST) + +if(Wayland_Client_FOUND AND XKB_FOUND) + add_subdirectory(wayland_client) +endif() + ######################################################## # Test ScreenPaintData ######################################################## diff --git a/autotests/wayland_client/CMakeLists.txt b/autotests/wayland_client/CMakeLists.txt new file mode 100644 index 0000000000..68bf544bd2 --- /dev/null +++ b/autotests/wayland_client/CMakeLists.txt @@ -0,0 +1,8 @@ +######################################################## +# Test WaylandConnectionThread +######################################################## +set( testWaylandConnectionThread_SRCS test_wayland_connection_thread.cpp ${KWIN_SOURCE_DIR}/wayland_client/connection_thread.cpp ) +add_executable(testWaylandConnectionThread ${testWaylandConnectionThread_SRCS}) +target_link_libraries( testWaylandConnectionThread Qt5::Test Wayland::Client) +add_test(kwin-testWaylandConnectionThread testWaylandConnectionThread) +ecm_mark_as_test(testWaylandConnectionThread) diff --git a/autotests/wayland_client/test_wayland_connection_thread.cpp b/autotests/wayland_client/test_wayland_connection_thread.cpp new file mode 100644 index 0000000000..3d22f8b1a7 --- /dev/null +++ b/autotests/wayland_client/test_wayland_connection_thread.cpp @@ -0,0 +1,222 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +// Qt +#include +// KWin +#include "../../wayland_client/connection_thread.h" +// Wayland +#include + +class TestWaylandConnectionThread : public QObject +{ + Q_OBJECT +public: + explicit TestWaylandConnectionThread(QObject *parent = nullptr); +private Q_SLOTS: + void init(); + void cleanup(); + + void testInitConnectionNoThread(); + void testConnectionFailure(); + void testConnectionDieing(); + void testConnectionThread(); + +private: + QProcess *m_westonProcess; +}; + +static const QString s_socketName = QStringLiteral("kwin-test-wayland-connection-0"); + +TestWaylandConnectionThread::TestWaylandConnectionThread(QObject *parent) + : QObject(parent) + , m_westonProcess(nullptr) +{ +} + +void TestWaylandConnectionThread::init() +{ + QVERIFY(!m_westonProcess); + // starts weston + m_westonProcess = new QProcess(this); + m_westonProcess->setProgram(QStringLiteral("weston")); + + m_westonProcess->setArguments(QStringList({QStringLiteral("--socket=%1").arg(s_socketName), QStringLiteral("--no-config")})); + m_westonProcess->start(); + QVERIFY(m_westonProcess->waitForStarted()); + + // wait for the socket to appear + QDir runtimeDir(qgetenv("XDG_RUNTIME_DIR")); + if (runtimeDir.exists(s_socketName)) { + return; + } + QFileSystemWatcher *socketWatcher = new QFileSystemWatcher(QStringList({runtimeDir.absolutePath()}), this); + QSignalSpy socketSpy(socketWatcher, SIGNAL(directoryChanged(QString))); + + // limit to maximum of 10 waits + for (int i = 0; i < 10; ++i) { + QVERIFY(socketSpy.wait()); + if (runtimeDir.exists(s_socketName)) { + delete socketWatcher; + return; + } + } +} + +void TestWaylandConnectionThread::cleanup() +{ + // terminates weston + m_westonProcess->terminate(); + QVERIFY(m_westonProcess->waitForFinished()); + delete m_westonProcess; + m_westonProcess = nullptr; +} + +void TestWaylandConnectionThread::testInitConnectionNoThread() +{ + if (m_westonProcess->state() != QProcess::Running) { + QSKIP("This test requires a running wayland server"); + } + QScopedPointer connection(new KWin::Wayland::ConnectionThread); + QCOMPARE(connection->socketName(), QStringLiteral("wayland-0")); + connection->setSocketName(s_socketName); + QCOMPARE(connection->socketName(), s_socketName); + + QSignalSpy connectedSpy(connection.data(), SIGNAL(connected())); + QSignalSpy failedSpy(connection.data(), SIGNAL(failed())); + connection->initConnection(); + QVERIFY(connectedSpy.wait()); + QCOMPARE(connectedSpy.count(), 1); + QCOMPARE(failedSpy.count(), 0); + QVERIFY(connection->display()); +} + +void TestWaylandConnectionThread::testConnectionFailure() +{ + if (m_westonProcess->state() != QProcess::Running) { + QSKIP("This test requires a running wayland server"); + } + QScopedPointer connection(new KWin::Wayland::ConnectionThread); + connection->setSocketName(QStringLiteral("kwin-test-socket-does-not-exist")); + + QSignalSpy connectedSpy(connection.data(), SIGNAL(connected())); + QSignalSpy failedSpy(connection.data(), SIGNAL(failed())); + connection->initConnection(); + QVERIFY(failedSpy.wait()); + QCOMPARE(connectedSpy.count(), 0); + QCOMPARE(failedSpy.count(), 1); + QVERIFY(!connection->display()); +} + +static void registryHandleGlobal(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + Q_UNUSED(data) + Q_UNUSED(registry) + Q_UNUSED(name) + Q_UNUSED(interface) + Q_UNUSED(version) +} + +static void registryHandleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name) +{ + Q_UNUSED(data) + Q_UNUSED(registry) + Q_UNUSED(name) +} + +static const struct wl_registry_listener s_registryListener = { + registryHandleGlobal, + registryHandleGlobalRemove +}; + +void TestWaylandConnectionThread::testConnectionThread() +{ + if (m_westonProcess->state() != QProcess::Running) { + QSKIP("This test requires a running wayland server"); + } + QScopedPointer connection(new KWin::Wayland::ConnectionThread); + connection->setSocketName(s_socketName); + + QThread *connectionThread = new QThread(this); + connection->moveToThread(connectionThread); + connectionThread->start(); + + QSignalSpy connectedSpy(connection.data(), SIGNAL(connected())); + QSignalSpy failedSpy(connection.data(), SIGNAL(failed())); + connection->initConnection(); + QVERIFY(connectedSpy.wait()); + QCOMPARE(connectedSpy.count(), 1); + QCOMPARE(failedSpy.count(), 0); + QVERIFY(connection->display()); + + // now we have the connection ready, let's get some events + QSignalSpy eventsSpy(connection.data(), SIGNAL(eventsRead())); + wl_display *display = connection->display(); + wl_event_queue *queue = wl_display_create_queue(display); + + wl_registry *registry = wl_display_get_registry(display); + wl_proxy_set_queue((wl_proxy*)registry, queue); + + wl_registry_add_listener(registry, &s_registryListener, this); + wl_display_flush(display); + + QVERIFY(eventsSpy.wait()); + + wl_registry_destroy(registry); + wl_event_queue_destroy(queue); + + connectionThread->quit(); + connectionThread->wait(); + delete connectionThread; +} + +void TestWaylandConnectionThread::testConnectionDieing() +{ + if (m_westonProcess->state() != QProcess::Running) { + QSKIP("This test requires a running wayland server"); + } + QScopedPointer connection(new KWin::Wayland::ConnectionThread); + QSignalSpy connectedSpy(connection.data(), SIGNAL(connected())); + connection->setSocketName(s_socketName); + connection->initConnection(); + QVERIFY(connectedSpy.wait()); + QVERIFY(connection->display()); + + QSignalSpy diedSpy(connection.data(), SIGNAL(connectionDied())); + m_westonProcess->terminate(); + QVERIFY(m_westonProcess->waitForFinished()); + QVERIFY(diedSpy.wait()); + QCOMPARE(diedSpy.count(), 1); + QVERIFY(!connection->display()); + + connectedSpy.clear(); + QVERIFY(connectedSpy.isEmpty()); + // restarts the server + delete m_westonProcess; + m_westonProcess = nullptr; + init(); + if (connectedSpy.count() == 0) { + QVERIFY(connectedSpy.wait()); + } + QCOMPARE(connectedSpy.count(), 1); +} + +QTEST_MAIN(TestWaylandConnectionThread) +#include "test_wayland_connection_thread.moc" diff --git a/composite.cpp b/composite.cpp index 1913d1f10e..2eef5772a5 100644 --- a/composite.cpp +++ b/composite.cpp @@ -113,14 +113,16 @@ Compositor::Compositor(QObject* workspace) 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); - } + } else #endif + { // delay the call to setup by one event cycle // The ctor of this class is invoked from the Workspace ctor, that means before // Workspace is completely constructed, so calling Workspace::self() would result // in undefined behavior. This is fixed by using a delayed invocation. QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); + } // register DBus new CompositorDBusInterface(this); diff --git a/main_wayland.cpp b/main_wayland.cpp index 794e73d577..85a83b5101 100644 --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -90,10 +90,12 @@ void ApplicationWayland::performStartup() // try creating the Wayland Backend Wayland::WaylandBackend *backend = Wayland::WaylandBackend::create(); - if (!backend) { - fputs(i18n("kwin_wayland: could not connect to Wayland Server, ensure WAYLAND_DISPLAY is set.\n").toLocal8Bit().constData(), stderr); - ::exit(1); - } + connect(backend, &Wayland::WaylandBackend::connectionFailed, this, + [] () { + fputs(i18n("kwin_wayland: could not connect to Wayland Server, ensure WAYLAND_DISPLAY is set.\n").toLocal8Bit().constData(), stderr); + ::exit(1); + } + ); createWorkspace(); diff --git a/wayland_backend.cpp b/wayland_backend.cpp index b341773e60..c7dd754019 100644 --- a/wayland_backend.cpp +++ b/wayland_backend.cpp @@ -22,12 +22,12 @@ along with this program. If not, see . // KWin #include "cursor.h" #include "input.h" +#include "wayland_client/connection_thread.h" // Qt #include #include -#include -#include #include +#include // xcb #include #include @@ -726,17 +726,13 @@ WaylandBackend *WaylandBackend::create(QObject *parent) return NULL; } s_self = new WaylandBackend(parent); - // check whether it has a display - if (!s_self->display()) { - delete s_self; - s_self = nullptr; - } return s_self; } WaylandBackend::WaylandBackend(QObject *parent) : QObject(parent) , m_display(nullptr) + , m_eventQueue(nullptr) , m_registry(nullptr) , m_compositor(NULL) , m_shell(NULL) @@ -745,15 +741,9 @@ WaylandBackend::WaylandBackend(QObject *parent) , m_shellSurfaceSize(displayWidth(), displayHeight()) , m_seat() , m_shm() - , m_systemCompositorDied(false) - , m_runtimeDir(qgetenv("XDG_RUNTIME_DIR")) - , m_socketWatcher(nullptr) + , m_connectionThreadObject(nullptr) + , m_connectionThread(nullptr) { - m_socketName = qgetenv("WAYLAND_DISPLAY"); - if (m_socketName.isEmpty()) { - m_socketName = QStringLiteral("wayland-0"); - } - initConnection(); } @@ -775,10 +765,13 @@ WaylandBackend::~WaylandBackend() if (m_registry) { wl_registry_destroy(m_registry); } - if (m_display) { - wl_display_flush(m_display); - wl_display_disconnect(m_display); - } + m_seat.reset(); + m_shm.reset(); + + m_connectionThreadObject->deleteLater(); + m_connectionThread->quit(); + m_connectionThread->wait(); + qDebug() << "Destroyed Wayland display"; s_self = NULL; } @@ -791,91 +784,67 @@ void WaylandBackend::destroyOutputs() 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); + m_connectionThreadObject = new ConnectionThread(nullptr); + connect(m_connectionThreadObject, &ConnectionThread::connected, this, + [this]() { + // create the event queue for the main gui thread + m_display = m_connectionThreadObject->display(); + m_eventQueue = wl_display_create_queue(m_display); + // setup registry + m_registry = wl_display_get_registry(m_display); + wl_proxy_set_queue((wl_proxy*)m_registry, m_eventQueue); + // setup the registry + wl_registry_add_listener(m_registry, &s_registryListener, this); + wl_display_flush(m_display); + }, + Qt::QueuedConnection); + connect(m_connectionThreadObject, &ConnectionThread::eventsRead, this, + [this]() { + if (!m_eventQueue) { + return; + } + wl_display_dispatch_queue_pending(m_display, m_eventQueue); + wl_display_flush(m_display); + }, + Qt::QueuedConnection); + connect(m_connectionThreadObject, &ConnectionThread::connectionDied, this, + [this]() { + emit systemCompositorDied(); + m_seat.reset(); + m_shm.reset(); + destroyOutputs(); + 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) { + m_display = nullptr; + } + }, + Qt::QueuedConnection); + connect(m_connectionThreadObject, &ConnectionThread::failed, this, &WaylandBackend::connectionFailed, Qt::QueuedConnection); - 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"; -} + m_connectionThread = new QThread(this); + m_connectionThreadObject->moveToThread(m_connectionThread); + m_connectionThread->start(); -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(); - destroyOutputs(); - 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; - } + m_connectionThreadObject->initConnection(); } void WaylandBackend::createSeat(uint32_t name) @@ -946,7 +915,7 @@ void WaylandBackend::addOutput(wl_output *o) void WaylandBackend::dispatchEvents() { - wl_display_dispatch_pending(m_display); + // TODO: integrate into event loop to flush before going to block wl_display_flush(m_display); } diff --git a/wayland_backend.h b/wayland_backend.h index 7745be2557..6af4abb0ac 100644 --- a/wayland_backend.h +++ b/wayland_backend.h @@ -33,10 +33,10 @@ along with this program. If not, see . class QTemporaryFile; class QImage; -class QFileSystemWatcher; struct wl_cursor_theme; struct wl_buffer; struct wl_shm; +struct wl_event_queue; namespace KWin { @@ -46,6 +46,7 @@ namespace Wayland class ShmPool; class WaylandBackend; class WaylandSeat; +class ConnectionThread; class CursorData { @@ -233,15 +234,13 @@ Q_SIGNALS: void systemCompositorDied(); void backendReady(); void outputsChanged(); -private Q_SLOTS: - void readEvents(); - void socketFileChanged(const QString &socket); - void socketDirectoryChanged(); + void connectionFailed(); private: void initConnection(); void createSurface(); void destroyOutputs(); wl_display *m_display; + wl_event_queue *m_eventQueue; wl_registry *m_registry; wl_compositor *m_compositor; wl_shell *m_shell; @@ -250,11 +249,9 @@ private: QSize m_shellSurfaceSize; QScopedPointer m_seat; QScopedPointer m_shm; - bool m_systemCompositorDied; - QString m_socketName; - QDir m_runtimeDir; - QFileSystemWatcher *m_socketWatcher; QList m_outputs; + ConnectionThread *m_connectionThreadObject; + QThread *m_connectionThread; KWIN_SINGLETON(WaylandBackend) }; diff --git a/wayland_client/connection_thread.cpp b/wayland_client/connection_thread.cpp new file mode 100644 index 0000000000..082b95c3bc --- /dev/null +++ b/wayland_client/connection_thread.cpp @@ -0,0 +1,146 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "connection_thread.h" +// Qt +#include +#include +#include +// Wayland +#include + +namespace KWin +{ + +namespace Wayland +{ + +ConnectionThread::ConnectionThread(QObject *parent) + : QObject(parent) + , m_display(nullptr) + , m_socketName(QString::fromUtf8(qgetenv("WAYLAND_DISPLAY"))) + , m_runtimeDir(QString::fromUtf8(qgetenv("XDG_RUNTIME_DIR"))) + , m_socketNotifier(nullptr) + , m_socketWatcher(nullptr) + , m_serverDied(false) +{ + if (m_socketName.isEmpty()) { + m_socketName = QStringLiteral("wayland-0"); + } +} + +ConnectionThread::~ConnectionThread() +{ + if (m_display) { + wl_display_flush(m_display); + wl_display_disconnect(m_display); + } +} + +void ConnectionThread::initConnection() +{ + QMetaObject::invokeMethod(this, "doInitConnection", Qt::QueuedConnection); +} + +void ConnectionThread::doInitConnection() +{ + m_display = wl_display_connect(m_socketName.toUtf8().constData()); + if (!m_display) { + qWarning() << "Failed connecting to Wayland display"; + emit failed(); + return; + } + qDebug() << "Connected to Wayland server at:" << m_socketName; + + // setup socket notifier + setupSocketNotifier(); + setupSocketFileWatcher(); + emit connected(); +} + +void ConnectionThread::setupSocketNotifier() +{ + const int fd = wl_display_get_fd(m_display); + m_socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(m_socketNotifier, &QSocketNotifier::activated, this, + [this]() { + if (!m_display) { + return; + } + wl_display_dispatch(m_display); + emit eventsRead(); + } + ); +} + +void ConnectionThread::setupSocketFileWatcher() +{ + if (!m_runtimeDir.exists()) { + return; + } + m_socketWatcher = new QFileSystemWatcher(this); + m_socketWatcher->addPath(m_runtimeDir.absoluteFilePath(m_socketName)); + connect(m_socketWatcher, &QFileSystemWatcher::fileChanged, this, + [this] (const QString &file) { + if (QFile::exists(file) || m_serverDied) { + return; + } + qWarning() << "Connection to server went away"; + m_serverDied = true; + if (m_display) { + free(m_display); + m_display = nullptr; + } + delete m_socketNotifier; + m_socketNotifier = 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, + [this]() { + if (!m_serverDied) { + return; + } + if (m_runtimeDir.exists(m_socketName)) { + qDebug() << "Socket reappeared"; + delete m_socketWatcher; + m_socketWatcher = nullptr; + m_serverDied = false; + initConnection(); + } + } + ); + emit connectionDied(); + } + ); +} + +void ConnectionThread::setSocketName(const QString &socketName) +{ + if (m_display) { + // already initialized + return; + } + m_socketName = socketName; +} + +} +} diff --git a/wayland_client/connection_thread.h b/wayland_client/connection_thread.h new file mode 100644 index 0000000000..4b03653014 --- /dev/null +++ b/wayland_client/connection_thread.h @@ -0,0 +1,109 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_WAYLAND_CONNECTION_THREAD_H +#define KWIN_WAYLAND_CONNECTION_THREAD_H + +#include +#include + +struct wl_display; +class QFileSystemWatcher; +class QSocketNotifier; + +namespace KWin +{ +namespace Wayland +{ + +class ConnectionThread : public QObject +{ + Q_OBJECT +public: + explicit ConnectionThread(QObject *parent = nullptr); + virtual ~ConnectionThread(); + + wl_display *display(); + /** + * @returns the name of the socket it connects to. + **/ + const QString &socketName() const; + /** + * Sets the @p socketName to connect to. + * Only applies if called before calling initConnection. + * The default socket name is derived from environment variable WAYLAND_DISPLAY + * and if not set is hard coded to "wayland-0". + **/ + void setSocketName(const QString &socketName); + +public Q_SLOTS: + void initConnection(); + +Q_SIGNALS: + /** + * Emitted once a connection to a Wayland server is established. + * Normally emitted after invoking initConnection(), but might also be + * emitted after re-connecting to another server. + **/ + void connected(); + /** + * Emitted if connecting to a Wayland server failed. + **/ + void failed(); + /** + * Emitted whenever new events are ready to be read. + **/ + void eventsRead(); + /** + * Emitted if the Wayland server connection dies. + * If the socket reappears, it is tried to reconnect. + **/ + void connectionDied(); + +private Q_SLOTS: + void doInitConnection(); + +private: + void setupSocketNotifier(); + void setupSocketFileWatcher(); + wl_display *m_display; + QString m_socketName; + QDir m_runtimeDir; + QSocketNotifier *m_socketNotifier; + QFileSystemWatcher *m_socketWatcher; + bool m_serverDied; +}; + +inline +const QString &ConnectionThread::socketName() const +{ + return m_socketName; +} + +inline +wl_display *ConnectionThread::display() +{ + return m_display; +} + +} +} + + +#endif