[kwin_wrapper] Transfer ownership of Xwayland socket creation and Xauthority to the wrapper
This way if kwin_wayland crashes we don't need to ensure that new environment variables need to get synced across the new env. This fixes an issue where spawning an xwayland application from a wayland window that survives a crash would fail. By moving the logic here we no longer need to wait for kwin_wayland to start before starting plasmashell or even ksmserver as all environment variables are set. As long as the wrapper is ready we can continue starting and clients will just block on connect. That should still allow for both a lot of optimisations both for speed and cleaning up the startplasma-wayland scripts. This will be addressed in follow up patches. Use of kwin_wayland directly with xwayland is still supported for testing.
This commit is contained in:
parent
9692b49219
commit
f541d851ed
13 changed files with 338 additions and 168 deletions
|
@ -270,32 +270,7 @@ target_link_libraries(kwin_x11 kwin KF5::Crash Qt::X11Extras)
|
|||
install(TARGETS kwin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP)
|
||||
install(TARGETS kwin_x11 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
ecm_qt_declare_logging_category(xwaylandlogging_SOURCES
|
||||
HEADER
|
||||
xwayland_logging.h
|
||||
IDENTIFIER
|
||||
KWIN_XWL
|
||||
CATEGORY_NAME
|
||||
kwin_xwl
|
||||
DEFAULT_SEVERITY
|
||||
Warning
|
||||
)
|
||||
add_library(KWinXwaylandServerModule OBJECT
|
||||
xwl/clipboard.cpp
|
||||
xwl/databridge.cpp
|
||||
xwl/dnd.cpp
|
||||
xwl/drag.cpp
|
||||
xwl/drag_wl.cpp
|
||||
xwl/drag_x.cpp
|
||||
xwl/selection.cpp
|
||||
xwl/selection_source.cpp
|
||||
xwl/transfer.cpp
|
||||
xwl/xwayland.cpp
|
||||
xwl/xwaylandsocket.cpp
|
||||
|
||||
${xwaylandlogging_SOURCES}
|
||||
)
|
||||
target_link_libraries(KWinXwaylandServerModule PUBLIC kwin)
|
||||
add_subdirectory(xwl)
|
||||
|
||||
add_executable(kwin_wayland
|
||||
main_wayland.cpp
|
||||
|
|
|
@ -1,4 +1,21 @@
|
|||
add_executable(kwin_wayland_wrapper kwin_wrapper.cpp wl-socket.c)
|
||||
target_link_libraries(kwin_wayland_wrapper Qt5::Core)
|
||||
set(kwin_wayland_wrapper_SOURCES
|
||||
kwin_wrapper.cpp
|
||||
wl-socket.c
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(kwin_wayland_wrapper_SOURCES
|
||||
HEADER
|
||||
wrapper_logging.h
|
||||
IDENTIFIER
|
||||
KWIN_WRAPPER
|
||||
CATEGORY_NAME
|
||||
kwin_wayland_wrapper
|
||||
DEFAULT_SEVERITY
|
||||
Warning
|
||||
)
|
||||
|
||||
add_executable(kwin_wayland_wrapper ${kwin_wayland_wrapper_SOURCES})
|
||||
|
||||
target_link_libraries(kwin_wayland_wrapper Qt5::Core KWinXwaylandCommon)
|
||||
set_property(TARGET kwin_wayland_wrapper PROPERTY C_STANDARD 11)
|
||||
install(TARGETS kwin_wayland_wrapper ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
|
|
@ -24,7 +24,12 @@
|
|||
#include <QDebug>
|
||||
#include <QProcess>
|
||||
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include "wl-socket.h"
|
||||
#include "xwaylandsocket.h"
|
||||
#include "xauthority.h"
|
||||
#include "wrapper_logging.h"
|
||||
|
||||
class KWinWrapper : public QObject
|
||||
{
|
||||
|
@ -37,6 +42,9 @@ public:
|
|||
|
||||
private:
|
||||
wl_socket *m_socket;
|
||||
|
||||
QScopedPointer<KWin::XwaylandSocket> m_xwlSocket;
|
||||
QTemporaryFile m_xauthorityFile;
|
||||
};
|
||||
|
||||
KWinWrapper::KWinWrapper(QObject *parent)
|
||||
|
@ -46,6 +54,21 @@ KWinWrapper::KWinWrapper(QObject *parent)
|
|||
if (!m_socket) {
|
||||
qFatal("Could not create wayland socket");
|
||||
}
|
||||
|
||||
if (qApp->arguments().contains(QLatin1String("--xwayland"))) {
|
||||
m_xwlSocket.reset(new KWin::XwaylandSocket(KWin::XwaylandSocket::OperationMode::TransferFdsOnExec));
|
||||
if (!m_xwlSocket->isValid()) {
|
||||
qCWarning(KWIN_WRAPPER) << "Failed to create Xwayland connection sockets";
|
||||
m_xwlSocket.reset();
|
||||
}
|
||||
if (m_xwlSocket) {
|
||||
if (!qEnvironmentVariableIsSet("KWIN_WAYLAND_NO_XAUTHORITY")) {
|
||||
if (!generateXauthorityFile(m_xwlSocket->display(), &m_xauthorityFile)) {
|
||||
qCWarning(KWIN_WRAPPER) << "Failed to create an Xauthority file";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KWinWrapper::~KWinWrapper()
|
||||
|
@ -66,12 +89,12 @@ void KWinWrapper::run()
|
|||
|
||||
if (exitStatus == 133) {
|
||||
crashCount = 1;
|
||||
qDebug() << "Compositor restarted, respawning";
|
||||
qCDebug(KWIN_WRAPPER) << "Compositor restarted, respawning";
|
||||
} else if (exitStatus == -1) {
|
||||
// kwin_crashed, lets go again
|
||||
qWarning() << "Compositor crashed, respawning";
|
||||
qWarning(KWIN_WRAPPER) << "Compositor crashed, respawning";
|
||||
} else {
|
||||
qWarning() << "Compositor exited with code: " << exitStatus;
|
||||
qWarning(KWIN_WRAPPER) << "Compositor exited with code: " << exitStatus;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -79,15 +102,26 @@ void KWinWrapper::run()
|
|||
|
||||
int KWinWrapper::runKwin()
|
||||
{
|
||||
qDebug() << "Launching kwin";
|
||||
qCDebug(KWIN_WRAPPER) << "Launching kwin";
|
||||
|
||||
auto process = new QProcess(qApp);
|
||||
process->setProgram("kwin_wayland");
|
||||
|
||||
QStringList args;
|
||||
|
||||
args << "--wayland_fd" << QString::number(wl_socket_get_fd(m_socket));
|
||||
args << "--socket" << QString::fromUtf8(wl_socket_get_display_name(m_socket));
|
||||
|
||||
if (m_xwlSocket) {
|
||||
args << "--xwayland-fd" << QString::number(m_xwlSocket->abstractFileDescriptor());
|
||||
args << "--xwayland-fd" << QString::number(m_xwlSocket->unixFileDescriptor());
|
||||
args << "--xwayland-display" << m_xwlSocket->name();
|
||||
if (m_xauthorityFile.open()) {
|
||||
args << "--xwayland-xauthority" << m_xauthorityFile.fileName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// attach our main process arguments
|
||||
// the first entry is dropped as it will be our program name
|
||||
args << qApp->arguments().mid(1);
|
||||
|
|
|
@ -198,6 +198,9 @@ void ApplicationWayland::continueStartupWithScreens()
|
|||
}
|
||||
|
||||
m_xwayland = new Xwl::Xwayland(this);
|
||||
m_xwayland->setListenFDs(m_xwaylandListenFds);
|
||||
m_xwayland->setDisplayName(m_xwaylandDisplay);
|
||||
m_xwayland->setXauthority(m_xwaylandXauthority);
|
||||
connect(m_xwayland, &Xwl::Xwayland::errorOccurred, this, &ApplicationWayland::finalizeStartup);
|
||||
connect(m_xwayland, &Xwl::Xwayland::started, this, &ApplicationWayland::finalizeStartup);
|
||||
m_xwayland->start();
|
||||
|
@ -469,6 +472,18 @@ int main(int argc, char * argv[])
|
|||
i18n("Wayland socket to use for incoming connections. This can be combined with --socket to name the socket"),
|
||||
QStringLiteral("wayland_fd"));
|
||||
|
||||
QCommandLineOption xwaylandListenFdOption(QStringLiteral("xwayland-fd"),
|
||||
i18n("XWayland socket to use for Xwayland's incoming connections. This can be set multiple times"),
|
||||
QStringLiteral("xwayland-fds"));
|
||||
|
||||
QCommandLineOption xwaylandDisplayOption(QStringLiteral("xwayland-display"),
|
||||
i18n("Name of the xwayland display that has been pre-set up"),
|
||||
"xwayland-display");
|
||||
|
||||
QCommandLineOption xwaylandXAuthorityOption(QStringLiteral("xwayland-xauthority"),
|
||||
i18n("Name of the xauthority file "),
|
||||
"xwayland-xauthority");
|
||||
|
||||
QCommandLineOption replaceOption(QStringLiteral("replace"),
|
||||
i18n("Exits this instance so it can be restarted by kwin_wayland_wrapper."));
|
||||
|
||||
|
@ -477,6 +492,9 @@ int main(int argc, char * argv[])
|
|||
parser.addOption(xwaylandOption);
|
||||
parser.addOption(waylandSocketOption);
|
||||
parser.addOption(waylandSocketFdOption);
|
||||
parser.addOption(xwaylandListenFdOption);
|
||||
parser.addOption(xwaylandDisplayOption);
|
||||
parser.addOption(xwaylandXAuthorityOption);
|
||||
parser.addOption(replaceOption);
|
||||
|
||||
if (hasX11Option) {
|
||||
|
@ -701,7 +719,33 @@ int main(int argc, char * argv[])
|
|||
environment.insert(QStringLiteral("WAYLAND_DISPLAY"), server->socketName());
|
||||
}
|
||||
a.setProcessStartupEnvironment(environment);
|
||||
a.setStartXwayland(parser.isSet(xwaylandOption));
|
||||
|
||||
if (parser.isSet(xwaylandOption)) {
|
||||
a.setStartXwayland(true);
|
||||
|
||||
if (parser.isSet(xwaylandListenFdOption)) {
|
||||
const QStringList fdStrings = parser.values(xwaylandListenFdOption);
|
||||
for (const QString &fdString: fdStrings){
|
||||
bool ok;
|
||||
int fd = fdString.toInt(&ok);
|
||||
if (ok ) {
|
||||
// make sure we don't leak this FD to children
|
||||
fcntl(fd, F_SETFD, O_CLOEXEC);
|
||||
a.addXwaylandSocketFileDescriptor(fd);
|
||||
}
|
||||
}
|
||||
if (parser.isSet(xwaylandDisplayOption)) {
|
||||
a.setXwaylandDisplay(parser.value(xwaylandDisplayOption));
|
||||
} else {
|
||||
std::cerr << "Using xwayland-fd without xwayland-display is undefined" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (parser.isSet(xwaylandXAuthorityOption)) {
|
||||
a.setXwaylandXauthority(parser.value(xwaylandXAuthorityOption));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.setApplicationsToStart(parser.positionalArguments());
|
||||
a.setInputMethodServerToStart(parser.value(inputMethodOption));
|
||||
a.start();
|
||||
|
|
|
@ -30,6 +30,15 @@ public:
|
|||
void setStartXwayland(bool start) {
|
||||
m_startXWayland = start;
|
||||
}
|
||||
void addXwaylandSocketFileDescriptor(int fd) {
|
||||
m_xwaylandListenFds << fd;
|
||||
}
|
||||
void setXwaylandDisplay(const QString &display) {
|
||||
m_xwaylandDisplay = display;
|
||||
}
|
||||
void setXwaylandXauthority(const QString &xauthority) {
|
||||
m_xwaylandXauthority= xauthority;
|
||||
}
|
||||
void setApplicationsToStart(const QStringList &applications) {
|
||||
m_applicationsToStart = applications;
|
||||
}
|
||||
|
@ -64,6 +73,9 @@ private:
|
|||
QString m_sessionArgument;
|
||||
|
||||
Xwl::Xwayland *m_xwayland = nullptr;
|
||||
QVector<int> m_xwaylandListenFds;
|
||||
QString m_xwaylandDisplay;
|
||||
QString m_xwaylandXauthority;
|
||||
KConfigWatcher::Ptr m_settingsWatcher;
|
||||
};
|
||||
|
||||
|
|
15
src/xwl/CMakeLists.txt
Normal file
15
src/xwl/CMakeLists.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
add_subdirectory(lib)
|
||||
|
||||
add_library(KWinXwaylandServerModule OBJECT
|
||||
clipboard.cpp
|
||||
databridge.cpp
|
||||
dnd.cpp
|
||||
drag.cpp
|
||||
drag_wl.cpp
|
||||
drag_x.cpp
|
||||
selection.cpp
|
||||
selection_source.cpp
|
||||
transfer.cpp
|
||||
xwayland.cpp
|
||||
)
|
||||
target_link_libraries(KWinXwaylandServerModule PUBLIC kwin KWinXwaylandCommon)
|
19
src/xwl/lib/CMakeLists.txt
Normal file
19
src/xwl/lib/CMakeLists.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
ecm_qt_declare_logging_category(xwaylandliblogging_SOURCES
|
||||
HEADER
|
||||
xwayland_logging.h
|
||||
IDENTIFIER
|
||||
KWIN_XWL
|
||||
CATEGORY_NAME
|
||||
kwin_xwl
|
||||
DEFAULT_SEVERITY
|
||||
Warning
|
||||
)
|
||||
|
||||
add_library(KWinXwaylandCommon STATIC
|
||||
xwaylandsocket.cpp
|
||||
xauthority.cpp
|
||||
${xwaylandliblogging_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(KWinXwaylandCommon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(KWinXwaylandCommon Qt::Core Qt::Network)
|
77
src/xwl/lib/xauthority.cpp
Normal file
77
src/xwl/lib/xauthority.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "xauthority.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QRandomGenerator>
|
||||
#include <QHostInfo>
|
||||
#include <QTemporaryFile>
|
||||
#include <QStandardPaths>
|
||||
|
||||
static void writeXauthorityEntry(QDataStream &stream, quint16 family,
|
||||
const QByteArray &address, const QByteArray &display,
|
||||
const QByteArray &name, const QByteArray &cookie)
|
||||
{
|
||||
stream << quint16(family);
|
||||
|
||||
auto writeArray = [&stream](const QByteArray &str) {
|
||||
stream << quint16(str.size());
|
||||
stream.writeRawData(str.constData(), str.size());
|
||||
};
|
||||
|
||||
writeArray(address);
|
||||
writeArray(display);
|
||||
writeArray(name);
|
||||
writeArray(cookie);
|
||||
}
|
||||
|
||||
static QByteArray generateXauthorityCookie()
|
||||
{
|
||||
QByteArray cookie;
|
||||
cookie.resize(16); // Cookie must be 128bits
|
||||
|
||||
QRandomGenerator *generator = QRandomGenerator::system();
|
||||
for (int i = 0; i < cookie.size(); ++i) {
|
||||
cookie[i] = uint8_t(generator->bounded(256));
|
||||
}
|
||||
return cookie;
|
||||
}
|
||||
|
||||
bool generateXauthorityFile(int display, QTemporaryFile *authorityFile)
|
||||
{
|
||||
const QString runtimeDirectory = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
|
||||
|
||||
authorityFile->setFileTemplate(runtimeDirectory + QStringLiteral("/xauth_XXXXXX"));
|
||||
if (!authorityFile->open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray hostname = QHostInfo::localHostName().toUtf8();
|
||||
const QByteArray displayName = QByteArray::number(display);
|
||||
const QByteArray name = QByteArrayLiteral("MIT-MAGIC-COOKIE-1");
|
||||
const QByteArray cookie = generateXauthorityCookie();
|
||||
|
||||
QDataStream stream(authorityFile);
|
||||
stream.setByteOrder(QDataStream::BigEndian);
|
||||
|
||||
// Write entry with FamilyLocal and the host name as address
|
||||
writeXauthorityEntry(stream, 256 /* FamilyLocal */, hostname, displayName, name, cookie);
|
||||
|
||||
// Write entry with FamilyWild, no address
|
||||
writeXauthorityEntry(stream, 65535 /* FamilyWild */, QByteArray{}, displayName, name, cookie);
|
||||
|
||||
if (stream.status() != QDataStream::Ok || !authorityFile->flush()) {
|
||||
authorityFile->remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
14
src/xwl/lib/xauthority.h
Normal file
14
src/xwl/lib/xauthority.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class QTemporaryFile;
|
||||
|
||||
bool generateXauthorityFile(int display, QTemporaryFile *authorityFile);
|
|
@ -106,11 +106,15 @@ static bool tryLockFile(const QString &lockFileName)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int listen_helper(const QString &filePath, UnixSocketAddress::Type type)
|
||||
static int listen_helper(const QString &filePath, UnixSocketAddress::Type type, XwaylandSocket::OperationMode mode)
|
||||
{
|
||||
const UnixSocketAddress socketAddress(filePath, type);
|
||||
|
||||
int fileDescriptor = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
int socketFlags = SOCK_STREAM;
|
||||
if (mode == XwaylandSocket::OperationMode::CloseFdsOnExec) {
|
||||
socketFlags |= SOCK_CLOEXEC;
|
||||
}
|
||||
int fileDescriptor = socket(AF_UNIX, socketFlags, 0);
|
||||
if (fileDescriptor == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -159,7 +163,7 @@ static bool checkSocketsDirectory()
|
|||
return true;
|
||||
}
|
||||
|
||||
XwaylandSocket::XwaylandSocket()
|
||||
XwaylandSocket::XwaylandSocket(OperationMode mode)
|
||||
{
|
||||
if (!checkSocketsDirectory()) {
|
||||
return;
|
||||
|
@ -173,13 +177,13 @@ XwaylandSocket::XwaylandSocket()
|
|||
continue;
|
||||
}
|
||||
|
||||
const int unixFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Unix);
|
||||
const int unixFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Unix, mode);
|
||||
if (unixFileDescriptor == -1) {
|
||||
QFile::remove(lockFilePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
const int abstractFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Abstract);
|
||||
const int abstractFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Abstract, mode);
|
||||
if (abstractFileDescriptor == -1) {
|
||||
QFile::remove(lockFilePath);
|
||||
QFile::remove(socketFilePath);
|
|
@ -15,7 +15,12 @@ namespace KWin
|
|||
class XwaylandSocket
|
||||
{
|
||||
public:
|
||||
XwaylandSocket();
|
||||
enum class OperationMode {
|
||||
CloseFdsOnExec,
|
||||
TransferFdsOnExec
|
||||
};
|
||||
|
||||
XwaylandSocket(OperationMode operationMode);
|
||||
~XwaylandSocket();
|
||||
|
||||
bool isValid() const;
|
|
@ -10,7 +10,6 @@
|
|||
*/
|
||||
#include "xwayland.h"
|
||||
#include "databridge.h"
|
||||
#include "xwaylandsocket.h"
|
||||
|
||||
#include "main_wayland.h"
|
||||
#include "options.h"
|
||||
|
@ -19,6 +18,8 @@
|
|||
#include "xcbutils.h"
|
||||
#include "xwayland_logging.h"
|
||||
|
||||
#include "xwaylandsocket.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <KNotification>
|
||||
#include <KSelectionOwner>
|
||||
|
@ -68,119 +69,50 @@ QProcess *Xwayland::process() const
|
|||
return m_xwaylandProcess;
|
||||
}
|
||||
|
||||
static void writeXauthorityEntry(QDataStream &stream, quint16 family,
|
||||
const QByteArray &address, const QByteArray &display,
|
||||
const QByteArray &name, const QByteArray &cookie)
|
||||
{
|
||||
stream << quint16(family);
|
||||
|
||||
auto writeArray = [&stream](const QByteArray &str) {
|
||||
stream << quint16(str.size());
|
||||
stream.writeRawData(str.constData(), str.size());
|
||||
};
|
||||
|
||||
writeArray(address);
|
||||
writeArray(display);
|
||||
writeArray(name);
|
||||
writeArray(cookie);
|
||||
}
|
||||
|
||||
static QByteArray generateXauthorityCookie()
|
||||
{
|
||||
QByteArray cookie;
|
||||
cookie.resize(16); // Cookie must be 128bits
|
||||
|
||||
QRandomGenerator *generator = QRandomGenerator::system();
|
||||
for (int i = 0; i < cookie.size(); ++i) {
|
||||
cookie[i] = uint8_t(generator->bounded(256));
|
||||
}
|
||||
return cookie;
|
||||
}
|
||||
|
||||
static bool generateXauthorityFile(int display, QTemporaryFile *authorityFile)
|
||||
{
|
||||
const QString runtimeDirectory = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
|
||||
|
||||
authorityFile->setFileTemplate(runtimeDirectory + QStringLiteral("/xauth_XXXXXX"));
|
||||
if (!authorityFile->open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray hostname = QHostInfo::localHostName().toUtf8();
|
||||
const QByteArray displayName = QByteArray::number(display);
|
||||
const QByteArray name = QByteArrayLiteral("MIT-MAGIC-COOKIE-1");
|
||||
const QByteArray cookie = generateXauthorityCookie();
|
||||
|
||||
QDataStream stream(authorityFile);
|
||||
stream.setByteOrder(QDataStream::BigEndian);
|
||||
|
||||
// Write entry with FamilyLocal and the host name as address
|
||||
writeXauthorityEntry(stream, 256 /* FamilyLocal */, hostname, displayName, name, cookie);
|
||||
|
||||
// Write entry with FamilyWild, no address
|
||||
writeXauthorityEntry(stream, 65535 /* FamilyWild */, QByteArray{}, displayName, name, cookie);
|
||||
|
||||
if (stream.status() != QDataStream::Ok || !authorityFile->flush()) {
|
||||
authorityFile->remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Xwayland::start()
|
||||
{
|
||||
if (m_xwaylandProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
QScopedPointer<XwaylandSocket> socket(new XwaylandSocket());
|
||||
if (!socket->isValid()) {
|
||||
qCWarning(KWIN_XWL) << "Failed to create Xwayland connection sockets";
|
||||
Q_EMIT errorOccurred();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qEnvironmentVariableIsSet("KWIN_WAYLAND_NO_XAUTHORITY")) {
|
||||
if (!generateXauthorityFile(socket->display(), &m_authorityFile)) {
|
||||
qCWarning(KWIN_XWL) << "Failed to create an Xauthority file";
|
||||
Q_EMIT errorOccurred();
|
||||
return;
|
||||
if (!m_listenFds.isEmpty()) {
|
||||
Q_ASSERT(!m_displayName.isEmpty());
|
||||
} else {
|
||||
m_socket.reset(new XwaylandSocket(XwaylandSocket::OperationMode::CloseFdsOnExec));
|
||||
if (!m_socket->isValid()) {
|
||||
qFatal("Failed to establish X11 socket");
|
||||
}
|
||||
setListenFDs({m_socket->unixFileDescriptor(), m_socket->abstractFileDescriptor()});
|
||||
m_displayName = m_socket->name();
|
||||
}
|
||||
|
||||
m_socket.reset(socket.take());
|
||||
startInternal();
|
||||
}
|
||||
|
||||
if (!startInternal()) {
|
||||
m_authorityFile.remove();
|
||||
m_socket.reset();
|
||||
}
|
||||
void Xwayland::setListenFDs(const QVector<int> &listenFds)
|
||||
{
|
||||
m_listenFds = listenFds;
|
||||
}
|
||||
|
||||
void Xwayland::setDisplayName(const QString &displayName)
|
||||
{
|
||||
m_displayName = displayName;
|
||||
}
|
||||
|
||||
void Xwayland::setXauthority(const QString &xauthority)
|
||||
{
|
||||
m_xAuthority = xauthority;
|
||||
}
|
||||
|
||||
bool Xwayland::startInternal()
|
||||
{
|
||||
Q_ASSERT(!m_xwaylandProcess);
|
||||
|
||||
// The abstract socket file descriptor will be passed to Xwayland and closed by us.
|
||||
const int abstractSocket = dup(m_socket->abstractFileDescriptor());
|
||||
if (abstractSocket == -1) {
|
||||
qCWarning(KWIN_XWL, "Failed to duplicate file descriptor: %s", strerror(errno));
|
||||
Q_EMIT errorOccurred();
|
||||
return false;
|
||||
}
|
||||
auto abstractSocketCleanup = qScopeGuard([&abstractSocket]() {
|
||||
close(abstractSocket);
|
||||
});
|
||||
|
||||
// The unix socket file descriptor will be passed to Xwayland and closed by us.
|
||||
const int unixSocket = dup(m_socket->unixFileDescriptor());
|
||||
if (unixSocket == -1) {
|
||||
qCWarning(KWIN_XWL, "Failed to duplicate file descriptor: %s", strerror(errno));
|
||||
Q_EMIT errorOccurred();
|
||||
return false;
|
||||
}
|
||||
auto unixSocketCleanup = qScopeGuard([&unixSocket]() {
|
||||
close(unixSocket);
|
||||
QVector<int> fdsToClose;
|
||||
auto cleanup = qScopeGuard([&fdsToClose] {
|
||||
for (const int fd : qAsConst(fdsToClose)) {
|
||||
close(fd);
|
||||
}
|
||||
});
|
||||
|
||||
int pipeFds[2];
|
||||
|
@ -189,6 +121,8 @@ bool Xwayland::startInternal()
|
|||
Q_EMIT errorOccurred();
|
||||
return false;
|
||||
}
|
||||
fdsToClose << pipeFds[1];
|
||||
|
||||
int sx[2];
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) {
|
||||
qCWarning(KWIN_XWL, "Failed to open socket for XCB connection: %s", strerror(errno));
|
||||
|
@ -217,24 +151,30 @@ bool Xwayland::startInternal()
|
|||
|
||||
m_xcbConnectionFd = sx[0];
|
||||
|
||||
QStringList arguments {
|
||||
m_socket->name(),
|
||||
QStringLiteral("-displayfd"), QString::number(pipeFds[1]),
|
||||
QStringLiteral("-rootless"),
|
||||
QStringLiteral("-wm"), QString::number(fd),
|
||||
};
|
||||
QStringList arguments;
|
||||
|
||||
if (m_authorityFile.isOpen()) {
|
||||
arguments << QStringLiteral("-auth") << m_authorityFile.fileName();
|
||||
arguments << m_displayName;
|
||||
|
||||
if (!m_listenFds.isEmpty()) {
|
||||
// xauthority externally set and managed
|
||||
if (!m_xAuthority.isEmpty()) {
|
||||
arguments << QStringLiteral("-auth") << m_xAuthority;
|
||||
}
|
||||
|
||||
for (int socket : qAsConst(m_listenFds)) {
|
||||
int dupSocket = dup(socket);
|
||||
fdsToClose << dupSocket;
|
||||
#if defined(HAVE_XWAYLAND_LISTENFD)
|
||||
arguments << QStringLiteral("-listenfd") << QString::number(dupSocket);
|
||||
#else
|
||||
arguments << QStringLiteral("-listen") << QString::number(dupSocket)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_XWAYLAND_LISTENFD)
|
||||
arguments << QStringLiteral("-listenfd") << QString::number(abstractSocket)
|
||||
<< QStringLiteral("-listenfd") << QString::number(unixSocket);
|
||||
#else
|
||||
arguments << QStringLiteral("-listen") << QString::number(abstractSocket)
|
||||
<< QStringLiteral("-listen") << QString::number(unixSocket);
|
||||
#endif
|
||||
arguments << QStringLiteral("-displayfd") << QString::number(pipeFds[1]);
|
||||
arguments << QStringLiteral("-rootless");
|
||||
arguments << QStringLiteral("-wm") << QString::number(fd);
|
||||
|
||||
m_xwaylandProcess = new Process(this);
|
||||
m_xwaylandProcess->setProcessChannelMode(QProcess::ForwardedErrorChannel);
|
||||
|
@ -257,7 +197,6 @@ bool Xwayland::startInternal()
|
|||
connect(m_readyNotifier, &QSocketNotifier::activated, this, &Xwayland::handleXwaylandReady);
|
||||
|
||||
m_xwaylandProcess->start();
|
||||
close(pipeFds[1]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -269,9 +208,6 @@ void Xwayland::stop()
|
|||
}
|
||||
|
||||
stopInternal();
|
||||
|
||||
m_socket.reset();
|
||||
m_authorityFile.remove();
|
||||
}
|
||||
|
||||
void Xwayland::stopInternal()
|
||||
|
@ -435,13 +371,7 @@ void Xwayland::handleXwaylandReady()
|
|||
return;
|
||||
}
|
||||
|
||||
const QByteArray displayName = ':' + QByteArray::number(m_socket->display());
|
||||
|
||||
qCInfo(KWIN_XWL) << "Xwayland server started on display" << displayName;
|
||||
qputenv("DISPLAY", displayName);
|
||||
if (m_authorityFile.isOpen()) {
|
||||
qputenv("XAUTHORITY", m_authorityFile.fileName().toUtf8());
|
||||
}
|
||||
qCInfo(KWIN_XWL) << "Xwayland server started on display" << m_displayName;
|
||||
|
||||
// create selection owner for WM_S0 - magic X display number expected by XWayland
|
||||
m_selectionOwner.reset(new KSelectionOwner("WM_S0", kwinApp()->x11Connection(), kwinApp()->x11RootWindow()));
|
||||
|
@ -456,10 +386,8 @@ void Xwayland::handleXwaylandReady()
|
|||
DataBridge::create(this);
|
||||
|
||||
auto env = m_app->processStartupEnvironment();
|
||||
env.insert(QStringLiteral("DISPLAY"), displayName);
|
||||
if (m_authorityFile.isOpen()) {
|
||||
env.insert(QStringLiteral("XAUTHORITY"), m_authorityFile.fileName());
|
||||
}
|
||||
env.insert(QStringLiteral("DISPLAY"), m_displayName);
|
||||
env.insert(QStringLiteral("XAUTHORITY"), m_xAuthority);
|
||||
m_app->setProcessStartupEnvironment(env);
|
||||
|
||||
Xcb::sync(); // Trigger possible errors, there's still a chance to abort
|
||||
|
|
|
@ -39,6 +39,27 @@ public:
|
|||
*/
|
||||
QProcess *process() const override;
|
||||
|
||||
/**
|
||||
* Set file descriptors that xwayland should use for listening
|
||||
* This is to be used in conjuction with kwin_wayland_wrapper which creates a socket externally
|
||||
* That external process is responsible for setting up the DISPLAY env with a valid value.
|
||||
* Ownership of the file descriptor is not transferrred.
|
||||
*/
|
||||
void setListenFDs(const QVector<int> &listenFds);
|
||||
|
||||
/**
|
||||
* Sets the display name used by XWayland (i.e ':0')
|
||||
* This is to be used in conjuction with kwin_wayland_wrapper to provide the name of the socket
|
||||
* created externally
|
||||
*/
|
||||
void setDisplayName(const QString &displayName);
|
||||
|
||||
/**
|
||||
* Sets the xauthority file to be used by XWayland
|
||||
* This is to be used in conjuction with kwin_wayland_wrapper
|
||||
*/
|
||||
void setXauthority(const QString &xauthority);
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Starts the Xwayland server.
|
||||
|
@ -111,8 +132,13 @@ private:
|
|||
QTimer *m_resetCrashCountTimer = nullptr;
|
||||
ApplicationWaylandAbstract *m_app;
|
||||
QScopedPointer<KSelectionOwner> m_selectionOwner;
|
||||
QTemporaryFile m_authorityFile;
|
||||
// this is only used when kwin is run without kwin_wayland_wrapper
|
||||
QScopedPointer<XwaylandSocket> m_socket;
|
||||
|
||||
QVector<int> m_listenFds;
|
||||
QString m_displayName;
|
||||
QString m_xAuthority;
|
||||
|
||||
int m_crashCount = 0;
|
||||
|
||||
Q_DISABLE_COPY(Xwayland)
|
||||
|
|
Loading…
Reference in a new issue