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:
parent
5317ca9da0
commit
ec9c2c3e68
4 changed files with 142 additions and 125 deletions
|
@ -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})
|
||||
|
|
|
@ -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;
|
||||
}
|
131
src/helpers/wayland_wrapper/kwin_wrapper.cpp
Normal file
131
src/helpers/wayland_wrapper/kwin_wrapper.cpp
Normal 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"
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue