package device import ( "encoding/json" "fmt" "os" "path/filepath" "github.com/wobkey/rgb/internal/hid" ) // Keyboard represents a VIA-compatible keyboard from keyboards.json. type Keyboard struct { Name string `json:"name"` VendorID uint16 `json:"vendorId"` ProductID uint16 `json:"productId"` Protocol string `json:"protocol"` VIAVersion int `json:"viaVersion"` LEDLayout string `json:"ledLayout"` } // Device represents a connected keyboard device. type Device struct { Keyboard Path string } // LoadKeyboards reads keyboard definitions from keyboards.json. func LoadKeyboards() ([]Keyboard, error) { path, err := findKeyboardsJSON() if err != nil { return nil, fmt.Errorf("find keyboards.json: %w", err) } data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read keyboards.json: %w", err) } var keyboards []Keyboard if err := json.Unmarshal(data, &keyboards); err != nil { return nil, fmt.Errorf("parse keyboards.json: %w", err) } return keyboards, nil } // findKeyboardsJSON searches for keyboards.json in common locations. var findKeyboardsJSON = func() (string, error) { exe, err := os.Executable() if err == nil { path := filepath.Join(filepath.Dir(exe), "keyboards.json") if _, err := os.Stat(path); err == nil { return path, nil } } path := "keyboards.json" if _, err := os.Stat(path); err == nil { return path, nil } cwd, err := os.Getwd() if err == nil { path := filepath.Join(cwd, "keyboards.json") if _, err := os.Stat(path); err == nil { return path, nil } } return "", fmt.Errorf("keyboards.json not found") } // Discover scans all HID devices and returns connected VIA-compatible keyboards. func Discover(keyboards []Keyboard) ([]Device, error) { devices, err := hid.DiscoverAll() if err != nil { return nil, fmt.Errorf("enumerate HID devices: %w", err) } fmt.Fprintf(os.Stderr, "DEBUG discover: keyboards=%d, hid_devices=%d\n", len(keyboards), len(devices)) for _, kb := range keyboards { fmt.Fprintf(os.Stderr, "DEBUG discover: kb=%s VID=%d PID=%d\n", kb.Name, kb.VendorID, kb.ProductID) } for _, d := range devices { fmt.Fprintf(os.Stderr, "DEBUG discover: hid=%s VID=%d PID=%d raw=%t\n", d.Path, d.VendorID, d.ProductID, d.RawHID) } return matchKeyboards(keyboards, devices), nil } func matchKeyboards(keyboards []Keyboard, devices []hid.DeviceInfo) []Device { var found []Device for _, kb := range keyboards { for _, d := range devices { if !d.RawHID || d.VendorID != kb.VendorID || d.ProductID != kb.ProductID { continue } fmt.Fprintf(os.Stderr, "DEBUG discover: MATCH %s %s rawHID=true\n", kb.Name, d.Path) found = append(found, Device{ Keyboard: kb, Path: d.Path, }) } } fmt.Fprintf(os.Stderr, "DEBUG discover: found=%d\n", len(found)) return found }