@@ -130,7 +130,7 @@ bool cg_reload_image(arg_t a) | |||||
load_image(fileidx); | load_image(fileidx); | ||||
} else { | } else { | ||||
win_set_cursor(&win, CURSOR_WATCH); | win_set_cursor(&win, CURSOR_WATCH); | ||||
if (!tns_load(&tns, fileidx, &files[fileidx], true, false)) { | if (!tns_load(&tns, fileidx, &files[fileidx], true)) { | ||||
remove_file(fileidx, false); | remove_file(fileidx, false); | ||||
tns.dirty = true; | tns.dirty = true; | ||||
} | } | ||||
@@ -2,33 +2,36 @@ | |||||
# Example for $XDG_CONFIG_HOME/sxiv/exec/key-handler | # Example for $XDG_CONFIG_HOME/sxiv/exec/key-handler | ||||
# Called by sxiv(1) after the external prefix key (C-x by default) is pressed. | # Called by sxiv(1) after the external prefix key (C-x by default) is pressed. | ||||
# The next key combo is passed as its first argument and the path of the | # The next key combo is passed as its first argument, followed by the paths of | ||||
# current image as its second argument. | # all marked images or the path of the current image, if no image is marked. | ||||
# sxiv(1) blocks until this script terminates. It then checks if the image | # sxiv(1) blocks until this script terminates. It then checks which images | ||||
# has been modified and reloads it. | # have been modified and reloads them. | ||||
# The key combo argument has the following form: "[C-][M-][S-]KEY", | # The key combo argument has the following form: "[C-][M-][S-]KEY", | ||||
# where C/M/S indicate Ctrl/Meta(Alt)/Shift modifier states and KEY is the X | # where C/M/S indicate Ctrl/Meta(Alt)/Shift modifier states and KEY is the X | ||||
# keysym as listed in /usr/include/X11/keysymdef.h without the "XK_" prefix. | # keysym as listed in /usr/include/X11/keysymdef.h without the "XK_" prefix. | ||||
case "$1" in | key="$1" | ||||
shift | |||||
case "$key" in | |||||
"C-c") | "C-c") | ||||
echo -n "$2" | xsel -i ;; | echo -n "$@" | xsel -i ;; | ||||
"C-e") | "C-e") | ||||
urxvt -bg "#444" -fg "#eee" -sl 0 -title "$2" -e sh -c "exiv2 pr -q -pa '$2' | less" & ;; | for file in "$@"; do urxvt -bg "#444" -fg "#eee" -sl 0 -title "$file" -e sh -c "exiv2 pr -q -pa '$file' | less" & done ;; | ||||
"C-g") | "C-g") | ||||
gimp "$2" & ;; | gimp "$@" & ;; | ||||
"C-comma") | "C-comma") | ||||
exec jpegtran -rotate 270 -copy all -outfile "$2" "$2" ;; | for file in "$@"; do jpegtran -rotate 270 -copy all -outfile "$file" "$file"; done ;; | ||||
"C-period") | "C-period") | ||||
exec jpegtran -rotate 90 -copy all -outfile "$2" "$2" ;; | for file in "$@"; do jpegtran -rotate 90 -copy all -outfile "$file" "$file"; done ;; | ||||
"C-slash") | "C-slash") | ||||
exec jpegtran -rotate 180 -copy all -outfile "$2" "$2" ;; | for file in "$@"; do jpegtran -rotate 180 -copy all -outfile "$file" "$file"; done ;; | ||||
"C-less") | "C-less") | ||||
exec mogrify -rotate -90 "$2" ;; | exec mogrify -rotate -90 "$@" ;; | ||||
"C-greater") | "C-greater") | ||||
exec mogrify -rotate +90 "$2" ;; | exec mogrify -rotate +90 "$@" ;; | ||||
"C-question") | "C-question") | ||||
exec mogrify -rotate 180 "$2" ;; | exec mogrify -rotate 180 "$@" ;; | ||||
esac | esac | ||||
@@ -467,10 +467,12 @@ void clear_resize(void) | |||||
void run_key_handler(const char *key, unsigned int mask) | void run_key_handler(const char *key, unsigned int mask) | ||||
{ | { | ||||
pid_t pid; | pid_t pid; | ||||
int retval, status; | int i, j, retval, status; | ||||
char kstr[32], oldbar[sizeof(win.bar.l)]; | int fcnt = mode == MODE_THUMB && markcnt > 0 ? markcnt : 1; | ||||
bool restore_bar = mode == MODE_IMAGE && info.cmd != NULL; | bool changed = false; | ||||
struct stat oldst, newst; | char **args, kstr[32], oldbar[sizeof(win.bar.l)]; | ||||
struct stat *oldst, newst; | |||||
struct { int fn; struct stat st; } *finfo; | |||||
if (keyhandler.cmd == NULL) { | if (keyhandler.cmd == NULL) { | ||||
if (!keyhandler.warned) { | if (!keyhandler.warned) { | ||||
@@ -482,20 +484,34 @@ void run_key_handler(const char *key, unsigned int mask) | |||||
if (key == NULL) | if (key == NULL) | ||||
return; | return; | ||||
finfo = s_malloc(fcnt * sizeof(*finfo)); | |||||
args = s_malloc((fcnt + 3) * sizeof(*args)); | |||||
args[0] = keyhandler.cmd; | |||||
args[1] = kstr; | |||||
args[fcnt+2] = NULL; | |||||
if (mode == MODE_IMAGE || markcnt == 0) { | |||||
finfo[0].fn = fileidx; | |||||
stat(files[fileidx].path, &finfo[0].st); | |||||
args[2] = (char*) files[fileidx].path; | |||||
} else for (i = j = 0; i < filecnt; i++) { | |||||
if (files[i].marked) { | |||||
finfo[j].fn = i; | |||||
stat(files[i].path, &finfo[j++].st); | |||||
args[j+1] = (char*) files[i].path; | |||||
} | |||||
} | |||||
snprintf(kstr, sizeof(kstr), "%s%s%s%s", | snprintf(kstr, sizeof(kstr), "%s%s%s%s", | ||||
mask & ControlMask ? "C-" : "", | mask & ControlMask ? "C-" : "", | ||||
mask & Mod1Mask ? "M-" : "", | mask & Mod1Mask ? "M-" : "", | ||||
mask & ShiftMask ? "S-" : "", key); | mask & ShiftMask ? "S-" : "", key); | ||||
if (restore_bar) | memcpy(oldbar, win.bar.l, sizeof(win.bar.l)); | ||||
memcpy(oldbar, win.bar.l, sizeof(win.bar.l)); | |||||
strncpy(win.bar.l, "Running key handler...", sizeof(win.bar.l)); | strncpy(win.bar.l, "Running key handler...", sizeof(win.bar.l)); | ||||
win_draw(&win); | win_draw(&win); | ||||
win_set_cursor(&win, CURSOR_WATCH); | win_set_cursor(&win, CURSOR_WATCH); | ||||
stat(files[fileidx].path, &oldst); | |||||
if ((pid = fork()) == 0) { | if ((pid = fork()) == 0) { | ||||
execl(keyhandler.cmd, keyhandler.cmd, kstr, files[fileidx].path, NULL); | execv(keyhandler.cmd, args); | ||||
warn("could not exec key handler"); | warn("could not exec key handler"); | ||||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | ||||
} else if (pid < 0) { | } else if (pid < 0) { | ||||
@@ -507,31 +523,31 @@ void run_key_handler(const char *key, unsigned int mask) | |||||
if (WIFEXITED(status) == 0 || retval != 0) | if (WIFEXITED(status) == 0 || retval != 0) | ||||
warn("key handler exited with non-zero return value: %d", retval); | warn("key handler exited with non-zero return value: %d", retval); | ||||
if (stat(files[fileidx].path, &newst) == 0 && | for (i = 0; i < fcnt; i++) { | ||||
memcmp(&oldst.st_mtime, &newst.st_mtime, sizeof(oldst.st_mtime)) == 0) | oldst = &finfo[i].st; | ||||
{ | if (stat(files[finfo[i].fn].path, &newst) != 0 || | ||||
/* file has not changed */ | memcmp(&oldst->st_mtime, &newst.st_mtime, sizeof(newst.st_mtime)) != 0) | ||||
goto end; | { | ||||
if (tns.thumbs != NULL) { | |||||
tns.thumbs[finfo[i].fn].loaded = false; | |||||
tns.loadnext = MIN(tns.loadnext, finfo[i].fn); | |||||
} | |||||
changed = true; | |||||
} | |||||
} | } | ||||
restore_bar = false; | end: | ||||
strncpy(win.bar.l, "Reloading image...", sizeof(win.bar.l)); | |||||
win_draw(&win); | |||||
if (mode == MODE_IMAGE) { | if (mode == MODE_IMAGE) { | ||||
img_close(&img, true); | if (changed) { | ||||
load_image(fileidx); | img_close(&img, true); | ||||
} | load_image(fileidx); | ||||
if (!tns_load(&tns, fileidx, &files[fileidx], true, mode == MODE_IMAGE) && | } else if (info.cmd != NULL) { | ||||
mode == MODE_THUMB) | memcpy(win.bar.l, oldbar, sizeof(win.bar.l)); | ||||
{ | } | ||||
remove_file(fileidx, false); | |||||
tns.dirty = true; | |||||
} | } | ||||
end: | |||||
if (restore_bar) | |||||
memcpy(win.bar.l, oldbar, sizeof(win.bar.l)); | |||||
reset_cursor(); | reset_cursor(); | ||||
redraw(); | redraw(); | ||||
free(finfo); | |||||
free(args); | |||||
} | } | ||||
#define MODMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask)) | #define MODMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask)) | ||||
@@ -651,7 +667,7 @@ void run(void) | |||||
int xfd; | int xfd; | ||||
fd_set fds; | fd_set fds; | ||||
struct timeval timeout; | struct timeval timeout; | ||||
bool discard, to_set; | bool discard, reload, to_set; | ||||
XEvent ev, nextev; | XEvent ev, nextev; | ||||
set_timeout(redraw, 25, false); | set_timeout(redraw, 25, false); | ||||
@@ -661,9 +677,10 @@ void run(void) | |||||
XPending(win.env.dpy) == 0) | XPending(win.env.dpy) == 0) | ||||
{ | { | ||||
/* load thumbnails */ | /* load thumbnails */ | ||||
reload = tns.loadnext != tns.cnt; | |||||
set_timeout(redraw, TO_REDRAW_THUMBS, false); | set_timeout(redraw, TO_REDRAW_THUMBS, false); | ||||
if (tns_load(&tns, tns.loadnext, &files[tns.loadnext], false, false)) { | if (tns_load(&tns, tns.loadnext, &files[tns.loadnext], reload)) { | ||||
if (tns.cnt == tns.loadnext) | if (!reload) | ||||
tns.cnt++; | tns.cnt++; | ||||
} else { | } else { | ||||
remove_file(tns.loadnext, false); | remove_file(tns.loadnext, false); | ||||
@@ -860,7 +877,7 @@ int main(int argc, char **argv) | |||||
if (options->thumb_mode) { | if (options->thumb_mode) { | ||||
mode = MODE_THUMB; | mode = MODE_THUMB; | ||||
tns_init(&tns, filecnt, &win, &fileidx); | tns_init(&tns, filecnt, &win, &fileidx); | ||||
while (!tns_load(&tns, 0, &files[0], false, false)) | while (!tns_load(&tns, 0, &files[0], false)) | ||||
remove_file(0, false); | remove_file(0, false); | ||||
tns.cnt = 1; | tns.cnt = 1; | ||||
} else { | } else { | ||||
@@ -112,7 +112,8 @@ Toggle fullscreen mode. | |||||
Toggle visibility of info bar on bottom of window. | Toggle visibility of info bar on bottom of window. | ||||
.TP | .TP | ||||
.B Ctrl-x | .B Ctrl-x | ||||
Send the next key to the external key-handler. | Send the next key to the external key-handler. See section EXTERNAL KEY HANDLER | ||||
for more information. | |||||
.TP | .TP | ||||
.B g | .B g | ||||
Go to the first image. | Go to the first image. | ||||
@@ -354,9 +355,10 @@ located in | |||||
.IR $XDG_CONFIG_HOME/sxiv/exec/key-handler . | .IR $XDG_CONFIG_HOME/sxiv/exec/key-handler . | ||||
The handler is invoked by pressing | The handler is invoked by pressing | ||||
.BR Ctrl-x . | .BR Ctrl-x . | ||||
The next key combo is then passed as its first argument and the path of the | The next key combo is then passed as its first argument, followed by the paths | ||||
current image as its second argument. sxiv(1) will block until the handler | of all marked images or the path of the current image, if no image is marked. | ||||
terminates. It then checks if the image has been modified and reloads it. | sxiv(1) will block until the handler terminates. It then checks which images | ||||
have been modified and reloads them. | |||||
The key combo argument has the following form: "[C-][M-][S-]KEY", | The key combo argument has the following form: "[C-][M-][S-]KEY", | ||||
where C/M/S indicate Ctrl/Meta(Alt)/Shift modifier states and KEY is the X | where C/M/S indicate Ctrl/Meta(Alt)/Shift modifier states and KEY is the X | ||||
@@ -209,8 +209,7 @@ void tns_free(tns_t *tns) | |||||
} | } | ||||
} | } | ||||
bool tns_load(tns_t *tns, int n, const fileinfo_t *file, | bool tns_load(tns_t *tns, int n, const fileinfo_t *file, bool force) | ||||
bool force, bool silent) | |||||
{ | { | ||||
int w, h; | int w, h; | ||||
bool cache_hit = false; | bool cache_hit = false; | ||||
@@ -295,8 +294,7 @@ bool tns_load(tns_t *tns, int n, const fileinfo_t *file, | |||||
if (im == NULL && (access(file->path, R_OK) < 0 || | if (im == NULL && (access(file->path, R_OK) < 0 || | ||||
(im = imlib_load_image(file->path)) == NULL)) | (im = imlib_load_image(file->path)) == NULL)) | ||||
{ | { | ||||
if (!silent) | warn("could not open image: %s", file->name); | ||||
warn("could not open image: %s", file->name); | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
@@ -57,7 +57,7 @@ void tns_clean_cache(tns_t*); | |||||
void tns_init(tns_t*, int, win_t*, int*); | void tns_init(tns_t*, int, win_t*, int*); | ||||
void tns_free(tns_t*); | void tns_free(tns_t*); | ||||
bool tns_load(tns_t*, int, const fileinfo_t*, bool, bool); | bool tns_load(tns_t*, int, const fileinfo_t*, bool); | ||||
void tns_render(tns_t*); | void tns_render(tns_t*); | ||||
void tns_mark(tns_t*, int, bool); | void tns_mark(tns_t*, int, bool); | ||||