kwin/helpers/wayland_wrapper/kwin_wrapper.c
2020-12-16 13:53:24 +01:00

119 lines
3.5 KiB
C

/*
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 *));
uint 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 */
if (WIFEXITED(status)) {
int exit_status = WEXITSTATUS(status);
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");
}
crashCount++;
}
wl_socket_destroy(socket);
free(old_wayland_env);
return 0;
}