package evidence import ( "context" "time" ) // pollLoop samples the active window via read and forwards snapshots through // onChange, but only when the observation changes — a different window OR a // different title. The title case is the one that matters: switching a browser // tab changes the window title without changing the active window, so a sensor // that re-reads only on active-window events never sees it and credits the stale // title. Polling re-reads on a fixed cadence, so a tab switch is caught within // one interval. // // It emits once immediately, then samples every interval, until ctx is // cancelled. WindowSnapshot is comparable, so the change check is a plain // equality. Factoring the loop here keeps the change-dedup identical across // sensors and lets it be tested without a display. (The Windows sensor predates // this and keeps its own equivalent loop.) func pollLoop(ctx context.Context, interval time.Duration, read func() WindowSnapshot, onChange func(WindowSnapshot)) { var last WindowSnapshot var haveLast bool emit := func() { s := read() if haveLast && s == last { return } last, haveLast = s, true onChange(s) } emit() // immediate current window t := time.NewTicker(interval) defer t.Stop() for { select { case <-ctx.Done(): return case <-t.C: emit() } } }