effects.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package rgb
  2. import (
  3. "fmt"
  4. "math"
  5. )
  6. // Effect defines an RGB lighting effect.
  7. type Effect uint8
  8. const (
  9. Static Effect = 0
  10. Breathing Effect = 1
  11. EffectAlmond Effect = 2
  12. BandSat Effect = 3
  13. BandVal Effect = 4
  14. BandHue Effect = 5
  15. Chroma Effect = 6
  16. Rainbow Effect = 7
  17. RainbowWave Effect = 8
  18. Dance Effect = 9
  19. RainbowDance Effect = 10
  20. ChromaDance Effect = 11
  21. Snake Effect = 12
  22. Tail Effect = 13
  23. Split Effect = 14
  24. Heartbeat Effect = 15
  25. Blink Effect = 16
  26. BlinkStatic Effect = 17
  27. Gradient Effect = 18
  28. Testing Effect = 19
  29. )
  30. // EffectName returns the human-readable name of an effect.
  31. func (e Effect) String() string {
  32. names := map[Effect]string{
  33. Static: "static",
  34. Breathing: "breathing",
  35. EffectAlmond: "almond",
  36. BandSat: "band_sat",
  37. BandVal: "band_val",
  38. BandHue: "band_hue",
  39. Chroma: "chroma",
  40. Rainbow: "rainbow",
  41. RainbowWave: "rainbow_wave",
  42. Dance: "dance",
  43. RainbowDance: "rainbow_dance",
  44. ChromaDance: "chroma_dance",
  45. Snake: "snake",
  46. Tail: "tail",
  47. Split: "split",
  48. Heartbeat: "heartbeat",
  49. Blink: "blink",
  50. BlinkStatic: "blink_static",
  51. Gradient: "gradient",
  52. Testing: "testing",
  53. }
  54. if name, ok := names[e]; ok {
  55. return name
  56. }
  57. return "unknown"
  58. }
  59. // ParseEffect converts a string name to an Effect.
  60. func ParseEffect(name string) (Effect, error) {
  61. switch name {
  62. case "static":
  63. return Static, nil
  64. case "breathing":
  65. return Breathing, nil
  66. case "almond":
  67. return EffectAlmond, nil
  68. case "band_sat":
  69. return BandSat, nil
  70. case "band_val":
  71. return BandVal, nil
  72. case "band_hue":
  73. return BandHue, nil
  74. case "chroma":
  75. return Chroma, nil
  76. case "rainbow":
  77. return Rainbow, nil
  78. case "rainbow_wave":
  79. return RainbowWave, nil
  80. case "dance":
  81. return Dance, nil
  82. case "rainbow_dance":
  83. return RainbowDance, nil
  84. case "chroma_dance":
  85. return ChromaDance, nil
  86. case "snake":
  87. return Snake, nil
  88. case "tail":
  89. return Tail, nil
  90. case "split":
  91. return Split, nil
  92. case "heartbeat":
  93. return Heartbeat, nil
  94. case "blink":
  95. return Blink, nil
  96. case "blink_static":
  97. return BlinkStatic, nil
  98. case "gradient":
  99. return Gradient, nil
  100. case "testing":
  101. return Testing, nil
  102. default:
  103. return 0, fmt.Errorf("unknown effect: %s", name)
  104. }
  105. }
  106. // LEDParam defines QMK rgblight parameter indices.
  107. type LEDParam uint8
  108. const (
  109. Mode LEDParam = 0
  110. Brightness LEDParam = 1
  111. Speed LEDParam = 2
  112. Enable LEDParam = 3
  113. Hue LEDParam = 26
  114. Saturation LEDParam = 27
  115. EffectID LEDParam = 48
  116. EffectSwitch LEDParam = 49
  117. BrightSet LEDParam = 50
  118. )
  119. // Color represents an RGB color.
  120. type Color struct {
  121. R uint8
  122. G uint8
  123. B uint8
  124. }
  125. // ParseHexColor parses a 6-digit hex color string (e.g. "ff0000").
  126. func ParseHexColor(s string) (Color, error) {
  127. if len(s) != 6 {
  128. return Color{}, fmt.Errorf("invalid hex color: %s", s)
  129. }
  130. r := hexDigit(s[0])<<4 + hexDigit(s[1])
  131. g := hexDigit(s[2])<<4 + hexDigit(s[3])
  132. b := hexDigit(s[4])<<4 + hexDigit(s[5])
  133. return Color{R: r, G: g, B: b}, nil
  134. }
  135. func hexDigit(c byte) uint8 {
  136. switch {
  137. case c >= '0' && c <= '9':
  138. return c - '0'
  139. case c >= 'a' && c <= 'f':
  140. return c - 'a' + 10
  141. case c >= 'A' && c <= 'F':
  142. return c - 'A' + 10
  143. default:
  144. return 0
  145. }
  146. }
  147. // State represents the current RGB state from the keyboard.
  148. type State struct {
  149. Enabled bool
  150. Mode Effect
  151. Brightness uint8
  152. Speed uint8
  153. Color Color
  154. }
  155. // HSV converts the color to HSV (QMK rgblight uses 0-255 ranges for H, S, V).
  156. func (c Color) HSV() (h, s, v uint8) {
  157. r, g, b := float64(c.R), float64(c.G), float64(c.B)
  158. max := math.Max(r, math.Max(g, b))
  159. min := math.Min(r, math.Min(g, b))
  160. v = uint8(max)
  161. d := max - min
  162. if d == 0 {
  163. return 0, 0, v
  164. }
  165. if max > 0 {
  166. s = uint8((d / max) * 255)
  167. } else {
  168. return 0, 0, 0
  169. }
  170. var hFloat float64
  171. switch {
  172. case max == r:
  173. hFloat = ((g - b) / d) * 60.0
  174. case max == g:
  175. hFloat = ((b-r)/d)*60.0 + 120.0
  176. case max == b:
  177. hFloat = ((r-g)/d)*60.0 + 240.0
  178. }
  179. if hFloat < 0 {
  180. hFloat += 360.0
  181. }
  182. h = uint8((hFloat / 360.0) * 255.0)
  183. if h >= 255 {
  184. h = 254
  185. }
  186. return h, s, v
  187. }