diff --git a/examples/Features/KeyGroups/ArrayBasedMembership/ArrayBasedMembership.ino b/examples/Features/KeyGroups/ArrayBasedMembership/ArrayBasedMembership.ino new file mode 100644 index 0000000000..a295db3308 --- /dev/null +++ b/examples/Features/KeyGroups/ArrayBasedMembership/ArrayBasedMembership.ino @@ -0,0 +1,113 @@ +/* -*- mode: c++ -*- + * Basic -- A very basic Kaleidoscope example + * Copyright (C) 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 "Kaleidoscope.h" + +#include "Kaleidoscope-LED-ActiveLayerColor.h" + +// This example demonstrates how keys can be grouped by +// a decision function that relies on a keymap layer kind of array +// that stores the key group id of every individual key. +// +// Please note that the function groupOfKey(..) may only return values +// in the range [0;5]. + +enum { AMap, BMap }; + +/* *INDENT-OFF* */ +KEYMAPS( + [AMap] = KEYMAP_STACKED + ( + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + + Key_A, Key_A, Key_A, Key_A, + KeyGroup(KEY_GROUP_2, ShiftToLayer(BMap)), + + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + + Key_A, Key_A, Key_A, Key_A, + KeyGroup(KEY_GROUP_1, ShiftToLayer(BMap)) + ), + + [BMap] = KEYMAP_STACKED + ( + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + + Key_B, Key_B, Key_B, Key_B, + ___, + + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + + Key_B, Key_B, Key_B, Key_B, + ___ + ) +) +/* *INDENT-ON* */ + +KALEIDOSCOPE_INIT_PLUGINS( + LEDControl, + LEDActiveLayerColorEffect +) + +KEY_GROUP_IDS_STACKED( + 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, + 0, + + 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, + 0 +) + +uint8_t groupOfKey(KeyAddr key_addr) { + return GROUP_OF_KEY(key_addr); +} + +void setup() { + Kaleidoscope.setup(); + + static const cRGB layerColormap[] PROGMEM = { + CRGB(128, 0, 0), + CRGB(0, 128, 0) + }; + + LEDActiveLayerColorEffect.setColormap(layerColormap); +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/examples/Features/KeyGroups/ConditionalMembership/ConditionalMembership.ino b/examples/Features/KeyGroups/ConditionalMembership/ConditionalMembership.ino new file mode 100644 index 0000000000..82e37d2831 --- /dev/null +++ b/examples/Features/KeyGroups/ConditionalMembership/ConditionalMembership.ino @@ -0,0 +1,94 @@ +/* -*- mode: c++ -*- + * Basic -- A very basic Kaleidoscope example + * Copyright (C) 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 "Kaleidoscope.h" + +#include "Kaleidoscope-LED-ActiveLayerColor.h" + +// This example demonstrates how keys can be grouped using conditionals +// in a decision function. +// +// Please note that the function groupOfKey(...) may only return values +// in the range [0;5]. + +enum { AMap, BMap }; + +/* *INDENT-OFF* */ +KEYMAPS( + [AMap] = KEYMAP_STACKED + ( + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + + Key_A, Key_A, Key_A, Key_A, + KeyGroup(KEY_GROUP_1, ShiftToLayer(BMap)), + + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + + Key_A, Key_A, Key_A, Key_A, + KeyGroup(KEY_GROUP_0 | KEY_GROUP_2, ShiftToLayer(BMap)) + ), + + [BMap] = KEYMAP_STACKED + ( + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + + Key_B, Key_B, Key_B, Key_B, + ___, + + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + + Key_B, Key_B, Key_B, Key_B, + ___ + ) +) +/* *INDENT-ON* */ + +KALEIDOSCOPE_INIT_PLUGINS( + LEDControl, + LEDActiveLayerColorEffect +) + +uint8_t groupOfKey(KeyAddr key_addr) { + return key_addr.row(); +} + +void setup() { + Kaleidoscope.setup(); + + static const cRGB layerColormap[] PROGMEM = { + CRGB(128, 0, 0), + CRGB(0, 128, 0) + }; + + LEDActiveLayerColorEffect.setColormap(layerColormap); +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/examples/Features/KeyGroups/Default/Default.ino b/examples/Features/KeyGroups/Default/Default.ino new file mode 100644 index 0000000000..57ba1238d7 --- /dev/null +++ b/examples/Features/KeyGroups/Default/Default.ino @@ -0,0 +1,88 @@ +/* -*- mode: c++ -*- + * Basic -- A very basic Kaleidoscope example + * Copyright (C) 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 "Kaleidoscope.h" + +#include "Kaleidoscope-LED-ActiveLayerColor.h" + +// This example demonstrates the default key grouping mechanism +// where the left hand is assigned to key group 0 (KEY_GROUP_LEFT_HAND), +// the right hand to key group 1 (KEY_GROUP_RIGHT_HAND). + +enum { AMap, BMap }; + +/* *INDENT-OFF* */ +KEYMAPS( + [AMap] = KEYMAP_STACKED + ( + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + + Key_A, Key_A, Key_A, Key_A, + KeyGroup(KEY_GROUP_RIGHT_HAND, ShiftToLayer(BMap)), + + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, Key_A, + + Key_A, Key_A, Key_A, Key_A, + KeyGroup(KEY_GROUP_LEFT_HAND, ShiftToLayer(BMap)) + ), + + [BMap] = KEYMAP_STACKED + ( + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + + Key_B, Key_B, Key_B, Key_B, + ___, + + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, Key_B, + + Key_B, Key_B, Key_B, Key_B, + ___ + ) +) +/* *INDENT-ON* */ + +KALEIDOSCOPE_INIT_PLUGINS( + LEDControl, + LEDActiveLayerColorEffect +) + +void setup() { + Kaleidoscope.setup(); + + static const cRGB layerColormap[] PROGMEM = { + CRGB(128, 0, 0), + CRGB(0, 128, 0) + }; + + LEDActiveLayerColorEffect.setColormap(layerColormap); +} + +void loop() { + Kaleidoscope.loop(); +} diff --git a/src/Kaleidoscope-KeyGroups.h b/src/Kaleidoscope-KeyGroups.h new file mode 100644 index 0000000000..73497b7973 --- /dev/null +++ b/src/Kaleidoscope-KeyGroups.h @@ -0,0 +1,20 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-EZ-ErgoDox -- ErgoDox hardware support for Kaleidoscope + * Copyright (C) 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 "kaleidoscope/key_groups.h" diff --git a/src/kaleidoscope/Hardware.h b/src/kaleidoscope/Hardware.h index 8f231c47e7..dc79a170a0 100644 --- a/src/kaleidoscope/Hardware.h +++ b/src/kaleidoscope/Hardware.h @@ -45,6 +45,7 @@ */ namespace kaleidoscope { + /** Kaleidoscope Hardware base class. * Essential methods all hardware libraries must implement. */ diff --git a/src/kaleidoscope/device/ez/ErgoDox.h b/src/kaleidoscope/device/ez/ErgoDox.h index c706b27ff6..0d7ca69047 100644 --- a/src/kaleidoscope/device/ez/ErgoDox.h +++ b/src/kaleidoscope/device/ez/ErgoDox.h @@ -48,10 +48,22 @@ namespace kaleidoscope { namespace device { namespace ez { +struct ErgoDoxKeyScannerProps : kaleidoscope::driver::keyscanner::BaseProps { + KEYSCANNER_PROPS(14, 6); +}; + +class ErgoDoxKeyScanner : public kaleidoscope::device::ATmega32U4KeyboardProps::KeyScanner { + public: + + static constexpr bool isOnLeftHalf(ErgoDoxKeyScannerProps::KeyAddr key_addr) { + return key_addr.row() < 7; + } +}; + struct ErgoDoxProps : public kaleidoscope::device::ATmega32U4KeyboardProps { - struct KeyScannerProps : kaleidoscope::driver::keyscanner::BaseProps { - KEYSCANNER_PROPS(14, 6); - }; + + typedef ErgoDoxKeyScannerProps KeyScannerProps; + typedef ErgoDoxKeyScanner KeyScanner; typedef kaleidoscope::driver::bootloader::avr::HalfKay Bootloader; }; diff --git a/src/kaleidoscope/device/keyboardio/Model01.cpp b/src/kaleidoscope/device/keyboardio/Model01.cpp index ae6ecb62b3..83504c9541 100644 --- a/src/kaleidoscope/device/keyboardio/Model01.cpp +++ b/src/kaleidoscope/device/keyboardio/Model01.cpp @@ -197,7 +197,7 @@ void Model01KeyScanner::scanMatrix() { // halves, with eight keys per logical row. constexpr byte HIGH_BIT = B10000000; -constexpr byte HAND_BIT = B00001000; +constexpr byte HAND_BIT = Model01KeyScannerProps::HAND_BIT; constexpr byte ROW_BITS = B00110000; constexpr byte COL_BITS = B00000111; diff --git a/src/kaleidoscope/device/keyboardio/Model01.h b/src/kaleidoscope/device/keyboardio/Model01.h index ceac5c5dbf..4da7462daa 100644 --- a/src/kaleidoscope/device/keyboardio/Model01.h +++ b/src/kaleidoscope/device/keyboardio/Model01.h @@ -73,6 +73,8 @@ class Model01LEDDriver; struct Model01KeyScannerProps : public kaleidoscope::driver::keyscanner::BaseProps { KEYSCANNER_PROPS(4, 16); + + static constexpr byte HAND_BIT = B00001000; }; #ifndef KALEIDOSCOPE_VIRTUAL_BUILD @@ -80,6 +82,7 @@ class Model01KeyScanner : public kaleidoscope::driver::keyscanner::Base. + */ + +#pragma once + +#include "kaleidoscope/key_defs.h" + +static constexpr uint8_t encodeKeyGroupFlags(uint8_t key_group_flags) { + + // We use the unused bits in the key flags of layer change/toggle keycodes + // to code the key groups that are affected by a layer operation. + // Every bit thereby represents one keygroup. + // Unfortunately, the unused bits that are available dont form + // a contiguous part of the flags byte. + // + // The following bits of the key flags bitfield are already used: + // SYNTHETIC | SWITCH_TO_KEYMAP + // + // Thus, the following bits (those set to one) B10111011 can be used + // to code affected key groups. Every group is assigned one bit. + // Because of this, only a maximum of six key groups can be defined. + // If all flags are set to zero, this means the same as if all related + // flags are set to one. This is necessary to provide backwards compatibility + // for those layer toggle/switch commands that are not associated with + // key groups. Those commands then mean "all groups affected". + + return (key_group_flags & B00000011) + | ((key_group_flags & B00011100) << 1) + | ((key_group_flags & B00100000) << 2); +} + +static constexpr uint8_t decodeKeyGroupFlags(uint8_t coded_flags) { + + return (coded_flags & B00000011) + | ((coded_flags & B00111000) >> 1) + | ((coded_flags & B10000000) >> 2); +} + +inline static constexpr Key KeyGroup(uint8_t key_group_flags, Key k) { + return Key(k.getKeyCode(), k.getFlags() | encodeKeyGroupFlags(key_group_flags)); +} + +// Key group flags +static constexpr uint8_t KEY_GROUP_0 = B00000001; +static constexpr uint8_t KEY_GROUP_1 = B00000010; +static constexpr uint8_t KEY_GROUP_2 = B00000100; +static constexpr uint8_t KEY_GROUP_3 = B00001000; +static constexpr uint8_t KEY_GROUP_4 = B00010000; +static constexpr uint8_t KEY_GROUP_5 = B00100000; + +// The default configuration of key grouping is that group 0 is assigned +// to the left hand, group 1 to the right. We provide constants to be used in +// user sketches. +static constexpr uint8_t KEY_GROUP_LEFT_HAND = KEY_GROUP_0; +static constexpr uint8_t KEY_GROUP_RIGHT_HAND = KEY_GROUP_1; + +// This is also the default if no key group is assigned to a layer toggle +// keycode. +static constexpr uint8_t ALL_KEY_GROUPS = B00111111; + +namespace kaleidoscope { + +// The maximum number of possible key groups. This cannot be exceeded +// as there are not more than six unused bits in the key flags left to +// code the keygroup. +static constexpr uint8_t max_num_key_groups = 6; + +static constexpr bool isKeyGroupFlagSet(uint8_t key_group_flags, uint8_t flag_id) { + return key_group_flags & (B00000001 << flag_id); +} + +} // end kaleidoscope + +#define KEY_GROUP_IDS_STACKED(group_ids_per_key...) \ + const uint8_t key_groups[kaleidoscope_internal::device.numKeys()] PROGMEM = KEYMAP_STACKED(group_ids_per_key); + +#define KEY_GROUP_IDS(group_ids_per_key...) \ + const uint8_t key_groups[kaleidoscope_internal::device.numKeys()] PROGMEM = KEYMAP(group_ids_per_key); + +#define GROUP_OF_KEY(key_addr) \ + pgm_read_byte(&(key_groups[key_addr.toInt()])) diff --git a/src/kaleidoscope/layers.cpp b/src/kaleidoscope/layers.cpp index f3efb80e78..4296d54835 100644 --- a/src/kaleidoscope/layers.cpp +++ b/src/kaleidoscope/layers.cpp @@ -36,29 +36,36 @@ __attribute__((weak)) extern constexpr Key keymaps_linear[][kaleidoscope_internal::device.matrix_rows * kaleidoscope_internal::device.matrix_columns] = {}; namespace kaleidoscope { -uint32_t Layer_::layer_state_; -uint8_t Layer_::top_active_layer_; +uint32_t Layer_::layer_state_[kaleidoscope::max_num_key_groups] = { 0, 0, 0, 0, 0, 0 }; +uint8_t Layer_::top_active_layer_[kaleidoscope::max_num_key_groups] = { 0, 0, 0, 0, 0, 0 }; Key Layer_::live_composite_keymap_[Kaleidoscope.device().numKeys()]; uint8_t Layer_::active_layers_[Kaleidoscope.device().numKeys()]; Key(*Layer_::getKey)(uint8_t layer, KeyAddr key_addr) = Layer.getKeyFromPROGMEM; void Layer_::handleKeymapKeyswitchEvent(Key keymapEntry, uint8_t keyState) { + + uint8_t key_group_flags = decodeKeyGroupFlags(keymapEntry.getFlags()); + + if (key_group_flags == 0) { + key_group_flags = ALL_KEY_GROUPS; + } + if (keymapEntry.getKeyCode() >= LAYER_SHIFT_OFFSET) { uint8_t target = keymapEntry.getKeyCode() - LAYER_SHIFT_OFFSET; switch (target) { case KEYMAP_NEXT: if (keyToggledOn(keyState)) - activateNext(); + activateNext(key_group_flags); else if (keyToggledOff(keyState)) - deactivateTop(); + deactivateTop(key_group_flags); break; case KEYMAP_PREVIOUS: if (keyToggledOn(keyState)) - deactivateTop(); + deactivateTop(key_group_flags); else if (keyToggledOff(keyState)) - activateNext(); + activateNext(key_group_flags); break; default: @@ -77,24 +84,24 @@ void Layer_::handleKeymapKeyswitchEvent(Key keymapEntry, uint8_t keyState) { * layer will toggle back on in the same cycle. */ if (keyIsPressed(keyState)) { - if (!Layer.isActive(target)) - activate(target); + if (!Layer.isActive(target, key_group_flags)) + activate(target, key_group_flags); } else if (keyToggledOff(keyState)) { - deactivate(target); + deactivate(target, key_group_flags); } break; } } else if (keyToggledOn(keyState)) { // switch keymap and stay there - if (Layer.isActive(keymapEntry.getKeyCode()) && keymapEntry.getKeyCode()) - deactivate(keymapEntry.getKeyCode()); + if (Layer.isActive(keymapEntry.getKeyCode(), key_group_flags) && keymapEntry.getKeyCode()) + deactivate(keymapEntry.getKeyCode(), key_group_flags); else - activate(keymapEntry.getKeyCode()); + activate(keymapEntry.getKeyCode(), key_group_flags); } } Key Layer_::eventHandler(Key mappedKey, KeyAddr key_addr, uint8_t keyState) { - if (mappedKey.getFlags() != (SYNTHETIC | SWITCH_TO_KEYMAP)) + if (!((mappedKey.getFlags() & SYNTHETIC) && (mappedKey.getFlags() & SWITCH_TO_KEYMAP))) return mappedKey; handleKeymapKeyswitchEvent(mappedKey, keyState); @@ -113,10 +120,13 @@ void Layer_::updateLiveCompositeKeymap(KeyAddr key_addr) { void Layer_::updateActiveLayers(void) { memset(active_layers_, 0, Kaleidoscope.device().numKeys()); for (auto key_addr : KeyAddr::all()) { - int8_t layer = top_active_layer_; + + uint8_t key_group = groupOfKey(key_addr); + + int8_t layer = top_active_layer_[key_group]; while (layer > 0) { - if (Layer.isActive(layer)) { + if (Layer.isActive(layer, key_group)) { Key mappedKey = (*getKey)(layer, key_addr); if (mappedKey != Key_Transparent) { @@ -129,43 +139,66 @@ void Layer_::updateActiveLayers(void) { } } -void Layer_::updateTopActiveLayer(void) { +void Layer_::updateTopActiveLayer(uint8_t key_group) { // 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; + if (bitRead(layer_state_[key_group], i)) { + top_active_layer_[key_group] = 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; + top_active_layer_[key_group] = 0; } -void Layer_::move(uint8_t layer) { - layer_state_ = 0; - activate(layer); +void Layer_::move(uint8_t layer, uint8_t key_group_flags) { + + for (uint8_t key_group = 0; key_group < kaleidoscope::max_num_key_groups; ++key_group) { + + if (!isKeyGroupFlagSet(key_group_flags, key_group)) { + continue; + } + + layer_state_[key_group] = 0; + } + + activate(layer, key_group_flags); } // 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; +void Layer_::activate(uint8_t layer, uint8_t key_group_flags) { + + bool changes_applied = false; + + for (uint8_t key_group = 0; key_group < kaleidoscope::max_num_key_groups; ++key_group) { + + if (!isKeyGroupFlagSet(key_group_flags, key_group)) { + continue; + } - // If the target layer was already on, return - if (isActive(layer)) - return; + // 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, key_group_flags)) + continue; + + changes_applied = true; - // Otherwise, turn on its bit in layer_state_ - bitSet(layer_state_, layer); + // Otherwise, turn on its bit in layer_state_ + bitSet(layer_state_[key_group], layer); + + // If the target layer is above the previous highest active layer, + // update top_active_layer_ + if (layer > top_active_layer_[key_group]) + updateTopActiveLayer(key_group); + } - // If the target layer is above the previous highest active layer, - // update top_active_layer_ - if (layer > top_active_layer_) - updateTopActiveLayer(); + if (!changes_applied) return; // Update the keymap cache (but not live_composite_keymap_; that gets // updated separately, when keys toggle on or off. See layers.h) @@ -175,18 +208,33 @@ void Layer_::activate(uint8_t layer) { } // 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); +void Layer_::deactivate(uint8_t layer, uint8_t key_group_flags) { - // If the target layer was the previous highest active layer, - // update top_active_layer_ - if (layer == top_active_layer_) - updateTopActiveLayer(); + bool changes_applied = false; + + for (uint8_t key_group = 0; key_group < kaleidoscope::max_num_key_groups; ++key_group) { + + if (!isKeyGroupFlagSet(key_group_flags, key_group)) { + continue; + } + + // If the target layer was already off, return + if (!bitRead(layer_state_[key_group], layer)) + continue; + + changes_applied = true; + + // Turn off its bit in layer_state_ + bitClear(layer_state_[key_group], layer); + + // If the target layer was the previous highest active layer, + // update top_active_layer_ + if (layer == top_active_layer_[key_group]) + updateTopActiveLayer(key_group); + } + + if (!changes_applied) return; // Update the keymap cache (but not live_composite_keymap_; that gets // updated separately, when keys toggle on or off. See layers.h) @@ -195,18 +243,37 @@ void Layer_::deactivate(uint8_t layer) { kaleidoscope::Hooks::onLayerChange(); } -boolean Layer_::isActive(uint8_t layer) { - return bitRead(layer_state_, layer); +boolean Layer_::isActive(uint8_t layer, uint8_t key_group) { + return bitRead(layer_state_[key_group], layer); +} + +void Layer_::activateNext(uint8_t key_group_flags) { + + for (uint8_t key_group = 0; key_group < kaleidoscope::max_num_key_groups; ++key_group) { + + if (!isKeyGroupFlagSet(key_group_flags, key_group)) { + continue; + } + activate(top_active_layer_[key_group] + 1, key_group_flags); + } } -void Layer_::activateNext(void) { - activate(top_active_layer_ + 1); +void Layer_::deactivateTop(uint8_t key_group_flags) { + + for (uint8_t key_group = 0; key_group < kaleidoscope::max_num_key_groups; ++key_group) { + + if (!isKeyGroupFlagSet(key_group_flags, key_group)) { + continue; + } + deactivate(top_active_layer_[key_group]); + } } -void Layer_::deactivateTop(void) { - deactivate(top_active_layer_); } +__attribute__((weak)) +uint8_t groupOfKey(KeyAddr key_addr) { + return Kaleidoscope.device().keyScanner().isOnLeftHalf(key_addr) ? 0 : 1; } kaleidoscope::Layer_ Layer; diff --git a/src/kaleidoscope/layers.h b/src/kaleidoscope/layers.h index c4e258bc45..1c039e6535 100644 --- a/src/kaleidoscope/layers.h +++ b/src/kaleidoscope/layers.h @@ -22,6 +22,7 @@ #include "kaleidoscope/device/device.h" #include "kaleidoscope_internal/device.h" #include "kaleidoscope_internal/sketch_exploration/sketch_exploration.h" +#include "kaleidoscope/key_groups.h" // Macro for defining the keymap. This should be used in the sketch // file (*.ino) to define the keymap[] array that holds the user's @@ -41,6 +42,7 @@ extern uint8_t layer_count; namespace kaleidoscope { + class Layer_ { public: Layer_() {} @@ -96,19 +98,19 @@ class Layer_ { return active_layers_[KeyAddr(row, col).toInt()]; } - static void activate(uint8_t layer); - static void deactivate(uint8_t layer); - static void activateNext(); - static void deactivateTop(); - static void move(uint8_t layer); + static void activate(uint8_t layer, uint8_t key_group_flags = ALL_KEY_GROUPS); + static void deactivate(uint8_t layer, uint8_t key_group_flags = ALL_KEY_GROUPS); + static void activateNext(uint8_t key_group_flags = ALL_KEY_GROUPS); + static void deactivateTop(uint8_t key_group_flags = ALL_KEY_GROUPS); + static void move(uint8_t layer, uint8_t key_group_flags = ALL_KEY_GROUPS); - static uint8_t top(void) { - return top_active_layer_; + static uint8_t top(uint8_t key_group = 0) { + return top_active_layer_[key_group]; } - static boolean isActive(uint8_t layer); + static boolean isActive(uint8_t layer, uint8_t key_group = 0); - static uint32_t getLayerState(void) { - return layer_state_; + static uint32_t getLayerState(uint8_t key_group = 0) { + return layer_state_[key_group]; } static Key eventHandler(Key mappedKey, KeyAddr key_addr, uint8_t keyState); @@ -136,14 +138,18 @@ class Layer_ { static void updateActiveLayers(void); private: - static uint32_t layer_state_; - static uint8_t top_active_layer_; + static uint32_t layer_state_[kaleidoscope::max_num_key_groups]; + static uint8_t top_active_layer_[kaleidoscope::max_num_key_groups]; static Key live_composite_keymap_[kaleidoscope_internal::device.numKeys()]; static uint8_t active_layers_[kaleidoscope_internal::device.numKeys()]; static void handleKeymapKeyswitchEvent(Key keymapEntry, uint8_t keyState); - static void updateTopActiveLayer(void); + + static void updateTopActiveLayer(uint8_t key_group); }; + } +extern uint8_t groupOfKey(KeyAddr key_addr); + extern kaleidoscope::Layer_ Layer; diff --git a/src/kaleidoscope/plugin/Colormap.cpp b/src/kaleidoscope/plugin/Colormap.cpp index d24707d8f4..7c54ae6c30 100644 --- a/src/kaleidoscope/plugin/Colormap.cpp +++ b/src/kaleidoscope/plugin/Colormap.cpp @@ -25,7 +25,6 @@ namespace plugin { uint16_t ColormapEffect::map_base_; uint8_t ColormapEffect::max_layers_; -uint8_t ColormapEffect::top_layer_; void ColormapEffect::max_layers(uint8_t max_) { if (map_base_ != 0) @@ -39,14 +38,16 @@ void ColormapEffect::TransientLEDMode::onActivate(void) { if (!Kaleidoscope.has_leds) return; - parent_->top_layer_ = Layer.top(); - if (parent_->top_layer_ <= parent_->max_layers_) - ::LEDPaletteTheme.updateHandler(parent_->map_base_, parent_->top_layer_); + for (auto key_addr : KeyAddr::all()) { + refreshAt(key_addr); + } } void ColormapEffect::TransientLEDMode::refreshAt(KeyAddr key_addr) { - if (parent_->top_layer_ <= parent_->max_layers_) - ::LEDPaletteTheme.refreshAt(parent_->map_base_, parent_->top_layer_, key_addr); + uint8_t key_group = groupOfKey(key_addr); + uint8_t top_layer = ::Layer.top(key_group); + if (top_layer <= parent_->max_layers_) + ::LEDPaletteTheme.refreshAt(parent_->map_base_, top_layer, key_addr); } EventHandlerResult ColormapEffect::onLayerChange() { diff --git a/src/kaleidoscope/plugin/Colormap.h b/src/kaleidoscope/plugin/Colormap.h index 4259c31db3..9163d8597e 100644 --- a/src/kaleidoscope/plugin/Colormap.h +++ b/src/kaleidoscope/plugin/Colormap.h @@ -59,7 +59,6 @@ class ColormapEffect : public Plugin, }; private: - static uint8_t top_layer_; static uint8_t max_layers_; static uint16_t map_base_; }; diff --git a/src/kaleidoscope/plugin/EEPROM-Keymap-Programmer.cpp b/src/kaleidoscope/plugin/EEPROM-Keymap-Programmer.cpp index 32a9c2d12f..44af44deee 100644 --- a/src/kaleidoscope/plugin/EEPROM-Keymap-Programmer.cpp +++ b/src/kaleidoscope/plugin/EEPROM-Keymap-Programmer.cpp @@ -60,6 +60,7 @@ EventHandlerResult EEPROMKeymapProgrammer::onKeyswitchEvent(Key &mapped_key, Key update_position_ = Layer.top() * Kaleidoscope.device().numKeys() + key_addr.toInt(); } if (keyToggledOff(key_state)) { + // TODO: Check use of Layer.top() in presence of multiple key groups if ((uint16_t)(Layer.top() * Kaleidoscope.device().numKeys() + key_addr.toInt()) == update_position_) nextState(); } @@ -68,9 +69,11 @@ EventHandlerResult EEPROMKeymapProgrammer::onKeyswitchEvent(Key &mapped_key, Key if (state_ == WAIT_FOR_SOURCE_KEY) { if (keyToggledOn(key_state)) { + // TODO: Check use of Layer.top() in presence of multiple key groups new_key_ = Layer.getKeyFromPROGMEM(Layer.top(), key_addr); } if (keyToggledOff(key_state)) { + // TODO: Check use of Layer.top() in presence of multiple key groups if (new_key_ == Layer.getKeyFromPROGMEM(Layer.top(), key_addr)) nextState(); } diff --git a/src/kaleidoscope/plugin/LED-ActiveLayerColor.cpp b/src/kaleidoscope/plugin/LED-ActiveLayerColor.cpp index 12eb2bf869..8c2d5853ff 100644 --- a/src/kaleidoscope/plugin/LED-ActiveLayerColor.cpp +++ b/src/kaleidoscope/plugin/LED-ActiveLayerColor.cpp @@ -32,11 +32,9 @@ void LEDActiveLayerColorEffect::setColormap(const cRGB colormap[]) { colormap_ = colormap; } -cRGB LEDActiveLayerColorEffect::TransientLEDMode::getActiveColor() { +cRGB LEDActiveLayerColorEffect::TransientLEDMode::getActiveColor(uint8_t top_layer) { cRGB color; - uint8_t top_layer = ::Layer.top(); - color.r = pgm_read_byte(&(parent_->colormap_[top_layer].r)); color.g = pgm_read_byte(&(parent_->colormap_[top_layer].g)); color.b = pgm_read_byte(&(parent_->colormap_[top_layer].b)); @@ -48,12 +46,16 @@ void LEDActiveLayerColorEffect::TransientLEDMode::onActivate(void) { if (!Kaleidoscope.has_leds) return; - active_color_ = getActiveColor(); - ::LEDControl.set_all_leds_to(active_color_); + for (auto key_addr : KeyAddr::all()) { + refreshAt(key_addr); + } } void LEDActiveLayerColorEffect::TransientLEDMode::refreshAt(KeyAddr key_addr) { - ::LEDControl.setCrgbAt(key_addr, active_color_); + uint8_t key_group = groupOfKey(key_addr); + uint8_t top_layer = ::Layer.top(key_group); + cRGB active_color = getActiveColor(top_layer); + ::LEDControl.setCrgbAt(key_addr, active_color); } EventHandlerResult LEDActiveLayerColorEffect::onLayerChange() { diff --git a/src/kaleidoscope/plugin/LED-ActiveLayerColor.h b/src/kaleidoscope/plugin/LED-ActiveLayerColor.h index 0ee80de938..5cd762c7d3 100644 --- a/src/kaleidoscope/plugin/LED-ActiveLayerColor.h +++ b/src/kaleidoscope/plugin/LED-ActiveLayerColor.h @@ -55,7 +55,7 @@ class LEDActiveLayerColorEffect : public Plugin, cRGB active_color_; - cRGB getActiveColor(); + cRGB getActiveColor(uint8_t top_layer); friend class LEDActiveLayerColorEffect; }; @@ -63,6 +63,8 @@ class LEDActiveLayerColorEffect : public Plugin, private: static const cRGB *colormap_; + + static cRGB getActiveColor(uint8_t top_layer); }; } } diff --git a/src/kaleidoscope/plugin/LED-ActiveModColor.cpp b/src/kaleidoscope/plugin/LED-ActiveModColor.cpp index 96269f6fa0..a3623f7fbd 100644 --- a/src/kaleidoscope/plugin/LED-ActiveModColor.cpp +++ b/src/kaleidoscope/plugin/LED-ActiveModColor.cpp @@ -79,7 +79,7 @@ EventHandlerResult ActiveModColorEffect::beforeReportingState() { if (layer >= LAYER_SHIFT_OFFSET) layer -= LAYER_SHIFT_OFFSET; - if (Layer.isActive(layer)) + if (Layer.isActive(layer, groupOfKey(key_addr))) ::LEDControl.setCrgbAt(key_addr, highlight_color); else ::LEDControl.refreshAt(key_addr); diff --git a/src/kaleidoscope/plugin/NumPad.cpp b/src/kaleidoscope/plugin/NumPad.cpp index 4a2629fe1c..2c15e62f52 100644 --- a/src/kaleidoscope/plugin/NumPad.cpp +++ b/src/kaleidoscope/plugin/NumPad.cpp @@ -57,6 +57,7 @@ void NumPad::setKeyboardLEDColors(void) { } EventHandlerResult NumPad::afterEachCycle() { + // TODO: How could this work with more than one keygroup? if (!Layer.isActive(numPadLayer)) { if (numpadActive) { ::LEDControl.set_mode(::LEDControl.get_mode_index());