xref: /titanic_51/usr/src/lib/libshell/common/scripts/termclock.sh (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
17c2fbfb3SApril Chin#!/usr/bin/ksh93
27c2fbfb3SApril Chin
37c2fbfb3SApril Chin#
47c2fbfb3SApril Chin# CDDL HEADER START
57c2fbfb3SApril Chin#
67c2fbfb3SApril Chin# The contents of this file are subject to the terms of the
77c2fbfb3SApril Chin# Common Development and Distribution License (the "License").
87c2fbfb3SApril Chin# You may not use this file except in compliance with the License.
97c2fbfb3SApril Chin#
107c2fbfb3SApril Chin# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
117c2fbfb3SApril Chin# or http://www.opensolaris.org/os/licensing.
127c2fbfb3SApril Chin# See the License for the specific language governing permissions
137c2fbfb3SApril Chin# and limitations under the License.
147c2fbfb3SApril Chin#
157c2fbfb3SApril Chin# When distributing Covered Code, include this CDDL HEADER in each
167c2fbfb3SApril Chin# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
177c2fbfb3SApril Chin# If applicable, add the following below this CDDL HEADER, with the
187c2fbfb3SApril Chin# fields enclosed by brackets "[]" replaced with your own identifying
197c2fbfb3SApril Chin# information: Portions Copyright [yyyy] [name of copyright owner]
207c2fbfb3SApril Chin#
217c2fbfb3SApril Chin# CDDL HEADER END
227c2fbfb3SApril Chin#
237c2fbfb3SApril Chin
247c2fbfb3SApril Chin#
25*3e14f97fSRoger A. Faulkner# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
267c2fbfb3SApril Chin#
277c2fbfb3SApril Chin
287c2fbfb3SApril Chin#
297c2fbfb3SApril Chin# termclock - a simple analog clock for terminals
307c2fbfb3SApril Chin#
317c2fbfb3SApril Chin
327c2fbfb3SApril Chin# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
337c2fbfb3SApril Chinexport PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
347c2fbfb3SApril Chin
357c2fbfb3SApril Chin# Make sure all math stuff runs in the "C" locale to avoid problems
367c2fbfb3SApril Chin# with alternative # radix point representations (e.g. ',' instead of
377c2fbfb3SApril Chin# '.' in de_DE.*-locales). This needs to be set _before_ any
387c2fbfb3SApril Chin# floating-point constants are defined in this script).
397c2fbfb3SApril Chinif [[ "${LC_ALL}" != "" ]] ; then
407c2fbfb3SApril Chin    export \
417c2fbfb3SApril Chin        LC_MONETARY="${LC_ALL}" \
427c2fbfb3SApril Chin        LC_MESSAGES="${LC_ALL}" \
437c2fbfb3SApril Chin        LC_COLLATE="${LC_ALL}" \
447c2fbfb3SApril Chin        LC_CTYPE="${LC_ALL}"
457c2fbfb3SApril Chin        unset LC_ALL
467c2fbfb3SApril Chinfi
477c2fbfb3SApril Chinexport LC_NUMERIC=C
487c2fbfb3SApril Chin
497c2fbfb3SApril Chinfunction fatal_error
507c2fbfb3SApril Chin{
517c2fbfb3SApril Chin	print -u2 "${progname}: $*"
527c2fbfb3SApril Chin	exit 1
537c2fbfb3SApril Chin}
547c2fbfb3SApril Chin
557c2fbfb3SApril Chin# cache tput values (to avoid |fork()|'ing a "tput" child every second)
567c2fbfb3SApril Chinfunction tput_cup
577c2fbfb3SApril Chin{
587c2fbfb3SApril Chin	# static variable as cache for "tput_cup"
597c2fbfb3SApril Chin	typeset -S -A tput_cup_cache
607c2fbfb3SApril Chin
617c2fbfb3SApril Chin	integer y="$1" x="$2"
627c2fbfb3SApril Chin	nameref c="tput_cup_cache[\"${y}_${x}\"]"
637c2fbfb3SApril Chin
647c2fbfb3SApril Chin	if [[ "$c" == "" ]] ; then
657c2fbfb3SApril Chin		# fast path for known terminal types
667c2fbfb3SApril Chin		if [[ ${TERM} == ~(Elr)(vt100|vt220|xterm|xterm-color|dtterm) ]] ; then
677c2fbfb3SApril Chin			c="${ printf "\E[%d;%dH" y+1 x+1 ; }"
687c2fbfb3SApril Chin		else
697c2fbfb3SApril Chin			c="${ tput cup $y $x ; }"
707c2fbfb3SApril Chin		fi
717c2fbfb3SApril Chin	fi
727c2fbfb3SApril Chin
737c2fbfb3SApril Chin	print -r -n -- "$c"
747c2fbfb3SApril Chin	return 0
757c2fbfb3SApril Chin}
767c2fbfb3SApril Chin
777c2fbfb3SApril Chin# Get terminal size and put values into a compound variable with the integer
787c2fbfb3SApril Chin# members "columns" and "lines"
797c2fbfb3SApril Chinfunction get_term_size
807c2fbfb3SApril Chin{
817c2fbfb3SApril Chin	nameref rect=$1
827c2fbfb3SApril Chin
837c2fbfb3SApril Chin	rect.columns=${ tput cols ; } || return 1
847c2fbfb3SApril Chin	rect.lines=${ tput lines ; }  || return 1
857c2fbfb3SApril Chin
867c2fbfb3SApril Chin	return 0
877c2fbfb3SApril Chin}
887c2fbfb3SApril Chin
897c2fbfb3SApril Chinfunction draw_clock
907c2fbfb3SApril Chin{
917c2fbfb3SApril Chin	float angle a
927c2fbfb3SApril Chin	float x y
937c2fbfb3SApril Chin	integer i=1
947c2fbfb3SApril Chin
957c2fbfb3SApril Chin	for(( angle=0.0 ; angle < 360. ; angle+=6 )) ; do
967c2fbfb3SApril Chin		((
977c2fbfb3SApril Chin			a=angle/360.*(2*M_PI) ,
987c2fbfb3SApril Chin
997c2fbfb3SApril Chin			x=clock.len_x*cos(a) ,
1007c2fbfb3SApril Chin			y=clock.len_y*sin(a)
1017c2fbfb3SApril Chin		))
1027c2fbfb3SApril Chin
1037c2fbfb3SApril Chin		tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x ))
1047c2fbfb3SApril Chin
1057c2fbfb3SApril Chin		# add "mark" every 30 degrees
1067c2fbfb3SApril Chin		if (( int(angle)%30 == 0 )) ; then
1077c2fbfb3SApril Chin			print -r -n "$(((++i)%12+1))"
1087c2fbfb3SApril Chin		else
1097c2fbfb3SApril Chin			print -r -n "x"
1107c2fbfb3SApril Chin		fi
1117c2fbfb3SApril Chin	done
1127c2fbfb3SApril Chin	return 0
1137c2fbfb3SApril Chin}
1147c2fbfb3SApril Chin
1157c2fbfb3SApril Chinfunction draw_hand
1167c2fbfb3SApril Chin{
1177c2fbfb3SApril Chin	float   angle="$1" a
1187c2fbfb3SApril Chin	typeset ch="$2"
1197c2fbfb3SApril Chin	float   length="$3"
1207c2fbfb3SApril Chin	float   x y
1217c2fbfb3SApril Chin
1227c2fbfb3SApril Chin	(( a=angle/360.*(2*M_PI) ))
1237c2fbfb3SApril Chin
1247c2fbfb3SApril Chin	for (( s=0.0 ; s < 10. ; s+=0.5 )) ; do
1257c2fbfb3SApril Chin		((
1267c2fbfb3SApril Chin			x=(clock.len_x*(s/10.)*(length/100.))*cos(a) ,
1277c2fbfb3SApril Chin			y=(clock.len_y*(s/10.)*(length/100.))*sin(a)
1287c2fbfb3SApril Chin		))
1297c2fbfb3SApril Chin
1307c2fbfb3SApril Chin		tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x ))
1317c2fbfb3SApril Chin		print -r -n -- "${ch}"
1327c2fbfb3SApril Chin	done
1337c2fbfb3SApril Chin	return 0
1347c2fbfb3SApril Chin}
1357c2fbfb3SApril Chin
1367c2fbfb3SApril Chinfunction draw_clock_hand
1377c2fbfb3SApril Chin{
1387c2fbfb3SApril Chin	nameref hand=$1
1397c2fbfb3SApril Chin	draw_hand $(( 360.*(hand.val/hand.scale)-90. )) "${hand.ch}" ${hand.length}
1407c2fbfb3SApril Chin	return 0
1417c2fbfb3SApril Chin}
1427c2fbfb3SApril Chin
1437c2fbfb3SApril Chinfunction clear_clock_hand
1447c2fbfb3SApril Chin{
1457c2fbfb3SApril Chin	nameref hand=$1
1467c2fbfb3SApril Chin	draw_hand $(( 360.*(hand.val/hand.scale)-90. )) " " ${hand.length}
1477c2fbfb3SApril Chin	return 0
1487c2fbfb3SApril Chin}
1497c2fbfb3SApril Chin
1507c2fbfb3SApril Chinfunction main_loop
1517c2fbfb3SApril Chin{
1527c2fbfb3SApril Chin	typeset c
1537c2fbfb3SApril Chin
1547c2fbfb3SApril Chin	# note: we can't use subshells when writing to the double-buffer file because this
1557c2fbfb3SApril Chin	# will render the tput value cache useless
1567c2fbfb3SApril Chin	while true ; do
1577c2fbfb3SApril Chin		if ${init_screen} ; then
1587c2fbfb3SApril Chin			init_screen="false"
1597c2fbfb3SApril Chin
1607c2fbfb3SApril Chin			get_term_size termsize || fatal_error $"Couldn't get terminal size."
1617c2fbfb3SApril Chin
1627c2fbfb3SApril Chin			((
1637c2fbfb3SApril Chin				clock.middle_x=termsize.columns/2.-.5 ,
1647c2fbfb3SApril Chin				clock.middle_y=termsize.lines/2.-.5 ,
1657c2fbfb3SApril Chin				clock.len_x=termsize.columns/2-2 ,
1667c2fbfb3SApril Chin				clock.len_y=termsize.lines/2-2 ,
1677c2fbfb3SApril Chin			))
1687c2fbfb3SApril Chin
1697c2fbfb3SApril Chin			{
1707c2fbfb3SApril Chin				clear
1717c2fbfb3SApril Chin				draw_clock
1727c2fbfb3SApril Chin			} >&6
1737c2fbfb3SApril Chin		fi
1747c2fbfb3SApril Chin
1757c2fbfb3SApril Chin		{
1767c2fbfb3SApril Chin			(( ${ date +"hours.val=%H , minutes.val=%M , seconds.val=%S" ; } ))
1777c2fbfb3SApril Chin
1787c2fbfb3SApril Chin			# small trick to get a smooth "analog" flair
1797c2fbfb3SApril Chin			((
1807c2fbfb3SApril Chin				hours.val+=minutes.val/60. ,
1817c2fbfb3SApril Chin				minutes.val+=seconds.val/60.
1827c2fbfb3SApril Chin			))
1837c2fbfb3SApril Chin
1847c2fbfb3SApril Chin			draw_clock_hand seconds
1857c2fbfb3SApril Chin			draw_clock_hand minutes
1867c2fbfb3SApril Chin			draw_clock_hand hours
1877c2fbfb3SApril Chin
1887c2fbfb3SApril Chin			# move cursor to home position
1897c2fbfb3SApril Chin			tput_cup 0 0
1907c2fbfb3SApril Chin		} >&6
1917c2fbfb3SApril Chin
1927c2fbfb3SApril Chin		6<#((0))
1937c2fbfb3SApril Chin		cat <&6
1947c2fbfb3SApril Chin
1957c2fbfb3SApril Chin		redirect 6<&- ;  rm -f "${scratchfile}" ; redirect 6<> "${scratchfile}"
1967c2fbfb3SApril Chin
1977c2fbfb3SApril Chin		c="" ; read -r -t ${update_interval} -N 1 c
1987c2fbfb3SApril Chin		if [[ "$c" != "" ]] ; then
1997c2fbfb3SApril Chin			case "$c" in
2007c2fbfb3SApril Chin				~(Fi)q | $'\E') return 0 ;;
2017c2fbfb3SApril Chin			esac
2027c2fbfb3SApril Chin		fi
2037c2fbfb3SApril Chin
2047c2fbfb3SApril Chin		{
2057c2fbfb3SApril Chin			clear_clock_hand hours
2067c2fbfb3SApril Chin			clear_clock_hand minutes
2077c2fbfb3SApril Chin			clear_clock_hand seconds
2087c2fbfb3SApril Chin		} >&6
2097c2fbfb3SApril Chin	done
2107c2fbfb3SApril Chin}
2117c2fbfb3SApril Chin
2127c2fbfb3SApril Chinfunction usage
2137c2fbfb3SApril Chin{
2147c2fbfb3SApril Chin	OPTIND=0
2157c2fbfb3SApril Chin	getopts -a "${progname}" "${termclock_usage}" OPT '-?'
2167c2fbfb3SApril Chin	exit 2
2177c2fbfb3SApril Chin}
2187c2fbfb3SApril Chin
2197c2fbfb3SApril Chin# program start
2207c2fbfb3SApril Chinbuiltin basename
2217c2fbfb3SApril Chinbuiltin cat
2227c2fbfb3SApril Chinbuiltin date
22334f9b3eeSRoland Mainzbuiltin mktemp
2247c2fbfb3SApril Chinbuiltin rm
2257c2fbfb3SApril Chin
2267c2fbfb3SApril Chintypeset progname="${ basename "${0}" ; }"
2277c2fbfb3SApril Chin
2287c2fbfb3SApril Chinfloat -r M_PI=3.14159265358979323846
2297c2fbfb3SApril Chin
2307c2fbfb3SApril Chin# terminal size rect
23134f9b3eeSRoland Mainzcompound termsize=(
2327c2fbfb3SApril Chin	integer columns=-1
2337c2fbfb3SApril Chin	integer lines=-1
2347c2fbfb3SApril Chin)
2357c2fbfb3SApril Chin
2367c2fbfb3SApril Chintypeset init_screen="true"
2377c2fbfb3SApril Chin
23834f9b3eeSRoland Mainzcompound clock=(
2397c2fbfb3SApril Chin	float   middle_x
2407c2fbfb3SApril Chin	float   middle_y
2417c2fbfb3SApril Chin	integer len_x
2427c2fbfb3SApril Chin	integer len_y
2437c2fbfb3SApril Chin)
2447c2fbfb3SApril Chin
2457c2fbfb3SApril Chin
2467c2fbfb3SApril Chin# set clock properties
24734f9b3eeSRoland Mainzcompound seconds=(
2487c2fbfb3SApril Chin	float val
2497c2fbfb3SApril Chin	typeset ch
2507c2fbfb3SApril Chin	float   scale
2517c2fbfb3SApril Chin	integer length )
25234f9b3eeSRoland Mainzcompound minutes=(
2537c2fbfb3SApril Chin	float val
2547c2fbfb3SApril Chin	typeset ch
2557c2fbfb3SApril Chin	float   scale
2567c2fbfb3SApril Chin	integer length )
25734f9b3eeSRoland Mainzcompound hours=(
2587c2fbfb3SApril Chin	float val
2597c2fbfb3SApril Chin	typeset ch
2607c2fbfb3SApril Chin	float   scale
2617c2fbfb3SApril Chin	integer length )
2627c2fbfb3SApril Chin
2637c2fbfb3SApril Chinseconds.length=90 seconds.scale=60 seconds.ch=$"s"
2647c2fbfb3SApril Chinminutes.length=75 minutes.scale=60 minutes.ch=$"m"
2657c2fbfb3SApril Chinhours.length=50   hours.scale=12   hours.ch=$"h"
2667c2fbfb3SApril Chin
2677c2fbfb3SApril Chinfloat update_interval=0.9
2687c2fbfb3SApril Chin
2697c2fbfb3SApril Chintypeset -r termclock_usage=$'+
270*3e14f97fSRoger A. Faulkner[-?\n@(#)\$Id: termclock (Roland Mainz) 2009-12-02 \$\n]
2717c2fbfb3SApril Chin[-author?Roland Mainz <roland.mainz@nrubsig.org>]
2727c2fbfb3SApril Chin[-author?David Korn <dgk@research.att.com>]
2737c2fbfb3SApril Chin[+NAME?termclock - analog clock for terminals]
2747c2fbfb3SApril Chin[+DESCRIPTION?\btermclock\b is an analog clock for terminals.
2757c2fbfb3SApril Chin        The termclock program displays the time in analog or digital
2767c2fbfb3SApril Chin        form. The time is continuously updated at a frequency which
2777c2fbfb3SApril Chin        may be specified by the user.]
2787c2fbfb3SApril Chin[u:update?Update interval (defaults to 0.9 seconds).]:[interval]
2797c2fbfb3SApril Chin[+SEE ALSO?\bksh93\b(1), \bxclock\b(1)]
2807c2fbfb3SApril Chin'
2817c2fbfb3SApril Chin
2827c2fbfb3SApril Chinwhile getopts -a "${progname}" "${termclock_usage}" OPT ; do
2837c2fbfb3SApril Chin#	printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
2847c2fbfb3SApril Chin	case ${OPT} in
2857c2fbfb3SApril Chin		u)    update_interval=${OPTARG} ;;
2867c2fbfb3SApril Chin		*)    usage ;;
2877c2fbfb3SApril Chin	esac
2887c2fbfb3SApril Chindone
2897c2fbfb3SApril Chinshift $((OPTIND-1))
2907c2fbfb3SApril Chin
2917c2fbfb3SApril Chin# prechecks
2927c2fbfb3SApril Chinwhich tput >/dev/null   || fatal_error $"tput not found."
2937c2fbfb3SApril Chin(( update_interval >= 0. && update_interval <= 7200. )) || fatal_error $"invalid update_interval value."
2947c2fbfb3SApril Chin
2957c2fbfb3SApril Chin# create temporary file for double-buffering and register an EXIT trap
2967c2fbfb3SApril Chin# to remove this file when the shell interpreter exits
297*3e14f97fSRoger A. Faulknerscratchfile="${ mktemp -t "termclock.ppid${PPID}_pid$$.XXXXXX" ; }"
2987c2fbfb3SApril Chin[[ "${scratchfile}" != "" ]] || fatal_error $"Could not create temporary file name."
2997c2fbfb3SApril Chintrap 'rm -f "${scratchfile}"' EXIT
3007c2fbfb3SApril Chinrm -f "${scratchfile}" ; redirect 6<> "${scratchfile}" || fatal_error $"Could not create temporary file."
3017c2fbfb3SApril Chin
3027c2fbfb3SApril Chin# register trap to handle window size changes
3037c2fbfb3SApril Chintrap 'init_screen="true"' WINCH
3047c2fbfb3SApril Chin
3057c2fbfb3SApril Chinmain_loop
3067c2fbfb3SApril Chin
3077c2fbfb3SApril Chin# exiting - put cursor below clock
3087c2fbfb3SApril Chintput_cup $((termsize.lines-2)) 0
3097c2fbfb3SApril Chin
3107c2fbfb3SApril Chinexit 0
3117c2fbfb3SApril Chin# EOF.
312