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());