diff options
| -rw-r--r-- | internal/collector/collector.go | 152 | ||||
| -rw-r--r-- | internal/collector/script_embed.go | 9 | ||||
| -rw-r--r-- | internal/config/config.go | 69 | ||||
| -rw-r--r-- | internal/display/display.go | 172 | ||||
| -rw-r--r-- | internal/display/display_test.go | 22 | ||||
| -rw-r--r-- | internal/display/font.go | 4 | ||||
| -rw-r--r-- | internal/display/tooltip.go | 6 | ||||
| -rw-r--r-- | internal/display/tooltip_test.go | 8 |
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, }, }, } |
