Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ Sockets=treeland-xwayland.socket
UnsetEnvironment=DISPLAY
ExecStart=@CMAKE_INSTALL_FULL_LIBEXECDIR@/treeland-sd --type xwayland
ExecStop=-/usr/bin/systemctl --user unset-environment DISPLAY
Slice=session.slice
Restart=on-failure
RestartSec=3s
Slice=session.slice
4 changes: 4 additions & 0 deletions misc/systemd/treeland.service.in
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Environment=ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer
Environment=ASAN_OPTIONS=log_path=/tmp/treeland-asan:detect_leaks=0:abort_on_error=1:symbolize=1
Environment=DDM_DISPLAY_MANAGER=1
Environment=TREELAND_INPUT_ENHANCE=1
# Kill orphaned Xwayland processes from previous treeland instance.
# When treeland crashes, its Xwayland child processes may become orphans
# because the cleanup code in Session::~Session() is not executed.
ExecStartPre=-/usr/bin/pkill -9 -u dde -f '^Xwayland[[:space:]]+:[0-9]+'
ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/treeland.sh --lockscreen
Restart=on-failure
RestartSec=1s
Expand Down
212 changes: 115 additions & 97 deletions src/systemd-socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
#include <QDBusServiceWatcher>
#include <QDBusUnixFileDescriptor>
#include <QDebug>
#include <QDir>

Check warning on line 16 in src/systemd-socket.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDir> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QFile>

Check warning on line 17 in src/systemd-socket.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QFile> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QLoggingCategory>

Check warning on line 18 in src/systemd-socket.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QLoggingCategory> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTimer>

Check warning on line 19 in src/systemd-socket.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTimer> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTemporaryFile>

Check warning on line 20 in src/systemd-socket.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTemporaryFile> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QtEnvironmentVariables>

Check warning on line 21 in src/systemd-socket.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QtEnvironmentVariables> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <sys/socket.h>
#include <sys/un.h>
Expand All @@ -29,19 +30,122 @@
typedef QMap<QString, QString> StringMap;
Q_DECLARE_METATYPE(StringMap)

class SignalReceiver : public QObject
class SocketActivator : public QObject
{
Q_OBJECT
public:
SignalReceiver(std::function<void()> activateFdFunc, QObject *parent = nullptr)
: QObject(parent), activateFd(activateFdFunc) {
SocketActivator(QDBusUnixFileDescriptor unixFileDescriptor,
QString type,
QDBusConnection connection,
QObject *parent = nullptr)
: QObject(parent)
, m_unixFileDescriptor(unixFileDescriptor)
, m_type(std::move(type))
, m_connection(connection) {
}

void start() {
m_connection.connect("org.deepin.Compositor1",
"/org/deepin/Compositor1",
"org.deepin.Compositor1",
"SessionChanged",
this,
SLOT(activate()));
activate();
}

~SocketActivator() {
qDeleteAll(m_tmpFiles);
}

public Q_SLOTS:

Check warning on line 61 in src/systemd-socket.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

There is an unknown macro here somewhere. Configuration is required. If Q_SLOTS is a macro then please configure it.
void onSessionChanged() {
activateFd();
void activate() {
QDBusInterface updateFd("org.deepin.Compositor1",
"/org/deepin/Compositor1",
"org.deepin.Compositor1",
m_connection);

if (updateFd.isValid()) {
if (m_type == "wayland") {
updateFd.call("ActivateWayland", QVariant::fromValue(m_unixFileDescriptor));

QDBusInterface dbus("org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
QDBusConnection::sessionBus());
StringMap env;
env["WAYLAND_DISPLAY"] = "treeland.socket";

const auto extraEnvs = qgetenv("TREELAND_SESSION_ENVIRONMENTS");
if (!extraEnvs.isEmpty()) {
const auto envs = extraEnvs.split('\n');
for (const auto &i : envs) {
const auto pair = i.split('=');
if (pair.size() == 2) {
env[QString::fromLocal8Bit(pair[0])] = QString::fromLocal8Bit(pair[1]);
}
}
}

dbus.call("UpdateActivationEnvironment", QVariant::fromValue(env));

sd_notify(0, "READY=1");
} else if (m_type == "xwayland") {
QDBusMessage reply = updateFd.call("XWaylandName");
if (reply.type() == QDBusMessage::ReplyMessage) {
QVariantList values = reply.arguments();
if (values.size() < 2) {
qCWarning(lcSdSocket) << "Invalid XWaylandName reply";
return;
}
QString xwaylandName = values.at(0).toString();
QByteArray auth = values.at(1).toByteArray();

QByteArray runtimeDir = qgetenv("XDG_RUNTIME_DIR");
if (runtimeDir.isEmpty())
runtimeDir = QDir::tempPath().toLocal8Bit();
QTemporaryFile *authFile = new QTemporaryFile();
authFile->setFileTemplate(QStringLiteral("%1/.xauth_XXXXXX").arg(runtimeDir));
if (!authFile->open()) {
qCWarning(lcSdSocket) << "Failed to create temporary xauth file";
return;
}
QString authFileName = authFile->fileName();
m_tmpFiles.append(authFile);
authFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner);
authFile->write(auth);
authFile->flush();
authFile->close();

QDBusInterface dbus("org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
QDBusConnection::sessionBus());
StringMap env;
env["DISPLAY"] = xwaylandName;
env["XAUTHORITY"] = authFileName;
dbus.call("UpdateActivationEnvironment", QVariant::fromValue(env));

sd_notify(0, "READY=1");
} else if (reply.type() == QDBusMessage::ErrorMessage) {
qCWarning(lcSdSocket) << "XWaylandName failed:" << reply.errorMessage();
}
}
}
if (m_type == "xwayland" && !updateFd.isValid()) {
if (++m_retryCount < 20)
QTimer::singleShot(500, this, &SocketActivator::activate);
else
qCWarning(lcSdSocket) << "XWayland activation timed out after 10s";
}
}

private:
std::function<void()> activateFd;
QDBusUnixFileDescriptor m_unixFileDescriptor;
QString m_type;
QDBusConnection m_connection;
QList<QTemporaryFile *> m_tmpFiles;
int m_retryCount = 0;
};

int main(int argc, char *argv[])
Expand Down Expand Up @@ -70,100 +174,14 @@
exit(1);
}

