device.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package device
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "github.com/wobkey/rgb/internal/hid"
  8. )
  9. // Keyboard represents a VIA-compatible keyboard from keyboards.json.
  10. type Keyboard struct {
  11. Name string `json:"name"`
  12. VendorID uint16 `json:"vendorId"`
  13. ProductID uint16 `json:"productId"`
  14. Protocol string `json:"protocol"`
  15. VIAVersion int `json:"viaVersion"`
  16. LEDLayout string `json:"ledLayout"`
  17. }
  18. // Device represents a connected keyboard device.
  19. type Device struct {
  20. Keyboard
  21. Path string
  22. }
  23. // LoadKeyboards reads keyboard definitions from keyboards.json.
  24. func LoadKeyboards() ([]Keyboard, error) {
  25. path, err := findKeyboardsJSON()
  26. if err != nil {
  27. return nil, fmt.Errorf("find keyboards.json: %w", err)
  28. }
  29. data, err := os.ReadFile(path)
  30. if err != nil {
  31. return nil, fmt.Errorf("read keyboards.json: %w", err)
  32. }
  33. var keyboards []Keyboard
  34. if err := json.Unmarshal(data, &keyboards); err != nil {
  35. return nil, fmt.Errorf("parse keyboards.json: %w", err)
  36. }
  37. return keyboards, nil
  38. }
  39. // findKeyboardsJSON searches for keyboards.json in common locations.
  40. var findKeyboardsJSON = func() (string, error) {
  41. exe, err := os.Executable()
  42. if err == nil {
  43. path := filepath.Join(filepath.Dir(exe), "keyboards.json")
  44. if _, err := os.Stat(path); err == nil {
  45. return path, nil
  46. }
  47. }
  48. path := "keyboards.json"
  49. if _, err := os.Stat(path); err == nil {
  50. return path, nil
  51. }
  52. cwd, err := os.Getwd()
  53. if err == nil {
  54. path := filepath.Join(cwd, "keyboards.json")
  55. if _, err := os.Stat(path); err == nil {
  56. return path, nil
  57. }
  58. }
  59. return "", fmt.Errorf("keyboards.json not found")
  60. }
  61. // Discover scans all HID devices and returns connected VIA-compatible keyboards.
  62. func Discover(keyboards []Keyboard) ([]Device, error) {
  63. devices, err := hid.DiscoverAll()
  64. if err != nil {
  65. return nil, fmt.Errorf("enumerate HID devices: %w", err)
  66. }
  67. fmt.Fprintf(os.Stderr, "DEBUG discover: keyboards=%d, hid_devices=%d\n", len(keyboards), len(devices))
  68. for _, kb := range keyboards {
  69. fmt.Fprintf(os.Stderr, "DEBUG discover: kb=%s VID=%d PID=%d\n", kb.Name, kb.VendorID, kb.ProductID)
  70. }
  71. for _, d := range devices {
  72. fmt.Fprintf(os.Stderr, "DEBUG discover: hid=%s VID=%d PID=%d raw=%t\n", d.Path, d.VendorID, d.ProductID, d.RawHID)
  73. }
  74. return matchKeyboards(keyboards, devices), nil
  75. }
  76. func matchKeyboards(keyboards []Keyboard, devices []hid.DeviceInfo) []Device {
  77. var found []Device
  78. for _, kb := range keyboards {
  79. for _, d := range devices {
  80. if !d.RawHID || d.VendorID != kb.VendorID || d.ProductID != kb.ProductID {
  81. continue
  82. }
  83. fmt.Fprintf(os.Stderr, "DEBUG discover: MATCH %s %s rawHID=true\n", kb.Name, d.Path)
  84. found = append(found, Device{
  85. Keyboard: kb,
  86. Path: d.Path,
  87. })
  88. }
  89. }
  90. fmt.Fprintf(os.Stderr, "DEBUG discover: found=%d\n", len(found))
  91. return found
  92. }