You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Kaleidoscope/src/kaleidoscope/layers.cpp

210 lines
6.3 KiB

/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-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/Kaleidoscope.h"
// The maximum number of layers allowed. `layer_state_`, which stores
// the on/off status of the layers in a bitfield has only 32 bits, and
// that should be enough for almost any layout.
#define MAX_LAYERS sizeof(uint32_t) * 8;
// The total number of defined layers in the firmware sketch keymaps[]
// array. If the keymap wasn't defined using KEYMAPS(), set it to the
// highest possible number of layers.
uint8_t layer_count __attribute__((weak)) = MAX_LAYERS;
namespace kaleidoscope {
uint32_t Layer_::layer_state_;
uint8_t Layer_::top_active_layer_;
Key Layer_::live_composite_keymap_[ROWS][COLS];
uint8_t Layer_::active_layers_[ROWS][COLS];
Key(*Layer_::getKey)(uint8_t layer, byte row, byte col) = Layer.getKeyFromPROGMEM;
void Layer_::handleKeymapKeyswitchEvent(Key keymapEntry, uint8_t keyState) {
if (keymapEntry.keyCode >= LAYER_SHIFT_OFFSET) {
uint8_t target = keymapEntry.keyCode - LAYER_SHIFT_OFFSET;
switch (target) {
case KEYMAP_NEXT:
if (keyToggledOn(keyState))
activateNext();
else if (keyToggledOff(keyState))
deactivateTop();
break;
case KEYMAP_PREVIOUS:
if (keyToggledOn(keyState))
deactivateTop();
else if (keyToggledOff(keyState))
activateNext();
break;
default:
/* The default case is when we are switching to a layer by its number, and
* is a bit more complicated than switching there when the key toggles on,
* and away when it toggles off.
*
* We want to handle the case where we have more than one momentary layer
* key on our keymap that point to the same target layer, and we hold
* both, and release one. In this case, the layer should remain active,
* because the second momentary key is still held.
*
* To do this, we turn the layer back on if the switcher key is still
* held, not only when it toggles on. So when one of them is released,
* that does turn the layer off, but with the other still being held, the
* layer will toggle back on in the same cycle.
*/
if (keyIsPressed(keyState)) {
if (!Layer.isActive(target))
activate(target);
} else if (keyToggledOff(keyState)) {
deactivate(target);
}
break;
}
} else if (keyToggledOn(keyState)) {
// switch keymap and stay there
if (Layer.isActive(keymapEntry.keyCode) && keymapEntry.keyCode)
deactivate(keymapEntry.keyCode);
else
activate(keymapEntry.keyCode);
}
}
Key Layer_::eventHandler(Key mappedKey, byte row, byte col, uint8_t keyState) {
if (mappedKey.flags != (SYNTHETIC | SWITCH_TO_KEYMAP))
return mappedKey;
handleKeymapKeyswitchEvent(mappedKey, keyState);
return Key_NoKey;
}
Key Layer_::getKeyFromPROGMEM(uint8_t layer, byte row, byte col) {
Key key;
key.raw = pgm_read_word(&(keymaps[layer][row][col]));
return key;
}
void Layer_::updateLiveCompositeKeymap(byte row, byte col) {
int8_t layer = active_layers_[row][col];
live_composite_keymap_[row][col] = (*getKey)(layer, row, col);
}
void Layer_::updateActiveLayers(void) {
memset(active_layers_, 0, ROWS * COLS);
for (byte row = 0; row < ROWS; row++) {
for (byte col = 0; col < COLS; col++) {
int8_t layer = top_active_layer_;
while (layer > 0) {
if (Layer.isActive(layer)) {
Key mappedKey = (*getKey)(layer, row, col);
if (mappedKey != Key_Transparent) {
active_layers_[row][col] = layer;
break;
}
}
layer--;
}
}
}
}
void Layer_::updateTopActiveLayer(void) {
// If layer_count is set, start there, otherwise search from the
// highest possible layer (MAX_LAYERS) for the top active layer
for (byte i = (layer_count - 1); i > 0; i--) {
if (bitRead(layer_state_, i)) {
top_active_layer_ = i;
return;
}
}
// It's not possible to turn off the default layer (see
// updateActiveLayers()), so if no other layers are active:
top_active_layer_ = 0;
}
void Layer_::move(uint8_t layer) {
layer_state_ = 0;
activate(layer);
}
// Activate a given layer
void Layer_::activate(uint8_t layer) {
// If we're trying to turn on a layer that doesn't exist, abort (but
// if the keymap wasn't defined using the KEYMAPS() macro, proceed anyway
if (layer >= layer_count)
return;
// If the target layer was already on, return
if (isActive(layer))
return;
// Otherwise, turn on its bit in layer_state_
bitSet(layer_state_, layer);
// If the target layer is above the previous highest active layer,
// update top_active_layer_
if (layer > top_active_layer_)
updateTopActiveLayer();
// Update the keymap cache (but not live_composite_keymap_; that gets
// updated separately, when keys toggle on or off. See layers.h)
updateActiveLayers();
kaleidoscope::Hooks::onLayerChange();
}
// Deactivate a given layer
void Layer_::deactivate(uint8_t layer) {
// If the target layer was already off, return
if (!bitRead(layer_state_, layer))
return;
// Turn off its bit in layer_state_
bitClear(layer_state_, layer);
// If the target layer was the previous highest active layer,
// update top_active_layer_
if (layer == top_active_layer_)
updateTopActiveLayer();
// Update the keymap cache (but not live_composite_keymap_; that gets
// updated separately, when keys toggle on or off. See layers.h)
updateActiveLayers();
kaleidoscope::Hooks::onLayerChange();
}
boolean Layer_::isActive(uint8_t layer) {
return bitRead(layer_state_, layer);
}
void Layer_::activateNext(void) {
activate(top_active_layer_ + 1);
}
void Layer_::deactivateTop(void) {
deactivate(top_active_layer_);
}
}
kaleidoscope::Layer_ Layer;