dotfiles

Older, unmaintained dotfiles by mrgrouse
Log | Files | Refs | README

preview-tabbed (6885B)


      1 #!/usr/bin/env bash
      2 
      3 # Description: tabbed/xembed based file previewer
      4 #
      5 # Dependencies:
      6 #   - tabbed (https://tools.suckless.org/tabbed): xembed host
      7 #   - xterm (or urxvt or st or alacritty) : xembed client for text-based preview
      8 #   - mpv (https://mpv.io): xembed client for video/audio
      9 #   - sxiv (https://github.com/muennich/sxiv) or,
     10 #   - nsxiv (https://codeberg.org/nsxiv/nsxiv) : xembed client for images
     11 #   - zathura (https://pwmt.org/projects/zathura): xembed client for PDF
     12 #   - nnn's nuke plugin for text preview and fallback
     13 #     nuke is a fallback for 'mpv', 'sxiv'/'nsxiv', and 'zathura', but has its
     14 #     own dependencies, see the script for more information
     15 #   - vim (or any editor/pager really)
     16 #   - file
     17 #   - mktemp
     18 #   - xdotool (optional, to keep main window focused)
     19 #
     20 # Usage:
     21 #   - Install the dependencies. Then set a NNN_FIFO
     22 #     and set a key for the plugin, then start `nnn`:
     23 #       $ NNN_FIFO=/tmp/nnn.fifo nnn
     24 #   - Launch the plugin with the designated key from nnn
     25 #
     26 # Notes:
     27 #   1. This plugin needs a "NNN_FIFO" to work. See man.
     28 #   2. If the same NNN_FIFO is used in multiple nnn instances, there will be one
     29 #      common preview window. With different FIFO paths, they will be independent.
     30 #   3. This plugin only works on X, not on Wayland.
     31 #
     32 # How it works:
     33 #   We use `tabbed` [1] as a xembed [2] host, to have a single window
     34 #   owning each previewer window. So each previewer must be a xembed client.
     35 #   For text previewers, this is not an issue, as there are a lot of
     36 #   xembed-able terminal emulator (we default to `xterm`, but examples are
     37 #   provided for `urxvt` and `st`). For graphic preview this can be trickier,
     38 #   but a few popular viewers are xembed-able, we use:
     39 #     - `mpv`: multimedia player, for video/audio preview
     40 #     - `sxiv`/`nsxiv`: image viewer
     41 #     - `zathura`: PDF viewer
     42 #     - but we always fallback to `nuke` plugin
     43 #
     44 # [1]: https://tools.suckless.org/tabbed/
     45 # [2]: https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html
     46 #
     47 # Shell: Bash (job control is weakly specified in POSIX)
     48 # Author: Léo Villeveygoux
     49 
     50 
     51 XDOTOOL_TIMEOUT=2
     52 PAGER=${PAGER:-"vim -R"}
     53 NUKE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke"
     54 
     55 if [ -n "$WAYLAND_DISPLAY" ] ; then
     56     echo "Wayland is not supported in preview-tabbed, this plugin could freeze your session!" >&2
     57     exit 1
     58 fi
     59 
     60 if type xterm >/dev/null 2>&1 ; then
     61     TERMINAL="xterm -into"
     62 elif type urxvt >/dev/null 2>&1 ; then
     63     TERMINAL="urxvt -embed"
     64 elif type st >/dev/null 2>&1 ; then
     65     TERMINAL="st -w"
     66 elif type alacritty >/dev/null 2>&1 ; then
     67     TERMINAL="alacritty --embed"
     68 else
     69     echo "No xembed term found" >&2
     70 fi
     71 
     72 if type xdg-user-dir >/dev/null 2>&1 ; then
     73     PICTURES_DIR=$(xdg-user-dir PICTURES)
     74 fi
     75 
     76 term_nuke () {
     77     # $1 -> $XID, $2 -> $FILE
     78     $TERMINAL "$1" -e "$NUKE" "$2" &
     79 }
     80 
     81 start_tabbed () {
     82     FIFO="$(mktemp -u)"
     83     mkfifo "$FIFO"
     84 
     85     tabbed > "$FIFO" &
     86 
     87     jobs # Get rid of the "Completed" entries
     88 
     89     TABBEDPID="$(jobs -p %%)"
     90 
     91     if [ -z "$TABBEDPID" ] ; then
     92         echo "Can't start tabbed"
     93         exit 1
     94     fi
     95 
     96     read -r XID < "$FIFO"
     97 
     98     rm -- "$FIFO"
     99 }
    100 
    101 get_viewer_pid () {
    102         VIEWERPID="$(jobs -p %%)"
    103 }
    104 
    105 kill_viewer () {
    106         if [ -n "$VIEWERPID" ] && jobs -p | grep "$VIEWERPID" ; then
    107             kill "$VIEWERPID"
    108         fi
    109 }
    110 
    111 sigint_kill () {
    112 	kill_viewer
    113 	kill "$TABBEDPID"
    114 	exit 0
    115 }
    116 
    117 previewer_loop () {
    118     unset -v NNN_FIFO
    119     # mute from now
    120     exec >/dev/null 2>&1
    121 
    122     MAINWINDOW="$(xdotool getactivewindow)"
    123 
    124     start_tabbed
    125     trap sigint_kill SIGINT
    126 
    127     xdotool windowactivate "$MAINWINDOW"
    128 
    129     # Bruteforce focus stealing prevention method,
    130     # works well in floating window managers like XFCE
    131     # but make interaction with the preview window harder
    132     # (uncomment to use):
    133     #xdotool behave "$XID" focus windowactivate "$MAINWINDOW" &
    134 
    135     while read -r FILE ; do
    136 
    137         jobs # Get rid of the "Completed" entries
    138 
    139         if ! jobs | grep tabbed ; then
    140             break
    141         fi
    142 
    143         if [ ! -e "$FILE" ] ; then
    144             continue
    145         fi
    146 
    147         kill_viewer
    148 
    149         MIME="$(file -bL --mime-type "$FILE")"
    150 
    151         case "$MIME" in
    152             video/*)
    153                 if type mpv >/dev/null 2>&1 ; then
    154                     mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" &
    155                 else
    156                     term_nuke "$XID" "$FILE"
    157                 fi
    158                 ;;
    159             audio/*)
    160                 if type mpv >/dev/null 2>&1 ; then
    161                     mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" &
    162                 else
    163                     term_nuke "$XID" "$FILE"
    164                 fi
    165                 ;;
    166             image/*)
    167                 if type sxiv >/dev/null 2>&1 ; then
    168                     sxiv -ae "$XID" "$FILE" &
    169                 elif type nsxiv >/dev/null 2>&1 ; then
    170                     nsxiv -ae "$XID" "$FILE" &
    171                 else
    172                     term_nuke "$XID" "$FILE"
    173                 fi
    174                 ;;
    175             application/pdf)
    176                 if type zathura >/dev/null 2>&1 ; then
    177                     zathura -e "$XID" "$FILE" &
    178                 else
    179                     term_nuke "$XID" "$FILE"
    180                 fi
    181                 ;;
    182             inode/directory)
    183                 if [[ -n $PICTURES_DIR && "$FILE" == "$PICTURES_DIR"* ]] ; then
    184                     if type sxiv >/dev/null 2>&1 ; then
    185                         sxiv -te "$XID" "$FILE" &
    186                     elif type nsxiv >/dev/null 2>&1 ; then
    187                         nsxiv -te "$XID" "$FILE" &
    188                     else
    189                         $TERMINAL "$XID" -e nnn "$FILE" &
    190                     fi
    191                 else
    192                     $TERMINAL "$XID" -e nnn "$FILE" &
    193                 fi
    194                 ;;
    195             text/*)
    196                 if [ -x "$NUKE" ] ; then
    197                     term_nuke "$XID" "$FILE"
    198                 else
    199                     # shellcheck disable=SC2086
    200                     $TERMINAL "$XID" -e $PAGER "$FILE" &
    201                 fi
    202                 ;;
    203             *)
    204                 if [ -x "$NUKE" ] ; then
    205                     term_nuke "$XID" "$FILE"
    206                 else
    207                     $TERMINAL "$XID" -e sh -c "file '$FILE' | $PAGER -" &
    208                 fi
    209                 ;;
    210         esac
    211         get_viewer_pid
    212 
    213         # following lines are not needed with the bruteforce xdotool method
    214         ACTIVE_XID="$(xdotool getactivewindow)"
    215         if [ $((ACTIVE_XID == XID)) -ne 0 ] ; then
    216             xdotool windowactivate "$MAINWINDOW"
    217         else
    218             timeout "$XDOTOOL_TIMEOUT" xdotool behave "$XID" focus windowactivate "$MAINWINDOW" &
    219         fi
    220     done
    221     kill "$TABBEDPID"
    222     kill_viewer
    223 }
    224 
    225 if [ ! -r "$NNN_FIFO" ] ; then
    226     echo "Can't read \$NNN_FIFO ('$NNN_FIFO')"
    227     exit 1
    228 fi
    229 
    230 previewer_loop < "$NNN_FIFO" &
    231 disown