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