diff --git a/doc/plugin/LED-Palette-Theme.md b/doc/plugin/LED-Palette-Theme.md new file mode 100644 index 00000000..34789469 --- /dev/null +++ b/doc/plugin/LED-Palette-Theme.md @@ -0,0 +1,128 @@ +# Kaleidoscope-LED-Palette-Theme + +A common base for plugins that want to provide themes, or theme-related +capabilities, using a 16 color palette. In other words, this is for plugin +authors primarily. The primary aim of the plugin is to provide not only a common +palette, but tools that make it easier to use it too. + +## Using the plugin + +To use the plugin, one needs to do a bit more than include the header, and tell +the firmware to use it. Itself being a mere building block, to use it to its +full extent, we need to create our own plugin on top of it. + +```c++ +#include +#include +#include +#include + +namespace example { + +class TestLEDMode : public LEDMode { + public: + TestLEDMode() {} + + protected: + void setup(void) final; + void update(void) final; + + kaleidoscope::EventHandlerResult onFocusEvent(const char *command); + + private: + static uint16_t map_base_; +}; + +uint16_t TestLEDMode::map_base_; + +void TestLEDMode::setup(void) { + map_base_ = LEDPaletteTheme.reserveThemes(1); +} + +void TestLEDMode::update(void) { + LEDPaletteTheme.updateHandler(map_base_, 0); +} + +kaleidoscope::EventHandlerResult +TestLEDMode::onFocusEvent(const char *command) { + return LEDPaletteTheme.themeFocusEvent(command, PSTR("testLedMode.map"), map_base_, 1); +} + +} + +example::TestLEDMode TestLEDMode; + +KALEIDOSCOPE_INIT_PLUGINS( + Focus, + LEDPaletteTheme, + TestLEDMode, + EEPROMSettings +); + +void setup() { + Kaleidoscope.setup(); + + TestLEDMode.activate(); +} +``` + +This is a simple extension, where it provides a `testLEDMode.map` Focus command, +with which one can set the theme which will be saved to EEPROM. + +## Plugin methods + +The plugin provides the `LEDPaletteTheme` object, which has the following methods and properties: + +### `.reserveThemes(max_themes)` + +> Reserve space in EEPROM for `max_themes`. Each key on a theme uses half a byte +> of space. The function returns the `theme_base` to be used with the rest of +> the methods. +> +> The `theme_base` is a pointer into the EEPROM where the theme storage starts. + +### `.updateHandler(theme_base, theme)` + +> A helper we can call in our plugin's `.update()` method: given an EEPROM +> location (`theme_base`), and a `theme` index, it will update the keyboard with +> the colors of the specified theme. +> +> The `theme` argument can be any index between zero and `max_themes`. How the +> plugin decides which theme to display depends entirely on the plugin. + +### `.themeFocusEvent(command, expected_command, theme_base, max_themes)` + +> To be used in a custom `Focus` handler: handles the `expected_command` Focus +> command, and provides a way to query and update the themes supported by the +> plugin. +> +> When queried, it will list the color indexes. When used as a setter, it +> expects one index per key. +> +> The palette can be set via the `palette` focus command, provided by the +> `LEDPaletteTheme` plugin. + +## Focus commands + +### `palette` + +> Without arguments, prints the palette: RGB values for all 16 colors. +> +> With arguments, updates the palette with new colors. One does not need to give +> the full palette, the plugin will process as many arguments as available, and +> ignore anything past the last index. It expects colors to have all three +> components specified, or none at all. Thus, partial palette updates are +> possible, but only on the color level, not at component level. + +## Dependencies + +* [Kaleidoscope-EEPROM-Settings](EEPROM-Settings.md) +* [Kaleidoscope-FocusSerial](FocusSerial.md) +* [Kaleidoscope-LEDControl](LEDControl.md) + +## Further reading + +Starting from the [example][plugin:example] is the recommended way of getting +started with the plugin. + + [plugin:example]: ../../examples/LED-Palette-Theme/LED-Palette-Theme.ino diff --git a/examples/LED-Palette-Theme/LED-Palette-Theme.ino b/examples/LED-Palette-Theme/LED-Palette-Theme.ino new file mode 100644 index 00000000..6abc5481 --- /dev/null +++ b/examples/LED-Palette-Theme/LED-Palette-Theme.ino @@ -0,0 +1,92 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-LED-Palette-Theme -- Palette-based LED theme foundation + * Copyright (C) 2017, 2018 Keyboard.io, Inc + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +namespace example { + +class TestLEDMode : public kaleidoscope::LEDMode { + public: + TestLEDMode() {} + + kaleidoscope::EventHandlerResult onFocusEvent(const char *command); + + protected: + void setup() final; + void update(void) final; + + private: + static uint16_t map_base_; +}; + +uint16_t TestLEDMode::map_base_; + +void +TestLEDMode::setup() { + map_base_ = LEDPaletteTheme.reserveThemes(1); +} + +void +TestLEDMode::update(void) { + LEDPaletteTheme.updateHandler(map_base_, 0); +} + +kaleidoscope::EventHandlerResult +TestLEDMode::onFocusEvent(const char *command) { + return LEDPaletteTheme.themeFocusEvent(command, PSTR("testLedMode.map"), map_base_, 1); +} + +} + +example::TestLEDMode TestLEDMode; + +// *INDENT-OFF* +const Key keymaps[][ROWS][COLS] PROGMEM = { + [0] = KEYMAP_STACKED + ( + Key_LEDEffectNext, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext, + Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, + Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, + Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, + + Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, + Key_NoKey, + + Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip, + Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, + Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, + Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, + + Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, + Key_NoKey), +}; +// *INDENT-ON* + +KALEIDOSCOPE_INIT_PLUGINS(Focus, LEDPaletteTheme, TestLEDMode, EEPROMSettings); + +void setup() { + Kaleidoscope.setup(); + + TestLEDMode.activate(); +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/src/Kaleidoscope-LED-Palette-Theme.h b/src/Kaleidoscope-LED-Palette-Theme.h new file mode 100644 index 00000000..4d1a5199 --- /dev/null +++ b/src/Kaleidoscope-LED-Palette-Theme.h @@ -0,0 +1,20 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-LED-Palette-Theme -- Palette-based LED theme foundation + * Copyright (C) 2017 Keyboard.io, Inc + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include diff --git a/src/kaleidoscope/plugin/LED-Palette-Theme.cpp b/src/kaleidoscope/plugin/LED-Palette-Theme.cpp new file mode 100644 index 00000000..bf441457 --- /dev/null +++ b/src/kaleidoscope/plugin/LED-Palette-Theme.cpp @@ -0,0 +1,178 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-LED-Palette-Theme -- Palette-based LED theme foundation + * Copyright (C) 2017, 2018 Keyboard.io, Inc + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include + +namespace kaleidoscope { +namespace plugin { + +uint16_t LEDPaletteTheme::palette_base_; + +EventHandlerResult LEDPaletteTheme::onSetup(void) { + if (!palette_base_) + palette_base_ = ::EEPROMSettings.requestSlice(16 * sizeof(cRGB)); + + return EventHandlerResult::OK; +} + +uint16_t LEDPaletteTheme::reserveThemes(uint8_t max_themes) { + return ::EEPROMSettings.requestSlice(max_themes * ROWS * COLS / 2); +} + +void LEDPaletteTheme::updateHandler(uint16_t theme_base, uint8_t theme) { + uint16_t map_base = theme_base + (theme * ROWS * COLS / 2); + + for (uint16_t pos = 0; pos < ROWS * COLS; pos++) { + cRGB color = lookupColorAtPosition(map_base, pos); + ::LEDControl.setCrgbAt(pos, color); + } +} + +void LEDPaletteTheme::refreshAt(uint16_t theme_base, uint8_t theme, byte row, byte col) { + uint16_t map_base = theme_base + (theme * ROWS * COLS / 2); + uint16_t pos = KeyboardHardware.getLedIndex(row, col); + + cRGB color = lookupColorAtPosition(map_base, pos); + ::LEDControl.setCrgbAt(pos, color); +} + + +const uint8_t LEDPaletteTheme::lookupColorIndexAtPosition(uint16_t map_base, uint16_t position) { + uint8_t color_index; + + color_index = EEPROM.read(map_base + position / 2); + if (position % 2) + color_index &= ~0xf0; + else + color_index >>= 4; + + return color_index; +} + +const cRGB LEDPaletteTheme::lookupColorAtPosition(uint16_t map_base, uint16_t position) { + uint8_t color_index = lookupColorIndexAtPosition(map_base, position); + + return lookupPaletteColor(color_index); +} + +const cRGB LEDPaletteTheme::lookupPaletteColor(uint8_t color_index) { + cRGB color; + + EEPROM.get(palette_base_ + color_index * sizeof(cRGB), color); + return color; +} + +void LEDPaletteTheme::updateColorIndexAtPosition(uint16_t map_base, uint16_t position, uint8_t color_index) { + uint8_t indexes; + + indexes = EEPROM.read(map_base + position / 2); + if (position % 2) { + uint8_t other = indexes >> 4; + indexes = (other << 4) + color_index; + } else { + uint8_t other = indexes & ~0xf0; + indexes = (color_index << 4) + other; + } + EEPROM.update(map_base + position / 2, indexes); +} + +EventHandlerResult LEDPaletteTheme::onFocusEvent(const char *command) { + const char *cmd = PSTR("palette"); + + if (::Focus.handleHelp(command, cmd)) + return EventHandlerResult::OK; + + if (strcmp_P(command, cmd) != 0) + return EventHandlerResult::OK; + + if (Serial.peek() == '\n') { + for (uint8_t i = 0; i < 16; i++) { + cRGB color; + + EEPROM.get(palette_base_ + i * sizeof(color), color); + ::Focus.printColor(color.r, color.g, color.b); + ::Focus.printSpace(); + } + Serial.println(); + return EventHandlerResult::EVENT_CONSUMED; + } + + uint8_t i = 0; + while (i < 16 && Serial.peek() != '\n') { + cRGB color; + + color.r = Serial.parseInt(); + color.g = Serial.parseInt(); + color.b = Serial.parseInt(); + + EEPROM.put(palette_base_ + i * sizeof(color), color); + i++; + } + + ::LEDControl.refreshAll(); + + return EventHandlerResult::EVENT_CONSUMED; +} + +EventHandlerResult LEDPaletteTheme::themeFocusEvent(const char *command, + const char *expected_command, + uint16_t theme_base, + uint8_t max_themes) { + if (::Focus.handleHelp(command, expected_command)) + return EventHandlerResult::OK; + + if (strcmp_P(command, expected_command) != 0) + return EventHandlerResult::OK; + + uint16_t max_index = (max_themes * ROWS * COLS) / 2; + + if (Serial.peek() == '\n') { + for (uint16_t pos = 0; pos < max_index; pos++) { + uint8_t indexes = EEPROM.read(theme_base + pos); + + ::Focus.printNumber(indexes >> 4); + ::Focus.printSpace(); + ::Focus.printNumber(indexes & ~0xf0); + ::Focus.printSpace(); + } + Serial.println(); + return EventHandlerResult::EVENT_CONSUMED; + } + + uint16_t pos = 0; + + while ((Serial.peek() != '\n') && (pos < max_index)) { + uint8_t idx1 = Serial.parseInt(); + uint8_t idx2 = Serial.parseInt(); + uint8_t indexes = (idx1 << 4) + idx2; + + EEPROM.update(theme_base + pos, indexes); + pos++; + } + + ::LEDControl.refreshAll(); + + return EventHandlerResult::EVENT_CONSUMED; +} + +} +} + +kaleidoscope::plugin::LEDPaletteTheme LEDPaletteTheme; diff --git a/src/kaleidoscope/plugin/LED-Palette-Theme.h b/src/kaleidoscope/plugin/LED-Palette-Theme.h new file mode 100644 index 00000000..0f097b8a --- /dev/null +++ b/src/kaleidoscope/plugin/LED-Palette-Theme.h @@ -0,0 +1,53 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-LED-Palette-Theme -- Palette-based LED theme foundation + * Copyright (C) 2017, 2018 Keyboard.io, Inc + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#pragma once + +#include +#include + +namespace kaleidoscope { +namespace plugin { + +class LEDPaletteTheme : public kaleidoscope::Plugin { + public: + LEDPaletteTheme(void) {} + + static uint16_t reserveThemes(uint8_t max_themes); + static void updateHandler(uint16_t theme_base, uint8_t theme); + static void refreshAt(uint16_t theme_base, uint8_t theme, byte row, byte col); + + static const uint8_t lookupColorIndexAtPosition(uint16_t theme_base, uint16_t position); + static const cRGB lookupColorAtPosition(uint16_t theme_base, uint16_t position); + static void updateColorIndexAtPosition(uint16_t theme_base, uint16_t position, uint8_t color_index); + + static const cRGB lookupPaletteColor(uint8_t palette_index); + + EventHandlerResult onFocusEvent(const char *command); + EventHandlerResult themeFocusEvent(const char *command, + const char *expected_command, + uint16_t theme_base, uint8_t max_themes); + EventHandlerResult onSetup(); + + private: + static uint16_t palette_base_; +}; + +} +} + +extern kaleidoscope::plugin::LEDPaletteTheme LEDPaletteTheme;