diff --git a/README.md b/README.md index 6baff5ff..34d7c9f9 100644 --- a/README.md +++ b/README.md @@ -9,26 +9,15 @@ [st:broken]: https://img.shields.io/badge/broken-X-black.svg?style=flat&colorA=e05d44&colorB=494e52 [st:experimental]: https://img.shields.io/badge/experimental----black.svg?style=flat&colorA=dfb317&colorB=494e52 -While keyboards usually ship with a keymap programmed in, to be able to change -that keymap, without flashing new firmware, we need a way to place the keymap -into a place we can update at run-time, and which persists across reboots. -Fortunately, we have a bit of `EEPROM` on the keyboard, and can use it to store -either the full keymap (and saving space in the firmware then), or store an -overlay there. In the latter case, whenever there is a non-transparent key on -the overlay, we will use that instead of the keyboard default. - -In short, this plugin allows us to change our keymaps, without having to compile -and flash new firmware. It does so through the use of the -[FocusSerial][plugin:focusSerial] plugin. +While keyboards usually ship with a keymap programmed in, to be able to change that keymap, without flashing new firmware, we need a way to place the keymap into a place we can update at run-time, and which persists across reboots. Fortunately, we have a bit of `EEPROM` on the keyboard, and can use it to store either the full keymap (and saving space in the firmware then), or store additional layers there. + +In short, this plugin allows us to change our keymaps, without having to compile and flash new firmware. It does so through the use of the [FocusSerial][plugin:focusSerial] plugin. [plugin:focusSerial]: https://github.com/keyboardio/Kaleidoscope-FocusSerial ## Using the plugin -Using the plugin is reasonably simple: after including the header, enable the -plugin, and configure how many layers at most we want to store in `EEPROM`. -There are other settings one can tweak, but these two steps are enough to get -started with. +Using the plugin is reasonably simple: after including the header, enable the plugin, and configure how many layers at most we want to store in `EEPROM`. There are other settings one can tweak, but these two steps are enough to get started with. Once these are set up, we can update the keymap via [Focus][plugin:focusSerial]. @@ -38,53 +27,40 @@ Once these are set up, we can update the keymap via [Focus][plugin:focusSerial]. #include KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap, - Focus, - FocusKeymapTransferCommand); + Focus); void setup() { Kaleidoscope.setup(); - EEPROMKeymap.max_layers(1); + EEPROMKeymap.setup(1); } ``` ## Plugin methods -The plugin provides the `EEPROMKeymap` object, which has the following methods: +The plugin provides the `EEPROMKeymap` object, which has the following method: -### `.max_layers(max)` +### `.setup(layers[, mode])` -> Tells the extension to reserve space in EEPROM for up to `max` layers. Can -> only be called once, any subsequent call will be a no-op. -> This should be set to the number of keymap layers you want to be -> able to program from EEPROM (probably the number of layers you have -> defined in your keymap). +> Reserve space in EEPROM for up to `layers` layers, and set things up to work according to the specified `mode` (see below for a list of supported modes). To be called from the `setup` method of one's sketch. +> +> Supported modes are: +> - `EEPROMKeymap.Mode::EXTEND`: Extend the keymap with layers from EEPROM, treating them as extensions of the main keymap embedded in the firmware. The first layer in EEPROM will have a number one higher than the last layer in PROGMEM. In this case, the total number of layers will be the number of them in PROGMEM plus `layers`. +> - `EEPROMKeymap.Mode::CUSTOM`: For advanced use cases where the `EXTEND` mode is not appropriate. In this case, the plugin merely reserves a slice of EEPROM for the requested amount of layers, but does no other configuration - that's entirely up to the Sketch. ## Focus commands -The plugin provides a `keymap.map` Focus command unconditionally, and a -`keymap.transfer` via the `FocusKeymapTransferCommand` object. +The plugin provides the `keymap.map` and a `keymap.roLayers` commands. ### `keymap.map [codes...]` -> Without arguments, displays the keymap currently in effect. Each key is -> printed as its raw, 16-bit keycode. +> Without arguments, displays the keymap currently in effect. Each key is printed as its raw, 16-bit keycode. > -> With arguments, it stores as many keys as given. One does not need to set all -> keys, on all layers: the command will start from the first key on the first -> layer, and go on as long as it has input. It will not go past the layer set -> via the `.max_layers()` method. +> With arguments, it stores as many keys as given. One does not need to set all keys, on all layers: the command will start from the first key on the first layer, and go on as long as it has input. It will not go past the total amount of layers (that is, `layer_count`). -### `keymap.transfer LAYER` +### `keymap.roLayers` -> Transfers the `LAYER` from the built-in memory of the keyboard into `EEPROM` -> storage. -> -> Useful mostly when one wants to remove the built-in keymap, and wants to -> easily transfer it into `EEPROM` first. -> -> This is generally not needed, and it is recommended to not enable this -> command, unless the feature this command implements is truly needed. +> Returns the number of read-only layers. This only makes sense for the `EEPROMKeymap.Mode::EXTEND` mode, where it returns the number of layers in PROGMEM. In any other case, it doesn't return anything, doing so is left for another event handler that understands what the correct value would be. ## Dependencies @@ -93,7 +69,6 @@ The plugin provides a `keymap.map` Focus command unconditionally, and a ## Further reading -Starting from the [example][plugin:example] is the recommended way of getting -started with the plugin. +Starting from the [example][plugin:example] is the recommended way of getting started with the plugin. [plugin:example]: https://github.com/keyboardio/Kaleidoscope-EEPROM-Keymap/blob/master/examples/EEPROM-Keymap/EEPROM-Keymap.ino diff --git a/examples/EEPROM-Keymap/EEPROM-Keymap.ino b/examples/EEPROM-Keymap/EEPROM-Keymap.ino index e5c35ec9..f9158099 100644 --- a/examples/EEPROM-Keymap/EEPROM-Keymap.ino +++ b/examples/EEPROM-Keymap/EEPROM-Keymap.ino @@ -40,12 +40,12 @@ KEYMAPS( ) // *INDENT-ON* -KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap, Focus, FocusKeymapTransferCommand); +KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap, Focus); void setup() { Kaleidoscope.setup(); - EEPROMKeymap.max_layers(1); + EEPROMKeymap.setup(1, EEPROMKeymap.Mode::EXTEND); } void loop() { diff --git a/src/Kaleidoscope-EEPROM-Keymap.h b/src/Kaleidoscope-EEPROM-Keymap.h index 3bcb13c8..7b3b8af0 100644 --- a/src/Kaleidoscope-EEPROM-Keymap.h +++ b/src/Kaleidoscope-EEPROM-Keymap.h @@ -18,4 +18,3 @@ #pragma once #include -#include diff --git a/src/Kaleidoscope/EEPROM-Keymap-Transfer.cpp b/src/Kaleidoscope/EEPROM-Keymap-Transfer.cpp deleted file mode 100644 index 77ec771b..00000000 --- a/src/Kaleidoscope/EEPROM-Keymap-Transfer.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- mode: c++ -*- - * Kaleidoscope-EEPROM-Keymap -- EEPROM-based keymap support. - * 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 - -namespace kaleidoscope { -namespace eeprom { - -EventHandlerResult FocusKeymapTransferCommand::onFocusEvent(const char *command) { - const char *cmd = PSTR("keymap.transfer"); - - if (::Focus.handleHelp(command, cmd)) - return EventHandlerResult::OK; - if (strcmp_P(command, cmd) != 0) - return EventHandlerResult::OK; - - uint8_t layer = Serial.parseInt(); - - for (uint8_t row = 0; row < ROWS; row++) { - for (uint8_t col = 0; col < COLS; col++) { - Key k = Layer.getKeyFromPROGMEM(layer, row, col); - uint16_t pos = ((layer * ROWS * COLS) + (row * COLS) + col); - - ::EEPROMKeymap.updateKey(pos, k); - } - } - - return EventHandlerResult::EVENT_CONSUMED; -} - -} -} - -kaleidoscope::eeprom::FocusKeymapTransferCommand FocusKeymapTransferCommand; diff --git a/src/Kaleidoscope/EEPROM-Keymap-Transfer.h b/src/Kaleidoscope/EEPROM-Keymap-Transfer.h deleted file mode 100644 index 6dfee13c..00000000 --- a/src/Kaleidoscope/EEPROM-Keymap-Transfer.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- mode: c++ -*- - * Kaleidoscope-EEPROM-Keymap -- EEPROM-based keymap support. - * 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 eeprom { -class FocusKeymapTransferCommand : public Plugin { - public: - FocusKeymapTransferCommand() {} - - EventHandlerResult onFocusEvent(const char *command); -}; -} -} - -extern kaleidoscope::eeprom::FocusKeymapTransferCommand FocusKeymapTransferCommand; diff --git a/src/Kaleidoscope/EEPROM-Keymap.cpp b/src/Kaleidoscope/EEPROM-Keymap.cpp index 28e5c608..249f1790 100644 --- a/src/Kaleidoscope/EEPROM-Keymap.cpp +++ b/src/Kaleidoscope/EEPROM-Keymap.cpp @@ -20,16 +20,30 @@ #include namespace kaleidoscope { +EEPROMKeymap::Mode EEPROMKeymap::mode_; uint16_t EEPROMKeymap::keymap_base_; uint8_t EEPROMKeymap::max_layers_; +uint8_t EEPROMKeymap::progmem_layers_; EventHandlerResult EEPROMKeymap::onSetup() { ::EEPROMSettings.onSetup(); - Layer.getKey = ::EEPROMKeymap.getKeyOverride; - + progmem_layers_ = layer_count; return EventHandlerResult::OK; } +void EEPROMKeymap::setup(uint8_t max, Mode mode) { + switch (mode) { + case Mode::CUSTOM: + break; + case Mode::EXTEND: + layer_count = progmem_layers_ + max; + Layer.getKey = getKeyExtended; + break; + } + mode_ = mode; + max_layers(max); +} + void EEPROMKeymap::max_layers(uint8_t max) { max_layers_ = max; keymap_base_ = ::EEPROMSettings.requestSlice(max_layers_ * ROWS * COLS * 2); @@ -49,22 +63,16 @@ Key EEPROMKeymap::getKey(uint8_t layer, byte row, byte col) { return key; } -Key EEPROMKeymap::getKeyOverride(uint8_t layer, byte row, byte col) { +Key EEPROMKeymap::getKeyExtended(uint8_t layer, byte row, byte col) { Key key; - key = getKey(layer, row, col); - - /* - * If we read a transparent key from EEPROM, or we're trying to read from a - * layer higher than what is available there (max_layers), check if we're below - * the layer count in PROGMEM (layer_count). If we are, read from PROGMEM, - * otherwise leave the key as-is (either transparent or NoKey). - */ - if ((key == Key_Transparent || layer >= max_layers_) && - (layer < layer_count)) - key = Layer.getKeyFromPROGMEM(layer, row, col); + // If the layer is within PROGMEM bounds, look it up from there + if (layer < progmem_layers_) { + return Layer.getKeyFromPROGMEM(layer, row, col); + } - return key; + // If the layer is outside of PROGMEM, look up from EEPROM + return getKey(layer - progmem_layers_, row, col); } uint16_t EEPROMKeymap::keymap_base(void) { @@ -90,14 +98,24 @@ void EEPROMKeymap::printKey(Key k) { EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) { const char *cmd = PSTR("keymap.map"); - if (::Focus.handleHelp(command, cmd)) + if (::Focus.handleHelp(command, PSTR("keymap.map\nkeymap.roLayers"))) + return EventHandlerResult::OK; + + if (strncmp_P(command, PSTR("keymap."), 7) != 0) return EventHandlerResult::OK; - if (strcmp_P(command, cmd) != 0) + if (strcmp_P(command + 7, PSTR("roLayers")) == 0) { + if (mode_ != Mode::EXTEND) + return EventHandlerResult::OK; + Serial.println(progmem_layers_); + return EventHandlerResult::EVENT_CONSUMED; + } + + if (strcmp_P(command + 7, PSTR("map")) != 0) return EventHandlerResult::OK; if (Serial.peek() == '\n') { - for (uint8_t layer = 0; layer < max_layers_; layer++) { + for (uint8_t layer = 0; layer < layer_count; layer++) { for (uint8_t row = 0; row < ROWS; row++) { for (uint8_t col = 0; col < COLS; col++) { Key k = Layer.getKey(layer, row, col); @@ -110,8 +128,19 @@ EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) { Serial.println(); } else { uint16_t i = 0; - while ((Serial.peek() != '\n') && (i < ROWS * COLS * max_layers_)) { - updateKey(i, parseKey()); + uint8_t layers = layer_count; + if (layers > 0) + layers--; + while ((Serial.peek() != '\n') && (i < ROWS * COLS * layers)) { + Key k = parseKey(); + + if (mode_ == Mode::EXTEND) { + uint8_t layer = i / (ROWS * COLS); + if (layer >= progmem_layers_) + updateKey(i - (progmem_layers_ * ROWS * COLS), k); + } else { + updateKey(i, k); + } i++; } } diff --git a/src/Kaleidoscope/EEPROM-Keymap.h b/src/Kaleidoscope/EEPROM-Keymap.h index 86368f3a..4cff00ea 100644 --- a/src/Kaleidoscope/EEPROM-Keymap.h +++ b/src/Kaleidoscope/EEPROM-Keymap.h @@ -23,23 +23,32 @@ namespace kaleidoscope { class EEPROMKeymap : public kaleidoscope::Plugin { public: + enum class Mode { + CUSTOM, + EXTEND, + }; + EEPROMKeymap(void) {} EventHandlerResult onSetup(); EventHandlerResult onFocusEvent(const char *command); + static void setup(uint8_t max, Mode mode = Mode::EXTEND); + static void max_layers(uint8_t max); static uint16_t keymap_base(void); static Key getKey(uint8_t layer, byte row, byte col); - static Key getKeyOverride(uint8_t layer, byte row, byte col); + static Key getKeyExtended(uint8_t layer, byte row, byte col); static void updateKey(uint16_t base_pos, Key key); private: static uint16_t keymap_base_; static uint8_t max_layers_; + static uint8_t progmem_layers_; + static Mode mode_; static Key parseKey(void); static void printKey(Key key);