Tree items render a display string snapshotted at build time, so
changing a user's volume updated the gain but left the stale
percentage on screen. Rebuild the tree and refresh after volume
change and reset, matching what the mute hotkeys already do.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
User.AudioSource, Boost, Volume, and LocallyMuted were accessed from
both the OnAudioStream audio goroutine and the UI goroutine without
synchronization, a data race under the Go memory model.
Replace direct field access with thread-safe getter/setter methods
protected by a per-user mutex:
- SetAudioSource/GetAudioSource for the OpenAL source pointer
- SetBoost/Boost for the audio boost multiplier
- SetVolume/Volume for the volume level
- SetLocallyMuted/LocallyMuted for the local mute state
Update all call sites across config/, barnard.go, client.go,
ui_tree.go, and stream.go.
Make the F11 action menu close through a shared close action so Escape and the explicit Close actions menu item follow the same path. Treat Escape-prefixed Up and Down events as close inputs while the action menu is active, which handles termbox InputAlt behavior after pressing Escape. Preserve and restore the user/channel tree selection across action menu open and close, and preserve selection across live tree rebuilds.
Tested with: GOCACHE=/tmp/barnard-go-cache go test ./...