Merge pull request #182 from Vifon/modifier-release-fix

Fix the layer-dependent modifiers handling
example_keyboards
Erez Zukerman 9 years ago
commit 153a6fb0d3

@ -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. `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 ### 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). 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).

@ -53,6 +53,22 @@ void action_exec(keyevent_t event)
#endif #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)) __attribute__ ((weak))
void process_action_kb(keyrecord_t *record) {} void process_action_kb(keyrecord_t *record) {}
@ -67,7 +83,7 @@ void process_action(keyrecord_t *record)
process_action_kb(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); dprint("ACTION: "); debug_action(action);
#ifndef NO_ACTION_LAYER #ifndef NO_ACTION_LAYER
dprint(" layer_state: "); layer_debug(); dprint(" layer_state: "); layer_debug();

@ -62,6 +62,10 @@ void action_function(keyrecord_t *record, uint8_t id, uint8_t opt);
void process_action_kb(keyrecord_t *record); void process_action_kb(keyrecord_t *record);
/* Utilities for actions. */ /* 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 process_action(keyrecord_t *record);
void register_code(uint8_t code); void register_code(uint8_t code);
void unregister_code(uint8_t code); void unregister_code(uint8_t code);

@ -110,9 +110,71 @@ void layer_debug(void)
} }
#endif #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_t action;
action.code = ACTION_TRANSPARENT; action.code = ACTION_TRANSPARENT;
@ -124,15 +186,18 @@ action_t layer_switch_get_action(keypos_t key)
if (layers & (1UL<<i)) { if (layers & (1UL<<i)) {
action = action_for_key(i, key); action = action_for_key(i, key);
if (action.code != ACTION_TRANSPARENT) { if (action.code != ACTION_TRANSPARENT) {
return action; return i;
} }
} }
} }
/* fall back to layer 0 */ /* fall back to layer 0 */
action = action_for_key(0, key); return 0;
return action;
#else #else
action = action_for_key(biton32(default_layer_state), key); return biton32(default_layer_state);
return action;
#endif #endif
} }
action_t layer_switch_get_action(keypos_t key)
{
return action_for_key(layer_switch_get_layer(key), key);
}

@ -70,6 +70,17 @@ void layer_xor(uint32_t state);
#define layer_debug() #define layer_debug()
#endif #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 */ /* return action depending on current layer status */
action_t layer_switch_get_action(keypos_t key); action_t layer_switch_get_action(keypos_t key);

Loading…
Cancel
Save