xref: /onnv-gate/usr/src/lib/libshell/common/scripts/termclock.sh (revision 12068:08a39a083754)
18462SApril.Chin@Sun.COM#!/usr/bin/ksh93
28462SApril.Chin@Sun.COM
38462SApril.Chin@Sun.COM#
48462SApril.Chin@Sun.COM# CDDL HEADER START
58462SApril.Chin@Sun.COM#
68462SApril.Chin@Sun.COM# The contents of this file are subject to the terms of the
78462SApril.Chin@Sun.COM# Common Development and Distribution License (the "License").
88462SApril.Chin@Sun.COM# You may not use this file except in compliance with the License.
98462SApril.Chin@Sun.COM#
108462SApril.Chin@Sun.COM# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
118462SApril.Chin@Sun.COM# or http://www.opensolaris.org/os/licensing.
128462SApril.Chin@Sun.COM# See the License for the specific language governing permissions
138462SApril.Chin@Sun.COM# and limitations under the License.
148462SApril.Chin@Sun.COM#
158462SApril.Chin@Sun.COM# When distributing Covered Code, include this CDDL HEADER in each
168462SApril.Chin@Sun.COM# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
178462SApril.Chin@Sun.COM# If applicable, add the following below this CDDL HEADER, with the
188462SApril.Chin@Sun.COM# fields enclosed by brackets "[]" replaced with your own identifying
198462SApril.Chin@Sun.COM# information: Portions Copyright [yyyy] [name of copyright owner]
208462SApril.Chin@Sun.COM#
218462SApril.Chin@Sun.COM# CDDL HEADER END
228462SApril.Chin@Sun.COM#
238462SApril.Chin@Sun.COM
248462SApril.Chin@Sun.COM#
25*12068SRoger.Faulkner@Oracle.COM# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
268462SApril.Chin@Sun.COM#
278462SApril.Chin@Sun.COM
288462SApril.Chin@Sun.COM#
298462SApril.Chin@Sun.COM# termclock - a simple analog clock for terminals
308462SApril.Chin@Sun.COM#
318462SApril.Chin@Sun.COM
328462SApril.Chin@Sun.COM# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
338462SApril.Chin@Sun.COMexport PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
348462SApril.Chin@Sun.COM
358462SApril.Chin@Sun.COM# Make sure all math stuff runs in the "C" locale to avoid problems
368462SApril.Chin@Sun.COM# with alternative # radix point representations (e.g. ',' instead of
378462SApril.Chin@Sun.COM# '.' in de_DE.*-locales). This needs to be set _before_ any
388462SApril.Chin@Sun.COM# floating-point constants are defined in this script).
398462SApril.Chin@Sun.COMif [[ "${LC_ALL}" != "" ]] ; then
408462SApril.Chin@Sun.COM    export \
418462SApril.Chin@Sun.COM        LC_MONETARY="${LC_ALL}" \
428462SApril.Chin@Sun.COM        LC_MESSAGES="${LC_ALL}" \
438462SApril.Chin@Sun.COM        LC_COLLATE="${LC_ALL}" \
448462SApril.Chin@Sun.COM        LC_CTYPE="${LC_ALL}"
458462SApril.Chin@Sun.COM        unset LC_ALL
468462SApril.Chin@Sun.COMfi
478462SApril.Chin@Sun.COMexport LC_NUMERIC=C
488462SApril.Chin@Sun.COM
498462SApril.Chin@Sun.COMfunction fatal_error
508462SApril.Chin@Sun.COM{
518462SApril.Chin@Sun.COM	print -u2 "${progname}: $*"
528462SApril.Chin@Sun.COM	exit 1
538462SApril.Chin@Sun.COM}
548462SApril.Chin@Sun.COM
558462SApril.Chin@Sun.COM# cache tput values (to avoid |fork()|'ing a "tput" child every second)
568462SApril.Chin@Sun.COMfunction tput_cup
578462SApril.Chin@Sun.COM{
588462SApril.Chin@Sun.COM	# static variable as cache for "tput_cup"
598462SApril.Chin@Sun.COM	typeset -S -A tput_cup_cache
608462SApril.Chin@Sun.COM
618462SApril.Chin@Sun.COM	integer y="$1" x="$2"
628462SApril.Chin@Sun.COM	nameref c="tput_cup_cache[\"${y}_${x}\"]"
638462SApril.Chin@Sun.COM
648462SApril.Chin@Sun.COM	if [[ "$c" == "" ]] ; then
658462SApril.Chin@Sun.COM		# fast path for known terminal types
668462SApril.Chin@Sun.COM		if [[ ${TERM} == ~(Elr)(vt100|vt220|xterm|xterm-color|dtterm) ]] ; then
678462SApril.Chin@Sun.COM			c="${ printf "\E[%d;%dH" y+1 x+1 ; }"
688462SApril.Chin@Sun.COM		else
698462SApril.Chin@Sun.COM			c="${ tput cup $y $x ; }"
708462SApril.Chin@Sun.COM		fi
718462SApril.Chin@Sun.COM	fi
728462SApril.Chin@Sun.COM
738462SApril.Chin@Sun.COM	print -r -n -- "$c"
748462SApril.Chin@Sun.COM	return 0
758462SApril.Chin@Sun.COM}
768462SApril.Chin@Sun.COM
778462SApril.Chin@Sun.COM# Get terminal size and put values into a compound variable with the integer
788462SApril.Chin@Sun.COM# members "columns" and "lines"
798462SApril.Chin@Sun.COMfunction get_term_size
808462SApril.Chin@Sun.COM{
818462SApril.Chin@Sun.COM	nameref rect=$1
828462SApril.Chin@Sun.COM
838462SApril.Chin@Sun.COM	rect.columns=${ tput cols ; } || return 1
848462SApril.Chin@Sun.COM	rect.lines=${ tput lines ; }  || return 1
858462SApril.Chin@Sun.COM
868462SApril.Chin@Sun.COM	return 0
878462SApril.Chin@Sun.COM}
888462SApril.Chin@Sun.COM
898462SApril.Chin@Sun.COMfunction draw_clock
908462SApril.Chin@Sun.COM{
918462SApril.Chin@Sun.COM	float angle a
928462SApril.Chin@Sun.COM	float x y
938462SApril.Chin@Sun.COM	integer i=1
948462SApril.Chin@Sun.COM
958462SApril.Chin@Sun.COM	for(( angle=0.0 ; angle < 360. ; angle+=6 )) ; do
968462SApril.Chin@Sun.COM		((
978462SApril.Chin@Sun.COM			a=angle/360.*(2*M_PI) ,
988462SApril.Chin@Sun.COM
998462SApril.Chin@Sun.COM			x=clock.len_x*cos(a) ,
1008462SApril.Chin@Sun.COM			y=clock.len_y*sin(a)
1018462SApril.Chin@Sun.COM		))
1028462SApril.Chin@Sun.COM
1038462SApril.Chin@Sun.COM		tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x ))
1048462SApril.Chin@Sun.COM
1058462SApril.Chin@Sun.COM		# add "mark" every 30 degrees
1068462SApril.Chin@Sun.COM		if (( int(angle)%30 == 0 )) ; then
1078462SApril.Chin@Sun.COM			print -r -n "$(((++i)%12+1))"
1088462SApril.Chin@Sun.COM		else
1098462SApril.Chin@Sun.COM			print -r -n "x"
1108462SApril.Chin@Sun.COM		fi
1118462SApril.Chin@Sun.COM	done
1128462SApril.Chin@Sun.COM	return 0
1138462SApril.Chin@Sun.COM}
1148462SApril.Chin@Sun.COM
1158462SApril.Chin@Sun.COMfunction draw_hand
1168462SApril.Chin@Sun.COM{
1178462SApril.Chin@Sun.COM	float   angle="$1" a
1188462SApril.Chin@Sun.COM	typeset ch="$2"
1198462SApril.Chin@Sun.COM	float   length="$3"
1208462SApril.Chin@Sun.COM	float   x y
1218462SApril.Chin@Sun.COM
1228462SApril.Chin@Sun.COM	(( a=angle/360.*(2*M_PI) ))
1238462SApril.Chin@Sun.COM
1248462SApril.Chin@Sun.COM	for (( s=0.0 ; s < 10. ; s+=0.5 )) ; do
1258462SApril.Chin@Sun.COM		((
1268462SApril.Chin@Sun.COM			x=(clock.len_x*(s/10.)*(length/100.))*cos(a) ,
1278462SApril.Chin@Sun.COM			y=(clock.len_y*(s/10.)*(length/100.))*sin(a)
1288462SApril.Chin@Sun.COM		))
1298462SApril.Chin@Sun.COM
1308462SApril.Chin@Sun.COM		tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x ))
1318462SApril.Chin@Sun.COM		print -r -n -- "${ch}"
1328462SApril.Chin@Sun.COM	done
1338462SApril.Chin@Sun.COM	return 0
1348462SApril.Chin@Sun.COM}
1358462SApril.Chin@Sun.COM
1368462SApril.Chin@Sun.COMfunction draw_clock_hand
1378462SApril.Chin@Sun.COM{
1388462SApril.Chin@Sun.COM	nameref hand=$1
1398462SApril.Chin@Sun.COM	draw_hand $(( 360.*(hand.val/hand.scale)-90. )) "${hand.ch}" ${hand.length}
1408462SApril.Chin@Sun.COM	return 0
1418462SApril.Chin@Sun.COM}
1428462SApril.Chin@Sun.COM
1438462SApril.Chin@Sun.COMfunction clear_clock_hand
1448462SApril.Chin@Sun.COM{
1458462SApril.Chin@Sun.COM	nameref hand=$1
1468462SApril.Chin@Sun.COM	draw_hand $(( 360.*(hand.val/hand.scale)-90. )) " " ${hand.length}
1478462SApril.Chin@Sun.COM	return 0
1488462SApril.Chin@Sun.COM}
1498462SApril.Chin@Sun.COM
1508462SApril.Chin@Sun.COMfunction main_loop
1518462SApril.Chin@Sun.COM{
1528462SApril.Chin@Sun.COM	typeset c
1538462SApril.Chin@Sun.COM
1548462SApril.Chin@Sun.COM	# note: we can't use subshells when writing to the double-buffer file because this
1558462SApril.Chin@Sun.COM	# will render the tput value cache useless
1568462SApril.Chin@Sun.COM	while true ; do
1578462SApril.Chin@Sun.COM		if ${init_screen} ; then
1588462SApril.Chin@Sun.COM			init_screen="false"
1598462SApril.Chin@Sun.COM
1608462SApril.Chin@Sun.COM			get_term_size termsize || fatal_error $"Couldn't get terminal size."
1618462SApril.Chin@Sun.COM
1628462SApril.Chin@Sun.COM			((
1638462SApril.Chin@Sun.COM				clock.middle_x=termsize.columns/2.-.5 ,
1648462SApril.Chin@Sun.COM				clock.middle_y=termsize.lines/2.-.5 ,
1658462SApril.Chin@Sun.COM				clock.len_x=termsize.columns/2-2 ,
1668462SApril.Chin@Sun.COM				clock.len_y=termsize.lines/2-2 ,
1678462SApril.Chin@Sun.COM			))
1688462SApril.Chin@Sun.COM
1698462SApril.Chin@Sun.COM			{
1708462SApril.Chin@Sun.COM				clear
1718462SApril.Chin@Sun.COM				draw_clock
1728462SApril.Chin@Sun.COM			} >&6
1738462SApril.Chin@Sun.COM		fi
1748462SApril.Chin@Sun.COM
1758462SApril.Chin@Sun.COM		{
1768462SApril.Chin@Sun.COM			(( ${ date +"hours.val=%H , minutes.val=%M , seconds.val=%S" ; } ))
1778462SApril.Chin@Sun.COM
1788462SApril.Chin@Sun.COM			# small trick to get a smooth "analog" flair
1798462SApril.Chin@Sun.COM			((
1808462SApril.Chin@Sun.COM				hours.val+=minutes.val/60. ,
1818462SApril.Chin@Sun.COM				minutes.val+=seconds.val/60.
1828462SApril.Chin@Sun.COM			))
1838462SApril.Chin@Sun.COM
1848462SApril.Chin@Sun.COM			draw_clock_hand seconds
1858462SApril.Chin@Sun.COM			draw_clock_hand minutes
1868462SApril.Chin@Sun.COM			draw_clock_hand hours
1878462SApril.Chin@Sun.COM
1888462SApril.Chin@Sun.COM			# move cursor to home position
1898462SApril.Chin@Sun.COM			tput_cup 0 0
1908462SApril.Chin@Sun.COM		} >&6
1918462SApril.Chin@Sun.COM
1928462SApril.Chin@Sun.COM		6<#((0))
1938462SApril.Chin@Sun.COM		cat <&6
1948462SApril.Chin@Sun.COM
19510898Sroland.mainz@nrubsig.org		redirect 6<&- ;  rm -f "${scratchfile}" ; redirect 6<> "${scratchfile}"
1968462SApril.Chin@Sun.COM
1978462SApril.Chin@Sun.COM		c="" ; read -r -t ${update_interval} -N 1 c
1988462SApril.Chin@Sun.COM		if [[ "$c" != "" ]] ; then
1998462SApril.Chin@Sun.COM			case "$c" in
2008462SApril.Chin@Sun.COM				~(Fi)q | $'\E') return 0 ;;
2018462SApril.Chin@Sun.COM			esac
2028462SApril.Chin@Sun.COM		fi
2038462SApril.Chin@Sun.COM
2048462SApril.Chin@Sun.COM		{
2058462SApril.Chin@Sun.COM			clear_clock_hand hours
2068462SApril.Chin@Sun.COM			clear_clock_hand minutes
2078462SApril.Chin@Sun.COM			clear_clock_hand seconds
2088462SApril.Chin@Sun.COM		} >&6
2098462SApril.Chin@Sun.COM	done
2108462SApril.Chin@Sun.COM}
2118462SApril.Chin@Sun.COM
2128462SApril.Chin@Sun.COMfunction usage
2138462SApril.Chin@Sun.COM{
2148462SApril.Chin@Sun.COM	OPTIND=0
2158462SApril.Chin@Sun.COM	getopts -a "${progname}" "${termclock_usage}" OPT '-?'
2168462SApril.Chin@Sun.COM	exit 2
2178462SApril.Chin@Sun.COM}
2188462SApril.Chin@Sun.COM
2198462SApril.Chin@Sun.COM# program start
2208462SApril.Chin@Sun.COMbuiltin basename
2218462SApril.Chin@Sun.COMbuiltin cat
2228462SApril.Chin@Sun.COMbuiltin date
22310898Sroland.mainz@nrubsig.orgbuiltin mktemp
2248462SApril.Chin@Sun.COMbuiltin rm
2258462SApril.Chin@Sun.COM
2268462SApril.Chin@Sun.COMtypeset progname="${ basename "${0}" ; }"
2278462SApril.Chin@Sun.COM
2288462SApril.Chin@Sun.COMfloat -r M_PI=3.14159265358979323846
2298462SApril.Chin@Sun.COM
2308462SApril.Chin@Sun.COM# terminal size rect
23110898Sroland.mainz@nrubsig.orgcompound termsize=(
2328462SApril.Chin@Sun.COM	integer columns=-1
2338462SApril.Chin@Sun.COM	integer lines=-1
2348462SApril.Chin@Sun.COM)
2358462SApril.Chin@Sun.COM
2368462SApril.Chin@Sun.COMtypeset init_screen="true"
2378462SApril.Chin@Sun.COM
23810898Sroland.mainz@nrubsig.orgcompound clock=(
2398462SApril.Chin@Sun.COM	float   middle_x
2408462SApril.Chin@Sun.COM	float   middle_y
2418462SApril.Chin@Sun.COM	integer len_x
2428462SApril.Chin@Sun.COM	integer len_y
2438462SApril.Chin@Sun.COM)
2448462SApril.Chin@Sun.COM
2458462SApril.Chin@Sun.COM
2468462SApril.Chin@Sun.COM# set clock properties
24710898Sroland.mainz@nrubsig.orgcompound seconds=(
2488462SApril.Chin@Sun.COM	float val
2498462SApril.Chin@Sun.COM	typeset ch
2508462SApril.Chin@Sun.COM	float   scale
2518462SApril.Chin@Sun.COM	integer length )
25210898Sroland.mainz@nrubsig.orgcompound minutes=(
2538462SApril.Chin@Sun.COM	float val
2548462SApril.Chin@Sun.COM	typeset ch
2558462SApril.Chin@Sun.COM	float   scale
2568462SApril.Chin@Sun.COM	integer length )
25710898Sroland.mainz@nrubsig.orgcompound hours=(
2588462SApril.Chin@Sun.COM	float val
2598462SApril.Chin@Sun.COM	typeset ch
2608462SApril.Chin@Sun.COM	float   scale
2618462SApril.Chin@Sun.COM	integer length )
2628462SApril.Chin@Sun.COM
2638462SApril.Chin@Sun.COMseconds.length=90 seconds.scale=60 seconds.ch=$"s"
2648462SApril.Chin@Sun.COMminutes.length=75 minutes.scale=60 minutes.ch=$"m"
2658462SApril.Chin@Sun.COMhours.length=50   hours.scale=12   hours.ch=$"h"
2668462SApril.Chin@Sun.COM
2678462SApril.Chin@Sun.COMfloat update_interval=0.9
2688462SApril.Chin@Sun.COM
2698462SApril.Chin@Sun.COMtypeset -r termclock_usage=$'+
270*12068SRoger.Faulkner@Oracle.COM[-?\n@(#)\$Id: termclock (Roland Mainz) 2009-12-02 \$\n]
2718462SApril.Chin@Sun.COM[-author?Roland Mainz <roland.mainz@nrubsig.org>]
2728462SApril.Chin@Sun.COM[-author?David Korn <dgk@research.att.com>]
2738462SApril.Chin@Sun.COM[+NAME?termclock - analog clock for terminals]
2748462SApril.Chin@Sun.COM[+DESCRIPTION?\btermclock\b is an analog clock for terminals.
2758462SApril.Chin@Sun.COM        The termclock program displays the time in analog or digital
2768462SApril.Chin@Sun.COM        form. The time is continuously updated at a frequency which
2778462SApril.Chin@Sun.COM        may be specified by the user.]
2788462SApril.Chin@Sun.COM[u:update?Update interval (defaults to 0.9 seconds).]:[interval]
2798462SApril.Chin@Sun.COM[+SEE ALSO?\bksh93\b(1), \bxclock\b(1)]
2808462SApril.Chin@Sun.COM'
2818462SApril.Chin@Sun.COM
2828462SApril.Chin@Sun.COMwhile getopts -a "${progname}" "${termclock_usage}" OPT ; do
2838462SApril.Chin@Sun.COM#	printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
2848462SApril.Chin@Sun.COM	case ${OPT} in
2858462SApril.Chin@Sun.COM		u)    update_interval=${OPTARG} ;;
2868462SApril.Chin@Sun.COM		*)    usage ;;
2878462SApril.Chin@Sun.COM	esac
2888462SApril.Chin@Sun.COMdone
2898462SApril.Chin@Sun.COMshift $((OPTIND-1))
2908462SApril.Chin@Sun.COM
2918462SApril.Chin@Sun.COM# prechecks
2928462SApril.Chin@Sun.COMwhich tput >/dev/null   || fatal_error $"tput not found."
2938462SApril.Chin@Sun.COM(( update_interval >= 0. && update_interval <= 7200. )) || fatal_error $"invalid update_interval value."
2948462SApril.Chin@Sun.COM
2958462SApril.Chin@Sun.COM# create temporary file for double-buffering and register an EXIT trap
2968462SApril.Chin@Sun.COM# to remove this file when the shell interpreter exits
297*12068SRoger.Faulkner@Oracle.COMscratchfile="${ mktemp -t "termclock.ppid${PPID}_pid$$.XXXXXX" ; }"
2988462SApril.Chin@Sun.COM[[ "${scratchfile}" != "" ]] || fatal_error $"Could not create temporary file name."
2998462SApril.Chin@Sun.COMtrap 'rm -f "${scratchfile}"' EXIT
30010898Sroland.mainz@nrubsig.orgrm -f "${scratchfile}" ; redirect 6<> "${scratchfile}" || fatal_error $"Could not create temporary file."
3018462SApril.Chin@Sun.COM
3028462SApril.Chin@Sun.COM# register trap to handle window size changes
3038462SApril.Chin@Sun.COMtrap 'init_screen="true"' WINCH
3048462SApril.Chin@Sun.COM
3058462SApril.Chin@Sun.COMmain_loop
3068462SApril.Chin@Sun.COM
3078462SApril.Chin@Sun.COM# exiting - put cursor below clock
3088462SApril.Chin@Sun.COMtput_cup $((termsize.lines-2)) 0
3098462SApril.Chin@Sun.COM
3108462SApril.Chin@Sun.COMexit 0
3118462SApril.Chin@Sun.COM# EOF.
312