diff --git a/lib/rgb_matrix/effect.ex b/lib/rgb_matrix/animation.ex similarity index 50% rename from lib/rgb_matrix/effect.ex rename to lib/rgb_matrix/animation.ex index 552cd52..efb4992 100644 --- a/lib/rgb_matrix/effect.ex +++ b/lib/rgb_matrix/animation.ex @@ -1,4 +1,4 @@ -defmodule RGBMatrix.Effect do +defmodule RGBMatrix.Animation do alias Layout.LED @callback new(leds :: list(LED.t()), config :: any) :: {render_in, any} @@ -15,7 +15,7 @@ defmodule RGBMatrix.Effect do defmacro __using__(_) do quote do - @behaviour RGBMatrix.Effect + @behaviour RGBMatrix.Animation end end @@ -30,7 +30,6 @@ defmodule RGBMatrix.Effect do | __MODULE__.SolidColor | __MODULE__.Breathing | __MODULE__.SolidReactive - | __MODULE__.Splash @doc """ Returns a list of the available types of animations. @@ -45,44 +44,45 @@ defmodule RGBMatrix.Effect do __MODULE__.RandomKeypresses, __MODULE__.SolidColor, __MODULE__.Breathing, - __MODULE__.SolidReactive, - __MODULE__.Splash + __MODULE__.SolidReactive ] end @doc """ - Returns an effect's initial state. + Returns an animation's initial state. """ - @spec new(effect_type :: type, leds :: list(LED.t())) :: {render_in, t} - def new(effect_type, leds) do - config_module = Module.concat([effect_type, Config]) - effect_config = config_module.new() - {render_in, effect_state} = effect_type.new(leds, effect_config) + @spec new(animation_type :: type, leds :: list(LED.t())) :: {render_in, t} + def new(animation_type, leds) do + config_module = Module.concat([animation_type, Config]) + animation_config = config_module.new() + {render_in, animation_state} = animation_type.new(leds, animation_config) - effect = %__MODULE__{ - type: effect_type, - config: effect_config, - state: effect_state + animation = %__MODULE__{ + type: animation_type, + config: animation_config, + state: animation_state } - {render_in, effect} + {render_in, animation} end @doc """ - Returns the next state of an effect based on its current state. + Returns the next state of an animation based on its current state. """ - @spec render(effect :: t) :: {list(RGBMatrix.any_color_model()), render_in, t} - def render(effect) do - {colors, render_in, effect_state} = effect.type.render(effect.state, effect.config) - {colors, render_in, %{effect | state: effect_state}} + @spec render(animation :: t) :: {list(RGBMatrix.any_color_model()), render_in, t} + def render(animation) do + {colors, render_in, animation_state} = + animation.type.render(animation.state, animation.config) + + {colors, render_in, %{animation | state: animation_state}} end @doc """ - Sends an interaction event to an effect. + Sends an interaction event to an animation. """ - @spec interact(effect :: t, led :: LED.t()) :: {render_in, t} - def interact(effect, led) do - {render_in, effect_state} = effect.type.interact(effect.state, effect.config, led) - {render_in, %{effect | state: effect_state}} + @spec interact(animation :: t, led :: LED.t()) :: {render_in, t} + def interact(animation, led) do + {render_in, animation_state} = animation.type.interact(animation.state, animation.config, led) + {render_in, %{animation | state: animation_state}} end end diff --git a/lib/rgb_matrix/effect/breathing.ex b/lib/rgb_matrix/animation/breathing.ex similarity index 87% rename from lib/rgb_matrix/effect/breathing.ex rename to lib/rgb_matrix/animation/breathing.ex index 924d6c6..7b59c1c 100644 --- a/lib/rgb_matrix/effect/breathing.ex +++ b/lib/rgb_matrix/animation/breathing.ex @@ -1,15 +1,15 @@ -defmodule RGBMatrix.Effect.Breathing do +defmodule RGBMatrix.Animation.Breathing do @moduledoc """ Single hue brightness cycling. """ alias Chameleon.HSV - alias RGBMatrix.Effect + alias RGBMatrix.Animation - use Effect + use Animation defmodule Config do - use RGBMatrix.Effect.Config + use RGBMatrix.Animation.Config end defmodule State do diff --git a/lib/rgb_matrix/effect/config.ex b/lib/rgb_matrix/animation/config.ex similarity index 81% rename from lib/rgb_matrix/effect/config.ex rename to lib/rgb_matrix/animation/config.ex index d7ad6b6..3e4adcf 100644 --- a/lib/rgb_matrix/effect/config.ex +++ b/lib/rgb_matrix/animation/config.ex @@ -1,23 +1,23 @@ -defmodule RGBMatrix.Effect.Config do +defmodule RGBMatrix.Animation.Config do @callback schema() :: keyword(any) @callback new(%{optional(atom) => any}) :: struct @callback update(struct, %{optional(atom) => any}) :: struct @types %{ - integer: RGBMatrix.Effect.Config.Integer, - option: RGBMatrix.Effect.Config.Option + integer: RGBMatrix.Animation.Config.Integer, + option: RGBMatrix.Animation.Config.Option } defmacro __using__(_) do quote do - import RGBMatrix.Effect.Config + import RGBMatrix.Animation.Config Module.register_attribute(__MODULE__, :fields, accumulate: true, persist: false ) - @before_compile RGBMatrix.Effect.Config + @before_compile RGBMatrix.Animation.Config end end @@ -36,17 +36,17 @@ defmodule RGBMatrix.Effect.Config do schema = Macro.escape(schema) quote do - @behaviour RGBMatrix.Effect.Config + @behaviour RGBMatrix.Animation.Config @enforce_keys unquote(keys) defstruct unquote(keys) - @impl RGBMatrix.Effect.Config + @impl RGBMatrix.Animation.Config def schema do unquote(schema) end - @impl RGBMatrix.Effect.Config + @impl RGBMatrix.Animation.Config def new(params \\ %{}) do schema() |> Map.new(fn {key, %mod{} = type} -> @@ -60,7 +60,7 @@ defmodule RGBMatrix.Effect.Config do |> (&struct!(__MODULE__, &1)).() end - @impl RGBMatrix.Effect.Config + @impl RGBMatrix.Animation.Config def update(config, params) do schema = schema() diff --git a/lib/rgb_matrix/effect/config/integer.ex b/lib/rgb_matrix/animation/config/integer.ex similarity index 95% rename from lib/rgb_matrix/effect/config/integer.ex rename to lib/rgb_matrix/animation/config/integer.ex index 3d206dc..71a4cb0 100644 --- a/lib/rgb_matrix/effect/config/integer.ex +++ b/lib/rgb_matrix/animation/config/integer.ex @@ -1,4 +1,4 @@ -defmodule RGBMatrix.Effect.Config.Integer do +defmodule RGBMatrix.Animation.Config.Integer do @enforce_keys [:default, :min, :max] defstruct [:default, :min, :max, step: 1] diff --git a/lib/rgb_matrix/effect/config/option.ex b/lib/rgb_matrix/animation/config/option.ex similarity index 92% rename from lib/rgb_matrix/effect/config/option.ex rename to lib/rgb_matrix/animation/config/option.ex index 1771348..583eebb 100644 --- a/lib/rgb_matrix/effect/config/option.ex +++ b/lib/rgb_matrix/animation/config/option.ex @@ -1,4 +1,4 @@ -defmodule RGBMatrix.Effect.Config.Option do +defmodule RGBMatrix.Animation.Config.Option do @enforce_keys [:default, :options] defstruct [:default, :options] diff --git a/lib/rgb_matrix/effect/cycle_all.ex b/lib/rgb_matrix/animation/cycle_all.ex similarity index 86% rename from lib/rgb_matrix/effect/cycle_all.ex rename to lib/rgb_matrix/animation/cycle_all.ex index bf947c0..334d90c 100644 --- a/lib/rgb_matrix/effect/cycle_all.ex +++ b/lib/rgb_matrix/animation/cycle_all.ex @@ -1,17 +1,17 @@ -defmodule RGBMatrix.Effect.CycleAll do +defmodule RGBMatrix.Animation.CycleAll do @moduledoc """ Cycles the hue of all LEDs at the same time. """ alias Chameleon.HSV - alias RGBMatrix.Effect + alias RGBMatrix.Animation - use Effect + use Animation import RGBMatrix.Utils, only: [mod: 2] defmodule Config do - use RGBMatrix.Effect.Config + use RGBMatrix.Animation.Config end defmodule State do diff --git a/lib/rgb_matrix/effect/hue_wave.ex b/lib/rgb_matrix/animation/hue_wave.ex similarity index 94% rename from lib/rgb_matrix/effect/hue_wave.ex rename to lib/rgb_matrix/animation/hue_wave.ex index 834c992..1134d9d 100644 --- a/lib/rgb_matrix/effect/hue_wave.ex +++ b/lib/rgb_matrix/animation/hue_wave.ex @@ -1,18 +1,18 @@ -defmodule RGBMatrix.Effect.HueWave do +defmodule RGBMatrix.Animation.HueWave do @moduledoc """ Creates a wave of shifting hue that moves across the matrix. """ alias Chameleon.HSV alias Layout.LED - alias RGBMatrix.Effect + alias RGBMatrix.Animation - use Effect + use Animation import RGBMatrix.Utils, only: [mod: 2] defmodule Config do - use RGBMatrix.Effect.Config + use RGBMatrix.Animation.Config @doc name: "Speed", description: """ diff --git a/lib/rgb_matrix/effect/pinwheel.ex b/lib/rgb_matrix/animation/pinwheel.ex similarity index 91% rename from lib/rgb_matrix/effect/pinwheel.ex rename to lib/rgb_matrix/animation/pinwheel.ex index d5961f3..ccf7c03 100644 --- a/lib/rgb_matrix/effect/pinwheel.ex +++ b/lib/rgb_matrix/animation/pinwheel.ex @@ -1,18 +1,18 @@ -defmodule RGBMatrix.Effect.Pinwheel do +defmodule RGBMatrix.Animation.Pinwheel do @moduledoc """ Cycles hue in a pinwheel pattern. """ alias Chameleon.HSV alias Layout.LED - alias RGBMatrix.Effect + alias RGBMatrix.Animation - use Effect + use Animation import RGBMatrix.Utils, only: [mod: 2] defmodule Config do - use RGBMatrix.Effect.Config + use RGBMatrix.Animation.Config end defmodule State do diff --git a/lib/rgb_matrix/effect/random_keypresses.ex b/lib/rgb_matrix/animation/random_keypresses.ex similarity index 87% rename from lib/rgb_matrix/effect/random_keypresses.ex rename to lib/rgb_matrix/animation/random_keypresses.ex index e837aab..30c78bd 100644 --- a/lib/rgb_matrix/effect/random_keypresses.ex +++ b/lib/rgb_matrix/animation/random_keypresses.ex @@ -1,15 +1,15 @@ -defmodule RGBMatrix.Effect.RandomKeypresses do +defmodule RGBMatrix.Animation.RandomKeypresses do @moduledoc """ Changes every key pressed to a random color. """ alias Chameleon.HSV - alias RGBMatrix.Effect + alias RGBMatrix.Animation - use Effect + use Animation defmodule Config do - use RGBMatrix.Effect.Config + use RGBMatrix.Animation.Config end defmodule State do diff --git a/lib/rgb_matrix/effect/random_solid.ex b/lib/rgb_matrix/animation/random_solid.ex similarity index 84% rename from lib/rgb_matrix/effect/random_solid.ex rename to lib/rgb_matrix/animation/random_solid.ex index ba7243d..6c510b4 100644 --- a/lib/rgb_matrix/effect/random_solid.ex +++ b/lib/rgb_matrix/animation/random_solid.ex @@ -1,15 +1,15 @@ -defmodule RGBMatrix.Effect.RandomSolid do +defmodule RGBMatrix.Animation.RandomSolid do @moduledoc """ A random solid color fills the entire matrix and changes every key-press. """ alias Chameleon.HSV - alias RGBMatrix.Effect + alias RGBMatrix.Animation - use Effect + use Animation defmodule Config do - use RGBMatrix.Effect.Config + use RGBMatrix.Animation.Config end defmodule State do diff --git a/lib/rgb_matrix/effect/solid_color.ex b/lib/rgb_matrix/animation/solid_color.ex similarity index 83% rename from lib/rgb_matrix/effect/solid_color.ex rename to lib/rgb_matrix/animation/solid_color.ex index 2d554a7..1ee7d6c 100644 --- a/lib/rgb_matrix/effect/solid_color.ex +++ b/lib/rgb_matrix/animation/solid_color.ex @@ -1,15 +1,15 @@ -defmodule RGBMatrix.Effect.SolidColor do +defmodule RGBMatrix.Animation.SolidColor do @moduledoc """ All LEDs are a solid color. """ alias Chameleon.HSV - alias RGBMatrix.Effect + alias RGBMatrix.Animation - use Effect + use Animation defmodule Config do - use RGBMatrix.Effect.Config + use RGBMatrix.Animation.Config end defmodule State do diff --git a/lib/rgb_matrix/effect/solid_reactive.ex b/lib/rgb_matrix/animation/solid_reactive.ex similarity index 95% rename from lib/rgb_matrix/effect/solid_reactive.ex rename to lib/rgb_matrix/animation/solid_reactive.ex index e00bba4..105e314 100644 --- a/lib/rgb_matrix/effect/solid_reactive.ex +++ b/lib/rgb_matrix/animation/solid_reactive.ex @@ -1,17 +1,17 @@ -defmodule RGBMatrix.Effect.SolidReactive do +defmodule RGBMatrix.Animation.SolidReactive do @moduledoc """ Static single hue, pulses keys hit to shifted hue then fades to current hue. """ alias Chameleon.HSV - alias RGBMatrix.Effect + alias RGBMatrix.Animation - use Effect + use Animation import RGBMatrix.Utils, only: [mod: 2] defmodule Config do - use RGBMatrix.Effect.Config + use RGBMatrix.Animation.Config @doc name: "Speed", description: """ diff --git a/lib/rgb_matrix/engine.ex b/lib/rgb_matrix/engine.ex index 2bca5fa..1641a30 100644 --- a/lib/rgb_matrix/engine.ex +++ b/lib/rgb_matrix/engine.ex @@ -2,18 +2,18 @@ require Logger defmodule RGBMatrix.Engine do @moduledoc """ - Renders [`Effect`](`RGBMatrix.Effect`)s and outputs colors to be displayed by - [`Paintable`](`RGBMatrix.Paintable`)s. + Renders [`Animation`](`RGBMatrix.Animation`)s and outputs colors to be + displayed by [`Paintable`](`RGBMatrix.Paintable`)s. """ use GenServer alias Layout.LED - alias RGBMatrix.Effect + alias RGBMatrix.Animation defmodule State do @moduledoc false - defstruct [:leds, :effect, :paintables, :last_frame, :timer] + defstruct [:leds, :animation, :paintables, :last_frame, :timer] end # Client @@ -21,31 +21,32 @@ defmodule RGBMatrix.Engine do @doc """ Start the engine. - This module registers its process globally and is expected to be started by - a supervisor. + This module registers its process globally and is expected to be started by a + supervisor. This function accepts the following arguments as a tuple: - `leds` - The list of LEDs to be painted on. - - `initial_effect` - The Effect type to initialize and play when the engine - starts. + - `initial_animation` - The Animation type to initialize and play when the + engine starts. - `paintables` - A list of modules to output colors to that implement the - `RGBMatrix.Paintable` behavior. If you want to register your paintables - dynamically, set this to an empty list `[]`. + `RGBMatrix.Paintable` behavior. If you want to register your paintables + dynamically, set this to an empty list `[]`. """ @spec start_link( - {leds :: [LED.t()], initial_effect_type :: Effect.type(), paintables :: list(module)} + {leds :: [LED.t()], initial_animation_type :: Animation.type(), + paintables :: list(module)} ) :: GenServer.on_start() - def start_link({leds, initial_effect_type, paintables}) do - GenServer.start_link(__MODULE__, {leds, initial_effect_type, paintables}, name: __MODULE__) + def start_link({leds, initial_animation_type, paintables}) do + GenServer.start_link(__MODULE__, {leds, initial_animation_type, paintables}, name: __MODULE__) end @doc """ - Sets the given effect as the currently active effect. + Sets the given animation as the currently active animation. """ - @spec set_effect(effect_type :: Effect.type(), opts :: keyword()) :: :ok - def set_effect(effect_type) do - GenServer.cast(__MODULE__, {:set_effect, effect_type}) + @spec set_animation(animation_type :: Animation.type(), opts :: keyword()) :: :ok + def set_animation(animation_type) do + GenServer.cast(__MODULE__, {:set_animation, animation_type}) end @doc """ @@ -58,8 +59,8 @@ defmodule RGBMatrix.Engine do end @doc """ - Unregister a `RGBMatrix.Paintable` so the engine no longer paints pixels to it. - This function is idempotent. + Unregister a `RGBMatrix.Paintable` so the engine no longer paints pixels to + it. This function is idempotent. """ @spec unregister_paintable(paintable :: module) :: :ok def unregister_paintable(paintable) do @@ -74,7 +75,7 @@ defmodule RGBMatrix.Engine do # Server @impl GenServer - def init({leds, initial_effect_type, paintables}) do + def init({leds, initial_animation_type, paintables}) do black = Chameleon.HSV.new(0, 0, 0) frame = Map.new(leds, &{&1.id, black}) @@ -84,7 +85,7 @@ defmodule RGBMatrix.Engine do Enum.reduce(paintables, initial_state, fn paintable, state -> add_paintable(paintable, state) end) - |> set_effect(initial_effect_type) + |> set_animation(initial_animation_type) {:ok, state} end @@ -99,12 +100,12 @@ defmodule RGBMatrix.Engine do %State{state | paintables: paintables} end - defp set_effect(state, effect_type) do - {render_in, effect} = Effect.new(effect_type, state.leds) + defp set_animation(state, animation_type) do + {render_in, animation} = Animation.new(animation_type, state.leds) state = schedule_next_render(state, render_in) - %State{state | effect: effect} + %State{state | animation: animation} end defp schedule_next_render(state, :ignore) do @@ -134,7 +135,7 @@ defmodule RGBMatrix.Engine do @impl true def handle_info(:render, state) do - {new_colors, render_in, effect} = Effect.render(state.effect) + {new_colors, render_in, animation} = Animation.render(state.animation) frame = update_frame(state.last_frame, new_colors) @@ -145,7 +146,7 @@ defmodule RGBMatrix.Engine do end) state = schedule_next_render(state, render_in) - state = %State{state | effect: effect, last_frame: frame} + state = %State{state | animation: animation, last_frame: frame} {:noreply, state} end @@ -157,18 +158,18 @@ defmodule RGBMatrix.Engine do end @impl GenServer - def handle_cast({:set_effect, effect_type}, state) do - state = set_effect(state, effect_type) + def handle_cast({:set_animation, animation_type}, state) do + state = set_animation(state, animation_type) {:noreply, state} end @impl GenServer def handle_cast({:interact, led}, state) do - {render_in, effect} = Effect.interact(state.effect, led) + {render_in, animation} = Animation.interact(state.animation, led) state = schedule_next_render(state, render_in) - state = %State{state | effect: effect} + state = %State{state | animation: animation} - {:noreply, %State{state | effect: effect}} + {:noreply, %State{state | animation: animation}} end @impl GenServer diff --git a/lib/rgb_matrix/paintable.ex b/lib/rgb_matrix/paintable.ex index 12b6e01..8d951cf 100644 --- a/lib/rgb_matrix/paintable.ex +++ b/lib/rgb_matrix/paintable.ex @@ -8,8 +8,8 @@ defmodule RGBMatrix.Paintable do @type frame :: %{required(LED.id()) => RGBMatrix.any_color_model()} @doc """ - Returns a function that can be called to paint the LEDs for a given frame. - The anonymous function's return value is unused. + Returns a function that can be called to paint the LEDs for a given frame. The + anonymous function's return value is unused. This callback makes any hardware implementation details opaque to the caller, while allowing the paintable to retain control of the physical LEDs. diff --git a/lib/xebow/application.ex b/lib/xebow/application.ex index 08f8416..a46957f 100644 --- a/lib/xebow/application.ex +++ b/lib/xebow/application.ex @@ -6,7 +6,7 @@ defmodule Xebow.Application do use Application @leds Xebow.layout() |> Layout.leds() - @effect_type RGBMatrix.Effect.types() |> List.first() + @animation_type RGBMatrix.Animation.types() |> List.first() def start(_type, _args) do # See https://hexdocs.pm/elixir/Supervisor.html @@ -39,7 +39,7 @@ defmodule Xebow.Application do # {Xebow.Worker, arg}, Xebow.HIDGadget, Xebow.LEDs, - {RGBMatrix.Engine, {@leds, @effect_type, [Xebow.LEDs]}}, + {RGBMatrix.Engine, {@leds, @animation_type, [Xebow.LEDs]}}, Xebow.Keyboard ] end diff --git a/lib/xebow/keyboard.ex b/lib/xebow/keyboard.ex index 478e823..83d7a71 100644 --- a/lib/xebow/keyboard.ex +++ b/lib/xebow/keyboard.ex @@ -17,7 +17,7 @@ defmodule Xebow.Keyboard do use GenServer alias Circuits.GPIO - alias RGBMatrix.{Effect, Engine} + alias RGBMatrix.{Animation, Engine} # maps the physical GPIO pins to key IDs @gpio_pins %{ @@ -74,9 +74,9 @@ defmodule Xebow.Keyboard do k001: AFK.Keycode.MFA.new({__MODULE__, :flash, ["red"]}), k002: AFK.Keycode.Transparent.new(), k003: AFK.Keycode.MFA.new({__MODULE__, :flash, ["green"]}), - k004: AFK.Keycode.MFA.new({__MODULE__, :previous_effect, []}), + k004: AFK.Keycode.MFA.new({__MODULE__, :previous_animation, []}), k005: AFK.Keycode.Transparent.new(), - k006: AFK.Keycode.MFA.new({__MODULE__, :next_effect, []}), + k006: AFK.Keycode.MFA.new({__MODULE__, :next_animation, []}), k007: AFK.Keycode.Transparent.new(), k008: AFK.Keycode.Transparent.new(), k009: AFK.Keycode.Transparent.new(), @@ -94,19 +94,19 @@ defmodule Xebow.Keyboard do end @doc """ - Cycle to the next effect + Cycle to the next animation """ - @spec next_effect() :: :ok - def next_effect do - GenServer.cast(__MODULE__, :next_effect) + @spec next_animation() :: :ok + def next_animation do + GenServer.cast(__MODULE__, :next_animation) end @doc """ - Cycle to the previous effect + Cycle to the previous animation """ - @spec previous_effect() :: :ok - def previous_effect do - GenServer.cast(__MODULE__, :previous_effect) + @spec previous_animation() :: :ok + def previous_animation do + GenServer.cast(__MODULE__, :previous_animation) end # Server @@ -138,47 +138,47 @@ defmodule Xebow.Keyboard do pins: pins, keyboard_state: keyboard_state, hid: hid, - effect_types: Effect.types(), - current_effect_index: 0 + animation_types: Animation.types(), + current_animation_index: 0 } {:ok, state} end @impl GenServer - def handle_cast(:next_effect, state) do - next_index = state.current_effect_index + 1 + def handle_cast(:next_animation, state) do + next_index = state.current_animation_index + 1 next_index = - case next_index < Enum.count(state.effect_types) do + case next_index < Enum.count(state.animation_types) do true -> next_index _ -> 0 end - effect_type = Enum.at(state.effect_types, next_index) + animation_type = Enum.at(state.animation_types, next_index) - RGBMatrix.Engine.set_effect(effect_type) + RGBMatrix.Engine.set_animation(animation_type) - state = %{state | current_effect_index: next_index} + state = %{state | current_animation_index: next_index} {:noreply, state} end @impl GenServer - def handle_cast(:previous_effect, state) do - previous_index = state.current_effect_index - 1 + def handle_cast(:previous_animation, state) do + previous_index = state.current_animation_index - 1 previous_index = case previous_index < 0 do - true -> Enum.count(state.effect_types) - 1 + true -> Enum.count(state.animation_types) - 1 _ -> previous_index end - effect_type = Enum.at(state.effect_types, previous_index) + animation_type = Enum.at(state.animation_types, previous_index) - RGBMatrix.Engine.set_effect(effect_type) + RGBMatrix.Engine.set_animation(animation_type) - state = %{state | current_effect_index: previous_index} + state = %{state | current_animation_index: previous_index} {:noreply, state} end diff --git a/lib/xebow/leds.ex b/lib/xebow/leds.ex index d078e83..edcf834 100644 --- a/lib/xebow/leds.ex +++ b/lib/xebow/leds.ex @@ -6,7 +6,7 @@ defmodule Xebow.LEDs do Keybow. It also implements the RGBMatrix.Paintable behavior so that the RGBMatrix - effects can be painted onto the keybow's RGB LEDs. + animations can be painted onto the keybow's RGB LEDs. """ @behaviour RGBMatrix.Paintable