diff --git a/src/common.h b/src/common.h index e5ebd47..df9c7f5 100644 --- a/src/common.h +++ b/src/common.h @@ -154,6 +154,8 @@ typedef struct session { void *backend_blur_context; /// graphic drivers used enum driver drivers; + /// file watch handle + void *file_watch_handle; /// libev mainloop struct ev_loop *loop; diff --git a/src/file_watch.c b/src/file_watch.c new file mode 100644 index 0000000..f72659b --- /dev/null +++ b/src/file_watch.c @@ -0,0 +1,97 @@ +#include +#include +#include + +#include +#include + +#include "file_watch.h" +#include "list.h" +#include "log.h" +#include "utils.h" + +struct watched_file { + int wd; + void *ud; + file_watch_cb_t cb; + + UT_hash_handle hh; +}; + +struct file_watch_registry { + struct ev_io w; + + struct watched_file *reg; +}; + +static void file_watch_ev_cb(EV_P_ struct ev_io *w, int revent attr_unused) { + auto fwr = (struct file_watch_registry *)w; + struct inotify_event inotify_event; + + while (true) { + auto ret = read(w->fd, &inotify_event, sizeof(struct inotify_event)); + if (ret < 0) { + if (errno != EAGAIN) { + log_error("Failed to read from inotify fd: %s", + strerror(errno)); + } + break; + } + + struct watched_file *wf = NULL; + HASH_FIND_INT(fwr->reg, &inotify_event.wd, wf); + if (!wf) { + log_warn("Got notification for a file I didn't watch."); + continue; + } + wf->cb(wf->ud); + } +} + +void *file_watch_init(EV_P) { + log_debug("Starting watching for file changes"); + int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (fd < 0) { + log_error("inotify_init1 failed: %s", strerror(errno)); + return NULL; + } + auto fwr = ccalloc(1, struct file_watch_registry); + ev_io_init(&fwr->w, file_watch_ev_cb, fd, EV_READ); + ev_io_start(EV_A_ & fwr->w); + + return fwr; +} + +void file_watch_destroy(EV_P_ void *_fwr) { + log_debug("Stopping watching for file changes"); + auto fwr = (struct file_watch_registry *)_fwr; + struct watched_file *i, *tmp; + + HASH_ITER(hh, fwr->reg, i, tmp) { + HASH_DEL(fwr->reg, i); + free(i); + } + + ev_io_stop(EV_A_ & fwr->w); + close(fwr->w.fd); + free(fwr); +} + +bool file_watch_add(void *_fwr, const char *filename, file_watch_cb_t cb, void *ud) { + log_debug("Adding \"%s\" to watched files", filename); + auto fwr = (struct file_watch_registry *)_fwr; + int wd = inotify_add_watch(fwr->w.fd, filename, + IN_CLOSE_WRITE | IN_MOVE_SELF | IN_DELETE_SELF); + if (wd < 0) { + log_error("Failed to watch file \"%s\": %s", filename, strerror(errno)); + return false; + } + + auto w = ccalloc(1, struct watched_file); + w->wd = wd; + w->cb = cb; + w->ud = ud; + + HASH_ADD_INT(fwr->reg, wd, w); + return true; +} diff --git a/src/file_watch.h b/src/file_watch.h new file mode 100644 index 0000000..c249cd2 --- /dev/null +++ b/src/file_watch.h @@ -0,0 +1,10 @@ +#pragma once +#include + +#include + +typedef void (*file_watch_cb_t)(void *); + +void *file_watch_init(EV_P); +bool file_watch_add(void *, const char *, file_watch_cb_t, void *); +void file_watch_destroy(EV_P_ void *); diff --git a/src/meson.build b/src/meson.build index de3aff2..e2e0e05 100644 --- a/src/meson.build +++ b/src/meson.build @@ -9,7 +9,7 @@ base_deps = [ srcs = [ files('picom.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.c', 'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c', - 'options.c', 'event.c', 'cache.c', 'atom.c') ] + 'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c') ] picom_inc = include_directories('.') cflags = [] diff --git a/src/picom.c b/src/picom.c index 2cb3954..2fc1939 100644 --- a/src/picom.c +++ b/src/picom.c @@ -54,6 +54,7 @@ #endif #include "atom.h" #include "event.h" +#include "file_watch.h" #include "list.h" #include "options.h" #include "uthash_extra.h" @@ -1538,6 +1539,11 @@ static void exit_enable(EV_P attr_unused, ev_signal *w, int revents attr_unused) quit(ps); } +static void config_file_change_cb(void *_ps) { + auto ps = (struct session *)_ps; + reset_enable(ps->loop, NULL, 0); +} + /** * Initialize a session. * @@ -1935,6 +1941,12 @@ static session_t *session_init(int argc, char **argv, Display *dpy, free(config_file_to_free); exit(0); } + + ps->file_watch_handle = file_watch_init(ps->loop); + if (ps->file_watch_handle) { + file_watch_add(ps->file_watch_handle, config_file, config_file_change_cb, ps); + } + free(config_file_to_free); if (bkend_use_glx(ps) && !ps->o.experimental_backends) { @@ -2100,6 +2112,9 @@ static void session_destroy(session_t *ps) { unredirect(ps); } + file_watch_destroy(ps->loop, ps->file_watch_handle); + ps->file_watch_handle = NULL; + // Stop listening to events on root window xcb_change_window_attributes(ps->c, ps->root, XCB_CW_EVENT_MASK, (const uint32_t[]){0});