// Package main has a comment package main import ( "flag" "fmt" "local/logger" "time" "github.com/eiannone/keyboard" "github.com/everdev/mack" ) var notified = false var alertMessage string var originalStart = time.Now() func main() { var duration string var offset string var interval string var repeat bool var invert bool var eta bool flag.BoolVar(&repeat, "repeat", true, "Whether to repeat the timer on complete") flag.BoolVar(&invert, "stopwatch", false, "Use as a stopwatch") flag.StringVar(&duration, "duration", "30m", "How long the timer should be") flag.StringVar(&offset, "offset", "0m", "How much time the initial time should skip") flag.StringVar(&alertMessage, "msg", "Timer up", "Message to display on timer expiration") flag.StringVar(&interval, "interval", "500ms", "Interval duration") flag.BoolVar(&eta, "eta", false, "Whether to display the ending time") flag.Parse() tickerInterval, err := time.ParseDuration(interval) if err != nil { panic(err) } monitor := time.NewTicker(tickerInterval) defer monitor.Stop() base, err := time.ParseDuration(duration) if err != nil { panic(err) } var cur time.Duration if invert { cur = time.Duration(0) } else { cur = base } skip, err := time.ParseDuration(offset) if err != nil { panic(err) } if invert { cur += skip } else { cur -= skip } stop := make(chan bool) pause := make(chan bool) confirm := make(chan bool) paused := false delim := ':' logger.Log("Quit with 'q', Pause with 'p', Reset with 'r'") keych := keyChannel() go func() { last := time.Now() printTime(&cur, base, &delim, invert, repeat, eta) for { select { case <-monitor.C: difference := time.Duration(time.Now().UnixNano() - last.UnixNano()) last = time.Now() if difference > time.Hour*2 { keych <- 'p' continue } else if invert { cur += difference } else { cur -= difference } case state := <-pause: if !state { monitor = time.NewTicker(tickerInterval) last = time.Now() } else { monitor.Stop() } case <-stop: logger.Logf() confirm <- true return } printTime(&cur, base, &delim, invert, repeat, eta) } }() for { b := <-keych switch b { case 'q': stop <- true <-confirm return case 'p': paused = !paused pause <- paused case 'r': skip = time.Duration(0) originalStart = time.Now() if invert { cur = time.Duration(0) } else { cur = base } notified = false printTime(&cur, base, &delim, invert, repeat, eta) case 'z': logger.Logf() printTime(&cur, base, &delim, invert, repeat, eta) } } } func printTime(pRemains *time.Duration, target time.Duration, delim *rune, reverse bool, repeat bool, eta bool) { var final string remains := *pRemains sec := remains.Seconds() min := int(sec / 60.0) seconds := int(sec) % 60 hrs := min / 60 min = min % 60 if *delim == ':' { *delim = ' ' } else { *delim = ':' } if reverse { remains := target - remains rMin := int(remains.Seconds() / 60.0) rSec := int(remains.Seconds()) % 60 rHrs := rMin / 60 rMin = rMin % 60 tdelim := byte(*delim) if remains < 0 { rMin = 0 rSec = 0 rHrs = 0 tdelim = ':' go alertTime(pRemains, target, reverse, repeat) } at := fmt.Sprintf("%2d%c%02d%c%02d ", hrs, byte(*delim), min, byte(*delim), seconds) rem := fmt.Sprintf("%2d%c%02d%c%02d ", rHrs, tdelim, rMin, tdelim, rSec) rem = fmt.Sprintf("%s \tAt: %s ", rem, at) if eta { rem = fmt.Sprintf("%s \tETA: %s", rem, time.Unix(0, (time.Now().UnixNano()+target.Nanoseconds()-pRemains.Nanoseconds())).Format("3:04")) } final = rem } else { rem := fmt.Sprintf("%2d%c%02d%c%02d ", hrs, byte(*delim), min, byte(*delim), seconds) final = rem + " " if eta { final = fmt.Sprintf("%s \tETA: %s", final, time.Unix(0, time.Now().UnixNano()+pRemains.Nanoseconds()).Format("3:04")) } if remains < 0 { go alertTime(pRemains, target, reverse, repeat) } } logger.Logf("\r%s", final) } func alertTime(pRemains *time.Duration, target time.Duration, reverse, repeat bool) { if !notified { notified = true _, err := mack.Alert(fmt.Sprintf("%s\nTimer for %s done", alertMessage, target.String())) if err != nil { panic(err) } if repeat { originalStart = time.Now() if reverse { for *pRemains > 0 { *pRemains -= target } *pRemains += target } else { for *pRemains < 0 { *pRemains += target } } notified = false } } } func keyChannel() chan rune { ch := make(chan rune, 20) go func() { if err := keyboard.Open(); err != nil { panic(err) } for { b, _, err := keyboard.GetKey() if err != nil { panic(err) } by := rune(b) if by == 'q' { keyboard.Close() ch <- by return } ch <- by } }() return ch }