summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/collector/collector.go152
-rw-r--r--internal/collector/script_embed.go9
-rw-r--r--internal/config/config.go69
-rw-r--r--internal/display/display.go172
-rw-r--r--internal/display/display_test.go22
-rw-r--r--internal/display/font.go4
-rw-r--r--internal/display/tooltip.go6
-rw-r--r--internal/display/tooltip_test.go8
8 files changed, 257 insertions, 185 deletions
diff --git a/internal/collector/collector.go b/internal/collector/collector.go
index 91f3d7b..0107d61 100644
--- a/internal/collector/collector.go
+++ b/internal/collector/collector.go
@@ -20,13 +20,13 @@ type StatsStore interface {
SetNet(host, iface string, net NetLine, stamp float64)
}
-// Run starts a collector for one host: runs the embedded remote script (local or over SSH) and parses the stream into store.
-// Host may be "host" or "host:user". It runs until ctx is cancelled or the command exits.
-// The script is embedded in the binary; no external script file is required.
+// Run starts a collector for one host: runs the embedded remote script (local or over SSH)
+// and parses the output stream into store. Host may be "host" or "host:user".
+// It runs until ctx is cancelled or the command exits.
func Run(ctx context.Context, host string, cfg *config.Config, store StatsStore) error {
hostKey, user := splitHostUser(host)
- // Select script: Only Linux supported for local monitoring
+ // Select script: only Linux is supported for local monitoring.
scriptBytes := LinuxScript
if isLocal(hostKey) {
scriptBytes = getLocalScript()
@@ -35,45 +35,67 @@ func Run(ctx context.Context, host string, cfg *config.Config, store StatsStore)
}
}
- script := bytes.NewReader(scriptBytes)
- var scanner *bufio.Scanner
+ var (
+ scanner *bufio.Scanner
+ cmd *exec.Cmd
+ err error
+ )
if isLocal(hostKey) {
- cmd := exec.CommandContext(ctx, "bash", "-s")
- cmd.Stdin = script
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return fmt.Errorf("%s: %w", hostKey, err)
- }
- if err := cmd.Start(); err != nil {
- return fmt.Errorf("%s: %w", hostKey, err)
- }
- defer cmd.Wait()
- scanner = bufio.NewScanner(stdout)
+ scanner, cmd, err = startLocalScanner(ctx, scriptBytes)
} else {
- args := []string{"-o", "StrictHostKeyChecking=no"}
- if cfg.SSHOpts != "" {
- args = append(args, strings.Fields(cfg.SSHOpts)...)
- }
- if user != "" {
- args = append(args, "-l", user)
- }
- args = append(args, hostKey, "bash -s")
- cmd := exec.CommandContext(ctx, "ssh", args...)
- cmd.Stdin = script
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return fmt.Errorf("%s: %w", hostKey, err)
- }
- if err := cmd.Start(); err != nil {
- return fmt.Errorf("%s: %w", hostKey, err)
- }
- defer cmd.Wait()
- scanner = bufio.NewScanner(stdout)
+ scanner, cmd, err = startRemoteScanner(ctx, hostKey, user, scriptBytes, cfg)
+ }
+ if err != nil {
+ return fmt.Errorf("%s: %w", hostKey, err)
}
+ defer cmd.Wait()
+
+ return parseCollectorStream(ctx, scanner, hostKey, store)
+}
+// startLocalScanner spawns "bash -s" with scriptBytes on stdin and returns a scanner
+// over its stdout along with the Cmd for deferred Wait.
+func startLocalScanner(ctx context.Context, scriptBytes []byte) (*bufio.Scanner, *exec.Cmd, error) {
+ cmd := exec.CommandContext(ctx, "bash", "-s")
+ cmd.Stdin = bytes.NewReader(scriptBytes)
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, nil, err
+ }
+ if err := cmd.Start(); err != nil {
+ return nil, nil, err
+ }
+ return bufio.NewScanner(stdout), cmd, nil
+}
+
+// startRemoteScanner spawns "ssh host bash -s" with scriptBytes on stdin and returns
+// a scanner over its stdout along with the Cmd for deferred Wait.
+func startRemoteScanner(ctx context.Context, hostKey, user string, scriptBytes []byte, cfg *config.Config) (*bufio.Scanner, *exec.Cmd, error) {
+ args := []string{"-o", "StrictHostKeyChecking=no"}
+ if cfg.SSHOpts != "" {
+ args = append(args, strings.Fields(cfg.SSHOpts)...)
+ }
+ if user != "" {
+ args = append(args, "-l", user)
+ }
+ args = append(args, hostKey, "bash -s")
+ cmd := exec.CommandContext(ctx, "ssh", args...)
+ cmd.Stdin = bytes.NewReader(scriptBytes)
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, nil, err
+ }
+ if err := cmd.Start(); err != nil {
+ return nil, nil, err
+ }
+ return bufio.NewScanner(stdout), cmd, nil
+}
+
+// parseCollectorStream reads lines from scanner and dispatches parsed stats to store.
+// Always collects all CPU lines (cpu, cpu0, cpu1, ...) so display can toggle per-core
+// view with key 1 without a reconnect.
+func parseCollectorStream(ctx context.Context, scanner *bufio.Scanner, hostKey string, store StatsStore) error {
mode := ""
- // Always collect all CPU lines (cpu, cpu0, cpu1, ...) so display can toggle per-core view with key 1
- cpustring := "cpu"
for scanner.Scan() {
select {
case <-ctx.Done():
@@ -85,38 +107,40 @@ func Run(ctx context.Context, host string, cfg *config.Config, store StatsStore)
mode = line
continue
}
- switch mode {
- case ModeLoadAvg:
- l := ParseLoadAvg(line)
- store.SetLoadAvg(hostKey, l.Load1, l.Load5, l.Load15)
- case ModeMemStats:
- if mem, ok := ParseMemLine(line); ok {
- store.SetMem(hostKey, mem.Key, mem.Value)
- }
- case ModeNetStats:
- if idx := strings.Index(line, ":"); idx >= 0 {
- iface := strings.TrimSpace(line[:idx])
- rest := line[idx+1:]
- net, err := ParseNetLine(iface + ":" + rest)
- if err != nil {
- continue
- }
+ dispatchCollectorLine(mode, line, hostKey, store)
+ }
+ if err := scanner.Err(); err != nil {
+ return fmt.Errorf("%s: read: %w", hostKey, err)
+ }
+ return nil
+}
+
+// dispatchCollectorLine routes one parsed line to the appropriate store setter
+// based on the current protocol mode marker.
+func dispatchCollectorLine(mode, line, hostKey string, store StatsStore) {
+ switch mode {
+ case ModeLoadAvg:
+ l := ParseLoadAvg(line)
+ store.SetLoadAvg(hostKey, l.Load1, l.Load5, l.Load15)
+ case ModeMemStats:
+ if mem, ok := ParseMemLine(line); ok {
+ store.SetMem(hostKey, mem.Key, mem.Value)
+ }
+ case ModeNetStats:
+ if idx := strings.Index(line, ":"); idx >= 0 {
+ iface := strings.TrimSpace(line[:idx])
+ net, err := ParseNetLine(iface + ":" + line[idx+1:])
+ if err == nil {
store.SetNet(hostKey, net.Iface, net, float64(time.Now().UnixNano())/1e9)
}
- case ModeCPUStats:
- if strings.HasPrefix(line, cpustring) {
- cu, err := ParseCPULine(line)
- if err != nil {
- continue
- }
+ }
+ case ModeCPUStats:
+ if strings.HasPrefix(line, "cpu") {
+ if cu, err := ParseCPULine(line); err == nil {
store.SetCPU(hostKey, cu.Name, cu)
}
}
}
- if err := scanner.Err(); err != nil {
- return fmt.Errorf("%s: read: %w", hostKey, err)
- }
- return nil
}
// splitHostUser splits "host:user" into (host, user). If no colon, returns (host, "").
diff --git a/internal/collector/script_embed.go b/internal/collector/script_embed.go
deleted file mode 100644
index 168ca37..0000000
--- a/internal/collector/script_embed.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package collector
-
-import _ "embed"
-
-// RemoteScript is the loadbars-remote.sh script embedded for local and SSH execution.
-// Path is relative to this file's directory (internal/collector).
-//
-//go:embed scriptdata/loadbars-remote.sh
-var RemoteScript []byte
diff --git a/internal/config/config.go b/internal/config/config.go
index 3625341..d9b367c 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -14,16 +14,16 @@ import (
// Config holds all loadbars configuration (file + CLI).
// Defaults match the Perl Shared.pm %C.
type Config struct {
- Hosts []string // Each entry is "host" or "host:user"
- Title string
- BarWidth int
- CPUAverage int
- Extended bool
- HasAgent bool
- Height int
- MaxWidth int
- NetAverage int
- NetLink string
+ Hosts []string // Each entry is "host" or "host:user"
+ Title string
+ BarWidth int
+ CPUAverage int
+ Extended bool
+ HasAgent bool
+ Height int
+ MaxWidth int
+ NetAverage int
+ NetLink string
ShowAvgLine bool
ShowIOAvgLine bool
CPUMode int // constants.CPUModeAverage / CPUModeCores / CPUModeOff
@@ -40,14 +40,14 @@ type Config struct {
// Default returns a Config with default values.
func Default() Config {
return Config{
- BarWidth: 1200,
- CPUAverage: 10,
- Extended: false,
- HasAgent: false,
- Height: 150,
- MaxWidth: 1900,
- NetAverage: 15,
- NetLink: "gbit",
+ BarWidth: 1200,
+ CPUAverage: 10,
+ Extended: false,
+ HasAgent: false,
+ Height: 150,
+ MaxWidth: 1900,
+ NetAverage: 15,
+ NetLink: "gbit",
CPUMode: constants.CPUModeAverage, // start with aggregate bar only
ShowMem: false,
ShowNet: false,
@@ -137,7 +137,14 @@ func (c *Config) parseReader(f *os.File) error {
return scanner.Err()
}
+// set applies a single key=value pair to the config, delegating to focused helpers.
func (c *Config) set(key, val string) {
+ c.setSizeAndTuning(key, val)
+ c.setDisplayFlags(key, val)
+}
+
+// setSizeAndTuning handles window dimension, SSH, sampling, and identity keys.
+func (c *Config) setSizeAndTuning(key, val string) {
switch key {
case "title":
c.Title = val
@@ -149,8 +156,6 @@ func (c *Config) set(key, val string) {
if n, err := strconv.Atoi(val); err == nil {
c.CPUAverage = n
}
- case "extended":
- c.Extended = parseBool(val)
case "hasagent":
c.HasAgent = parseBool(val)
case "height":
@@ -167,6 +172,22 @@ func (c *Config) set(key, val string) {
}
case "netlink":
c.NetLink = val
+ case "maxbarsperrow":
+ if n, err := strconv.Atoi(val); err == nil {
+ c.MaxBarsPerRow = n
+ }
+ case "sshopts":
+ c.SSHOpts = val
+ case "cluster":
+ c.Cluster = val
+ }
+}
+
+// setDisplayFlags handles keys that control what is shown and how CPU/load are scaled.
+func (c *Config) setDisplayFlags(key, val string) {
+ switch key {
+ case "extended":
+ c.Extended = parseBool(val)
case "showavgline":
c.ShowAvgLine = parseBool(val)
case "showioavgline":
@@ -196,14 +217,6 @@ func (c *Config) set(key, val string) {
}
case "showseparators":
c.ShowSeparators = parseBool(val)
- case "maxbarsperrow":
- if n, err := strconv.Atoi(val); err == nil {
- c.MaxBarsPerRow = n
- }
- case "sshopts":
- c.SSHOpts = val
- case "cluster":
- c.Cluster = val
}
}
diff --git a/internal/display/display.go b/internal/display/display.go
index 555e904..874abe3 100644
--- a/internal/display/display.go
+++ b/internal/display/display.go
@@ -49,6 +49,37 @@ type runState struct {
mouseLastMove time.Time // timestamp of last mouse movement; tooltip hidden after 3s idle
}
+// newRunState builds initial run state from config.
+// When cfg.LoadMax > 0 the load bar uses a fixed scale; otherwise it
+// starts at the auto-scale floor of 2.0 and tracks the live maximum.
+func newRunState(cfg *config.Config, winW, winH int32) *runState {
+ initLoadPeak := 2.0
+ if cfg.LoadMax > 0 {
+ initLoadPeak = cfg.LoadMax
+ }
+ return &runState{
+ showAvgLine: cfg.ShowAvgLine,
+ showIOAvgLine: cfg.ShowIOAvgLine,
+ cpuMode: cfg.CPUMode,
+ showMem: cfg.ShowMem,
+ showNet: cfg.ShowNet,
+ showLoad: cfg.ShowLoad,
+ loadPeak: initLoadPeak,
+ 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,
+ }
+}
+
// Run runs the SDL display loop until ctx is cancelled or user presses 'q'.
func Run(ctx context.Context, cfg *config.Config, src stats.Source) error {
if err := sdl.Init(sdl.INIT_VIDEO); err != nil {
@@ -97,37 +128,6 @@ 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,
- showLoad: cfg.ShowLoad,
- // Use the fixed cap when set; otherwise start at the auto-scale floor of 2.0.
- loadPeak: func() float64 {
- if cfg.LoadMax > 0 {
- return cfg.LoadMax
- }
- return 2.0
- }(),
- 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
@@ -164,10 +164,21 @@ func handleEvents(window *sdl.Window, cfg *config.Config, state *runState) bool
}
// handleKey handles one key press; returns true to quit.
+// handleKey handles one key press; returns true to quit.
+// It delegates to focused helpers for toggle, adjust/save, and resize keys.
func handleKey(sym sdl.Keycode, window *sdl.Window, cfg *config.Config, state *runState) bool {
- switch sym {
- case sdl.K_q:
+ if sym == sdl.K_q {
return true
+ }
+ handleToggleKeys(sym, cfg, state)
+ handleAdjustAndSave(sym, cfg, state)
+ handleResizeKeys(sym, window, cfg, state)
+ return false
+}
+
+// handleToggleKeys processes display-toggle hotkeys (1, 2/m, 3/n, 4/l, r, e, g, i, s).
+func handleToggleKeys(sym sdl.Keycode, cfg *config.Config, state *runState) {
+ switch sym {
case sdl.K_1:
// Cycle through three CPU display modes: average → all cores → off → average
state.cpuMode = (state.cpuMode + 1) % constants.CPUModeCount
@@ -209,6 +220,12 @@ func handleKey(sym sdl.Keycode, window *sdl.Window, cfg *config.Config, state *r
case sdl.K_s:
state.showSeparators = !state.showSeparators
fmt.Println("==> Toggled host separators:", state.showSeparators)
+ }
+}
+
+// handleAdjustAndSave processes sampling-adjust and config-write hotkeys (a, y, d, c, f, v, h, w).
+func handleAdjustAndSave(sym sdl.Keycode, cfg *config.Config, state *runState) {
+ switch sym {
case sdl.K_a:
cfg.CPUAverage++
fmt.Println("==> CPU average samples:", cfg.CPUAverage)
@@ -232,6 +249,7 @@ func handleKey(sym sdl.Keycode, window *sdl.Window, cfg *config.Config, state *r
case sdl.K_h:
printHotkeys()
case sdl.K_w:
+ // Copy mutable display state back to config before persisting.
cfg.ShowAvgLine = state.showAvgLine
cfg.ShowIOAvgLine = state.showIOAvgLine
cfg.CPUMode = state.cpuMode
@@ -245,6 +263,16 @@ func handleKey(sym sdl.Keycode, window *sdl.Window, cfg *config.Config, state *r
} else {
fmt.Println("==> Config written to ~/.loadbarsrc")
}
+ }
+}
+
+// handleResizeKeys processes window-resize hotkeys (arrow keys).
+// window may be nil in tests; the guard prevents a nil-pointer panic.
+func handleResizeKeys(sym sdl.Keycode, window *sdl.Window, cfg *config.Config, state *runState) {
+ if window == nil {
+ return
+ }
+ switch sym {
case sdl.K_LEFT:
state.winW -= 100
if state.winW < 1 {
@@ -267,7 +295,6 @@ func handleKey(sym sdl.Keycode, window *sdl.Window, cfg *config.Config, state *r
state.winH += 100
window.SetSize(state.winW, state.winH)
}
- return false
}
// barBounds calculates the x position and width for a bar at the given index.
@@ -657,16 +684,16 @@ func drawCPUBarFromPcts(renderer *sdl.Renderer, s *[10]float64, barW int32, x, y
renderer.SetDrawColor(r, g, b, 255)
renderer.FillRect(&sdl.Rect{X: x, Y: int32(curY), W: barW, H: hh})
}
- fill(constants.Blue.R, constants.Blue.G, constants.Blue.B, (*s)[0]) // system
- fill(constants.Yellow.R, constants.Yellow.G, constants.Yellow.B, (*s)[1]) // user
- fill(constants.Green.R, constants.Green.G, constants.Green.B, (*s)[2]) // nice
+ fill(constants.Blue.R, constants.Blue.G, constants.Blue.B, (*s)[0]) // system
+ fill(constants.Yellow.R, constants.Yellow.G, constants.Yellow.B, (*s)[1]) // user
+ fill(constants.Green.R, constants.Green.G, constants.Green.B, (*s)[2]) // nice
fill(constants.LimeGreen.R, constants.LimeGreen.G, constants.LimeGreen.B, (*s)[9]) // guestnice
- fill(constants.Black.R, constants.Black.G, constants.Black.B, (*s)[3]) // idle
- fill(constants.Purple.R, constants.Purple.G, constants.Purple.B, (*s)[4]) // iowait
- fill(constants.White.R, constants.White.G, constants.White.B, (*s)[5]) // irq
- fill(constants.White.R, constants.White.G, constants.White.B, (*s)[6]) // softirq
- fill(constants.Red.R, constants.Red.G, constants.Red.B, (*s)[7]) // guest
- fill(constants.Red.R, constants.Red.G, constants.Red.B, (*s)[8]) // steal
+ fill(constants.Black.R, constants.Black.G, constants.Black.B, (*s)[3]) // idle
+ fill(constants.Purple.R, constants.Purple.G, constants.Purple.B, (*s)[4]) // iowait
+ fill(constants.White.R, constants.White.G, constants.White.B, (*s)[5]) // irq
+ fill(constants.White.R, constants.White.G, constants.White.B, (*s)[6]) // softirq
+ fill(constants.Red.R, constants.Red.G, constants.Red.B, (*s)[7]) // guest
+ fill(constants.Red.R, constants.Red.G, constants.Red.B, (*s)[8]) // steal
// Extended: 1px peak line at max (system+user) over history
if extended && peakPct > 0 {
peakY := y + barH - int32(peakPct*pxPerPct)
@@ -828,6 +855,12 @@ func sumNonLoNet(h *stats.HostStats) (sum stats.NetStamp, hasIface bool) {
// Smoothed values and prevNet are only updated when new collector data arrives
// (cur.Stamp > prev.Stamp), so the bar holds steady between collector cycles
// instead of decaying toward zero on frames with no new data.
+// drawNetBarSmoothed sums RX/TX across all non-lo interfaces, computes utilization
+// vs link speed, smooths toward target, and draws one net bar (RX left from top, TX right from bottom).
+// The bar occupies the region (x, y) with dimensions (barW, barH).
+// Smoothed values and prevNet are only updated when new collector data arrives
+// (cur.Stamp > prev.Stamp), so the bar holds steady between collector cycles
+// instead of decaying toward zero on frames with no new data.
func drawNetBarSmoothed(renderer *sdl.Renderer, h *stats.HostStats, cfg *config.Config, smoothed *struct{ rxPct, txPct float64 }, prev stats.NetStamp, factor float64, barW int32, x, y, barH int32) stats.NetStamp {
// Clear this slot so we never leave previous (e.g. CPU/mem) content visible
renderer.SetDrawColor(constants.Black.R, constants.Black.G, constants.Black.B, 255)
@@ -845,31 +878,43 @@ func drawNetBarSmoothed(renderer *sdl.Renderer, h *stats.HostStats, cfg *config.
// target to 0 (no delta) and smooth the bar toward zero, making real
// traffic invisible.
if cur.Stamp > prev.Stamp && prev.Stamp > 0 {
- linkBps := netLinkBytesPerSec(cfg)
- if linkBps <= 0 {
- linkBps = int64(constants.BytesGbit)
- }
- dt := cur.Stamp - prev.Stamp
- if dt > 0 {
- deltaB := cur.B - prev.B
- deltaTb := cur.Tb - prev.Tb
- if deltaB < 0 {
- deltaB = 0
- }
- if deltaTb < 0 {
- deltaTb = 0
- }
- targetRx := 100 * float64(deltaB) / (float64(linkBps) * dt)
- targetTx := 100 * float64(deltaTb) / (float64(linkBps) * dt)
- smoothed.rxPct += (targetRx - smoothed.rxPct) * factor
- smoothed.txPct += (targetTx - smoothed.txPct) * factor
- }
- prev = cur // only advance prev when we consumed new data
+ prev = smoothNetUtilization(cur, prev, cfg, smoothed, factor)
} else if prev.Stamp == 0 {
// First sample: record it but don't draw yet (no delta available)
prev = cur
}
+ drawNetHalves(renderer, smoothed, x, y, barW, barH)
+ return prev
+}
+
+// smoothNetUtilization computes RX/TX utilization deltas and blends them into smoothed.
+// Returns the updated previous stamp (cur) so callers can advance the baseline.
+func smoothNetUtilization(cur, prev stats.NetStamp, cfg *config.Config, smoothed *struct{ rxPct, txPct float64 }, factor float64) stats.NetStamp {
+ linkBps := netLinkBytesPerSec(cfg)
+ if linkBps <= 0 {
+ linkBps = int64(constants.BytesGbit)
+ }
+ dt := cur.Stamp - prev.Stamp
+ if dt > 0 {
+ deltaB := cur.B - prev.B
+ deltaTb := cur.Tb - prev.Tb
+ if deltaB < 0 {
+ deltaB = 0
+ }
+ if deltaTb < 0 {
+ deltaTb = 0
+ }
+ targetRx := 100 * float64(deltaB) / (float64(linkBps) * dt)
+ targetTx := 100 * float64(deltaTb) / (float64(linkBps) * dt)
+ smoothed.rxPct += (targetRx - smoothed.rxPct) * factor
+ smoothed.txPct += (targetTx - smoothed.txPct) * factor
+ }
+ return cur // advance the baseline to the consumed sample
+}
+// drawNetHalves renders the RX (left half, from top) and TX (right half, from bottom)
+// filled rectangles for one network bar using pre-smoothed utilization percentages.
+func drawNetHalves(renderer *sdl.Renderer, smoothed *struct{ rxPct, txPct float64 }, x, y, barW, barH int32) {
halfW := barW / 2
pxPerPct := float64(barH) / 100.0
halfH := barH / 2
@@ -899,7 +944,6 @@ func drawNetBarSmoothed(renderer *sdl.Renderer, h *stats.HostStats, cfg *config.
renderer.SetDrawColor(constants.Black.R, constants.Black.G, constants.Black.B, 255)
renderer.FillRect(&sdl.Rect{X: x + halfW, Y: y, W: halfW, H: barH - txH})
}
- return prev
}
// updateLoadPeak maintains the load scale used by the bar renderer.
diff --git a/internal/display/display_test.go b/internal/display/display_test.go
index 7d3e82e..16a7700 100644
--- a/internal/display/display_test.go
+++ b/internal/display/display_test.go
@@ -224,8 +224,8 @@ func TestMemBar_RamAndSwap(t *testing.T) {
"cpu": {User: 100, System: 100, Idle: 800}, // needed so countBars > 0
},
Mem: map[string]int64{
- "MemTotal": 1000,
- "MemFree": 400, // 60% used
+ "MemTotal": 1000,
+ "MemFree": 400, // 60% used
"SwapTotal": 1000,
"SwapFree": 600, // 40% used
},
@@ -385,13 +385,13 @@ func TestMultiHost_BarCount(t *testing.T) {
data: map[string]*stats.HostStats{
"alpha": {
CPU: map[string]collector.CPULine{"cpu": alphaCur},
- Mem: map[string]int64{"MemTotal": 100, "MemFree": 50, "SwapTotal": 0, "SwapFree": 0},
- Net: map[string]stats.NetStamp{"eth0": {B: 0, Tb: 0, Stamp: 1.0}},
+ Mem: map[string]int64{"MemTotal": 100, "MemFree": 50, "SwapTotal": 0, "SwapFree": 0},
+ Net: map[string]stats.NetStamp{"eth0": {B: 0, Tb: 0, Stamp: 1.0}},
},
"beta": {
CPU: map[string]collector.CPULine{"cpu": betaCur},
- Mem: map[string]int64{"MemTotal": 100, "MemFree": 50, "SwapTotal": 0, "SwapFree": 0},
- Net: map[string]stats.NetStamp{"eth0": {B: 0, Tb: 0, Stamp: 1.0}},
+ Mem: map[string]int64{"MemTotal": 100, "MemFree": 50, "SwapTotal": 0, "SwapFree": 0},
+ Net: map[string]stats.NetStamp{"eth0": {B: 0, Tb: 0, Stamp: 1.0}},
},
},
}
@@ -631,7 +631,7 @@ func newHotkeyTestEnv(t *testing.T, cpuMode int, showMem, showNet bool) (
"SwapFree": 600,
},
Net: map[string]stats.NetStamp{
- "eth0": {B: 12500000, Tb: 6250000, Stamp: 2.0},
+ "eth0": {B: 12500000, Tb: 6250000, Stamp: 2.0},
"wlan0": {B: 1000000, Tb: 500000, Stamp: 2.0},
},
},
@@ -1216,7 +1216,7 @@ func TestGlobalIOAvgLine_MultiHost(t *testing.T) {
defer surface.Free()
prev1, cur1 := makeCPUPairWithIO(10, 10, 20, 20, 5, 5) // 30% IO
- prev2, cur2 := makeCPUPair(40, 40, 20) // 0% IO
+ prev2, cur2 := makeCPUPair(40, 40, 20) // 0% IO
cfg := defaultTestConfig()
@@ -1554,9 +1554,9 @@ func TestMultiRow_DrawFrame(t *testing.T) {
cfg.MaxBarsPerRow = 2
prev1, cur1 := makeCPUPair(100, 0, 0) // all system → blue
- prev2, cur2 := makeCPUPair(0, 100, 0) // all user → yellow
- prev3, cur3 := makeCPUPair(0, 0, 100) // all idle → black
- prev4, cur4 := makeCPUPair(100, 0, 0) // all system → blue
+ prev2, cur2 := makeCPUPair(0, 100, 0) // all user → yellow
+ prev3, cur3 := makeCPUPair(0, 0, 100) // all idle → black
+ prev4, cur4 := makeCPUPair(100, 0, 0) // all system → blue
src := &mockSource{
data: map[string]*stats.HostStats{
diff --git a/internal/display/font.go b/internal/display/font.go
index d6dae18..e936c4c 100644
--- a/internal/display/font.go
+++ b/internal/display/font.go
@@ -6,8 +6,8 @@ import "github.com/veandco/go-sdl2/sdl"
// Each glyph is 7 rows of 5 bits (MSB = leftmost pixel).
const (
- glyphW = 5 // pixels per character width
- glyphH = 7 // pixels per character height
+ glyphW = 5 // pixels per character width
+ glyphH = 7 // pixels per character height
charGap = 1 // horizontal gap between characters
)
diff --git a/internal/display/tooltip.go b/internal/display/tooltip.go
index 77ee365..e612b2c 100644
--- a/internal/display/tooltip.go
+++ b/internal/display/tooltip.go
@@ -16,9 +16,9 @@ import (
const mouseIdleTimeout = 3 * time.Second
const (
- tooltipScale int32 = 2 // pixel scale for bitmap font
- tooltipPadX int32 = 6 // horizontal padding inside tooltip box
- tooltipPadY int32 = 4 // vertical padding inside tooltip box
+ tooltipScale int32 = 2 // pixel scale for bitmap font
+ tooltipPadX int32 = 6 // horizontal padding inside tooltip box
+ tooltipPadY int32 = 4 // vertical padding inside tooltip box
tooltipOffsetX int32 = 12 // offset from cursor to tooltip
tooltipOffsetY int32 = 12
)
diff --git a/internal/display/tooltip_test.go b/internal/display/tooltip_test.go
index e4f6afc..0d4dc57 100644
--- a/internal/display/tooltip_test.go
+++ b/internal/display/tooltip_test.go
@@ -261,10 +261,10 @@ func TestTooltipLines_Mem(t *testing.T) {
"myhost": {
CPU: map[string]collector.CPULine{"cpu": {}},
Mem: map[string]int64{
- "MemTotal": 8*1024*1024, // 8 GB in KB
- "MemFree": 2*1024*1024,
- "SwapTotal": 4*1024*1024,
- "SwapFree": 3*1024*1024,
+ "MemTotal": 8 * 1024 * 1024, // 8 GB in KB
+ "MemFree": 2 * 1024 * 1024,
+ "SwapTotal": 4 * 1024 * 1024,
+ "SwapFree": 3 * 1024 * 1024,
},
},
}