From bbf7a20b33de2d203518687cb5cd1aa85005ea27 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 13 Feb 2023 03:19:02 +1100 Subject: Refactor Leader key feature (#19632) Co-authored-by: Drashna Jaelre --- quantum/keyboard.c | 7 ++ quantum/leader.c | 101 ++++++++++++++++++++++++++ quantum/leader.h | 119 +++++++++++++++++++++++++++++++ quantum/process_keycode/process_leader.c | 77 ++++++-------------- quantum/process_keycode/process_leader.h | 24 ------- quantum/quantum.h | 1 + 6 files changed, 248 insertions(+), 81 deletions(-) create mode 100644 quantum/leader.c create mode 100644 quantum/leader.h (limited to 'quantum') diff --git a/quantum/keyboard.c b/quantum/keyboard.c index 1de5f1cd0c..e8e3292dbc 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -105,6 +105,9 @@ along with this program. If not, see . #ifdef CAPS_WORD_ENABLE # include "caps_word.h" #endif +#ifdef LEADER_ENABLE +# include "leader.h" +#endif static uint32_t last_input_modification_time = 0; uint32_t last_input_activity_time(void) { @@ -546,6 +549,10 @@ void quantum_task(void) { combo_task(); #endif +#ifdef LEADER_ENABLE + leader_task(); +#endif + #ifdef WPM_ENABLE decay_wpm(); #endif diff --git a/quantum/leader.c b/quantum/leader.c new file mode 100644 index 0000000000..272609ad0c --- /dev/null +++ b/quantum/leader.c @@ -0,0 +1,101 @@ +// Copyright 2023 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "leader.h" +#include "timer.h" +#include "util.h" + +#include + +#ifndef LEADER_TIMEOUT +# define LEADER_TIMEOUT 300 +#endif + +// Leader key stuff +bool leading = false; +uint16_t leader_time = 0; +uint16_t leader_sequence[5] = {0, 0, 0, 0, 0}; +uint8_t leader_sequence_size = 0; + +__attribute__((weak)) void leader_start_user(void) {} + +__attribute__((weak)) void leader_end_user(void) {} + +void leader_start(void) { + if (leading) { + return; + } + leader_start_user(); + leading = true; + leader_time = timer_read(); + leader_sequence_size = 0; + memset(leader_sequence, 0, sizeof(leader_sequence)); +} + +void leader_end(void) { + leading = false; + leader_end_user(); +} + +void leader_task(void) { + if (leader_sequence_active() && leader_sequence_timed_out()) { + leader_end(); + } +} + +bool leader_sequence_active(void) { + return leading; +} + +bool leader_sequence_add(uint16_t keycode) { + if (leader_sequence_size >= ARRAY_SIZE(leader_sequence)) { + return false; + } + +#if defined(LEADER_NO_TIMEOUT) + if (leader_sequence_size == 0) { + leader_reset_timer(); + } +#endif + + leader_sequence[leader_sequence_size] = keycode; + leader_sequence_size++; + + return true; +} + +bool leader_sequence_timed_out(void) { +#if defined(LEADER_NO_TIMEOUT) + return leader_sequence_size > 0 && timer_elapsed(leader_time) > LEADER_TIMEOUT; +#else + return timer_elapsed(leader_time) > LEADER_TIMEOUT; +#endif +} + +void leader_reset_timer(void) { + leader_time = timer_read(); +} + +bool leader_sequence_is(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4, uint16_t kc5) { + return leader_sequence[0] == kc1 && leader_sequence[1] == kc2 && leader_sequence[2] == kc3 && leader_sequence[3] == kc4 && leader_sequence[4] == kc5; +} + +bool leader_sequence_one_key(uint16_t kc) { + return leader_sequence_is(kc, 0, 0, 0, 0); +} + +bool leader_sequence_two_keys(uint16_t kc1, uint16_t kc2) { + return leader_sequence_is(kc1, kc2, 0, 0, 0); +} + +bool leader_sequence_three_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3) { + return leader_sequence_is(kc1, kc2, kc3, 0, 0); +} + +bool leader_sequence_four_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4) { + return leader_sequence_is(kc1, kc2, kc3, kc4, 0); +} + +bool leader_sequence_five_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4, uint16_t kc5) { + return leader_sequence_is(kc1, kc2, kc3, kc4, kc5); +} diff --git a/quantum/leader.h b/quantum/leader.h new file mode 100644 index 0000000000..1999006c56 --- /dev/null +++ b/quantum/leader.h @@ -0,0 +1,119 @@ +// Copyright 2023 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +/** + * \defgroup leader + * + * Leader Key + * \{ + */ + +/** + * \brief User callback, invoked when the leader sequence begins. + */ +void leader_start_user(void); + +/** + * \brief User callback, invoked when the leader sequence ends. + */ +void leader_end_user(void); + +/** + * Begin the leader sequence, resetting the buffer and timer. + */ +void leader_start(void); + +/** + * End the leader sequence. + */ +void leader_end(void); + +void leader_task(void); + +/** + * Whether the leader sequence is active. + */ +bool leader_sequence_active(void); + +/** + * Add the given keycode to the sequence buffer. + * + * If `LEADER_NO_TIMEOUT` is defined, the timer is reset if the buffer is empty. + * + * \param keycode The keycode to add. + * + * \return `true` if the keycode was added, `false` if the buffer is full. + */ +bool leader_sequence_add(uint16_t keycode); + +/** + * Whether the leader sequence has reached the timeout. + * + * If `LEADER_NO_TIMEOUT` is defined, the buffer must also contain at least one key. + */ +bool leader_sequence_timed_out(void); + +/** + * Reset the leader sequence timer. + */ +void leader_reset_timer(void); + +/** + * Check the sequence buffer for the given keycode. + * + * \param kc The keycode to check. + * + * \return `true` if the sequence buffer matches. + */ +bool leader_sequence_one_key(uint16_t kc); + +/** + * Check the sequence buffer for the given keycodes. + * + * \param kc1 The first keycode to check. + * \param kc2 The second keycode to check. + * + * \return `true` if the sequence buffer matches. + */ +bool leader_sequence_two_keys(uint16_t kc1, uint16_t kc2); + +/** + * Check the sequence buffer for the given keycodes. + * + * \param kc1 The first keycode to check. + * \param kc2 The second keycode to check. + * \param kc3 The third keycode to check. + * + * \return `true` if the sequence buffer matches. + */ +bool leader_sequence_three_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3); + +/** + * Check the sequence buffer for the given keycodes. + * + * \param kc1 The first keycode to check. + * \param kc2 The second keycode to check. + * \param kc3 The third keycode to check. + * \param kc4 The fourth keycode to check. + * + * \return `true` if the sequence buffer matches. + */ +bool leader_sequence_four_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4); + +/** + * Check the sequence buffer for the given keycodes. + * + * \param kc1 The first keycode to check. + * \param kc2 The second keycode to check. + * \param kc3 The third keycode to check. + * \param kc4 The fourth keycode to check. + * \param kc5 The fifth keycode to check. + * + * \return `true` if the sequence buffer matches. + */ +bool leader_sequence_five_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4, uint16_t kc5); + +/** \} */ diff --git a/quantum/process_keycode/process_leader.c b/quantum/process_keycode/process_leader.c index 80bc96e65f..a9823b6285 100644 --- a/quantum/process_keycode/process_leader.c +++ b/quantum/process_keycode/process_leader.c @@ -15,71 +15,34 @@ */ #include "process_leader.h" -#include +#include "leader.h" -#ifndef LEADER_TIMEOUT -# define LEADER_TIMEOUT 300 +bool process_leader(uint16_t keycode, keyrecord_t *record) { + if (record->event.pressed) { + if (leader_sequence_active() && !leader_sequence_timed_out()) { +#ifndef LEADER_KEY_STRICT_KEY_PROCESSING + if (IS_QK_MOD_TAP(keycode)) { + keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode); + } else if (IS_QK_LAYER_TAP(keycode)) { + keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode); + } #endif -__attribute__((weak)) void leader_start_user(void) {} - -__attribute__((weak)) void leader_end_user(void) {} - -// Leader key stuff -bool leading = false; -uint16_t leader_time = 0; - -uint16_t leader_sequence[5] = {0, 0, 0, 0, 0}; -uint8_t leader_sequence_size = 0; - -void leader_start(void) { - if (leading) { - return; - } - leader_start_user(); - leading = true; - leader_time = timer_read(); - leader_sequence_size = 0; - memset(leader_sequence, 0, sizeof(leader_sequence)); -} + if (!leader_sequence_add(keycode)) { + leader_end(); -void leader_end(void) { - leader_end_user(); -} + return true; + } -bool process_leader(uint16_t keycode, keyrecord_t *record) { - // Leader key set-up - if (record->event.pressed) { - if (leading) { -#ifndef LEADER_NO_TIMEOUT - if (timer_elapsed(leader_time) < LEADER_TIMEOUT) -#endif // LEADER_NO_TIMEOUT - { -#ifndef LEADER_KEY_STRICT_KEY_PROCESSING - if (IS_QK_MOD_TAP(keycode)) { - keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode); - } else if (IS_QK_LAYER_TAP(keycode)) { - keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode); - } -#endif // LEADER_KEY_STRICT_KEY_PROCESSING - if (leader_sequence_size < ARRAY_SIZE(leader_sequence)) { - leader_sequence[leader_sequence_size] = keycode; - leader_sequence_size++; - } else { - leading = false; - leader_end_user(); - return true; - } #ifdef LEADER_PER_KEY_TIMING - leader_time = timer_read(); + leader_reset_timer(); #endif - return false; - } - } else { - if (keycode == QK_LEADER) { - leader_start(); - } + + return false; + } else if (keycode == QK_LEADER) { + leader_start(); } } + return true; } diff --git a/quantum/process_keycode/process_leader.h b/quantum/process_keycode/process_leader.h index 82b4a3ed7b..eb0f721f60 100644 --- a/quantum/process_keycode/process_leader.h +++ b/quantum/process_keycode/process_leader.h @@ -19,27 +19,3 @@ #include "quantum.h" bool process_leader(uint16_t keycode, keyrecord_t *record); - -void leader_start_user(void); -void leader_end_user(void); - -void leader_start(void); -void leader_end(void); - -#define SEQ_ONE_KEY(key) if (leader_sequence[0] == (key) && leader_sequence[1] == 0 && leader_sequence[2] == 0 && leader_sequence[3] == 0 && leader_sequence[4] == 0) -#define SEQ_TWO_KEYS(key1, key2) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == 0 && leader_sequence[3] == 0 && leader_sequence[4] == 0) -#define SEQ_THREE_KEYS(key1, key2, key3) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == (key3) && leader_sequence[3] == 0 && leader_sequence[4] == 0) -#define SEQ_FOUR_KEYS(key1, key2, key3, key4) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == (key3) && leader_sequence[3] == (key4) && leader_sequence[4] == 0) -#define SEQ_FIVE_KEYS(key1, key2, key3, key4, key5) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == (key3) && leader_sequence[3] == (key4) && leader_sequence[4] == (key5)) - -#define LEADER_EXTERNS() \ - extern bool leading; \ - extern uint16_t leader_time; \ - extern uint16_t leader_sequence[5]; \ - extern uint8_t leader_sequence_size - -#ifdef LEADER_NO_TIMEOUT -# define LEADER_DICTIONARY() if (leading && leader_sequence_size > 0 && timer_elapsed(leader_time) > LEADER_TIMEOUT) -#else -# define LEADER_DICTIONARY() if (leading && timer_elapsed(leader_time) > LEADER_TIMEOUT) -#endif diff --git a/quantum/quantum.h b/quantum/quantum.h index 708d325a32..315fa25568 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -93,6 +93,7 @@ extern layer_state_t layer_state; #endif #ifdef LEADER_ENABLE +# include "leader.h" # include "process_leader.h" #endif -- cgit v1.2.3