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

Update store_or_get_action to cache keycode on key press, return cached keycode on key release #24517

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions docs/config_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ If you define these options you will enable the associated feature, which may in
* NKRO by default requires to be turned on, this forces it on during keyboard startup regardless of EEPROM setting. NKRO can still be turned off but will be turned on again if the keyboard reboots.
* `#define STRICT_LAYER_RELEASE`
* force a key release to be evaluated using the current layer stack instead of remembering which layer it came from (used for advanced cases)
* `#define KEYCODE_CACHE_ENABLE`
* Cache keycode for pressed keys, to be used on key release, across entire physical keyboard layout.

## Behaviors That Can Be Configured

Expand Down
73 changes: 72 additions & 1 deletion quantum/action_layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,64 @@ uint8_t read_source_layers_cache(keypos_t key) {
# endif // ENCODER_MAP_ENABLE
return 0;
}

# ifdef KEYCODE_CACHE_ENABLE
uint16_t keycode_map[((MATRIX_ROWS * MATRIX_COLS) + (CHAR_BIT)-1) / (CHAR_BIT)][16] = {{KC_NO}};
# ifdef ENCODER_MAP_ENABLE
uint16_t encoder_keycode_map[(NUM_ENCODERS + (CHAR_BIT)-1) / (CHAR_BIT)][16] = {{KC_NO}};
# endif // ENCODER_MAP_ENABLE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks overcomplicated. The source layer cache code uses such tricks to avoid allocating a whole byte for each entry that needs just 2 bits in the most common case (4 layers); if you are storing the whole 16-bit keycode, you may just use

static uint16_t keycode_map[MATRIX_ROWS][MATRIX_COLS];

and

static uint16_t encoder_keycode_map[NUM_ENCODERS][NUM_DIRECTIONS];

(Actually the encoder code might not need such handling at all, because the press and release events are emitted without running the rest of main loop code in between, therefore keymap updates should not happen.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a ton for the feedback, I cleaned up the overcomplicated keycode_map and also removed the encoder handling


/** \brief update keycode map
*
* Updates map of keycodes when a key is pressed down
*/
void update_keycode_map_impl(uint16_t entry_number, uint16_t keycode, uint16_t cache[][16]) {
cache[entry_number / 16][entry_number % 16] = keycode;
}

/** \brief read keycode map
*
* reads from map of keycodes when a key is released
*/
uint16_t read_keycode_map_impl(uint16_t entry_number, uint16_t cache[][16]) {
return cache[entry_number / 16][entry_number % 16];
}

/** \brief update keycode map
*
* Updates map of keycodes when a key is pressed down
*/
void update_keycode_map(keypos_t key, uint16_t keycode) {
if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) {
const uint16_t entry_number = (uint16_t)(key.row * MATRIX_COLS) + key.col;
update_keycode_map_impl(entry_number, keycode, keycode_map);
}
# ifdef ENCODER_MAP_ENABLE
else if (key.row == KEYLOC_ENCODER_CW || key.row == KEYLOC_ENCODER_CCW) {
const uint16_t entry_number = key.col;
update_keycode_map_impl(entry_number, keycode, encoder_keycode_map);
}
# endif // ENCODER_MAP_ENABLE
}

/** \brief read keycode map
*
* reads from map of keycodes when a key is released
*/
uint16_t read_keycode_map(keypos_t key) {
if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) {
const uint16_t entry_number = (uint16_t)(key.row * MATRIX_COLS) + key.col;
return read_keycode_map_impl(entry_number, keycode_map);
}
# ifdef ENCODER_MAP_ENABLE
else if (key.row == KEYLOC_ENCODER_CW || key.row == KEYLOC_ENCODER_CCW) {
const uint16_t entry_number = key.col;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general you may need to consider key.row too (so that entries for different rotation direction don't overwrite each other); maybe it's simple to just do that instead of thinking why that should not happen.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since I removed the encoder handling code, is this safe to not consider?

return read_keycode_map_impl(entry_number, encoder_keycode_map);
}
# endif // ENCODER_MAP_ENABLE
return KC_NO;
}
# endif
#endif

/** \brief Store or get action (FIXME: Needs better summary)
Expand All @@ -303,14 +361,27 @@ action_t store_or_get_action(bool pressed, keypos_t key) {
}

uint8_t layer;

# ifdef KEYCODE_CACHE_ENABLE
uint16_t keycode;
# endif
if (pressed) {
layer = layer_switch_get_layer(key);
update_source_layers_cache(key, layer);
# ifdef KEYCODE_CACHE_ENABLE
keycode = keymap_key_to_keycode(layer, key);
update_keycode_map(key, keycode);
# endif
} else {
layer = read_source_layers_cache(key);
# ifdef KEYCODE_CACHE_ENABLE
keycode = read_keycode_map(key);
# endif
}
# ifndef KEYCODE_CACHE_ENABLE
return action_for_key(layer, key);
# else
return action_for_keycode(keycode);
# endif
#else
return layer_switch_get_action(key);
#endif
Expand Down
5 changes: 5 additions & 0 deletions quantum/action_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_

void update_source_layers_cache(keypos_t key, uint8_t layer);
uint8_t read_source_layers_cache(keypos_t key);
# ifdef KEYCODE_CACHE_ENABLE
void update_keycode_map(keypos_t key, uint16_t keycode);
uint16_t read_keycode_map(keypos_t key);
uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key);
# endif
#endif
action_t store_or_get_action(bool pressed, keypos_t key);

Expand Down