hdmistat/internal/fbdraw/EXAMPLE.md
2025-07-24 14:32:50 +02:00

4.8 KiB

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.

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.