184 lines
4.8 KiB
Markdown
184 lines
4.8 KiB
Markdown
# fbdraw Carousel Example
|
|
|
|
This example demonstrates how to use the fbdraw carousel API to create a rotating display with multiple screens, each updating at its own frame rate.
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"math/rand"
|
|
"time"
|
|
|
|
"git.eeqj.de/sneak/hdmistat/internal/fbdraw"
|
|
"git.eeqj.de/sneak/hdmistat/internal/font"
|
|
)
|
|
|
|
// SystemStatusGenerator generates frames showing system status
|
|
type SystemStatusGenerator struct {
|
|
frameCount int
|
|
}
|
|
|
|
func (g *SystemStatusGenerator) GenerateFrame(grid *fbdraw.CharGrid) error {
|
|
g.frameCount++
|
|
w := fbdraw.NewGridWriter(grid)
|
|
|
|
// Clear and draw header
|
|
w.Clear()
|
|
w.SetColor(fbdraw.Cyan).SetWeight(font.WeightBold)
|
|
w.MoveAbs(0, 0).WriteLine("=== SYSTEM STATUS ===")
|
|
|
|
// Animate with frame count
|
|
w.SetColor(fbdraw.White).SetWeight(font.WeightRegular)
|
|
w.MoveAbs(0, 2).WriteLine("Frame: %d", g.frameCount)
|
|
w.MoveAbs(0, 3).WriteLine("Time: %s", time.Now().Format("15:04:05.000"))
|
|
|
|
// Animated CPU meter
|
|
cpuUsage := 50 + 30*math.Sin(float64(g.frameCount)*0.1)
|
|
w.MoveAbs(0, 5).Write("CPU: [")
|
|
w.DrawMeter(cpuUsage, 20)
|
|
w.Write("] %.1f%%", cpuUsage)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (g *SystemStatusGenerator) FramesPerSecond() float64 {
|
|
return 15.0 // 15 FPS
|
|
}
|
|
|
|
// NetworkMonitorGenerator shows network activity
|
|
type NetworkMonitorGenerator struct {
|
|
packets []float64
|
|
}
|
|
|
|
func (g *NetworkMonitorGenerator) GenerateFrame(grid *fbdraw.CharGrid) error {
|
|
w := fbdraw.NewGridWriter(grid)
|
|
|
|
// Update data
|
|
if len(g.packets) > 50 {
|
|
g.packets = g.packets[1:]
|
|
}
|
|
g.packets = append(g.packets, rand.Float64()*100)
|
|
|
|
// Draw
|
|
w.Clear()
|
|
w.SetColor(fbdraw.Green).SetWeight(font.WeightBold)
|
|
w.MoveAbs(0, 0).WriteLine("=== NETWORK MONITOR ===")
|
|
|
|
// Draw graph
|
|
w.SetColor(fbdraw.White).SetWeight(font.WeightRegular)
|
|
for i, val := range g.packets {
|
|
height := int(val / 10) // Scale to 0-10
|
|
for y := 10; y > 10-height; y-- {
|
|
w.MoveAbs(i+5, y).Write("█")
|
|
}
|
|
}
|
|
|
|
w.MoveAbs(0, 12).WriteLine("Packets/sec: %.0f", g.packets[len(g.packets)-1])
|
|
|
|
return nil
|
|
}
|
|
|
|
func (g *NetworkMonitorGenerator) FramesPerSecond() float64 {
|
|
return 10.0 // 10 FPS
|
|
}
|
|
|
|
// ProcessListGenerator shows top processes
|
|
type ProcessListGenerator struct {
|
|
updateCount int
|
|
}
|
|
|
|
func (g *ProcessListGenerator) GenerateFrame(grid *fbdraw.CharGrid) error {
|
|
g.updateCount++
|
|
w := fbdraw.NewGridWriter(grid)
|
|
|
|
w.Clear()
|
|
w.SetColor(fbdraw.Yellow).SetWeight(font.WeightBold)
|
|
w.MoveAbs(0, 0).WriteLine("=== TOP PROCESSES ===")
|
|
|
|
// Table header
|
|
w.MoveAbs(0, 2).SetColor(fbdraw.White).SetWeight(font.WeightBold)
|
|
w.WriteLine("PID CPU% PROCESS")
|
|
w.WriteLine("----- ----- ----------------")
|
|
|
|
// Fake process data
|
|
w.SetWeight(font.WeightRegular)
|
|
processes := []struct {
|
|
pid int
|
|
cpu float64
|
|
name string
|
|
}{
|
|
{1234, 42.1 + float64(g.updateCount%10), "firefox"},
|
|
{5678, 18.7, "vscode"},
|
|
{9012, 8.3, "dockerd"},
|
|
}
|
|
|
|
for i, p := range processes {
|
|
if p.cpu > 30 {
|
|
w.SetColor(fbdraw.Red)
|
|
} else if p.cpu > 15 {
|
|
w.SetColor(fbdraw.Yellow)
|
|
} else {
|
|
w.SetColor(fbdraw.White)
|
|
}
|
|
|
|
w.MoveAbs(0, 4+i)
|
|
w.WriteLine("%-5d %5.1f %s", p.pid, p.cpu, p.name)
|
|
}
|
|
|
|
w.MoveAbs(0, 10).SetColor(fbdraw.Gray60)
|
|
w.WriteLine("Update #%d", g.updateCount)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (g *ProcessListGenerator) FramesPerSecond() float64 {
|
|
return 1.0 // 1 FPS - processes don't change that fast
|
|
}
|
|
|
|
func main() {
|
|
// Initialize display (auto-detect framebuffer)
|
|
display, err := fbdraw.NewFBDisplayAuto()
|
|
if err != nil {
|
|
// Fall back to terminal display
|
|
display = fbdraw.NewTerminalDisplay(80, 25)
|
|
}
|
|
defer display.Close()
|
|
|
|
// Create carousel with 10 second rotation
|
|
carousel := fbdraw.NewCarousel(display, 10*time.Second)
|
|
|
|
// Add screens with their generators
|
|
carousel.AddScreen("System Status", &SystemStatusGenerator{})
|
|
carousel.AddScreen("Network Monitor", &NetworkMonitorGenerator{})
|
|
carousel.AddScreen("Process List", &ProcessListGenerator{})
|
|
|
|
// Start the carousel (blocks until interrupted)
|
|
if err := carousel.Run(); err != nil {
|
|
fmt.Printf("Carousel error: %v\n", err)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Key Features Demonstrated
|
|
|
|
1. **Multiple Display Types**: The example tries to auto-detect a framebuffer, falling back to terminal display if not available.
|
|
|
|
2. **Different Frame Rates**: Each screen updates at its own rate:
|
|
- System Status: 15 FPS (smooth animations)
|
|
- Network Monitor: 10 FPS (moderate updates)
|
|
- Process List: 1 FPS (slow changing data)
|
|
|
|
3. **GridWriter API**: Shows various drawing operations:
|
|
- `MoveAbs()` for absolute positioning
|
|
- `Move()` for relative movement
|
|
- `DrawMeter()` for progress bars with automatic coloring
|
|
- `SetColor()`, `SetWeight()` for styling
|
|
|
|
4. **Carousel Management**: The carousel automatically:
|
|
- Rotates screens every 10 seconds
|
|
- Manages frame timing for each screen
|
|
- Only renders the active screen
|
|
|
|
5. **Animation**: The system status screen demonstrates smooth animation using frame counting and sine waves. |