|
@@ -0,0 +1,349 @@ |
|
|
|
|
|
#!/usr/bin/env bash |
|
|
|
|
|
|
|
|
|
|
|
# lsix: like ls, but for images. |
|
|
|
|
|
# Shows thumbnails of images with titles directly in terminal. |
|
|
|
|
|
|
|
|
|
|
|
# Requirements: just ImageMagick (and a Sixel terminal, of course) |
|
|
|
|
|
|
|
|
|
|
|
# Version 1.7.1 |
|
|
|
|
|
# B9 April 2020 |
|
|
|
|
|
|
|
|
|
|
|
# See end of file for USAGE. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# The following defaults may be overridden if autodetection succeeds. |
|
|
|
|
|
numcolors=16 # Default number of colors in the palette. |
|
|
|
|
|
background=white # Default montage background. |
|
|
|
|
|
foreground=black # Default text color. |
|
|
|
|
|
width=800 # Default width of screen in pixels. |
|
|
|
|
|
|
|
|
|
|
|
# Feel free to edit these defaults to your liking. |
|
|
|
|
|
tilesize=120 # Width and height of each tile in the montage. |
|
|
|
|
|
tilewidth=$tilesize # (or specify separately, if you prefer) |
|
|
|
|
|
tileheight=$tilesize |
|
|
|
|
|
|
|
|
|
|
|
# If you get questionmarks for Unicode filenames, try using a different font. |
|
|
|
|
|
# You can list fonts available using `convert -list font`. |
|
|
|
|
|
#fontfamily=Droid-Sans-Fallback # Great Asian font coverage |
|
|
|
|
|
#fontfamily=Dejavu-Sans # Wide coverage, comes with GNU/Linux |
|
|
|
|
|
#fontfamily=Mincho # Wide coverage, comes with MS Windows |
|
|
|
|
|
|
|
|
|
|
|
# Default font size is based on width of each tile in montage. |
|
|
|
|
|
fontsize=$((tilewidth/10)) |
|
|
|
|
|
#fontsize=16 # (or set the point size directly, if you prefer) |
|
|
|
|
|
|
|
|
|
|
|
timeout=0.25 # How long to wait for terminal to respond |
|
|
|
|
|
# to a control sequence (in seconds). |
|
|
|
|
|
|
|
|
|
|
|
# Sanity checks and compatibility |
|
|
|
|
|
if [[ ${BASH_VERSINFO[0]} -eq 3 ]]; then |
|
|
|
|
|
if bash --version | head -1 | grep -q "version 3"; then |
|
|
|
|
|
cat <<-EOF >&2 |
|
|
|
|
|
Error: The version of Bash is extremely out of date. |
|
|
|
|
|
(2007, the same year Steve Jobs announced the iPhone!) |
|
|
|
|
|
|
|
|
|
|
|
This is almost always due to Apple's MacOS being silly. |
|
|
|
|
|
Please let Apple know that their users expect current UNIX tools. |
|
|
|
|
|
|
|
|
|
|
|
In the meantime, try using "brew install bash". |
|
|
|
|
|
EOF |
|
|
|
|
|
exit 1 |
|
|
|
|
|
else |
|
|
|
|
|
exec bash "$0" "$@" || echo "Exec failed" >&2 |
|
|
|
|
|
exit 1 |
|
|
|
|
|
fi |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
if ! command -v montage >/dev/null; then |
|
|
|
|
|
echo "Please install ImageMagick" >&2 |
|
|
|
|
|
exit 1 |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
if command -v gsed >/dev/null; then |
|
|
|
|
|
alias sed=gsed # Use GNU sed for MacOS & BSD |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
cleanup() { |
|
|
|
|
|
echo -n $'\e\\' # Escape sequence to stop SIXEL. |
|
|
|
|
|
stty echo # Reset terminal to show characters. |
|
|
|
|
|
exit 0 |
|
|
|
|
|
} |
|
|
|
|
|
trap cleanup SIGINT SIGHUP SIGABRT EXIT |
|
|
|
|
|
|
|
|
|
|
|
autodetect() { |
|
|
|
|
|
# Various terminal automatic configuration routines. |
|
|
|
|
|
|
|
|
|
|
|
# Don't show escape sequences the terminal doesn't understand. |
|
|
|
|
|
stty -echo # Hush-a Mandara Ni Pari |
|
|
|
|
|
|
|
|
|
|
|
# IS TERMINAL SIXEL CAPABLE? # Send Device Attributes |
|
|
|
|
|
IFS=";" read -a REPLY -s -t 1 -d "c" -p $'\e[c' >&2 |
|
|
|
|
|
for code in "${REPLY[@]}"; do |
|
|
|
|
|
if [[ $code == "4" ]]; then |
|
|
|
|
|
hassixel=yup |
|
|
|
|
|
break |
|
|
|
|
|
fi |
|
|
|
|
|
done |
|
|
|
|
|
|
|
|
|
|
|
# YAFT is vt102 compatible, cannot respond to vt220 escape sequence. |
|
|
|
|
|
if [[ "$TERM" == yaft* ]]; then hassixel=yeah; fi |
|
|
|
|
|
|
|
|
|
|
|
if [[ -z "$hassixel" ]]; then |
|
|
|
|
|
cat <<-EOF >&2 |
|
|
|
|
|
Error: Your terminal does not report having sixel graphics support. |
|
|
|
|
|
|
|
|
|
|
|
Please use a sixel capable terminal, such as xterm -ti vt340, or |
|
|
|
|
|
ask your terminal manufacturer to add sixel support. |
|
|
|
|
|
|
|
|
|
|
|
You may test your terminal by viewing a single image, like so: |
|
|
|
|
|
|
|
|
|
|
|
convert foo.jpg -geometry 800x480 sixel:- |
|
|
|
|
|
|
|
|
|
|
|
If your terminal actually does support sixel, please file a bug |
|
|
|
|
|
report at http://github.com/hackerb9/lsix/issues |
|
|
|
|
|
EOF |
|
|
|
|
|
read -s -t 1 -d "c" -p $'\e[c' >&2 |
|
|
|
|
|
if [[ "$REPLY" ]]; then |
|
|
|
|
|
echo |
|
|
|
|
|
cat -v <<< "(Please mention device attribute codes: ${REPLY}c)" |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
exit 1 |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# ENABLE SIXEL SCROLLING so image will appear right after cursor. |
|
|
|
|
|
if [[ $TERM != "mlterm" ]]; then |
|
|
|
|
|
echo -ne $'\e[?80h' |
|
|
|
|
|
else |
|
|
|
|
|
# Except... mlterm (as of 3.5.0) has a bug that reverses the sense |
|
|
|
|
|
echo -ne $'\e[?80l' |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# TERMINAL COLOR AUTODETECTION. |
|
|
|
|
|
# Find out how many color registers the terminal has |
|
|
|
|
|
IFS=";" read -a REPLY -s -t ${timeout} -d "S" -p $'\e[?1;1;0S' >&2 |
|
|
|
|
|
[[ ${REPLY[1]} == "0" ]] && numcolors=${REPLY[2]} |
|
|
|
|
|
|
|
|
|
|
|
# Bug workaround: mlterm does not report number of colors. |
|
|
|
|
|
if [[ $TERM =~ mlterm ]]; then numcolors=1024; fi |
|
|
|
|
|
# YAFT is vt102 compatible, cannot respond to vt220 escape sequence. |
|
|
|
|
|
if [[ "$TERM" == yaft* ]]; then numcolors=256; fi |
|
|
|
|
|
|
|
|
|
|
|
# Increase colors, if needed |
|
|
|
|
|
if [[ $numcolors -lt 256 ]]; then |
|
|
|
|
|
# Attempt to set the number of colors to 256. |
|
|
|
|
|
# This will work for xterm, but fail on a real vt340. |
|
|
|
|
|
IFS=";" read -a REPLY -s -t ${timeout} -d "S" -p $'\e[?1;3;256S' >&2 |
|
|
|
|
|
[[ ${REPLY[1]} == "0" ]] && numcolors=${REPLY[2]} |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# Query the terminal background and foreground colors. |
|
|
|
|
|
IFS=";:/" read -a REPLY -r -s -t ${timeout} -d "\\" -p $'\e]11;?\e\\' >&2 |
|
|
|
|
|
if [[ ${REPLY[1]} =~ ^rgb ]]; then |
|
|
|
|
|
# Return value format: $'\e]11;rgb:ffff/0000/ffff\e\\'. |
|
|
|
|
|
# ImageMagick wants colors formatted as #ffff0000ffff. |
|
|
|
|
|
background='#'${REPLY[2]}${REPLY[3]}${REPLY[4]%%$'\e'*} |
|
|
|
|
|
IFS=";:/" read -a REPLY -r -s -t ${timeout} -d "\\" -p $'\e]10;?\e\\' >&2 |
|
|
|
|
|
if [[ ${REPLY[1]} =~ ^rgb ]]; then |
|
|
|
|
|
foreground='#'${REPLY[2]}${REPLY[3]}${REPLY[4]%%$'\e'*} |
|
|
|
|
|
# Check for "Reverse Video" (DECSCNM screen mode). |
|
|
|
|
|
IFS=";?$" read -a REPLY -s -t ${timeout} -d "y" -p $'\e[?5$p' |
|
|
|
|
|
if [[ ${REPLY[2]} == 1 || ${REPLY[2]} == 3 ]]; then |
|
|
|
|
|
temp=$foreground |
|
|
|
|
|
foreground=$background |
|
|
|
|
|
background=$temp |
|
|
|
|
|
fi |
|
|
|
|
|
fi |
|
|
|
|
|
fi |
|
|
|
|
|
# YAFT is vt102 compatible, cannot respond to vt220 escape sequence. |
|
|
|
|
|
if [[ "$TERM" == yaft* ]]; then background=black; foreground=white; fi |
|
|
|
|
|
|
|
|
|
|
|
# Send control sequence to query the sixel graphics geometry to |
|
|
|
|
|
# find out how large of a sixel image can be shown. |
|
|
|
|
|
IFS=";" read -a REPLY -s -t ${timeout} -d "S" -p $'\e[?2;1;0S' >&2 |
|
|
|
|
|
if [[ ${REPLY[2]} -gt 0 ]]; then |
|
|
|
|
|
width=${REPLY[2]} |
|
|
|
|
|
else |
|
|
|
|
|
# Nope. Fall back to dtterm WindowOps to approximate sixel geometry. |
|
|
|
|
|
IFS=";" read -a REPLY -s -t ${timeout} -d "t" -p $'\e[14t' >&2 |
|
|
|
|
|
if [[ $? == 0 && ${REPLY[2]} -gt 0 ]]; then |
|
|
|
|
|
width=${REPLY[2]} |
|
|
|
|
|
fi |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# BUG WORKAROUND: XTerm cannot show images wider than 1000px. |
|
|
|
|
|
# Remove this hack once XTerm gets fixed. Last checked: XTerm(327) |
|
|
|
|
|
if [[ $TERM =~ xterm && $width -ge 1000 ]]; then width=1000; fi |
|
|
|
|
|
|
|
|
|
|
|
# Space on either side of each tile is less than 0.5% of total screen width |
|
|
|
|
|
tilexspace=$((width/201)) |
|
|
|
|
|
tileyspace=$((tilexspace/2)) |
|
|
|
|
|
# Figure out how many tiles we can fit per row. ("+ 1" is for -shadow). |
|
|
|
|
|
numtiles=$((width/(tilewidth + 2*tilexspace + 1))) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
main() { |
|
|
|
|
|
# Discover and setup the terminal |
|
|
|
|
|
autodetect |
|
|
|
|
|
|
|
|
|
|
|
if [[ $# == 0 ]]; then |
|
|
|
|
|
# No command line args? Use a sorted list of image files in CWD. |
|
|
|
|
|
shopt -s nullglob nocaseglob nocasematch |
|
|
|
|
|
set -- *{jpg,jpeg,png,gif,tiff,tif,p?m,x[pb]m,bmp,ico,svg,eps} |
|
|
|
|
|
[[ $# != 0 ]] || exit |
|
|
|
|
|
readarray -t < <(printf "%s\n" "$@" | sort) |
|
|
|
|
|
|
|
|
|
|
|
# Only show first frame of animated GIFs if filename not specified. |
|
|
|
|
|
for x in ${!MAPFILE[@]}; do |
|
|
|
|
|
if [[ ${MAPFILE[$x]} =~ gif$ ]]; then |
|
|
|
|
|
MAPFILE[$x]=":${MAPFILE[$x]}[0]" |
|
|
|
|
|
fi |
|
|
|
|
|
done |
|
|
|
|
|
set -- "${MAPFILE[@]}" |
|
|
|
|
|
else |
|
|
|
|
|
# Command line args specified. Check for directories. |
|
|
|
|
|
lsix=$(realpath "$0") |
|
|
|
|
|
for arg; do |
|
|
|
|
|
if [ -d "$arg" ]; then |
|
|
|
|
|
echo Recursing on $arg |
|
|
|
|
|
(cd "$arg"; $lsix) |
|
|
|
|
|
else |
|
|
|
|
|
nodirs+=($arg) |
|
|
|
|
|
fi |
|
|
|
|
|
done |
|
|
|
|
|
set -- "${nodirs[@]}" |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Resize on load: Save memory by appending this suffix to every filename. |
|
|
|
|
|
resize="[${tilewidth}x${tileheight}]" |
|
|
|
|
|
|
|
|
|
|
|
imoptions="-tile ${numtiles}x1" # Each montage is 1 row x $numtiles columns |
|
|
|
|
|
imoptions+=" -geometry ${tilewidth}x${tileheight}>+${tilexspace}+${tileyspace}" # Size of each tile and spacing |
|
|
|
|
|
imoptions+=" -background $background -fill $foreground" # Use terminal's colors |
|
|
|
|
|
imoptions+=" -auto-orient " # Properly rotate JPEGs from cameras |
|
|
|
|
|
if [[ $numcolors -gt 16 ]]; then |
|
|
|
|
|
imoptions+=" -shadow " # Just for fun :-) |
|
|
|
|
|
fi |
|
|
|
|
|
|
|
|
|
|
|
# See top of this file to change fontfamily and fontsize. |
|
|
|
|
|
[[ "$fontfamily" ]] && imoptions+=" -font $fontfamily " |
|
|
|
|
|
[[ "$fontsize" ]] && imoptions+=" -pointsize $fontsize " |
|
|
|
|
|
|
|
|
|
|
|
# Create and display montages one row at a time. |
|
|
|
|
|
while [ $# -gt 0 ]; do |
|
|
|
|
|
# While we still have images to process... |
|
|
|
|
|
onerow=() |
|
|
|
|
|
goal=$(($# - numtiles)) # How many tiles left after this row |
|
|
|
|
|
while [ $# -gt 0 -a $# -gt $goal ]; do |
|
|
|
|
|
len=${#onerow[@]} |
|
|
|
|
|
onerow[len++]="-label" |
|
|
|
|
|
onerow[len++]=$(processlabel "$1") |
|
|
|
|
|
onerow[len++]="$1" |
|
|
|
|
|
shift |
|
|
|
|
|
done |
|
|
|
|
|
montage "${onerow[@]}" $imoptions gif:- \ |
|
|
|
|
|
| convert - -colors $numcolors sixel:- |
|
|
|
|
|
done |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
processlabel() { |
|
|
|
|
|
# This routine is all about appeasing ImageMagick. |
|
|
|
|
|
# 1. Remove silly [0] suffix and : prefix. |
|
|
|
|
|
# 2. Quote percent backslash, and at sign. |
|
|
|
|
|
# 3. Replace control characters with question marks. |
|
|
|
|
|
# 4. If a filename is too long, remove extension (.jpg). |
|
|
|
|
|
# 5. Split long filenames with newlines (recursively) |
|
|
|
|
|
span=15 # filenames longer than span will be split |
|
|
|
|
|
echo -n "$1" | |
|
|
|
|
|
sed 's|^:||; s|\[0]$||;' | tr '[:cntrl:]' '?' | |
|
|
|
|
|
awk -v span=$span -v ORS="" ' |
|
|
|
|
|
function halve(s, l,h) { # l and h are locals |
|
|
|
|
|
l=length(s); h=int(l/2); |
|
|
|
|
|
if (l <= span) { return s; } |
|
|
|
|
|
return halve(substr(s, 1, h)) "\n" halve(substr(s, h+1)); |
|
|
|
|
|
} |
|
|
|
|
|
{ |
|
|
|
|
|
if ( length($0) > span ) gsub(/\..?.?.?.?$/, ""); |
|
|
|
|
|
print halve($0); |
|
|
|
|
|
} |
|
|
|
|
|
' | |
|
|
|
|
|
sed 's|%|%%|g; s|\\|\\\\|g; s|@|\\@|g;' |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#### |
|
|
|
|
|
|
|
|
|
|
|
main "$@" |
|
|
|
|
|
|
|
|
|
|
|
# Send an escape sequence and wait for a response from the terminal |
|
|
|
|
|
# so that the program won't quit until images have finished transferring. |
|
|
|
|
|
read -s -t 60 -d "c" -p $'\e[c' >&2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
###################################################################### |
|
|
|
|
|
# NOTES: |
|
|
|
|
|
|
|
|
|
|
|
# Usage: lsix [ FILES ... ] |
|
|
|
|
|
|
|
|
|
|
|
# * FILES can be any image file that ImageMagick can handle. |
|
|
|
|
|
# |
|
|
|
|
|
# * If no FILES are specified the most common file extensions are tried. |
|
|
|
|
|
# (For now, lsix only searches the current working directory.) |
|
|
|
|
|
# |
|
|
|
|
|
# * Non-bitmap graphics often work fine (.svg, .eps, .pdf, .xcf). |
|
|
|
|
|
# |
|
|
|
|
|
# * Files containing multiple images (e.g., animated GIFs) will show |
|
|
|
|
|
# all the images if the filename is specified at the command line. |
|
|
|
|
|
# Only the first frame will be shown if "lsix" is called with no |
|
|
|
|
|
# arguments. |
|
|
|
|
|
# |
|
|
|
|
|
# * Because this uses escape sequences, it works seamlessly through ssh. |
|
|
|
|
|
# |
|
|
|
|
|
# * If your terminal supports reporting the background and foreground |
|
|
|
|
|
# color, lsix will use those for the montage background and text fill. |
|
|
|
|
|
# |
|
|
|
|
|
# * If your terminal supports changing the number of color registers |
|
|
|
|
|
# to improve the picture quality, lsix will do so. |
|
|
|
|
|
|
|
|
|
|
|
# * Only software needed is ImageMagick (e.g., apt-get install imagemagick). |
|
|
|
|
|
|
|
|
|
|
|
# Your terminal must support SIXEL graphics. E.g., |
|
|
|
|
|
# |
|
|
|
|
|
# xterm -ti vt340 |
|
|
|
|
|
|
|
|
|
|
|
# * To make vt340 be the default xterm type, set this in .Xresources: |
|
|
|
|
|
# |
|
|
|
|
|
# ! Allow sixel graphics. (Try: "convert -colors 16 foo.jpg sixel:-"). |
|
|
|
|
|
# xterm*decTerminalID : vt340 |
|
|
|
|
|
|
|
|
|
|
|
# * Xterm does not support reporting the screen size in pixels unless |
|
|
|
|
|
# you add this to your .Xresources: |
|
|
|
|
|
# |
|
|
|
|
|
# ! Allow xterm to read the terminal window size (op #14) |
|
|
|
|
|
# xterm*allowWindowOps : False |
|
|
|
|
|
# xterm*disallowedWindowOps : 1,2,3,4,5,6,7,8,9,11,13,18,19,20,21,GetSelection,SetSelection,SetWinLines,SetXprop |
|
|
|
|
|
|
|
|
|
|
|
# * Be cautious using lsix on videos (lsix *.avi) as ImageMagick will |
|
|
|
|
|
# try to make a montage of every single frame and likely exhaust |
|
|
|
|
|
# your memory and/or your patience. |
|
|
|
|
|
|
|
|
|
|
|
# BUGS |
|
|
|
|
|
|
|
|
|
|
|
# * Sort order is a bit strange. |
|
|
|
|
|
# * ImageMagick's Montage doesn't handle long filenames nicely. |
|
|
|
|
|
# * Some transparent images (many .eps files) presume a white background |
|
|
|
|
|
# and will not show up if your terminal's background is black. |
|
|
|
|
|
# * This file is getting awfully long for a one line kludge. :-) |
|
|
|
|
|
|
|
|
|
|
|
# LICENSE INFORMATION |
|
|
|
|
|
# (AKA, You know your kludge has gotten out of hand when...) |
|
|
|
|
|
|
|
|
|
|
|
# Dual license: |
|
|
|
|
|
# * You have all the freedoms permitted to you under the |
|
|
|
|
|
# GNU GPL >=3. (See the included LICENSE file). |
|
|
|
|
|
|
|
|
|
|
|
# * Additionally, this program can be used under the terms of whatever |
|
|
|
|
|
# license 'xterm' is using (now or in the future). This is primarily |
|
|
|
|
|
# so that, if the xterm maintainer (currently Thomas E. Dickey) so |
|
|
|
|
|
# wishes, this program may be included with xterm as a Sixel test. |
|
|
|
|
|
# However, anyone who wishes to take advantage of this is free to do so. |