diff options
| -rw-r--r-- | Makefile | 7 | ||||
| -rw-r--r-- | autoreload.h | 43 | ||||
| -rw-r--r-- | autoreload_inotify.c | 154 | ||||
| -rw-r--r-- | autoreload_nop.c | 31 | ||||
| -rw-r--r-- | main.c | 13 | 
5 files changed, 247 insertions, 1 deletions
@@ -24,6 +24,13 @@ endif  .PHONY: clean install uninstall  SRC := commands.c image.c main.c options.c thumbs.c util.c window.c +# conditionally compile in autoreload-backend; usage: `make AUTORELOAD=nop` +ifeq ($(AUTORELOAD),nop) +	SRC += autoreload_nop.c +else +	SRC += autoreload_inotify.c +endif +  DEP := $(SRC:.c=.d)  OBJ := $(SRC:.c=.o) diff --git a/autoreload.h b/autoreload.h new file mode 100644 index 0000000..439b695 --- /dev/null +++ b/autoreload.h @@ -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 <http://www.gnu.org/licenses/>. + */ + +#ifndef AUTORELOAD_H +#define AUTORELOAD_H + +#include "types.h" + +void arl_cleanup(void); +void arl_handle(void); +void arl_init(void); +void arl_setup(void); +void arl_setup_dir(void); + +typedef struct { +	int fd; +	int wd; +	bool watching_dir; +} autoreload_t; + +extern autoreload_t autoreload; +extern int fileidx; +extern fileinfo_t *files; + +void load_image(int); +void redraw(void); + +#endif /* AUTORELOAD_H */ diff --git a/autoreload_inotify.c b/autoreload_inotify.c new file mode 100644 index 0000000..4a8e455 --- /dev/null +++ b/autoreload_inotify.c @@ -0,0 +1,154 @@ +/* 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 <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/inotify.h> +#include <libgen.h> +#include <time.h> + +#include "util.h" +#include "autoreload.h" + +const struct timespec ten_ms = {0, 10000000}; + +void arl_cleanup(void) +{ +	if (autoreload.fd != -1 && autoreload.wd != -1) +	{ +		if(inotify_rm_watch(autoreload.fd, autoreload.wd)) +			error(0, 0, "Failed to remove inotify watch."); +	} +} + +void arl_handle(void) +{ +	ssize_t len; +	char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event)))); +	const struct inotify_event *event; +	char *ptr; +	char *fntmp, *fn; + +	len = read(autoreload.fd, buf, sizeof buf); +	if (len == -1) +	{ +		error(0, 0, "Failed to read inotify events."); +		return; +	} + +	for (ptr = buf; ptr < buf + len; +			ptr += sizeof(struct inotify_event) + event->len) +	{ + +		event = (const struct inotify_event *) ptr; + +		/* events from watching the file itself */ +		if (event->mask & IN_CLOSE_WRITE) +		{ +			load_image(fileidx); +			redraw(); +		} + +		if (event->mask & IN_DELETE_SELF) +			arl_setup_dir(); + +		/* events from watching the file's directory */ +		if (event->mask & IN_CREATE) +		{ +			fntmp = strdup(files[fileidx].path); +			fn = basename(fntmp); + +			if (0 == strcmp(event->name, fn)) +			{ +				/* this is the file we're looking for */ + +				/* cleanup, this has not been one-shot */ +				if (autoreload.watching_dir) +				{ +					if(inotify_rm_watch(autoreload.fd, autoreload.wd)) +						error(0, 0, "Failed to remove inotify watch."); +					autoreload.watching_dir = false; +				} + +				/* when too fast, imlib2 can't load the image */ +				nanosleep(&ten_ms, NULL); +				load_image(fileidx); +				redraw(); +			} +			free(fntmp); +		} +	} +} + +void arl_init(void) +{ +	/* this needs to be done only once */ +	autoreload.fd = inotify_init(); +	autoreload.watching_dir = false; +	if (autoreload.fd == -1) +		error(0, 0, "Could not initialize inotify."); +} + +void arl_setup(void) +{ +	if (autoreload.fd == -1) +	{ +		error(0, 0, "Uninitialized, could not add inotify watch."); +		return; +	} + +	/* may have switched from a deleted to another image */ +	if (autoreload.watching_dir) +	{ +		if (inotify_rm_watch(autoreload.fd, autoreload.wd)) +			error(0, 0, "Failed to remove inotify watch."); +		autoreload.watching_dir = false; +	} + +	autoreload.wd = inotify_add_watch(autoreload.fd, files[fileidx].path, +		IN_ONESHOT | IN_CLOSE_WRITE | IN_DELETE_SELF); +	if (autoreload.wd == -1) +		error(0, 0, "Failed to add inotify watch on file '%s'.", files[fileidx].path); +} + +void arl_setup_dir(void) +{ +	char *dntmp, *dn; + +	if (autoreload.fd == -1) +	{ +		error(0, 0, "Uninitialized, could not add inotify watch on directory."); +		return; +	} + +	/* get dirname */ +	dntmp = (char*) strdup(files[fileidx].path); +	dn = (char*) dirname(dntmp); + +	/* this is not one-shot as other stuff may be created too +	   note: we won't handle deletion of the directory itself, +	     this is a design decision								*/ +	autoreload.wd = inotify_add_watch(autoreload.fd, dn,IN_CREATE); +	if (autoreload.wd == -1) +		error(0, 0, "Failed to add inotify watch on directory '%s'.", dn); +	else +		autoreload.watching_dir = true; + +	free(dntmp); +} diff --git a/autoreload_nop.c b/autoreload_nop.c new file mode 100644 index 0000000..19641f8 --- /dev/null +++ b/autoreload_nop.c @@ -0,0 +1,31 @@ +/* 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 <http://www.gnu.org/licenses/>. + */ + +#include "autoreload.h" + +void arl_cleanup(void) { } + +void arl_handle(void) { } + +void arl_init(void) { } + +void arl_setup(void) { } + +void arl_setup_dir(void) { } + + @@ -38,6 +38,7 @@  #include "thumbs.h"  #include "util.h"  #include "window.h" +#include "autoreload.h"  #define _MAPPINGS_CONFIG  #include "config.h" @@ -64,6 +65,7 @@ fileinfo_t *files;  int filecnt, fileidx;  int alternate;  int markcnt; +autoreload_t autoreload;  int prefix;  bool extprefix; @@ -98,6 +100,7 @@ timeout_t timeouts[] = {  void cleanup(void)  {  	img_close(&img, false); +	arl_cleanup();  	tns_free(&tns);  	win_close(&win);  } @@ -317,6 +320,7 @@ void load_image(int new)  	info.open = false;  	open_info(); +	arl_setup();  	if (img.multi.cnt > 0 && img.multi.animate)  		set_timeout(animate, img.multi.frames[img.multi.sel].delay, true); @@ -685,7 +689,7 @@ 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) && +		if ((init_thumb || load_thumb || to_set || info.fd != -1 || autoreload.fd != -1) &&  		    XPending(win.env.dpy) == 0)  		{  			if (load_thumb) { @@ -708,9 +712,15 @@ void run(void)  					FD_SET(info.fd, &fds);  					xfd = MAX(xfd, info.fd);  				} +				if (autoreload.fd != -1) { +					FD_SET(autoreload.fd, &fds); +					xfd = MAX(xfd, autoreload.fd); +				}  				select(xfd + 1, &fds, 0, 0, to_set ? &timeout : NULL);  				if (info.fd != -1 && FD_ISSET(info.fd, &fds))  					read_info(); +				if (autoreload.fd != -1 && FD_ISSET(autoreload.fd, &fds)) +					arl_handle();  			}  			continue;  		} @@ -854,6 +864,7 @@ int main(int argc, char **argv)  	win_init(&win);  	img_init(&img, &win); +	arl_init();  	if ((homedir = getenv("XDG_CONFIG_HOME")) == NULL || homedir[0] == '\0') {  		homedir = getenv("HOME");  | 
