Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the layer-dependent modifiers handling #182

Merged
merged 25 commits into from
Apr 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
18 changes: 17 additions & 1 deletion tmk_core/common/action.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}

Expand All @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions tmk_core/common/action.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
77 changes: 71 additions & 6 deletions tmk_core/common/action_layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
11 changes: 11 additions & 0 deletions tmk_core/common/action_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down