diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-18 09:00:35 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-18 09:00:35 +0200 |
| commit | 88f4e239a7521112a4db8c7842e3a05db4446cd4 (patch) | |
| tree | 8c331f9f2e23ad9c9319d6dc8275205b23ce811a /internal/display/display.go | |
| parent | 11204092b5ab5dc0f71515adfcaa6f07111363e5 (diff) | |
feat: triple-toggle CPU display mode via 1 key; add tooltip, font, hit-test
CPU display now cycles through three states with each press of 1:
0 = CPUModeAverage – aggregate bar only (default)
1 = CPUModeCores – individual core bars + aggregate
2 = CPUModeOff – all CPU bars hidden
Config file stores cpumode=N (integer); old showcores=0/1 is read for
backward compatibility. CLI flag --showcores replaced by --cpumode.
Other improvements landed in this commit:
- internal/display: add font.go (text rendering), hittest.go (bar hit
testing), tooltip.go (mouse-over tooltip), tooltip_test.go
- internal/display: mouse tracking and drawOverlay hook in display.go
- internal/display: update build tags to //go:build form
- internal/collector: embed remote script via script_embed.go /
scriptdata/loadbars-remote.sh
- internal/collector: CPULine.Total() changed to value receiver
- internal/collector: table test improvements (name field, t.Run)
- internal/constants: BytesPerSec consts promoted from var to const
- Magefile.go: fix error formatting and install path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/display/display.go')
| -rw-r--r-- | internal/display/display.go | 90 |
1 files changed, 58 insertions, 32 deletions
diff --git a/internal/display/display.go b/internal/display/display.go index 749c26c..47f8f10 100644 --- a/internal/display/display.go +++ b/internal/display/display.go @@ -17,7 +17,9 @@ import ( "github.com/veandco/go-sdl2/sdl" ) -const smoothFactor = 0.12 // blend toward target each frame; lower = smoother +// smoothFactor controls how quickly bars blend toward their target values each frame. +// Lower values produce smoother animations. +const smoothFactor = 0.12 // linkScales lists the supported network link speeds in ascending order, // used by the f/v hotkeys to cycle through link scale values. @@ -27,7 +29,7 @@ var linkScales = []string{"mbit", "10mbit", "100mbit", "gbit", "10gbit"} type runState struct { showAvgLine bool showIOAvgLine bool - showCores bool + cpuMode int // constants.CPUModeAverage / CPUModeCores / CPUModeOff showMem bool showNet bool showSeparators bool @@ -40,27 +42,9 @@ type runState struct { smoothedNet map[string]*struct{ rxPct, txPct float64 } prevNet map[string]stats.NetStamp // aggregated (summed) previous net stamp per host peakHistory map[string][]float64 -} - -// newRunState builds initial run state from config. -func newRunState(cfg *config.Config, winW, winH int32) *runState { - return &runState{ - showAvgLine: cfg.ShowAvgLine, - showIOAvgLine: cfg.ShowIOAvgLine, - showCores: cfg.ShowCores, - showMem: cfg.ShowMem, - showNet: cfg.ShowNet, - showSeparators: cfg.ShowSeparators, - extended: cfg.Extended, - winW: winW, - winH: winH, - prevCPU: make(map[string]collector.CPULine), - smoothedCPU: make(map[string]*[10]float64), - smoothedMem: make(map[string]*struct{ ramUsed, swapUsed float64 }), - smoothedNet: make(map[string]*struct{ rxPct, txPct float64 }), - prevNet: make(map[string]stats.NetStamp), - peakHistory: make(map[string][]float64), - } + mouseX int32 // last known mouse X position (for tooltip hit testing) + mouseY int32 // last known mouse Y position (for tooltip hit testing) + mouseLastMove time.Time // timestamp of last mouse movement; tooltip hidden after 3s idle } // Run runs the SDL display loop until ctx is cancelled or user presses 'q'. @@ -111,6 +95,29 @@ func Run(ctx context.Context, cfg *config.Config, src stats.Source) error { } } +// newRunState builds initial run state from config. +func newRunState(cfg *config.Config, winW, winH int32) *runState { + return &runState{ + showAvgLine: cfg.ShowAvgLine, + showIOAvgLine: cfg.ShowIOAvgLine, + cpuMode: cfg.CPUMode, + showMem: cfg.ShowMem, + showNet: cfg.ShowNet, + showSeparators: cfg.ShowSeparators, + extended: cfg.Extended, + winW: winW, + winH: winH, + prevCPU: make(map[string]collector.CPULine), + smoothedCPU: make(map[string]*[10]float64), + smoothedMem: make(map[string]*struct{ ramUsed, swapUsed float64 }), + smoothedNet: make(map[string]*struct{ rxPct, txPct float64 }), + prevNet: make(map[string]stats.NetStamp), + peakHistory: make(map[string][]float64), + mouseX: -1, // off-screen until first mouse move + mouseY: -1, + } +} + func clampInt(v, min, max int) int { if v < min { return min @@ -134,6 +141,9 @@ func handleEvents(window *sdl.Window, cfg *config.Config, state *runState) bool if handleKey(ev.Keysym.Sym, window, cfg, state) { return true } + case *sdl.MouseMotionEvent: + state.mouseX, state.mouseY = ev.X, ev.Y + state.mouseLastMove = time.Now() case *sdl.WindowEvent: if ev.Event == sdl.WINDOWEVENT_RESIZED { state.winW, state.winH = ev.Data1, ev.Data2 @@ -149,8 +159,16 @@ func handleKey(sym sdl.Keycode, window *sdl.Window, cfg *config.Config, state *r case sdl.K_q: return true case sdl.K_1: - state.showCores = !state.showCores - fmt.Println("==> Toggled show cores:", state.showCores) + // Cycle through three CPU display modes: average → all cores → off → average + state.cpuMode = (state.cpuMode + 1) % constants.CPUModeCount + switch state.cpuMode { + case constants.CPUModeAverage: + fmt.Println("==> CPU: average bar only") + case constants.CPUModeCores: + fmt.Println("==> CPU: individual cores") + case constants.CPUModeOff: + fmt.Println("==> CPU: off") + } case sdl.K_2, sdl.K_m: state.showMem = !state.showMem fmt.Println("==> Toggled show mem:", state.showMem) @@ -194,7 +212,7 @@ func handleKey(sym sdl.Keycode, window *sdl.Window, cfg *config.Config, state *r case sdl.K_w: cfg.ShowAvgLine = state.showAvgLine cfg.ShowIOAvgLine = state.showIOAvgLine - cfg.ShowCores = state.showCores + cfg.CPUMode = state.cpuMode cfg.ShowMem = state.showMem cfg.ShowNet = state.showNet cfg.ShowSeparators = state.showSeparators @@ -270,7 +288,7 @@ func barRect(winW, winH int32, numBars, maxPerRow, barIndex int) (x, y, w, h int // When showAvgLine/showIOAvgLine are enabled, global average lines are drawn on top. func drawFrame(renderer *sdl.Renderer, src stats.Source, cfg *config.Config, state *runState) { snap := src.Snapshot() - numBars := countBars(snap, state.showCores, state.showMem, state.showNet) + numBars := countBars(snap, state.cpuMode, state.showMem, state.showNet) // Always clear the entire window before drawing. SDL2 uses double-buffering, // so skipping clear leaves stale content in the back buffer. renderer.SetDrawColor(0, 0, 0, 255) @@ -282,13 +300,15 @@ func drawFrame(renderer *sdl.Renderer, src stats.Source, cfg *config.Config, sta if state.showIOAvgLine { drawGlobalIOAvgLine(renderer, snap, state, numBars, cfg.MaxBarsPerRow) } + // Draw mouse-over tooltip and host highlight inversion on top of all bars + drawOverlay(renderer, snap, cfg, state) } -func countBars(snap map[string]*stats.HostStats, showCores, showMem, showNet bool) int { +func countBars(snap map[string]*stats.HostStats, cpuMode int, showMem, showNet bool) int { n := 0 for _, host := range sortedHosts(snap) { if h := snap[host]; h != nil { - n += len(sortedCPUNames(h.CPU, showCores)) + n += len(sortedCPUNames(h.CPU, cpuMode)) if showMem { n++ } @@ -430,7 +450,7 @@ func drawGlobalIOAvgLine(renderer *sdl.Renderer, snap map[string]*stats.HostStat // drawHostBars draws CPU, mem, and net bars for one host and advances barIndex. // maxPerRow controls multi-row wrapping (0 = single row). func drawHostBars(renderer *sdl.Renderer, h *stats.HostStats, host string, cfg *config.Config, state *runState, numBars, maxPerRow int, barIndex *int) { - cpuNames := sortedCPUNames(h.CPU, state.showCores) + cpuNames := sortedCPUNames(h.CPU, state.cpuMode) for _, name := range cpuNames { key := host + ";" + name cur := h.CPU[name] @@ -506,14 +526,20 @@ func sortedHosts(snap map[string]*stats.HostStats) []string { return out } -func sortedCPUNames(cpu map[string]collector.CPULine, showCores bool) []string { +func sortedCPUNames(cpu map[string]collector.CPULine, cpuMode int) []string { + // CPUModeOff: hide all CPU bars + if cpuMode == constants.CPUModeOff { + return nil + } var names []string for name := range cpu { if name == "cpu" { + // Aggregate bar always shown unless CPUModeOff names = append(names, "cpu") continue } - if showCores { + // Individual core bars only shown in CPUModeCores + if cpuMode == constants.CPUModeCores { names = append(names, name) } } |
