summaryrefslogtreecommitdiff
path: root/internal/display/font.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-18 09:00:35 +0200
committerPaul Buetow <paul@buetow.org>2026-02-18 09:00:35 +0200
commit88f4e239a7521112a4db8c7842e3a05db4446cd4 (patch)
tree8c331f9f2e23ad9c9319d6dc8275205b23ce811a /internal/display/font.go
parent11204092b5ab5dc0f71515adfcaa6f07111363e5 (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.go248
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
+}