From 0bf417bff40f6aa0ae4ac4c13e61b0803dcd4281 Mon Sep 17 00:00:00 2001 From: leep-frog <66687468+leep-frog@users.noreply.github.com> Date: Fri, 24 Mar 2023 14:42:17 +0000 Subject: [PATCH 1/9] tap dance on_each_release_fn --- docs/feature_tap_dance.md | 2 +- quantum/process_keycode/process_tap_dance.c | 7 +++++++ quantum/process_keycode/process_tap_dance.h | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/feature_tap_dance.md b/docs/feature_tap_dance.md index b7d8a5528fb8..2b8390b563d5 100644 --- a/docs/feature_tap_dance.md +++ b/docs/feature_tap_dance.md @@ -28,7 +28,7 @@ After this, you'll want to use the `tap_dance_actions` array to specify what act * `ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: Sends the `kc` keycode when tapped once, or toggles the state of `layer`. (this functions like the `TG` layer keycode). * `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the final tap count of the tap dance action. * `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: Calls the first specified function - defined in the user keymap - on every tap, the second function when the dance action finishes (like the previous option), and the last function when the tap dance action resets. - +* When defining the `TAP_DANCE_ON_EACH_RELEASE` variable in your `config.h` file, the signature of the function in the previous bullet is updated to `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, *on_each_release_fn*, on_dance_finished_fn, on_dance_reset_fn)`. The original three function arguments are unchanged, but the `on_each_release_fn` is called on every key release. The first option is enough for a lot of cases, that just want dual roles. For example, `ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT)` will result in `Space` being sent on single-tap, `Enter` otherwise. diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index 1b2c6c8a448f..8b9472cea20b 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -95,6 +95,10 @@ static inline void process_tap_dance_action_on_each_tap(tap_dance_action_t *acti _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_tap); } +#ifdef TAP_DANCE_ON_EACH_RELEASE +static inline void process_tap_dance_action_on_each_release(tap_dance_action_t *action) { _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_release); } +#endif + static inline void process_tap_dance_action_on_reset(tap_dance_action_t *action) { _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_reset); del_weak_mods(action->state.weak_mods); @@ -158,6 +162,9 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { process_tap_dance_action_on_each_tap(action); active_td = action->state.finished ? 0 : keycode; } else { +#ifdef TAP_DANCE_ON_EACH_RELEASE + process_tap_dance_action_on_each_release(action); +#endif if (action->state.finished) { process_tap_dance_action_on_reset(action); } diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h index 386f50461926..53670050cad0 100644 --- a/quantum/process_keycode/process_tap_dance.h +++ b/quantum/process_keycode/process_tap_dance.h @@ -37,7 +37,9 @@ typedef void (*tap_dance_user_fn_t)(tap_dance_state_t *state, void *user_data); typedef struct { tap_dance_state_t state; struct { - tap_dance_user_fn_t on_each_tap; +# ifdef TAP_DANCE_ON_EACH_RELEASE + tap_dance_user_fn_t on_each_release; +# endif tap_dance_user_fn_t on_dance_finished; tap_dance_user_fn_t on_reset; } fn; From 6378c85c51ff6798578456b173c3ec69593f3a83 Mon Sep 17 00:00:00 2001 From: Groog Date: Sat, 8 Apr 2023 10:21:43 -0400 Subject: [PATCH 2/9] Re-add on_each_tap field --- quantum/process_keycode/process_tap_dance.h | 1 + 1 file changed, 1 insertion(+) diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h index 53670050cad0..103b70919a5c 100644 --- a/quantum/process_keycode/process_tap_dance.h +++ b/quantum/process_keycode/process_tap_dance.h @@ -37,6 +37,7 @@ typedef void (*tap_dance_user_fn_t)(tap_dance_state_t *state, void *user_data); typedef struct { tap_dance_state_t state; struct { + tap_dance_user_fn_t on_each_tap; # ifdef TAP_DANCE_ON_EACH_RELEASE tap_dance_user_fn_t on_each_release; # endif From 78cb4cdf6c1ca5b21ffa88b3bf2e8b8d5ac9ad6a Mon Sep 17 00:00:00 2001 From: Groog Date: Sat, 8 Apr 2023 10:34:50 -0400 Subject: [PATCH 3/9] add function indent to fix linting issue --- quantum/process_keycode/process_tap_dance.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index 8b9472cea20b..1f3d61093d7a 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -96,7 +96,9 @@ static inline void process_tap_dance_action_on_each_tap(tap_dance_action_t *acti } #ifdef TAP_DANCE_ON_EACH_RELEASE -static inline void process_tap_dance_action_on_each_release(tap_dance_action_t *action) { _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_release); } +static inline void process_tap_dance_action_on_each_release(tap_dance_action_t *action) { + _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_release); +} #endif static inline void process_tap_dance_action_on_reset(tap_dance_action_t *action) { From 3ac61071828d7c5d4c6394ebafc90fe361a32282 Mon Sep 17 00:00:00 2001 From: Greg Leeper Date: Thu, 6 Jul 2023 22:14:33 -0400 Subject: [PATCH 4/9] Switch on-each-release function to simply be a separate macro --- docs/feature_tap_dance.md | 2 +- quantum/process_keycode/process_tap_dance.c | 4 ---- quantum/process_keycode/process_tap_dance.h | 17 +++++++++-------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/feature_tap_dance.md b/docs/feature_tap_dance.md index 2b8390b563d5..26b80e4f2e87 100644 --- a/docs/feature_tap_dance.md +++ b/docs/feature_tap_dance.md @@ -28,7 +28,7 @@ After this, you'll want to use the `tap_dance_actions` array to specify what act * `ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: Sends the `kc` keycode when tapped once, or toggles the state of `layer`. (this functions like the `TG` layer keycode). * `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the final tap count of the tap dance action. * `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: Calls the first specified function - defined in the user keymap - on every tap, the second function when the dance action finishes (like the previous option), and the last function when the tap dance action resets. -* When defining the `TAP_DANCE_ON_EACH_RELEASE` variable in your `config.h` file, the signature of the function in the previous bullet is updated to `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, *on_each_release_fn*, on_dance_finished_fn, on_dance_reset_fn)`. The original three function arguments are unchanged, but the `on_each_release_fn` is called on every key release. +* `ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(on_each_tap_fn, on_each_release_fn, on_dance_finished_fn, on_dance_reset_fn)`: This macro is identical to `ACTION_TAP_DANCE_FN_ADVANCED` with the addition of `on_each_release_fn` which is invoked on every key release (only for the key in the dance, not for other keys that interrupt the dance). The first option is enough for a lot of cases, that just want dual roles. For example, `ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT)` will result in `Space` being sent on single-tap, `Enter` otherwise. diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index 1f3d61093d7a..4efe4b0aae2d 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -95,11 +95,9 @@ static inline void process_tap_dance_action_on_each_tap(tap_dance_action_t *acti _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_tap); } -#ifdef TAP_DANCE_ON_EACH_RELEASE static inline void process_tap_dance_action_on_each_release(tap_dance_action_t *action) { _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_release); } -#endif static inline void process_tap_dance_action_on_reset(tap_dance_action_t *action) { _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_reset); @@ -164,9 +162,7 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { process_tap_dance_action_on_each_tap(action); active_td = action->state.finished ? 0 : keycode; } else { -#ifdef TAP_DANCE_ON_EACH_RELEASE process_tap_dance_action_on_each_release(action); -#endif if (action->state.finished) { process_tap_dance_action_on_reset(action); } diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h index 103b70919a5c..2b114dabd36d 100644 --- a/quantum/process_keycode/process_tap_dance.h +++ b/quantum/process_keycode/process_tap_dance.h @@ -38,11 +38,9 @@ typedef struct { tap_dance_state_t state; struct { tap_dance_user_fn_t on_each_tap; -# ifdef TAP_DANCE_ON_EACH_RELEASE - tap_dance_user_fn_t on_each_release; -# endif tap_dance_user_fn_t on_dance_finished; tap_dance_user_fn_t on_reset; + tap_dance_user_fn_t on_each_release; } fn; void *user_data; } tap_dance_action_t; @@ -59,19 +57,22 @@ typedef struct { } tap_dance_dual_role_t; #define ACTION_TAP_DANCE_DOUBLE(kc1, kc2) \ - { .fn = {tap_dance_pair_on_each_tap, tap_dance_pair_finished, tap_dance_pair_reset}, .user_data = (void *)&((tap_dance_pair_t){kc1, kc2}), } + { .fn = {tap_dance_pair_on_each_tap, tap_dance_pair_finished, tap_dance_pair_reset, NULL}, .user_data = (void *)&((tap_dance_pair_t){kc1, kc2}), } #define ACTION_TAP_DANCE_LAYER_MOVE(kc, layer) \ - { .fn = {tap_dance_dual_role_on_each_tap, tap_dance_dual_role_finished, tap_dance_dual_role_reset}, .user_data = (void *)&((tap_dance_dual_role_t){kc, layer, layer_move}), } + { .fn = {tap_dance_dual_role_on_each_tap, tap_dance_dual_role_finished, tap_dance_dual_role_reset, NULL}, .user_data = (void *)&((tap_dance_dual_role_t){kc, layer, layer_move}), } #define ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer) \ - { .fn = {NULL, tap_dance_dual_role_finished, tap_dance_dual_role_reset}, .user_data = (void *)&((tap_dance_dual_role_t){kc, layer, layer_invert}), } + { .fn = {NULL, tap_dance_dual_role_finished, tap_dance_dual_role_reset, NULL}, .user_data = (void *)&((tap_dance_dual_role_t){kc, layer, layer_invert}), } #define ACTION_TAP_DANCE_FN(user_fn) \ - { .fn = {NULL, user_fn, NULL}, .user_data = NULL, } + { .fn = {NULL, user_fn, NULL, NULL}, .user_data = NULL, } #define ACTION_TAP_DANCE_FN_ADVANCED(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset) \ - { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset}, .user_data = NULL, } + { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, NULL}, .user_data = NULL, } + +#define ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(user_fn_on_each_tap, user_fn_on_each_release, user_fn_on_dance_finished, user_fn_on_dance_reset) \ + { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, user_fn_on_each_release}, .user_data = NULL, } #define TD(n) (QK_TAP_DANCE | TD_INDEX(n)) #define TD_INDEX(code) ((code)&0xFF) From dc0da959aa4f4adcaf2fa5f64be427840fe91a84 Mon Sep 17 00:00:00 2001 From: Greg Leeper Date: Wed, 12 Jul 2023 10:35:47 -0400 Subject: [PATCH 5/9] Add tap dance AdvancedWithRelease tests --- tests/tap_dance/examples.c | 14 ++++++- tests/tap_dance/examples.h | 1 + tests/tap_dance/test_examples.cpp | 67 +++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/tests/tap_dance/examples.c b/tests/tap_dance/examples.c index af7438820954..00f0b76c5e88 100644 --- a/tests/tap_dance/examples.c +++ b/tests/tap_dance/examples.c @@ -187,13 +187,25 @@ void x_reset(tap_dance_state_t *state, void *user_data) { xtap_state.state = TD_NONE; } +static void release_press(tap_dance_state_t *state, void *user_data) { + tap_code16(KC_P); +} + +static void release_unpress(tap_dance_state_t *state, void *user_data) { + tap_code16(KC_U); +} + +static void release_finished(tap_dance_state_t *state, void *user_data) { + tap_code16(KC_F); +} tap_dance_action_t tap_dance_actions[] = { [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), [CT_EGG] = ACTION_TAP_DANCE_FN(dance_egg), [CT_FLSH] = ACTION_TAP_DANCE_FN_ADVANCED(dance_flsh_each, dance_flsh_finished, dance_flsh_reset), [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN), - [X_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset) + [X_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset), + [TD_RELEASE] = ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(release_press, release_unpress, release_finished, NULL), }; // clang-format on diff --git a/tests/tap_dance/examples.h b/tests/tap_dance/examples.h index 2622af6b2f4a..868a0c721bbe 100644 --- a/tests/tap_dance/examples.h +++ b/tests/tap_dance/examples.h @@ -26,6 +26,7 @@ enum { CT_FLSH, CT_CLN, X_CTL, + TD_RELEASE, }; #ifdef __cplusplus diff --git a/tests/tap_dance/test_examples.cpp b/tests/tap_dance/test_examples.cpp index 6dabc45513af..6ae9a2bfdc50 100644 --- a/tests/tap_dance/test_examples.cpp +++ b/tests/tap_dance/test_examples.cpp @@ -316,3 +316,70 @@ TEST_F(TapDance, QuadFunction) { EXPECT_EMPTY_REPORT(driver); run_one_scan_loop(); } + + +TEST_F(TapDance, DanceFnAdvancedWithRelease) { + TestDriver driver; + InSequence s; + auto key_rls = KeymapKey(0, 1, 0, TD(TD_RELEASE)); + + set_keymap({key_rls}); + + /* Single press and unpress */ + key_rls.press(); + EXPECT_REPORT(driver, (KC_P)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + key_rls.release(); + EXPECT_REPORT(driver, (KC_U)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + EXPECT_REPORT(driver, (KC_F)); + EXPECT_EMPTY_REPORT(driver); + idle_for(TAPPING_TERM); + run_one_scan_loop(); + + /* Double press and unpress */ + key_rls.press(); + EXPECT_REPORT(driver, (KC_P)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + key_rls.release(); + EXPECT_REPORT(driver, (KC_U)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + key_rls.press(); + EXPECT_REPORT(driver, (KC_P)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + key_rls.release(); + EXPECT_REPORT(driver, (KC_U)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + EXPECT_REPORT(driver, (KC_F)); + EXPECT_EMPTY_REPORT(driver); + idle_for(TAPPING_TERM); + run_one_scan_loop(); + + /* Unpress after tapping term has elapsed (key is registered as held) */ + key_rls.press(); + EXPECT_REPORT(driver, (KC_P)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + EXPECT_REPORT(driver, (KC_F)); + EXPECT_EMPTY_REPORT(driver); + idle_for(TAPPING_TERM); + run_one_scan_loop(); + + key_rls.release(); + EXPECT_REPORT(driver, (KC_U)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} From c21343091aba0f56e930a92006d71548f8c25dbb Mon Sep 17 00:00:00 2001 From: Greg Leeper Date: Wed, 12 Jul 2023 10:40:02 -0400 Subject: [PATCH 6/9] Update tap dance doc on_each_release_fn description --- docs/feature_tap_dance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/feature_tap_dance.md b/docs/feature_tap_dance.md index 26b80e4f2e87..f407b6d2ddb1 100644 --- a/docs/feature_tap_dance.md +++ b/docs/feature_tap_dance.md @@ -28,7 +28,7 @@ After this, you'll want to use the `tap_dance_actions` array to specify what act * `ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: Sends the `kc` keycode when tapped once, or toggles the state of `layer`. (this functions like the `TG` layer keycode). * `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the final tap count of the tap dance action. * `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: Calls the first specified function - defined in the user keymap - on every tap, the second function when the dance action finishes (like the previous option), and the last function when the tap dance action resets. -* `ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(on_each_tap_fn, on_each_release_fn, on_dance_finished_fn, on_dance_reset_fn)`: This macro is identical to `ACTION_TAP_DANCE_FN_ADVANCED` with the addition of `on_each_release_fn` which is invoked on every key release (only for the key in the dance, not for other keys that interrupt the dance). +* `ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(on_each_tap_fn, on_each_release_fn, on_dance_finished_fn, on_dance_reset_fn)`: This macro is identical to `ACTION_TAP_DANCE_FN_ADVANCED` with the addition of `on_each_release_fn` which is invoked every time the key for the tap dance is released. It is worth noting that `on_each_release_fn` will still be called even when the key is released after the dance finishes or resets (e.g. if the key is released after being pressed and held for longer than the `TAPPING_TERM`). The first option is enough for a lot of cases, that just want dual roles. For example, `ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT)` will result in `Space` being sent on single-tap, `Enter` otherwise. From 29b3589d1fdd95c65169b4a69a44133880a20564 Mon Sep 17 00:00:00 2001 From: Greg Leeper Date: Wed, 12 Jul 2023 16:18:15 -0400 Subject: [PATCH 7/9] remove extra newline --- tests/tap_dance/test_examples.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/tap_dance/test_examples.cpp b/tests/tap_dance/test_examples.cpp index 6ae9a2bfdc50..6786df88be72 100644 --- a/tests/tap_dance/test_examples.cpp +++ b/tests/tap_dance/test_examples.cpp @@ -317,7 +317,6 @@ TEST_F(TapDance, QuadFunction) { run_one_scan_loop(); } - TEST_F(TapDance, DanceFnAdvancedWithRelease) { TestDriver driver; InSequence s; From 1313404907fa9fd9a42483689c35846db5a1fc19 Mon Sep 17 00:00:00 2001 From: leep-frog <66687468+leep-frog@users.noreply.github.com> Date: Thu, 13 Jul 2023 11:06:29 -0400 Subject: [PATCH 8/9] Update docs/feature_tap_dance.md Co-authored-by: Sergey Vlasov --- docs/feature_tap_dance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/feature_tap_dance.md b/docs/feature_tap_dance.md index f407b6d2ddb1..42ea23396235 100644 --- a/docs/feature_tap_dance.md +++ b/docs/feature_tap_dance.md @@ -28,7 +28,7 @@ After this, you'll want to use the `tap_dance_actions` array to specify what act * `ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: Sends the `kc` keycode when tapped once, or toggles the state of `layer`. (this functions like the `TG` layer keycode). * `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the final tap count of the tap dance action. * `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: Calls the first specified function - defined in the user keymap - on every tap, the second function when the dance action finishes (like the previous option), and the last function when the tap dance action resets. -* `ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(on_each_tap_fn, on_each_release_fn, on_dance_finished_fn, on_dance_reset_fn)`: This macro is identical to `ACTION_TAP_DANCE_FN_ADVANCED` with the addition of `on_each_release_fn` which is invoked every time the key for the tap dance is released. It is worth noting that `on_each_release_fn` will still be called even when the key is released after the dance finishes or resets (e.g. if the key is released after being pressed and held for longer than the `TAPPING_TERM`). +* `ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(on_each_tap_fn, on_each_release_fn, on_dance_finished_fn, on_dance_reset_fn)`: This macro is identical to `ACTION_TAP_DANCE_FN_ADVANCED` with the addition of `on_each_release_fn` which is invoked every time the key for the tap dance is released. It is worth noting that `on_each_release_fn` will still be called even when the key is released after the dance finishes (e.g. if the key is released after being pressed and held for longer than the `TAPPING_TERM`). The first option is enough for a lot of cases, that just want dual roles. For example, `ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT)` will result in `Space` being sent on single-tap, `Enter` otherwise. From 29ac6aece0948c2d2934ed869f575181bf84e611 Mon Sep 17 00:00:00 2001 From: Greg Leeper Date: Mon, 17 Jul 2023 10:45:37 -0400 Subject: [PATCH 9/9] ensure reset fn is only called once in on_each_release tap dance function --- quantum/process_keycode/process_tap_dance.c | 3 ++ tests/tap_dance/examples.c | 12 ++++- tests/tap_dance/examples.h | 1 + tests/tap_dance/test_examples.cpp | 50 +++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index 4efe4b0aae2d..b8a8d32f350c 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -165,6 +165,9 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { process_tap_dance_action_on_each_release(action); if (action->state.finished) { process_tap_dance_action_on_reset(action); + if (active_td == keycode) { + active_td = 0; + } } } diff --git a/tests/tap_dance/examples.c b/tests/tap_dance/examples.c index 00f0b76c5e88..13086bbb4bba 100644 --- a/tests/tap_dance/examples.c +++ b/tests/tap_dance/examples.c @@ -195,17 +195,27 @@ static void release_unpress(tap_dance_state_t *state, void *user_data) { tap_code16(KC_U); } +static void release_unpress_mark_finished(tap_dance_state_t *state, void *user_data) { + tap_code16(KC_U); + state->finished = true; +} + static void release_finished(tap_dance_state_t *state, void *user_data) { tap_code16(KC_F); } +static void release_reset(tap_dance_state_t *state, void *user_data) { + tap_code16(KC_R); +} + tap_dance_action_t tap_dance_actions[] = { [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), [CT_EGG] = ACTION_TAP_DANCE_FN(dance_egg), [CT_FLSH] = ACTION_TAP_DANCE_FN_ADVANCED(dance_flsh_each, dance_flsh_finished, dance_flsh_reset), [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN), [X_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset), - [TD_RELEASE] = ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(release_press, release_unpress, release_finished, NULL), + [TD_RELEASE] = ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(release_press, release_unpress, release_finished, release_reset), + [TD_RELEASE_AND_FINISH] = ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(release_press, release_unpress_mark_finished, release_finished, release_reset), }; // clang-format on diff --git a/tests/tap_dance/examples.h b/tests/tap_dance/examples.h index 868a0c721bbe..6118188dd185 100644 --- a/tests/tap_dance/examples.h +++ b/tests/tap_dance/examples.h @@ -27,6 +27,7 @@ enum { CT_CLN, X_CTL, TD_RELEASE, + TD_RELEASE_AND_FINISH, }; #ifdef __cplusplus diff --git a/tests/tap_dance/test_examples.cpp b/tests/tap_dance/test_examples.cpp index 6786df88be72..7858dab92b9d 100644 --- a/tests/tap_dance/test_examples.cpp +++ b/tests/tap_dance/test_examples.cpp @@ -337,6 +337,8 @@ TEST_F(TapDance, DanceFnAdvancedWithRelease) { EXPECT_REPORT(driver, (KC_F)); EXPECT_EMPTY_REPORT(driver); + EXPECT_REPORT(driver, (KC_R)); + EXPECT_EMPTY_REPORT(driver); idle_for(TAPPING_TERM); run_one_scan_loop(); @@ -363,6 +365,52 @@ TEST_F(TapDance, DanceFnAdvancedWithRelease) { EXPECT_REPORT(driver, (KC_F)); EXPECT_EMPTY_REPORT(driver); + EXPECT_REPORT(driver, (KC_R)); + EXPECT_EMPTY_REPORT(driver); + idle_for(TAPPING_TERM); + run_one_scan_loop(); + + /* Unpress after tapping term has elapsed (key is registered as held) */ + key_rls.press(); + EXPECT_REPORT(driver, (KC_P)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + EXPECT_REPORT(driver, (KC_F)); + EXPECT_EMPTY_REPORT(driver); + idle_for(TAPPING_TERM); + run_one_scan_loop(); + + key_rls.release(); + EXPECT_REPORT(driver, (KC_U)); + EXPECT_EMPTY_REPORT(driver); + EXPECT_REPORT(driver, (KC_R)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, DanceFnAdvancedWithReleaseAndFinish) { + TestDriver driver; + InSequence s; + auto key_rls = KeymapKey(0, 1, 0, TD(TD_RELEASE_AND_FINISH)); + + set_keymap({key_rls}); + + /* Single press and unpress */ + key_rls.press(); + EXPECT_REPORT(driver, (KC_P)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + key_rls.release(); + EXPECT_REPORT(driver, (KC_U)); + EXPECT_EMPTY_REPORT(driver); + EXPECT_REPORT(driver, (KC_R)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + // Verify the finished and/or reset functions aren't called + // after the tapping term elapses idle_for(TAPPING_TERM); run_one_scan_loop(); @@ -380,5 +428,7 @@ TEST_F(TapDance, DanceFnAdvancedWithRelease) { key_rls.release(); EXPECT_REPORT(driver, (KC_U)); EXPECT_EMPTY_REPORT(driver); + EXPECT_REPORT(driver, (KC_R)); + EXPECT_EMPTY_REPORT(driver); run_one_scan_loop(); }