package main import ( "fmt" "strings" "github.com/spf13/cobra" intrgb "netdome.biz/paul/qmk-rgb/internal/rgb" "netdome.biz/paul/qmk-rgb/internal/via" ) var listEffects bool // resolveEffectTargets turns a name into one target per channel. Whether the // user named a channel is passed in, because a board with a single channel is // not an explicit request for it. func resolveEffectTargets(catalog *intrgb.Catalog, name string, channels []via.Channel) ([]intrgb.EffectTarget, []string, error) { return intrgb.ResolveEffect(catalog, name, channels, targetZone != "") } func NewEffectCmd() *cobra.Command { cmd := &cobra.Command{ Use: "effect [name]", Short: "Set or list RGB effects", Long: "Set the RGB lighting effect on the connected keyboard. Without an argument, lists all effects.\n" + "Effect names come from a per-board catalog; a keyboard without one has no names\n" + "and is driven with `mode ` instead.", Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if listEffects || len(args) == 0 { return listAllEffects(cmd) } return runEffectSet(cmd, args) }, } cmd.Flags().BoolVar(&listEffects, "list", false, "List all available effects per zone") return cmd } func listAllEffects(cmd *cobra.Command) error { proto, target, channels, err := openTarget() if err != nil { return err } defer proto.Close() catalog, _ := intrgb.CatalogFor(target.Device.VendorID, target.Device.ProductID) return printEffectList(cmd, catalog, channels, target.Display) } func printEffectList(cmd *cobra.Command, catalog *intrgb.Catalog, channels []via.Channel, display map[uint16]string) error { type ZoneEffectList struct { Zone string `json:"zone"` Channel uint8 `json:"channel"` Subsystem string `json:"subsystem"` Effect string `json:"effect"` ID uint8 `json:"id"` } type EffectList struct { Catalog string `json:"catalog"` Zones []ZoneEffectList `json:"zones"` } list := EffectList{Catalog: catalog.Name(), Zones: []ZoneEffectList{}} for _, ch := range channels { for id, name := range catalog.Names(ch) { list.Zones = append(list.Zones, ZoneEffectList{ Zone: channelName(ch, display), Channel: uint8(ch), Subsystem: ch.Subsystem(), Effect: name, ID: uint8(id), }) } } return encodeJSON(cmd.OutOrStdout(), list) } func runEffectSet(cmd *cobra.Command, args []string) error { proto, target, channels, err := openTarget() if err != nil { return err } defer proto.Close() catalog, _ := intrgb.CatalogFor(target.Device.VendorID, target.Device.ProductID) targets, skipped, err := resolveEffectTargets(catalog, args[0], channels) if err != nil { return err } if len(skipped) > 0 { fmt.Fprintf(cmd.ErrOrStderr(), "Warning: effect not supported on channel(s): %s\n", strings.Join(skipped, ", ")) } results, err := setEffectVerified(proto, targets, target.Display, catalog) if err != nil { return err } // Claiming the requested effect when the keyboard kept another one is the // contradiction this read-back exists to prevent: on the logo and side // channels an effect of ID 0 leaves the previous effect running. if anyEffectMismatch(results) { fmt.Fprintln(cmd.OutOrStdout(), formatEffectResults(args[0], results)) return nil } fmt.Fprintf(cmd.OutOrStdout(), "Effect set to %q\n", args[0]) return nil }