diff --git a/src/key_events.cpp b/src/key_events.cpp index 9d5160a6..782e301b 100644 --- a/src/key_events.cpp +++ b/src/key_events.cpp @@ -44,8 +44,10 @@ static bool handleKeyswitchEventDefault(Key mappedKey, byte row, byte col, uint8 } void handleKeyswitchEvent(Key mappedKey, byte row, byte col, uint8_t keyState) { + /* If a key had an on or off event, we update the effective keymap cache. See + * layers.h for an explanation about the different caches we have. */ if (keyToggledOn(keyState) || keyToggledOff(keyState)) - Layer.updateKeymapCache(row, col); + Layer.updateEffectiveKeymapCache(row, col); /* If the key we are dealing with is masked, ignore it until it is released. * When releasing it, clear the mask, so future key events can be handled diff --git a/src/layers.cpp b/src/layers.cpp index 53254889..ea20e201 100644 --- a/src/layers.cpp +++ b/src/layers.cpp @@ -4,7 +4,8 @@ static uint8_t DefaultLayer; static uint32_t LayerState; uint8_t Layer_::highestLayer; -Key Layer_::keyMap[ROWS][COLS]; +Key Layer_::effectiveKeymapCache[ROWS][COLS]; +uint8_t Layer_::keymapCache[ROWS][COLS]; Key(*Layer_::getKey)(uint8_t layer, byte row, byte col) = Layer.getKeyFromPROGMEM; static void handleKeymapKeyswitchEvent(Key keymapEntry, uint8_t keyState) { @@ -62,29 +63,29 @@ Layer_::getKeyFromPROGMEM(uint8_t layer, byte row, byte col) { } void -Layer_::updateKeymapCache(byte row, byte col) { - int8_t layer = highestLayer; - - if (row >= ROWS || col >= COLS) - return; - - for (layer = highestLayer; layer >= DefaultLayer; layer--) { - if (Layer.isOn(layer)) { - Key mappedKey = (*getKey)(layer, row, col); - - if (mappedKey != Key_Transparent) { - keyMap[row][col] = mappedKey; - break; - } - } - } +Layer_::updateEffectiveKeymapCache(byte row, byte col) { + int8_t layer = keymapCache[row][col]; + effectiveKeymapCache[row][col] = (*getKey)(layer, row, col); } void Layer_::updateKeymapCache(void) { + memset(keymapCache, DefaultLayer, ROWS * COLS); for (byte row = 0; row < ROWS; row++) { for (byte col = 0; col < COLS; col++) { - updateKeymapCache(row, col); + int8_t layer = highestLayer; + + while (layer > DefaultLayer) { + if (Layer.isOn(layer)) { + Key mappedKey = (*getKey)(layer, row, col); + + if (mappedKey != Key_Transparent) { + keymapCache[row][col] = layer; + break; + } + } + layer--; + } } } } @@ -109,8 +110,8 @@ void Layer_::on(uint8_t layer) { if (layer > highestLayer) highestLayer = layer; - // Update the key cache, so that if anything depends on knowing the active - // layout, the layout will be in sync. + /* If the layer did turn on, update the keymap cache. See layers.h for an + * explanation about the caches we have. */ if (!wasOn) updateKeymapCache(); } @@ -122,8 +123,8 @@ void Layer_::off(uint8_t layer) { if (layer == highestLayer) highestLayer = top(); - // Update the key cache, so that if anything depends on knowing the active - // layout, the layout will be in sync. + /* If the layer did turn off, update the keymap cache. See layers.h for an + * explanation about the caches we have. */ if (wasOn) updateKeymapCache(); } diff --git a/src/layers.h b/src/layers.h index 091b1417..8990c6e1 100644 --- a/src/layers.h +++ b/src/layers.h @@ -8,9 +8,41 @@ class Layer_ { public: Layer_(void); + /* There are two lookup functions, because we have two caches, and different + * parts of the firmware will want to use either this or that (or perhaps + * both, in rare cases). + * + * First of all, we use caches because looking up a key through all the layers + * is costy, and the cost increases dramatically the more layers we have. + * + * Then, we have the `effectiveKeymapCache`, because to have layer behaviours + * we want, that is, if you hold a key on a layer, release the layer key but + * continue holding the other, we want for the layered keycode to continue + * repeating. At the same time, we want other keys to not be affected by the + * now-turned-off layer. So we update the keycode in the cache on-demand, when + * the key is pressed or released. (see the top of `handleKeyswitchEvent`). + * + * On the other hand, we also have plugins that scan the whole keymap, and do + * things based on that information, such as highlighting keys that changed + * between layers. These need to be able to look at a state of where the + * keymap *should* be, not necessarily where it is. The `effectiveKeymapCache` + * is not useful here. So we use a `keymapCache` which we update whenever + * layers change (see `Layer.on` and `Layer.off`), and it updates the cache to + * show how the keymap should look, without the `effectiveKeymapCache`-induced + * behaviour. + * + * Thus, if we are curious about what a given key will do, use `lookup`. If we + * are curious what the active layer state describes the key as, use + * `lookupUncached`. + */ static Key lookup(byte row, byte col) { - return keyMap[row][col]; + return effectiveKeymapCache[row][col]; } + static Key lookupUncached(byte row, byte col) { + uint8_t layer = keymapCache[row][col]; + return (*getKey)(layer, row, col); + } + static void on(uint8_t layer); static void off(uint8_t layer); static void move(uint8_t layer); @@ -32,12 +64,13 @@ class Layer_ { static Key getKeyFromPROGMEM(uint8_t layer, byte row, byte col); - static void updateKeymapCache(byte row, byte col); + static void updateEffectiveKeymapCache(byte row, byte col); static void updateKeymapCache(void); private: static uint8_t highestLayer; - static Key keyMap[ROWS][COLS]; + static Key effectiveKeymapCache[ROWS][COLS]; + static uint8_t keymapCache[ROWS][COLS]; }; Key layer_getKey(uint8_t layer, uint8_t r, uint8_t c);