diff --git a/examples/Features/KeyGroups/ArrayBasedMembership/ArrayBasedMembership.ino b/examples/Features/KeyGroups/ArrayBasedMembership/ArrayBasedMembership.ino new file mode 100644 index 0000000000..7682e462ca --- /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(uint8_t row, uint8_t col) { + return GROUP_OF_KEY(row, col); +} + +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..1a5df7f8ec --- /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(uint8_t row, uint8_t col) { + return 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 35a303a4b7..f7bef6bd9d 100644 --- a/src/kaleidoscope/Hardware.h +++ b/src/kaleidoscope/Hardware.h @@ -50,11 +50,18 @@ typedef struct cRGB cRGB; namespace kaleidoscope { + /** Kaleidoscope Hardware base class. * Essential methods all hardware libraries must implement. */ class Hardware { public: + + // This class method can be overridden by hardware implementations. + constexpr static bool isOnLeftHalf(uint8_t /*row*/, uint8_t /*col*/) { + return true; + } + /** * @defgroup kaleidoscope_hardware_leds Kaleidoscope::Hardware/LEDs * @{ diff --git a/src/kaleidoscope/hardware/ez/ErgoDox.h b/src/kaleidoscope/hardware/ez/ErgoDox.h index 8724956067..ca17047e35 100644 --- a/src/kaleidoscope/hardware/ez/ErgoDox.h +++ b/src/kaleidoscope/hardware/ez/ErgoDox.h @@ -56,6 +56,10 @@ class ErgoDox : public kaleidoscope::Hardware { static constexpr byte matrix_rows = 14; static constexpr int8_t led_count = 0; + static constexpr bool isOnLeftHalf(uint8_t row, uint8_t /*col*/) { + return row < 7; + } + void scanMatrix(void); void readMatrix(void); void actOnMatrixScan(void); diff --git a/src/kaleidoscope/hardware/kbdfans/KBD4x.h b/src/kaleidoscope/hardware/kbdfans/KBD4x.h index 11951015d4..de914c42ab 100644 --- a/src/kaleidoscope/hardware/kbdfans/KBD4x.h +++ b/src/kaleidoscope/hardware/kbdfans/KBD4x.h @@ -67,6 +67,9 @@ class KBD4x: public kaleidoscope::hardware::ATMegaKeyboard { static constexpr int8_t led_count = 0; + // TODO: Implement + // static constexpr bool isOnLeftHalf(uint8_t row, uint8_t col); + void resetDevice(); }; diff --git a/src/kaleidoscope/hardware/keyboardio/Model01.cpp b/src/kaleidoscope/hardware/keyboardio/Model01.cpp index 0db6545843..7487eac396 100644 --- a/src/kaleidoscope/hardware/keyboardio/Model01.cpp +++ b/src/kaleidoscope/hardware/keyboardio/Model01.cpp @@ -244,7 +244,6 @@ void Model01::rebootBootloader() { // halves, with eight keys per logical row. constexpr byte HIGH_BIT = B10000000; -constexpr byte HAND_BIT = B00001000; constexpr byte ROW_BITS = B00110000; constexpr byte COL_BITS = B00000111; diff --git a/src/kaleidoscope/hardware/keyboardio/Model01.h b/src/kaleidoscope/hardware/keyboardio/Model01.h index 5457f87bd1..1494532f99 100644 --- a/src/kaleidoscope/hardware/keyboardio/Model01.h +++ b/src/kaleidoscope/hardware/keyboardio/Model01.h @@ -37,6 +37,14 @@ namespace keyboardio { class Model01 : public kaleidoscope::Hardware { public: + + static constexpr byte HAND_BIT = B00001000; + + static constexpr bool isOnLeftHalf(uint8_t /*row*/, uint8_t col) { + // If HAND_BIT is set, we are on the right hand side + return !(col & HAND_BIT); + } + Model01(void); static constexpr byte matrix_rows = 4; diff --git a/src/kaleidoscope/hardware/olkb/Planck.h b/src/kaleidoscope/hardware/olkb/Planck.h index e42eb15cd5..c331df9100 100644 --- a/src/kaleidoscope/hardware/olkb/Planck.h +++ b/src/kaleidoscope/hardware/olkb/Planck.h @@ -40,6 +40,9 @@ class Planck: public kaleidoscope::hardware::ATMegaKeyboard { COL_PIN_LIST({ PIN_F1, PIN_F0, PIN_B0, PIN_C7, PIN_F4, PIN_F5, PIN_F6, PIN_F7, PIN_D4, PIN_D6, PIN_B4, PIN_D7 }) ); + // TODO: Implement + // static constexpr bool isOnLeftHalf(uint8_t row, uint8_t col); + static constexpr int8_t led_count = 0; }; diff --git a/src/kaleidoscope/hardware/softhruf/Splitography.h b/src/kaleidoscope/hardware/softhruf/Splitography.h index 01d42b779e..98a11cbb9e 100644 --- a/src/kaleidoscope/hardware/softhruf/Splitography.h +++ b/src/kaleidoscope/hardware/softhruf/Splitography.h @@ -68,6 +68,9 @@ class Splitography: public kaleidoscope::hardware::ATMegaKeyboard { COL_PIN_LIST({ PIN_F0, PIN_F1, PIN_F4, PIN_F5, PIN_F6, PIN_F7, PIN_C7, PIN_C6, PIN_B6, PIN_B5, PIN_B4, PIN_D7 }) ); + // TODO: Implement + // static constexpr bool isOnLeftHalf(uint8_t row, uint8_t col); + static constexpr int8_t led_count = 0; }; diff --git a/src/kaleidoscope/hardware/technomancy/Atreus.h b/src/kaleidoscope/hardware/technomancy/Atreus.h index 00ce781b98..ac1c474d1e 100644 --- a/src/kaleidoscope/hardware/technomancy/Atreus.h +++ b/src/kaleidoscope/hardware/technomancy/Atreus.h @@ -67,6 +67,9 @@ class Atreus: public kaleidoscope::hardware::ATMegaKeyboard { void resetDevice(); + // TODO: Implement + // static constexpr bool isOnLeftHalf(uint8_t row, uint8_t col); + protected: }; diff --git a/src/kaleidoscope/key_groups.h b/src/kaleidoscope/key_groups.h new file mode 100644 index 0000000000..3bd490d137 --- /dev/null +++ b/src/kaleidoscope/key_groups.h @@ -0,0 +1,94 @@ +/* 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 . + */ + +#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(B01000000) | SWITCH_TO_KEYMAP(B00000100) + // + // 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.keyCode, k.flags | 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[ROWS][COLS] PROGMEM = KEYMAP_STACKED(group_ids_per_key); + +#define KEY_GROUP_IDS(group_ids_per_key...) \ + const uint8_t key_groups[ROWS][COLS] PROGMEM = KEYMAP(group_ids_per_key); + +#define GROUP_OF_KEY(row, col) \ + pgm_read_byte(&(key_groups[row][col])) diff --git a/src/kaleidoscope/layers.cpp b/src/kaleidoscope/layers.cpp index e581703ec9..00f01210b3 100644 --- a/src/kaleidoscope/layers.cpp +++ b/src/kaleidoscope/layers.cpp @@ -27,29 +27,37 @@ uint8_t layer_count __attribute__((weak)) = MAX_LAYERS; 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_[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) { + + uint8_t key_group_flags = decodeKeyGroupFlags(keymapEntry.flags); + + if (key_group_flags == 0) { + key_group_flags = ALL_KEY_GROUPS; + } + if (keymapEntry.keyCode >= LAYER_SHIFT_OFFSET) { + uint8_t target = keymapEntry.keyCode - 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: @@ -68,24 +76,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.keyCode) && keymapEntry.keyCode) - deactivate(keymapEntry.keyCode); + if (Layer.isActive(keymapEntry.keyCode, key_group_flags) && keymapEntry.keyCode) + deactivate(keymapEntry.keyCode, key_group_flags); else - activate(keymapEntry.keyCode); + activate(keymapEntry.keyCode, key_group_flags); } } Key Layer_::eventHandler(Key mappedKey, byte row, byte col, uint8_t keyState) { - if (mappedKey.flags != (SYNTHETIC | SWITCH_TO_KEYMAP)) + if (!((mappedKey.flags & SYNTHETIC) && (mappedKey.flags & SWITCH_TO_KEYMAP))) return mappedKey; handleKeymapKeyswitchEvent(mappedKey, keyState); @@ -109,10 +117,13 @@ 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_; + + uint8_t key_group = groupOfKey(row, col); + + 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, row, col); if (mappedKey != Key_Transparent) { @@ -126,43 +137,67 @@ 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 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; - // If the target layer was already on, return - if (isActive(layer)) - return; + 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_) - updateTopActiveLayer(); + // 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 (!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) @@ -172,18 +207,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) { + + 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 the target layer was 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) @@ -192,18 +242,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(void) { - activate(top_active_layer_ + 1); +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_::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(uint8_t row, uint8_t col) { + return HARDWARE_IMPLEMENTATION::isOnLeftHalf(row, col) ? 0 : 1; } kaleidoscope::Layer_ Layer; diff --git a/src/kaleidoscope/layers.h b/src/kaleidoscope/layers.h index 587a5e5aa4..72ab68c69c 100644 --- a/src/kaleidoscope/layers.h +++ b/src/kaleidoscope/layers.h @@ -18,6 +18,7 @@ #include #include "kaleidoscope/key_defs.h" +#include "kaleidoscope/key_groups.h" #include KALEIDOSCOPE_HARDWARE_H extern const Key keymaps[][ROWS][COLS]; @@ -33,6 +34,7 @@ extern const Key keymaps[][ROWS][COLS]; extern uint8_t layer_count; namespace kaleidoscope { + class Layer_ { public: Layer_() {} @@ -77,19 +79,19 @@ class Layer_ { return active_layers_[row][col]; } - 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, byte row, byte col, uint8_t keyState); @@ -102,14 +104,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_[ROWS][COLS]; static uint8_t active_layers_[ROWS][COLS]; static void handleKeymapKeyswitchEvent(Key keymapEntry, uint8_t keyState); - static void updateTopActiveLayer(void); + + static void updateTopActiveLayer(uint8_t key_group); }; + } +extern uint8_t groupOfKey(uint8_t row, uint8_t col); + extern kaleidoscope::Layer_ Layer; diff --git a/src/kaleidoscope/plugin/Colormap.cpp b/src/kaleidoscope/plugin/Colormap.cpp index ac7b3144b1..bb18670729 100644 --- a/src/kaleidoscope/plugin/Colormap.cpp +++ b/src/kaleidoscope/plugin/Colormap.cpp @@ -28,7 +28,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) @@ -42,14 +41,18 @@ void ColormapEffect::onActivate(void) { if (!Kaleidoscope.has_leds) return; - top_layer_ = Layer.top(); - if (top_layer_ <= max_layers_) - ::LEDPaletteTheme.updateHandler(map_base_, top_layer_); + for (uint8_t row = 0; row < ROWS; ++row) { + for (uint8_t col = 0; col < COLS; ++col) { + refreshAt(row, col); + } + } } void ColormapEffect::refreshAt(byte row, byte col) { - if (top_layer_ <= max_layers_) - ::LEDPaletteTheme.refreshAt(map_base_, top_layer_, row, col); + uint8_t key_group = groupOfKey(row, col); + uint8_t top_layer = ::Layer.top(key_group); + if (top_layer <= max_layers_) + ::LEDPaletteTheme.refreshAt(map_base_, top_layer, row, col); } EventHandlerResult ColormapEffect::onLayerChange() { diff --git a/src/kaleidoscope/plugin/Colormap.h b/src/kaleidoscope/plugin/Colormap.h index 7837a8c354..6219b3bbc1 100644 --- a/src/kaleidoscope/plugin/Colormap.h +++ b/src/kaleidoscope/plugin/Colormap.h @@ -36,7 +36,6 @@ class ColormapEffect : public LEDMode { void refreshAt(byte row, byte col) final; 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 956c6bb866..1c61f60a12 100644 --- a/src/kaleidoscope/plugin/EEPROM-Keymap-Programmer.cpp +++ b/src/kaleidoscope/plugin/EEPROM-Keymap-Programmer.cpp @@ -56,9 +56,11 @@ EventHandlerResult EEPROMKeymapProgrammer::onKeyswitchEvent(Key &mapped_key, byt if (state_ == WAIT_FOR_KEY) { if (keyToggledOn(key_state)) { + // TODO: Check use of Layer.top() in presence of multiple key groups update_position_ = Layer.top() * ROWS * COLS + row * COLS + col; } if (keyToggledOff(key_state)) { + // TODO: Check use of Layer.top() in presence of multiple key groups if ((uint16_t)(Layer.top() * ROWS * COLS + row * COLS + col) == update_position_) nextState(); } @@ -67,9 +69,11 @@ EventHandlerResult EEPROMKeymapProgrammer::onKeyswitchEvent(Key &mapped_key, byt 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(), row, col); } if (keyToggledOff(key_state)) { + // TODO: Check use of Layer.top() in presence of multiple key groups if (new_key_ == Layer.getKeyFromPROGMEM(Layer.top(), row, col)) nextState(); } diff --git a/src/kaleidoscope/plugin/LED-ActiveLayerColor.cpp b/src/kaleidoscope/plugin/LED-ActiveLayerColor.cpp index ae7fd6185c..8136839d16 100644 --- a/src/kaleidoscope/plugin/LED-ActiveLayerColor.cpp +++ b/src/kaleidoscope/plugin/LED-ActiveLayerColor.cpp @@ -20,18 +20,16 @@ namespace kaleidoscope { namespace plugin { -cRGB LEDActiveLayerColorEffect::active_color_; +// cRGB LEDActiveLayerColorEffect::active_color_; const cRGB *LEDActiveLayerColorEffect::colormap_; void LEDActiveLayerColorEffect::setColormap(const cRGB colormap[]) { colormap_ = colormap; } -cRGB LEDActiveLayerColorEffect::getActiveColor() { +cRGB LEDActiveLayerColorEffect::getActiveColor(uint8_t top_layer) { cRGB color; - uint8_t top_layer = ::Layer.top(); - color.r = pgm_read_byte(&(colormap_[top_layer].r)); color.g = pgm_read_byte(&(colormap_[top_layer].g)); color.b = pgm_read_byte(&(colormap_[top_layer].b)); @@ -42,13 +40,22 @@ cRGB LEDActiveLayerColorEffect::getActiveColor() { void LEDActiveLayerColorEffect::onActivate(void) { if (!Kaleidoscope.has_leds) return; + /* + active_color_ = getActiveColor(); + ::LEDControl.set_all_leds_to(active_color_);*/ - active_color_ = getActiveColor(); - ::LEDControl.set_all_leds_to(active_color_); + for (uint8_t row = 0; row < ROWS; ++row) { + for (uint8_t col = 0; col < COLS; ++col) { + refreshAt(row, col); + } + } } void LEDActiveLayerColorEffect::refreshAt(byte row, byte col) { - ::LEDControl.setCrgbAt(row, col, active_color_); + uint8_t key_group = groupOfKey(row, col); + uint8_t top_layer = ::Layer.top(key_group); + cRGB active_color = getActiveColor(top_layer); + ::LEDControl.setCrgbAt(row, col, active_color); } EventHandlerResult LEDActiveLayerColorEffect::onLayerChange() { diff --git a/src/kaleidoscope/plugin/LED-ActiveLayerColor.h b/src/kaleidoscope/plugin/LED-ActiveLayerColor.h index 611b6b19f3..38f2738630 100644 --- a/src/kaleidoscope/plugin/LED-ActiveLayerColor.h +++ b/src/kaleidoscope/plugin/LED-ActiveLayerColor.h @@ -34,9 +34,9 @@ class LEDActiveLayerColorEffect : public LEDMode { private: static const cRGB *colormap_; - static cRGB active_color_; +// static cRGB active_color_; - static cRGB getActiveColor(); + static cRGB getActiveColor(uint8_t top_layer); }; } } diff --git a/src/kaleidoscope/plugin/LED-ActiveModColor.cpp b/src/kaleidoscope/plugin/LED-ActiveModColor.cpp index 8b49f20fa7..8b18b082ff 100644 --- a/src/kaleidoscope/plugin/LED-ActiveModColor.cpp +++ b/src/kaleidoscope/plugin/LED-ActiveModColor.cpp @@ -80,7 +80,7 @@ EventHandlerResult ActiveModColorEffect::beforeReportingState() { if (layer >= LAYER_SHIFT_OFFSET) layer -= LAYER_SHIFT_OFFSET; - if (Layer.isActive(layer)) + if (Layer.isActive(layer, groupOfKey(r, c))) ::LEDControl.setCrgbAt(r, c, highlight_color); else ::LEDControl.refreshAt(r, c); diff --git a/src/kaleidoscope/plugin/NumPad.cpp b/src/kaleidoscope/plugin/NumPad.cpp index 64257af70f..d3a6fd6432 100644 --- a/src/kaleidoscope/plugin/NumPad.cpp +++ b/src/kaleidoscope/plugin/NumPad.cpp @@ -86,6 +86,7 @@ void NumPad::setKeyboardLEDColors(void) { } EventHandlerResult NumPad::afterEachCycle() { + // TODO: How could this work with more than one keygroup? if (!Layer.isActive(numPadLayer)) { cleanupNumlockState(); } else {