@@ -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) | |||
@@ -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 */ |
@@ -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); | |||
} |
@@ -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"); | |||