diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a01956..158d254 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,9 +58,8 @@ jobs: env: GH_TOKEN: ${{ secrets.WORKFLOW_BRADRF_PAT }} - - uses: th0th/notify-discord@v0.4.1 - env: - DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} - GITHUB_ACTOR: ${{ github.actor }} - GITHUB_JOB_NAME: "${{ steps.release.outputs.url }}" - GITHUB_JOB_STATUS: ${{ job.status }} + - uses: sarisia/actions-status-discord@v1 + with: + webhook: ${{ secrets.DISCORD_WEBHOOK_URL }} + nodetail: true + title: "Released ${{ steps.release.outputs.url }}" diff --git a/.vscode/settings.json b/.vscode/settings.json index 3a83432..5eb2977 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,8 @@ "https://json.schemastore.org/github-workflow.json": "file:///workspaces/huekeys/.github/workflows/build.yml" }, "cSpell.words": [ + "eventfile", + "eventpath", "huekeys", "sockpath", "systray", diff --git a/cmd/root.go b/cmd/root.go index 5a90c5f..fc474b2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -132,11 +132,15 @@ func atStart(cmd *cobra.Command, _ []string) error { } else { viper.OnConfigChange(func(e fsnotify.Event) { confLogLevel := viper.GetString("log-level") - level, err := zerolog.ParseLevel(confLogLevel) + newLevel, err := zerolog.ParseLevel(confLogLevel) if err != nil { log.Err(err).Str("level", confLogLevel).Msg("unable to parse new log level") } else { - zerolog.SetGlobalLevel(level) + origLevel := zerolog.GlobalLevel() + if origLevel != newLevel { + zerolog.SetGlobalLevel(newLevel) + log.Info().Str("from", origLevel.String()).Str("to", confLogLevel).Msg("log level changed") + } } }) diff --git a/go.mod b/go.mod index e960b49..d85a7db 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( go.opentelemetry.io/otel/trace v1.7.0 // indirect go.uber.org/multierr v1.8.0 // indirect go.uber.org/zap v1.21.0 // indirect - golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect + golang.org/x/sys v0.0.0-20220702020025-31831981b65f // indirect gopkg.in/ini.v1 v1.66.6 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 8c05d41..34f89b4 100644 --- a/go.sum +++ b/go.sum @@ -388,8 +388,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8= -golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220702020025-31831981b65f h1:xdsejrW/0Wf2diT5CPp3XmKUNbr7Xvw8kYilQ+6qjRY= +golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/pkg/patterns/typing.go b/pkg/patterns/typing.go index 36942b3..10dd424 100644 --- a/pkg/patterns/typing.go +++ b/pkg/patterns/typing.go @@ -6,7 +6,6 @@ import ( "context" "encoding/binary" "fmt" - "io" "os" "regexp" "strings" @@ -25,6 +24,7 @@ import ( type TypingPattern struct { BasePattern + eventfile *os.File lastReportAt time.Time lastReadAt time.Time } @@ -77,19 +77,33 @@ func (p *TypingPattern) run() error { } eventpath := "/dev/input/" + inputEventID - f, err := os.Open(eventpath) - if err != nil { - return fmt.Errorf("can't open input events device (%s): %w", eventpath, err) - } keyPressCount := int32(0) - err = keyboard.ColorFileHandler(coldHotColors[0]) + err := keyboard.ColorFileHandler(coldHotColors[0]) if err != nil { return err } go p.setColor(&keyPressCount) - go p.processTypingEvents(f, &keyPressCount) + + err = p.startTypingProcessor(eventpath, &keyPressCount) + if err != nil { + return err + } + + // defer as a closure to make sure we close the most recently set eventfile + defer func() { + p.eventfile.Close() + }() + + wokeWatcher := util.StartWokeWatch(10*time.Second, time.Second, func(diff time.Duration) { + p.log.Warn().Dur("diff", diff).Msg("woke detected: reopening input") + err := p.startTypingProcessor(eventpath, &keyPressCount) + if err != nil { + p.log.Err(err).Msg("typing processor failed to reopen input") + } + }) + defer wokeWatcher.Stop() <-p.ctx.Done() p.stopRequested = true @@ -193,13 +207,28 @@ func (p *TypingPattern) setColor(keyPressCount *int32) { } } -func (p *TypingPattern) processTypingEvents(eventF io.Reader, keyPressCount *int32) { +func (p *TypingPattern) startTypingProcessor(eventpath string, keyPressCount *int32) error { + if p.eventfile != nil { + p.eventfile.Close() + } + + var err error + p.eventfile, err = os.Open(eventpath) + if err != nil { + return fmt.Errorf("can't open input events device (%s): %w", eventpath, err) + } + + go p.processTypingEvents(keyPressCount) + return nil +} + +func (p *TypingPattern) processTypingEvents(keyPressCount *int32) { defer util.LogRecover() // https://janczer.github.io/work-with-dev-input/ buf := make([]byte, 24) for !p.stopRequested { - _, err := eventF.Read(buf) + _, err := p.eventfile.Read(buf) if err != nil { p.log.Err(err).Msg("can't read input events device") return diff --git a/pkg/util/woke.go b/pkg/util/woke.go new file mode 100644 index 0000000..a018516 --- /dev/null +++ b/pkg/util/woke.go @@ -0,0 +1,49 @@ +package util + +import "time" + +type Woke struct { + delay time.Duration + diffMin time.Duration + onWake WokeFunc + stop chan bool +} + +type WokeFunc func(diff time.Duration) + +func StartWokeWatch(delay time.Duration, diffMin time.Duration, onWake WokeFunc) *Woke { + w := &Woke{ + delay: delay, + diffMin: diffMin, + onWake: onWake, + stop: make(chan bool, 1), + } + + go w.start() + + return w +} + +func (w *Woke) Stop() { + w.stop <- true +} + +func (w *Woke) start() { + for { + timer := time.NewTimer(w.delay) + start := time.Now() + select { + case <-w.stop: + if !timer.Stop() { + <-timer.C + } + return + case <-timer.C: + elapsed := time.Since(start) + diff := elapsed - w.delay + if diff > w.diffMin { + w.onWake(diff) + } + } + } +}