package device import ( "fmt" "sort" "netdome.biz/paul/qmk-rgb/internal/hid" ) // Device is a connected QMK Raw HID keyboard. // Index is 1-based and is what --device accepts. type Device struct { Index int `json:"index"` Path string `json:"path"` VendorID uint16 `json:"vendorId"` ProductID uint16 `json:"productId"` Name string `json:"name,omitempty"` } // DiscoverAll returns every connected QMK Raw HID keyboard, ordered // deterministically and numbered from 1. A keyboard that does not report a USB // product string comes back with no Name; that costs the name and nothing else. func DiscoverAll() ([]Device, error) { infos, err := hid.DiscoverAll() if err != nil { return nil, fmt.Errorf("discover qmk devices: %w", err) } devices := make([]Device, 0, len(infos)) for _, info := range infos { devices = append(devices, Device{ Path: info.Path, VendorID: info.VendorID, ProductID: info.ProductID, Name: info.ProductString, }) } return indexDevices(devices), nil } // indexDevices sorts devices deterministically and assigns 1-based indexes. // The input slice is left untouched. func indexDevices(devices []Device) []Device { sorted := make([]Device, len(devices)) copy(sorted, devices) sort.SliceStable(sorted, func(i, j int) bool { a, b := sorted[i], sorted[j] switch { case a.VendorID != b.VendorID: return a.VendorID < b.VendorID case a.ProductID != b.ProductID: return a.ProductID < b.ProductID default: return a.Path < b.Path } }) for i := range sorted { sorted[i].Index = i + 1 } return sorted }