diff --git a/Makefile b/Makefile index f375fa3..8b3f4b5 100644 --- a/Makefile +++ b/Makefile @@ -21,9 +21,13 @@ ifndef NO_LIBEXIF LIBS += -lexif endif +# select autoreload backend +# overwritten with `make AUTORELOAD=nop` +AUTORELOAD := inotify + .PHONY: clean install uninstall -SRC := commands.c image.c main.c options.c thumbs.c util.c window.c +SRC := autoreload_$(AUTORELOAD).c commands.c image.c main.c options.c thumbs.c util.c window.c DEP := $(SRC:.c=.d) OBJ := $(SRC:.c=.o) diff --git a/autoreload.h b/autoreload.h new file mode 100644 index 0000000..bb30eb6 --- /dev/null +++ b/autoreload.h @@ -0,0 +1,36 @@ +/* Copyright 2017 Max Voit + * + * This file is part of sxiv. + * + * sxiv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * sxiv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sxiv. If not, see . + */ + +#ifndef AUTORELOAD_H +#define AUTORELOAD_H + +#include "types.h" + +typedef struct { + int fd; + int wd_dir; + int wd_file; + char *filename; +} arl_t; + +void arl_init(arl_t*); +void arl_cleanup(arl_t*); +void arl_setup(arl_t*, const char* /* result of realpath(3) */); +bool arl_handle(arl_t*); + +#endif /* AUTORELOAD_H */ diff --git a/autoreload_inotify.c b/autoreload_inotify.c new file mode 100644 index 0000000..fe05483 --- /dev/null +++ b/autoreload_inotify.c @@ -0,0 +1,113 @@ +/* Copyright 2017 Max Voit + * + * This file is part of sxiv. + * + * sxiv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * sxiv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sxiv. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "autoreload.h" + +void arl_init(arl_t *arl) +{ + arl->fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); + arl->wd_dir = arl->wd_file = -1; + if (arl->fd == -1) + error(0, 0, "Could not initialize inotify, no automatic image reloading"); +} + +CLEANUP void arl_cleanup(arl_t *arl) +{ + if (arl->fd != -1) + close(arl->fd); + free(arl->filename); +} + +static void rm_watch(int fd, int *wd) +{ + if (*wd != -1) { + inotify_rm_watch(fd, *wd); + *wd = -1; + } +} + +static void add_watch(int fd, int *wd, const char *path, uint32_t mask) +{ + *wd = inotify_add_watch(fd, path, mask); + if (*wd == -1) + error(0, errno, "inotify: %s", path); +} + +void arl_setup(arl_t *arl, const char *filepath) +{ + char *base = strrchr(filepath, '/'); + + if (arl->fd == -1) + return; + + rm_watch(arl->fd, &arl->wd_dir); + rm_watch(arl->fd, &arl->wd_file); + + add_watch(arl->fd, &arl->wd_file, filepath, IN_CLOSE_WRITE | IN_DELETE_SELF); + + free(arl->filename); + arl->filename = estrdup(filepath); + + if (base != NULL) { + arl->filename[++base - filepath] = '\0'; + add_watch(arl->fd, &arl->wd_dir, arl->filename, IN_CREATE | IN_MOVED_TO); + strcpy(arl->filename, base); + } +} + +union { + char d[4096]; /* aligned buffer */ + struct inotify_event e; +} buf; + +bool arl_handle(arl_t *arl) +{ + bool reload = false; + char *ptr; + const struct inotify_event *event; + + for (;;) { + ssize_t len = read(arl->fd, buf.d, sizeof(buf.d)); + + if (len == -1) { + if (errno == EINTR) + continue; + break; + } + for (ptr = buf.d; ptr < buf.d + len; ptr += sizeof(*event) + event->len) { + event = (const struct inotify_event*) ptr; + if (event->mask & IN_CLOSE_WRITE) { + reload = true; + } else if (event->mask & IN_DELETE_SELF) { + rm_watch(arl->fd, &arl->wd_file); + } else if (event->mask & (IN_CREATE | IN_MOVED_TO)) { + if (STREQ(event->name, arl->filename)) + reload = true; + } + } + } + return reload; +} + diff --git a/autoreload_nop.c b/autoreload_nop.c new file mode 100644 index 0000000..41dbf47 --- /dev/null +++ b/autoreload_nop.c @@ -0,0 +1,43 @@ +/* Copyright 2017 Max Voit + * + * This file is part of sxiv. + * + * sxiv is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * sxiv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with sxiv. If not, see . + */ + +#include "autoreload.h" + +void arl_cleanup(arl_t *arl) +{ + (void) arl; +} + +bool arl_handle(arl_t *arl, const char *filepath) +{ + (void) arl; + (void) filepath; + return false; +} + +void arl_init(arl_t *arl) +{ + (void) arl; +} + +void arl_setup(arl_t *arl, const char *filepath) +{ + (void) arl; + (void) filepath; +} + diff --git a/main.c b/main.c index 9e337f4..aa11616 100644 --- a/main.c +++ b/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,7 @@ #include "thumbs.h" #include "util.h" #include "window.h" +#include "autoreload.h" #define _MAPPINGS_CONFIG #include "config.h" @@ -56,6 +58,7 @@ void slideshow(void); void clear_resize(void); appmode_t mode; +arl_t arl; img_t img; tns_t tns; win_t win; @@ -98,6 +101,7 @@ timeout_t timeouts[] = { void cleanup(void) { img_close(&img, false); + arl_cleanup(&arl); tns_free(&tns); win_close(&win); } @@ -317,6 +321,7 @@ void load_image(int new) info.open = false; open_info(); + arl_setup(&arl, files[fileidx].path); if (img.multi.cnt > 0 && img.multi.animate) set_timeout(animate, img.multi.frames[img.multi.sel].delay, true); @@ -672,6 +677,8 @@ void on_buttonpress(XButtonEvent *bev) prefix = 0; } +const struct timespec ten_ms = {0, 10000000}; + void run(void) { int xfd; @@ -685,8 +692,8 @@ void run(void) init_thumb = mode == MODE_THUMB && tns.initnext < filecnt; load_thumb = mode == MODE_THUMB && tns.loadnext < tns.end; - if ((init_thumb || load_thumb || to_set || info.fd != -1) && - XPending(win.env.dpy) == 0) + if ((init_thumb || load_thumb || to_set || info.fd != -1 || + arl.fd != -1) && XPending(win.env.dpy) == 0) { if (load_thumb) { set_timeout(redraw, TO_REDRAW_THUMBS, false); @@ -708,9 +715,21 @@ void run(void) FD_SET(info.fd, &fds); xfd = MAX(xfd, info.fd); } + if (arl.fd != -1) { + FD_SET(arl.fd, &fds); + xfd = MAX(xfd, arl.fd); + } select(xfd + 1, &fds, 0, 0, to_set ? &timeout : NULL); if (info.fd != -1 && FD_ISSET(info.fd, &fds)) read_info(); + if (arl.fd != -1 && FD_ISSET(arl.fd, &fds)) { + if (arl_handle(&arl)) { + /* when too fast, imlib2 can't load the image */ + nanosleep(&ten_ms, NULL); + load_image(fileidx); + redraw(); + } + } } continue; } @@ -854,6 +873,7 @@ int main(int argc, char **argv) win_init(&win); img_init(&img, &win); + arl_init(&arl); if ((homedir = getenv("XDG_CONFIG_HOME")) == NULL || homedir[0] == '\0') { homedir = getenv("HOME");