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