Features: - Beautiful system statistics display using IBM Plex Mono font - Direct framebuffer rendering without X11/Wayland - Multiple screens with automatic carousel rotation - Real-time system monitoring (CPU, memory, disk, network, processes) - Systemd service integration with install command - Clean architecture using uber/fx dependency injection Architecture: - Cobra CLI with daemon, install, status, and info commands - Modular design with separate packages for display, rendering, and stats - Font embedding for zero runtime dependencies - Layout API for clean text rendering - Support for multiple screen types (overview, top CPU, top memory) Technical details: - Uses gopsutil for cross-platform system stats collection - Direct Linux framebuffer access via memory mapping - Anti-aliased text rendering with freetype - Configurable screen rotation and update intervals - Structured logging with slog - Comprehensive test coverage and linting setup This initial version provides a solid foundation for displaying rich system information on resource-constrained devices like Raspberry Pis.
87 lines
1.9 KiB
Go
87 lines
1.9 KiB
Go
package hdmistat
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"git.eeqj.de/sneak/hdmistat/internal/app"
|
|
"git.eeqj.de/sneak/hdmistat/internal/display"
|
|
"git.eeqj.de/sneak/hdmistat/internal/font"
|
|
"git.eeqj.de/sneak/hdmistat/internal/renderer"
|
|
"git.eeqj.de/sneak/hdmistat/internal/statcollector"
|
|
"github.com/golang/freetype/truetype"
|
|
"github.com/spf13/cobra"
|
|
"go.uber.org/fx"
|
|
)
|
|
|
|
var (
|
|
framebufferDevice string
|
|
configFile string
|
|
|
|
daemonCmd = &cobra.Command{
|
|
Use: "daemon",
|
|
Short: "Run hdmistat as a daemon",
|
|
Long: `Run hdmistat as a daemon that displays system statistics on the framebuffer.`,
|
|
Run: runDaemon,
|
|
}
|
|
)
|
|
|
|
func init() {
|
|
daemonCmd.Flags().StringVarP(&framebufferDevice, "framebuffer", "f", "/dev/fb0", "Framebuffer device to use")
|
|
daemonCmd.Flags().StringVarP(&configFile, "config", "c", "/etc/hdmistat/config.yaml", "Configuration file path")
|
|
}
|
|
|
|
func runDaemon(cmd *cobra.Command, args []string) {
|
|
// Set up signal handling
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
<-sigChan
|
|
logger.Info("received shutdown signal")
|
|
cancel()
|
|
}()
|
|
|
|
// Create fx application
|
|
fxApp := fx.New(
|
|
fx.Provide(
|
|
func() *slog.Logger { return logger },
|
|
func() context.Context { return ctx },
|
|
|
|
// Provide font
|
|
func() (*truetype.Font, error) {
|
|
return font.LoadIBMPlexMono()
|
|
},
|
|
|
|
// Provide display
|
|
func(logger *slog.Logger) (display.Display, error) {
|
|
return display.NewFramebufferDisplay(framebufferDevice, logger)
|
|
},
|
|
|
|
// Provide collector
|
|
func(logger *slog.Logger) statcollector.Collector {
|
|
return statcollector.NewSystemCollector(logger)
|
|
},
|
|
|
|
// Provide renderer
|
|
renderer.NewRenderer,
|
|
|
|
// Provide app
|
|
app.NewApp,
|
|
),
|
|
|
|
fx.Invoke(func(a *app.App) {
|
|
// App will be started by fx lifecycle
|
|
}),
|
|
)
|
|
|
|
// Start the application
|
|
fxApp.Run()
|
|
}
|