func tick(s tcell.Screen, data *Data, manual bool, quit <-chan struct{}) {
	t := time.NewTicker(refreshInterval)
	for {
		select {
		case <-quit:
			return
		case <-t.C:
			draw(s, data, manual)
		}
	}
}
tick configures the goroutine for the scheduled calculateCost update.
package tui
import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"sync"
	"time"
	"unicode"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"github.com/gdamore/tcell/v2"
	"github.com/mattn/go-runewidth"
)
const refreshInterval time.Duration = time.Millisecond * 500
func initScreen() (tcell.Screen, error) {
	tcell.SetEncodingFallback(tcell.EncodingFallbackASCII)
	s, err := tcell.NewScreen()
	if err != nil {
		return s, err
	}
	if err = s.Init(); err != nil {
		return s, err
	}
	s.SetStyle(tcell.StyleDefault)
	s.Clear()
	return s, nil
}
func Start(manual bool, data *Data) {
	s, err := initScreen()
	if err != nil {
		log.Fatal(err)
	}
	averageSalary := viper.GetViper().GetInt("averageSalary")
	go func() {
		calculateCost(data, averageSalary)
	}()
	go func() {
		writeCostFile(data)
	}()
	quit := make(chan struct{})
	go func() {
		for {
			ev := s.PollEvent()
			switch ev := ev.(type) {
			case *tcell.EventKey:
				switch ev.Key() {
				case tcell.KeyEscape, tcell.KeyCtrlC:
					close(quit)
					return
				}
			case *tcell.EventResize:
				s.Sync()
				draw(s, data, manual)
			}
			if manual {
				switch ev := ev.(type) {
				case *tcell.EventKey:
					switch ev.Key() {
					case tcell.KeyEscape, tcell.KeyCtrlC:
						close(quit)
						return
					case tcell.KeyUp, tcell.KeyRight:
						c := data.GetCount()
						c++
						data.SetCount(c)
					case tcell.KeyDown, tcell.KeyLeft:
						c := data.GetCount()
						if c > 0 {
							c--
							data.SetCount(c)
						}
					case tcell.KeyRune:
						r := ev.Rune()
						if unicode.IsDigit(r) {
							i := data.getInput()
							i += string(r)
							data.setInput(i)
						}
					case tcell.KeyEnter:
						i := data.getInput()
						if i == "" {
							continue
						}
						c, _ := strconv.Atoi(i)
						data.SetCount(c)
						data.setInput("")
					case tcell.KeyBackspace, tcell.KeyBackspace2:
						i := data.getInput()
						if i != "" {
							data.setInput(i[0 : len(i)-1])
						}
					}
					draw(s, data, manual)
				}
			}
		}
	}()
	tick(s, data, manual, quit)
	s.Fini()
	log.Infof("Total cost: $%.2f", data.getCost())
}
type Data struct {
	mtx   sync.Mutex
	count int
	cost  float32
	input string
}
func (data *Data) GetCount() int {
	data.mtx.Lock()
	defer data.mtx.Unlock()
	return data.count
}
func (data *Data) SetCount(count int) {
	data.mtx.Lock()
	defer data.mtx.Unlock()
	data.count = count
}
func (data *Data) getCost() float32 {
	data.mtx.Lock()
	defer data.mtx.Unlock()
	return data.cost
}
func (data *Data) setCost(cost float32) {
	data.mtx.Lock()
	defer data.mtx.Unlock()
	data.cost = cost
}
func (data *Data) getInput() string {
	data.mtx.Lock()
	defer data.mtx.Unlock()
	return data.input
}
func (data *Data) setInput(input string) {
	data.mtx.Lock()
	defer data.mtx.Unlock()
	data.input = input
}