@@ -2,8 +2,9 @@ | |||||
# 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, followed by the paths of | # The next key combo is passed as its first argument. The paths of all marked | ||||
# all marked images or the path of the current image, if no image is marked. | # images--or of the current image, if no image is marked--are passed via stdin, | ||||
# one file path per line. | |||||
# sxiv(1) blocks until this script terminates. It then checks which images | # sxiv(1) blocks until this script terminates. It then checks which images | ||||
# have been modified and reloads them. | # have been modified and reloads them. | ||||
@@ -11,12 +12,13 @@ | |||||
# 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. | ||||
readonly KEY="$1"; shift | readonly KEY="$1"; | ||||
readonly TAGFILE="$HOME/.config/sxiv/tags" | readonly TAGFILE="$HOME/.config/sxiv/tags" | ||||
readonly TMPFILE="/tmp/sxiv.$$" | |||||
rotate() { | rotate() { | ||||
degree="$1"; shift | degree="$1" | ||||
for file in "$@"; do | while read file; do | ||||
case "$(file -b -i "$file")" in | case "$(file -b -i "$file")" in | ||||
image/jpeg*) jpegtran -rotate "$degree" -copy all -outfile "$file" "$file" ;; | image/jpeg*) jpegtran -rotate "$degree" -copy all -outfile "$file" "$file" ;; | ||||
*) mogrify -rotate "$degree" "$file" ;; | *) mogrify -rotate "$degree" "$file" ;; | ||||
@@ -28,25 +30,27 @@ tag_add() { | |||||
>>"$TAGFILE" | >>"$TAGFILE" | ||||
tags=$(dmenu <"$TAGFILE" | tr '\n' ',') | tags=$(dmenu <"$TAGFILE" | tr '\n' ',') | ||||
[ -z "$tags" ] && return | [ -z "$tags" ] && return | ||||
iptckwed -a "$tags" "$@" | iptckwed -i -a "$tags" | ||||
echo -n "$tags" | tr ',' '\n' | sort - "$TAGFILE" | uniq >"$TAGFILE.new" | echo -n "$tags" | tr ',' '\n' | sort - "$TAGFILE" | uniq >"$TAGFILE.new" | ||||
mv -f "$TAGFILE"{.new,} | mv -f "$TAGFILE"{.new,} | ||||
} | } | ||||
tag_del() { | tag_del() { | ||||
tags=$(iptckwed -ql "$@" | cut -f 2 | tr ',' '\n' | sort | uniq | dmenu | tr '\n' ',') | cat >"$TMPFILE" | ||||
tags=$(iptckwed -iql <"$TMPFILE" | cut -f 2 | tr ',' '\n' | sort | uniq | dmenu | tr '\n' ',') | |||||
[ -z "$tags" ] && return | [ -z "$tags" ] && return | ||||
iptckwed -r "$tags" "$@" | iptckwed -i -r "$tags" <"$TMPFILE" | ||||
rm -f "$TMPFILE" | |||||
} | } | ||||
case "$KEY" in | case "$KEY" in | ||||
"C-c") echo -n "$@" | xsel -i ;; | "C-c") tr '\n' ' ' | xsel -i ;; | ||||
"C-e") for file in "$@"; do urxvt -bg "#444" -fg "#eee" -sl 0 -title "$file" -e sh -c "exiv2 pr -q -pa '$file' | less" & done ;; | "C-e") while read file; do urxvt -bg "#444" -fg "#eee" -sl 0 -title "$file" -e sh -c "exiv2 pr -q -pa '$file' | less" & done ;; | ||||
"C-g") gimp "$@" & ;; | "C-g") tr '\n' '\0' | xargs -0 gimp & ;; | ||||
"C-comma") rotate 270 "$@" ;; | "C-comma") rotate 270 ;; | ||||
"C-period") rotate 90 "$@" ;; | "C-period") rotate 90 ;; | ||||
"C-slash") rotate 180 "$@" ;; | "C-slash") rotate 180 ;; | ||||
"C-t") tag_add "$@" ;; | "C-t") tag_add ;; | ||||
"M-T") tag_del "$@" ;; | "M-T") tag_del ;; | ||||
esac | esac | ||||
@@ -481,12 +481,13 @@ 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 i, j, retval, status; | FILE *pfs; | ||||
int fcnt = mode == MODE_THUMB && markcnt > 0 ? markcnt : 1; | bool marked = mode == MODE_THUMB && markcnt > 0; | ||||
bool changed = false; | bool changed = false; | ||||
char **args, kstr[32], oldbar[BAR_L_LEN]; | int f, i, pfd[2], retval, status; | ||||
struct stat *oldst, newst; | int fcnt = marked ? markcnt : 1; | ||||
struct { int fn; struct stat st; } *finfo; | char kstr[32], oldbar[BAR_L_LEN]; | ||||
struct stat *oldst, st; | |||||
if (keyhandler.cmd == NULL) { | if (keyhandler.cmd == NULL) { | ||||
if (!keyhandler.warned) { | if (!keyhandler.warned) { | ||||
@@ -498,55 +499,66 @@ void run_key_handler(const char *key, unsigned int mask) | |||||
if (key == NULL) | if (key == NULL) | ||||
return; | return; | ||||
finfo = s_malloc(fcnt * sizeof(*finfo)); | if (pipe(pfd) < 0) { | ||||
args = s_malloc((fcnt + 3) * sizeof(*args)); | warn("could not create pipe for key handler"); | ||||
args[0] = keyhandler.cmd; | return; | ||||
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", | if ((pfs = fdopen(pfd[1], "w")) == NULL) { | ||||
mask & ControlMask ? "C-" : "", | close(pfd[0]), close(pfd[1]); | ||||
mask & Mod1Mask ? "M-" : "", | warn("could not open pipe for key handler"); | ||||
mask & ShiftMask ? "S-" : "", key); | return; | ||||
} | |||||
oldst = s_malloc(fcnt * sizeof(*oldst)); | |||||
memcpy(oldbar, win.bar.l.buf, sizeof(oldbar)); | memcpy(oldbar, win.bar.l.buf, sizeof(oldbar)); | ||||
strncpy(win.bar.l.buf, "Running key handler...", win.bar.l.size); | strncpy(win.bar.l.buf, "Running key handler...", win.bar.l.size); | ||||
win_draw(&win); | win_draw(&win); | ||||
win_set_cursor(&win, CURSOR_WATCH); | win_set_cursor(&win, CURSOR_WATCH); | ||||
snprintf(kstr, sizeof(kstr), "%s%s%s%s", | |||||
mask & ControlMask ? "C-" : "", | |||||
mask & Mod1Mask ? "M-" : "", | |||||
mask & ShiftMask ? "S-" : "", key); | |||||
if ((pid = fork()) == 0) { | if ((pid = fork()) == 0) { | ||||
execv(keyhandler.cmd, args); | close(pfd[1]); | ||||
dup2(pfd[0], 0); | |||||
execl(keyhandler.cmd, keyhandler.cmd, kstr, NULL); | |||||
warn("could not exec key handler"); | warn("could not exec key handler"); | ||||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | ||||
} else if (pid < 0) { | } | ||||
close(pfd[0]); | |||||
if (pid < 0) { | |||||
fclose(pfs); | |||||
warn("could not fork key handler"); | warn("could not fork key handler"); | ||||
goto end; | goto end; | ||||
} | } | ||||
for (f = i = 0; f < fcnt; i++) { | |||||
if ((marked && files[i].marked) || (!marked && i == fileidx)) { | |||||
stat(files[i].path, &oldst[f]); | |||||
fprintf(pfs, "%s\n", files[i].name); | |||||
f++; | |||||
} | |||||
} | |||||
fclose(pfs); | |||||
waitpid(pid, &status, 0); | waitpid(pid, &status, 0); | ||||
retval = WEXITSTATUS(status); | retval = WEXITSTATUS(status); | ||||
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); | ||||
for (i = 0; i < fcnt; i++) { | for (f = i = 0; f < fcnt; i++) { | ||||
oldst = &finfo[i].st; | if ((marked && files[i].marked) || (!marked && i == fileidx)) { | ||||
if (stat(files[finfo[i].fn].path, &newst) != 0 || | if (stat(files[i].path, &st) != 0 || | ||||
memcmp(&oldst->st_mtime, &newst.st_mtime, sizeof(newst.st_mtime)) != 0) | memcmp(&oldst[f].st_mtime, &st.st_mtime, sizeof(st.st_mtime)) != 0) | ||||
{ | { | ||||
if (tns.thumbs != NULL) { | if (tns.thumbs != NULL) { | ||||
tns_unload(&tns, finfo[i].fn); | tns_unload(&tns, i); | ||||
tns.loadnext = MIN(tns.loadnext, finfo[i].fn); | tns.loadnext = MIN(tns.loadnext, i); | ||||
} | |||||
changed = true; | |||||
} | } | ||||
changed = true; | f++; | ||||
} | } | ||||
} | } | ||||
end: | end: | ||||
@@ -558,10 +570,9 @@ end: | |||||
memcpy(win.bar.l.buf, oldbar, win.bar.l.size); | memcpy(win.bar.l.buf, oldbar, win.bar.l.size); | ||||
} | } | ||||
} | } | ||||
free(oldst); | |||||
reset_cursor(); | reset_cursor(); | ||||
redraw(); | redraw(); | ||||
free(finfo); | |||||
free(args); | |||||
} | } | ||||
#define MODMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask)) | #define MODMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask)) | ||||
@@ -358,8 +358,9 @@ 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, followed by the paths | The next key combo is passed as its first argument. The paths of all marked | ||||
of all marked images or the path of the current image, if no image is marked. | images--or of the current image, if no image is marked--are passed via stdin, | ||||
one file path per line. | |||||
sxiv(1) will block until the handler terminates. It then checks which images | sxiv(1) will block until the handler terminates. It then checks which images | ||||
have been modified and reloads them. | have been modified and reloads them. | ||||