zones.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package main
  2. import (
  3. "fmt"
  4. "sort"
  5. "netdome.biz/paul/qmk-rgb/internal/via"
  6. )
  7. // resolveZoneName maps a --zone value to the channels it may mean, without
  8. // opening the keyboard. A display name of the connected board wins over a
  9. // subsystem name, and a value that would mean two channels is refused rather
  10. // than resolved to one of them.
  11. //
  12. // An empty name means every channel and is reported as a nil slice.
  13. func resolveZoneName(name string, display map[uint16]string) ([]via.Channel, error) {
  14. if name == "" {
  15. return nil, nil
  16. }
  17. if channels := displayNameChannels(display, name); len(channels) > 1 {
  18. return nil, fmt.Errorf("zone %q matches several channels of this keyboard; keyboards.json gives the same name to more than one", name)
  19. } else if len(channels) == 1 {
  20. return channels, nil
  21. }
  22. for c := via.Channel(1); c <= via.AssignedChannelMax; c++ {
  23. if c.Subsystem() == name {
  24. return []via.Channel{c}, nil
  25. }
  26. }
  27. return nil, fmt.Errorf("unknown zone %q: use a channel name such as rgb_matrix, or a name from keyboards.json", name)
  28. }
  29. // displayNameChannels returns every channel the board names displayName.
  30. func displayNameChannels(display map[uint16]string, displayName string) []via.Channel {
  31. var channels []via.Channel
  32. for number, name := range display {
  33. if name == displayName {
  34. channels = append(channels, via.Channel(number))
  35. }
  36. }
  37. sort.Slice(channels, func(i, j int) bool { return channels[i] < channels[j] })
  38. return channels
  39. }
  40. // displayNameConflicts rejects a board whose display name is also the subsystem
  41. // name of a different channel the keyboard actually has. A name that means two
  42. // channels is a silent retarget, so the tool refuses it.
  43. //
  44. // Presence is only known after the probe, which is why this is checked here
  45. // rather than when the file is read: a static check would reject the Impact 80,
  46. // which calls channel 3 "backlight" while channel 1 is absent.
  47. func displayNameConflicts(display map[uint16]string, present []via.Channel) error {
  48. names := make([]string, 0, len(display))
  49. for _, name := range display {
  50. names = append(names, name)
  51. }
  52. sort.Strings(names)
  53. for _, name := range names {
  54. named := via.Channel(0)
  55. for _, ch := range displayNameChannels(display, name) {
  56. named = ch
  57. break
  58. }
  59. for _, other := range present {
  60. if other != named && other.Subsystem() == name {
  61. return fmt.Errorf("keyboards.json: channel %d is named %q, which is also the subsystem name of channel %d", named, name, other)
  62. }
  63. }
  64. }
  65. return nil
  66. }
  67. // channelName is how a channel is printed: the board's display name when it has
  68. // one, otherwise the QMK subsystem name.
  69. func channelName(c via.Channel, display map[uint16]string) string {
  70. if name, ok := display[uint16(c)]; ok {
  71. return name
  72. }
  73. return c.Subsystem()
  74. }