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
# 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
# all marked images or the path of the current image, if no image is marked.
# The next key combo is passed as its first argument. The paths of all 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
# 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
# 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 TMPFILE="/tmp/sxiv.$$"
rotate() {
degree="$1"; shift
for file in "$@"; do
degree="$1"
while read file; do
case "$(file -b -i "$file")" in
image/jpeg*) jpegtran -rotate "$degree" -copy all -outfile "$file" "$file" ;;
*) mogrify -rotate "$degree" "$file" ;;
@ -28,25 +30,27 @@ tag_add() {
>>"$TAGFILE"
tags=$(dmenu <"$TAGFILE" | tr '\n' ',')
[ -z "$tags" ] && return
iptckwed -a "$tags" "$@"
iptckwed -i -a "$tags"
echo -n "$tags" | tr ',' '\n' | sort - "$TAGFILE" | uniq >"$TAGFILE.new"
mv -f "$TAGFILE"{.new,}
}
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
iptckwed -r "$tags" "$@"
iptckwed -i -r "$tags" <"$TMPFILE"
rm -f "$TMPFILE"
}
case "$KEY" in
"C-c") echo -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-g") gimp "$@" & ;;
"C-comma") rotate 270 "$@" ;;
"C-period") rotate 90 "$@" ;;
"C-slash") rotate 180 "$@" ;;
"C-t") tag_add "$@" ;;
"M-T") tag_del "$@" ;;
"C-c") tr '\n' ' ' | xsel -i ;;
"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") tr '\n' '\0' | xargs -0 gimp & ;;
"C-comma") rotate 270 ;;
"C-period") rotate 90 ;;
"C-slash") rotate 180 ;;
"C-t") tag_add ;;
"M-T") tag_del ;;
esac

85
main.c
View File

@ -481,12 +481,13 @@ void clear_resize(void)
void run_key_handler(const char *key, unsigned int mask)
{
pid_t pid;
int i, j, retval, status;
int fcnt = mode == MODE_THUMB && markcnt > 0 ? markcnt : 1;
FILE *pfs;
bool marked = mode == MODE_THUMB && markcnt > 0;
bool changed = false;
char **args, kstr[32], oldbar[BAR_L_LEN];
struct stat *oldst, newst;
struct { int fn; struct stat st; } *finfo;
int f, i, pfd[2], retval, status;
int fcnt = marked ? markcnt : 1;
char kstr[32], oldbar[BAR_L_LEN];
struct stat *oldst, st;
if (keyhandler.cmd == NULL) {
if (!keyhandler.warned) {
@ -498,55 +499,66 @@ void run_key_handler(const char *key, unsigned int mask)
if (key == NULL)
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;
}
if (pipe(pfd) < 0) {
warn("could not create pipe for key handler");
return;
}
snprintf(kstr, sizeof(kstr), "%s%s%s%s",
mask & ControlMask ? "C-" : "",
mask & Mod1Mask ? "M-" : "",
mask & ShiftMask ? "S-" : "", key);
if ((pfs = fdopen(pfd[1], "w")) == NULL) {
close(pfd[0]), close(pfd[1]);
warn("could not open pipe for key handler");
return;
}
oldst = s_malloc(fcnt * sizeof(*oldst));
memcpy(oldbar, win.bar.l.buf, sizeof(oldbar));
strncpy(win.bar.l.buf, "Running key handler...", win.bar.l.size);
win_draw(&win);
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) {
execv(keyhandler.cmd, args);
close(pfd[1]);
dup2(pfd[0], 0);
execl(keyhandler.cmd, keyhandler.cmd, kstr, NULL);
warn("could not exec key handler");
exit(EXIT_FAILURE);
} else if (pid < 0) {
}
close(pfd[0]);
if (pid < 0) {
fclose(pfs);
warn("could not fork key handler");
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);
retval = WEXITSTATUS(status);
if (WIFEXITED(status) == 0 || retval != 0)
warn("key handler exited with non-zero return value: %d", retval);
for (i = 0; i < fcnt; i++) {
oldst = &finfo[i].st;
if (stat(files[finfo[i].fn].path, &newst) != 0 ||
memcmp(&oldst->st_mtime, &newst.st_mtime, sizeof(newst.st_mtime)) != 0)
{
if (tns.thumbs != NULL) {
tns_unload(&tns, finfo[i].fn);
tns.loadnext = MIN(tns.loadnext, finfo[i].fn);
for (f = i = 0; f < fcnt; i++) {
if ((marked && files[i].marked) || (!marked && i == fileidx)) {
if (stat(files[i].path, &st) != 0 ||
memcmp(&oldst[f].st_mtime, &st.st_mtime, sizeof(st.st_mtime)) != 0)
{
if (tns.thumbs != NULL) {
tns_unload(&tns, i);
tns.loadnext = MIN(tns.loadnext, i);
}
changed = true;
}
changed = true;
f++;
}
}
end:
@ -558,10 +570,9 @@ end:
memcpy(win.bar.l.buf, oldbar, win.bar.l.size);
}
}
free(oldst);
reset_cursor();
redraw();
free(finfo);
free(args);
}
#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 .
The handler is invoked by pressing
.BR Ctrl-x .
The next key combo is then passed as its first argument, followed by the paths
of all marked images or the path of the current image, if no image is marked.
The next key combo is passed as its first argument. The paths of all 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
have been modified and reloads them.