1package handlers
2
3import (
4 "context"
5 "strings"
6
7 "github.com/bnema/archup/internal/domain/ports"
8 "github.com/bnema/archup/internal/domain/system"
9)
10
11// GPUHandler handles GPU detection.
12type GPUHandler struct {
13 cmdExec ports.CommandExecutor
14 logger ports.Logger
15}
16
17// NewGPUHandler creates a new GPU handler.
18func NewGPUHandler(cmdExec ports.CommandExecutor, logger ports.Logger) *GPUHandler {
19 return &GPUHandler{
20 cmdExec: cmdExec,
21 logger: logger,
22 }
23}
24
25// Detect detects the system GPU and returns recommended configuration.
26func (h *GPUHandler) Detect(ctx context.Context) (*system.GPU, error) {
27 output, err := h.cmdExec.Execute(ctx, "lspci")
28 if err != nil {
29 h.logger.Warn("Failed to run lspci for GPU detection", "error", err)
30 return system.NewGPU(system.GPUVendorUnknown, "", nil, nil), err
31 }
32
33 line := firstGPULine(string(output))
34 if line == "" {
35 h.logger.Warn("No GPU detected in lspci output")
36 return system.NewGPU(system.GPUVendorUnknown, "", nil, nil), nil
37 }
38
39 vendor := detectGPUVendor(line)
40 model := extractGPUModel(line)
41 drivers := recommendedDrivers(vendor)
42
43 h.logger.Info("Detected GPU", "vendor", vendor, "model", model)
44 return system.NewGPU(vendor, model, drivers, nil), nil
45}
46
47func firstGPULine(output string) string {
48 lines := strings.Split(output, "\n")
49 for _, line := range lines {
50 if strings.Contains(line, "VGA compatible controller") ||
51 strings.Contains(line, "3D controller") ||
52 strings.Contains(line, "Display controller") {
53 return line
54 }
55 }
56 return ""
57}
58
59func detectGPUVendor(line string) system.GPUVendor {
60 lower := strings.ToLower(line)
61 switch {
62 case strings.Contains(lower, "nvidia"):
63 return system.GPUVendorNVIDIA
64 case strings.Contains(lower, "advanced micro devices") || strings.Contains(lower, "amd") || strings.Contains(lower, "ati"):
65 return system.GPUVendorAMD
66 case strings.Contains(lower, "intel"):
67 return system.GPUVendorIntel
68 default:
69 return system.GPUVendorUnknown
70 }
71}
72
73func extractGPUModel(line string) string {
74 parts := strings.SplitN(line, ": ", 2)
75 if len(parts) < 2 {
76 return "Unknown"
77 }
78 return strings.TrimSpace(parts[1])
79}
80
81func recommendedDrivers(vendor system.GPUVendor) []string {
82 switch vendor {
83 case system.GPUVendorAMD:
84 return []string{"mesa", "vulkan-radeon", "libva-mesa-driver", "mesa-vdpau"}
85 case system.GPUVendorIntel:
86 return []string{"mesa", "vulkan-intel", "intel-media-driver"}
87 case system.GPUVendorNVIDIA:
88 return []string{"nvidia-open", "nvidia-utils", "libva-nvidia-driver"}
89 default:
90 return []string{}
91 }
92}