summaryrefslogtreecommitdiff
path: root/bin/lsix
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2020-10-15 22:23:27 +0200
committerMarc Vertes <mvertes@free.fr>2020-10-15 22:23:27 +0200
commit42f34848a96bf0f63acde79ebe79b073847c3cd9 (patch)
treee3fb2fae7f317f7637488f9660520966bae369b4 /bin/lsix
parentc2f239ff635d09d28a9536b821a877579729d2d0 (diff)
add sixel tools
Diffstat (limited to 'bin/lsix')
-rwxr-xr-xbin/lsix339
1 files changed, 339 insertions, 0 deletions
diff --git a/bin/lsix b/bin/lsix
new file mode 100755
index 0000000..0f96b36
--- /dev/null
+++ b/bin/lsix
@@ -0,0 +1,339 @@
+#!/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.6.2
+# B9 December 2018
+
+# 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.
+#fontfamily=Monaco
+fontfamily=MiscFixedSC613
+fontsize=$((tilewidth/10))
+fontsize=12 # (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[@]}"
+ 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
+
+# * Directories are not handled nicely.
+# * 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.