[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
This commit is contained in:
parent
12c8facc34
commit
3185530ed6
10 changed files with 581 additions and 118 deletions
|
@ -397,7 +397,10 @@ if(KWIN_HAVE_EGL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(Wayland_Client_FOUND AND XKB_FOUND)
|
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)
|
if(KWIN_HAVE_EGL AND Wayland_Egl_FOUND)
|
||||||
set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} egl_wayland_backend.cpp)
|
set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} egl_wayland_backend.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
add_definitions(-DKWIN_UNIT_TEST)
|
add_definitions(-DKWIN_UNIT_TEST)
|
||||||
|
|
||||||
|
if(Wayland_Client_FOUND AND XKB_FOUND)
|
||||||
|
add_subdirectory(wayland_client)
|
||||||
|
endif()
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
# Test ScreenPaintData
|
# Test ScreenPaintData
|
||||||
########################################################
|
########################################################
|
||||||
|
|
8
autotests/wayland_client/CMakeLists.txt
Normal file
8
autotests/wayland_client/CMakeLists.txt
Normal file
|
@ -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)
|
222
autotests/wayland_client/test_wayland_connection_thread.cpp
Normal file
222
autotests/wayland_client/test_wayland_connection_thread.cpp
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*********************************************************************/
|
||||||
|
// Qt
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
// KWin
|
||||||
|
#include "../../wayland_client/connection_thread.h"
|
||||||
|
// Wayland
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
|
|
||||||
|
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<KWin::Wayland::ConnectionThread> 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<KWin::Wayland::ConnectionThread> 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<KWin::Wayland::ConnectionThread> 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<KWin::Wayland::ConnectionThread> 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"
|
|
@ -113,14 +113,16 @@ Compositor::Compositor(QObject* workspace)
|
||||||
if (kwinApp()->operationMode() != Application::OperationModeX11) {
|
if (kwinApp()->operationMode() != Application::OperationModeX11) {
|
||||||
connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::systemCompositorDied, this, &Compositor::finish);
|
connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::systemCompositorDied, this, &Compositor::finish);
|
||||||
connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::backendReady, this, &Compositor::setup);
|
connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::backendReady, this, &Compositor::setup);
|
||||||
}
|
} else
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
{
|
||||||
// delay the call to setup by one event cycle
|
// delay the call to setup by one event cycle
|
||||||
// The ctor of this class is invoked from the Workspace ctor, that means before
|
// The ctor of this class is invoked from the Workspace ctor, that means before
|
||||||
// Workspace is completely constructed, so calling Workspace::self() would result
|
// Workspace is completely constructed, so calling Workspace::self() would result
|
||||||
// in undefined behavior. This is fixed by using a delayed invocation.
|
// in undefined behavior. This is fixed by using a delayed invocation.
|
||||||
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
// register DBus
|
// register DBus
|
||||||
new CompositorDBusInterface(this);
|
new CompositorDBusInterface(this);
|
||||||
|
|
|
@ -90,10 +90,12 @@ void ApplicationWayland::performStartup()
|
||||||
|
|
||||||
// try creating the Wayland Backend
|
// try creating the Wayland Backend
|
||||||
Wayland::WaylandBackend *backend = Wayland::WaylandBackend::create();
|
Wayland::WaylandBackend *backend = Wayland::WaylandBackend::create();
|
||||||
if (!backend) {
|
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);
|
fputs(i18n("kwin_wayland: could not connect to Wayland Server, ensure WAYLAND_DISPLAY is set.\n").toLocal8Bit().constData(), stderr);
|
||||||
::exit(1);
|
::exit(1);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
createWorkspace();
|
createWorkspace();
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
// KWin
|
// KWin
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
#include "wayland_client/connection_thread.h"
|
||||||
// Qt
|
// Qt
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QFileSystemWatcher>
|
|
||||||
#include <QSocketNotifier>
|
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
#include <QThread>
|
||||||
// xcb
|
// xcb
|
||||||
#include <xcb/xtest.h>
|
#include <xcb/xtest.h>
|
||||||
#include <xcb/xfixes.h>
|
#include <xcb/xfixes.h>
|
||||||
|
@ -726,17 +726,13 @@ WaylandBackend *WaylandBackend::create(QObject *parent)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
s_self = new WaylandBackend(parent);
|
s_self = new WaylandBackend(parent);
|
||||||
// check whether it has a display
|
|
||||||
if (!s_self->display()) {
|
|
||||||
delete s_self;
|
|
||||||
s_self = nullptr;
|
|
||||||
}
|
|
||||||
return s_self;
|
return s_self;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaylandBackend::WaylandBackend(QObject *parent)
|
WaylandBackend::WaylandBackend(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_display(nullptr)
|
, m_display(nullptr)
|
||||||
|
, m_eventQueue(nullptr)
|
||||||
, m_registry(nullptr)
|
, m_registry(nullptr)
|
||||||
, m_compositor(NULL)
|
, m_compositor(NULL)
|
||||||
, m_shell(NULL)
|
, m_shell(NULL)
|
||||||
|
@ -745,15 +741,9 @@ WaylandBackend::WaylandBackend(QObject *parent)
|
||||||
, m_shellSurfaceSize(displayWidth(), displayHeight())
|
, m_shellSurfaceSize(displayWidth(), displayHeight())
|
||||||
, m_seat()
|
, m_seat()
|
||||||
, m_shm()
|
, m_shm()
|
||||||
, m_systemCompositorDied(false)
|
, m_connectionThreadObject(nullptr)
|
||||||
, m_runtimeDir(qgetenv("XDG_RUNTIME_DIR"))
|
, m_connectionThread(nullptr)
|
||||||
, m_socketWatcher(nullptr)
|
|
||||||
{
|
{
|
||||||
m_socketName = qgetenv("WAYLAND_DISPLAY");
|
|
||||||
if (m_socketName.isEmpty()) {
|
|
||||||
m_socketName = QStringLiteral("wayland-0");
|
|
||||||
}
|
|
||||||
|
|
||||||
initConnection();
|
initConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -775,10 +765,13 @@ WaylandBackend::~WaylandBackend()
|
||||||
if (m_registry) {
|
if (m_registry) {
|
||||||
wl_registry_destroy(m_registry);
|
wl_registry_destroy(m_registry);
|
||||||
}
|
}
|
||||||
if (m_display) {
|
m_seat.reset();
|
||||||
wl_display_flush(m_display);
|
m_shm.reset();
|
||||||
wl_display_disconnect(m_display);
|
|
||||||
}
|
m_connectionThreadObject->deleteLater();
|
||||||
|
m_connectionThread->quit();
|
||||||
|
m_connectionThread->wait();
|
||||||
|
|
||||||
qDebug() << "Destroyed Wayland display";
|
qDebug() << "Destroyed Wayland display";
|
||||||
s_self = NULL;
|
s_self = NULL;
|
||||||
}
|
}
|
||||||
|
@ -791,43 +784,31 @@ void WaylandBackend::destroyOutputs()
|
||||||
|
|
||||||
void WaylandBackend::initConnection()
|
void WaylandBackend::initConnection()
|
||||||
{
|
{
|
||||||
m_display = wl_display_connect(nullptr);
|
m_connectionThreadObject = new ConnectionThread(nullptr);
|
||||||
if (!m_display) {
|
connect(m_connectionThreadObject, &ConnectionThread::connected, this,
|
||||||
// TODO: maybe we should now really tear down
|
[this]() {
|
||||||
qWarning() << "Failed connecting to Wayland display";
|
// create the event queue for the main gui thread
|
||||||
return;
|
m_display = m_connectionThreadObject->display();
|
||||||
}
|
m_eventQueue = wl_display_create_queue(m_display);
|
||||||
|
// setup registry
|
||||||
m_registry = wl_display_get_registry(m_display);
|
m_registry = wl_display_get_registry(m_display);
|
||||||
|
wl_proxy_set_queue((wl_proxy*)m_registry, m_eventQueue);
|
||||||
// setup the registry
|
// setup the registry
|
||||||
wl_registry_add_listener(m_registry, &s_registryListener, this);
|
wl_registry_add_listener(m_registry, &s_registryListener, this);
|
||||||
wl_display_dispatch(m_display);
|
wl_display_flush(m_display);
|
||||||
int fd = wl_display_get_fd(m_display);
|
},
|
||||||
QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
|
Qt::QueuedConnection);
|
||||||
connect(notifier, &QSocketNotifier::activated, this, &WaylandBackend::readEvents);
|
connect(m_connectionThreadObject, &ConnectionThread::eventsRead, this,
|
||||||
|
[this]() {
|
||||||
if (m_runtimeDir.exists()) {
|
if (!m_eventQueue) {
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
wl_display_dispatch_queue_pending(m_display, m_eventQueue);
|
||||||
wl_display_flush(m_display);
|
wl_display_flush(m_display);
|
||||||
wl_display_dispatch(m_display);
|
},
|
||||||
}
|
Qt::QueuedConnection);
|
||||||
|
connect(m_connectionThreadObject, &ConnectionThread::connectionDied, this,
|
||||||
void WaylandBackend::socketFileChanged(const QString &socket)
|
[this]() {
|
||||||
{
|
|
||||||
if (!QFile::exists(socket) && !m_systemCompositorDied) {
|
|
||||||
qDebug() << "We lost the system compositor at:" << socket;
|
|
||||||
m_systemCompositorDied = true;
|
|
||||||
emit systemCompositorDied();
|
emit systemCompositorDied();
|
||||||
m_seat.reset();
|
m_seat.reset();
|
||||||
m_shm.reset();
|
m_shm.reset();
|
||||||
|
@ -853,29 +834,17 @@ void WaylandBackend::socketFileChanged(const QString &socket)
|
||||||
m_registry = nullptr;
|
m_registry = nullptr;
|
||||||
}
|
}
|
||||||
if (m_display) {
|
if (m_display) {
|
||||||
free(m_display);
|
|
||||||
m_display = nullptr;
|
m_display = nullptr;
|
||||||
}
|
}
|
||||||
// need a new filesystem watcher
|
},
|
||||||
delete m_socketWatcher;
|
Qt::QueuedConnection);
|
||||||
m_socketWatcher = new QFileSystemWatcher(this);
|
connect(m_connectionThreadObject, &ConnectionThread::failed, this, &WaylandBackend::connectionFailed, Qt::QueuedConnection);
|
||||||
m_socketWatcher->addPath(m_runtimeDir.absolutePath());
|
|
||||||
connect(m_socketWatcher, &QFileSystemWatcher::directoryChanged, this, &WaylandBackend::socketDirectoryChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaylandBackend::socketDirectoryChanged()
|
m_connectionThread = new QThread(this);
|
||||||
{
|
m_connectionThreadObject->moveToThread(m_connectionThread);
|
||||||
if (!m_systemCompositorDied) {
|
m_connectionThread->start();
|
||||||
return;
|
|
||||||
}
|
m_connectionThreadObject->initConnection();
|
||||||
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)
|
void WaylandBackend::createSeat(uint32_t name)
|
||||||
|
@ -946,7 +915,7 @@ void WaylandBackend::addOutput(wl_output *o)
|
||||||
|
|
||||||
void WaylandBackend::dispatchEvents()
|
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);
|
wl_display_flush(m_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
class QTemporaryFile;
|
class QTemporaryFile;
|
||||||
class QImage;
|
class QImage;
|
||||||
class QFileSystemWatcher;
|
|
||||||
struct wl_cursor_theme;
|
struct wl_cursor_theme;
|
||||||
struct wl_buffer;
|
struct wl_buffer;
|
||||||
struct wl_shm;
|
struct wl_shm;
|
||||||
|
struct wl_event_queue;
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
@ -46,6 +46,7 @@ namespace Wayland
|
||||||
class ShmPool;
|
class ShmPool;
|
||||||
class WaylandBackend;
|
class WaylandBackend;
|
||||||
class WaylandSeat;
|
class WaylandSeat;
|
||||||
|
class ConnectionThread;
|
||||||
|
|
||||||
class CursorData
|
class CursorData
|
||||||
{
|
{
|
||||||
|
@ -233,15 +234,13 @@ Q_SIGNALS:
|
||||||
void systemCompositorDied();
|
void systemCompositorDied();
|
||||||
void backendReady();
|
void backendReady();
|
||||||
void outputsChanged();
|
void outputsChanged();
|
||||||
private Q_SLOTS:
|
void connectionFailed();
|
||||||
void readEvents();
|
|
||||||
void socketFileChanged(const QString &socket);
|
|
||||||
void socketDirectoryChanged();
|
|
||||||
private:
|
private:
|
||||||
void initConnection();
|
void initConnection();
|
||||||
void createSurface();
|
void createSurface();
|
||||||
void destroyOutputs();
|
void destroyOutputs();
|
||||||
wl_display *m_display;
|
wl_display *m_display;
|
||||||
|
wl_event_queue *m_eventQueue;
|
||||||
wl_registry *m_registry;
|
wl_registry *m_registry;
|
||||||
wl_compositor *m_compositor;
|
wl_compositor *m_compositor;
|
||||||
wl_shell *m_shell;
|
wl_shell *m_shell;
|
||||||
|
@ -250,11 +249,9 @@ private:
|
||||||
QSize m_shellSurfaceSize;
|
QSize m_shellSurfaceSize;
|
||||||
QScopedPointer<WaylandSeat> m_seat;
|
QScopedPointer<WaylandSeat> m_seat;
|
||||||
QScopedPointer<ShmPool> m_shm;
|
QScopedPointer<ShmPool> m_shm;
|
||||||
bool m_systemCompositorDied;
|
|
||||||
QString m_socketName;
|
|
||||||
QDir m_runtimeDir;
|
|
||||||
QFileSystemWatcher *m_socketWatcher;
|
|
||||||
QList<Output*> m_outputs;
|
QList<Output*> m_outputs;
|
||||||
|
ConnectionThread *m_connectionThreadObject;
|
||||||
|
QThread *m_connectionThread;
|
||||||
|
|
||||||
KWIN_SINGLETON(WaylandBackend)
|
KWIN_SINGLETON(WaylandBackend)
|
||||||
};
|
};
|
||||||
|
|
146
wayland_client/connection_thread.cpp
Normal file
146
wayland_client/connection_thread.cpp
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*********************************************************************/
|
||||||
|
#include "connection_thread.h"
|
||||||
|
// Qt
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
#include <QSocketNotifier>
|
||||||
|
// Wayland
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
109
wayland_client/connection_thread.h
Normal file
109
wayland_client/connection_thread.h
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*********************************************************************/
|
||||||
|
#ifndef KWIN_WAYLAND_CONNECTION_THREAD_H
|
||||||
|
#define KWIN_WAYLAND_CONNECTION_THREAD_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
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
|
Loading…
Reference in a new issue