File uhr of Package mksh
#!/bin/mksh
# -*- mode: sh -*-
# $MirOS: contrib/hosted/tg/uhr,v 1.23 2020/05/19 22:21:45 tg Exp $
#-
# Copyright © 2012, 2013, 2015, 2017, 2018, 2020
# mirabilos <m@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
# are retained or reproduced in an accompanying document, permission
# is granted to deal in this work without restriction, including un‐
# limited rights to use, publicly perform, distribute, sell, modify,
# merge, give away, or sublicence.
#
# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
# the utmost extent permitted by applicable law, neither express nor
# implied; without malicious intent or gross negligence. In no event
# may a licensor, author or contributor be held liable for indirect,
# direct, other damage, loss, or other issues arising in any way out
# of dealing in the work, even if advised of the possibility of such
# damage or existence of a defect, except proven that it results out
# of said person’s immediate fault when using the work as intended.
#-
# Analoguhr mit Digitalanzeige. Grundlegende Annahme: schnelles Ter‐
# minal, d.h. keine Voroptimierung der Darstellung durch das Skript;
# Font im Seitenverhältnis 1:2 (z.B. 9x18 aus XFree86® fixed-misc).
if [[ $KSH_VERSION != @(\@\(#\)MIRBSD KSH R)@(4[1-9]|[5-9][0-9]|[1-9][0-9]+([0-9]))\ +([0-9])/+([0-9])/+([0-9])?(\ *) ]]; then
print -u2 Uhr requires mksh R41 or newer.
exit 1
fi
set -U
print '\r\e[K\rLoading…'
function graceful {
print -n '\033[;H\033[J'
exit 0
}
trap graceful INT TERM HUP
# Shell library for easy display of a progress bar, modified for Uhr
#
# Usage:
# – before: init_progress_bar $n
# – $n times: draw_progress_bar
# – after: done_progress_bar
#
# init_progress_bar trashes the EXIT trap, done_progress_bar clears it.
# global variables used by this library
_cnt_progress_bar=0
_cur_progress_bar=0
isin_progress_bar=0
nlin_progress_bar=0
_cch_progress_bar=
[[ $KSH_VERSION = '@(''#)MIRBSD KSH R'@(4[0-9]|5[0-4])\ * ]] || \
alias global='typeset -g'
# args: $1 = number of draw_progress_bar calls to make up 100%
function init_progress_bar {
global -i _cnt_progress_bar=$1 _cur_progress_bar=0
global -i nlin_progress_bar=$LINES isin_progress_bar=1
_cch_progress_bar=
trap 'done_progress_bar $?' EXIT
# set up scrolling region, draw initial empty bar
sigwinch_uhr
got_sigwinch=0
}
unalias global
function sigwinch_uhr {
got_sigwinch=1
(( isin_progress_bar )) || return 0
# get new terminal size
nlin_progress_bar=$LINES
# save position; clear scrolling region; restore position; newline;
# up one line (to ensure we are not in the last line); save position;
# clear rest of screen; set new scrolling region; restore position
print -nu2 "\\e7\\e[0;0r\\e8\\n\\e[A\\e7\\e[J\\e[1;$((# nlin_progress_bar - 1))r\\e8"
# redraw progress bar
draw_progress_bar_internal
}
function done_progress_bar {
(( isin_progress_bar )) || return 0
isin_progress_bar=0
_cch_progress_bar=
# save position; clear scrolling region; restore position;
# save position; clear rest of screen; restore position
print -nu2 "\\e7\\e[0;0r\\e8\\e7\\e[J\\e8"
trap - EXIT
[[ -z $1 ]] || return $1
(( _cur_progress_bar == _cnt_progress_bar )) || \
print -ru2 W: expected $_cnt_progress_bar draw_progress_bar calls, \
got only $_cur_progress_bar
}
function draw_progress_bar {
# increment current progress
if (( ++_cur_progress_bar > _cnt_progress_bar )); then
print -ru2 "W: too many draw_progress_bar calls"
_cur_progress_bar=$_cnt_progress_bar
fi
# remaining drawing code
draw_progress_bar_internal
}
function draw_progress_bar_internal {
local bar num w=$COLUMNS pct
((# (num = (_cur_progress_bar * w * 8) / _cnt_progress_bar), 1 ))
((# (pct = _cur_progress_bar * 100 / _cnt_progress_bar), 1 ))
[[ $_cch_progress_bar != $num.$pct ]] || return 0
while ((# num >= 8 )); do
bar+=█
((# (num -= 8), 1 ))
done
case $num {
(7) bar+=▉ ;;
(6) bar+=▊ ;;
(5) bar+=▋ ;;
(4) bar+=▌ ;;
(3) bar+=▍ ;;
(2) bar+=▎ ;;
(1) bar+=▏ ;;
}
# fill complete line, right-align completion percentage display
local -R$w spc="$pct%"
# elide percentage when it stops fitting
((# (_cur_progress_bar * w / _cnt_progress_bar) <= (w - 4) )) || spc=
# save position; go to last line; set colours;
# output a line full of spaces (and completion percentage);
# jump to first column; output bar (line præfix); restore position
print -nu2 -- "\\e7\\e[$nlin_progress_bar;1H\\e[0;1;33;44m$spc\\r$bar\\e8"
_cch_progress_bar=$num.$pct
}
trap sigwinch_uhr WINCH
# stupid GNU idiots breaking everything by default… grml…
bcopt=
bc --help >/dev/null 2>&1 && bcopt=-q
integer F_NO=0x00 M_NO=0x1F
integer F_BG=0x01 M_BG=0x1E
integer F_CC=0x02 M_CC=0x1D
integer F_HP=0x04 M_HP=0x1B
integer F_MP=0x08 M_MP=0x17
integer F_SP=0x10 M_SP=0x0F
integer B_BG=0x01 B_BLK=0x02 B_NB=0x0C B_DOT=0x10
# - BLK BG NB DOT NB|DOT
set -A m2c \
' ' '▀' '*' '▀' '·' '░' \
'▄' '█' '█' '█' '▆' '█' \
'*' '█' '#' '◘' '⁂' '◙' \
'▄' '█' '▆' '█' '▒' '▓' \
'.' '▛' '☿' '▛' ':' '▒' \
'▄' '█' '◙' '█' '▆' '▓'
set -A m2m
integer m2m
integer i=-1 j
while (( ++i <= 0x1F )); do
(( m2m[i] = !i ? 0 : (i & B_BLK) ? 1 :
(i & B_NB) ? ((i & B_DOT) ? 5 : 3) : (i & B_DOT) ? 4 : 2 ))
done
function refresh {
local -i10 i j k l=-2 q=0
local t
unset changed[$(((r / 2) * n + r))]
for k in "${!changed[@]}"; do
(( i = m2m[fb[phys_v[k]]] ))
(( j = m2m[fb[phys_v[k] + n]] ))
(( phys_l[k] == l )) || t+=${phys_p[k]}
(( l = k ))
t+=${m2c[j * 6 + i]}
(( ++q & 7 )) && continue
print -nr -- "$t"
t=
done
set -A changed
print -n "$t\e[$((r / 2 + 1));$((r + 1))H\e[7mⓄ\e[0m\e[1;9H"
}
# put arrayname x y
function put {
local _x=$(($2)) _y=$(($3)) _i
nameref _px=$1
(( _i = (r - _y) * n + _x + r ))
_px+=($_i)
}
# retrace arrayname maskname colourname
function retrace {
nameref _px=$1 _m=$2 _c=$3
local _i _k _z _s
for _i in "${_px[@]}"; do
(( fb[_i] = (fb[_i] & _m) | _c ))
# map to physical coordinates
if [[ -z ${phys_z[_i]} ]]; then
(( phys_z[_i] = _z = (_i / n) / 2 ))
(( phys_s[_i] = _s = _i % n ))
(( phys_i[_i] = _z * n + _s ))
fi
_k=${phys_i[_i]}
if [[ -z ${phys_v[_k]} ]]; then
_z=${phys_z[_i]}
_s=${phys_s[_i]}
(( phys_v[_k] = _z * n * 2 + _s ))
(( phys_l[_k] = (_s && _z) ? _k - 1 : -3 ))
phys_p[_k]=$'\e['$((_z + 1))\;$((_s + 1))H
fi
changed[_k]= #set
done
}
function draw_hour_marker {
draw_progress_bar
f=$1 e=$2 S=$3
(( i = mkx[f] ))
(( j = mky[f] & ~1 ))
Y='0 1 2'
if (( L > 26 )); then
d='###########'
S="${d::e+2} ${S::e} ${S: e:e} ${S:2*e} ${d::e+2}"
(( e += 2 ))
Y+=' 3 4'
(( j += 2 ))
fi
(( i -= e / 2 ))
k=0
for y in $Y; do
(( y = j - y * 2 + 1 + (r & 1) ))
(( dy = y + 1 ))
(( x = i - 1 ))
while (( ++x < (i + e) )); do
[[ ${S: k++:1} = ' ' ]] && continue
put lb x y
put lb x dy
done
done
}
function draw_hour_markers {
set -A lb
draw_hour_marker 0 7 '# # # # # # ## # # #'
draw_hour_marker 1 1 '###'
draw_hour_marker 2 3 '# ## ## #'
draw_hour_marker 3 5 '# # ## # ## # #'
draw_hour_marker 4 5 '# # ## # ## # '
draw_hour_marker 5 3 '# ## # # '
draw_hour_marker 6 5 '# # ## # # # #'
draw_hour_marker 7 7 '# # # ## # # # # # #'
draw_hour_marker 8 9 '# # # # ## # # # # # # # #'
draw_hour_marker 9 5 '# # ## # # # #'
draw_hour_marker 10 3 '# # # # #'
draw_hour_marker 11 5 '# # # # ## # #'
retrace lb M_BG F_BG
}
# draw outer circle with Bresenham
function draw_outer_circle {
draw_progress_bar
set -A lc
integer x=r y=-1 f=r dx dy
while (( y < x )); do
(( dy = y++ * 2 + 1 ))
if (( y )); then
(( f -= dy ))
if (( f < 0 )); then
(( dx = 1 - x-- * 2 ))
(( f -= dx ))
fi
fi
put lc x y
put lc -x y
put lc -x -y
put lc x -y
put lc y x
put lc -y x
put lc -y -x
put lc y -x
done
retrace lc M_CC F_CC
}
function main_loop {
typeset -Z6 tosleep
set -A do -- -1 -1 -1
dodate_get
(( got_sigwinch )) && return
dodate_draw
while (( !got_sigwinch )); do
(( tosleep = 1000000 - ${EPOCHREALTIME#*.} ))
if (( tosleep > 999999 )); then
sleep 0.2
(( tosleep = 1000000 - ${EPOCHREALTIME#*.} ))
fi
if (( tosleep > 999999 )); then
# huh… maybe no gettimeofday(2) here
while :; do
d=$(date +'%H %M %S,%d %b %Y')
set -A dt $d
(( dt[2] == do[2] )) || break
sleep 0.1
done
else
sleep 0.$tosleep
fi
dodate_get
retrace lms$((do[2])) M_SP F_NO
(( do[1] == dt[1] )) || retrace lms$((do[1])) M_MP F_NO
(( do[0] == dt[0] )) || retrace lh$((do[0])) M_HP F_NO
(( got_sigwinch )) || dodate_draw
done
}
function dodate_get {
d=$(date +'%H %M %S,%d %b %Y')
S=${d#*,}
d=${d%,*}
print -n "\e[1;$((n - ${%S} + 1))H$S\e[1;1H${d// /:}"
set -A dt $d
(( dt[0] = (dt[0] % 12) * 5 + (dt[1] / 12) ))
}
function dodate_draw {
(( do[0] == dt[0] )) || retrace lh$((dt[0])) M_HP F_HP
(( do[1] == dt[1] )) || retrace lms$((dt[1])) M_MP F_MP
retrace lms$((dt[2])) M_SP F_SP
refresh
set -A do -- "${dt[@]}"
}
while :; do
(( L = LINES >= (COLUMNS / 2) ? (COLUMNS / 2) : LINES ))
init_progress_bar $((60 + 60 + (L > 21 ? (12 + 1 + 12) : 0) + 1 ))
S='Pregenerating arrays, please wait...'
if (( (r = (COLUMNS - ${%S}) / 2 - 2) > 0 )); then
d=
(( n = ${%S} + 2 ))
while (( n-- )); do
d+=─
done
S="\\e[$((LINES / 2 - 1));${r}H┌$d┐\\e[$((LINES / 2));${r}H│ $S │\\e[$((LINES / 2 + 1));${r}H└$d┘"
fi
print "\\e7\\e[0m$S\\e8"
(( r = LINES * 2 ))
(( r = (r > COLUMNS ? COLUMNS : r) / 2 - 1))
(( n = 2 * r + 1 ))
set -A fb
integer fb
set -A changed
set -A phys_z
set -A phys_s
set -A phys_i
set -A phys_v
set -A phys_p
# doch eine (minimale) Voroptimierung der Bildschirmausgabe
set -A phys_l
# precalculate all lines’ endpoints with bc and paths with Bresenham
integer x y dx sx dy sy e f
bc -l $bcopt |&
print -p scale=8
print -p r=$r
print -p o=r
print -p 'define p(t) {
auto d
d = 90 - t
if (d < 0) d = 360 + d
return (d * 3.1415926535897932 / 180)
}'
# minutes and seconds – full length, 60 items
i=-1
while (( ++i < 60 )); do
draw_progress_bar
eval set -A lms$i
print -p "r * c(p($i * 6))"
read -p S; [[ $S = ?(-).* ]] && S=0
x=${S%%.*}
print -p "r * s(p($i * 6))"
read -p S; [[ $S = ?(-).* ]] && S=0
y=${S%%.*}
(( dx = x < 0 ? -x : x ))
(( sx = x < 0 ? 1 : -1 ))
(( dy = y < 0 ? y : -y ))
(( sy = y < 0 ? 1 : -1 ))
(( e = dx + dy ))
while :; do
put lms$i x y
(( !x && !y )) && break
(( f = 2 * e ))
if (( f > dy )); then
(( e += dy ))
(( x += sx ))
fi
if (( f < dx )); then
(( e += dx ))
(( y += sy ))
fi
done
done
# hours – 2/3 length, 60 items (5 per hour)
print -p 'r = o * 2 / 3'
i=-1
while (( ++i < 60 )); do
draw_progress_bar
eval set -A lh$i
print -p "r * c(p($i * 6))"
read -p S; [[ $S = ?(-).* ]] && S=0
x=${S%%.*}
print -p "r * s(p($i * 6))"
read -p S; [[ $S = ?(-).* ]] && S=0
y=${S%%.*}
(( dx = x < 0 ? -x : x ))
(( sx = x < 0 ? 1 : -1 ))
(( dy = y < 0 ? y : -y ))
(( sy = y < 0 ? 1 : -1 ))
(( e = dx + dy ))
while :; do
put lh$i x y
(( !x && !y )) && break
(( f = 2 * e ))
if (( f > dy )); then
(( e += dy ))
(( x += sx ))
fi
if (( f < dx )); then
(( e += dx ))
(( y += sy ))
fi
done
done
# hour markers – 80% length, 12 items
if (( L > 21 )); then
print -p 'r = o * 8 / 10'
i=-1
set -A mkx
set -A mky
while (( ++i < 12 )); do
draw_progress_bar
print -p "r * c(p($i * 30))"
read -p S; [[ $S = ?(-).* ]] && S=0
mkx[i]=${S%%.*}
print -p "r * s(p($i * 30))"
read -p S; [[ $S = ?(-).* ]] && S=0
mky[i]=${S%%.*}
done
draw_progress_bar
# fine-tuning of roman numeral position via screen size
(( ++mkx[7] ))
(( ++mkx[8] ))
case $L {
(22|23) (( ++mkx[6] )) ;|
(23)
(( mky[1] += 2 ))
(( mky[2] += 2 ))
(( mky[10] += 2 ))
(( mky[11] += 2 ))
;;
(24|25|29|30|31|34)
(( mky[4] += 2 ))
(( mky[8] += 2 ))
;|
(27|28|29)
(( ++mkx[10] ))
(( mky[8] += 2 ))
(( mky[9] += 2 ))
(( mky[10] += 2 ))
;|
(27|29|31)
(( mky[0] -= 2 ))
;|
(27)
(( --mkx[4] ))
(( --mkx[5] ))
(( ++mkx[6] ))
(( mkx[7] += 2 ))
(( ++mkx[8] ))
(( ++mkx[10] ))
;;
(29)
(( mky[5] += 2 ))
(( mky[7] += 2 ))
;;
(30)
(( mky[11] -= 2 ))
;;
}
(( mky[0] += 2 * (L & 1) ))
fi
exec 3>&p; exec 3>&-
draw_outer_circle
(( L > 21 )) && draw_hour_markers
done_progress_bar
print -n -- '\e[H\e[J'
refresh
main_loop
done