| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- package main
- import (
- "fmt"
- "os"
- "path/filepath"
- intrgb "netdome.biz/paul/qmk-rgb/internal/rgb"
- "netdome.biz/paul/qmk-rgb/internal/via"
- )
- // definitionFlag names a definition file to use instead of looking one up.
- var definitionFlag string
- // resolveCatalog returns the effect catalog for a board, in the order the tool
- // trusts: the file named by --definition, then the definition in the data
- // directory that matches the board, then the catalog compiled in for a known
- // board. The second return value says which of the three it was, because a
- // board that has no names at all is a different situation from one whose names
- // came from somewhere the user can see.
- //
- // This is the one place a catalog is looked up. A command that reached for a
- // catalog itself would silently ignore a definition file the user had placed,
- // which is the whole point of having one.
- func resolveCatalog(target targetDeviceData) (*intrgb.Catalog, string, error) {
- if definitionFlag != "" {
- def, err := intrgb.LoadDefinition(definitionFlag)
- if err != nil {
- return nil, "", err
- }
- if !def.Matches(target.Device.VendorID, target.Device.ProductID) {
- return nil, "", fmt.Errorf("%s is a definition for %s (0x%04X/0x%04X), not for this keyboard (0x%04X/0x%04X)",
- definitionFlag, def.Name, def.VendorID, def.ProductID,
- target.Device.VendorID, target.Device.ProductID)
- }
- return def.Catalog, def.Path, nil
- }
- catalog, source, err := resolveCatalogFor(target.Device.VendorID, target.Device.ProductID)
- return catalog, source, err
- }
- // resolveCatalogFor is the lookup without a target, for the commands that report
- // on a board rather than open it.
- func resolveCatalogFor(vendorID, productID uint16) (*intrgb.Catalog, string, error) {
- if definitionFlag != "" {
- def, err := intrgb.LoadDefinition(definitionFlag)
- if err != nil {
- return nil, "", err
- }
- if !def.Matches(vendorID, productID) {
- return nil, "", nil
- }
- return def.Catalog, def.Path, nil
- }
- if dir, err := definitionsDir(); err == nil {
- if defs, loadErr := intrgb.LoadDefinitionsDir(dir); loadErr == nil {
- if def := intrgb.FindDefinition(defs, vendorID, productID); def != nil {
- return def.Catalog, def.Path, nil
- }
- }
- }
- catalog, ok := intrgb.CatalogFor(vendorID, productID)
- if !ok {
- return nil, "", nil
- }
- return catalog, fmt.Sprintf("built in (%s)", catalog.Name()), nil
- }
- // lookupPaths returns the directories a data file is looked for in: the one
- // holding the executable, then the working directory, which is the order the
- // profile directory uses too.
- func lookupPaths() []string {
- var paths []string
- if exe, err := os.Executable(); err == nil {
- paths = append(paths, filepath.Dir(exe))
- }
- if cwd, err := os.Getwd(); err == nil {
- paths = append(paths, cwd)
- }
- return paths
- }
- // definitionLabels returns the channel names a definition gives the board, which
- // are the names VIA shows. They are the only channel names there are: a board
- // without a definition is addressed by its QMK subsystem name.
- func definitionLabels(vendorID, productID uint16) map[uint16]string {
- def := loadedDefinition(vendorID, productID)
- if def == nil {
- return nil
- }
- return def.Labels
- }
- // loadedDefinition returns the definition file for a board, from the data
- // directory or from the file --definition names, and nil when there is none.
- func loadedDefinition(vendorID, productID uint16) *intrgb.Definition {
- if definitionFlag != "" {
- if def, err := intrgb.LoadDefinition(definitionFlag); err == nil && def.Matches(vendorID, productID) {
- return def
- }
- return nil
- }
- dir, err := definitionsDir()
- if err != nil {
- return nil
- }
- defs, err := intrgb.LoadDefinitionsDir(dir)
- if err != nil {
- return nil
- }
- return intrgb.FindDefinition(defs, vendorID, productID)
- }
- // applyDefinitionLabels returns the display names with the definition's names
- // laid over them, and the names each channel keeps as alternatives. A definition
- // names a channel as VIA does, and the keyboards.json name and the QMK subsystem
- // name both stay accepted, so a command written before the definition arrived
- // keeps working.
- func applyDefinitionLabels(display map[uint16]string, vendorID, productID uint16) (map[uint16]string, map[uint16][]string) {
- labels := definitionLabels(vendorID, productID)
- merged := make(map[uint16]string, len(display)+len(labels))
- for number, name := range display {
- merged[number] = name
- }
- alternatives := make(map[uint16][]string, len(display))
- for number, label := range labels {
- if existing, ok := merged[number]; ok && existing != label {
- alternatives[number] = append(alternatives[number], existing)
- }
- merged[number] = label
- }
- for number, name := range merged {
- if subsystem := via.Channel(number).Subsystem(); subsystem != "" && subsystem != name {
- alternatives[number] = append(alternatives[number], subsystem)
- }
- }
- return merged, alternatives
- }
- // definitionsDir returns the data directory, looked for next to the executable
- // first and then in the working directory.
- var definitionsDir = func() (string, error) {
- paths := lookupPaths()
- for _, p := range paths {
- dir := filepath.Join(p, intrgb.DefinitionsDir)
- if info, err := os.Stat(dir); err == nil && info.IsDir() {
- return dir, nil
- }
- }
- if len(paths) == 0 {
- return intrgb.DefinitionsDir, nil
- }
- return filepath.Join(paths[0], intrgb.DefinitionsDir), nil
- }
- // ensureDefinitionsDir returns the data directory, creating it if it is not
- // there yet, so a fetch has somewhere to write to.
- func ensureDefinitionsDir() (string, error) {
- dir, err := definitionsDir()
- if err != nil {
- return "", err
- }
- if err := os.MkdirAll(dir, 0o755); err != nil {
- return "", fmt.Errorf("create %s: %w", dir, err)
- }
- return dir, nil
- }
|