zones.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package main
  2. import (
  3. "fmt"
  4. "sort"
  5. "strings"
  6. "netdome.biz/paul/qmk-rgb/internal/via"
  7. )
  8. // resolveZoneName maps a --zone value to the channels it may mean, without
  9. // opening the keyboard. A display name of the connected board wins over a
  10. // subsystem name, and a value that would mean two channels is refused rather
  11. // than resolved to one of them.
  12. //
  13. // An empty name means every channel and is reported as a nil slice.
  14. func resolveZoneName(name string, display map[uint16]string, alternatives map[uint16][]string) ([]via.Channel, error) {
  15. if name == "" {
  16. return nil, nil
  17. }
  18. if channels := dedupeChannels(append(displayNameChannels(display, name), alternateNameChannels(alternatives, name)...)); len(channels) > 1 {
  19. return nil, fmt.Errorf("zone %q matches several channels of this keyboard; its definition gives the same name to more than one", name)
  20. } else if len(channels) == 1 {
  21. return channels, nil
  22. }
  23. for c := via.Channel(1); c <= via.AssignedChannelMax; c++ {
  24. if c.Subsystem() == name {
  25. return []via.Channel{c}, nil
  26. }
  27. }
  28. return nil, fmt.Errorf("unknown zone %q: use a channel name such as rgb_matrix, or the name this keyboard's definition gives it", name)
  29. }
  30. // displayNameChannels returns every channel the board names displayName. The
  31. // comparison ignores case, because a definition writes "Backlight" where a user
  32. // types "backlight", and the two mean the same channel.
  33. func displayNameChannels(display map[uint16]string, displayName string) []via.Channel {
  34. var channels []via.Channel
  35. for number, name := range display {
  36. if strings.EqualFold(name, displayName) {
  37. channels = append(channels, via.Channel(number))
  38. }
  39. }
  40. sort.Slice(channels, func(i, j int) bool { return channels[i] < channels[j] })
  41. return channels
  42. }
  43. // alternateNameChannels returns the channels a name reaches through the
  44. // alternatives a channel carries, which is how a QMK subsystem name keeps
  45. // working for a board whose definition calls that channel something else.
  46. func alternateNameChannels(alternatives map[uint16][]string, displayName string) []via.Channel {
  47. var channels []via.Channel
  48. for number, names := range alternatives {
  49. for _, name := range names {
  50. if strings.EqualFold(name, displayName) {
  51. channels = append(channels, via.Channel(number))
  52. break
  53. }
  54. }
  55. }
  56. sort.Slice(channels, func(i, j int) bool { return channels[i] < channels[j] })
  57. return channels
  58. }
  59. // displayNameConflicts rejects a board whose display name is also the subsystem
  60. // name of a different channel the keyboard actually has. A name that means two
  61. // channels is a silent retarget, so the tool refuses it.
  62. //
  63. // Presence is only known after the probe, which is why this is checked here
  64. // rather than when the file is read: a static check would reject the Impact 80,
  65. // which calls channel 3 "backlight" while channel 1 is absent.
  66. func displayNameConflicts(display map[uint16]string, present []via.Channel) error {
  67. names := make([]string, 0, len(display))
  68. for _, name := range display {
  69. names = append(names, name)
  70. }
  71. sort.Strings(names)
  72. for _, name := range names {
  73. named := via.Channel(0)
  74. for _, ch := range displayNameChannels(display, name) {
  75. named = ch
  76. break
  77. }
  78. for _, other := range present {
  79. if other != named && other.Subsystem() == name {
  80. return fmt.Errorf("channel %d is named %q, which is also the subsystem name of channel %d", named, name, other)
  81. }
  82. }
  83. }
  84. return nil
  85. }
  86. // channelName is how a channel is printed: the board's display name when it has
  87. // one, otherwise the QMK subsystem name.
  88. func channelName(c via.Channel, display map[uint16]string) string {
  89. if name, ok := display[uint16(c)]; ok {
  90. return name
  91. }
  92. return c.Subsystem()
  93. }
  94. // dedupeChannels drops repeats, keeping the order. A name can reach the same
  95. // channel twice — once as the definition's label and once as the subsystem name
  96. // it replaces — and that is one channel, not a conflict.
  97. func dedupeChannels(channels []via.Channel) []via.Channel {
  98. seen := make(map[via.Channel]bool, len(channels))
  99. out := make([]via.Channel, 0, len(channels))
  100. for _, ch := range channels {
  101. if seen[ch] {
  102. continue
  103. }
  104. seen[ch] = true
  105. out = append(out, ch)
  106. }
  107. return out
  108. }