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/font.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/font.go')
| -rw-r--r-- | internal/display/font.go | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/internal/display/font.go b/internal/display/font.go new file mode 100644 index 0000000..d6dae18 --- /dev/null +++ b/internal/display/font.go @@ -0,0 +1,248 @@ +package display + +import "github.com/veandco/go-sdl2/sdl" + +// Bitmap font: 5x7 pixel glyphs for ASCII 32–126, rendered via FillRect. +// Each glyph is 7 rows of 5 bits (MSB = leftmost pixel). + +const ( + glyphW = 5 // pixels per character width + glyphH = 7 // pixels per character height + charGap = 1 // horizontal gap between characters +) + +// font5x7 maps ASCII 32–126 to 7-byte bitmaps (one byte per row, top 5 bits used). +// Index 0 = space (0x20), index 94 = tilde (0x7E). +var font5x7 = [95][7]byte{ + // space (0x20) + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + // ! (0x21) + {0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20}, + // " (0x22) + {0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00}, + // # (0x23) + {0x50, 0xF8, 0x50, 0x50, 0x50, 0xF8, 0x50}, + // $ (0x24) + {0x20, 0x70, 0xA0, 0x70, 0x28, 0x70, 0x20}, + // % (0x25) + {0xC8, 0xC8, 0x10, 0x20, 0x40, 0x98, 0x98}, + // & (0x26) + {0x40, 0xA0, 0xA0, 0x40, 0xA8, 0x90, 0x68}, + // ' (0x27) + {0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00}, + // ( (0x28) + {0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10}, + // ) (0x29) + {0x40, 0x20, 0x10, 0x10, 0x10, 0x20, 0x40}, + // * (0x2A) + {0x00, 0x20, 0xA8, 0x70, 0xA8, 0x20, 0x00}, + // + (0x2B) + {0x00, 0x20, 0x20, 0xF8, 0x20, 0x20, 0x00}, + // , (0x2C) + {0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40}, + // - (0x2D) + {0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00}, + // . (0x2E) + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20}, + // / (0x2F) + {0x08, 0x08, 0x10, 0x20, 0x40, 0x80, 0x80}, + // 0 (0x30) + {0x70, 0x88, 0x98, 0xA8, 0xC8, 0x88, 0x70}, + // 1 (0x31) + {0x20, 0x60, 0x20, 0x20, 0x20, 0x20, 0x70}, + // 2 (0x32) + {0x70, 0x88, 0x08, 0x10, 0x20, 0x40, 0xF8}, + // 3 (0x33) + {0x70, 0x88, 0x08, 0x30, 0x08, 0x88, 0x70}, + // 4 (0x34) + {0x10, 0x30, 0x50, 0x90, 0xF8, 0x10, 0x10}, + // 5 (0x35) + {0xF8, 0x80, 0xF0, 0x08, 0x08, 0x88, 0x70}, + // 6 (0x36) + {0x30, 0x40, 0x80, 0xF0, 0x88, 0x88, 0x70}, + // 7 (0x37) + {0xF8, 0x08, 0x10, 0x20, 0x40, 0x40, 0x40}, + // 8 (0x38) + {0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70}, + // 9 (0x39) + {0x70, 0x88, 0x88, 0x78, 0x08, 0x10, 0x60}, + // : (0x3A) + {0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00}, + // ; (0x3B) + {0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x40}, + // < (0x3C) + {0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08}, + // = (0x3D) + {0x00, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x00}, + // > (0x3E) + {0x80, 0x40, 0x20, 0x10, 0x20, 0x40, 0x80}, + // ? (0x3F) + {0x70, 0x88, 0x08, 0x10, 0x20, 0x00, 0x20}, + // @ (0x40) + {0x70, 0x88, 0xB8, 0xA8, 0xB8, 0x80, 0x70}, + // A (0x41) + {0x70, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88}, + // B (0x42) + {0xF0, 0x88, 0x88, 0xF0, 0x88, 0x88, 0xF0}, + // C (0x43) + {0x70, 0x88, 0x80, 0x80, 0x80, 0x88, 0x70}, + // D (0x44) + {0xF0, 0x88, 0x88, 0x88, 0x88, 0x88, 0xF0}, + // E (0x45) + {0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8}, + // F (0x46) + {0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80}, + // G (0x47) + {0x70, 0x88, 0x80, 0xB8, 0x88, 0x88, 0x70}, + // H (0x48) + {0x88, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88}, + // I (0x49) + {0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70}, + // J (0x4A) + {0x38, 0x10, 0x10, 0x10, 0x10, 0x90, 0x60}, + // K (0x4B) + {0x88, 0x90, 0xA0, 0xC0, 0xA0, 0x90, 0x88}, + // L (0x4C) + {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8}, + // M (0x4D) + {0x88, 0xD8, 0xA8, 0xA8, 0x88, 0x88, 0x88}, + // N (0x4E) + {0x88, 0xC8, 0xA8, 0x98, 0x88, 0x88, 0x88}, + // O (0x4F) + {0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70}, + // P (0x50) + {0xF0, 0x88, 0x88, 0xF0, 0x80, 0x80, 0x80}, + // Q (0x51) + {0x70, 0x88, 0x88, 0x88, 0xA8, 0x90, 0x68}, + // R (0x52) + {0xF0, 0x88, 0x88, 0xF0, 0xA0, 0x90, 0x88}, + // S (0x53) + {0x70, 0x88, 0x80, 0x70, 0x08, 0x88, 0x70}, + // T (0x54) + {0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}, + // U (0x55) + {0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70}, + // V (0x56) + {0x88, 0x88, 0x88, 0x88, 0x88, 0x50, 0x20}, + // W (0x57) + {0x88, 0x88, 0x88, 0xA8, 0xA8, 0xD8, 0x88}, + // X (0x58) + {0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88}, + // Y (0x59) + {0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x20}, + // Z (0x5A) + {0xF8, 0x08, 0x10, 0x20, 0x40, 0x80, 0xF8}, + // [ (0x5B) + {0x70, 0x40, 0x40, 0x40, 0x40, 0x40, 0x70}, + // \ (0x5C) + {0x80, 0x80, 0x40, 0x20, 0x10, 0x08, 0x08}, + // ] (0x5D) + {0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x70}, + // ^ (0x5E) + {0x20, 0x50, 0x88, 0x00, 0x00, 0x00, 0x00}, + // _ (0x5F) + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8}, + // ` (0x60) + {0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00}, + // a (0x61) + {0x00, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78}, + // b (0x62) + {0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0xF0}, + // c (0x63) + {0x00, 0x00, 0x70, 0x80, 0x80, 0x80, 0x70}, + // d (0x64) + {0x08, 0x08, 0x78, 0x88, 0x88, 0x88, 0x78}, + // e (0x65) + {0x00, 0x00, 0x70, 0x88, 0xF8, 0x80, 0x70}, + // f (0x66) + {0x30, 0x48, 0x40, 0xE0, 0x40, 0x40, 0x40}, + // g (0x67) + {0x00, 0x00, 0x78, 0x88, 0x78, 0x08, 0x70}, + // h (0x68) + {0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x88}, + // i (0x69) + {0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70}, + // j (0x6A) + {0x10, 0x00, 0x30, 0x10, 0x10, 0x90, 0x60}, + // k (0x6B) + {0x80, 0x80, 0x90, 0xA0, 0xC0, 0xA0, 0x90}, + // l (0x6C) + {0x60, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70}, + // m (0x6D) + {0x00, 0x00, 0xD0, 0xA8, 0xA8, 0xA8, 0xA8}, + // n (0x6E) + {0x00, 0x00, 0xF0, 0x88, 0x88, 0x88, 0x88}, + // o (0x6F) + {0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70}, + // p (0x70) + {0x00, 0x00, 0xF0, 0x88, 0xF0, 0x80, 0x80}, + // q (0x71) + {0x00, 0x00, 0x78, 0x88, 0x78, 0x08, 0x08}, + // r (0x72) + {0x00, 0x00, 0xB0, 0xC8, 0x80, 0x80, 0x80}, + // s (0x73) + {0x00, 0x00, 0x78, 0x80, 0x70, 0x08, 0xF0}, + // t (0x74) + {0x40, 0x40, 0xE0, 0x40, 0x40, 0x48, 0x30}, + // u (0x75) + {0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x78}, + // v (0x76) + {0x00, 0x00, 0x88, 0x88, 0x88, 0x50, 0x20}, + // w (0x77) + {0x00, 0x00, 0x88, 0x88, 0xA8, 0xA8, 0x50}, + // x (0x78) + {0x00, 0x00, 0x88, 0x50, 0x20, 0x50, 0x88}, + // y (0x79) + {0x00, 0x00, 0x88, 0x88, 0x78, 0x08, 0x70}, + // z (0x7A) + {0x00, 0x00, 0xF8, 0x10, 0x20, 0x40, 0xF8}, + // { (0x7B) + {0x10, 0x20, 0x20, 0x40, 0x20, 0x20, 0x10}, + // | (0x7C) + {0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}, + // } (0x7D) + {0x40, 0x20, 0x20, 0x10, 0x20, 0x20, 0x40}, + // ~ (0x7E) + {0x00, 0x00, 0x40, 0xA8, 0x10, 0x00, 0x00}, +} + +// drawChar renders a single ASCII character at (x, y) using filled rectangles. +// Each pixel of the 5x7 glyph is drawn as a scale×scale block. +func drawChar(renderer *sdl.Renderer, ch byte, x, y, scale int32) { + if ch < 0x20 || ch > 0x7E { + ch = '?' // replace unprintable characters + } + glyph := font5x7[ch-0x20] + for row := 0; row < glyphH; row++ { + bits := glyph[row] + for col := 0; col < glyphW; col++ { + if bits&(0x80>>uint(col)) != 0 { + renderer.FillRect(&sdl.Rect{ + X: x + int32(col)*scale, + Y: y + int32(row)*scale, + W: scale, + H: scale, + }) + } + } + } +} + +// drawString renders a string at (x, y) with the given pixel scale. +// Returns the total width in pixels consumed by the string. +func drawString(renderer *sdl.Renderer, s string, x, y, scale int32) int32 { + cx := x + for i := 0; i < len(s); i++ { + drawChar(renderer, s[i], cx, y, scale) + cx += (glyphW + charGap) * scale + } + return cx - x +} + +// stringWidth returns the pixel width of a string at the given scale. +func stringWidth(s string, scale int32) int32 { + if len(s) == 0 { + return 0 + } + return int32(len(s))*(glyphW+charGap)*scale - charGap*scale +} |
