| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- package device
- import (
- "encoding/json"
- "fmt"
- "os"
- "path/filepath"
- "sort"
- "netdome.biz/paul/impact-80/internal/hid"
- )
- // Keyboard is a metadata record from keyboards.json.
- // It names a known keyboard, but it is not required for discovery: unknown
- // QMK keyboards are usable too. Fields the tool does not read are ignored,
- // so existing entries may keep carrying them.
- type Keyboard struct {
- Name string `json:"name"`
- VendorID uint16 `json:"vendorId"`
- ProductID uint16 `json:"productId"`
- }
- // 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"`
- Known bool `json:"known"`
- }
- // DiscoverAll returns every connected QMK Raw HID keyboard, ordered
- // deterministically and numbered from 1. Keyboards absent from
- // keyboards.json are included with Known false and no Name.
- func DiscoverAll() ([]Device, error) {
- infos, err := hid.DiscoverAll()
- if err != nil {
- return nil, fmt.Errorf("discover qmk devices: %w", err)
- }
- known, err := knownKeyboards()
- if err != nil {
- // Missing or malformed keyboards.json only costs us names, not
- // the ability to drive the keyboard.
- known = nil
- }
- devices := make([]Device, 0, len(infos))
- for _, info := range infos {
- kb, ok := known[deviceKey(info.VendorID, info.ProductID)]
- devices = append(devices, Device{
- Path: info.Path,
- VendorID: info.VendorID,
- ProductID: info.ProductID,
- Name: kb.Name,
- Known: ok,
- })
- }
- 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
- }
- func deviceKey(vendorID, productID uint16) string {
- return fmt.Sprintf("%04x:%04x", vendorID, productID)
- }
- func knownKeyboards() (map[string]Keyboard, error) {
- keyboards, err := LoadKeyboards()
- if err != nil {
- return nil, err
- }
- known := make(map[string]Keyboard, len(keyboards))
- for _, kb := range keyboards {
- known[deviceKey(kb.VendorID, kb.ProductID)] = kb
- }
- return known, nil
- }
- // 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")
- }
|