From b4c07442ac8a432db8a6c34b746a3564b111e197 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 15 Aug 2017 22:24:27 +0200 Subject: [PATCH] Major update of how LED modes work There were a number of issues with the model we had before, namely that plugins that changed LED colors outside of LED modes had no way to signal "go back to whatever color this key was". To this end, the `LEDMode.refreshAt` method is introduced, which these plugins can call to tell the mode to update a given key. As part of this, the API was redesigned, with code that is common between all LED modes moving to the base class, among other things, much better names, and a flow of control that is easier to follow. In the new setup, there are four methods a LED mode can implement: - `setup()` to do boot-time initialization (registering hooks, etc). - `onActivate()` called every time the mode is activated. - `update()` called each cycle. - `refreshAt()` may be called by other plugins to refresh a particular key. All of these are protected methods, to be called via `LEDControl` only. Much of the new API design was done by @cdisselkoen, huge thanks for his work! Fixes #9. Signed-off-by: Gergely Nagy --- src/Kaleidoscope-LEDControl.cpp | 144 ++++++++++++++------------------ src/Kaleidoscope-LEDControl.h | 96 ++++++++++++++++++--- src/LED-Off.cpp | 12 ++- src/LED-Off.h | 12 ++- 4 files changed, 167 insertions(+), 97 deletions(-) diff --git a/src/Kaleidoscope-LEDControl.cpp b/src/Kaleidoscope-LEDControl.cpp index c0c1e2ce..be12b6fb 100644 --- a/src/Kaleidoscope-LEDControl.cpp +++ b/src/Kaleidoscope-LEDControl.cpp @@ -1,83 +1,77 @@ #include "Kaleidoscope-LEDControl.h" #include "Kaleidoscope-Focus.h" -LEDMode *LEDControl_::modes[LED_MAX_MODES]; -uint8_t LEDControl_::previousMode, LEDControl_::mode; -uint16_t LEDControl_::syncDelay = 16; -uint32_t LEDControl_::syncTimer; +namespace kaleidoscope { -void -LEDMode::activate(void) { - LEDControl.activate(this); +LEDMode *LEDControl::modes[LED_MAX_MODES]; +uint8_t LEDControl::mode; +uint16_t LEDControl::syncDelay = 16; +uint32_t LEDControl::syncTimer; + +void LEDMode::activate(void) { + ::LEDControl.activate(this); } -void -LEDMode::begin(void) { - Kaleidoscope.use(&LEDControl, NULL); - LEDControl.mode_add(this); +void LEDMode::begin(void) { + Kaleidoscope.use(&::LEDControl); + ::LEDControl.mode_add(this); + setup(); } -LEDControl_::LEDControl_(void) { - mode = previousMode = 0; +LEDControl::LEDControl(void) { + mode = 0; memset(modes, 0, LED_MAX_MODES * sizeof(modes[0])); } -void -LEDControl_::next_mode(void) { +void LEDControl::next_mode(void) { mode++; - if (mode >= LED_MAX_MODES) { - mode = 0; - return; + if (mode >= LED_MAX_MODES || !modes[mode]) { + return set_mode(0); } - if (modes[mode]) - return; - - mode = 0; + return set_mode(mode); } -void -LEDControl_::update(void) { - if (previousMode != mode) { - set_all_leds_to({0, 0, 0}); - if (modes[mode]) - (modes[mode]->init)(); - } - +#if 0 +void LEDControl::update(void) { if (modes[mode]) (modes[mode]->update)(); - - previousMode = mode; } -void -LEDControl_::init_mode(void) { +void LEDControl::refreshAt(byte row, byte col) { if (modes[mode]) - (modes[mode]->init)(); + modes[mode]->refreshAt(row, col); } +#endif void -LEDControl_::set_mode(uint8_t mode_) { - if (mode_ < LED_MAX_MODES) - mode = mode_; +LEDControl::set_mode(uint8_t mode_) { + if (mode_ >= LED_MAX_MODES) + return; + + set_all_leds_to({0, 0, 0}); + mode = mode_; + if (modes[mode]) + modes[mode]->onActivate(); } -uint8_t -LEDControl_::get_mode(void) { +uint8_t LEDControl::get_mode_index(void) { return mode; } -void -LEDControl_::activate(LEDMode *mode) { +LEDMode *LEDControl::get_mode(void) { + return modes[mode]; +} + +void LEDControl::activate(LEDMode *mode) { for (uint8_t i = 0; i < LED_MAX_MODES; i++) { if (modes[i] == mode) return set_mode(i); } } -int8_t -LEDControl_::mode_add(LEDMode *mode) { +int8_t LEDControl::mode_add(LEDMode *mode) { for (int i = 0; i < LED_MAX_MODES; i++) { if (modes[i]) continue; @@ -88,8 +82,7 @@ LEDControl_::mode_add(LEDMode *mode) { return -1; } -void -LEDControl_::set_all_leds_to(uint8_t r, uint8_t g, uint8_t b) { +void LEDControl::set_all_leds_to(uint8_t r, uint8_t g, uint8_t b) { cRGB color; color.r = r; color.g = g; @@ -97,35 +90,29 @@ LEDControl_::set_all_leds_to(uint8_t r, uint8_t g, uint8_t b) { set_all_leds_to(color); } -void -LEDControl_::set_all_leds_to(cRGB color) { +void LEDControl::set_all_leds_to(cRGB color) { for (uint8_t i = 0; i < LED_COUNT; i++) { setCrgbAt(i, color); } } -void -LEDControl_::setCrgbAt(uint8_t i, cRGB crgb) { +void LEDControl::setCrgbAt(uint8_t i, cRGB crgb) { KeyboardHardware.setCrgbAt(i, crgb); } -void -LEDControl_::setCrgbAt(byte row, byte col, cRGB color) { +void LEDControl::setCrgbAt(byte row, byte col, cRGB color) { KeyboardHardware.setCrgbAt(row, col, color); } -cRGB -LEDControl_::getCrgbAt(uint8_t i) { +cRGB LEDControl::getCrgbAt(uint8_t i) { return KeyboardHardware.getCrgbAt(i); } -void -LEDControl_::syncLeds(void) { +void LEDControl::syncLeds(void) { KeyboardHardware.syncLeds(); } -void -LEDControl_::begin(void) { +void LEDControl::begin(void) { set_all_leds_to({0, 0, 0}); for (uint8_t i = 0; i < LED_MAX_MODES; i++) { @@ -133,25 +120,23 @@ LEDControl_::begin(void) { (modes[i]->setup)(); } - event_handler_hook_use(eventHandler); - loop_hook_use(loopHook); + Kaleidoscope.useEventHandlerHook(eventHandler); + Kaleidoscope.useLoopHook(loopHook); syncTimer = millis() + syncDelay; } -Key -LEDControl_::eventHandler(Key mappedKey, byte row, byte col, uint8_t keyState) { +Key LEDControl::eventHandler(Key mappedKey, byte row, byte col, uint8_t keyState) { if (mappedKey.flags != (SYNTHETIC | IS_INTERNAL | LED_TOGGLE)) return mappedKey; if (keyToggledOn(keyState)) - LEDControl.next_mode(); + next_mode(); return Key_NoKey; } -void -LEDControl_::loopHook(bool postClear) { +void LEDControl::loopHook(bool postClear) { if (postClear) return; @@ -162,8 +147,7 @@ LEDControl_::loopHook(bool postClear) { update(); } -bool -LEDControl_::focusHook(const char *command) { +bool LEDControl::focusHook(const char *command) { enum { SETALL, MODE, @@ -189,9 +173,9 @@ LEDControl_::focusHook(const char *command) { uint8_t idx = Serial.parseInt(); if (Serial.peek() == '\n') { - cRGB c = LEDControl.getCrgbAt(idx); + cRGB c = getCrgbAt(idx); - Focus.printColor(c.r, c.g, c.b); + ::Focus.printColor(c.r, c.g, c.b); Serial.println(); } else { cRGB c; @@ -200,7 +184,7 @@ LEDControl_::focusHook(const char *command) { c.g = Serial.parseInt(); c.b = Serial.parseInt(); - LEDControl.setCrgbAt(idx, c); + setCrgbAt(idx, c); } break; } @@ -211,16 +195,16 @@ LEDControl_::focusHook(const char *command) { c.g = Serial.parseInt(); c.b = Serial.parseInt(); - LEDControl.set_all_leds_to(c); + set_all_leds_to(c); break; } case MODE: { char peek = Serial.peek(); if (peek == '\n') { - Serial.println(LEDControl.get_mode()); + Serial.println(get_mode_index()); } else if (peek == 'n') { - LEDControl.next_mode(); + next_mode(); Serial.read(); } else if (peek == 'p') { // TODO(algernon) @@ -228,17 +212,17 @@ LEDControl_::focusHook(const char *command) { } else { uint8_t mode = Serial.parseInt(); - LEDControl.set_mode(mode); + set_mode(mode); } break; } case THEME: { if (Serial.peek() == '\n') { for (uint8_t idx = 0; idx < LED_COUNT; idx++) { - cRGB c = LEDControl.getCrgbAt(idx); + cRGB c = getCrgbAt(idx); - Focus.printColor(c.r, c.g, c.b); - Focus.printSpace(); + ::Focus.printColor(c.r, c.g, c.b); + ::Focus.printSpace(); } Serial.println(); break; @@ -252,7 +236,7 @@ LEDControl_::focusHook(const char *command) { color.g = Serial.parseInt(); color.b = Serial.parseInt(); - LEDControl.setCrgbAt(idx, color); + setCrgbAt(idx, color); idx++; } break; @@ -262,4 +246,6 @@ LEDControl_::focusHook(const char *command) { return true; } -LEDControl_ LEDControl; +} + +kaleidoscope::LEDControl LEDControl; diff --git a/src/Kaleidoscope-LEDControl.h b/src/Kaleidoscope-LEDControl.h index 5ece0b2a..e38012a9 100644 --- a/src/Kaleidoscope-LEDControl.h +++ b/src/Kaleidoscope-LEDControl.h @@ -8,27 +8,100 @@ #define Key_LEDEffectNext (Key) { 0, KEY_FLAGS | SYNTHETIC | IS_INTERNAL | LED_TOGGLE } +namespace kaleidoscope { +/** Base class for LED modes. + * + * LED modes are a special kind of plugin, they are in charge of updating LED + * colors, setting a theme. While it is possible to have other plugins + * override the mode's colors, the LED mode is the baseline. + * + * Most of its functionality is called via @ref LEDControl, with only a few + * public methods. + * + * A LED mode **must** implement at least one of @ref onActivate or @ref + * update, and possibly @ref refreshAt too. + */ class LEDMode : public KaleidoscopePlugin { - public: - virtual void begin(void); + friend class LEDControl; + protected: + // These methods should only be called by LEDControl. + + /** One-time setup, called at keyboard boot. + * + * Any hooks that need registering, any one-time setup that needs to be + * performed, shall be done here. This is purely for preparation purposes, the + * LEDs should not be touched yet at this time. + */ virtual void setup(void) {} - virtual void init(void) {} + + /** Function to call whenever the mode is activated. + * + * Like @ref setup, this method need not touch LEDs, @ref update will be + * called right after it. The purpose of this callback is to allow a plugin to + * do some preparation whenever it is activated, instead of only on boot, or + * always at each cycle. + * + * However, unlike @ref setup, this method can change LED colors, if so + * desired. Either to provide an initial state, or a static color set. In the + * latter case, consider implementing @ref refreshAt too, because other + * plugins may override some of the colors set at activation time, and @ref + * refreshAt can be used to restore them when needed. + * + * Before the callback runs, LEDs will be blanked. + */ + virtual void onActivate(void) {} + + /** Update the LEDs once per cycle. + * + * Usually the brains of the plugin, which updates the LEDs each cycle. It is + * called after the matrix has been scanned, once per cycle. + */ virtual void update(void) {} - virtual void activate(void); + + /** Refresh the color of a given key. + * + * If we have another plugin that overrides colors set by the active LED mode + * (either at @onActivate time, or via @ref update), if that plugin wants to + * restore whatever color the mode would set the key color to, this is the + * method it will call. + * + * @param row is the row coordinate of the key to refresh the color of. + * @param col is the column coordinate of the key to refresh the color of. + */ + virtual void refreshAt(byte row, byte col) {} + + public: + /** Activate the current object as the LED mode. + */ + void activate(void); + + /** Plugin initialization. + * + * Called via `Kaleidoscope.use()`, registers the LED mode, and does the + * necessary initialization steps. Calls @ref setup at the end. + */ + void begin(void) final; }; -class LEDControl_ : public KaleidoscopePlugin { +class LEDControl : public KaleidoscopePlugin { public: - LEDControl_(void); + LEDControl(void); void begin(void) final; static void next_mode(void); static void setup(void); - static void update(void); + static void update(void) { + if (modes[mode]) + modes[mode]->update(); + } + static void refreshAt(byte row, byte col) { + if (modes[mode]) + modes[mode]->refreshAt(row, col); + } static void set_mode(uint8_t mode); - static uint8_t get_mode(); - static void init_mode(void); + static uint8_t get_mode_index(); + static LEDMode *get_mode(); static int8_t mode_add(LEDMode *mode); @@ -49,13 +122,14 @@ class LEDControl_ : public KaleidoscopePlugin { private: static uint32_t syncTimer; static LEDMode *modes[LED_MAX_MODES]; - static uint8_t previousMode, mode; + static uint8_t mode; static Key eventHandler(Key mappedKey, byte row, byte col, uint8_t keyState); static void loopHook(bool postClear); }; +} -extern LEDControl_ LEDControl; +extern kaleidoscope::LEDControl LEDControl; #define FOCUS_HOOK_LEDCONTROL FOCUS_HOOK (LEDControl.focusHook, \ "led.at\n" \ diff --git a/src/LED-Off.cpp b/src/LED-Off.cpp index 1cacc667..25a74c04 100644 --- a/src/LED-Off.cpp +++ b/src/LED-Off.cpp @@ -1,7 +1,13 @@ #include "LED-Off.h" -void LEDOff_::update(void) { - LEDControl.set_all_leds_to({0, 0, 0}); +namespace kaleidoscope { +void LEDOff::onActivate(void) { + ::LEDControl.set_all_leds_to({0, 0, 0}); } -LEDOff_ LEDOff; +void LEDOff::refreshAt(byte row, byte col) { + ::LEDControl.setCrgbAt(row, col, {0, 0, 0}); +} +} + +kaleidoscope::LEDOff LEDOff; diff --git a/src/LED-Off.h b/src/LED-Off.h index 19d65cde..1da39edf 100644 --- a/src/LED-Off.h +++ b/src/LED-Off.h @@ -2,11 +2,15 @@ #include "Kaleidoscope-LEDControl.h" -class LEDOff_ : public LEDMode { +namespace kaleidoscope { +class LEDOff : public LEDMode { public: - LEDOff_(void) { } + LEDOff(void) { } - void update(void) final; + protected: + void onActivate(void) final; + void refreshAt(byte row, byte col) final; }; +} -extern LEDOff_ LEDOff; +extern kaleidoscope::LEDOff LEDOff;