nsxive267dc7
Pass marked files to external key handler in thumbnail mode; fixes issue #135
+76 −56
6 files changed
- M commands.c +1 −1
- M exec/key-handler +17 −14
- M main.c +49 −32
- M sxiv.1 +6 −4
- M thumbs.c +2 −4
- M thumbs.h +1 −1
| @@ -130,7 +130,7 @@ bool cg_reload_image(arg_t a) | |||
| 130 | 130 | load_image(fileidx); | |
| 131 | 131 | } else { | |
| 132 | 132 | win_set_cursor(&win, CURSOR_WATCH); | |
| 133 | − | if (!tns_load(&tns, fileidx, &files[fileidx], true, false)) { | |
| 133 | + | if (!tns_load(&tns, fileidx, &files[fileidx], true)) { | |
| 134 | 134 | remove_file(fileidx, false); | |
| 135 | 135 | tns.dirty = true; | |
| 136 | 136 | } | |
| @@ -2,33 +2,36 @@ | |||
| 2 | 2 | ||
| 3 | 3 | # Example for $XDG_CONFIG_HOME/sxiv/exec/key-handler | |
| 4 | 4 | # Called by sxiv(1) after the external prefix key (C-x by default) is pressed. | |
| 5 | − | # The next key combo is passed as its first argument and the path of the | |
| 6 | − | # current image as its second argument. | |
| 7 | − | # sxiv(1) blocks until this script terminates. It then checks if the image | |
| 8 | − | # has been modified and reloads it. | |
| 5 | + | # The next key combo is passed as its first argument, followed by the paths of | |
| 6 | + | # all marked images or the path of the current image, if no image is marked. | |
| 7 | + | # sxiv(1) blocks until this script terminates. It then checks which images | |
| 8 | + | # have been modified and reloads them. | |
| 9 | 9 | ||
| 10 | 10 | # The key combo argument has the following form: "[C-][M-][S-]KEY", | |
| 11 | 11 | # where C/M/S indicate Ctrl/Meta(Alt)/Shift modifier states and KEY is the X | |
| 12 | 12 | # keysym as listed in /usr/include/X11/keysymdef.h without the "XK_" prefix. | |
| 13 | 13 | ||
| 14 | − | case "$1" in | |
| 14 | + | key="$1" | |
| 15 | + | shift | |
| 16 | + | ||
| 17 | + | case "$key" in | |
| 15 | 18 | "C-c") | |
| 16 | − | echo -n "$2" | xsel -i ;; | |
| 19 | + | echo -n "$@" | xsel -i ;; | |
| 17 | 20 | "C-e") | |
| 18 | − | urxvt -bg "#444" -fg "#eee" -sl 0 -title "$2" -e sh -c "exiv2 pr -q -pa '$2' | less" & ;; | |
| 21 | + | for file in "$@"; do urxvt -bg "#444" -fg "#eee" -sl 0 -title "$file" -e sh -c "exiv2 pr -q -pa '$file' | less" & done ;; | |
| 19 | 22 | "C-g") | |
| 20 | − | gimp "$2" & ;; | |
| 23 | + | gimp "$@" & ;; | |
| 21 | 24 | "C-comma") | |
| 22 | − | exec jpegtran -rotate 270 -copy all -outfile "$2" "$2" ;; | |
| 25 | + | for file in "$@"; do jpegtran -rotate 270 -copy all -outfile "$file" "$file"; done ;; | |
| 23 | 26 | "C-period") | |
| 24 | − | exec jpegtran -rotate 90 -copy all -outfile "$2" "$2" ;; | |
| 27 | + | for file in "$@"; do jpegtran -rotate 90 -copy all -outfile "$file" "$file"; done ;; | |
| 25 | 28 | "C-slash") | |
| 26 | − | exec jpegtran -rotate 180 -copy all -outfile "$2" "$2" ;; | |
| 29 | + | for file in "$@"; do jpegtran -rotate 180 -copy all -outfile "$file" "$file"; done ;; | |
| 27 | 30 | "C-less") | |
| 28 | − | exec mogrify -rotate -90 "$2" ;; | |
| 31 | + | exec mogrify -rotate -90 "$@" ;; | |
| 29 | 32 | "C-greater") | |
| 30 | − | exec mogrify -rotate +90 "$2" ;; | |
| 33 | + | exec mogrify -rotate +90 "$@" ;; | |
| 31 | 34 | "C-question") | |
| 32 | − | exec mogrify -rotate 180 "$2" ;; | |
| 35 | + | exec mogrify -rotate 180 "$@" ;; | |
| 33 | 36 | esac | |
| 34 | 37 | ||
| @@ -467,10 +467,12 @@ void clear_resize(void) | |||
| 467 | 467 | void run_key_handler(const char *key, unsigned int mask) | |
| 468 | 468 | { | |
| 469 | 469 | pid_t pid; | |
| 470 | − | int retval, status; | |
| 471 | − | char kstr[32], oldbar[sizeof(win.bar.l)]; | |
| 472 | − | bool restore_bar = mode == MODE_IMAGE && info.cmd != NULL; | |
| 473 | − | struct stat oldst, newst; | |
| 470 | + | int i, j, retval, status; | |
| 471 | + | int fcnt = mode == MODE_THUMB && markcnt > 0 ? markcnt : 1; | |
| 472 | + | bool changed = false; | |
| 473 | + | char **args, kstr[32], oldbar[sizeof(win.bar.l)]; | |
| 474 | + | struct stat *oldst, newst; | |
| 475 | + | struct { int fn; struct stat st; } *finfo; | |
| 474 | 476 | ||
| 475 | 477 | if (keyhandler.cmd == NULL) { | |
| 476 | 478 | if (!keyhandler.warned) { | |
| @@ -482,20 +484,34 @@ void run_key_handler(const char *key, unsigned int mask) | |||
| 482 | 484 | if (key == NULL) | |
| 483 | 485 | return; | |
| 484 | 486 | ||
| 487 | + | finfo = s_malloc(fcnt * sizeof(*finfo)); | |
| 488 | + | args = s_malloc((fcnt + 3) * sizeof(*args)); | |
| 489 | + | args[0] = keyhandler.cmd; | |
| 490 | + | args[1] = kstr; | |
| 491 | + | args[fcnt+2] = NULL; | |
| 492 | + | if (mode == MODE_IMAGE || markcnt == 0) { | |
| 493 | + | finfo[0].fn = fileidx; | |
| 494 | + | stat(files[fileidx].path, &finfo[0].st); | |
| 495 | + | args[2] = (char*) files[fileidx].path; | |
| 496 | + | } else for (i = j = 0; i < filecnt; i++) { | |
| 497 | + | if (files[i].marked) { | |
| 498 | + | finfo[j].fn = i; | |
| 499 | + | stat(files[i].path, &finfo[j++].st); | |
| 500 | + | args[j+1] = (char*) files[i].path; | |
| 501 | + | } | |
| 502 | + | } | |
| 485 | 503 | snprintf(kstr, sizeof(kstr), "%s%s%s%s", | |
| 486 | 504 | mask & ControlMask ? "C-" : "", | |
| 487 | 505 | mask & Mod1Mask ? "M-" : "", | |
| 488 | 506 | mask & ShiftMask ? "S-" : "", key); | |
| 489 | 507 | ||
| 490 | − | if (restore_bar) | |
| 491 | − | memcpy(oldbar, win.bar.l, sizeof(win.bar.l)); | |
| 508 | + | memcpy(oldbar, win.bar.l, sizeof(win.bar.l)); | |
| 492 | 509 | strncpy(win.bar.l, "Running key handler...", sizeof(win.bar.l)); | |
| 493 | 510 | win_draw(&win); | |
| 494 | 511 | win_set_cursor(&win, CURSOR_WATCH); | |
| 495 | − | stat(files[fileidx].path, &oldst); | |
| 496 | 512 | ||
| 497 | 513 | if ((pid = fork()) == 0) { | |
| 498 | − | execl(keyhandler.cmd, keyhandler.cmd, kstr, files[fileidx].path, NULL); | |
| 514 | + | execv(keyhandler.cmd, args); | |
| 499 | 515 | warn("could not exec key handler"); | |
| 500 | 516 | exit(EXIT_FAILURE); | |
| 501 | 517 | } else if (pid < 0) { | |
| @@ -507,31 +523,31 @@ void run_key_handler(const char *key, unsigned int mask) | |||
| 507 | 523 | if (WIFEXITED(status) == 0 || retval != 0) | |
| 508 | 524 | warn("key handler exited with non-zero return value: %d", retval); | |
| 509 | 525 | ||
| 510 | − | if (stat(files[fileidx].path, &newst) == 0 && | |
| 511 | − | memcmp(&oldst.st_mtime, &newst.st_mtime, sizeof(oldst.st_mtime)) == 0) | |
| 512 | − | { | |
| 513 | − | /* file has not changed */ | |
| 514 | − | goto end; | |
| 526 | + | for (i = 0; i < fcnt; i++) { | |
| 527 | + | oldst = &finfo[i].st; | |
| 528 | + | if (stat(files[finfo[i].fn].path, &newst) != 0 || | |
| 529 | + | memcmp(&oldst->st_mtime, &newst.st_mtime, sizeof(newst.st_mtime)) != 0) | |
| 530 | + | { | |
| 531 | + | if (tns.thumbs != NULL) { | |
| 532 | + | tns.thumbs[finfo[i].fn].loaded = false; | |
| 533 | + | tns.loadnext = MIN(tns.loadnext, finfo[i].fn); | |
| 534 | + | } | |
| 535 | + | changed = true; | |
| 536 | + | } | |
| 515 | 537 | } | |
| 516 | − | restore_bar = false; | |
| 517 | − | strncpy(win.bar.l, "Reloading image...", sizeof(win.bar.l)); | |
| 518 | − | win_draw(&win); | |
| 519 | − | ||
| 538 | + | end: | |
| 520 | 539 | if (mode == MODE_IMAGE) { | |
| 521 | − | img_close(&img, true); | |
| 522 | − | load_image(fileidx); | |
| 523 | − | } | |
| 524 | − | if (!tns_load(&tns, fileidx, &files[fileidx], true, mode == MODE_IMAGE) && | |
| 525 | − | mode == MODE_THUMB) | |
| 526 | − | { | |
| 527 | − | remove_file(fileidx, false); | |
| 528 | − | tns.dirty = true; | |
| 540 | + | if (changed) { | |
| 541 | + | img_close(&img, true); | |
| 542 | + | load_image(fileidx); | |
| 543 | + | } else if (info.cmd != NULL) { | |
| 544 | + | memcpy(win.bar.l, oldbar, sizeof(win.bar.l)); | |
| 545 | + | } | |
| 529 | 546 | } | |
| 530 | − | end: | |
| 531 | − | if (restore_bar) | |
| 532 | − | memcpy(win.bar.l, oldbar, sizeof(win.bar.l)); | |
| 533 | 547 | reset_cursor(); | |
| 534 | 548 | redraw(); | |
| 549 | + | free(finfo); | |
| 550 | + | free(args); | |
| 535 | 551 | } | |
| 536 | 552 | ||
| 537 | 553 | #define MODMASK(mask) ((mask) & (ShiftMask|ControlMask|Mod1Mask)) | |
| @@ -651,7 +667,7 @@ void run(void) | |||
| 651 | 667 | int xfd; | |
| 652 | 668 | fd_set fds; | |
| 653 | 669 | struct timeval timeout; | |
| 654 | − | bool discard, to_set; | |
| 670 | + | bool discard, reload, to_set; | |
| 655 | 671 | XEvent ev, nextev; | |
| 656 | 672 | ||
| 657 | 673 | set_timeout(redraw, 25, false); | |
| @@ -661,9 +677,10 @@ void run(void) | |||
| 661 | 677 | XPending(win.env.dpy) == 0) | |
| 662 | 678 | { | |
| 663 | 679 | /* load thumbnails */ | |
| 680 | + | reload = tns.loadnext != tns.cnt; | |
| 664 | 681 | set_timeout(redraw, TO_REDRAW_THUMBS, false); | |
| 665 | − | if (tns_load(&tns, tns.loadnext, &files[tns.loadnext], false, false)) { | |
| 666 | − | if (tns.cnt == tns.loadnext) | |
| 682 | + | if (tns_load(&tns, tns.loadnext, &files[tns.loadnext], reload)) { | |
| 683 | + | if (!reload) | |
| 667 | 684 | tns.cnt++; | |
| 668 | 685 | } else { | |
| 669 | 686 | remove_file(tns.loadnext, false); | |
| @@ -860,7 +877,7 @@ int main(int argc, char **argv) | |||
| 860 | 877 | if (options->thumb_mode) { | |
| 861 | 878 | mode = MODE_THUMB; | |
| 862 | 879 | tns_init(&tns, filecnt, &win, &fileidx); | |
| 863 | − | while (!tns_load(&tns, 0, &files[0], false, false)) | |
| 880 | + | while (!tns_load(&tns, 0, &files[0], false)) | |
| 864 | 881 | remove_file(0, false); | |
| 865 | 882 | tns.cnt = 1; | |
| 866 | 883 | } else { | |
| @@ -112,7 +112,8 @@ Toggle fullscreen mode. | |||
| 112 | 112 | Toggle visibility of info bar on bottom of window. | |
| 113 | 113 | .TP | |
| 114 | 114 | .B Ctrl-x | |
| 115 | − | Send the next key to the external key-handler. | |
| 115 | + | Send the next key to the external key-handler. See section EXTERNAL KEY HANDLER | |
| 116 | + | for more information. | |
| 116 | 117 | .TP | |
| 117 | 118 | .B g | |
| 118 | 119 | Go to the first image. | |
| @@ -354,9 +355,10 @@ located in | |||
| 354 | 355 | .IR $XDG_CONFIG_HOME/sxiv/exec/key-handler . | |
| 355 | 356 | The handler is invoked by pressing | |
| 356 | 357 | .BR Ctrl-x . | |
| 357 | − | The next key combo is then passed as its first argument and the path of the | |
| 358 | − | current image as its second argument. sxiv(1) will block until the handler | |
| 359 | − | terminates. It then checks if the image has been modified and reloads it. | |
| 358 | + | The next key combo is then passed as its first argument, followed by the paths | |
| 359 | + | of all marked images or the path of the current image, if no image is marked. | |
| 360 | + | sxiv(1) will block until the handler terminates. It then checks which images | |
| 361 | + | have been modified and reloads them. | |
| 360 | 362 | ||
| 361 | 363 | The key combo argument has the following form: "[C-][M-][S-]KEY", | |
| 362 | 364 | 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) | |||
| 209 | 209 | } | |
| 210 | 210 | } | |
| 211 | 211 | ||
| 212 | − | bool tns_load(tns_t *tns, int n, const fileinfo_t *file, | |
| 213 | − | bool force, bool silent) | |
| 212 | + | bool tns_load(tns_t *tns, int n, const fileinfo_t *file, bool force) | |
| 214 | 213 | { | |
| 215 | 214 | int w, h; | |
| 216 | 215 | bool cache_hit = false; | |
| @@ -295,8 +294,7 @@ bool tns_load(tns_t *tns, int n, const fileinfo_t *file, | |||
| 295 | 294 | if (im == NULL && (access(file->path, R_OK) < 0 || | |
| 296 | 295 | (im = imlib_load_image(file->path)) == NULL)) | |
| 297 | 296 | { | |
| 298 | − | if (!silent) | |
| 299 | − | warn("could not open image: %s", file->name); | |
| 297 | + | warn("could not open image: %s", file->name); | |
| 300 | 298 | return false; | |
| 301 | 299 | } | |
| 302 | 300 | } | |
| @@ -57,7 +57,7 @@ void tns_clean_cache(tns_t*); | |||
| 57 | 57 | void tns_init(tns_t*, int, win_t*, int*); | |
| 58 | 58 | void tns_free(tns_t*); | |
| 59 | 59 | ||
| 60 | − | bool tns_load(tns_t*, int, const fileinfo_t*, bool, bool); | |
| 60 | + | bool tns_load(tns_t*, int, const fileinfo_t*, bool); | |
| 61 | 61 | ||
| 62 | 62 | void tns_render(tns_t*); | |
| 63 | 63 | void tns_mark(tns_t*, int, bool); | |