-
Notifications
You must be signed in to change notification settings - Fork 330
Previews
Note
The original Überzug project is no longer maintained. You might want to use one of the alternative forks instead:
If you want to use Überzug for image previews, it will be necessary to start both lf and Überzug in a wrapper script. Place a file with the following contents in your PATH
:
#!/bin/sh
set -euf
if [ -n "${DISPLAY-}" ] && [ -z "${FIFO_UEBERZUG-}" ]; then
export FIFO_UEBERZUG="${TMPDIR:-/tmp}/lf-ueberzug-$$"
cleanup() {
exec 3>&-
rm -- "$FIFO_UEBERZUG"
}
mkfifo -- "$FIFO_UEBERZUG"
# Execute ueberzug in a loop in case it crashes. Ueberzug dies if its
# associated window is closed. This breaks image previews when using tmux and
# reattaching to an existing session.
while [ -p "$FIFO_UEBERZUG" ] && ! ueberzug layer -s <"$FIFO_UEBERZUG"; do :; done &
# Open the FIFO for writing. FIFO readers receive an EOF once all writers
# have closed their respective file descriptors. Holding a file descriptor
# will effectively keep ueberzug alive as long as lf lives.
exec 3>"$FIFO_UEBERZUG"
trap cleanup EXIT
# Start lf without passing in the file descriptor. This is done to avoid the
# lf server being passed the file descriptor, which would cause ueberzug to
# live longer than is strictly necessary.
lf "$@" 3>&-
else
exec lf "$@"
fi
Now, make a file named cleaner
in the lf
configuration directory with the following contents:
#!/bin/sh
[ -p "$FIFO_UEBERZUG" ] && printf '{"action":"remove","identifier":"preview"}\n' >"$FIFO_UEBERZUG"
Next, make a file named previewer
in the lf
configuration directory. Your previewer script may look like this:
#!/bin/sh
draw() {
path="$(readlink -f -- "$1" | sed 's/\\/\\\\/g;s/"/\\"/g')"
printf '{"action":"add","identifier":"preview","x":%d,"y":%d,"width":%d,"height":%d,"scaler":"contain","scaling_position_x":0.5,"scaling_position_y":0.5,"path":"%s"}\n' \
"$x" "$y" "$width" "$height" "$path" >"$FIFO_UEBERZUG"
exit 1
}
hash() {
cache="$HOME/.cache/lf/$(stat --printf '%n\0%i\0%F\0%s\0%W\0%Y' -- "$(readlink -f -- "$1")" | sha256sum | cut -d' ' -f1).jpg"
}
cache() {
if ! [ -f "$cache" ]; then
dir="$(dirname -- "$cache")"
[ -d "$dir" ] || mkdir -p -- "$dir"
"$@"
fi
draw "$cache"
}
file="$1"
width="$2"
height="$3"
x="$4"
y="$5"
case "$(file -Lb --mime-type -- "$file")" in
image/*)
if [ -p "$FIFO_UEBERZUG" ]; then
# ueberzug doesn't handle image orientation correctly
orientation="$(magick identify -format '%[orientation]\n' -- "$file[0]")"
if [ -n "$orientation" ] \
&& [ "$orientation" != Undefined ] \
&& [ "$orientation" != TopLeft ]; then
hash "$file"
cache magick -- "$file[0]" -auto-orient "$cache"
else
draw "$file"
fi
fi
;;
video/*)
if [ -p "$FIFO_UEBERZUG" ]; then
hash "$file"
cache ffmpegthumbnailer -i "$file" -o "$cache" -s 0
fi
;;
text/*)
exec cat "$file"
;;
esac
file -Lb -- "$file" | fold -s -w "$width"
exit 0
Using a preview cache in ~/.cache/lf
is entirely optional. A path under /tmp/
may be used instead.
Make sure all of the above files are executable. Take note that the draw()
function exits with code 1. This is to signal lf
not to cache the result of the previewer script so that the next time the user selects the same file the previewer script will be executed again.
Finally, place the following lines in your lfrc
:
set previewer ~/.config/lf/previewer
set cleaner ~/.config/lf/cleaner
More extensive examples are available at the following repositories:
- https://github.com/neeshy/lfimg
- https://github.com/slavistan/howto-lf-image-previews
- https://github.com/slavistan/lf-gadgets/tree/master/lf-ueberzug
- https://github.com/OliverLew/dotfiles/tree/master/lf
- https://github.com/cirala/lfimg
- https://github.com/Naheel-Azawy/stpv and https://github.com/NikitaIvanovV/ctpv (Read more here)
The following setup will use kitty to display images, and fall back to pistol for everything else. Other terminals like wezterm and Konsole also implement Kitty's image protocol, so those can be substituted if desired.
As usual, we'll specify a previewer and a cleaner in ~/.config/lf/lfrc
:
set previewer ~/.config/lf/previewer
set cleaner ~/.config/lf/cleaner
The cleaner script is ~/.config/lf/cleaner
:
#!/bin/sh
exec kitten icat --clear --stdin no --transfer-mode file </dev/null >/dev/tty
And the previewer is ~/.config/lf/previewer
:
#!/bin/sh
draw() {
kitten icat --stdin no --transfer-mode file --place "${w}x${h}@${x}x${y}" "$1" </dev/null >/dev/tty
exit 1
}
file="$1"
w="$2"
h="$3"
x="$4"
y="$5"
case "$(file -Lb --mime-type "$file")" in
image/*)
draw "$file"
;;
video/*)
# vidthumb is from here:
# https://raw.githubusercontent.com/duganchen/kitty-pistol-previewer/main/vidthumb
draw "$(vidthumb "$file")"
;;
esac
pistol "$file"
Note that this example uses the vidthumb script.
If your terminal has Sixel support, you can display image previews in Sixel format:
- Install
chafa
or any similar program that can convert images to Sixel. - Set the
sixel
andpreviewer
options in your config file. - Add the following
previewer
script:
#!/bin/sh
case "$(file -Lb --mime-type -- "$1")" in
image/*)
chafa -f sixel -s "$2x$3" --animate off --polite on "$1"
exit 1
;;
text/*)
cat "$1"
;;
esac
Note
This only works partially for KDE konsole (images can't be cleaned up automatically, only by redrawing the screen with ctrl+l
).
In the official Sixel manual, printing new characters over an image is undefined, but most other terminals will overwrite the image.
Note
Sixel previews will appear distorted if chafa
is run in a new session (e.g. bwrap
sandbox with the --new-session
option). This is because chafa
is unable to obtain the terminal cell size in pixels, which is necessary to calculate the pixel size of the Sixel image.
Note
tmux does not display Sixel images over 1MB by default but you can change the limit with INPUT_BUF_LIMIT
in input.c
.
stpv and ctpv are previewer utilities made for integrating into lf. No wrapper scripts are needed. You only need to add 4 lines in lf config to make either one of them work.
stpv is a POSIX shell script, while ctpv is a rewrite of stpv written in C. ctpv is faster and has a few additional features.
stpv:
set previewer stpv
set cleaner stpvimgclr
&stpvimg --listen $id
cmd on-quit $stpvimg --end $id
ctpv:
set previewer ctpv
set cleaner ctpvclear
&ctpv -s $id
&ctpvquit $id
Note
Previews using w3mimgdisplay are not persistent (scrolling, making selections, or moving the window will erase the image), since they are drawn directly on top of the terminal window. Unless you are a framebuffer user, it is not recommended to use w3mimgdisplay.
Make a file named draw_img.sh
in the lf
configuration folder with the contents below:
#!/bin/bash
clear_screen() {
printf '\e[%sH\e[9999C\e[1J%b\e[1;%sr' \
"$((LINES-2))" "${TMUX:+\e[2J}" "$max_items"
}
# Get a file's mime_type.
mime_type=$(file -bi "$1")
# File isn't an image file, give warning.
if [[ $mime_type != image/* ]]; then
lf -remote "send $id echoerr 'Not an image'"
exit
fi
w3m_paths=(/usr/{local/,}{lib,libexec,lib64,libexec64}/w3m/w3mi*)
read -r w3m _ < <(type -p w3mimgdisplay "${w3m_paths[@]}")
read -r LINES COLUMNS < <(stty size)
# Get terminal window size in pixels and set it to WIDTH and HEIGHT.
export $(xdotool getactivewindow getwindowgeometry --shell)
# Get the image size in pixels.
read -r img_width img_height < <("$w3m" <<< "5;${CACHE:-$1}")
((img_width > WIDTH)) && {
((img_height=img_height*WIDTH/img_width))
((img_width=WIDTH))
}
((img_height > HEIGHT)) && {
((img_width=img_width*HEIGHT/img_height))
((img_height=HEIGHT))
}
# Variable needed for centering image.
HALF_HEIGHT=$(expr $HEIGHT / 2)
HALF_WIDTH=$(expr $WIDTH / 2)
HALF_IMG_HEIGHT=$(expr $img_height / 2)
HALF_IMG_WIDTH=$(expr $img_width / 2)
X_POS=$(expr $HALF_WIDTH - $HALF_IMG_WIDTH)
Y_POS=$(expr $HALF_HEIGHT - $HALF_IMG_HEIGHT)
clear_screen
# Hide the cursor.
printf '\e[?25l'
# Display the image.
printf '0;1;%s;%s;%s;%s;;;;;%s\n3;\n4\n' \
${X_POS:-0} \
${Y_POS:-0} \
"$img_width" \
"$img_height" \
"${CACHE:-$1}" | "$w3m" &>/dev/null
# Wait for user input.
read -ern 1
# Clear the image.
printf '6;%s;%s;%s;%s\n3;' \
"${X_POS:-0}" \
"${Y_POS:-0}" \
"$WIDTH" \
"$HEIGHT" | "$w3m" &>/dev/null
clear_screen
Now add key mappings corresponding to the script.
map - $~/.config/lf/draw_img.sh "$f"
And likewise for video previews:
cmd video_preview ${{
cache="$(mktemp "${TMPDIR:-/tmp}/thumb_cache.XXXXX")"
ffmpegthumbnailer -i "$f" -o "$cache" -s 0
~/.config/lf/draw_img.sh "$cache"
}}
map + :video_preview
While the default lf configuration only previews text files, using more complex preview parsers is somewhat dangerous. In case there is a vulnerability in a preview parser like pdftotext, it is possible to use this simple script to sandbox the previewer:
#!/bin/bash
## ~/.config/lf/previewer_sandbox
set -euo pipefail
(
exec bwrap \
--ro-bind /usr/bin /usr/bin \
--ro-bind /usr/share/ /usr/share/ \
--ro-bind /usr/lib /usr/lib \
--ro-bind /usr/lib64 /usr/lib64 \
--symlink /usr/bin /bin \
--symlink /usr/bin /sbin \
--symlink /usr/lib /lib \
--symlink /usr/lib64 /lib64 \
--proc /proc \
--dev /dev \
--ro-bind /etc /etc \
--ro-bind ~/.config ~/.config \
--ro-bind ~/.cache ~/.cache \
--ro-bind "$PWD" "$PWD" \
--unshare-all \
--new-session \
bash ~/.config/lf/preview.sh "$@"
)
Set your previewer to the sandbox script and have your real preview script at ~/.config/lf/previewer
set previewer ~/.config/lf/previewer_sandbox