168 lines
4.5 KiB
Go
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()
|
|
}
|