diff --git a/src/main.cpp b/src/main.cpp index 045b2cd49..12fd64d1f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,9 @@ #include #include #include +#include +#include #include #include #include @@ -70,35 +72,90 @@ void startSignalThread() { } } +static int signal_fds[2]; + +extern "C" void raw_signal_handler(int signum) { + // just write a single byte to be more reliable + auto sigbyte = static_cast(signum); + // The only way I know of that this could fail is if we have queued a truly + // remarkable number of signals without handling them + write(signal_fds[1], &sigbyte, 1); +} + +static void dispatch_signal(unsigned char signum) { + auto client = waybar::Client::inst(); + if (client == nullptr) { + // TODO: should we do something for SIGINT? + return; + } + if (signum == SIGUSR1) { + for (auto& bar : client->bars) { + bar->toggle(); + } + } else if (signum == SIGUSR2) { + spdlog::info("Reloading..."); + reload = true; + client->reset(); + } else if (signum == SIGINT) { + spdlog::info("Quitting."); + reload = false; + client->reset(); + } else if (signum > SIGRTMIN && signum <= SIGRTMAX) { + for (auto& bar : client->bars) { + bar->handleSignal(signum); + } + } +} + +static bool handle_signals(Glib::IOCondition cond) { + unsigned char buf[16]; + const size_t bufsize = sizeof(buf); + while (true) { + auto n = read(signal_fds[0], buf, bufsize); + if (n < 0) { + if (errno == EAGAIN) { + return true; + } + throw std::system_error(errno, std::system_category()); + } + for (int i = 0; i < n; i++) { + dispatch_signal(buf[i]); + } + if (static_cast(n) < bufsize) { + return true; + } + } +} + int main(int argc, char* argv[]) { try { auto* client = waybar::Client::inst(); - std::signal(SIGUSR1, [](int /*signal*/) { - for (auto& bar : waybar::Client::inst()->bars) { - bar->toggle(); - } - }); + // It would be nice if we could use g_unix_signal_add, but unfortunately, that + // doesn't support RT signals + if (pipe(signal_fds)) { + throw std::runtime_error("Failed to create pipe"); + } + std::signal(SIGUSR1, &raw_signal_handler); + std::signal(SIGUSR2, &raw_signal_handler); + std::signal(SIGINT, &raw_signal_handler); + for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) { + std::signal(sig, &raw_signal_handler); + } - std::signal(SIGUSR2, [](int /*signal*/) { - spdlog::info("Reloading..."); - reload = true; - waybar::Client::inst()->reset(); - }); + auto signalPipe = Glib::IOChannel::create_from_fd(signal_fds[0]); + // Make the read side, non-blocking + int pipe_flags = fcntl(signal_fds[0], F_GETFL); + if (pipe_flags != -1) { + pipe_flags = fcntl(signal_fds[0], F_SETFL, pipe_flags | O_NONBLOCK); + } + if (pipe_flags == -1) { + throw std::runtime_error("Failed to set pipe to nonblocking mode"); + } - std::signal(SIGINT, [](int /*signal*/) { - spdlog::info("Quitting."); - reload = false; - waybar::Client::inst()->reset(); - }); + Glib::signal_io().connect(sigc::ptr_fun(handle_signals), signal_fds[0], + Glib::IOCondition::IO_IN); - for (int sig = SIGRTMIN + 1; sig <= SIGRTMAX; ++sig) { - std::signal(sig, [](int sig) { - for (auto& bar : waybar::Client::inst()->bars) { - bar->handleSignal(sig); - } - }); - } startSignalThread(); auto ret = 0;