.. | ||
README.md |
Framebuffer Display API
A high-level Go package for easily creating text-based status displays on Linux framebuffers. Perfect for system monitors, embedded displays, IoT dashboards, and more.
API Design Concepts
Below are four different API design approaches for creating framebuffer displays. Each example shows how you might implement a system status display.
Concept 1: Builder Pattern
package main
import (
"time"
fb "github.com/example/framebufferdisplay"
)
func main() {
// Create and configure display with fluent interface
display := fb.New().
AutoDetect(). // Find first available framebuffer
WithFont("IBM Plex Mono", 14). // Default font
WithUpdateInterval(time.Second). // Auto-refresh rate
Build()
defer display.Close()
// Define the layout
display.Layout(func(canvas *fb.Canvas) {
// Header section
canvas.Section("header", fb.TopCenter).
Font("IBM Plex Mono", 24).
Color(fb.White).
Text("System Status")
// System info section
canvas.Section("info", fb.TopLeft).
Margin(20).
Rows(
fb.Row().Label("Hostname:").Value(getHostname()),
fb.Row().Label("Uptime:").Value(getUptime()),
fb.Row().Label("Load:").Value(getLoad()).Color(fb.Red),
)
// CPU meters
canvas.Section("cpu", fb.CenterLeft).
Title("CPU Usage").
Meters(getCPUMeters()...)
// Memory bar
canvas.Section("memory", fb.BottomLeft).
Title("Memory").
ProgressBar(getMemoryPercent(), fb.Green)
})
// Start the display loop
display.Run()
}
Concept 2: Declarative/React-like
package main
import (
fb "github.com/example/framebufferdisplay"
)
type SystemStatus struct {
fb.Component
hostname string
uptime time.Duration
}
func (s *SystemStatus) Render() fb.Element {
return fb.Screen(
fb.Header(
fb.Text("System Status").
Font("IBM Plex Mono", 48).
Color(fb.RGB(100, 200, 255)),
),
fb.Grid(fb.GridOptions{Columns: 2, Gap: 20},
// Left column
fb.Column(
fb.Card(
fb.Title("System Info"),
fb.List(
fb.ListItem("Hostname", s.hostname),
fb.ListItem("Uptime", formatDuration(s.uptime)),
fb.ListItem("OS", getOS()),
),
),
fb.Card(
fb.Title("Network"),
fb.List(getNetworkInfo()...),
),
),
// Right column
fb.Column(
fb.Card(
fb.Title("CPU Usage"),
fb.BarChart(getCPUData(), fb.ChartOptions{
Height: 200,
Color: fb.Gradient(fb.Green, fb.Red),
}),
),
fb.Card(
fb.Title("Memory"),
fb.CircularProgress(getMemoryPercent(), fb.Blue),
fb.Text(getMemoryDetails()).Size(12),
),
),
),
)
}
func main() {
fb.Run(&SystemStatus{})
}
Concept 3: Immediate Mode
package main
import (
fb "github.com/example/framebufferdisplay"
)
func main() {
// Auto-detect and initialize
ctx := fb.Init()
defer ctx.Close()
// Main render loop
ctx.Loop(func(d *fb.Draw) {
// Clear with background
d.Clear(fb.Black)
// Draw header
d.SetFont("IBM Plex Mono Bold", 36)
d.SetColor(fb.White)
d.TextCenter(d.Width/2, 50, "System Monitor")
// System info box
d.SetFont("IBM Plex Mono", 14)
d.Box(20, 100, 400, 200, fb.Gray)
d.SetColor(fb.Green)
d.Text(30, 120, "Hostname: %s", getHostname())
d.Text(30, 140, "Uptime: %s", getUptime())
d.Text(30, 160, "Load: %.2f %.2f %.2f", getLoad())
// CPU visualization
cpus := getCPUPercents()
for i, cpu := range cpus {
y := 320 + i*30
d.Text(30, y, "CPU%d", i)
d.ProgressBar(80, y-10, 300, 20, cpu, fb.Heat(cpu))
}
// Memory meter
mem := getMemoryPercent()
d.SetFont("IBM Plex Mono", 18)
d.Text(30, 500, "Memory: %.1f%%", mem)
d.Gauge(30, 520, 350, 40, mem, fb.Blue)
// Update display
d.Present()
})
}
Concept 4: Template/Widget-based
package main
import (
fb "github.com/example/framebufferdisplay"
)
func main() {
// Create display with auto-detection
display := fb.NewDisplay()
// Create a dashboard with predefined widgets
dashboard := fb.Dashboard{
Title: "System Status",
Theme: fb.Themes.Dark,
Layout: fb.GridLayout(3, 3), // 3x3 grid
Widgets: []fb.Widget{
// Row 1
fb.BigNumber{
GridPos: fb.Pos(0, 0),
Label: "CPU Temp",
Value: getCPUTemp,
Unit: "°C",
Color: fb.TempColor, // Auto-colors based on value
},
fb.LineGraph{
GridPos: fb.Pos(1, 0).Span(2, 1), // Spans 2 columns
Title: "CPU History",
Duration: 5 * time.Minute,
Source: streamCPUData,
},
// Row 2
fb.InfoTable{
GridPos: fb.Pos(0, 1),
Rows: []fb.TableRow{
{"Host", getHostname},
{"Kernel", getKernel},
{"Uptime", getUptime},
},
},
fb.MultiMeter{
GridPos: fb.Pos(1, 1),
Title: "CPU Cores",
Meters: getCPUCoreMeters,
Compact: true,
},
fb.PieChart{
GridPos: fb.Pos(2, 1),
Title: "Disk Usage",
Data: getDiskUsage,
},
// Row 3
fb.MemoryWidget{
GridPos: fb.Pos(0, 2).Span(2, 1),
ShowDetails: true,
},
fb.NetworkTraffic{
GridPos: fb.Pos(2, 2),
Interface: "eth0",
},
},
// Optional: Add alerts
Alerts: []fb.Alert{
fb.Alert{
Condition: func() bool { return getCPUTemp() > 80 },
Message: "High CPU Temperature!",
Color: fb.Red,
},
},
}
// Run the dashboard
display.RunDashboard(dashboard)
}
Key Features Across All Concepts
- Auto-detection: Automatically finds and configures the first available framebuffer
- Resolution independence: Layouts adapt to the detected resolution
- Font management: Easy font loading and sizing
- Color utilities: Named colors, RGB, gradients, and conditional coloring
- Common widgets: Progress bars, meters, graphs, tables, etc.
- Refresh control: Configurable update intervals or manual control
- Error handling: Graceful fallbacks for missing fonts, permissions, etc.
Design Considerations
Each approach offers different benefits:
- Builder Pattern: Familiar to Go developers, good for static layouts
- Declarative: Clean separation of data and presentation, easy to test
- Immediate Mode: Simple and direct, good for dynamic content
- Widget-based: Highest level abstraction, fastest to build common dashboards
The final API could combine elements from multiple approaches, such as using the widget system from Concept 4 with the immediate mode drawing primitives from Concept 3 for custom widgets.