Files
fcitx5-m3ui/src/plasmathemewatchdog.cpp
T
justmeandClaude Sonnet 5.5 0804634dc1 Add Material 3 candidate window addon for Fcitx5
Fork of the classicui addon (Fcitx5 5.1.23) with a Material 3 look, spring
based animations and a right click action menu for candidates.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 21:58:54 +08:00

155 lines
4.6 KiB
C++

/*
* SPDX-FileCopyrightText: 2022~2022 CSSlayer <wengxt@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
*/
#include "plasmathemewatchdog.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <cassert>
#include <cerrno>
#include <csignal>
#include <cstdint>
#include <functional>
#include <stdexcept>
#include <string>
#include <utility>
#include "fcitx-utils/event.h"
#include "fcitx-utils/eventloopinterface.h"
#include "fcitx-utils/fs.h"
#include "fcitx-utils/standardpaths.h"
#include "fcitx-utils/unixfd.h"
#include "common.h"
#if defined(__FreeBSD__)
#include <sys/procctl.h>
#elif defined(__linux__)
#include <sys/prctl.h>
#endif
namespace fcitx::m3ui {
#define PLASMA_THEME_GENERATOR "fcitx5-plasma-theme-generator";
bool PlasmaThemeWatchdog::isAvailable() {
static const std::string binaryName = PLASMA_THEME_GENERATOR;
return StandardPaths::hasExecutable(binaryName);
}
PlasmaThemeWatchdog::PlasmaThemeWatchdog(EventLoop *event,
std::function<void()> callback)
: callback_(std::move(callback)) {
int pipefd[2];
int ret = ::pipe(pipefd);
if (ret == -1) {
throw std::runtime_error("Failed to create pipe");
}
::fcntl(pipefd[0], F_SETFD, FD_CLOEXEC);
::fcntl(pipefd[0], F_SETFL, ::fcntl(pipefd[0], F_GETFL) | O_NONBLOCK);
::fcntl(pipefd[1], F_SETFL, ::fcntl(pipefd[1], F_GETFL) | O_NONBLOCK);
auto pid = fork();
if (pid == 0) {
close(pipefd[0]);
#if defined(__FreeBSD__)
int procctl_value = SIGKILL;
procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &procctl_value);
#elif defined(__linux__)
prctl(PR_SET_PDEATHSIG, SIGKILL);
#endif
// Redirect stdin to /dev/null.
UnixFD fd = UnixFD::own(open("/dev/null", O_RDWR | O_CLOEXEC));
if (!fd.isValid()) {
_exit(1);
}
if (fd.fd() < 3) {
UnixFD copyFd(fd.fd(), 3);
if (!copyFd.isValid()) {
_exit(1);
}
fd = std::move(copyFd);
}
assert(fd.fd() >= 3);
if (dup2(fd.fd(), STDIN_FILENO) < 0) {
_exit(1);
}
fd.reset();
signal(SIGINT, SIG_IGN);
char arg0[] = PLASMA_THEME_GENERATOR;
char arg1[] = "--fd";
std::string value = std::to_string(pipefd[1]);
char *args[] = {arg0, arg1, value.data(), nullptr};
execvp(arg0, args);
_exit(1);
} else {
close(pipefd[1]);
monitorFD_.give(pipefd[0]);
generator_ = pid;
running_ = true;
ioEvent_ = event->addIOEvent(
monitorFD_.fd(),
{IOEventFlag::In, IOEventFlag::Err, IOEventFlag::Hup},
[this, event](EventSourceIO *, int fd, IOEventFlags flags) {
if (flags.testAny(
IOEventFlags{IOEventFlag::Err, IOEventFlag::Hup})) {
cleanup();
return true;
}
if (!flags.test(IOEventFlag::In)) {
return true;
}
ssize_t result = 0;
do {
uint8_t dummy;
result = fs::safeRead(fd, &dummy, sizeof(dummy));
} while (result > 0);
// Check if pipe EOF, or other errors.
if (result == 0 || (result < 0 && errno != EAGAIN)) {
cleanup();
return true;
}
timerEvent_ = event->addTimeEvent(
CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000000, 0,
[this](EventSourceTime *, uint64_t) {
callback_();
return true;
});
return true;
});
}
}
PlasmaThemeWatchdog::~PlasmaThemeWatchdog() {
destruct_ = true;
cleanup();
}
void PlasmaThemeWatchdog::cleanup() {
running_ = false;
CLASSICUI_INFO() << "Cleanup Plasma Theme generator.";
if (!destruct_) {
// For certain distribution, it is possible that
// fcitx5-plasma-theme-generator command presents, but not usable since
// plasma is set to be an optional dependency to fcitx5-configtool. If
// that happens, we need to notify to reload the theme.
callback_();
}
if (generator_ == 0) {
return;
}
int stat = 0;
int ret;
kill(generator_, SIGKILL);
do {
ret = waitpid(generator_, &stat, WNOHANG);
} while (ret == -1 && errno == EINTR);
generator_ = 0;
ioEvent_.reset();
}
} // namespace fcitx::m3ui