[xwl] Move Xwayland parts into separate class
Summary: The Xwayland code path is moved from ApplicationWayland to a dedicated class Xwayland in a new top-level directory xwl. This is a direct preparation step for generic support of Xwayland Selections. On a longer timescale this should also allow us to further separate Wayland native functionality from Xwayland to allow us at one point to build KWin's Wayland binary optionally without X dependencies. Another long term goal, that becomes possible through this separation is to recover from Xwayland crashes. Test Plan: Manually and auto tests Reviewers: #kwin, davidedmundson Subscribers: davidedmundson, zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D15020
This commit is contained in:
parent
58de7d6862
commit
050cf0451e
5 changed files with 339 additions and 197 deletions
|
@ -651,7 +651,13 @@ install(TARGETS kwin ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELIN
|
||||||
install(TARGETS kdeinit_kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} )
|
install(TARGETS kdeinit_kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} )
|
||||||
install(TARGETS 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)
|
target_link_libraries(kwin_wayland kwin KF5::Crash)
|
||||||
if (HAVE_LIBCAP)
|
if (HAVE_LIBCAP)
|
||||||
target_link_libraries(kwin_wayland ${Libcap_LIBRARIES})
|
target_link_libraries(kwin_wayland ${Libcap_LIBRARIES})
|
||||||
|
|
204
main_wayland.cpp
204
main_wayland.cpp
|
@ -27,7 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "tabletmodemanager.h"
|
#include "tabletmodemanager.h"
|
||||||
#include "wayland_server.h"
|
#include "wayland_server.h"
|
||||||
#include "xcbutils.h"
|
#include "xwl/xwayland.h"
|
||||||
|
|
||||||
// KWayland
|
// KWayland
|
||||||
#include <KWayland/Server/display.h>
|
#include <KWayland/Server/display.h>
|
||||||
|
@ -41,29 +41,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Qt
|
// Qt
|
||||||
#include <qplatformdefs.h>
|
#include <qplatformdefs.h>
|
||||||
#include <QAbstractEventDispatcher>
|
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
#include <QtConcurrentRun>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QFutureWatcher>
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QSocketNotifier>
|
|
||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
#include <QThread>
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
|
|
||||||
// system
|
// system
|
||||||
#ifdef HAVE_UNISTD_H
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif // HAVE_UNISTD_H
|
|
||||||
|
|
||||||
#if HAVE_SYS_PRCTL_H
|
#if HAVE_SYS_PRCTL_H
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#endif
|
#endif
|
||||||
#if HAVE_SYS_PROCCTL_H
|
#if HAVE_SYS_PROCCTL_H
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/procctl.h>
|
#include <sys/procctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -92,8 +81,6 @@ void disableDrKonqi()
|
||||||
// that would enable drkonqi
|
// that would enable drkonqi
|
||||||
Q_CONSTRUCTOR_FUNCTION(disableDrKonqi)
|
Q_CONSTRUCTOR_FUNCTION(disableDrKonqi)
|
||||||
|
|
||||||
static void readDisplay(int pipe);
|
|
||||||
|
|
||||||
enum class RealTimeFlags
|
enum class RealTimeFlags
|
||||||
{
|
{
|
||||||
DontReset,
|
DontReset,
|
||||||
|
@ -143,24 +130,13 @@ ApplicationWayland::~ApplicationWayland()
|
||||||
}
|
}
|
||||||
destroyWorkspace();
|
destroyWorkspace();
|
||||||
waylandServer()->dispatch();
|
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()) {
|
if (QStyle *s = style()) {
|
||||||
s->unpolish(this);
|
s->unpolish(this);
|
||||||
}
|
}
|
||||||
|
// kill Xwayland before terminating its connection
|
||||||
|
delete m_xwayland;
|
||||||
|
m_xwayland = nullptr;
|
||||||
waylandServer()->terminateClientConnections();
|
waylandServer()->terminateClientConnections();
|
||||||
destroyCompositor();
|
destroyCompositor();
|
||||||
}
|
}
|
||||||
|
@ -207,7 +183,7 @@ void ApplicationWayland::continueStartupWithScreens()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
createCompositor();
|
createCompositor();
|
||||||
connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::startXwaylandServer);
|
connect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithXwayland);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplicationWayland::continueStartupWithSceen()
|
void ApplicationWayland::continueStartupWithSceen()
|
||||||
|
@ -218,55 +194,18 @@ void ApplicationWayland::continueStartupWithSceen()
|
||||||
notifyKSplash();
|
notifyKSplash();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplicationWayland::continueStartupWithX()
|
void ApplicationWayland::continueStartupWithXwayland()
|
||||||
{
|
{
|
||||||
createX11Connection();
|
disconnect(Compositor::self(), &Compositor::sceneCreated, this, &ApplicationWayland::continueStartupWithXwayland);
|
||||||
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);
|
|
||||||
|
|
||||||
// create selection owner for WM_S0 - magic X display number expected by XWayland
|
m_xwayland = new Xwl::Xwayland(this);
|
||||||
KSelectionOwner owner("WM_S0", c, x11RootWindow());
|
connect(m_xwayland, &Xwl::Xwayland::criticalError, this, [](int code) {
|
||||||
owner.claim(true);
|
// we currently exit on Xwayland errors always directly
|
||||||
|
// TODO: restart Xwayland
|
||||||
createAtoms();
|
std::cerr << "Xwayland had a critical error. Going to exit now." << std::endl;
|
||||||
|
exit(code);
|
||||||
setupEventFilters();
|
});
|
||||||
|
m_xwayland->init();
|
||||||
// Check whether another windowmanager is running
|
|
||||||
const uint32_t maskValues[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT};
|
|
||||||
ScopedCPointer<xcb_generic_error_t> 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplicationWayland::startSession()
|
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<void (QProcess::*)(QProcess::ProcessError)>(&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<void> *watcher = new QFutureWatcher<void>(this);
|
|
||||||
QObject::connect(watcher, &QFutureWatcher<void>::finished, this, &ApplicationWayland::continueStartupWithX, Qt::QueuedConnection);
|
|
||||||
QObject::connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::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_waylandPlugin = QStringLiteral("KWinWaylandWaylandBackend");
|
||||||
static const QString s_x11Plugin = QStringLiteral("KWinWaylandX11Backend");
|
static const QString s_x11Plugin = QStringLiteral("KWinWaylandX11Backend");
|
||||||
static const QString s_fbdevPlugin = QStringLiteral("KWinWaylandFbdevBackend");
|
static const QString s_fbdevPlugin = QStringLiteral("KWinWaylandFbdevBackend");
|
||||||
|
|
|
@ -22,10 +22,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
|
|
||||||
class QProcess;
|
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
namespace Xwl
|
||||||
|
{
|
||||||
|
class Xwayland;
|
||||||
|
}
|
||||||
|
|
||||||
class ApplicationWayland : public Application
|
class ApplicationWayland : public Application
|
||||||
{
|
{
|
||||||
|
@ -58,22 +60,21 @@ protected:
|
||||||
void performStartup() override;
|
void performStartup() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class Xwl::Xwayland;
|
||||||
|
|
||||||
void createBackend();
|
void createBackend();
|
||||||
void createX11Connection();
|
|
||||||
void continueStartupWithScreens();
|
void continueStartupWithScreens();
|
||||||
void continueStartupWithSceen();
|
void continueStartupWithSceen();
|
||||||
void continueStartupWithX();
|
void continueStartupWithXwayland();
|
||||||
void startXwaylandServer();
|
|
||||||
void startSession();
|
void startSession();
|
||||||
|
|
||||||
bool m_startXWayland = false;
|
bool m_startXWayland = false;
|
||||||
int m_xcbConnectionFd = -1;
|
|
||||||
QStringList m_applicationsToStart;
|
QStringList m_applicationsToStart;
|
||||||
QString m_inputMethodServerToStart;
|
QString m_inputMethodServerToStart;
|
||||||
QProcess *m_xwaylandProcess = nullptr;
|
|
||||||
QMetaObject::Connection m_xwaylandFailConnection;
|
|
||||||
QProcessEnvironment m_environment;
|
QProcessEnvironment m_environment;
|
||||||
QString m_sessionArgument;
|
QString m_sessionArgument;
|
||||||
|
|
||||||
|
Xwl::Xwayland *m_xwayland = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
245
xwl/xwayland.cpp
Normal file
245
xwl/xwayland.cpp
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||||
|
Copyright 2019 Roman Gilg <subdiff@gmail.com>
|
||||||
|
|
||||||
|
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 "xwayland.h"
|
||||||
|
#include "wayland_server.h"
|
||||||
|
#include "main_wayland.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <KLocalizedString>
|
||||||
|
|
||||||
|
#include "xcbutils.h"
|
||||||
|
|
||||||
|
#include <QAbstractEventDispatcher>
|
||||||
|
#include <QtConcurrentRun>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QSocketNotifier>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
// system
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#if HAVE_SYS_PROCCTL_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
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<void (QProcess::*)(QProcess::ProcessError)>(&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<void> *watcher = new QFutureWatcher<void>(this);
|
||||||
|
QObject::connect(watcher, &QFutureWatcher<void>::finished, this, &Xwayland::continueStartupWithX, Qt::QueuedConnection);
|
||||||
|
QObject::connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<void>::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<xcb_generic_error_t> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
62
xwl/xwayland.h
Normal file
62
xwl/xwayland.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright 2019 Roman Gilg <subdiff@gmail.com>
|
||||||
|
|
||||||
|
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_XWL_XWAYLAND
|
||||||
|
#define KWIN_XWL_XWAYLAND
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
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
|
Loading…
Reference in a new issue