5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

tclock.cをTinyCoreLinuxのシェル(ash)で再現してみた

Last updated at Posted at 2017-04-15

Tiny Core Linuxでターミナル上で動くアナログ時計「tclock.c」を動かそう思うとライブラリをインストールしたりコンパイルが必要だったりして面倒そうだったので、Tiny Core LinuxのCoreのインストール直後でも動くシェル(ash)で再現してみようという話です。

どんな画面になるの?

実行すると以下のような画面になります。以下の画面はTinyCoreですがインストール直後のTiny Core LinuxのCoreでも動きます。

image.png

課題は?

課題は私自身がシェルになれていないことと、今まで当然のように使っていたコマンドがTiny Core Linuxやash上では使えなかったりしますので、それをどう置き換えるかかなと思います。

課題1. 画面の大きさの取得

tput colstput linesを使えば画面の大きさを取得できるのですが、素のTiny Core Linuxにはtputコマンドが入っていません。代わりにsttyコマンドを使って取得します。

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コマンドを使って計算します。

sin,cosを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が書けます。

ash+TinyCoreLinuxでも動くforの書き方
for i in $(seq 0 11);

とりあえず動いたという感じ

今回は検証という位置づけなのと私のパワー不足ということもあって完全再現するには至りませんでした。tclock.cはpythonにも移植されていますので、そっちを使えば簡単だったかも…と思う今日このごろです。

ソースコード

以下がソースコードです。Tiny Core LinuxとUbuntuのbashとshでの動作することを確認しています。

tclock.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
5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?