Krolyxon
Commit edb117e

Branches

Refs →

nsxivedb117e

Add autoreload support by inotify (and dummy backend nop)

Author
Max Voit
Date
2017-01-26 22:18 +0100
Committer
Bert Münnich · 2017-05-17 15:52 +0200
Parent
1fb3ec5
Commit
edb117e3bdb4d6bef4a4749d94144df8472c0a4d

+247 −1

5 files changed

  1. M Makefile +7 −0
  2. A autoreload.h +43 −0
  3. A autoreload_inotify.c +154 −0
  4. A autoreload_nop.c +31 −0
  5. M main.c +12 −1
M Makefile View
@@ -24,6 +24,13 @@ endif
2424 .PHONY: clean install uninstall
2525
2626 SRC := commands.c image.c main.c options.c thumbs.c util.c window.c
27+# conditionally compile in autoreload-backend; usage: `make AUTORELOAD=nop`
28+ifeq ($(AUTORELOAD),nop)
29+ SRC += autoreload_nop.c
30+else
31+ SRC += autoreload_inotify.c
32+endif
33+
2734 DEP := $(SRC:.c=.d)
2835 OBJ := $(SRC:.c=.o)
2936
A autoreload.h View
@@ -0,0 +1,43 @@
1+/* Copyright 2017 Max Voit
2+ *
3+ * This file is part of sxiv.
4+ *
5+ * sxiv is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published
7+ * by the Free Software Foundation; either version 2 of the License,
8+ * or (at your option) any later version.
9+ *
10+ * sxiv is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License
16+ * along with sxiv. If not, see <http://www.gnu.org/licenses/>.
17+ */
18+
19+#ifndef AUTORELOAD_H
20+#define AUTORELOAD_H
21+
22+#include "types.h"
23+
24+void arl_cleanup(void);
25+void arl_handle(void);
26+void arl_init(void);
27+void arl_setup(void);
28+void arl_setup_dir(void);
29+
30+typedef struct {
31+ int fd;
32+ int wd;
33+ bool watching_dir;
34+} autoreload_t;
35+
36+extern autoreload_t autoreload;
37+extern int fileidx;
38+extern fileinfo_t *files;
39+
40+void load_image(int);
41+void redraw(void);
42+
43+#endif /* AUTORELOAD_H */
A autoreload_inotify.c View
@@ -0,0 +1,154 @@
1+/* Copyright 2017 Max Voit
2+ *
3+ * This file is part of sxiv.
4+ *
5+ * sxiv is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published
7+ * by the Free Software Foundation; either version 2 of the License,
8+ * or (at your option) any later version.
9+ *
10+ * sxiv is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License
16+ * along with sxiv. If not, see <http://www.gnu.org/licenses/>.
17+ */
18+
19+#include <stdlib.h>
20+#include <string.h>
21+#include <unistd.h>
22+#include <sys/inotify.h>
23+#include <libgen.h>
24+#include <time.h>
25+
26+#include "util.h"
27+#include "autoreload.h"
28+
29+const struct timespec ten_ms = {0, 10000000};
30+
31+void arl_cleanup(void)
32+{
33+ if (autoreload.fd != -1 && autoreload.wd != -1)
34+ {
35+ if(inotify_rm_watch(autoreload.fd, autoreload.wd))
36+ error(0, 0, "Failed to remove inotify watch.");
37+ }
38+}
39+
40+void arl_handle(void)
41+{
42+ ssize_t len;
43+ char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
44+ const struct inotify_event *event;
45+ char *ptr;
46+ char *fntmp, *fn;
47+
48+ len = read(autoreload.fd, buf, sizeof buf);
49+ if (len == -1)
50+ {
51+ error(0, 0, "Failed to read inotify events.");
52+ return;
53+ }
54+
55+ for (ptr = buf; ptr < buf + len;
56+ ptr += sizeof(struct inotify_event) + event->len)
57+ {
58+
59+ event = (const struct inotify_event *) ptr;
60+
61+ /* events from watching the file itself */
62+ if (event->mask & IN_CLOSE_WRITE)
63+ {
64+ load_image(fileidx);
65+ redraw();
66+ }
67+
68+ if (event->mask & IN_DELETE_SELF)
69+ arl_setup_dir();
70+
71+ /* events from watching the file's directory */
72+ if (event->mask & IN_CREATE)
73+ {
74+ fntmp = strdup(files[fileidx].path);
75+ fn = basename(fntmp);
76+
77+ if (0 == strcmp(event->name, fn))
78+ {
79+ /* this is the file we're looking for */
80+
81+ /* cleanup, this has not been one-shot */
82+ if (autoreload.watching_dir)
83+ {
84+ if(inotify_rm_watch(autoreload.fd, autoreload.wd))
85+ error(0, 0, "Failed to remove inotify watch.");
86+ autoreload.watching_dir = false;
87+ }
88+
89+ /* when too fast, imlib2 can't load the image */
90+ nanosleep(&ten_ms, NULL);
91+ load_image(fileidx);
92+ redraw();
93+ }
94+ free(fntmp);
95+ }
96+ }
97+}
98+
99+void arl_init(void)
100+{
101+ /* this needs to be done only once */
102+ autoreload.fd = inotify_init();
103+ autoreload.watching_dir = false;
104+ if (autoreload.fd == -1)
105+ error(0, 0, "Could not initialize inotify.");
106+}
107+
108+void arl_setup(void)
109+{
110+ if (autoreload.fd == -1)
111+ {
112+ error(0, 0, "Uninitialized, could not add inotify watch.");
113+ return;
114+ }
115+
116+ /* may have switched from a deleted to another image */
117+ if (autoreload.watching_dir)
118+ {
119+ if (inotify_rm_watch(autoreload.fd, autoreload.wd))
120+ error(0, 0, "Failed to remove inotify watch.");
121+ autoreload.watching_dir = false;
122+ }
123+
124+ autoreload.wd = inotify_add_watch(autoreload.fd, files[fileidx].path,
125+ IN_ONESHOT | IN_CLOSE_WRITE | IN_DELETE_SELF);
126+ if (autoreload.wd == -1)
127+ error(0, 0, "Failed to add inotify watch on file '%s'.", files[fileidx].path);
128+}
129+
130+void arl_setup_dir(void)
131+{
132+ char *dntmp, *dn;
133+
134+ if (autoreload.fd == -1)
135+ {
136+ error(0, 0, "Uninitialized, could not add inotify watch on directory.");
137+ return;
138+ }
139+
140+ /* get dirname */
141+ dntmp = (char*) strdup(files[fileidx].path);
142+ dn = (char*) dirname(dntmp);
143+
144+ /* this is not one-shot as other stuff may be created too
145+ note: we won't handle deletion of the directory itself,
146+ this is a design decision */
147+ autoreload.wd = inotify_add_watch(autoreload.fd, dn,IN_CREATE);
148+ if (autoreload.wd == -1)
149+ error(0, 0, "Failed to add inotify watch on directory '%s'.", dn);
150+ else
151+ autoreload.watching_dir = true;
152+
153+ free(dntmp);
154+}
A autoreload_nop.c View
@@ -0,0 +1,31 @@
1+/* Copyright 2017 Max Voit
2+ *
3+ * This file is part of sxiv.
4+ *
5+ * sxiv is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published
7+ * by the Free Software Foundation; either version 2 of the License,
8+ * or (at your option) any later version.
9+ *
10+ * sxiv is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License
16+ * along with sxiv. If not, see <http://www.gnu.org/licenses/>.
17+ */
18+
19+#include "autoreload.h"
20+
21+void arl_cleanup(void) { }
22+
23+void arl_handle(void) { }
24+
25+void arl_init(void) { }
26+
27+void arl_setup(void) { }
28+
29+void arl_setup_dir(void) { }
30+
31+
M main.c View
@@ -38,6 +38,7 @@
3838 #include "thumbs.h"
3939 #include "util.h"
4040 #include "window.h"
41+#include "autoreload.h"
4142
4243 #define _MAPPINGS_CONFIG
4344 #include "config.h"
@@ -64,6 +65,7 @@ fileinfo_t *files;
6465 int filecnt, fileidx;
6566 int alternate;
6667 int markcnt;
68+autoreload_t autoreload;
6769
6870 int prefix;
6971 bool extprefix;
@@ -98,6 +100,7 @@ timeout_t timeouts[] = {
98100 void cleanup(void)
99101 {
100102 img_close(&img, false);
103+ arl_cleanup();
101104 tns_free(&tns);
102105 win_close(&win);
103106 }
@@ -317,6 +320,7 @@ void load_image(int new)
317320
318321 info.open = false;
319322 open_info();
323+ arl_setup();
320324
321325 if (img.multi.cnt > 0 && img.multi.animate)
322326 set_timeout(animate, img.multi.frames[img.multi.sel].delay, true);
@@ -685,7 +689,7 @@ void run(void)
685689 init_thumb = mode == MODE_THUMB && tns.initnext < filecnt;
686690 load_thumb = mode == MODE_THUMB && tns.loadnext < tns.end;
687691
688− if ((init_thumb || load_thumb || to_set || info.fd != -1) &&
692+ if ((init_thumb || load_thumb || to_set || info.fd != -1 || autoreload.fd != -1) &&
689693 XPending(win.env.dpy) == 0)
690694 {
691695 if (load_thumb) {
@@ -708,9 +712,15 @@ void run(void)
708712 FD_SET(info.fd, &fds);
709713 xfd = MAX(xfd, info.fd);
710714 }
715+ if (autoreload.fd != -1) {
716+ FD_SET(autoreload.fd, &fds);
717+ xfd = MAX(xfd, autoreload.fd);
718+ }
711719 select(xfd + 1, &fds, 0, 0, to_set ? &timeout : NULL);
712720 if (info.fd != -1 && FD_ISSET(info.fd, &fds))
713721 read_info();
722+ if (autoreload.fd != -1 && FD_ISSET(autoreload.fd, &fds))
723+ arl_handle();
714724 }
715725 continue;
716726 }
@@ -854,6 +864,7 @@ int main(int argc, char **argv)
854864
855865 win_init(&win);
856866 img_init(&img, &win);
867+ arl_init();
857868
858869 if ((homedir = getenv("XDG_CONFIG_HOME")) == NULL || homedir[0] == '\0') {
859870 homedir = getenv("HOME");