diff --git a/src/helpers/wayland_wrapper/CMakeLists.txt b/src/helpers/wayland_wrapper/CMakeLists.txt index 7976e29a7f..0627c33ae1 100644 --- a/src/helpers/wayland_wrapper/CMakeLists.txt +++ b/src/helpers/wayland_wrapper/CMakeLists.txt @@ -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}) diff --git a/src/helpers/wayland_wrapper/kwin_wrapper.c b/src/helpers/wayland_wrapper/kwin_wrapper.c deleted file mode 100644 index fd44b79454..0000000000 --- a/src/helpers/wayland_wrapper/kwin_wrapper.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2020 - - 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 -#include -#include -#include -#include -#include - -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; -} diff --git a/src/helpers/wayland_wrapper/kwin_wrapper.cpp b/src/helpers/wayland_wrapper/kwin_wrapper.cpp new file mode 100644 index 0000000000..2cd450b218 --- /dev/null +++ b/src/helpers/wayland_wrapper/kwin_wrapper.cpp @@ -0,0 +1,131 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2020 + + 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 +#include +#include + +#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" diff --git a/src/helpers/wayland_wrapper/wl-socket.h b/src/helpers/wayland_wrapper/wl-socket.h index 2fd970b5e2..ba327206e8 100644 --- a/src/helpers/wayland_wrapper/wl-socket.h +++ b/src/helpers/wayland_wrapper/wl-socket.h @@ -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