Port kwin_wayland_wrapper to Qt/C++

We inintially created the wrapper to be light so avoided the QtCore
dependency and did it all in C.

In hindsight changing requirements means we want to add several more
features in here. The first step is to port to a more readable language
and toolkit.
This commit is contained in:
David Edmundson 2021-03-30 16:59:15 +01:00
parent 5317ca9da0
commit ec9c2c3e68
4 changed files with 142 additions and 125 deletions

View file

@ -1,3 +1,4 @@
add_executable(kwin_wayland_wrapper kwin_wrapper.c wl-socket.c)
add_executable(kwin_wayland_wrapper kwin_wrapper.cpp wl-socket.c)
target_link_libraries(kwin_wayland_wrapper Qt5::Core)
set_property(TARGET kwin_wayland_wrapper PROPERTY C_STANDARD 11)
install(TARGETS kwin_wayland_wrapper ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

View file

@ -1,124 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2020 <davidedmundson@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
/**
* This tiny executable creates a socket, then starts kwin passing it the FD to the wayland socket.
* The WAYLAND_DISPLAY environment variable gets set here and passed to all spawned kwin instances.
* On any non-zero kwin exit kwin gets restarted.
*
* After restart kwin is relaunched but now with the KWIN_RESTART_COUNT env set to an incrementing counter
*
* It's somewhat akin to systemd socket activation, but we also need the lock file, finding the next free socket
* and so on, hence our own binary.
*
* Usage kwin_wayland_wrapper [argForKwin] [argForKwin] ...
*/
#include "wl-socket.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
char *old_wayland_env = NULL;
#define WAYLAND_ENV_NAME "WAYLAND_DISPLAY"
pid_t launch_kwin(struct wl_socket *socket, int argc, char **argv)
{
printf("Launching kwin\n");
pid_t pid = fork();
if (pid == 0) { /* child process */
char fdString[5]; // long enough string to contain what is probably a 1 digit number.
snprintf(fdString, sizeof(fdString) - 1, "%d", wl_socket_get_fd(socket));
char **args = calloc(argc + 6, sizeof(char *));
unsigned int pos = 0;
args[pos++] = (char *)"kwin_wayland"; //process name is the first argument by convention
args[pos++] = (char *)"--wayland_fd";
args[pos++] = fdString;
// for running in nested wayland, pass the original socket name
if (old_wayland_env) {
args[pos++] = (char *)"--wayland-display";
args[pos++] = old_wayland_env;
}
//copy passed args
for (int i = 0; i < argc; i++) {
args[pos++] = argv[i];
}
args[pos++] = NULL;
execvp("kwin_wayland", args);
free(args);
exit(127); /* if exec fails */
} else {
return pid;
}
}
int main(int argc, char **argv)
{
struct wl_socket *socket = wl_socket_create();
if (!socket) {
return -1;
}
// copy the old WAYLAND_DISPLAY as we are about to overwrite it and kwin may need it
if (getenv(WAYLAND_ENV_NAME)) {
old_wayland_env = strdup(getenv(WAYLAND_ENV_NAME));
}
setenv(WAYLAND_ENV_NAME, wl_socket_get_display_name(socket), 1);
int crashCount = 0;
while (crashCount < 10) {
int status;
if (crashCount > 0) {
char restartCountEnv[3];
snprintf(restartCountEnv, sizeof(restartCountEnv) - 1, "%d", crashCount);
setenv("KWIN_RESTART_COUNT", restartCountEnv, 1);
}
// forward our own arguments, but drop the first, as that will be our own executable name
pid_t pid = launch_kwin(socket, argc - 1, &argv[1]);
if (pid < 0) {
// failed to launch kwin, we can just quit immediately
break;
}
waitpid(pid, &status, 0); /* wait for child */
crashCount++;
if (WIFEXITED(status)) {
int exit_status = WEXITSTATUS(status);
if (exit_status == 133) {
crashCount = 0;
fprintf(stderr, "Compositor restarted, respawning\n");
} else {
fprintf(stderr, "KWin exited with code %d\n", exit_status);
break;
}
} else if (WIFSIGNALED(status)) {
// we crashed! Let's go again!
pid = 0;
fprintf(stderr, "Compositor crashed, respawning\n");
}
}
wl_socket_destroy(socket);
free(old_wayland_env);
return 0;
}

View file

@ -0,0 +1,131 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2020 <davidedmundson@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
/**
* This tiny executable creates a socket, then starts kwin passing it the FD to the wayland socket.
* The WAYLAND_DISPLAY environment variable gets set here and passed to all spawned kwin instances.
* On any non-zero kwin exit kwin gets restarted.
*
* After restart kwin is relaunched but now with the KWIN_RESTART_COUNT env set to an incrementing counter
*
* It's somewhat akin to systemd socket activation, but we also need the lock file, finding the next free socket
* and so on, hence our own binary.
*
* Usage kwin_wayland_wrapper [argForKwin] [argForKwin] ...
*/
#include <QCoreApplication>
#include <QProcess>
#include <QDebug>
#include "wl-socket.h"
#define WAYLAND_ENV_NAME "WAYLAND_DISPLAY"
class KWinWrapper : public QObject
{
Q_OBJECT
public:
KWinWrapper(QObject *parent);
~KWinWrapper();
void run();
int runKwin();
private:
wl_socket *m_socket;
QString m_oldWaylandEnv;
};
KWinWrapper::KWinWrapper(QObject* parent)
: QObject(parent)
{
m_socket = wl_socket_create();
if (!m_socket) {
qFatal("Could not create wayland socket");
}
// copy the old WAYLAND_DISPLAY as we are about to overwrite it and kwin may need it
if (qEnvironmentVariableIsSet(WAYLAND_ENV_NAME)) {
m_oldWaylandEnv = qgetenv(WAYLAND_ENV_NAME);
}
qputenv(WAYLAND_ENV_NAME, wl_socket_get_display_name(m_socket));
}
KWinWrapper::~KWinWrapper()
{
wl_socket_destroy(m_socket);
}
void KWinWrapper::run()
{
int crashCount = 0;
while (crashCount < 10) {
if (crashCount > 0) {
qputenv("KWIN_RESTART_COUNT", QByteArray::number(crashCount));
}
int exitStatus = runKwin();
if (exitStatus == 133) {
crashCount = 0;
qDebug() << "Compositor restarted, respawning" << Qt::endl;
} else if (exitStatus == -1) {
// kwin_crashed, lets go again
qWarning() << "Compositor crashed, respawning" << Qt::endl;
} else {
qWarning() << "Compositor exited with code" << exitStatus << Qt::endl;
break;
}
}
}
int KWinWrapper::runKwin()
{
qDebug() << "Launching kwin\n";
auto process = new QProcess(qApp);
process->setProgram("kwin_wayland");
QStringList args;
args << "--wayland_fd" << QString::number(wl_socket_get_fd(m_socket));
if (!m_oldWaylandEnv.isEmpty()) {
args << "--wayland-display" << m_oldWaylandEnv;
}
// attach our main process arguments
// the first entry is dropped as it will be our program name
args << qApp->arguments().mid(1);
process->setProcessChannelMode(QProcess::ForwardedChannels);
process->setArguments(args);
process->start();
process->waitForFinished(-1);
if (process->exitStatus() == QProcess::CrashExit) {
return -1;
}
return process->exitCode();
}
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
KWinWrapper wrapper(&app);
wrapper.run();
return 0;
}
#include "kwin_wrapper.moc"

View file

@ -9,6 +9,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* Allocate and create a socket
* It is bound and accepted
@ -29,3 +34,7 @@ char *wl_socket_get_display_name(struct wl_socket *);
* Cleanup resources and close the FD
*/
void wl_socket_destroy(struct wl_socket *socket);
#ifdef __cplusplus
}
#endif