| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- package rgb
- import (
- "fmt"
- "netdome.biz/paul/qmk-rgb/internal/via"
- )
- // EffectTarget is one effect ID to write to one channel.
- type EffectTarget struct {
- Channel via.Channel
- ID uint8
- }
- const unknownEffectName = "unknown"
- // Catalog is one board's effect names, per channel. The keyboard holds numbers,
- // not names, so `effect <name>` needs a catalog and a board without one is
- // driven through raw IDs.
- type Catalog struct {
- board string
- names map[via.Channel][]string
- aliases map[via.Channel]map[string]string
- defaults map[via.Channel]uint8
- }
- // CatalogFor returns the effect catalog of a board, and whether one exists.
- //
- // Provenance, because these lists look invented and are not:
- //
- // - The 46 backlight names are QMK's rgb_matrix_effects.inc in order, each ID
- // verified against the live register of an Impact 80.
- // - The 7 logo and 7 side names are this board's vendor VIA definition, whose
- // dropdowns read "fixed wave" and "breathe". The tool spells them
- // fixed_wave and breathing and accepts the vendor's spellings as aliases.
- // - The brightness and speed transforms documented in README.md were measured
- // on the unit, not read from anywhere.
- func CatalogFor(vendorID, productID uint16) (*Catalog, bool) {
- if vendorID != 0x36B0 || productID != 0x309F {
- return nil, false
- }
- return &Catalog{
- board: "impact80",
- names: map[via.Channel][]string{
- via.ChannelRgblight: impact80LogoEffects[:],
- via.ChannelRgbMatrix: impact80BacklightEffects[:],
- via.ChannelAudio: impact80SideEffects[:],
- },
- aliases: map[via.Channel]map[string]string{
- via.ChannelRgblight: {
- "off": "none",
- "breathe": "breathing",
- "rainbow": "spectrum",
- "rainbow_wave": "wave",
- "solid": "light",
- "static": "solid",
- },
- via.ChannelRgbMatrix: {
- "off": "none",
- "breathe": "breathing",
- "rainbow": "rainbow_moving_chevron",
- "solid": "solid_color",
- "static": "solid",
- },
- via.ChannelAudio: {
- "off": "none",
- "breathe": "breathing",
- "rainbow": "spectrum",
- "rainbow_wave": "wave",
- "solid": "light",
- "static": "solid",
- },
- },
- defaults: map[via.Channel]uint8{
- via.ChannelRgblight: 4,
- via.ChannelRgbMatrix: 5,
- via.ChannelAudio: 4,
- },
- }, true
- }
- // Name returns the catalog's board name, which `effect --list` reports.
- func (c *Catalog) Name() string {
- if c == nil {
- return ""
- }
- return c.board
- }
- // Names returns the effect names of a channel, or nil when the catalog says
- // nothing about it.
- func (c *Catalog) Names(ch via.Channel) []string {
- if c == nil {
- return nil
- }
- return c.names[ch]
- }
- // EffectName returns the name of an effect ID, or "unknown" when the catalog has
- // no entry for it.
- func (c *Catalog) EffectName(ch via.Channel, id uint8) string {
- if c == nil {
- return unknownEffectName
- }
- names := c.names[ch]
- if int(id) >= len(names) {
- return unknownEffectName
- }
- return names[id]
- }
- // EffectID resolves an effect name on a channel, following one level of alias.
- func (c *Catalog) EffectID(ch via.Channel, name string) (uint8, bool) {
- if c == nil {
- return 0, false
- }
- for id, candidate := range c.names[ch] {
- if candidate == name {
- return uint8(id), true
- }
- }
- if canonical, ok := c.aliases[ch][name]; ok {
- for id, candidate := range c.names[ch] {
- if candidate == canonical {
- return uint8(id), true
- }
- }
- }
- return 0, false
- }
- // DefaultEffect returns the effect ID enable writes when turning a channel on.
- func (c *Catalog) DefaultEffect(ch via.Channel) (uint8, bool) {
- if c == nil {
- return 0, false
- }
- id, ok := c.defaults[ch]
- return id, ok
- }
- // ResolveEffect turns an effect name into one target per channel that supports
- // it, plus the subsystem names of those that do not. A skip is an error rather
- // than a warning when the caller asked for one channel explicitly, because a
- // command that silently did nothing looks like a command that worked. That is
- // the caller's knowledge to pass: a board with a single lighting channel is not
- // an explicit request for it.
- func ResolveEffect(catalog *Catalog, name string, channels []via.Channel, explicit bool) ([]EffectTarget, []string, error) {
- if catalog == nil {
- return nil, nil, fmt.Errorf("no effect catalog for this keyboard; set an effect by number with `mode <index>`")
- }
- // The compatibility spelling every catalog shares, so a caller cannot
- // resolve "static" differently from another.
- if name == "static" {
- name = "solid"
- }
- known := false
- for _, ch := range channels {
- if _, ok := catalog.EffectID(ch, name); ok {
- known = true
- break
- }
- }
- if !known {
- return nil, nil, fmt.Errorf("unknown effect: %s", name)
- }
- var targets []EffectTarget
- var skipped []string
- for _, ch := range channels {
- id, ok := catalog.EffectID(ch, name)
- if !ok {
- if explicit {
- return nil, nil, fmt.Errorf("effect %s is not supported on %s", name, ch.Subsystem())
- }
- skipped = append(skipped, ch.Subsystem())
- continue
- }
- targets = append(targets, EffectTarget{Channel: ch, ID: id})
- }
- return targets, skipped, nil
- }
|