diff --git a/CMakeLists.txt b/CMakeLists.txt
index db07ca1f42..d70dbc50bc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -651,7 +651,13 @@ install(TARGETS kwin ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELIN
install(TARGETS kdeinit_kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} )
install(TARGETS kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} )
-add_executable(kwin_wayland tabletmodemanager.cpp main_wayland.cpp)
+set(kwin_WAYLAND_SRCS
+ tabletmodemanager.cpp
+ main_wayland.cpp
+ xwl/xwayland.cpp
+ )
+
+add_executable(kwin_wayland ${kwin_WAYLAND_SRCS})
target_link_libraries(kwin_wayland kwin KF5::Crash)
if (HAVE_LIBCAP)
target_link_libraries(kwin_wayland ${Libcap_LIBRARIES})
diff --git a/main_wayland.cpp b/main_wayland.cpp
index ef3a653b36..f5b6e78744 100644
--- a/main_wayland.cpp
+++ b/main_wayland.cpp
@@ -27,7 +27,7 @@ along with this program. If not, see .
#include "effects.h"
#include "tabletmodemanager.h"
#include "wayland_server.h"
-#include "xcbutils.h"
+#include "xwl/xwayland.h"
// KWayland
#include
@@ -41,29 +41,18 @@ along with this program. If not, see .
// Qt
#include
-#include
#include
-#include
-#include
#include
-#include
#include
-#include
#include
-#include
#include
#include
// system
-#ifdef HAVE_UNISTD_H
-#include
-#endif // HAVE_UNISTD_H
-
#if HAVE_SYS_PRCTL_H
#include
#endif
#if HAVE_SYS_PROCCTL_H
-#include
#include
#endif
@@ -92,8 +81,6 @@ void disableDrKonqi()
// that would enable drkonqi
Q_CONSTRUCTOR_FUNCTION(disableDrKonqi)
-static void readDisplay(int pipe);
-
enum class RealTimeFlags
{
DontReset,
@@ -143,24 +130,13 @@ ApplicationWayland::~ApplicationWayland()
}
destroyWorkspace();
waylandServer()->dispatch();
- disconnect(m_xwaylandFailConnection);
- if (x11Connection()) {
- Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT);
- destroyAtoms();
- emit x11ConnectionAboutToBeDestroyed();
- xcb_disconnect(x11Connection());
- setX11Connection(nullptr);
- }
- if (m_xwaylandProcess) {
- m_xwaylandProcess->terminate();
- while (m_xwaylandProcess->state() != QProcess::NotRunning) {
- processEvents(QEventLoop::WaitForMoreEvents);
- }
- waylandServer()->destroyXWaylandConnection();
- }
+
if (QStyle *s = style()) {
s->unpolish(this);
}
+ // kill Xwayland before terminating its connection
+ delete m_xwayland;
+ m_xwayland = nullptr;
waylandServer()->terminateClientConnections();
destroyCompositor();
}
@@ -207,7 +183,7 @@ void ApplicationWayland::continueStartupWithScreens()
return;
}
createCompositor();
- connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::startXwaylandServer);
+ connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithXwayland);
}
void ApplicationWayland::continueStartupWithSceen()
@@ -218,55 +194,18 @@ void ApplicationWayland::continueStartupWithSceen()
notifyKSplash();
}
-void ApplicationWayland::continueStartupWithX()
+void ApplicationWayland::continueStartupWithXwayland()
{
- createX11Connection();
- xcb_connection_t *c = x11Connection();
- if (!c) {
- // about to quit
- return;
- }
- QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(c), QSocketNotifier::Read, this);
- auto processXcbEvents = [this, c] {
- while (auto event = xcb_poll_for_event(c)) {
- long result = 0;
- QThread::currentThread()->eventDispatcher()->filterNativeEvent(QByteArrayLiteral("xcb_generic_event_t"), event, &result);
- free(event);
- }
- xcb_flush(c);
- };
- connect(notifier, &QSocketNotifier::activated, this, processXcbEvents);
- connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents);
- connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents);
+ disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithXwayland);
- // create selection owner for WM_S0 - magic X display number expected by XWayland
- KSelectionOwner owner("WM_S0", c, x11RootWindow());
- owner.claim(true);
-
- createAtoms();
-
- setupEventFilters();
-
- // Check whether another windowmanager is running
- const uint32_t maskValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT};
- ScopedCPointer redirectCheck(xcb_request_check(connection(),
- xcb_change_window_attributes_checked(connection(),
- rootWindow(),
- XCB_CW_EVENT_MASK,
- maskValues)));
- if (!redirectCheck.isNull()) {
- fputs(i18n("kwin_wayland: an X11 window manager is running on the X11 Display.\n").toLocal8Bit().constData(), stderr);
- ::exit(1);
- }
-
- m_environment.insert(QStringLiteral("DISPLAY"), QString::fromUtf8(qgetenv("DISPLAY")));
-
- startSession();
- createWorkspace();
-
- Xcb::sync(); // Trigger possible errors, there's still a chance to abort
-
- notifyKSplash();
+ m_xwayland = new Xwl::Xwayland(this);
+ connect(m_xwayland, &Xwl::Xwayland::criticalError, this, [](int code) {
+ // we currently exit on Xwayland errors always directly
+ // TODO: restart Xwayland
+ std::cerr << "Xwayland had a critical error. Going to exit now." << std::endl;
+ exit(code);
+ });
+ m_xwayland->init();
}
void ApplicationWayland::startSession()
@@ -318,117 +257,6 @@ void ApplicationWayland::startSession()
}
}
-void ApplicationWayland::createX11Connection()
-{
- int screenNumber = 0;
- xcb_connection_t *c = nullptr;
- if (m_xcbConnectionFd == -1) {
- c = xcb_connect(nullptr, &screenNumber);
- } else {
- c = xcb_connect_to_fd(m_xcbConnectionFd, nullptr);
- }
- if (int error = xcb_connection_has_error(c)) {
- std::cerr << "FATAL ERROR: Creating connection to XServer failed: " << error << std::endl;
- exit(1);
- return;
- }
- setX11Connection(c);
- // we don't support X11 multi-head in Wayland
- setX11ScreenNumber(screenNumber);
- setX11RootWindow(defaultScreen()->root);
-}
-
-void ApplicationWayland::startXwaylandServer()
-{
- disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::startXwaylandServer);
- int pipeFds[2];
- if (pipe(pipeFds) != 0) {
- std::cerr << "FATAL ERROR failed to create pipe to start Xwayland " << std::endl;
- exit(1);
- return;
- }
- int sx[2];
- if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) {
- std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl;
- exit(1);
- return;
- }
- int fd = dup(sx[1]);
- if (fd < 0) {
- std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl;
- exit(20);
- return;
- }
-
- const int waylandSocket = waylandServer()->createXWaylandConnection();
- if (waylandSocket == -1) {
- std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl;
- exit(1);
- return;
- }
- const int wlfd = dup(waylandSocket);
- if (wlfd < 0) {
- std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl;
- exit(20);
- return;
- }
-
- m_xcbConnectionFd = sx[0];
-
- m_xwaylandProcess = new Process(kwinApp());
- m_xwaylandProcess->setProcessChannelMode(QProcess::ForwardedErrorChannel);
- m_xwaylandProcess->setProgram(QStringLiteral("Xwayland"));
- QProcessEnvironment env = m_environment;
- env.insert("WAYLAND_SOCKET", QByteArray::number(wlfd));
- env.insert("EGL_PLATFORM", QByteArrayLiteral("DRM"));
- m_xwaylandProcess->setProcessEnvironment(env);
- m_xwaylandProcess->setArguments({QStringLiteral("-displayfd"),
- QString::number(pipeFds[1]),
- QStringLiteral("-rootless"),
- QStringLiteral("-wm"),
- QString::number(fd)});
- m_xwaylandFailConnection = connect(m_xwaylandProcess, static_cast(&QProcess::error), this,
- [] (QProcess::ProcessError error) {
- if (error == QProcess::FailedToStart) {
- std::cerr << "FATAL ERROR: failed to start Xwayland" << std::endl;
- } else {
- std::cerr << "FATAL ERROR: Xwayland failed, going to exit now" << std::endl;
- }
- exit(1);
- }
- );
- const int xDisplayPipe = pipeFds[0];
- connect(m_xwaylandProcess, &QProcess::started, this,
- [this, xDisplayPipe] {
- QFutureWatcher *watcher = new QFutureWatcher(this);
- QObject::connect(watcher, &QFutureWatcher::finished, this, &ApplicationWayland::continueStartupWithX, Qt::QueuedConnection);
- QObject::connect(watcher, &QFutureWatcher::finished, watcher, &QFutureWatcher::deleteLater, Qt::QueuedConnection);
- watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe));
- }
- );
- m_xwaylandProcess->start();
- close(pipeFds[1]);
-}
-
-static void readDisplay(int pipe)
-{
- QFile readPipe;
- if (!readPipe.open(pipe, QIODevice::ReadOnly)) {
- std::cerr << "FATAL ERROR failed to open pipe to start X Server" << std::endl;
- exit(1);
- }
- QByteArray displayNumber = readPipe.readLine();
-
- displayNumber.prepend(QByteArray(":"));
- displayNumber.remove(displayNumber.size() -1, 1);
- std::cout << "X-Server started on display " << displayNumber.constData() << std::endl;
-
- setenv("DISPLAY", displayNumber.constData(), true);
-
- // close our pipe
- close(pipe);
-}
-
static const QString s_waylandPlugin = QStringLiteral("KWinWaylandWaylandBackend");
static const QString s_x11Plugin = QStringLiteral("KWinWaylandX11Backend");
static const QString s_fbdevPlugin = QStringLiteral("KWinWaylandFbdevBackend");
diff --git a/main_wayland.h b/main_wayland.h
index 31b7ebd55e..9e13082acd 100644
--- a/main_wayland.h
+++ b/main_wayland.h
@@ -22,10 +22,12 @@ along with this program. If not, see .
#include "main.h"
#include
-class QProcess;
-
namespace KWin
{
+namespace Xwl
+{
+class Xwayland;
+}
class ApplicationWayland : public Application
{
@@ -58,22 +60,21 @@ protected:
void performStartup() override;
private:
+ friend class Xwl::Xwayland;
+
void createBackend();
- void createX11Connection();
void continueStartupWithScreens();
void continueStartupWithSceen();
- void continueStartupWithX();
- void startXwaylandServer();
+ void continueStartupWithXwayland();
void startSession();
bool m_startXWayland = false;
- int m_xcbConnectionFd = -1;
QStringList m_applicationsToStart;
QString m_inputMethodServerToStart;
- QProcess *m_xwaylandProcess = nullptr;
- QMetaObject::Connection m_xwaylandFailConnection;
QProcessEnvironment m_environment;
QString m_sessionArgument;
+
+ Xwl::Xwayland *m_xwayland = nullptr;
};
}
diff --git a/xwl/xwayland.cpp b/xwl/xwayland.cpp
new file mode 100644
index 0000000000..28c704d3bb
--- /dev/null
+++ b/xwl/xwayland.cpp
@@ -0,0 +1,245 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright 2014 Martin Gräßlin
+Copyright 2019 Roman Gilg
+
+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 "xwayland.h"
+#include "wayland_server.h"
+#include "main_wayland.h"
+#include "utils.h"
+
+#include
+
+#include "xcbutils.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// system
+#ifdef HAVE_UNISTD_H
+#include
+#endif
+#if HAVE_SYS_PROCCTL_H
+#include
+#endif
+
+#include
+#include
+
+static void readDisplay(int pipe)
+{
+ QFile readPipe;
+ if (!readPipe.open(pipe, QIODevice::ReadOnly)) {
+ std::cerr << "FATAL ERROR failed to open pipe to start X Server" << std::endl;
+ exit(1);
+ }
+ QByteArray displayNumber = readPipe.readLine();
+
+ displayNumber.prepend(QByteArray(":"));
+ displayNumber.remove(displayNumber.size() -1, 1);
+ std::cout << "X-Server started on display " << displayNumber.constData() << std::endl;
+
+ setenv("DISPLAY", displayNumber.constData(), true);
+
+ // close our pipe
+ close(pipe);
+}
+
+namespace KWin {
+namespace Xwl
+{
+
+Xwayland::Xwayland(ApplicationWayland *app, QObject *parent)
+ : QObject(parent),
+ m_app(app)
+{
+}
+
+Xwayland::~Xwayland()
+{
+ disconnect(m_xwaylandFailConnection);
+ if (m_app->x11Connection()) {
+ Xcb::setInputFocus(XCB_INPUT_FOCUS_POINTER_ROOT);
+ m_app->destroyAtoms();
+ Q_EMIT m_app->x11ConnectionAboutToBeDestroyed();
+ xcb_disconnect(m_app->x11Connection());
+ m_app->setX11Connection(nullptr);
+ }
+
+ if (m_xwaylandProcess) {
+ m_xwaylandProcess->terminate();
+ while (m_xwaylandProcess->state() != QProcess::NotRunning) {
+ m_app->processEvents(QEventLoop::WaitForMoreEvents);
+ }
+ waylandServer()->destroyXWaylandConnection();
+ }
+}
+
+void Xwayland::init()
+{
+ int pipeFds[2];
+ if (pipe(pipeFds) != 0) {
+ std::cerr << "FATAL ERROR failed to create pipe to start Xwayland " << std::endl;
+ Q_EMIT criticalError(1);
+ return;
+ }
+ int sx[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) {
+ std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl;
+ Q_EMIT criticalError(1);
+ return;
+ }
+ int fd = dup(sx[1]);
+ if (fd < 0) {
+ std::cerr << "FATAL ERROR: failed to open socket to open XCB connection" << std::endl;
+ Q_EMIT criticalError(20);
+ return;
+ }
+
+ const int waylandSocket = waylandServer()->createXWaylandConnection();
+ if (waylandSocket == -1) {
+ std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl;
+ Q_EMIT criticalError(1);
+ return;
+ }
+ const int wlfd = dup(waylandSocket);
+ if (wlfd < 0) {
+ std::cerr << "FATAL ERROR: failed to open socket for Xwayland" << std::endl;
+ Q_EMIT criticalError(20);
+ return;
+ }
+
+ m_xcbConnectionFd = sx[0];
+
+ m_xwaylandProcess = new Process(this);
+ m_xwaylandProcess->setProcessChannelMode(QProcess::ForwardedErrorChannel);
+ m_xwaylandProcess->setProgram(QStringLiteral("Xwayland"));
+ QProcessEnvironment env = m_app->processStartupEnvironment();
+ env.insert("WAYLAND_SOCKET", QByteArray::number(wlfd));
+ env.insert("EGL_PLATFORM", QByteArrayLiteral("DRM"));
+ m_xwaylandProcess->setProcessEnvironment(env);
+ m_xwaylandProcess->setArguments({QStringLiteral("-displayfd"),
+ QString::number(pipeFds[1]),
+ QStringLiteral("-rootless"),
+ QStringLiteral("-wm"),
+ QString::number(fd)});
+ m_xwaylandFailConnection = connect(m_xwaylandProcess, static_cast(&QProcess::error), this,
+ [this] (QProcess::ProcessError error) {
+ if (error == QProcess::FailedToStart) {
+ std::cerr << "FATAL ERROR: failed to start Xwayland" << std::endl;
+ } else {
+ std::cerr << "FATAL ERROR: Xwayland failed, going to exit now" << std::endl;
+ }
+ Q_EMIT criticalError(1);
+ }
+ );
+ const int xDisplayPipe = pipeFds[0];
+ connect(m_xwaylandProcess, &QProcess::started, this,
+ [this, xDisplayPipe] {
+ QFutureWatcher *watcher = new QFutureWatcher(this);
+ QObject::connect(watcher, &QFutureWatcher::finished, this, &Xwayland::continueStartupWithX, Qt::QueuedConnection);
+ QObject::connect(watcher, &QFutureWatcher::finished, watcher, &QFutureWatcher::deleteLater, Qt::QueuedConnection);
+ watcher->setFuture(QtConcurrent::run(readDisplay, xDisplayPipe));
+ }
+ );
+ m_xwaylandProcess->start();
+ close(pipeFds[1]);
+}
+
+void Xwayland::createX11Connection()
+{
+ int screenNumber = 0;
+ xcb_connection_t *c = nullptr;
+ if (m_xcbConnectionFd == -1) {
+ c = xcb_connect(nullptr, &screenNumber);
+ } else {
+ c = xcb_connect_to_fd(m_xcbConnectionFd, nullptr);
+ }
+ if (int error = xcb_connection_has_error(c)) {
+ std::cerr << "FATAL ERROR: Creating connection to XServer failed: " << error << std::endl;
+ Q_EMIT criticalError(1);
+ return;
+ }
+
+ m_app->setX11Connection(c);
+ // we don't support X11 multi-head in Wayland
+ m_app->setX11ScreenNumber(screenNumber);
+ m_app->setX11RootWindow(defaultScreen()->root);
+}
+
+void Xwayland::continueStartupWithX()
+{
+ createX11Connection();
+ auto *xcbConn = m_app->x11Connection();
+ if (!xcbConn) {
+ // about to quit
+ Q_EMIT criticalError(1);
+ return;
+ }
+ QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(xcbConn), QSocketNotifier::Read, this);
+ auto processXcbEvents = [this, xcbConn] {
+ while (auto event = xcb_poll_for_event(xcbConn)) {
+ long result = 0;
+ QThread::currentThread()->eventDispatcher()->filterNativeEvent(QByteArrayLiteral("xcb_generic_event_t"), event, &result);
+ free(event);
+ }
+ xcb_flush(xcbConn);
+ };
+ connect(notifier, &QSocketNotifier::activated, this, processXcbEvents);
+ connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents);
+ connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents);
+
+ // create selection owner for WM_S0 - magic X display number expected by XWayland
+ KSelectionOwner owner("WM_S0", xcbConn, m_app->x11RootWindow());
+ owner.claim(true);
+
+ m_app->createAtoms();
+ m_app->setupEventFilters();
+
+ // Check whether another windowmanager is running
+ const uint32_t maskValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT};
+ ScopedCPointer redirectCheck(xcb_request_check(connection(),
+ xcb_change_window_attributes_checked(connection(),
+ rootWindow(),
+ XCB_CW_EVENT_MASK,
+ maskValues)));
+ if (!redirectCheck.isNull()) {
+ fputs(i18n("kwin_wayland: an X11 window manager is running on the X11 Display.\n").toLocal8Bit().constData(), stderr);
+ Q_EMIT criticalError(1);
+ return;
+ }
+
+ auto env = m_app->processStartupEnvironment();
+ env.insert(QStringLiteral("DISPLAY"), QString::fromUtf8(qgetenv("DISPLAY")));
+ m_app->setProcessStartupEnvironment(env);
+
+ m_app->startSession();
+ m_app->createWorkspace();
+
+ Xcb::sync(); // Trigger possible errors, there's still a chance to abort
+
+ m_app->notifyKSplash();
+}
+
+}
+}
diff --git a/xwl/xwayland.h b/xwl/xwayland.h
new file mode 100644
index 0000000000..a46442858b
--- /dev/null
+++ b/xwl/xwayland.h
@@ -0,0 +1,62 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright 2019 Roman Gilg
+
+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_XWL_XWAYLAND
+#define KWIN_XWL_XWAYLAND
+
+#include
+
+class QProcess;
+
+class xcb_screen_t;
+
+namespace KWin
+{
+class ApplicationWayland;
+
+namespace Xwl
+{
+
+class Xwayland : public QObject
+{
+ Q_OBJECT
+public:
+ Xwayland(ApplicationWayland *app, QObject *parent = nullptr);
+ virtual ~Xwayland();
+
+ void init();
+
+Q_SIGNALS:
+ void criticalError(int code);
+
+private:
+ void createX11Connection();
+ void continueStartupWithX();
+
+ int m_xcbConnectionFd = -1;
+ QProcess *m_xwaylandProcess = nullptr;
+ QMetaObject::Connection m_xwaylandFailConnection;
+
+ ApplicationWayland *m_app;
+};
+
+}
+}
+
+#endif