merge current assockit from CVS, not yet enough
[shellsnippets/shellsnippets.git] / mksh / uhr
1 #!/bin/mksh
2 # $MirOS: contrib/hosted/tg/uhr,v 1.15 2014/08/21 20:08:21 tg Exp $
3 #-
4 # Copyright © 2012, 2013
5 #       Thorsten Glaser <tg@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 function graceful {
37         print -n '\033[;H\033[J'
38         exit 0
39 }
40 trap graceful INT TERM HUP
41
42 trap got_sigwinch=1 WINCH
43 while :; do
44 got_sigwinch=0
45
46 print "\e[0m\nPregenerating arrays, please wait..."
47 (( r = LINES * 2 ))
48 (( r = (r > COLUMNS ? COLUMNS : r) / 2 - 1))
49 (( n = 2 * r + 1 ))
50 set -A fb
51 integer fb
52
53 integer F_NO=0x00 M_NO=0x1F
54 integer F_BG=0x01 M_BG=0x1E
55 integer F_CC=0x02 M_CC=0x1D
56 integer F_HP=0x04 M_HP=0x1B
57 integer F_MP=0x08 M_MP=0x17
58 integer F_SP=0x10 M_SP=0x0F
59 integer B_BG=0x01 B_BLK=0x02 B_NB=0x0C B_DOT=0x10
60
61 set -U
62 #       -       BLK     BG      NB      DOT     NB|DOT
63 set -A m2c \
64         0x20    1#▀   1#*     1#▀   1#·    1#░   \
65         1#▄   1#█   1#█   1#█   1#▆   1#█   \
66         1#*     1#█   1##     1#◘   1#⁂   1#◙   \
67         1#▄   1#█   1#▆   1#█   1#▒   1#▓   \
68         1#.     1#▛   1#☿   1#▛   1#:     1#▒   \
69         1#▄   1#█   1#◙   1#█   1#▆   1#▓
70 typeset -i1 m2c[*]
71
72 set -A m2m
73 integer m2m
74
75 integer i=-1 j
76 while (( ++i <= 0x1F )); do
77         (( m2m[i] = !i ? 0 : (i & B_BLK) ? 1 :
78             (i & B_NB) ? ((i & B_DOT) ? 5 : 3) : (i & B_DOT) ? 4 : 2 ))
79 done
80
81 function refresh {
82         local -i10 i j z s c
83         local t
84
85         for i in "$@"; do
86                 (( z = (i / n) & 0xFFFE ))
87                 (( s = i % n ))
88                 (( i = m2m[fb[z * n + s]] ))
89                 (( j = m2m[fb[(z + 1) * n + s]] ))
90                 print -n "\e[$((z / 2 + 1));$((s + 1))H${m2c[j * 6 + i]#1#}"
91         done
92         print -n "\e[$((r / 2 + 1));$((r + 1))H\e[7mⓄ\e[0m"
93 }
94
95 # put arrayname x y
96 function put {
97         local _x=$(($2)) _y=$(($3)) _i
98         nameref _px=$1
99
100         (( _i = (r - _y) * n + _x + r ))
101         _px+=($_i)
102 }
103
104 # retrace arrayname maskname colourname
105 set -A px
106 function retrace {
107         nameref _px=$1 _m=$2 _c=$3
108         local _i
109
110         for _i in "${_px[@]}"; do
111                 (( fb[_i] = (fb[_i] & _m) | _c ))
112         done
113         px+=("${_px[@]}")
114 }
115
116 # precalculate all lines’ endpoints with bc and paths with Bresenham
117 integer x y dx sx dy sy e f
118 bc -l $bcopt |&
119 print -p scale=20
120 print -p r=$r
121 print -p o=r
122 print -p 'define p(t) {
123         auto d
124         d = 90 - t
125         if (d < 0) d = 360 + d
126         return (d * 3.1415926535897932 / 180)
127 }'
128 # minutes and seconds – full length, 60 items
129 i=-1
130 while (( ++i < 60 )); do
131         eval set -A lms$i
132         print -p "r * c(p($i * 6))"
133         read -p S; [[ $S = ?(-).* ]] && S=0
134         x=${S%%.*}
135         print -p "r * s(p($i * 6))"
136         read -p S; [[ $S = ?(-).* ]] && S=0
137         y=${S%%.*}
138         (( dx = x < 0 ? -x : x ))
139         (( sx = x < 0 ? 1 : -1 ))
140         (( dy = y < 0 ? y : -y ))
141         (( sy = y < 0 ? 1 : -1 ))
142         (( e = dx + dy ))
143         while :; do
144                 put lms$i x y
145                 (( !x && !y )) && break
146                 (( f = 2 * e ))
147                 if (( f > dy )); then
148                         (( e += dy ))
149                         (( x += sx ))
150                 fi
151                 if (( f < dx )); then
152                         (( e += dx ))
153                         (( y += sy ))
154                 fi
155         done
156 done
157 # hours – 2/3 length, 60 items (5 per hour)
158 print -p 'r = o * 2 / 3'
159 i=-1
160 while (( ++i < 60 )); do
161         eval set -A lh$i
162         print -p "r * c(p($i * 6))"
163         read -p S; [[ $S = ?(-).* ]] && S=0
164         x=${S%%.*}
165         print -p "r * s(p($i * 6))"
166         read -p S; [[ $S = ?(-).* ]] && S=0
167         y=${S%%.*}
168         (( dx = x < 0 ? -x : x ))
169         (( sx = x < 0 ? 1 : -1 ))
170         (( dy = y < 0 ? y : -y ))
171         (( sy = y < 0 ? 1 : -1 ))
172         (( e = dx + dy ))
173         while :; do
174                 put lh$i x y
175                 (( !x && !y )) && break
176                 (( f = 2 * e ))
177                 if (( f > dy )); then
178                         (( e += dy ))
179                         (( x += sx ))
180                 fi
181                 if (( f < dx )); then
182                         (( e += dx ))
183                         (( y += sy ))
184                 fi
185         done
186 done
187 # hour markers – 80% length, 12 items
188 print -p 'r = o * 8 / 10'
189 i=-1
190 set -A mkx
191 set -A mky
192 while (( ++i < 12 )); do
193         print -p "r * c(p($i * 30))"
194         read -p S; [[ $S = ?(-).* ]] && S=0
195         mkx[i]=${S%%.*}
196         print -p "r * s(p($i * 30))"
197         read -p S; [[ $S = ?(-).* ]] && S=0
198         mky[i]=${S%%.*}
199 done
200 exec 3>&p; exec 3>&-
201
202 (( L = LINES >= (COLUMNS / 2) ? (COLUMNS / 2) : LINES ))
203 # fine-tuning of roman numeral position via screen size
204 (( ++mkx[7] ))
205 (( ++mkx[8] ))
206 case $L {
207 (22|23) (( ++mkx[6] )) ;|
208 (23)
209         (( mky[1] += 2 ))
210         (( mky[2] += 2 ))
211         (( mky[10] += 2 ))
212         (( mky[11] += 2 ))
213         ;;
214 (24|25|29|30|31|34)
215         (( mky[4] += 2 ))
216         (( mky[8] += 2 ))
217         ;|
218 (27|28|29)
219         (( ++mkx[10] ))
220         (( mky[8] += 2 ))
221         (( mky[9] += 2 ))
222         (( mky[10] += 2 ))
223         ;|
224 (27|29|31)
225         (( mky[0] -= 2 ))
226         ;|
227 (27)
228         (( --mkx[4] ))
229         (( --mkx[5] ))
230         (( ++mkx[6] ))
231         (( mkx[7] += 2 ))
232         (( ++mkx[8] ))
233         (( ++mkx[10] ))
234         ;;
235 (29)
236         (( mky[5] += 2 ))
237         (( mky[7] += 2 ))
238         ;;
239 (30)
240         (( mky[11] -= 2 ))
241         ;;
242 }
243 (( mky[0] += 2 * (L & 1) ))
244
245 # clear framebuffer and screen
246 set -A fb
247 integer fb
248 print -n -- '\e[H\e[J'
249
250 # draw hour markers
251 set -A lb
252 integer e f=-1 k
253 (( L > 21 )) && while (( ++f < 12 )); do
254         (( i=mkx[f] ))
255         (( j = mky[f] & ~1 ))
256         case $f {
257         (0) e=7 S='# # # # #  # ## # # #' ;;
258         (1) e=1 S='###' ;;
259         (2) e=3 S='# ## ## #' ;;
260         (3) e=5 S='# # ## # ## # #' ;;
261         (4) e=5 S='# # ## # ##  # ' ;;
262         (5) e=3 S='# ## # # ' ;;
263         (6) e=5 S='# # ## # # #  #' ;;
264         (7) e=7 S='# # # ## # # # #  # #' ;;
265         (8) e=9 S='# # # # ## # # # # #  # # #' ;;
266         (9) e=5 S='# # ##  # # # #' ;;
267         (10) e=3 S='# # # # #' ;;
268         (11) e=5 S='# # # #  ## # #' ;;
269         }
270         Y='0 1 2'
271         if (( L > 26 )); then
272                 d='###########'
273                 S="${d::e+2} ${S::e}  ${S: e:e}  ${S:2*e} ${d::e+2}"
274                 (( e += 2 ))
275                 Y+=' 3 4'
276                 (( j += 2 ))
277         fi
278         (( i -= e / 2 ))
279         k=0
280         for y in $Y; do
281                 (( y = j - y * 2 + 1 + (r & 1) ))
282                 (( dy = y + 1 ))
283                 (( x = i - 1 ))
284                 while (( ++x < (i + e) )); do
285                         [[ ${S: k++:1} = ' ' ]] && continue
286                         put lb x y
287                         put lb x dy
288                 done
289         done
290 done
291 retrace lb M_BG F_BG
292
293 # draw outer circle with Bresenham
294 set -A lc
295 integer x=r y=-1 f=r dx dy
296 while (( y < x )); do
297         (( dy = y++ * 2 + 1 ))
298         if (( y )); then
299                 (( f -= dy ))
300                 if (( f < 0 )); then
301                         (( dx = 1 - x-- * 2 ))
302                         (( f -= dx ))
303                 fi
304         fi
305         put lc x y
306         put lc -x y
307         put lc -x -y
308         put lc x -y
309         put lc y x
310         put lc -y x
311         put lc -y -x
312         put lc y -x
313 done
314 retrace lc M_CC F_CC
315 refresh "${px[@]}"; set -A px
316
317 set -A do -- -1 -1 -1
318 isfirst=1
319 while (( !got_sigwinch )); do
320         if (( isfirst )); then
321                 isfirst=0
322         else
323                 (( tosleep = 1000000 - ${EPOCHREALTIME#*.} ))
324                 if (( tosleep > 999999 )); then
325                         sleep 0.2
326                         (( tosleep = 1000000 - ${EPOCHREALTIME#*.} ))
327                 fi
328                 if (( tosleep > 999999 )); then
329                         # huh… maybe no gettimeofday(2) here
330                         while :; do
331                                 d=$(date +'%H %M %S,%d %b %Y')
332                                 set -A dt $d
333                                 (( dt[2] == do[2] )) || break
334                                 sleep 0.1
335                         done
336                 else
337                         sleep 0.$tosleep
338                 fi
339         fi
340
341         d=$(date +'%H %M %S,%d %b %Y')
342         S=${d#*,}
343         d=${d%,*}
344         set -A dt $d
345
346         (( dt[0] = (dt[0] % 12) * 5 + (dt[1] / 12) ))
347         if (( do[2] != -1 )); then
348                 retrace lms$((do[2])) M_SP F_NO
349                 (( do[1] == dt[1] )) || retrace lms$((do[1])) M_MP F_NO
350                 (( do[0] == dt[0] )) || retrace lh$((do[0])) M_HP F_NO
351         fi
352         (( do[0] == dt[0] )) || retrace lh$((dt[0])) M_HP F_HP
353         (( do[1] == dt[1] )) || retrace lms$((dt[1])) M_MP F_MP
354         retrace lms$((dt[2])) M_SP F_SP
355         refresh "${px[@]}"; set -A px
356         set -A do -- "${dt[@]}"
357
358         print -n "\e[1;$((n - ${%S} + 1))H$S\e[1;1H${d// /:}"
359 done
360 done