sync from MirBSD CVS
[shellsnippets/shellsnippets.git] / mksh / uhr
1 #!/bin/mksh
2 # $MirOS: contrib/hosted/tg/uhr,v 1.17 2015/11/29 21:34:06 tg Exp $
3 #-
4 # Copyright © 2012, 2013, 2015
5 #       mirabilos <m@mirbsd.org>
6 #
7 # Provided that these terms and disclaimer and all copyright notices
8 # are retained or reproduced in an accompanying document, permission
9 # is granted to deal in this work without restriction, including un‐
10 # limited rights to use, publicly perform, distribute, sell, modify,
11 # merge, give away, or sublicence.
12 #
13 # This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
14 # the utmost extent permitted by applicable law, neither express nor
15 # implied; without malicious intent or gross negligence. In no event
16 # may a licensor, author or contributor be held liable for indirect,
17 # direct, other damage, loss, or other issues arising in any way out
18 # of dealing in the work, even if advised of the possibility of such
19 # damage or existence of a defect, except proven that it results out
20 # of said person’s immediate fault when using the work as intended.
21 #-
22 # Analoguhr mit Digitalanzeige. Grundlegende Annahme: schnelles Ter‐
23 # minal, d.h. keine Voroptimierung der Darstellung durch das Skript;
24 # Font im Seitenverhältnis 1:2 (z.B. 9x18 aus XFree86® fixed-misc).
25
26 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
27         print -u2 Uhr requires mksh R41 or newer.
28         exit 1
29 fi
30 typeset -Z6 tosleep
31
32 # stupid GNU idiots breaking everything by default… grml…
33 bcopt=
34 bc --help >/dev/null 2>&1 && bcopt=-q
35
36 # global variables used by progress bar
37 _cnt_progress_bar=0
38 _cur_progress_bar=0
39 isin_progress_bar=0
40 nlin_progress_bar=0
41
42 # args: $1 = number of draw_progress_bar calls to make up 100%
43 function init_progress_bar {
44         global -i _cnt_progress_bar=$1 _cur_progress_bar=0
45         global -i nlin_progress_bar=$LINES isin_progress_bar=1
46
47         trap 'done_progress_bar' EXIT
48         # newline; up one line (to ensure we are not in the last line);
49         # save position; set scrolling region; restore position
50         print -n "\\n\\e[A\\e7\\e[1;$((# nlin_progress_bar - 1))r\\e8"
51 }
52
53 function sigwinch_uhr {
54         got_sigwinch=1
55         (( isin_progress_bar )) || return 0
56
57         # get new terminal size
58         nlin_progress_bar=$LINES
59         # newline; up one line (to ensure we are not in the last line);
60         # save position; set scrolling region; restore position
61         print -n "\\n\\e[A\\e7\\e[1;$((# nlin_progress_bar - 1))r\\e8"
62 }
63
64 function done_progress_bar {
65         (( isin_progress_bar )) || return 0
66         # save position; clear scrolling region;
67         # go to last line; delete line; restore position
68         print "\\e7\\e[0;0r\\e[$nlin_progress_bar;0H\\e[M\\e8"
69         isin_progress_bar=0
70         trap - EXIT
71 }
72
73 function draw_progress_bar {
74         local bar num w=$COLUMNS
75
76         ((# num = (++_cur_progress_bar * w * 8) / _cnt_progress_bar ))
77         while ((# num >= 8 )); do
78                 bar+=█
79                 ((# num -= 8 ))
80         done
81         case $num {
82         (7) bar+=▉ ;;
83         (6) bar+=▊ ;;
84         (5) bar+=▋ ;;
85         (4) bar+=▌ ;;
86         (3) bar+=▍ ;;
87         (2) bar+=▎ ;;
88         (1) bar+=▏ ;;
89         }
90         # fill complete line, right-align completion percentage display
91         local -R$w spc="$((# _cur_progress_bar * 100 / _cnt_progress_bar))%"
92         # elide percentage when it stops fitting
93         ((# (_cur_progress_bar * w / _cnt_progress_bar) > (w - 4) )) && spc=
94         # save position; go to last line; set colours;
95         # output a line full of spaces (and completion percentage);
96         # jump to first column; output bar (line præfix); restore position
97         print -n -- "\\e7\\e[$nlin_progress_bar;0H\\e[0;1;33;44m$spc\\r$bar\\e8"
98 }
99
100 function graceful {
101         print -n '\033[;H\033[J'
102         exit 0
103 }
104 trap graceful INT TERM HUP
105
106 trap sigwinch_uhr WINCH
107 while :; do
108 got_sigwinch=0
109
110 init_progress_bar 135
111 draw_progress_bar
112 S='Pregenerating arrays, please wait...'
113 if (( (r = (COLUMNS - ${%S}) / 2 - 2) < 1 )); then
114         d="\\e[0m\\n$S"
115 else
116         d=
117         (( n = ${%S} + 2 ))
118         while (( n-- )); do
119                 d+=─
120         done
121         d="\\e[0m\\e[$((LINES / 2 - 1));${r}H\\e7┌$d┐\\e8\\e[B│ $S │\\e8\\e[2B└$d┘"
122 fi
123 print "$d"
124
125 (( r = LINES * 2 ))
126 (( r = (r > COLUMNS ? COLUMNS : r) / 2 - 1))
127 (( n = 2 * r + 1 ))
128 set -A fb
129 integer fb
130
131 integer F_NO=0x00 M_NO=0x1F
132 integer F_BG=0x01 M_BG=0x1E
133 integer F_CC=0x02 M_CC=0x1D
134 integer F_HP=0x04 M_HP=0x1B
135 integer F_MP=0x08 M_MP=0x17
136 integer F_SP=0x10 M_SP=0x0F
137 integer B_BG=0x01 B_BLK=0x02 B_NB=0x0C B_DOT=0x10
138
139 set -U
140 #       -       BLK     BG      NB      DOT     NB|DOT
141 set -A m2c \
142         0x20    1#▀   1#*     1#▀   1#·    1#░   \
143         1#▄   1#█   1#█   1#█   1#▆   1#█   \
144         1#*     1#█   1##     1#◘   1#⁂   1#◙   \
145         1#▄   1#█   1#▆   1#█   1#▒   1#▓   \
146         1#.     1#▛   1#☿   1#▛   1#:     1#▒   \
147         1#▄   1#█   1#◙   1#█   1#▆   1#▓
148 typeset -i1 m2c[*]
149
150 set -A m2m
151 integer m2m
152
153 integer i=-1 j
154 while (( ++i <= 0x1F )); do
155         (( m2m[i] = !i ? 0 : (i & B_BLK) ? 1 :
156             (i & B_NB) ? ((i & B_DOT) ? 5 : 3) : (i & B_DOT) ? 4 : 2 ))
157 done
158
159 function refresh {
160         local -i10 i j z s c
161         local t
162
163         for i in "$@"; do
164                 (( z = (i / n) & 0xFFFE ))
165                 (( s = i % n ))
166                 (( i = m2m[fb[z * n + s]] ))
167                 (( j = m2m[fb[(z + 1) * n + s]] ))
168                 print -n "\e[$((z / 2 + 1));$((s + 1))H${m2c[j * 6 + i]#1#}"
169         done
170         print -n "\e[$((r / 2 + 1));$((r + 1))H\e[7mⓄ\e[0m"
171 }
172
173 # put arrayname x y
174 function put {
175         local _x=$(($2)) _y=$(($3)) _i
176         nameref _px=$1
177
178         (( _i = (r - _y) * n + _x + r ))
179         _px+=($_i)
180 }
181
182 # retrace arrayname maskname colourname
183 set -A px
184 function retrace {
185         nameref _px=$1 _m=$2 _c=$3
186         local _i
187
188         for _i in "${_px[@]}"; do
189                 (( fb[_i] = (fb[_i] & _m) | _c ))
190         done
191         px+=("${_px[@]}")
192 }
193
194 draw_progress_bar
195
196 # precalculate all lines’ endpoints with bc and paths with Bresenham
197 integer x y dx sx dy sy e f
198 bc -l $bcopt |&
199 print -p scale=20
200 print -p r=$r
201 print -p o=r
202 print -p 'define p(t) {
203         auto d
204         d = 90 - t
205         if (d < 0) d = 360 + d
206         return (d * 3.1415926535897932 / 180)
207 }'
208 # minutes and seconds – full length, 60 items
209 i=-1
210 while (( ++i < 60 )); do
211         draw_progress_bar
212         eval set -A lms$i
213         print -p "r * c(p($i * 6))"
214         read -p S; [[ $S = ?(-).* ]] && S=0
215         x=${S%%.*}
216         print -p "r * s(p($i * 6))"
217         read -p S; [[ $S = ?(-).* ]] && S=0
218         y=${S%%.*}
219         (( dx = x < 0 ? -x : x ))
220         (( sx = x < 0 ? 1 : -1 ))
221         (( dy = y < 0 ? y : -y ))
222         (( sy = y < 0 ? 1 : -1 ))
223         (( e = dx + dy ))
224         while :; do
225                 put lms$i x y
226                 (( !x && !y )) && break
227                 (( f = 2 * e ))
228                 if (( f > dy )); then
229                         (( e += dy ))
230                         (( x += sx ))
231                 fi
232                 if (( f < dx )); then
233                         (( e += dx ))
234                         (( y += sy ))
235                 fi
236         done
237 done
238 # hours – 2/3 length, 60 items (5 per hour)
239 print -p 'r = o * 2 / 3'
240 i=-1
241 while (( ++i < 60 )); do
242         draw_progress_bar
243         eval set -A lh$i
244         print -p "r * c(p($i * 6))"
245         read -p S; [[ $S = ?(-).* ]] && S=0
246         x=${S%%.*}
247         print -p "r * s(p($i * 6))"
248         read -p S; [[ $S = ?(-).* ]] && S=0
249         y=${S%%.*}
250         (( dx = x < 0 ? -x : x ))
251         (( sx = x < 0 ? 1 : -1 ))
252         (( dy = y < 0 ? y : -y ))
253         (( sy = y < 0 ? 1 : -1 ))
254         (( e = dx + dy ))
255         while :; do
256                 put lh$i x y
257                 (( !x && !y )) && break
258                 (( f = 2 * e ))
259                 if (( f > dy )); then
260                         (( e += dy ))
261                         (( x += sx ))
262                 fi
263                 if (( f < dx )); then
264                         (( e += dx ))
265                         (( y += sy ))
266                 fi
267         done
268 done
269 # hour markers – 80% length, 12 items
270 print -p 'r = o * 8 / 10'
271 i=-1
272 set -A mkx
273 set -A mky
274 while (( ++i < 12 )); do
275         draw_progress_bar
276         print -p "r * c(p($i * 30))"
277         read -p S; [[ $S = ?(-).* ]] && S=0
278         mkx[i]=${S%%.*}
279         print -p "r * s(p($i * 30))"
280         read -p S; [[ $S = ?(-).* ]] && S=0
281         mky[i]=${S%%.*}
282 done
283 exec 3>&p; exec 3>&-
284
285 draw_progress_bar
286 (( L = LINES >= (COLUMNS / 2) ? (COLUMNS / 2) : LINES ))
287 # fine-tuning of roman numeral position via screen size
288 (( ++mkx[7] ))
289 (( ++mkx[8] ))
290 case $L {
291 (22|23) (( ++mkx[6] )) ;|
292 (23)
293         (( mky[1] += 2 ))
294         (( mky[2] += 2 ))
295         (( mky[10] += 2 ))
296         (( mky[11] += 2 ))
297         ;;
298 (24|25|29|30|31|34)
299         (( mky[4] += 2 ))
300         (( mky[8] += 2 ))
301         ;|
302 (27|28|29)
303         (( ++mkx[10] ))
304         (( mky[8] += 2 ))
305         (( mky[9] += 2 ))
306         (( mky[10] += 2 ))
307         ;|
308 (27|29|31)
309         (( mky[0] -= 2 ))
310         ;|
311 (27)
312         (( --mkx[4] ))
313         (( --mkx[5] ))
314         (( ++mkx[6] ))
315         (( mkx[7] += 2 ))
316         (( ++mkx[8] ))
317         (( ++mkx[10] ))
318         ;;
319 (29)
320         (( mky[5] += 2 ))
321         (( mky[7] += 2 ))
322         ;;
323 (30)
324         (( mky[11] -= 2 ))
325         ;;
326 }
327 (( mky[0] += 2 * (L & 1) ))
328 done_progress_bar
329
330 # clear framebuffer and screen
331 set -A fb
332 integer fb
333 print -n -- '\e[H\e[J'
334
335 # draw hour markers
336 set -A lb
337 integer e f=-1 k
338 (( L > 21 )) && while (( ++f < 12 )); do
339         (( i=mkx[f] ))
340         (( j = mky[f] & ~1 ))
341         case $f {
342         (0) e=7 S='# # # # #  # ## # # #' ;;
343         (1) e=1 S='###' ;;
344         (2) e=3 S='# ## ## #' ;;
345         (3) e=5 S='# # ## # ## # #' ;;
346         (4) e=5 S='# # ## # ##  # ' ;;
347         (5) e=3 S='# ## # # ' ;;
348         (6) e=5 S='# # ## # # #  #' ;;
349         (7) e=7 S='# # # ## # # # #  # #' ;;
350         (8) e=9 S='# # # # ## # # # # #  # # #' ;;
351         (9) e=5 S='# # ##  # # # #' ;;
352         (10) e=3 S='# # # # #' ;;
353         (11) e=5 S='# # # #  ## # #' ;;
354         }
355         Y='0 1 2'
356         if (( L > 26 )); then
357                 d='###########'
358                 S="${d::e+2} ${S::e}  ${S: e:e}  ${S:2*e} ${d::e+2}"
359                 (( e += 2 ))
360                 Y+=' 3 4'
361                 (( j += 2 ))
362         fi
363         (( i -= e / 2 ))
364         k=0
365         for y in $Y; do
366                 (( y = j - y * 2 + 1 + (r & 1) ))
367                 (( dy = y + 1 ))
368                 (( x = i - 1 ))
369                 while (( ++x < (i + e) )); do
370                         [[ ${S: k++:1} = ' ' ]] && continue
371                         put lb x y
372                         put lb x dy
373                 done
374         done
375 done
376 retrace lb M_BG F_BG
377
378 # draw outer circle with Bresenham
379 set -A lc
380 integer x=r y=-1 f=r dx dy
381 while (( y < x )); do
382         (( dy = y++ * 2 + 1 ))
383         if (( y )); then
384                 (( f -= dy ))
385                 if (( f < 0 )); then
386                         (( dx = 1 - x-- * 2 ))
387                         (( f -= dx ))
388                 fi
389         fi
390         put lc x y
391         put lc -x y
392         put lc -x -y
393         put lc x -y
394         put lc y x
395         put lc -y x
396         put lc -y -x
397         put lc y -x
398 done
399 retrace lc M_CC F_CC
400 refresh "${px[@]}"; set -A px
401
402 set -A do -- -1 -1 -1
403 isfirst=1
404 while (( !got_sigwinch )); do
405         if (( isfirst )); then
406                 isfirst=0
407         else
408                 (( tosleep = 1000000 - ${EPOCHREALTIME#*.} ))
409                 if (( tosleep > 999999 )); then
410                         sleep 0.2
411                         (( tosleep = 1000000 - ${EPOCHREALTIME#*.} ))
412                 fi
413                 if (( tosleep > 999999 )); then
414                         # huh… maybe no gettimeofday(2) here
415                         while :; do
416                                 d=$(date +'%H %M %S,%d %b %Y')
417                                 set -A dt $d
418                                 (( dt[2] == do[2] )) || break
419                                 sleep 0.1
420                         done
421                 else
422                         sleep 0.$tosleep
423                 fi
424         fi
425
426         d=$(date +'%H %M %S,%d %b %Y')
427         S=${d#*,}
428         d=${d%,*}
429         set -A dt $d
430
431         (( dt[0] = (dt[0] % 12) * 5 + (dt[1] / 12) ))
432         if (( do[2] != -1 )); then
433                 retrace lms$((do[2])) M_SP F_NO
434                 (( do[1] == dt[1] )) || retrace lms$((do[1])) M_MP F_NO
435                 (( do[0] == dt[0] )) || retrace lh$((do[0])) M_HP F_NO
436         fi
437         (( do[0] == dt[0] )) || retrace lh$((dt[0])) M_HP F_HP
438         (( do[1] == dt[1] )) || retrace lms$((dt[1])) M_MP F_MP
439         retrace lms$((dt[2])) M_SP F_SP
440         refresh "${px[@]}"; set -A px
441         set -A do -- "${dt[@]}"
442
443         print -n "\e[1;$((n - ${%S} + 1))H$S\e[1;1H${d// /:}"
444 done
445 done