Tiny Core Linuxでターミナル上で動くアナログ時計「tclock.c」を動かそう思うとライブラリをインストールしたりコンパイルが必要だったりして面倒そうだったので、Tiny Core LinuxのCoreのインストール直後でも動くシェル(ash)で再現してみようという話です。
どんな画面になるの?
実行すると以下のような画面になります。以下の画面はTinyCoreですがインストール直後のTiny Core LinuxのCoreでも動きます。
課題は?
課題は私自身がシェルになれていないことと、今まで当然のように使っていたコマンドがTiny Core Linuxやash上では使えなかったりしますので、それをどう置き換えるかかなと思います。
課題1. 画面の大きさの取得
tput cols
やtput lines
を使えば画面の大きさを取得できるのですが、素のTiny Core Linuxにはtput
コマンドが入っていません。代わりにstty
コマンドを使って取得します。
echo $(stty size | awk '{print $2}') #cols
echo $(stty size | awk '{print $1}') #lines
課題2. カーソルの移動
tput
コマンドならtput cup $y $x
のようにカーソル移動できるのですが、その方法は使えないので今回はエスケープシーケンスを使って実現しています。
/bin/echo -n -e "\e[$(($y+1));$(($x+1))H"
課題3. 三角関数
tclockでは文字盤や針の座標を計算するのにsin、cosを使っています。bc
コマンドに-l
オプションをつければs(90)
やc(90)
という風に計算できるのですが素のTiny Core Linuxにはbc
コマンドがインストールされていません。代わりにawk
コマンドを使って計算します。
echo "90" | awk '{print sin($1)}' #sin関数
echo "90" | awk '{print cos($1)}' #cos関数
課題4. for
これまでforを書く時はfor ((i=0; i < 12; i++));
のように書いていたのですが、この書き方はbashでしかできないようです。以下のように書き換える事でどちらでも動くforが書けます。
for i in $(seq 0 11);
とりあえず動いたという感じ
今回は検証という位置づけなのと私のパワー不足ということもあって完全再現するには至りませんでした。tclock.cはpythonにも移植されていますので、そっちを使えば簡単だったかも…と思う今日このごろです。
ソースコード
以下がソースコードです。Tiny Core LinuxとUbuntuのbashとshでの動作することを確認しています。
#!/bin/sh
#see https://github.com/wereHamster/ncurses/blob/master/test/tclock.c
a2x() {
echo "2.2 $1 $2" | awk '{print int($1*$3*sin($2)+0.5)}'
return 0
}
a2y() {
echo "$1 $2" | awk '{print int($2*cos($1)+0.5)}'
return 0
}
sign() {
[ $1 -lt 0 ] && echo -1
[ $1 -ge 0 ] && echo 1
return 0
}
plot() {
str=$(echo -n $3 | sed 's/ /_/g')
loc=$(($1+$2*$COLS+1))
for i in $(seq 1 ${#str}); do
ch=$(echo -n $str | cut -c $i)
stdscr=$(echo -n $stdscr | sed -r "s/./$ch/$loc")
loc=$((loc+1))
done
return 0
}
refresh() {
/bin/echo -n -e "\e[1;1H$(echo -n $stdscr|sed 's/_/ /g')" #tput cup 0 0 ;echo -n "$stdscr"
}
dline() {
pair=$1
from_x=$2
from_y=$3
x2=$4
y2=$5
ch=$6
dx=$(($x2 - $from_x))
dy=$(($y2 - $from_y))
ax=$(($dx * 2));ax=$(echo "$ax" | sed 's/^-//')
ay=$(($dy * 2));ay=$(echo "$ay" | sed 's/^-//')
sx=$(sign $dx)
sy=$(sign $dy)
x=$from_x
y=$from_y
if [ $ax -gt $ay ] ; then
d=$(($ay - ($ax / 2)))
while true; do
plot $x $y "$ch"
[ $x -eq $x2 ] && break
if [ $d -ge 0 ]; then
y=$(($y + $sy))
d=$(($d - $ax))
fi
x=$(($x + $sx))
d=$(($d + $ay))
done
return 0
else
d=$(($ax - ($ay / 2)))
while true; do
plot $x $y "$ch"
[ $y -eq $y2 ] && break
if [ $d -ge 0 ]; then
x=$(($x + $sx))
d=$(($d - $ay))
fi
y=$(($y + $sy))
d=$(($d + $ax))
done
return 0
fi
}
init() {
clear #tput clear
COLS=$(stty size | awk '{print $2}')
LINES=$(stty size | awk '{print $1}')
stdscr=$(seq -s'_' $((COLS*LINES)) | sed 's/[0-9]//g')
cx=$(( ($COLS - 1) / 2 ))
cy=$(( $LINES / 2))
cr=$cy
[ $(($cx / 2)) -lt $cy ] && cr=$(( $cx / 2 ))
sradius=$(echo "$cr" | awk '{print (5 * $1) / 6}') #/* 10 */
mradius=$(echo "$cr" | awk '{print (3 * $1) / 4}') #/* 9 */
hradius=$(echo "$cr" | awk '{print $1 / 2}') #/* 6 */
for i in $(seq 0 11); do
sangle=$(echo "$i" | awk '{print ($1 + 1) * (2.0 * 3.14) / 12.0}')
sdx=$(a2x $sangle $sradius)
sdy=$(a2y $sangle $sradius)
szChar=$(printf "%d" $(($i+1)))
plot $(($cx + $sdx)) $(($cy - $sdy)) $szChar
done
plot 0 0 "ASCII Clock by Howard Jones (ha.jones@ic.ac.uk),1994"
sradius=$(echo "$sradius" | awk '{print (4 * $1) / 5}')
}
init
trap '/bin/echo -e "\e[?12;25h";exit' 2 #trap "tput cnorm;exit" SIGINT
/bin/echo -e "\e[?25l" #tput civis
trap 'init' 28 #SIGWINCH
while true; do
date=$(date)
hours=$(date +'%H')
min=$(date +'%M')
sec=$(date +'%S')
[ $hours -gt 12 ] && hours=$(($hours - 12))
hours=$(echo "$hours $min" | awk '{print $1 + $2/60}')
mangle=$(echo "$min $sec" | awk '{print (($1 + $2 / 60) * (2.0 * 3.14) / 60.0)}')
mdx=$(a2x $mangle $mradius)
mdy=$(a2y $mangle $mradius)
hangle=$(echo "$hours" | awk '{print $1 * (2.0 * 3.14) / 12.0}')
hdx=$(a2x $hangle $hradius)
hdy=$(a2y $hangle $hradius)
sangle=$(echo "$sec" | awk '{print $1 * (2.0 * 3.14) / 60.0}')
sdx=$(a2x $sangle $sradius)
sdy=$(a2y $sangle $sradius)
dline 3 $cx $cy $(($cx + $mdx)) $(($cy - $mdy)) '#'
dline 2 $cx $cy $(($cx + $hdx)) $(($cy - $hdy)) '.'
dline 1 $cx $cy $(($cx + $sdx)) $(($cy - $sdy)) '0'
plot 0 $(($LINES - 2)) "$date"
refresh
sleep 1
dline 0 $cx $cy $(($cx + $hdx)) $(($cy - $hdy)) '_'
dline 0 $cx $cy $(($cx + $mdx)) $(($cy - $mdy)) '_'
dline 0 $cx $cy $(($cx + $sdx)) $(($cy - $sdy)) '_'
done