472 lines
10 KiB
Bash
472 lines
10 KiB
Bash
#!/bin/sh
|
|
#simple curses library to create windows on terminal
|
|
#
|
|
#ShellCurses patch by Nomagno
|
|
#
|
|
#original author: Patrice Ferlet metal3d@copix.org
|
|
#
|
|
#create_buffer patch by Laurent Bachelier
|
|
#support for bgcolors by Markus Mikkolainen
|
|
#
|
|
##license: new BSD
|
|
#
|
|
|
|
|
|
version() {
|
|
printf 'Version 1.0\n'
|
|
}
|
|
usage() {
|
|
|
|
printf 'USAGE AFTER SOURCING\nwindow TITLE COLOR [WIDTH] | create a window with title, color and width
|
|
append TEXT [CENTERING] [COLOR] [BGCOLOR] | append text to the window
|
|
addsep | add separator
|
|
main_loop | initialization\n
|
|
Please see the included tictactoe example for more information and auxiliary commands.\n'
|
|
}
|
|
|
|
if [ "$1" = "-v" ] || [ "$1" = "--version" ]; then
|
|
version
|
|
exit
|
|
elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
|
usage
|
|
exit
|
|
fi
|
|
|
|
VERBOSE=0
|
|
BSC_MODE="dashboard"
|
|
|
|
export captured_input=$(mktemp)
|
|
|
|
bsc_create_buffer(){
|
|
# Try to use SHM, then $TMPDIR, then /tmp
|
|
if [ -d "/dev/shm" ]; then
|
|
BUFFER_DIR="/dev/shm"
|
|
elif [ -n "$TMPDIR" ]; then
|
|
BUFFER_DIR="$TMPDIR"
|
|
else
|
|
BUFFER_DIR="/tmp"
|
|
fi
|
|
|
|
_buffername=""
|
|
[ "$1" != "" ] && _buffername=$1 || _buffername="bashsimplecurses"
|
|
|
|
# Try to use mktemp before using the unsafe method
|
|
echo "$(mktemp)"
|
|
}
|
|
|
|
#Usefull variables
|
|
BSC_BUFFER=$(bsc_create_buffer)
|
|
BSC_STDERR=$(bsc_create_buffer stderr)
|
|
|
|
|
|
reset_layout() {
|
|
BSC_COLLFT=0
|
|
BSC_COLWIDTH=0
|
|
BSC_COLWIDTH_MAX=0
|
|
BSC_WLFT=0
|
|
# Height are not dynamically updated
|
|
# Only at window and endwin call
|
|
# Height of the current window
|
|
BSC_WNDHGT=0
|
|
# Height of the bottom of the current window
|
|
BSC_COLHGT=0
|
|
# Heigh of the bottom of the current column
|
|
BSC_COLBOT=0
|
|
# Height of the maximum bottom ever
|
|
BSC_COLHGT_MAX=0
|
|
# Flags to code the lib user window placement request
|
|
BSC_NEWWIN_TOP_REQ=0
|
|
BSC_NEWWIN_RGT_REQ=0
|
|
}
|
|
|
|
clean_env(){
|
|
rm -rf "$BSC_BUFFER"
|
|
reset_colors
|
|
tput cnorm
|
|
tput cvvis
|
|
setterm -cursor on
|
|
}
|
|
#call on SIGINT and SIGKILL
|
|
#it removes buffer before to stop
|
|
bsc_on_kill(){
|
|
clean_env
|
|
exit 15
|
|
}
|
|
|
|
BSC_SIGINT=0
|
|
bsc_flag_sigint()
|
|
{
|
|
# Defer sigint processing because otherwise commands are pushed into BSC_BUFFER due to redirect in main_loop, which is deleted in clean_env ...
|
|
# This does not seem to be problematic with SIGKILL
|
|
# lets admit it this handling of SIGINT is tedious
|
|
BSC_SIGINT=1
|
|
}
|
|
trap bsc_on_kill TERM
|
|
trap bsc_flag_sigint INT
|
|
|
|
#initialize terminal
|
|
bsc_term_init(){
|
|
if [ "$BSC_MODE" = dashboard ]; then
|
|
tput clear
|
|
fi
|
|
# tput civis
|
|
}
|
|
|
|
|
|
#change line
|
|
bsc__nl(){
|
|
BSC_WNDHGT=$((BSC_WNDHGT+1))
|
|
tput cud1
|
|
tput cub "$(expr $(tput cols) - 3)"
|
|
[ $BSC_WLFT -gt 0 ] && tput cuf $BSC_WLFT
|
|
tput sc
|
|
}
|
|
|
|
|
|
move_up(){
|
|
BSC_NEWWIN_TOP_REQ=1
|
|
}
|
|
|
|
col_right(){
|
|
BSC_NEWWIN_RGT_REQ=1
|
|
}
|
|
|
|
#initialize chars to use
|
|
|
|
|
|
bsc_init_chars() {
|
|
_TL="+"
|
|
_TR="+"
|
|
_BL="+"
|
|
_BR="+"
|
|
_SEPL="+"
|
|
_SEPR="+"
|
|
_VLINE="|"
|
|
_HLINE="-"
|
|
SPINNER='-'
|
|
_DIAMOND="*"
|
|
_BLOCK="#"
|
|
}
|
|
|
|
backtotoprow () {
|
|
_travelback=""
|
|
_travelback=$1
|
|
|
|
# Testing if layout would require non destructive scrolling
|
|
nbrows=$(tput lines)
|
|
scrollback=$(( _travelback -nbrows ))
|
|
if [ $scrollback -gt 0 ]; then
|
|
# tput rin $scrollback
|
|
# _travelback=$(( _travelback - scrollback ))
|
|
echo "Warning: Current layout is exceeding terminal size. This will break window top alignment. Increase terminal height/reduce window content for proper rendering." >&2
|
|
fi
|
|
[ "$_travelback" -gt 0 ] && tput cuu "$_travelback"
|
|
}
|
|
|
|
#Append a window
|
|
window() {
|
|
_color=""
|
|
_bgcolor=""
|
|
_title=$1
|
|
_color=$2
|
|
_bgcolor=$4
|
|
|
|
[ "$VERBOSE" -eq 2 ] && echo "Begin of window $_title" >&2
|
|
|
|
# Manage new window position
|
|
case "$BSC_NEWWIN_TOP_REQ$BSC_NEWWIN_RGT_REQ" in
|
|
"00" )
|
|
# Window is requested to be displayed under the previous one
|
|
;;
|
|
"01" )
|
|
# Window is requested to be displayed to the right of the last one
|
|
|
|
BSC_WLFT=$(( BSC_WLFT + BSC_COLWIDTH ))
|
|
[ $BSC_WLFT -gt 0 ] && tput cuf $(( BSC_WLFT + BSC_COLWIDTH ))
|
|
backtotoprow $BSC_WNDHGT
|
|
BSC_COLHGT=$(( BSC_COLHGT - BSC_WNDHGT))
|
|
;;
|
|
"10" )
|
|
# Window is requested to be displayed overwriting the ones above (??!??)
|
|
# Instead, we reset the layout, enabling more possibilities
|
|
tput cud $(( BSC_COLHGT_MAX - BSC_COLBOT ))
|
|
reset_layout
|
|
;;
|
|
"11" )
|
|
# Window is requested to be displayed in a new column starting from top
|
|
backtotoprow $BSC_COLHGT
|
|
|
|
BSC_COLLFT=$(( BSC_COLLFT + BSC_COLWIDTH_MAX ))
|
|
BSC_WLFT=$BSC_COLLFT
|
|
|
|
BSC_COLHGT=0
|
|
BSC_COLBOT=0
|
|
BSC_COLWIDTH_MAX=0
|
|
;;
|
|
* )
|
|
echo "Unexpected window position requirement"
|
|
clean_env
|
|
exit 1
|
|
esac
|
|
|
|
# Reset window position mechanism for next window
|
|
BSC_NEWWIN_TOP_REQ=0
|
|
BSC_NEWWIN_RGT_REQ=0
|
|
BSC_WNDHGT=0
|
|
|
|
bsc_cols=$(expr $(expr $(tput cols) - 3) - 3)
|
|
case $3 in
|
|
"" )
|
|
# No witdh given
|
|
;;
|
|
*% )
|
|
w=$(echo "$3" | sed 's/%//')
|
|
bsc_cols=$((w*bsc_cols/100))
|
|
;;
|
|
* )
|
|
bsc_cols=$3
|
|
;;
|
|
esac
|
|
|
|
if [ "$bsc_cols" -lt 3 ]; then
|
|
echo "Column width of window \"$_title\" is too narrow to render (sz=$bsc_cols)." >&2
|
|
exit 1;
|
|
fi
|
|
|
|
BSC_COLWIDTH=$bsc_cols
|
|
[ "$BSC_COLWIDTH" -gt $BSC_COLWIDTH_MAX ] && BSC_COLWIDTH_MAX=$BSC_COLWIDTH
|
|
|
|
# Create an empty line for this window
|
|
BSC_BLANKLINE=$(head -c "$BSC_COLWIDTH" /dev/zero | tr '\0' ' ')
|
|
BSC_LINEBODY=${BSC_BLANKLINE}
|
|
contentLen=${#BSC_LINEBODY}
|
|
BSC_LINEBODY=$(echo "$BSC_LINEBODY" | sed "s/ /$_HLINE/g")
|
|
|
|
_len=""
|
|
_len=${#_title}
|
|
|
|
|
|
bsc_left=$(( (bsc_cols - _len)/2 -1 ))
|
|
|
|
# Init top left window corner
|
|
tput cub "$(expr $(tput cols) - 3)"
|
|
[ $BSC_WLFT -gt 0 ] && tput cuf $BSC_WLFT
|
|
tput sc
|
|
|
|
#draw upper line
|
|
printf "$_TL$BSC_LINEBODY$_TR"
|
|
|
|
#next line, draw title
|
|
bsc__nl
|
|
append "$_title" center "$_color" "$_bgcolor"
|
|
|
|
#then draw bottom line for title
|
|
addsep
|
|
}
|
|
|
|
reset_colors(){
|
|
printf "\033[00m"
|
|
}
|
|
setcolor(){
|
|
_color=""
|
|
_color=$1
|
|
case $_color in
|
|
grey|gray)
|
|
printf "\033[01;30m"
|
|
;;
|
|
red)
|
|
printf "\033[01;31m"
|
|
;;
|
|
green)
|
|
printf "\033[01;32m"
|
|
;;
|
|
yellow)
|
|
printf "\033[01;33m"
|
|
;;
|
|
blue)
|
|
printf "\033[01;34m"
|
|
;;
|
|
magenta)
|
|
printf "\033[01;35m"
|
|
;;
|
|
cyan)
|
|
printf "\033[01;36m"
|
|
;;
|
|
white)
|
|
printf "\033[01;37m"
|
|
;;
|
|
*) #default should be 39 maybe?
|
|
printf "\033[01;39m"
|
|
;;
|
|
esac
|
|
}
|
|
setbgcolor(){
|
|
_2bgcolor=""
|
|
_2bgcolor=$1
|
|
case $_2bgcolor in
|
|
grey|gray)
|
|
printf "\033[01;40m"
|
|
;;
|
|
red)
|
|
printf "\033[01;41m"
|
|
;;
|
|
green)
|
|
printf "\033[01;42m"
|
|
;;
|
|
yellow)
|
|
printf "\033[01;43m"
|
|
;;
|
|
blue)
|
|
printf "\033[01;44m"
|
|
;;
|
|
magenta)
|
|
printf "\033[01;45m"
|
|
;;
|
|
cyan)
|
|
printf "\033[01;46m"
|
|
;;
|
|
white)
|
|
printf "\033[01;47m"
|
|
;;
|
|
black)
|
|
printf "\033[01;49m"
|
|
;;
|
|
*) #default should be 49
|
|
printf "\033[01;49m"
|
|
;;
|
|
esac
|
|
|
|
}
|
|
|
|
#append a separator, new line
|
|
addsep (){
|
|
clean_line
|
|
printf "%s" "$BSC_LINEBODY"
|
|
bsc__nl
|
|
}
|
|
|
|
#clean the current line
|
|
clean_line(){
|
|
#set default color
|
|
reset_colors
|
|
|
|
tput sc
|
|
printf "$BSC_BLANKLINE"
|
|
#tput el
|
|
tput rc
|
|
}
|
|
|
|
append(){
|
|
tmp8=$(mktemp)
|
|
printf "%s\n" "$1" | fold -w $((BSC_COLWIDTH-2)) -s > $tmp8
|
|
while read -r line; do
|
|
bsc__append "$line" "$2" "$3" "$4"
|
|
done < $tmp8
|
|
}
|
|
|
|
#
|
|
# bsc__append <text> [centering] [color] [bgcolor]
|
|
#
|
|
bsc__append(){
|
|
clean_line
|
|
tput sc
|
|
printf "$_VLINE"
|
|
_5len=""
|
|
tmp10=$(mktemp)
|
|
printf "$1" > "$tmp10"
|
|
_5len=$(wc -c < $tmp10)
|
|
bsc_left=$(( (BSC_COLWIDTH - _5len)/2 - 1 ))
|
|
|
|
[ "$2" = "left" ] && bsc_left=0
|
|
|
|
[ $bsc_left -gt 0 ] && tput cuf $bsc_left
|
|
setcolor "$3"
|
|
setbgcolor "$4"
|
|
printf "$1"
|
|
reset_colors
|
|
tput rc
|
|
tput cuf $((BSC_COLWIDTH-1))
|
|
printf "$_VLINE"
|
|
bsc__nl
|
|
}
|
|
|
|
|
|
#close the window display
|
|
endwin(){
|
|
# Plot bottom line
|
|
printf "$_BL$BSC_LINEBODY$_BR"
|
|
bsc__nl
|
|
|
|
BSC_COLHGT=$(( BSC_COLHGT + BSC_WNDHGT ))
|
|
|
|
if [ $BSC_COLHGT -gt $BSC_COLBOT ]; then
|
|
BSC_COLBOT=$BSC_COLHGT
|
|
fi
|
|
|
|
if [ $BSC_COLBOT -gt $BSC_COLHGT_MAX ]; then
|
|
BSC_COLHGT_MAX=$BSC_COLBOT
|
|
fi
|
|
|
|
[ "$VERBOSE" -eq 2 ] && echo "End of window $_title" >&2
|
|
}
|
|
|
|
|
|
|
|
#main loop called
|
|
main_loop (){
|
|
|
|
bsc_term_init
|
|
bsc_init_chars
|
|
|
|
if [ "$BSC_MODE" = dashboard ]; then
|
|
trap "tput clear" WINCH
|
|
fi
|
|
|
|
while true; do
|
|
reset_layout
|
|
echo "" > "$BSC_BUFFER"
|
|
rm -f "$BSC_STDERR"
|
|
|
|
if [ "$BSC_MODE" = dashboard ]; then
|
|
tput clear >> "$BSC_BUFFER"
|
|
tput cup 0 0 >> "$BSC_BUFFER"
|
|
fi
|
|
|
|
main >> "$BSC_BUFFER" 2>"$BSC_STDERR"
|
|
|
|
# Go under the higest column, from under the last displayed window
|
|
tput cud $(( BSC_COLHGT_MAX - BSC_COLBOT )) >> "$BSC_BUFFER"
|
|
tput cub "$(expr $(tput cols) - 3)" >> "$BSC_BUFFER"
|
|
|
|
sigint_check
|
|
|
|
# Display the buffer
|
|
cat "$BSC_BUFFER"
|
|
|
|
[ $VERBOSE -gt 0 ] && [ -f "$BSC_STDERR" ] && cat "$BSC_STDERR" && rm "$BSC_STDERR"
|
|
|
|
dash -ic '{ read line; echo "$line" > $captured_input; kill 0; kill 0; } | { sleep 0.3; kill 0; }' 3>&1 2>/dev/null
|
|
|
|
retval=$?
|
|
if [ $retval -eq 255 ]; then
|
|
clean_env
|
|
exit "$retval"
|
|
fi
|
|
|
|
|
|
sigint_check
|
|
done
|
|
}
|
|
|
|
|
|
# Calls to this function are placed so as to avoid stdout mangling
|
|
sigint_check (){
|
|
if [ $BSC_SIGINT -eq 1 ]; then
|
|
clean_env
|
|
[ -f "$BSC_STDERR" ] && cat "$BSC_STDERR" && rm "$BSC_STDERR"
|
|
# https://mywiki.wooledge.org/SignalTrap
|
|
trap - INT
|
|
kill -s INT "$$"
|
|
fi
|
|
}
|