1
0
mirror of https://github.com/emersion/kanshi synced 2024-09-19 10:21:35 +02:00
kanshi/event-loop.c

151 lines
3.1 KiB
C
Raw Normal View History

#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "kanshi.h"
static int set_pipe_flags(int fd) {
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
perror("fnctl F_GETFL failed");
return -1;
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
perror("fnctl F_SETFL failed");
return -1;
}
flags = fcntl(fd, F_GETFD);
if (flags == -1) {
perror("fnctl F_GETFD failed");
return -1;
}
flags |= O_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1) {
perror("fnctl F_SETFD failed");
return -1;
}
return 0;
}
static int signal_pipefds[2];
static void signal_handler(int signum) {
if (write(signal_pipefds[1], &signum, sizeof(signum)) == -1) {
abort();
}
}
enum readfds_type {
FD_WAYLAND,
FD_SIGNAL,
FD_COUNT,
};
int kanshi_main_loop(struct kanshi_state *state) {
if (pipe(signal_pipefds) == -1) {
perror("read from signalfd failed");
return EXIT_FAILURE;
}
if (set_pipe_flags(signal_pipefds[0]) == -1) {
return EXIT_FAILURE;
}
if (set_pipe_flags(signal_pipefds[1]) == -1) {
return EXIT_FAILURE;
}
struct sigaction action;
sigfillset(&action.sa_mask);
action.sa_flags = 0;
action.sa_handler = signal_handler;
sigaction(SIGINT, &action, NULL);
sigaction(SIGQUIT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGHUP, &action, NULL);
struct pollfd readfds[FD_COUNT] = {0};
readfds[FD_WAYLAND].fd = wl_display_get_fd(state->display);
readfds[FD_WAYLAND].events = POLLIN;
readfds[FD_SIGNAL].fd = signal_pipefds[0];
readfds[FD_SIGNAL].events = POLLIN;
while (state->running) {
while (wl_display_prepare_read(state->display) != 0) {
if (wl_display_dispatch_pending(state->display) == -1) {
return EXIT_FAILURE;
}
}
int ret;
while (true) {
ret = wl_display_flush(state->display);
if (ret != -1 || errno != EAGAIN) {
break;
}
}
if (ret < 0 && errno != EPIPE) {
goto read_error;
}
do {
ret = poll(readfds, sizeof(readfds) / sizeof(readfds[0]), -1);
} while (ret == -1 && errno == EINTR);
/* will only be -1 if errno wasn't EINTR */
if (ret == -1) {
goto read_error;
}
if (wl_display_read_events(state->display) == -1) {
return EXIT_FAILURE;
}
if (readfds[FD_SIGNAL].revents & POLLIN) {
for (;;) {
int signum;
ssize_t s
= read(readfds[FD_SIGNAL].fd, &signum, sizeof(signum));
if (s == 0) {
break;
}
if (s < 0) {
if (errno == EAGAIN) {
break;
}
perror("read from signal pipe failed");
return EXIT_FAILURE;
}
if (s < (ssize_t) sizeof(signum)) {
fprintf(stderr, "read too few bytes from signal pipe\n");
return EXIT_FAILURE;
}
switch (signum) {
case SIGHUP:
kanshi_reload_config(state);
break;
default:
/* exiting after signal considered successful */
return EXIT_SUCCESS;
}
}
}
if (wl_display_dispatch_pending(state->display) == -1) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
read_error:
wl_display_cancel_read(state->display);
return EXIT_FAILURE;
}