fix(keymaps): Handle matching then-layers.

* Proporly handle multiple  conditonal layers w/ the same target
  `then-layer` values.
* Move handling to work callback, to avoid re-entrance for cascading
  layers enabling other layers.
This commit is contained in:
Peter Johanson
2022-01-05 04:03:50 +00:00
committed by Pete Johanson
parent 11ac8c4782
commit 4af3d272fc
10 changed files with 297 additions and 12 deletions

View File

@@ -7,6 +7,7 @@
#define DT_DRV_COMPAT zmk_conditional_layers
#include <stdint.h>
#include <kernel.h>
#include <devicetree.h>
#include <logging/log.h>
@@ -19,6 +20,8 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static K_SEM_DEFINE(conditional_layer_sem, 1, 1);
// Conditional layer configuration that activates the specified then-layer when all if-layers are
// active. With two if-layers, this is referred to as "tri-layer", and is commonly used to activate
// a third "adjust" layer if and only if the "lower" and "raise" layers are both active.
@@ -66,22 +69,53 @@ static void conditional_layer_deactivate(int8_t layer) {
}
}
// On layer state changes, examines each conditional layer config to determine if then-layer in the
// config should activate based on the currently active set of if-layers.
static int layer_state_changed_listener(const zmk_event_t *ev) {
for (int i = 0; i < NUM_CONDITIONAL_LAYER_CFGS; i++) {
const struct conditional_layer_cfg *cfg = CONDITIONAL_LAYER_CFGS + i;
zmk_keymap_layers_state_t mask = cfg->if_layers_state_mask;
static bool conditional_layer_updates_needed;
// Activate then-layer if and only if all if-layers are already active. Note that we
// reevaluate the current layer state for each config since activation of one layer can also
// trigger activation of another.
if ((zmk_keymap_layer_state() & mask) == mask) {
conditional_layer_activate(cfg->then_layer);
} else {
conditional_layer_deactivate(cfg->then_layer);
conditional_layer_updates_needed = true;
// Semaphore ensures we don't re-enter the loop in the middle of doing update, and
// ensures that "waterfalling layer updates" are all processed to trigger subsequent
// nested conditional layers properly.
if (k_sem_take(&conditional_layer_sem, K_NO_WAIT) < 0) {
return 0;
}
while (conditional_layer_updates_needed) {
int8_t max_then_layer = -1;
uint32_t then_layers = 0;
uint32_t then_layer_state = 0;
conditional_layer_updates_needed = false;
// On layer state changes, examines each conditional layer config to determine if then-layer
// in the config should activate based on the currently active set of if-layers.
for (int i = 0; i < NUM_CONDITIONAL_LAYER_CFGS; i++) {
const struct conditional_layer_cfg *cfg = CONDITIONAL_LAYER_CFGS + i;
zmk_keymap_layers_state_t mask = cfg->if_layers_state_mask;
then_layers |= BIT(cfg->then_layer);
max_then_layer = MAX(max_then_layer, cfg->then_layer);
// Activate then-layer if and only if all if-layers are already active. Note that we
// reevaluate the current layer state for each config since activation of one layer can
// also trigger activation of another.
if ((zmk_keymap_layer_state() & mask) == mask) {
then_layer_state |= BIT(cfg->then_layer);
}
}
for (uint8_t layer = 0; layer <= max_then_layer; layer++) {
if ((BIT(layer) & then_layers) != 0U) {
if ((BIT(layer) & then_layer_state) != 0U) {
conditional_layer_activate(layer);
} else {
conditional_layer_deactivate(layer);
}
}
}
}
k_sem_give(&conditional_layer_sem);
return 0;
}