package renderer import ( "fmt" "image/color" "sort" "git.eeqj.de/sneak/hdmistat/internal/layout" "git.eeqj.de/sneak/hdmistat/internal/statcollector" ) // ProcessScreen displays top processes type ProcessScreen struct { SortBy string // "cpu" or "memory" } func NewProcessScreenCPU() *ProcessScreen { return &ProcessScreen{SortBy: "cpu"} } func NewProcessScreenMemory() *ProcessScreen { return &ProcessScreen{SortBy: "memory"} } func (s *ProcessScreen) Name() string { if s.SortBy == "cpu" { return "Top Processes by CPU" } return "Top Processes by Memory" } func (s *ProcessScreen) Render(canvas *layout.Canvas, info *statcollector.SystemInfo) error { width, _ := canvas.Size() // Colors textColor := color.RGBA{255, 255, 255, 255} headerColor := color.RGBA{100, 200, 255, 255} dimColor := color.RGBA{150, 150, 150, 255} // Styles titleStyle := layout.TextStyle{Size: 36, Color: headerColor} headerStyle := layout.TextStyle{Size: 20, Color: headerColor} normalStyle := layout.TextStyle{Size: 16, Color: textColor} smallStyle := layout.TextStyle{Size: 14, Color: dimColor} y := 50 // Title canvas.DrawText(s.Name(), layout.Point{X: width / 2, Y: y}, layout.TextStyle{ Size: titleStyle.Size, Color: titleStyle.Color, Alignment: layout.AlignCenter, }) y += 70 // Sort processes processes := make([]statcollector.ProcessInfo, len(info.Processes)) copy(processes, info.Processes) if s.SortBy == "cpu" { sort.Slice(processes, func(i, j int) bool { return processes[i].CPUPercent > processes[j].CPUPercent }) } else { sort.Slice(processes, func(i, j int) bool { return processes[i].MemoryRSS > processes[j].MemoryRSS }) } // Table headers x := 50 canvas.DrawText("PID", layout.Point{X: x, Y: y}, headerStyle) canvas.DrawText("USER", layout.Point{X: x + 100, Y: y}, headerStyle) canvas.DrawText("PROCESS", layout.Point{X: x + 250, Y: y}, headerStyle) canvas.DrawText("CPU %", layout.Point{X: x + 600, Y: y}, headerStyle) canvas.DrawText("MEMORY", layout.Point{X: x + 700, Y: y}, headerStyle) y += 30 canvas.DrawHLine(x, y, width-100, color.RGBA{100, 100, 100, 255}) y += 20 // Display top 20 processes for i, proc := range processes { if i >= 20 { break } // Truncate long names name := proc.Name if len(name) > 30 { name = name[:27] + "..." } user := proc.Username if len(user) > 12 { user = user[:9] + "..." } canvas.DrawText(fmt.Sprintf("%d", proc.PID), layout.Point{X: x, Y: y}, normalStyle) canvas.DrawText(user, layout.Point{X: x + 100, Y: y}, normalStyle) canvas.DrawText(name, layout.Point{X: x + 250, Y: y}, normalStyle) canvas.DrawText(fmt.Sprintf("%.1f", proc.CPUPercent), layout.Point{X: x + 600, Y: y}, normalStyle) canvas.DrawText(layout.FormatBytes(proc.MemoryRSS), layout.Point{X: x + 700, Y: y}, normalStyle) // Highlight bar for high usage if s.SortBy == "cpu" && proc.CPUPercent > 50 { canvas.DrawBox(x-5, y-15, width-90, 20, color.RGBA{100, 50, 50, 100}) } else if s.SortBy == "memory" && float64(proc.MemoryRSS)/float64(info.MemoryTotal) > 0.1 { canvas.DrawBox(x-5, y-15, width-90, 20, color.RGBA{50, 50, 100, 100}) } y += 25 } // Footer with system totals y = 950 canvas.DrawHLine(50, y, width-100, color.RGBA{100, 100, 100, 255}) y += 30 totalCPU := 0.0 for _, cpu := range info.CPUPercent { totalCPU += cpu } avgCPU := totalCPU / float64(len(info.CPUPercent)) footerText := fmt.Sprintf("System: CPU %.1f%% | Memory: %s / %s (%.1f%%)", avgCPU, layout.FormatBytes(info.MemoryUsed), layout.FormatBytes(info.MemoryTotal), float64(info.MemoryUsed)/float64(info.MemoryTotal)*100) canvas.DrawText(footerText, layout.Point{X: width / 2, Y: y}, layout.TextStyle{ Size: smallStyle.Size, Color: smallStyle.Color, Alignment: layout.AlignCenter, }) return nil }