# -*- mode: sh; sh-shell: bash -*-
# vim: set ft=bash:
# shellcheck shell=bash
### Module for terminal control codes
###
### These codes are stored in the 'TTYCTL' associative array. The 'TTYNIL' mirrors the keys
### from 'TTYCTL' but maps them to empty strings. Finally 'TTYOUT' and 'TTYERR' refer to
### 'TTYCTL' when the associated output stream is a non dumb terminals and refer to 'TTYNIL'
### when it is not a terminal or a dumb terminal.
###
### The keys in 'TTYCTL/TTYNIL' are as following.
###
### Two character control sequences:
### 'ce' - Clears from cursor to the end of the line.
### 'cl' - Puts the cursor in column 1 and clears the line.
### 'cs' - Clears the entire screen.
### 'cr' - Puts the cursor in column 1.
###
### Three character styles:
### The first two characters set the foreground and background color:
### '_' - no change.
### 'k' - black
### 'r' - red.
### 'g' - green
### 'b' - blue.
### 'y' - yellow.
### 'm' - magenta.
### 'c' - cyan.
### 'K' - bright black
### 'R' - bright red.
### 'G' - bright green
### 'B' - bright blue.
### 'Y' - bright yellow.
### 'M' - bright magenta.
### 'C' - bright cyan.
### The third character set the style uppercase enables, lowercase disables:
### '_' - no change.
### 'B' - bold.
### 'D' - dim.
### 'U' - underline.
### 'I' - italic.
### 'R' - reverse.
### 'S' - strike-through.
### 'n' - normal (has no uppercase).
###
### Finally there is a the one character 'n' code that resets the style completely.
###
### For an example to see how this is used look at the 'std_lib' module.
# compute lookup table for styles once
tty_init_maps() {
[[ -v TTYCTL ]] && return 0
# fg / bg / style maps
declare -A fg_colors=(
[k]=30 [r]=31 [g]=32 [y]=33 [b]=34 [m]=35 [c]=36 [w]=37
[K]=90 [R]=91 [G]=92 [Y]=93 [B]=94 [M]=95 [C]=96 [W]=97
[_]=''
)
declare -A bg_colors=(
[k]=40 [r]=41 [g]=42 [y]=43 [b]=44 [m]=45 [c]=46 [w]=47
[K]=100 [R]=101 [G]=102 [Y]=103 [B]=104 [M]=105 [C]=106 [W]=107
[_]=''
)
declare -A styles=(
[B]=1 [D]=2 [U]=4 [I]=3 [R]=7 [S]=9
[b]=22 [d]=22 [u]=24 [i]=23 [r]=27 [s]=29
[n]=0 [_]=''
)
# build maps
declare -gxA TTYCTL=() TTYNIL=()
for fg in "${!fg_colors[@]}"; do
for bg in "${!bg_colors[@]}"; do
[[ $fg == "$bg" && $fg != _ ]] && continue
for st in "${!styles[@]}"; do
code=()
[[ -n ${fg_colors[$fg]} ]] && code+=("${fg_colors[$fg]}")
[[ -n ${bg_colors[$bg]} ]] && code+=("${bg_colors[$bg]}")
[[ -n ${styles[$st]} ]] && code+=("${styles[$st]}")
key="${fg}${bg}${st}"
if ((${#code[@]})); then
local IFS=";"
val=$'\033['"${code[*]}"'m'
else
val=''
fi
TTYCTL[$key]="$val"
TTYNIL[$key]=''
done
done
done
# specials
TTYCTL[ce]=$'\033[K' TTYNIL[ce]=''
TTYCTL[cl]=$'\r\033[K' TTYNIL[cl]=''
TTYCTL[cs]=$'\033[2J\033[H' TTYNIL[cs]=''
TTYCTL[cr]=$'\r' TTYNIL[cr]='\r'
TTYCTL[n]=$'\033[0m' TTYNIL[n]=''
# assign TTYOUT/TTYERR
unset TTYOUT TTYERR
[[ "${TERM:-dumb}" = "dumb" ]] && COLOR=never
case "${COLOR:-auto}" in
always) declare -gn TTYOUT=TTYCTL; declare -gn TTYERR=TTYCTL ;;
never) declare -gn TTYOUT=TTYNIL; declare -gn TTYERR=TTYNIL ;;
auto|*)
# shellcheck disable=2015
[[ -t 1 ]] && declare -gn TTYOUT=TTYCTL || declare -gn TTYOUT=TTYNIL
# shellcheck disable=2015
[[ -t 2 ]] && declare -gn TTYERR=TTYCTL || declare -gn TTYERR=TTYNIL
;;
esac
# Mark the arrays readonly
declare -gxrA TTYCTL TTYNIL
declare -gxrn TTYOUT TTYERR
}
tty_init_maps
unset -f tty_init_maps
function tty_echo ## [echo_args] - Echos only when the output is a tty.
{
[[ -t 1 ]] && echo "$@"
}
[[ "${TERM:-dumb}" = "dumb" ]] || function tty_newline ## Outputs a newline when we are not at column 1 and on a tty.
{
if [[ -t 1 ]]; then
local col
IFS=';' read -srdR -p $'\033[6n' _ col || true
col="${col%R}"
[[ "$col" != 1 ]] && echo
fi
return 0
}