Merge pull request #17 from keyboardio/f/extend

Implement an Extend mode
pull/365/head
Jesse Vincent 6 years ago committed by GitHub
commit 47808afb1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,26 +9,15 @@
[st:broken]: https://img.shields.io/badge/broken-X-black.svg?style=flat&colorA=e05d44&colorB=494e52 [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 [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 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.
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. 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.
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.
[plugin:focusSerial]: https://github.com/keyboardio/Kaleidoscope-FocusSerial [plugin:focusSerial]: https://github.com/keyboardio/Kaleidoscope-FocusSerial
## Using the plugin ## Using the plugin
Using the plugin is reasonably simple: after including the header, enable the 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.
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]. 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-FocusSerial.h> #include <Kaleidoscope-FocusSerial.h>
KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap, KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap,
Focus, Focus);
FocusKeymapTransferCommand);
void setup() { void setup() {
Kaleidoscope.setup(); Kaleidoscope.setup();
EEPROMKeymap.max_layers(1); EEPROMKeymap.setup(1);
} }
``` ```
## Plugin methods ## 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 > 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.
> 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 > Supported modes are:
> able to program from EEPROM (probably the number of layers you have > - `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`.
> defined in your keymap). > - `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 ## Focus commands
The plugin provides a `keymap.map` Focus command unconditionally, and a The plugin provides the `keymap.map` and a `keymap.roLayers` commands.
`keymap.transfer` via the `FocusKeymapTransferCommand` object.
### `keymap.map [codes...]` ### `keymap.map [codes...]`
> Without arguments, displays the keymap currently in effect. Each key is > Without arguments, displays the keymap currently in effect. Each key is printed as its raw, 16-bit keycode.
> printed as its raw, 16-bit keycode.
> >
> With arguments, it stores as many keys as given. One does not need to set all > 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`).
> 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.
### `keymap.transfer LAYER` ### `keymap.roLayers`
> Transfers the `LAYER` from the built-in memory of the keyboard into `EEPROM` > 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.
> 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.
## Dependencies ## Dependencies
@ -93,7 +69,6 @@ The plugin provides a `keymap.map` Focus command unconditionally, and a
## Further reading ## Further reading
Starting from the [example][plugin:example] is the recommended way of getting Starting from the [example][plugin:example] is the recommended way of getting started with the plugin.
started with the plugin.
[plugin:example]: https://github.com/keyboardio/Kaleidoscope-EEPROM-Keymap/blob/master/examples/EEPROM-Keymap/EEPROM-Keymap.ino [plugin:example]: https://github.com/keyboardio/Kaleidoscope-EEPROM-Keymap/blob/master/examples/EEPROM-Keymap/EEPROM-Keymap.ino

@ -40,12 +40,12 @@ KEYMAPS(
) )
// *INDENT-ON* // *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap, Focus, FocusKeymapTransferCommand); KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap, Focus);
void setup() { void setup() {
Kaleidoscope.setup(); Kaleidoscope.setup();
EEPROMKeymap.max_layers(1); EEPROMKeymap.setup(1, EEPROMKeymap.Mode::EXTEND);
} }
void loop() { void loop() {

@ -18,4 +18,3 @@
#pragma once #pragma once
#include <Kaleidoscope/EEPROM-Keymap.h> #include <Kaleidoscope/EEPROM-Keymap.h>
#include <Kaleidoscope/EEPROM-Keymap-Transfer.h>

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <Kaleidoscope-EEPROM-Settings.h>
#include <Kaleidoscope-EEPROM-Keymap.h>
#include <Kaleidoscope-FocusSerial.h>
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;

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Kaleidoscope.h>
#include <Kaleidoscope/EEPROM-Keymap.h>
namespace kaleidoscope {
namespace eeprom {
class FocusKeymapTransferCommand : public Plugin {
public:
FocusKeymapTransferCommand() {}
EventHandlerResult onFocusEvent(const char *command);
};
}
}
extern kaleidoscope::eeprom::FocusKeymapTransferCommand FocusKeymapTransferCommand;

@ -20,16 +20,30 @@
#include <Kaleidoscope-FocusSerial.h> #include <Kaleidoscope-FocusSerial.h>
namespace kaleidoscope { namespace kaleidoscope {
EEPROMKeymap::Mode EEPROMKeymap::mode_;
uint16_t EEPROMKeymap::keymap_base_; uint16_t EEPROMKeymap::keymap_base_;
uint8_t EEPROMKeymap::max_layers_; uint8_t EEPROMKeymap::max_layers_;
uint8_t EEPROMKeymap::progmem_layers_;
EventHandlerResult EEPROMKeymap::onSetup() { EventHandlerResult EEPROMKeymap::onSetup() {
::EEPROMSettings.onSetup(); ::EEPROMSettings.onSetup();
Layer.getKey = ::EEPROMKeymap.getKeyOverride; progmem_layers_ = layer_count;
return EventHandlerResult::OK; 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) { void EEPROMKeymap::max_layers(uint8_t max) {
max_layers_ = max; max_layers_ = max;
keymap_base_ = ::EEPROMSettings.requestSlice(max_layers_ * ROWS * COLS * 2); 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; 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 key;
key = getKey(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);
* 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);
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) { uint16_t EEPROMKeymap::keymap_base(void) {
@ -90,14 +98,24 @@ void EEPROMKeymap::printKey(Key k) {
EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) { EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) {
const char *cmd = PSTR("keymap.map"); const char *cmd = PSTR("keymap.map");
if (::Focus.handleHelp(command, cmd)) if (::Focus.handleHelp(command, PSTR("keymap.map\nkeymap.roLayers")))
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (strcmp_P(command, cmd) != 0) if (strncmp_P(command, PSTR("keymap."), 7) != 0)
return EventHandlerResult::OK;
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; return EventHandlerResult::OK;
if (Serial.peek() == '\n') { 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 row = 0; row < ROWS; row++) {
for (uint8_t col = 0; col < COLS; col++) { for (uint8_t col = 0; col < COLS; col++) {
Key k = Layer.getKey(layer, row, col); Key k = Layer.getKey(layer, row, col);
@ -110,8 +128,19 @@ EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) {
Serial.println(); Serial.println();
} else { } else {
uint16_t i = 0; uint16_t i = 0;
while ((Serial.peek() != '\n') && (i < ROWS * COLS * max_layers_)) { uint8_t layers = layer_count;
updateKey(i, parseKey()); 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++; i++;
} }
} }

@ -23,23 +23,32 @@
namespace kaleidoscope { namespace kaleidoscope {
class EEPROMKeymap : public kaleidoscope::Plugin { class EEPROMKeymap : public kaleidoscope::Plugin {
public: public:
enum class Mode {
CUSTOM,
EXTEND,
};
EEPROMKeymap(void) {} EEPROMKeymap(void) {}
EventHandlerResult onSetup(); EventHandlerResult onSetup();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *command);
static void setup(uint8_t max, Mode mode = Mode::EXTEND);
static void max_layers(uint8_t max); static void max_layers(uint8_t max);
static uint16_t keymap_base(void); static uint16_t keymap_base(void);
static Key getKey(uint8_t layer, byte row, byte col); 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); static void updateKey(uint16_t base_pos, Key key);
private: private:
static uint16_t keymap_base_; static uint16_t keymap_base_;
static uint8_t max_layers_; static uint8_t max_layers_;
static uint8_t progmem_layers_;
static Mode mode_;
static Key parseKey(void); static Key parseKey(void);
static void printKey(Key key); static void printKey(Key key);

Loading…
Cancel
Save