Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified tmlshock
Binary file not shown.
58 changes: 56 additions & 2 deletions util/stopwatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,54 @@ import (
"github.com/nsf/termbox-go"
"github.com/urfave/cli/v2"
"os"
"sync"
"time"
)

var SmallStopWatchDiff = [11]int{7, 6, 6, 7, 6, 6, 7, 6, 4, 7, 7}
var SmallStopWatchWithoutHourDiff = [8]int{7, 6, 6, 7, 6, 4, 7, 7}

// stopwatchState tracks the running/paused state so the space bar can
// pause, resume, or reset-and-restart the stopwatch from the event loop
// while the render goroutine keeps reading the current elapsed time.
type stopwatchState struct {
mu sync.Mutex
running bool
elapsed time.Duration
lastStart time.Time
}

func (s *stopwatchState) current() time.Duration {
s.mu.Lock()
defer s.mu.Unlock()
if s.running {
return s.elapsed + time.Since(s.lastStart)
}
return s.elapsed
}

// togglePauseResume pauses a running stopwatch or resumes a paused one
// from where it left off.
func (s *stopwatchState) togglePauseResume() {
s.mu.Lock()
defer s.mu.Unlock()
if s.running {
s.elapsed += time.Since(s.lastStart)
s.running = false
} else {
s.lastStart = time.Now()
s.running = true
}
}

func (s *stopwatchState) resetAndStart() {
s.mu.Lock()
defer s.mu.Unlock()
s.elapsed = 0
s.lastStart = time.Now()
s.running = true
}

func Stopwatch(cCtx *cli.Context) error {
err := termbox.Init()
if err != nil {
Expand All @@ -27,11 +69,12 @@ func Stopwatch(cCtx *cli.Context) error {
}
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

state := &stopwatchState{running: true, lastStart: time.Now()}

go func() {
start := time.Now()
for {
termWidth, termHeight := termbox.Size()
current := time.Since(start).Round(time.Millisecond)
current := state.current().Round(time.Millisecond)
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
NowTime := ""
if cCtx.String("disable-hour") == "true" {
Expand Down Expand Up @@ -65,6 +108,8 @@ func Stopwatch(cCtx *cli.Context) error {
}
}()

var lastSpacePress time.Time

mainLoop:
for {
ev := termbox.PollEvent()
Expand All @@ -75,6 +120,15 @@ mainLoop:
break mainLoop
case ev.Key == termbox.KeyCtrlC:
break mainLoop
case ev.Key == termbox.KeySpace:
now := time.Now()
if !lastSpacePress.IsZero() && now.Sub(lastSpacePress) <= doublePressWindow {
state.resetAndStart()
lastSpacePress = time.Time{}
} else {
state.togglePauseResume()
lastSpacePress = now
}
}
case termbox.EventError:
os.Exit(1)
Expand Down
61 changes: 58 additions & 3 deletions util/timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/urfave/cli/v2"
"os"
"strconv"
"sync"
"time"
)

Expand All @@ -15,6 +16,48 @@ var SmallTimerWithoutHourDiff = [11]int{7, 6, 6, 7, 6, 4, 7, 7}
var SmallTimerWithoutHourAndMillisecondDiff = [11]int{7, 6, 6, 7, 6}
var SmallTimerWithoutMillisecondDiff = [11]int{7, 6, 6, 7, 6, 6, 7, 6}

// timerState tracks the running/paused state so the space bar can pause,
// resume, or reset-and-restart the countdown from the event loop while the
// render goroutine keeps reading the current remaining time.
type timerState struct {
mu sync.Mutex
running bool
elapsed time.Duration
lastStart time.Time
}

func (s *timerState) remaining(total time.Duration) time.Duration {
s.mu.Lock()
defer s.mu.Unlock()
elapsed := s.elapsed
if s.running {
elapsed += time.Since(s.lastStart)
}
return total - elapsed
}

// togglePauseResume pauses a running countdown or resumes a paused one
// from where it left off.
func (s *timerState) togglePauseResume() {
s.mu.Lock()
defer s.mu.Unlock()
if s.running {
s.elapsed += time.Since(s.lastStart)
s.running = false
} else {
s.lastStart = time.Now()
s.running = true
}
}

func (s *timerState) resetAndStart() {
s.mu.Lock()
defer s.mu.Unlock()
s.elapsed = 0
s.lastStart = time.Now()
s.running = true
}

func Timer(cCtx *cli.Context) error {
err := termbox.Init()
if err != nil {
Expand Down Expand Up @@ -45,12 +88,13 @@ func Timer(cCtx *cli.Context) error {
if cCtx.String("colon-color") == "" && cCtx.String("color") != "" {
colonColor = FlagColor(cCtx.String("color"))
}
duration := time.Duration(total) * time.Second
state := &timerState{running: true, lastStart: time.Now()}

go func() {
duration := time.Duration(total) * time.Second
end := time.Now().Add(duration)
for {
termWidth, termHeight := termbox.Size()
current := time.Until(end)
current := state.remaining(duration)
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
NowTime := ""

Expand Down Expand Up @@ -104,6 +148,8 @@ func Timer(cCtx *cli.Context) error {
}
}()

var lastSpacePress time.Time

mainLoop:
for {
ev := termbox.PollEvent()
Expand All @@ -114,6 +160,15 @@ mainLoop:
break mainLoop
case ev.Key == termbox.KeyCtrlC:
break mainLoop
case ev.Key == termbox.KeySpace:
now := time.Now()
if !lastSpacePress.IsZero() && now.Sub(lastSpacePress) <= doublePressWindow {
state.resetAndStart()
lastSpacePress = time.Time{}
} else {
state.togglePauseResume()
lastSpacePress = now
}
}
case termbox.EventError:
os.Exit(1)
Expand Down
5 changes: 5 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import (
"github.com/nsf/termbox-go"
)

// doublePressWindow is the maximum gap between two space bar presses for
// them to count as a double press (reset and auto-restart) rather than
// two separate single presses (pause/resume).
const doublePressWindow = 500 * time.Millisecond

func StopwatchFormatTime(d time.Duration, disableMillisecond bool) string {
h := d / time.Hour
d -= h * time.Hour
Expand Down