diff options
author | Erez Zukerman <bulk@ezuk.org> | 2016-04-06 22:47:31 +0300 |
---|---|---|
committer | Erez Zukerman <bulk@ezuk.org> | 2016-04-06 22:47:31 +0300 |
commit | 153a6fb0d3e9b54c4d6241c44ffdb9ce8a65de7f (patch) | |
tree | cd210829c02686007ff02784dad65accf0779cd9 | |
parent | 27ee47d0bd467e86e36feff8c66b3aea661f91b5 (diff) | |
parent | 567f256c5d4598adb4dcd63fa4e4a7b4df553b12 (diff) | |
download | qmk_firmware-153a6fb0d3e9b54c4d6241c44ffdb9ce8a65de7f.tar.gz qmk_firmware-153a6fb0d3e9b54c4d6241c44ffdb9ce8a65de7f.zip |
Merge pull request #182 from Vifon/modifier-release-fix
Fix the layer-dependent modifiers handling
-rw-r--r-- | README.md | 21 | ||||
-rw-r--r-- | tmk_core/common/action.c | 18 | ||||
-rw-r--r-- | tmk_core/common/action.h | 4 | ||||
-rw-r--r-- | tmk_core/common/action_layer.c | 77 | ||||
-rw-r--r-- | tmk_core/common/action_layer.h | 11 |
5 files changed, 124 insertions, 7 deletions
@@ -98,6 +98,27 @@ We've added shortcuts to make common modifier/tap (mod-tap) mappings more compac `DF(layer)` - sets default layer to *layer*. The default layer is the one at the "bottom" of the layer stack - the ultimate fallback layer. This currently does not persist over power loss. When you plug the keyboard back in, layer 0 will always be the default. It is theoretically possible to work around that, but that's not what `DF` does. +### Prevent stuck modifiers + +Consider the following scenario: + +1. Layer 0 has a key defined as Shift. +2. The same key is defined on layer 1 as the letter A. +3. User presses Shift. +4. User switches to layer 1 for whatever reason. +5. User releases Shift, or rather the letter A. +6. User switches back to layer 0. + +Shift was actually never released and is still considered pressed. + +If such situation bothers you add this to your `config.h`: + + #define PREVENT_STUCK_MODIFIERS + +This option uses 5 bytes of memory per every 8 keys on the keyboard +rounded up (5 bits per key). For example on Planck (48 keys) it uses +(48/8)\*5 = 30 bytes. + ### Remember: These are just aliases These functions work the same way that their `ACTION_*` functions do - they're just quick aliases. To dig into all of the tmk ACTION_* functions, please see the [TMK documentation](https://github.com/jackhumbert/qmk_firmware/blob/master/tmk_core/doc/keymap.md#2-action). diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index 9010896343..f9e6c17dc3 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -53,6 +53,22 @@ void action_exec(keyevent_t event) #endif } +#if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) +bool disable_action_cache = false; + +void process_action_nocache(keyrecord_t *record) +{ + disable_action_cache = true; + process_action(record); + disable_action_cache = false; +} +#else +void process_action_nocache(keyrecord_t *record) +{ + process_action(record); +} +#endif + __attribute__ ((weak)) void process_action_kb(keyrecord_t *record) {} @@ -67,7 +83,7 @@ void process_action(keyrecord_t *record) process_action_kb(record); - action_t action = layer_switch_get_action(event.key); + action_t action = store_or_get_action(event.pressed, event.key); dprint("ACTION: "); debug_action(action); #ifndef NO_ACTION_LAYER dprint(" layer_state: "); layer_debug(); diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h index 9f528af4b9..44ec3047ba 100644 --- a/tmk_core/common/action.h +++ b/tmk_core/common/action.h @@ -62,6 +62,10 @@ void action_function(keyrecord_t *record, uint8_t id, uint8_t opt); void process_action_kb(keyrecord_t *record); /* Utilities for actions. */ +#if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) +extern bool disable_action_cache; +#endif +void process_action_nocache(keyrecord_t *record); void process_action(keyrecord_t *record); void register_code(uint8_t code); void unregister_code(uint8_t code); diff --git a/tmk_core/common/action_layer.c b/tmk_core/common/action_layer.c index c535615f44..fc721a7323 100644 --- a/tmk_core/common/action_layer.c +++ b/tmk_core/common/action_layer.c @@ -110,9 +110,71 @@ void layer_debug(void) } #endif +#if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) +uint8_t source_layers_cache[MAX_LAYER_BITS][(MATRIX_ROWS * MATRIX_COLS + 7) / 8] = {0}; +void update_source_layers_cache(keypos_t key, uint8_t layer) +{ + const uint8_t key_number = key.col + (key.row * MATRIX_COLS); + const uint8_t storage_row = key_number / 8; + const uint8_t storage_bit = key_number % 8; -action_t layer_switch_get_action(keypos_t key) + for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { + source_layers_cache[bit_number][storage_row] ^= + (-((layer & (1U << bit_number)) != 0) + ^ source_layers_cache[bit_number][storage_row]) + & (1U << storage_bit); + } +} + +uint8_t read_source_layers_cache(keypos_t key) +{ + const uint8_t key_number = key.col + (key.row * MATRIX_COLS); + const uint8_t storage_row = key_number / 8; + const uint8_t storage_bit = key_number % 8; + uint8_t layer = 0; + + for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { + layer |= + ((source_layers_cache[bit_number][storage_row] + & (1U << storage_bit)) != 0) + << bit_number; + } + + return layer; +} +#endif + +/* + * Make sure the action triggered when the key is released is the same + * one as the one triggered on press. It's important for the mod keys + * when the layer is switched after the down event but before the up + * event as they may get stuck otherwise. + */ +action_t store_or_get_action(bool pressed, keypos_t key) +{ +#if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) + if (disable_action_cache) { + return layer_switch_get_action(key); + } + + uint8_t layer; + + if (pressed) { + layer = layer_switch_get_layer(key); + update_source_layers_cache(key, layer); + } + else { + layer = read_source_layers_cache(key); + } + return action_for_key(layer, key); +#else + return layer_switch_get_action(key); +#endif +} + + +int8_t layer_switch_get_layer(keypos_t key) { action_t action; action.code = ACTION_TRANSPARENT; @@ -124,15 +186,18 @@ action_t layer_switch_get_action(keypos_t key) if (layers & (1UL<<i)) { action = action_for_key(i, key); if (action.code != ACTION_TRANSPARENT) { - return action; + return i; } } } /* fall back to layer 0 */ - action = action_for_key(0, key); - return action; + return 0; #else - action = action_for_key(biton32(default_layer_state), key); - return action; + return biton32(default_layer_state); #endif } + +action_t layer_switch_get_action(keypos_t key) +{ + return action_for_key(layer_switch_get_layer(key), key); +} diff --git a/tmk_core/common/action_layer.h b/tmk_core/common/action_layer.h index b6da353cfd..3a4b1e3349 100644 --- a/tmk_core/common/action_layer.h +++ b/tmk_core/common/action_layer.h @@ -70,6 +70,17 @@ void layer_xor(uint32_t state); #define layer_debug() #endif +/* pressed actions cache */ +#if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) +/* The number of bits needed to represent the layer number: log2(32). */ +#define MAX_LAYER_BITS 5 +void update_source_layers_cache(keypos_t key, uint8_t layer); +uint8_t read_source_layers_cache(keypos_t key); +#endif +action_t store_or_get_action(bool pressed, keypos_t key); + +/* return the topmost non-transparent layer currently associated with key */ +int8_t layer_switch_get_layer(keypos_t key); /* return action depending on current layer status */ action_t layer_switch_get_action(keypos_t key); |