xref: /titanic_51/usr/src/cmd/ast/libshell/common/scripts/termclock.sh (revision b4dd7d09880f14016feece03929a224eca1cf39a)
1*b4dd7d09SAndy Fiddaman#!/usr/bin/ksh93
2*b4dd7d09SAndy Fiddaman
3*b4dd7d09SAndy Fiddaman#
4*b4dd7d09SAndy Fiddaman# CDDL HEADER START
5*b4dd7d09SAndy Fiddaman#
6*b4dd7d09SAndy Fiddaman# The contents of this file are subject to the terms of the
7*b4dd7d09SAndy Fiddaman# Common Development and Distribution License (the "License").
8*b4dd7d09SAndy Fiddaman# You may not use this file except in compliance with the License.
9*b4dd7d09SAndy Fiddaman#
10*b4dd7d09SAndy Fiddaman# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11*b4dd7d09SAndy Fiddaman# or http://www.opensolaris.org/os/licensing.
12*b4dd7d09SAndy Fiddaman# See the License for the specific language governing permissions
13*b4dd7d09SAndy Fiddaman# and limitations under the License.
14*b4dd7d09SAndy Fiddaman#
15*b4dd7d09SAndy Fiddaman# When distributing Covered Code, include this CDDL HEADER in each
16*b4dd7d09SAndy Fiddaman# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17*b4dd7d09SAndy Fiddaman# If applicable, add the following below this CDDL HEADER, with the
18*b4dd7d09SAndy Fiddaman# fields enclosed by brackets "[]" replaced with your own identifying
19*b4dd7d09SAndy Fiddaman# information: Portions Copyright [yyyy] [name of copyright owner]
20*b4dd7d09SAndy Fiddaman#
21*b4dd7d09SAndy Fiddaman# CDDL HEADER END
22*b4dd7d09SAndy Fiddaman#
23*b4dd7d09SAndy Fiddaman
24*b4dd7d09SAndy Fiddaman#
25*b4dd7d09SAndy Fiddaman# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
26*b4dd7d09SAndy Fiddaman#
27*b4dd7d09SAndy Fiddaman
28*b4dd7d09SAndy Fiddaman#
29*b4dd7d09SAndy Fiddaman# termclock - a simple analog clock for terminals
30*b4dd7d09SAndy Fiddaman#
31*b4dd7d09SAndy Fiddaman
32*b4dd7d09SAndy Fiddaman# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
33*b4dd7d09SAndy Fiddamanexport PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
34*b4dd7d09SAndy Fiddaman
35*b4dd7d09SAndy Fiddaman# Make sure all math stuff runs in the "C" locale to avoid problems
36*b4dd7d09SAndy Fiddaman# with alternative # radix point representations (e.g. ',' instead of
37*b4dd7d09SAndy Fiddaman# '.' in de_DE.*-locales). This needs to be set _before_ any
38*b4dd7d09SAndy Fiddaman# floating-point constants are defined in this script).
39*b4dd7d09SAndy Fiddamanif [[ "${LC_ALL}" != "" ]] ; then
40*b4dd7d09SAndy Fiddaman    export \
41*b4dd7d09SAndy Fiddaman        LC_MONETARY="${LC_ALL}" \
42*b4dd7d09SAndy Fiddaman        LC_MESSAGES="${LC_ALL}" \
43*b4dd7d09SAndy Fiddaman        LC_COLLATE="${LC_ALL}" \
44*b4dd7d09SAndy Fiddaman        LC_CTYPE="${LC_ALL}"
45*b4dd7d09SAndy Fiddaman        unset LC_ALL
46*b4dd7d09SAndy Fiddamanfi
47*b4dd7d09SAndy Fiddamanexport LC_NUMERIC=C
48*b4dd7d09SAndy Fiddaman
49*b4dd7d09SAndy Fiddamanfunction fatal_error
50*b4dd7d09SAndy Fiddaman{
51*b4dd7d09SAndy Fiddaman	print -u2 "${progname}: $*"
52*b4dd7d09SAndy Fiddaman	exit 1
53*b4dd7d09SAndy Fiddaman}
54*b4dd7d09SAndy Fiddaman
55*b4dd7d09SAndy Fiddaman# cache tput values (to avoid |fork()|'ing a "tput" child every second)
56*b4dd7d09SAndy Fiddamanfunction tput_cup
57*b4dd7d09SAndy Fiddaman{
58*b4dd7d09SAndy Fiddaman	# static variable as cache for "tput_cup"
59*b4dd7d09SAndy Fiddaman	typeset -S -A tput_cup_cache
60*b4dd7d09SAndy Fiddaman
61*b4dd7d09SAndy Fiddaman	integer y="$1" x="$2"
62*b4dd7d09SAndy Fiddaman	nameref c="tput_cup_cache[\"${y}_${x}\"]"
63*b4dd7d09SAndy Fiddaman
64*b4dd7d09SAndy Fiddaman	if [[ "$c" == "" ]] ; then
65*b4dd7d09SAndy Fiddaman		# fast path for known terminal types
66*b4dd7d09SAndy Fiddaman		if [[ ${TERM} == ~(Elr)(vt100|vt220|xterm|xterm-color|dtterm) ]] ; then
67*b4dd7d09SAndy Fiddaman			c="${ printf "\E[%d;%dH" y+1 x+1 ; }"
68*b4dd7d09SAndy Fiddaman		else
69*b4dd7d09SAndy Fiddaman			c="${ tput cup $y $x ; }"
70*b4dd7d09SAndy Fiddaman		fi
71*b4dd7d09SAndy Fiddaman	fi
72*b4dd7d09SAndy Fiddaman
73*b4dd7d09SAndy Fiddaman	print -r -n -- "$c"
74*b4dd7d09SAndy Fiddaman	return 0
75*b4dd7d09SAndy Fiddaman}
76*b4dd7d09SAndy Fiddaman
77*b4dd7d09SAndy Fiddaman# Get terminal size and put values into a compound variable with the integer
78*b4dd7d09SAndy Fiddaman# members "columns" and "lines"
79*b4dd7d09SAndy Fiddamanfunction get_term_size
80*b4dd7d09SAndy Fiddaman{
81*b4dd7d09SAndy Fiddaman	nameref rect=$1
82*b4dd7d09SAndy Fiddaman
83*b4dd7d09SAndy Fiddaman	rect.columns=${ tput cols ; } || return 1
84*b4dd7d09SAndy Fiddaman	rect.lines=${ tput lines ; }  || return 1
85*b4dd7d09SAndy Fiddaman
86*b4dd7d09SAndy Fiddaman	return 0
87*b4dd7d09SAndy Fiddaman}
88*b4dd7d09SAndy Fiddaman
89*b4dd7d09SAndy Fiddamanfunction draw_clock
90*b4dd7d09SAndy Fiddaman{
91*b4dd7d09SAndy Fiddaman	float angle a
92*b4dd7d09SAndy Fiddaman	float x y
93*b4dd7d09SAndy Fiddaman	integer i=1
94*b4dd7d09SAndy Fiddaman
95*b4dd7d09SAndy Fiddaman	for(( angle=0.0 ; angle < 360. ; angle+=6 )) ; do
96*b4dd7d09SAndy Fiddaman		((
97*b4dd7d09SAndy Fiddaman			a=angle/360.*(2*M_PI) ,
98*b4dd7d09SAndy Fiddaman
99*b4dd7d09SAndy Fiddaman			x=clock.len_x*cos(a) ,
100*b4dd7d09SAndy Fiddaman			y=clock.len_y*sin(a)
101*b4dd7d09SAndy Fiddaman		))
102*b4dd7d09SAndy Fiddaman
103*b4dd7d09SAndy Fiddaman		tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x ))
104*b4dd7d09SAndy Fiddaman
105*b4dd7d09SAndy Fiddaman		# add "mark" every 30 degrees
106*b4dd7d09SAndy Fiddaman		if (( int(angle)%30 == 0 )) ; then
107*b4dd7d09SAndy Fiddaman			print -r -n "$(((++i)%12+1))"
108*b4dd7d09SAndy Fiddaman		else
109*b4dd7d09SAndy Fiddaman			print -r -n "x"
110*b4dd7d09SAndy Fiddaman		fi
111*b4dd7d09SAndy Fiddaman	done
112*b4dd7d09SAndy Fiddaman	return 0
113*b4dd7d09SAndy Fiddaman}
114*b4dd7d09SAndy Fiddaman
115*b4dd7d09SAndy Fiddamanfunction draw_hand
116*b4dd7d09SAndy Fiddaman{
117*b4dd7d09SAndy Fiddaman	float   angle="$1" a
118*b4dd7d09SAndy Fiddaman	typeset ch="$2"
119*b4dd7d09SAndy Fiddaman	float   length="$3"
120*b4dd7d09SAndy Fiddaman	float   x y
121*b4dd7d09SAndy Fiddaman
122*b4dd7d09SAndy Fiddaman	(( a=angle/360.*(2*M_PI) ))
123*b4dd7d09SAndy Fiddaman
124*b4dd7d09SAndy Fiddaman	for (( s=0.0 ; s < 10. ; s+=0.5 )) ; do
125*b4dd7d09SAndy Fiddaman		((
126*b4dd7d09SAndy Fiddaman			x=(clock.len_x*(s/10.)*(length/100.))*cos(a) ,
127*b4dd7d09SAndy Fiddaman			y=(clock.len_y*(s/10.)*(length/100.))*sin(a)
128*b4dd7d09SAndy Fiddaman		))
129*b4dd7d09SAndy Fiddaman
130*b4dd7d09SAndy Fiddaman		tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x ))
131*b4dd7d09SAndy Fiddaman		print -r -n -- "${ch}"
132*b4dd7d09SAndy Fiddaman	done
133*b4dd7d09SAndy Fiddaman	return 0
134*b4dd7d09SAndy Fiddaman}
135*b4dd7d09SAndy Fiddaman
136*b4dd7d09SAndy Fiddamanfunction draw_clock_hand
137*b4dd7d09SAndy Fiddaman{
138*b4dd7d09SAndy Fiddaman	nameref hand=$1
139*b4dd7d09SAndy Fiddaman	draw_hand $(( 360.*(hand.val/hand.scale)-90. )) "${hand.ch}" ${hand.length}
140*b4dd7d09SAndy Fiddaman	return 0
141*b4dd7d09SAndy Fiddaman}
142*b4dd7d09SAndy Fiddaman
143*b4dd7d09SAndy Fiddamanfunction clear_clock_hand
144*b4dd7d09SAndy Fiddaman{
145*b4dd7d09SAndy Fiddaman	nameref hand=$1
146*b4dd7d09SAndy Fiddaman	draw_hand $(( 360.*(hand.val/hand.scale)-90. )) " " ${hand.length}
147*b4dd7d09SAndy Fiddaman	return 0
148*b4dd7d09SAndy Fiddaman}
149*b4dd7d09SAndy Fiddaman
150*b4dd7d09SAndy Fiddamanfunction main_loop
151*b4dd7d09SAndy Fiddaman{
152*b4dd7d09SAndy Fiddaman	typeset c
153*b4dd7d09SAndy Fiddaman
154*b4dd7d09SAndy Fiddaman	# note: we can't use subshells when writing to the double-buffer file because this
155*b4dd7d09SAndy Fiddaman	# will render the tput value cache useless
156*b4dd7d09SAndy Fiddaman	while true ; do
157*b4dd7d09SAndy Fiddaman		if ${init_screen} ; then
158*b4dd7d09SAndy Fiddaman			init_screen="false"
159*b4dd7d09SAndy Fiddaman
160*b4dd7d09SAndy Fiddaman			get_term_size termsize || fatal_error $"Couldn't get terminal size."
161*b4dd7d09SAndy Fiddaman
162*b4dd7d09SAndy Fiddaman			((
163*b4dd7d09SAndy Fiddaman				clock.middle_x=termsize.columns/2.-.5 ,
164*b4dd7d09SAndy Fiddaman				clock.middle_y=termsize.lines/2.-.5 ,
165*b4dd7d09SAndy Fiddaman				clock.len_x=termsize.columns/2-2 ,
166*b4dd7d09SAndy Fiddaman				clock.len_y=termsize.lines/2-2 ,
167*b4dd7d09SAndy Fiddaman			))
168*b4dd7d09SAndy Fiddaman
169*b4dd7d09SAndy Fiddaman			{
170*b4dd7d09SAndy Fiddaman				clear
171*b4dd7d09SAndy Fiddaman				draw_clock
172*b4dd7d09SAndy Fiddaman			} >&6
173*b4dd7d09SAndy Fiddaman		fi
174*b4dd7d09SAndy Fiddaman
175*b4dd7d09SAndy Fiddaman		{
176*b4dd7d09SAndy Fiddaman			(( ${ date +"hours.val=%H , minutes.val=%M , seconds.val=%S" ; } ))
177*b4dd7d09SAndy Fiddaman
178*b4dd7d09SAndy Fiddaman			# small trick to get a smooth "analog" flair
179*b4dd7d09SAndy Fiddaman			((
180*b4dd7d09SAndy Fiddaman				hours.val+=minutes.val/60. ,
181*b4dd7d09SAndy Fiddaman				minutes.val+=seconds.val/60.
182*b4dd7d09SAndy Fiddaman			))
183*b4dd7d09SAndy Fiddaman
184*b4dd7d09SAndy Fiddaman			draw_clock_hand seconds
185*b4dd7d09SAndy Fiddaman			draw_clock_hand minutes
186*b4dd7d09SAndy Fiddaman			draw_clock_hand hours
187*b4dd7d09SAndy Fiddaman
188*b4dd7d09SAndy Fiddaman			# move cursor to home position
189*b4dd7d09SAndy Fiddaman			tput_cup 0 0
190*b4dd7d09SAndy Fiddaman		} >&6
191*b4dd7d09SAndy Fiddaman
192*b4dd7d09SAndy Fiddaman		6<#((0))
193*b4dd7d09SAndy Fiddaman		cat <&6
194*b4dd7d09SAndy Fiddaman
195*b4dd7d09SAndy Fiddaman		redirect 6<&- ;  rm -f "${scratchfile}" ; redirect 6<> "${scratchfile}"
196*b4dd7d09SAndy Fiddaman
197*b4dd7d09SAndy Fiddaman		c="" ; read -r -t ${update_interval} -N 1 c
198*b4dd7d09SAndy Fiddaman		if [[ "$c" != "" ]] ; then
199*b4dd7d09SAndy Fiddaman			case "$c" in
200*b4dd7d09SAndy Fiddaman				~(Fi)q | $'\E') return 0 ;;
201*b4dd7d09SAndy Fiddaman			esac
202*b4dd7d09SAndy Fiddaman		fi
203*b4dd7d09SAndy Fiddaman
204*b4dd7d09SAndy Fiddaman		{
205*b4dd7d09SAndy Fiddaman			clear_clock_hand hours
206*b4dd7d09SAndy Fiddaman			clear_clock_hand minutes
207*b4dd7d09SAndy Fiddaman			clear_clock_hand seconds
208*b4dd7d09SAndy Fiddaman		} >&6
209*b4dd7d09SAndy Fiddaman	done
210*b4dd7d09SAndy Fiddaman}
211*b4dd7d09SAndy Fiddaman
212*b4dd7d09SAndy Fiddamanfunction usage
213*b4dd7d09SAndy Fiddaman{
214*b4dd7d09SAndy Fiddaman	OPTIND=0
215*b4dd7d09SAndy Fiddaman	getopts -a "${progname}" "${termclock_usage}" OPT '-?'
216*b4dd7d09SAndy Fiddaman	exit 2
217*b4dd7d09SAndy Fiddaman}
218*b4dd7d09SAndy Fiddaman
219*b4dd7d09SAndy Fiddaman# program start
220*b4dd7d09SAndy Fiddamanbuiltin basename
221*b4dd7d09SAndy Fiddamanbuiltin cat
222*b4dd7d09SAndy Fiddamanbuiltin date
223*b4dd7d09SAndy Fiddamanbuiltin mktemp
224*b4dd7d09SAndy Fiddamanbuiltin rm
225*b4dd7d09SAndy Fiddaman
226*b4dd7d09SAndy Fiddamantypeset progname="${ basename "${0}" ; }"
227*b4dd7d09SAndy Fiddaman
228*b4dd7d09SAndy Fiddamanfloat -r M_PI=3.14159265358979323846
229*b4dd7d09SAndy Fiddaman
230*b4dd7d09SAndy Fiddaman# terminal size rect
231*b4dd7d09SAndy Fiddamancompound termsize=(
232*b4dd7d09SAndy Fiddaman	integer columns=-1
233*b4dd7d09SAndy Fiddaman	integer lines=-1
234*b4dd7d09SAndy Fiddaman)
235*b4dd7d09SAndy Fiddaman
236*b4dd7d09SAndy Fiddamantypeset init_screen="true"
237*b4dd7d09SAndy Fiddaman
238*b4dd7d09SAndy Fiddamancompound clock=(
239*b4dd7d09SAndy Fiddaman	float   middle_x
240*b4dd7d09SAndy Fiddaman	float   middle_y
241*b4dd7d09SAndy Fiddaman	integer len_x
242*b4dd7d09SAndy Fiddaman	integer len_y
243*b4dd7d09SAndy Fiddaman)
244*b4dd7d09SAndy Fiddaman
245*b4dd7d09SAndy Fiddaman
246*b4dd7d09SAndy Fiddaman# set clock properties
247*b4dd7d09SAndy Fiddamancompound seconds=(
248*b4dd7d09SAndy Fiddaman	float val
249*b4dd7d09SAndy Fiddaman	typeset ch
250*b4dd7d09SAndy Fiddaman	float   scale
251*b4dd7d09SAndy Fiddaman	integer length )
252*b4dd7d09SAndy Fiddamancompound minutes=(
253*b4dd7d09SAndy Fiddaman	float val
254*b4dd7d09SAndy Fiddaman	typeset ch
255*b4dd7d09SAndy Fiddaman	float   scale
256*b4dd7d09SAndy Fiddaman	integer length )
257*b4dd7d09SAndy Fiddamancompound hours=(
258*b4dd7d09SAndy Fiddaman	float val
259*b4dd7d09SAndy Fiddaman	typeset ch
260*b4dd7d09SAndy Fiddaman	float   scale
261*b4dd7d09SAndy Fiddaman	integer length )
262*b4dd7d09SAndy Fiddaman
263*b4dd7d09SAndy Fiddamanseconds.length=90 seconds.scale=60 seconds.ch=$"s"
264*b4dd7d09SAndy Fiddamanminutes.length=75 minutes.scale=60 minutes.ch=$"m"
265*b4dd7d09SAndy Fiddamanhours.length=50   hours.scale=12   hours.ch=$"h"
266*b4dd7d09SAndy Fiddaman
267*b4dd7d09SAndy Fiddamanfloat update_interval=0.9
268*b4dd7d09SAndy Fiddaman
269*b4dd7d09SAndy Fiddamantypeset -r termclock_usage=$'+
270*b4dd7d09SAndy Fiddaman[-?\n@(#)\$Id: termclock (Roland Mainz) 2009-12-02 \$\n]
271*b4dd7d09SAndy Fiddaman[-author?Roland Mainz <roland.mainz@nrubsig.org>]
272*b4dd7d09SAndy Fiddaman[-author?David Korn <dgk@research.att.com>]
273*b4dd7d09SAndy Fiddaman[+NAME?termclock - analog clock for terminals]
274*b4dd7d09SAndy Fiddaman[+DESCRIPTION?\btermclock\b is an analog clock for terminals.
275*b4dd7d09SAndy Fiddaman        The termclock program displays the time in analog or digital
276*b4dd7d09SAndy Fiddaman        form. The time is continuously updated at a frequency which
277*b4dd7d09SAndy Fiddaman        may be specified by the user.]
278*b4dd7d09SAndy Fiddaman[u:update?Update interval (defaults to 0.9 seconds).]:[interval]
279*b4dd7d09SAndy Fiddaman[+SEE ALSO?\bksh93\b(1), \bxclock\b(1)]
280*b4dd7d09SAndy Fiddaman'
281*b4dd7d09SAndy Fiddaman
282*b4dd7d09SAndy Fiddamanwhile getopts -a "${progname}" "${termclock_usage}" OPT ; do
283*b4dd7d09SAndy Fiddaman#	printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
284*b4dd7d09SAndy Fiddaman	case ${OPT} in
285*b4dd7d09SAndy Fiddaman		u)    update_interval=${OPTARG} ;;
286*b4dd7d09SAndy Fiddaman		*)    usage ;;
287*b4dd7d09SAndy Fiddaman	esac
288*b4dd7d09SAndy Fiddamandone
289*b4dd7d09SAndy Fiddamanshift $((OPTIND-1))
290*b4dd7d09SAndy Fiddaman
291*b4dd7d09SAndy Fiddaman# prechecks
292*b4dd7d09SAndy Fiddamanwhich tput >/dev/null   || fatal_error $"tput not found."
293*b4dd7d09SAndy Fiddaman(( update_interval >= 0. && update_interval <= 7200. )) || fatal_error $"invalid update_interval value."
294*b4dd7d09SAndy Fiddaman
295*b4dd7d09SAndy Fiddaman# create temporary file for double-buffering and register an EXIT trap
296*b4dd7d09SAndy Fiddaman# to remove this file when the shell interpreter exits
297*b4dd7d09SAndy Fiddamanscratchfile="${ mktemp -t "termclock.ppid${PPID}_pid$$.XXXXXX" ; }"
298*b4dd7d09SAndy Fiddaman[[ "${scratchfile}" != "" ]] || fatal_error $"Could not create temporary file name."
299*b4dd7d09SAndy Fiddamantrap 'rm -f "${scratchfile}"' EXIT
300*b4dd7d09SAndy Fiddamanrm -f "${scratchfile}" ; redirect 6<> "${scratchfile}" || fatal_error $"Could not create temporary file."
301*b4dd7d09SAndy Fiddaman
302*b4dd7d09SAndy Fiddaman# register trap to handle window size changes
303*b4dd7d09SAndy Fiddamantrap 'init_screen="true"' WINCH
304*b4dd7d09SAndy Fiddaman
305*b4dd7d09SAndy Fiddamanmain_loop
306*b4dd7d09SAndy Fiddaman
307*b4dd7d09SAndy Fiddaman# exiting - put cursor below clock
308*b4dd7d09SAndy Fiddamantput_cup $((termsize.lines-2)) 0
309*b4dd7d09SAndy Fiddaman
310*b4dd7d09SAndy Fiddamanexit 0
311*b4dd7d09SAndy Fiddaman# EOF.
312