hdmistat/internal/fbdraw/header_wrapper.go
2025-07-24 16:09:00 +02:00

168 lines
4.5 KiB
Go

//nolint:mnd
package fbdraw
import (
"os/exec"
"strings"
"time"
)
// HeaderWrapper wraps a FrameGenerator and adds a 3-line header
type HeaderWrapper struct {
wrapped FrameGenerator
width int
height int
unameCache string
lsbCache string
}
// NewHeaderWrapper creates a new header wrapper around a FrameGenerator
func NewHeaderWrapper(wrapped FrameGenerator) *HeaderWrapper {
return &HeaderWrapper{
wrapped: wrapped,
}
}
// Init initializes both the wrapper and the wrapped generator
func (h *HeaderWrapper) Init(width, height int) error {
h.width = width
h.height = height
// Cache uname output since it doesn't change
// Get OS, hostname, kernel version, and architecture separately
var parts []string
// OS name (e.g., "Linux", "Darwin")
if output, err := exec.Command("uname", "-s").Output(); err == nil {
parts = append(parts, strings.TrimSpace(string(output)))
}
// Hostname
if output, err := exec.Command("uname", "-n").Output(); err == nil {
parts = append(parts, strings.TrimSpace(string(output)))
}
// Kernel version
if output, err := exec.Command("uname", "-r").Output(); err == nil {
parts = append(parts, strings.TrimSpace(string(output)))
}
// Machine architecture
if output, err := exec.Command("uname", "-m").Output(); err == nil {
parts = append(parts, strings.TrimSpace(string(output)))
}
if len(parts) > 0 {
h.unameCache = strings.Join(parts, " ")
} else {
h.unameCache = "Unknown System"
}
// Get LSB release info
if output, err := exec.Command("lsb_release", "-ds").Output(); err == nil {
h.lsbCache = strings.TrimSpace(string(output))
// Remove quotes if present
h.lsbCache = strings.Trim(h.lsbCache, "\"")
}
// Initialize wrapped generator with reduced height (minus 3 for header)
return h.wrapped.Init(width, height-3)
}
// GenerateFrame generates a frame with header and wrapped content
func (h *HeaderWrapper) GenerateFrame(grid *CharGrid) error {
// Create a temporary grid for the wrapped content
contentGrid := NewCharGrid(h.width, h.height-3)
// Copy font settings from main grid
contentGrid.FontSize = grid.FontSize
contentGrid.FontFamily = grid.FontFamily
// Let the wrapped generator fill its content
if err := h.wrapped.GenerateFrame(contentGrid); err != nil {
return err
}
// Now we'll assemble the final grid
// First, clear the entire grid
grid.Clear(Black)
// Draw the header
h.drawHeader(grid)
// Copy content from wrapped generator below the header
for y := 0; y < contentGrid.Height; y++ {
for x := 0; x < contentGrid.Width; x++ {
if y < len(contentGrid.Cells) && x < len(contentGrid.Cells[y]) {
grid.Cells[y+3][x] = contentGrid.Cells[y][x]
}
}
}
return nil
}
// drawHeader draws the 3-line header
func (h *HeaderWrapper) drawHeader(grid *CharGrid) {
writer := NewGridWriter(grid)
// Line 1: uname + lsb_release output (truncated if needed)
writer.MoveAbs(0, 0)
writer.SetColor(Gray60)
sysInfo := h.unameCache
if h.lsbCache != "" {
sysInfo += " " + h.lsbCache
}
// Account for the UTC time on the right - never truncate time
now := time.Now()
utcTime := now.UTC().Format("Mon 2006-01-02 15:04:05 UTC")
maxLen := h.width - len(utcTime) - 1
if len(sysInfo) > maxLen {
sysInfo = sysInfo[:maxLen-3] + "..."
}
writer.Write("%s", sysInfo)
// Check if local time is different from UTC
localZone, offset := now.Zone()
showLocalTime := offset != 0 // Only show local time if not UTC
// Line 2: uptime output
writer.MoveAbs(0, 1)
if output, err := exec.Command("uptime").Output(); err == nil {
uptime := strings.TrimSpace(string(output))
// Don't cut off at "user" - show the full uptime output
maxLen := h.width - 1
if showLocalTime {
// Account for the local time on the right - never truncate time
localTime := now.Format("Mon 2006-01-02 15:04:05 ") + localZone
maxLen = h.width - len(localTime) - 1
}
if len(uptime) > maxLen {
uptime = uptime[:maxLen-3] + "..."
}
writer.Write("%s", uptime)
}
// Right side - UTC time (line 1) - always show full time
writer.MoveAbs(h.width-len(utcTime), 0)
writer.Write("%s", utcTime)
// Right side - Local time (line 2) - only show if different from UTC
if showLocalTime {
localTime := now.Format("Mon 2006-01-02 15:04:05 ") + localZone
writer.MoveAbs(h.width-len(localTime), 1)
writer.Write("%s", localTime)
}
// Line 3: Horizontal rule
writer.MoveAbs(0, 2)
writer.SetColor(Gray30)
for i := 0; i < h.width; i++ {
writer.Write("─")
}
}
// FramesPerSecond returns the wrapped generator's frame rate
func (h *HeaderWrapper) FramesPerSecond() float64 {
return h.wrapped.FramesPerSecond()
}