Pass file paths to key handler via stdin; fixes issue #187

This commit is contained in:
Bert Münnich 2014-11-27 22:37:20 +01:00
parent 51854c6148
commit 216ad81b59
3 changed files with 71 additions and 55 deletions

View file

@ -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

85
main.c
View file

@ -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))

5
sxiv.1
View file

@ -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.