3 # Description: tabbed/xembed based file previewer
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)
18 # - xdotool (optional, to keep main window focused)
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
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.
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
44 # [1]: https://tools.suckless.org/tabbed/
45 # [2]: https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html
47 # Shell: Bash (job control is weakly specified in POSIX)
48 # Author: Léo Villeveygoux
52 PAGER=${PAGER:-"vim -R"}
53 NUKE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke"
55 if [ -n "$WAYLAND_DISPLAY" ] ; then
56 echo "Wayland is not supported in preview-tabbed, this plugin could freeze your session!" >&2
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
66 elif type alacritty >/dev/null 2>&1 ; then
67 TERMINAL="alacritty --embed"
69 echo "No xembed term found" >&2
74 # $1 -> $XID, $2 -> $FILE
75 $TERMINAL "$1" -e "$NUKE" "$2" &
84 jobs # Get rid of the "Completed" entries
86 TABBEDPID="$(jobs -p %%)"
88 if [ -z "$TABBEDPID" ] ; then
89 echo "Can't start tabbed"
99 VIEWERPID="$(jobs -p %%)"
103 if [ -n "$VIEWERPID" ] && jobs -p | grep "$VIEWERPID" ; then
119 MAINWINDOW="$(xdotool getactivewindow)"
122 trap sigint_kill SIGINT
124 xdotool windowactivate "$MAINWINDOW"
126 # Bruteforce focus stealing prevention method,
127 # works well in floating window managers like XFCE
128 # but make interaction with the preview window harder
129 # (uncomment to use):
130 #xdotool behave "$XID" focus windowactivate "$MAINWINDOW" &
132 while read -r FILE ; do
134 jobs # Get rid of the "Completed" entries
136 if ! jobs | grep tabbed ; then
140 if [ ! -e "$FILE" ] ; then
146 MIME="$(file -bL --mime-type "$FILE")"
150 if type mpv >/dev/null 2>&1 ; then
151 mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" &
153 term_nuke "$XID" "$FILE"
157 if type mpv >/dev/null 2>&1 ; then
158 mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" &
160 term_nuke "$XID" "$FILE"
164 if type sxiv >/dev/null 2>&1 ; then
165 sxiv -ae "$XID" "$FILE" &
166 elif type nsxiv >/dev/null 2>&1 ; then
167 nsxiv -ae "$XID" "$FILE" &
169 term_nuke "$XID" "$FILE"
173 if type zathura >/dev/null 2>&1 ; then
174 zathura -e "$XID" "$FILE" &
176 term_nuke "$XID" "$FILE"
180 $TERMINAL "$XID" -e nnn "$FILE" &
183 if [ -x "$NUKE" ] ; then
184 term_nuke "$XID" "$FILE"
186 # shellcheck disable=SC2086
187 $TERMINAL "$XID" -e $PAGER "$FILE" &
191 if [ -x "$NUKE" ] ; then
192 term_nuke "$XID" "$FILE"
194 $TERMINAL "$XID" -e sh -c "file '$FILE' | $PAGER -" &
200 # following lines are not needed with the bruteforce xdotool method
201 ACTIVE_XID="$(xdotool getactivewindow)"
202 if [ $((ACTIVE_XID == XID)) -ne 0 ] ; then
203 xdotool windowactivate "$MAINWINDOW"
205 timeout "$XDOTOOL_TIMEOUT" xdotool behave "$XID" focus windowactivate "$MAINWINDOW" &
212 if [ ! -r "$NNN_FIFO" ] ; then
213 echo "Can't read \$NNN_FIFO ('$NNN_FIFO')"
217 previewer_loop < "$NNN_FIFO" &