QList<QTemporaryFile *> tmpFiles;
QDBusUnixFileDescriptor unixFileDescriptor(SD_LISTEN_FDS_START);

auto active = [unixFileDescriptor, type, &tmpFiles](QDBusConnection connection) {
auto activateFd = [unixFileDescriptor, type, &connection, &tmpFiles] {
QDBusInterface updateFd("org.deepin.Compositor1",
"/org/deepin/Compositor1",
"org.deepin.Compositor1",
connection);
auto *s1 = new SocketActivator(unixFileDescriptor, type, QDBusConnection::sessionBus(), &app);
auto *s2 = new SocketActivator(unixFileDescriptor, type, QDBusConnection::systemBus(), &app);
s1->start();
s2->start();

if (updateFd.isValid()) {
if (type == "wayland") {
updateFd.call("ActivateWayland", QVariant::fromValue(unixFileDescriptor));

QDBusInterface dbus("org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
QDBusConnection::sessionBus());
StringMap env;
env["WAYLAND_DISPLAY"] = "treeland.socket";

const auto extraEnvs = qgetenv("TREELAND_SESSION_ENVIRONMENTS");
if (!extraEnvs.isEmpty()) {
const auto envs = extraEnvs.split('\n');
for (const auto &i : envs) {
const auto pair = i.split('=');
if (pair.size() == 2) {
env[QString::fromLocal8Bit(pair[0])] = QString::fromLocal8Bit(pair[1]);
}
}
}

auto reply = dbus.call("UpdateActivationEnvironment", QVariant::fromValue(env));

sd_notify(0, "READY=1");
} else if (type == "xwayland") {
QDBusMessage reply = updateFd.call("XWaylandName");
if (reply.type() == QDBusMessage::ReplyMessage) {
QVariantList values = reply.arguments();
if (values.size() < 2) {
qCWarning(lcSdSocket) << "Invalid XWaylandName reply";
return;
}
QString xwaylandName = values.at(0).toString();
QByteArray auth = values.at(1).toByteArray();

QByteArray runtimeDir = qgetenv("XDG_RUNTIME_DIR");
if (runtimeDir.isEmpty())
runtimeDir = QDir::tempPath().toLocal8Bit();
QTemporaryFile *authFile = new QTemporaryFile();
authFile->setFileTemplate(QStringLiteral("%1/.xauth_XXXXXX").arg(runtimeDir));
if (!authFile->open()) {
qCWarning(lcSdSocket) << "Failed to create temporary xauth file";
return;
}
QString authFileName = authFile->fileName();
tmpFiles.append(authFile);
authFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner);
authFile->write(auth);
authFile->flush();
authFile->close();

QDBusInterface dbus("org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
QDBusConnection::sessionBus());
StringMap env;
env["DISPLAY"] = xwaylandName;
env["XAUTHORITY"] = authFileName;
auto reply =
dbus.call("UpdateActivationEnvironment", QVariant::fromValue(env));

sd_notify(0, "READY=1");
}
}
}
};

connection.connect("org.deepin.Compositor1",
"/org/deepin/Compositor1",
"org.deepin.Compositor1",
"SessionChanged",
new SignalReceiver(activateFd),
SLOT(onSessionChanged()));
activateFd();
};

active(QDBusConnection::sessionBus());
active(QDBusConnection::systemBus());

int ret = app.exec();
for (auto i : std::as_const(tmpFiles))
delete i;
return ret;
return app.exec();
}

#include "systemd-socket.moc"

Check warning on line 187 in src/systemd-socket.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "systemd-socket.moc" not found.
Loading