|
- #!/usr/bin/env bash
-
- # Description: tabbed/xembed based file previewer
- #
- # Note: This plugin needs a "NNN_FIFO" to work. See man.
- #
- # Dependencies:
- # - tabbed (https://tools.suckless.org/tabbed): xembed host
- # - xterm (or urxvt or st) : xembed client for text-based preview
- # - mpv (https://mpv.io): xembed client for video/audio
- # - sxiv (https://github.com/muennich/sxiv): xembed client for images
- # - zathura (https://pwmt.org/projects/zathura): xembed client for PDF documents
- # - nnn's nuke plugin for text preview and fallback (should be in plugins directory)
- # nuke is a fallback for 'mpv', 'sxiv', and 'zathura', but it has has its own
- # dependencies, see the script itself
- # - vim (or any editor/pager really)
- # - file
- # - mktemp
- # - xdotool (optional, to keep main window focused)
- #
- # How to use:
- # First, install the dependencies. Then you need to set a NNN_FIFO path
- # and set a key for the plugin, then start `nnn`:
- #
- # $ NNN_FIFO=/tmp/nnn.fifo nnn
- #
- # Then in `nnn`, launch the `preview-tabbed` plugin.
- #
- # If you provide the same NNN_FIFO to all nnn instances, there will be a
- # single common preview window. I you provide different FIFO path, they
- # will be independent.
- #
- # How it works:
- # We use `tabbed` [1] as a xembed [2] host, to have a single window
- # owning each previewer window. So each previewer must be a xembed client.
- # For text previewers, this is not an issue, as there are a lot of
- # xembed-able terminal emulator (we default to `xterm`, but examples are
- # provided for `urxvt` and `st`). For graphic preview this can be trickier,
- # but a few popular viewers are xembed-able, we use:
- # - `mpv`: multimedia player, for video/audio preview
- # - `sxiv`: image viewer
- # - `zathura`: PDF viewer
- # - but we always fallback to `nuke` plugin
- #
- # [1]: https://tools.suckless.org/tabbed/
- # [2]: https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html
- #
- # Shell: bash (job control is weakly specified in POSIX)
- # Author: Léo Villeveygoux
-
-
- XDOTOOL_TIMEOUT=2
- PAGER=${PAGER:-"vim -R"}
- NUKE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke"
-
-
- if which xterm >/dev/null 2>&1 ; then
- TERMINAL="xterm -into"
- elif which urxvt >/dev/null 2>&1 ; then
- TERMINAL="urxvt -embed"
- elif which st >/dev/null 2>&1 ; then
- TERMINAL="st -w"
- else
- echo "No xembed term found" >&2
- fi
-
-
- term_nuke () {
- # $1 -> $XID, $2 -> $FILE
- $TERMINAL "$1" -e "$NUKE" "$2" &
- }
-
- start_tabbed () {
- FIFO="$(mktemp -u)"
- mkfifo "$FIFO"
-
- tabbed > "$FIFO" &
-
- jobs # Get rid of the "Completed" entries
-
- TABBEDPID="$(jobs -p %%)"
-
- if [ -z "$TABBEDPID" ] ; then
- echo "Can't start tabbed"
- exit 1
- fi
-
- read -r XID < "$FIFO"
-
- rm "$FIFO"
- }
-
- get_viewer_pid () {
- VIEWERPID="$(jobs -p %%)"
- }
-
- kill_viewer () {
- if [ -n "$VIEWERPID" ] && jobs -p | grep "$VIEWERPID" ; then
- kill "$VIEWERPID"
- fi
- }
-
- sigint_kill () {
- kill_viewer
- kill "$TABBEDPID"
- exit 0
- }
-
- previewer_loop () {
- unset -v NNN_FIFO
- # mute from now
- exec >/dev/null 2>&1
-
- MAINWINDOW="$(xdotool getactivewindow)"
-
- start_tabbed
- trap sigint_kill SIGINT
-
- xdotool windowactivate "$MAINWINDOW"
-
- # Bruteforce focus stealing prevention method,
- # works well in floating window managers like XFCE
- # but make interaction with the preview window harder
- # (uncomment to use):
- #xdotool behave "$XID" focus windowactivate "$MAINWINDOW" &
-
- while read -r FILE ; do
-
- jobs # Get rid of the "Completed" entries
-
- if ! jobs | grep tabbed ; then
- break
- fi
-
- if [ ! -e "$FILE" ] ; then
- continue
- fi
-
- kill_viewer
-
- MIME="$(file -b --mime-type "$FILE")"
-
- case "$MIME" in
- video/*)
- if which mpv >/dev/null 2>&1 ; then
- mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" &
- else
- term_nuke "$XID" "$FILE"
- fi
- ;;
- audio/*)
- if which mpv >/dev/null 2>&1 ; then
- mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" &
- else
- term_nuke "$XID" "$FILE"
- fi
- ;;
- image/*)
- if which sxiv >/dev/null 2>&1 ; then
- sxiv -e "$XID" "$FILE" &
- else
- term_nuke "$XID" "$FILE"
- fi
- ;;
- application/pdf)
- if which zathura >/dev/null 2>&1 ; then
- zathura -e "$XID" "$FILE" &
- else
- term_nuke "$XID" "$FILE"
- fi
- ;;
- inode/directory)
- $TERMINAL "$XID" -e nnn "$FILE" &
- ;;
- text/*)
- if [ -x "$NUKE" ] ; then
- term_nuke "$XID" "$FILE"
- else
- # shellcheck disable=SC2086
- $TERMINAL "$XID" -e $PAGER "$FILE" &
- fi
- ;;
- *)
- if [ -x "$NUKE" ] ; then
- term_nuke "$XID" "$FILE"
- else
- $TERMINAL "$XID" -e sh -c "file '$FILE' | $PAGER -" &
- fi
- ;;
- esac
- get_viewer_pid
-
- # following lines are not needed with the bruteforce xdotool method
- ACTIVE_XID="$(xdotool getactivewindow)"
- if [ $((ACTIVE_XID == XID)) -ne 0 ] ; then
- xdotool windowactivate "$MAINWINDOW"
- else
- timeout "$XDOTOOL_TIMEOUT" xdotool behave "$XID" focus windowactivate "$MAINWINDOW" &
- fi
- done
- kill "$TABBEDPID"
- kill_viewer
- }
-
- if [ ! -r "$NNN_FIFO" ] ; then
- echo "Can't read \$NNN_FIFO ('$NNN_FIFO')"
- exit 1
- fi
-
- previewer_loop < "$NNN_FIFO" &
- disown
|