/* Pominal Copyright (C) 2019 Daniel Anglin This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package main import ( "flag" "fmt" "math" "os" "os/exec" "os/signal" "syscall" "time" ) const ( workTimerLabel string = "Work" shortBreakTimerLabel string = "Short break" longBreakTimerLabel string = "Long break" ) var ( workTimer string shortBreakTimer string longBreakTimer string maxWorkCycles int printVersion bool ) func init() { flag.StringVar(&workTimer, "work-timer", "25m", "sets the timer for your work session.") flag.StringVar(&shortBreakTimer, "short-break-timer", "5m", "sets the timer for your short break.") flag.StringVar(&longBreakTimer, "long-break-timer", "20m", "sets the timer for your long break.") flag.IntVar(&maxWorkCycles, "max-work-cycles", 4, "sets the maximum number of work cycles to complete before taking a long break.") flag.BoolVar(&printVersion, "version", false, "print version and exit.") flag.Parse() } type Pominal struct { timer *time.Timer finish time.Time work time.Duration shortBreak time.Duration longBreak time.Duration maxWorkCycles int session int label string } func main() { if printVersion { Version() os.Exit(0) } workTimeDuration, err := time.ParseDuration(workTimer) if err != nil { fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) os.Exit(1) } shortBreakTimeDuration, err := time.ParseDuration(shortBreakTimer) if err != nil { fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) os.Exit(1) } longBreakTimeDuration, err := time.ParseDuration(longBreakTimer) if err != nil { fmt.Printf("ERROR: Unable to set the work timer. %s", err.Error()) os.Exit(1) } pominal := NewPominal(workTimeDuration, shortBreakTimeDuration, longBreakTimeDuration, maxWorkCycles) pominal.Run() } // NewPominal creates a new pominal instance func NewPominal(w, s, l time.Duration, m int) Pominal { return Pominal{ work: w, shortBreak: s, longBreak: l, maxWorkCycles: m, session: 1, } } // Run Pominal func (p *Pominal) Run() { workCycleCount := 1 t := time.NewTicker(1 * time.Second) s := make(chan os.Signal, 1) signal.Notify(s, syscall.SIGINT, syscall.SIGTERM) p.Start(workTimerLabel, workCycleCount) infinite: for { select { case sig := <-s: fmt.Printf("\n\nReceived signal '%s'. Closing Pominal.\n", sig) p.timer.Stop() t.Stop() break infinite case <-t.C: printScreen(p.TimeRemaining(), p.session, workCycleCount, p.maxWorkCycles, p.label) case <-p.timer.C: fmt.Printf("\n%s timer has finished\n", p.label) p.timer.Stop() t.Stop() time.Sleep(2 * time.Second) switch p.label { case workTimerLabel: if workCycleCount >= p.maxWorkCycles { p.Start(longBreakTimerLabel, workCycleCount) } else { p.Start(shortBreakTimerLabel, workCycleCount) } case shortBreakTimerLabel: workCycleCount++ p.Start(workTimerLabel, workCycleCount) case longBreakTimerLabel: workCycleCount = 1 p.IncrementCount() p.Start(workTimerLabel, workCycleCount) } t = time.NewTicker(1 * time.Second) } } } // Start begins the timer specified by the // label argument. func (p *Pominal) Start(label string, workCycleCount int) { var d time.Duration switch label { case workTimerLabel: d = p.work case shortBreakTimerLabel: d = p.shortBreak case longBreakTimerLabel: d = p.longBreak } p.label = label if p.timer == nil { p.timer = time.NewTimer(d) } else { p.timer.Reset(d) } p.finish = time.Now().Add(d) } // IncrementCount increments the pominal session count func (p *Pominal) IncrementCount() { p.session++ } // TimeRemaining returns the remaining time left // on the timer func (p *Pominal) TimeRemaining() float64 { return p.finish.Sub(time.Now()).Seconds() } // printScreen prints the details of the Pominal session on screen including // the current work cycle number, the timer's current label and the time remaining // on the timer. func printScreen(remaining float64, pominalCount, workCycle, maxWorkCycle int, label string) { clearScreen() remainingSecs := int(math.Ceil(remaining)) fmt.Printf("Pominal session: %d\nWork cycle: %d of %d\n\n", pominalCount, workCycle, maxWorkCycle) fmt.Printf("Timer: %s\nTime remaining: %02d:%02d", label, (remainingSecs/60)%60, remainingSecs%60) } // clearScreen clears the terminal screen func clearScreen() { cmd := exec.Command("clear") cmd.Stdout = os.Stdout cmd.Run() }