//go:build windows package evidence import ( "context" "time" "keel/internal/winapi" ) // pollInterval is how often the Windows sensor samples the foreground window. // ~1s latency on a window switch is immaterial for a focus tracker, and polling // avoids the message-loop/callback machinery a SetWinEventHook source needs. const pollInterval = 750 * time.Millisecond // NewSource returns the Windows active-window sensor (polling). func NewSource() Source { return windowsSource{} } type windowsSource struct{} // Watch emits the current window immediately, then samples every pollInterval, // emitting only when the foreground window or its title changes. A read with no // foreground window yields an Unavailable snapshot (once, until it recovers). It // runs until ctx is cancelled. It never panics the daemon. func (windowsSource) Watch(ctx context.Context, onChange func(WindowSnapshot)) { var tr foregroundTracker poll := func() { hwnd, title, class, ok := winapi.ForegroundWindow() if !tr.changed(ok, hwnd, title) { return } if !ok { onChange(unavailable("no foreground window")) return } onChange(WindowSnapshot{Title: title, Class: class, Health: EvidenceHealth{Available: true}}) } poll() // immediate current window ticker := time.NewTicker(pollInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: poll() } } }