From 1706da9054f8c4aa77493062a8937a7b64970a9e Mon Sep 17 00:00:00 2001 From: Jouke Witteveen Date: Mon, 13 Jun 2022 22:12:55 +0200 Subject: [PATCH] tap-dance: Restructure code and document in more detail (#16394) --- builddefs/build_test.mk | 2 +- docs/feature_tap_dance.md | 143 +++++--- docs/ja/feature_tap_dance.md | 1 - .../slice/rev1/keymaps/2moons/keymap.c | 4 +- .../rev1_rgb/keymaps/2moons_rgb/keymap.c | 4 +- .../frosty_flake/keymaps/nikchi/config.h | 1 + .../frosty_flake/keymaps/nikchi/keymap.c | 32 +- .../keymaps/nikchi/variableTapDance.md | 5 +- .../duck/lightsaver/keymaps/rasmus/keymap.c | 4 +- .../ergodox_ez/keymaps/hacker_dvorak/config.h | 1 + .../tap_dance/tap_dance_actions.c | 38 ++- .../gergoplex/keymaps/georgepetri/config.h | 2 + .../gergoplex/keymaps/georgepetri/keymap.c | 11 +- .../handwired/ortho5x14/keymaps/2u/keymap.c | 6 +- .../ortho5x14/keymaps/split1/keymap.c | 6 +- .../jones/v03/keymaps/default_jp/config.h | 1 + .../jones/v03/keymaps/default_jp/keymap.c | 15 +- .../jones/v03_1/keymaps/default_ansi/config.h | 1 + .../jones/v03_1/keymaps/default_ansi/keymap.c | 15 +- .../jones/v03_1/keymaps/default_jp/config.h | 1 + .../jones/v03_1/keymaps/default_jp/keymap.c | 15 +- .../keyhive/navi10/keymaps/default/keymap.c | 2 +- .../keyhive/navi10/keymaps/devdev/keymap.c | 2 +- .../keyhive/navi10/keymaps/emdarcher/keymap.c | 2 +- .../jj50/keymaps/archetype/config.h | 1 + .../jj50/keymaps/archetype/keymap.c | 11 +- keyboards/planck/keymaps/ariccb/config.h | 1 + keyboards/planck/keymaps/ariccb/keymap.c | 13 +- keyboards/planck/keymaps/rootiest/config.h | 1 + keyboards/planck/keymaps/rootiest/keymap.c | 23 +- .../7skb/keymaps/salicylic/keymap.c | 4 +- .../7splus/keymaps/salicylic/keymap.c | 4 +- .../jisplit89/keymaps/salicylic/keymap.c | 4 +- .../naked60/keymaps/salicylic/keymap.c | 4 +- .../keymaps/salicylic_with_nafuda/keymap.c | 4 +- .../keymaps/salicylic_with_setta21/keymap.c | 4 +- .../naked64/keymaps/salicylic/keymap.c | 4 +- .../keymaps/salicylic_with_setta21/keymap.c | 4 +- .../minivan/keymaps/belak/keymap.c | 21 +- quantum/process_keycode/process_tap_dance.c | 131 +++---- quantum/process_keycode/process_tap_dance.h | 35 +- tests/tapdance/config.h | 19 ++ tests/tapdance/examples.c | 199 +++++++++++ tests/tapdance/examples.h | 33 ++ tests/tapdance/test.mk | 22 ++ tests/tapdance/test_examples.cpp | 319 ++++++++++++++++++ users/edvorakjp/edvorakjp_tap_dance.c | 13 +- users/gourdo1/gourdo1.c | 2 +- users/mnil/config.h | 1 + users/mnil/mnil.c | 4 +- users/ninjonas/config.h | 3 +- users/ninjonas/tap_dances.c | 16 +- 52 files changed, 970 insertions(+), 244 deletions(-) create mode 100644 tests/tapdance/config.h create mode 100644 tests/tapdance/examples.c create mode 100644 tests/tapdance/examples.h create mode 100644 tests/tapdance/test.mk create mode 100644 tests/tapdance/test_examples.cpp diff --git a/builddefs/build_test.mk b/builddefs/build_test.mk index 5ad33b19c5..834184f221 100644 --- a/builddefs/build_test.mk +++ b/builddefs/build_test.mk @@ -42,7 +42,7 @@ VPATH += \ all: elf -VPATH += $(COMMON_VPATH) +VPATH += $(TEST_PATH) $(COMMON_VPATH) PLATFORM:=TEST PLATFORM_KEY:=test BOOTLOADER_TYPE:=none diff --git a/docs/feature_tap_dance.md b/docs/feature_tap_dance.md index c055a9989a..05134ec229 100644 --- a/docs/feature_tap_dance.md +++ b/docs/feature_tap_dance.md @@ -14,55 +14,48 @@ Optionally, you might want to set a custom `TAPPING_TERM` time by adding somethi ```c #define TAPPING_TERM 175 +#define TAPPING_TERM_PER_KEY ``` -The `TAPPING_TERM` time is the maximum time allowed between taps of your Tap Dance key, and is measured in milliseconds. For example, if you used the above `#define` statement and set up a Tap Dance key that sends `Space` on single-tap and `Enter` on double-tap, then this key will send `ENT` only if you tap this key twice in less than 175ms. If you tap the key, wait more than 175ms, and tap the key again you'll end up sending `SPC SPC` instead. +The `TAPPING_TERM` time is the maximum time allowed between taps of your Tap Dance key, and is measured in milliseconds. For example, if you used the above `#define` statement and set up a Tap Dance key that sends `Space` on single-tap and `Enter` on double-tap, then this key will send `ENT` only if you tap this key twice in less than 175ms. If you tap the key, wait more than 175ms, and tap the key again you'll end up sending `SPC SPC` instead. The `TAPPING_TERM_PER_KEY` definition is only needed if you control the tapping term through a [custom `get_tapping_term` function](tap_hold.md#tapping_term), which may be needed because `TAPPING_TERM` affects not just tap-dance keys. -Next, you will want to define some tap-dance keys, which is easiest to do with the `TD()` macro, that takes a number which will later be used as an index into the `tap_dance_actions` array. +Next, you will want to define some tap-dance keys, which is easiest to do with the `TD()` macro. That macro takes a number which will later be used as an index into the `tap_dance_actions` array and turns it into a tap-dance keycode. After this, you'll want to use the `tap_dance_actions` array to specify what actions shall be taken when a tap-dance key is in action. Currently, there are five possible options: * `ACTION_TAP_DANCE_DOUBLE(kc1, kc2)`: Sends the `kc1` keycode when tapped once, `kc2` otherwise. When the key is held, the appropriate keycode is registered: `kc1` when pressed and held, `kc2` when tapped once, then pressed and held. * `ACTION_TAP_DANCE_LAYER_MOVE(kc, layer)`: Sends the `kc` keycode when tapped once, or moves to `layer`. (this functions like the `TO` layer keycode). - * This is the same as `ACTION_TAP_DANCE_DUAL_ROLE`, but renamed to something that is clearer about its functionality. Both names will work. * `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_TIME(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn, tap_specific_tapping_term)`~~: This functions identically to the `ACTION_TAP_DANCE_FN_ADVANCED` function, but uses a custom tapping term for it, instead of the predefined `TAPPING_TERM`. - * This is deprecated in favor of the Per Key Tapping Term functionality, as outlined [here](tap_hold.md#tapping-term). You'd want to check for the specific `TD()` macro that you want to use (such as `TD(TD_ESC_CAPS)`) instead of using this specific Tap Dance function. 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. !> Keep in mind that only [basic keycodes](keycodes_basic.md) are supported here. Custom keycodes are not supported. -Similar to the first option, the second option is good for simple layer-switching cases. +Similar to the first option, the second and third option are good for simple layer-switching cases. -For more complicated cases, use the third or fourth options (examples of each are listed below). - -Finally, the fifth option is particularly useful if your non-Tap-Dance keys start behaving weirdly after adding the code for your Tap Dance keys. The likely problem is that you changed the `TAPPING_TERM` time to make your Tap Dance keys easier for you to use, and that this has changed the way your other keys handle interrupts. +For more complicated cases, like blink the LEDs, fiddle with the backlighting, and so on, use the fourth or fifth option. Examples of each are listed below. ## Implementation Details :id=implementation Well, that's the bulk of it! You should now be able to work through the examples below, and to develop your own Tap Dance functionality. But if you want a deeper understanding of what's going on behind the scenes, then read on for the explanation of how it all works! -The main entry point is `process_tap_dance()`, called from `process_record_quantum()`, which is run for every keypress, and our handler gets to run early. This function checks whether the key pressed is a tap-dance key. If it is not, and a tap-dance was in action, we handle that first, and enqueue the newly pressed key. If it is a tap-dance key, then we check if it is the same as the already active one (if there's one active, that is). If it is not, we fire off the old one first, then register the new one. If it was the same, we increment the counter and reset the timer. +Let's go over the three functions mentioned in `ACTION_TAP_DANCE_FN_ADVANCED` in a little more detail. They all receive the same too arguments: a pointer to a structure that holds all dance related state information, and a pointer to a use case specific state variable. The three functions differ in when they are called. The first, `on_each_tap_fn()`, is called every time the tap dance key is *pressed*. Before it is called, the counter is incremented and the timer is reset. The second function, `on_dance_finished_fn()`, is called when the tap dance is interrupted or ends because `TAPPING_TERM` milliseconds have passed since the last tap. When the `finished` field of the dance state structure is set to `true`, the `on_dance_finished_fn()` is skipped. After `on_dance_finished_fn()` was called or would have been called, but no sooner than when the tap dance key is *released*, `on_dance_reset_fn()` is called. It is possible to end a tap dance immediately, skipping `on_dance_finished_fn()`, but not `on_dance_reset_fn`, by calling `reset_tap_dance(state)`. + +To accomplish this logic, the tap dance mechanics use three entry points. The main entry point is `process_tap_dance()`, called from `process_record_quantum()` *after* `process_record_kb()` and `process_record_user()`. This function is responsible for calling `on_each_tap_fn()` and `on_dance_reset_fn()`. In order to handle interruptions of a tap dance, another entry point, `preprocess_tap_dance()` is run right at the beginning of `process_record_quantum()`. This function checks whether the key pressed is a tap-dance key. If it is not, and a tap-dance was in action, we handle that first, and enqueue the newly pressed key. If it is a tap-dance key, then we check if it is the same as the already active one (if there's one active, that is). If it is not, we fire off the old one first, then register the new one. Finally, `tap_dance_task()` periodically checks whether `TAPPING_TERM` has passed since the last key press and finishes a tap dance if that is the case. This means that you have `TAPPING_TERM` time to tap the key again; you do not have to input all the taps within a single `TAPPING_TERM` timeframe. This allows for longer tap counts, with minimal impact on responsiveness. -Our next stop is `tap_dance_task()`. This handles the timeout of tap-dance keys. - -For the sake of flexibility, tap-dance actions can be either a pair of keycodes, or a user function. The latter allows one to handle higher tap counts, or do extra things, like blink the LEDs, fiddle with the backlighting, and so on. This is accomplished by using an union, and some clever macros. - ## Examples :id=examples -### Simple Example :id=simple-example +### Simple Example: Send `ESC` on Single Tap, `CAPS_LOCK` on Double Tap :id=simple-example Here's a simple example for a single definition: 1. In your `rules.mk`, add `TAP_DANCE_ENABLE = yes` -2. In your `config.h` (which you can copy from `qmk_firmware/keyboards/planck/config.h` to your keymap directory), add `#define TAPPING_TERM 200` -3. In your `keymap.c` file, define the variables and definitions, then add to your keymap: +2. In your `keymap.c` file, define the variables and definitions, then add to your keymap: ```c // Tap Dance declarations @@ -92,40 +85,15 @@ All the enums used in the examples are declared like this: ```c // Enums defined for all examples: enum { - CT_SE, - CT_CLN, + TD_ESC_CAPS, CT_EGG, CT_FLSH, - X_TAP_DANCE + CT_CLN, + X_CTL, }; ``` -#### Example 1: Send `:` on Single Tap, `;` on Double Tap :id=example-1 - -```c -void dance_cln_finished(qk_tap_dance_state_t *state, void *user_data) { - if (state->count == 1) { - register_code16(KC_COLN); - } else { - register_code(KC_SCLN); - } -} - -void dance_cln_reset(qk_tap_dance_state_t *state, void *user_data) { - if (state->count == 1) { - unregister_code16(KC_COLN); - } else { - unregister_code(KC_SCLN); - } -} - -// All tap dance functions would go here. Only showing this one. -qk_tap_dance_action_t tap_dance_actions[] = { - [CT_CLN] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, dance_cln_finished, dance_cln_reset), -}; -``` - -#### Example 2: Send "Safety Dance!" After 100 Taps :id=example-2 +#### Example 1: Send "Safety Dance!" After 100 Taps :id=example-1 ```c void dance_egg(qk_tap_dance_state_t *state, void *user_data) { @@ -140,7 +108,7 @@ qk_tap_dance_action_t tap_dance_actions[] = { }; ``` -#### Example 3: Turn LED Lights On Then Off, One at a Time :id=example-3 +#### Example 2: Turn LED Lights On Then Off, One at a Time :id=example-2 ```c // On each tap, light up one LED, from right to left @@ -181,15 +149,74 @@ void dance_flsh_reset(qk_tap_dance_state_t *state, void *user_data) { ergodox_right_led_3_off(); } -// All tap dances now put together. Example 3 is "CT_FLASH" +// All tap dances now put together. Example 2 is "CT_FLSH" qk_tap_dance_action_t tap_dance_actions[] = { - [CT_SE] = ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT), - [CT_CLN] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, dance_cln_finished, dance_cln_reset), + [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) }; ``` +#### Example 3: Send `:` on Tap, `;` on Hold :id=example-3 + +With a little effort, powerful tap-hold configurations can be implemented as tap dances. To emit taps as early as possible, we need to act on releases of the tap dance key. There is no callback for this in the tap dance framework, so we use `process_record_user()`. + +```c +typedef struct { + uint16_t tap; + uint16_t hold; + uint16_t held; +} tap_dance_tap_hold_t; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + qk_tap_dance_action_t *action; + + switch (keycode) { + case TD(CT_CLN): // list all tap dance keycodes with tap-hold configurations + action = &tap_dance_actions[TD_INDEX(keycode)]; + if (!record->event.pressed && action->state.count && !action->state.finished) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; + tap_code16(tap_hold->tap); + } + } + return true; +} + +void tap_dance_tap_hold_finished(qk_tap_dance_state_t *state, void *user_data) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + + if (state->pressed) { + if (state->count == 1 +#ifndef PERMISSIVE_HOLD + && !state->interrupted +#endif + ) { + register_code16(tap_hold->hold); + tap_hold->held = tap_hold->hold; + } else { + register_code16(tap_hold->tap); + tap_hold->held = tap_hold->tap; + } + } +} + +void tap_dance_tap_hold_reset(qk_tap_dance_state_t *state, void *user_data) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + + if (tap_hold->held) { + unregister_code16(tap_hold->held); + tap_hold->held = 0; + } +} + +#define ACTION_TAP_DANCE_TAP_HOLD(tap, hold) \ + { .fn = {NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, .user_data = (void *)&((tap_dance_tap_hold_t){tap, hold, 0}), } + +qk_tap_dance_action_t tap_dance_actions[] = { + [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN), +}; +``` + #### Example 4: 'Quad Function Tap-Dance' :id=example-4 By [DanielGGordon](https://github.com/danielggordon) @@ -329,7 +356,7 @@ And then simply use `TD(X_CTL)` anywhere in your keymap. If you want to implement this in your userspace, then you may want to check out how [DanielGGordon](https://github.com/qmk/qmk_firmware/tree/master/users/gordon) has implemented this in their userspace. -> In this configuration "hold" takes place **after** tap dance timeout (see `ACTION_TAP_DANCE_FN_ADVANCED_TIME`). To achieve instant hold, remove `state->interrupted` checks in conditions. As a result you may use comfortable longer tapping periods to have more time for taps and not to wait too long for holds (try starting with doubled `TAPPING_TERM`). +> In this configuration "hold" takes place **after** tap dance timeout. To achieve instant hold, remove `state->interrupted` checks in conditions. As a result you may use comfortable longer tapping periods to have more time for taps and not to wait too long for holds (try starting with doubled `TAPPING_TERM`). #### Example 5: Using tap dance for advanced mod-tap and layer-tap keys :id=example-5 @@ -511,8 +538,18 @@ void ql_reset(qk_tap_dance_state_t *state, void *user_data) { // Associate our tap dance key with its functionality qk_tap_dance_action_t tap_dance_actions[] = { - [QUOT_LAYR] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, ql_finished, ql_reset, 275) + [QUOT_LAYR] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset) }; + +// Set a long-ish tapping term for tap-dance keys +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: + return 275; + default: + return TAPPING_TERM; + } +} ``` The above code is similar to that used in previous examples. The one point to note is that we need to be able to check which layers are active at any time so we can toggle them if needed. To do this we use the `layer_state_is(layer)` function which returns `true` if the given `layer` is active. @@ -521,6 +558,6 @@ The use of `cur_dance()` and `ql_tap_state` mirrors the above examples. The `case: TD_SINGLE_TAP` in `ql_finished` is similar to the above examples. The `TD_SINGLE_HOLD` case works in conjunction with `ql_reset()` to switch to `_MY_LAYER` while the tap dance key is held, and to switch away from `_MY_LAYER` when the key is released. This mirrors the use of `MO(_MY_LAYER)`. The `TD_DOUBLE_TAP` case works by checking whether `_MY_LAYER` is the active layer, and toggling it on or off accordingly. This mirrors the use of `TG(_MY_LAYER)`. -`tap_dance_actions[]` works similar to the above examples. Note that I used `ACTION_TAP_DANCE_FN_ADVANCED_TIME()` instead of `ACTION_TAP_DANCE_FN_ADVANCED()`. This is because I like my `TAPPING_TERM` to be short (\~175ms) for my non-tap-dance keys but find that this is too quick for me to reliably complete tap dance actions - thus the increased time of 275ms here. +`tap_dance_actions[]` works similar to the above examples. Note that, additionally, I set a longer tapping term for the tap dance keys. This is because I like my `TAPPING_TERM` to be short (\~175ms) for my non-tap-dance keys but find that this is too quick for me to reliably complete tap dance actions - thus the increased time of 275ms here. In order for the per-key tapping terms to take effect, `TAPPING_TERM_PER_KEY` must be defined in your `config.h`. Finally, to get this tap dance key working, be sure to include `TD(QUOT_LAYR)` in your `keymaps[]`. diff --git a/docs/ja/feature_tap_dance.md b/docs/ja/feature_tap_dance.md index a6d108f1e9..762816f21b 100644 --- a/docs/ja/feature_tap_dance.md +++ b/docs/ja/feature_tap_dance.md @@ -28,7 +28,6 @@ * `ACTION_TAP_DANCE_DOUBLE(kc1, kc2)`: 1回タップすると `kc1` キーコードを送信し、2回タップすると `kc2` キーコードを送信します。キーを押し続けているときは、適切なキーコードが登録されます: キーを押し続けた場合は `kc1`、一度タップしてから続けてもう一度キーを押してそのまま押し続けたときは、 `kc2` が登録されます。 * `ACTION_TAP_DANCE_LAYER_MOVE(kc, layer)`: 1回タップすると `kc` キーコードが送信され、2回タップすると `layer` レイヤーに移動します(これは `TO` レイヤーキーコードのように機能します)。 - * この機能は `ACTION_TAP_DANCE_DUAL_ROLE` と同じですが、機能が明確になるように関数名を変更しました。どちらの関数名でも実行できます。 * `ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: 1回タップすると `kc` キーコードが送信され、2回タップすると `layer` の状態をトグルします(これは `TG` レイヤーキーコードのように機能します)。 * `ACTION_TAP_DANCE_FN(fn)`: ユーザーキーマップに定義した指定の関数が呼び出されます。タップダンス実行の回数分タップすると、最後の時点で呼び出されます。 * `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: タップする度にユーザーキーマップに定義した最初の関数が呼び出されます。タップダンスの実行が終わった時点で2番目の関数が呼び出され、タップダンスの実行をリセットするときに最後の関数が呼び出されます。 diff --git a/keyboards/basekeys/slice/rev1/keymaps/2moons/keymap.c b/keyboards/basekeys/slice/rev1/keymaps/2moons/keymap.c index 26ca151089..1292f2d2ed 100644 --- a/keyboards/basekeys/slice/rev1/keymaps/2moons/keymap.c +++ b/keyboards/basekeys/slice/rev1/keymaps/2moons/keymap.c @@ -46,8 +46,8 @@ enum tapdances{ }; qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESFL] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _FLOCK), - [TD_ESQW] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _QWERTY), + [TD_ESFL] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _FLOCK), + [TD_ESQW] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/basekeys/slice/rev1_rgb/keymaps/2moons_rgb/keymap.c b/keyboards/basekeys/slice/rev1_rgb/keymaps/2moons_rgb/keymap.c index 05de0e42d0..7e82c7b7d2 100644 --- a/keyboards/basekeys/slice/rev1_rgb/keymaps/2moons_rgb/keymap.c +++ b/keyboards/basekeys/slice/rev1_rgb/keymaps/2moons_rgb/keymap.c @@ -39,8 +39,8 @@ enum tapdances{ }; qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESFL] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _FLOCK), - [TD_ESQW] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _QWERTY), + [TD_ESFL] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _FLOCK), + [TD_ESQW] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/bpiphany/frosty_flake/keymaps/nikchi/config.h b/keyboards/bpiphany/frosty_flake/keymaps/nikchi/config.h index 4bc6d2c3c0..e45034f9a8 100644 --- a/keyboards/bpiphany/frosty_flake/keymaps/nikchi/config.h +++ b/keyboards/bpiphany/frosty_flake/keymaps/nikchi/config.h @@ -2,6 +2,7 @@ // place overrides here #define TAPPING_TERM 200 +#define TAPPING_TERM_PER_KEY #define LEADER_TIMEOUT 800 #define DISABLE_SPACE_CADET_ROLLOVER diff --git a/keyboards/bpiphany/frosty_flake/keymaps/nikchi/keymap.c b/keyboards/bpiphany/frosty_flake/keymaps/nikchi/keymap.c index dd2098d945..6db177c183 100644 --- a/keyboards/bpiphany/frosty_flake/keymaps/nikchi/keymap.c +++ b/keyboards/bpiphany/frosty_flake/keymaps/nikchi/keymap.c @@ -52,17 +52,33 @@ qk_tap_dance_action_t tap_dance_actions[] = { // Tap once for CTRL, twice for Caps Lock [TD_CTCPS] = ACTION_TAP_DANCE_DOUBLE(KC_LCTL, KC_CAPS), [COPA] = ACTION_TAP_DANCE_DOUBLE(LCTL(KC_C), LCTL(KC_V)), - [EMOJIS] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(cycleEmojis, NULL, NULL, 800), - [ANIMAL] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(cycleAnimals, NULL, NULL, 800), - //[SYMBOLS] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(cycleSymbols, NULL, NULL, 800), - [FOODS] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(cycleFoods, NULL, NULL, 800), - [ETC] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(cycleEtc, NULL, NULL, 800), - //[VEHICLES] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(cycleVehicles, NULL, NULL, 800), - //[SUPPLEMENT] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(cycleSupplement, NULL, NULL, 800), - [ALLS] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(cycleAll, NULL, NULL, 800) + [EMOJIS] = ACTION_TAP_DANCE_FN_ADVANCED(cycleEmojis, NULL, NULL), + [ANIMAL] = ACTION_TAP_DANCE_FN_ADVANCED(cycleAnimals, NULL, NULL), + //[SYMBOLS] = ACTION_TAP_DANCE_FN_ADVANCED(cycleSymbols, NULL, NULL), + [FOODS] = ACTION_TAP_DANCE_FN_ADVANCED(cycleFoods, NULL, NULL), + [ETC] = ACTION_TAP_DANCE_FN_ADVANCED(cycleEtc, NULL, NULL), + //[VEHICLES] = ACTION_TAP_DANCE_FN_ADVANCED(cycleVehicles, NULL, NULL), + //[SUPPLEMENT] = ACTION_TAP_DANCE_FN_ADVANCED(cycleSupplement, NULL, NULL), + [ALLS] = ACTION_TAP_DANCE_FN_ADVANCED(cycleAll, NULL, NULL) // Other declarations would go here, separated by commas, if you have them }; +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case TD(EMOJIS): + case TD(ANIMAL): + //case TD(SYMBOLS): + case TD(FOODS): + case TD(ETC): + //case TD(VEHICLES): + //case TD(SUPPLEMENT): + case TD(ALLS): + return 800; + default: + return TAPPING_TERM; + } +} + // macros const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { switch(id) { diff --git a/keyboards/bpiphany/frosty_flake/keymaps/nikchi/variableTapDance.md b/keyboards/bpiphany/frosty_flake/keymaps/nikchi/variableTapDance.md index b2e5041393..c3fce50f2a 100644 --- a/keyboards/bpiphany/frosty_flake/keymaps/nikchi/variableTapDance.md +++ b/keyboards/bpiphany/frosty_flake/keymaps/nikchi/variableTapDance.md @@ -3,7 +3,4 @@ Tap Dance is constrained normally by `TAPPING_TERM` defined in your keyboard's c -- `ACTION_TAP_DANCE_FN_ADVANCED_TIME(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, tap_specific_tapping_term)` : This works the same as `ACTION_TAP_DANCE_FN_ADVANCED` just with the extra `tap_specific_tapping_term` arguement at the end. This way you can set a specific tap dance to have a longer or shorter tap in between your taps, giving you more, or less, time in between each tap. - - -`tap_specific_tapping_term` should be the same type and range of values that one would put into the `TAPPING_TERM` definition in the config.h file. +- Implementing `uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record)`, you can set a specific tap dance to have a longer or shorter tap in between your taps, giving you more, or less, time in between each tap. The return value should be the same type and range of values that one would put into the `TAPPING_TERM` definition in the config.h file. diff --git a/keyboards/duck/lightsaver/keymaps/rasmus/keymap.c b/keyboards/duck/lightsaver/keymaps/rasmus/keymap.c index 70dc17bb13..65f128a26b 100644 --- a/keyboards/duck/lightsaver/keymaps/rasmus/keymap.c +++ b/keyboards/duck/lightsaver/keymaps/rasmus/keymap.c @@ -34,8 +34,8 @@ enum { //Tap Dance Definitions qk_tap_dance_action_t tap_dance_actions[] = { - [TD_F1_GAME] = ACTION_TAP_DANCE_DUAL_ROLE(KC_F1, GAME), - [TD_CAPS_FN] = ACTION_TAP_DANCE_DUAL_ROLE(KC_CAPS, 5) + [TD_F1_GAME] = ACTION_TAP_DANCE_LAYER_MOVE(KC_F1, GAME), + [TD_CAPS_FN] = ACTION_TAP_DANCE_LAYER_MOVE(KC_CAPS, 5) }; enum macro_id { diff --git a/keyboards/ergodox_ez/keymaps/hacker_dvorak/config.h b/keyboards/ergodox_ez/keymaps/hacker_dvorak/config.h index da20820787..c35963d842 100644 --- a/keyboards/ergodox_ez/keymaps/hacker_dvorak/config.h +++ b/keyboards/ergodox_ez/keymaps/hacker_dvorak/config.h @@ -7,6 +7,7 @@ #undef TAPPING_TERM #define TAPPING_TERM 175 +#define TAPPING_TERM_PER_KEY #undef DEBOUNCE #define DEBOUNCE 15 diff --git a/keyboards/ergodox_ez/keymaps/hacker_dvorak/tap_dance/tap_dance_actions.c b/keyboards/ergodox_ez/keymaps/hacker_dvorak/tap_dance/tap_dance_actions.c index 59e3e2b0dc..1d5e1cee00 100644 --- a/keyboards/ergodox_ez/keymaps/hacker_dvorak/tap_dance/tap_dance_actions.c +++ b/keyboards/ergodox_ez/keymaps/hacker_dvorak/tap_dance/tap_dance_actions.c @@ -13,14 +13,32 @@ qk_tap_dance_action_t tap_dance_actions[] = { [RPRN_RBRC] = ACTION_TAP_DANCE_DOUBLE(KC_RPRN, KC_RBRC), [LCBR_LABK] = ACTION_TAP_DANCE_DOUBLE(KC_LCBR, KC_LABK), [RCBR_RABK] = ACTION_TAP_DANCE_DOUBLE(KC_RCBR, KC_RABK), - [SCLN_COLN] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, scln_coln_finished, scln_coln_reset, DANCING_TERM), - [QUOT_DQUO] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, quot_dquot_finished, quot_dquot_reset, DANCING_TERM), - [DOT_COMM] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, dot_comm_finished, dot_comm_reset, DANCING_TERM), - [NONE_LEAD] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, none_lead_finished, none_lead_reset, DANCING_TERM), - [U_ARR_GUI] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, u_arrows_gui_finished, u_arrows_gui_reset, DANCING_TERM), - [H_MOU_GUI] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, h_mouse_gui_finished, h_mouse_gui_reset, DANCING_TERM), - [J_MED_MEH] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, j_media_meh_finished, j_media_meh_reset, DANCING_TERM), - [W_MED_MEH] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, w_media_meh_finished, w_media_meh_reset, DANCING_TERM), - [K_NUM_HYP] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, k_numpad_hyper_finished, k_numpad_hyper_reset, DANCING_TERM), - [M_CHO_HYP] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, m_chords_hyper_finished, m_chords_hyper_reset, DANCING_TERM), + [SCLN_COLN] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, scln_coln_finished, scln_coln_reset), + [QUOT_DQUO] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, quot_dquot_finished, quot_dquot_reset), + [DOT_COMM] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, dot_comm_finished, dot_comm_reset), + [NONE_LEAD] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, none_lead_finished, none_lead_reset), + [U_ARR_GUI] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, u_arrows_gui_finished, u_arrows_gui_reset), + [H_MOU_GUI] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, h_mouse_gui_finished, h_mouse_gui_reset), + [J_MED_MEH] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, j_media_meh_finished, j_media_meh_reset), + [W_MED_MEH] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, w_media_meh_finished, w_media_meh_reset), + [K_NUM_HYP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, k_numpad_hyper_finished, k_numpad_hyper_reset), + [M_CHO_HYP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, m_chords_hyper_finished, m_chords_hyper_reset), }; + +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case TD(SCLN_COLN): + case TD(QUOT_DQUO): + case TD(DOT_COMM): + case TD(NONE_LEAD): + case TD(U_ARR_GUI): + case TD(H_MOU_GUI): + case TD(J_MED_MEH): + case TD(W_MED_MEH): + case TD(K_NUM_HYP): + case TD(M_CHO_HYP): + return DANCING_TERM; + default: + return TAPPING_TERM; + } +} diff --git a/keyboards/gboards/gergoplex/keymaps/georgepetri/config.h b/keyboards/gboards/gergoplex/keymaps/georgepetri/config.h index f66c334b85..e2c27583fa 100644 --- a/keyboards/gboards/gergoplex/keymaps/georgepetri/config.h +++ b/keyboards/gboards/gergoplex/keymaps/georgepetri/config.h @@ -27,3 +27,5 @@ along with this program. If not, see . #define COMBO_ALLOW_ACTION_KEYS #define COMBO_VARIABLE_LEN + +#define TAPPING_TERM_PER_KEY diff --git a/keyboards/gboards/gergoplex/keymaps/georgepetri/keymap.c b/keyboards/gboards/gergoplex/keymaps/georgepetri/keymap.c index e3b8944814..8b832cbac8 100644 --- a/keyboards/gboards/gergoplex/keymaps/georgepetri/keymap.c +++ b/keyboards/gboards/gergoplex/keymaps/georgepetri/keymap.c @@ -199,5 +199,14 @@ void ql_reset(qk_tap_dance_state_t *state, void *user_data) { } qk_tap_dance_action_t tap_dance_actions[] = { - [GAME] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, ql_finished, ql_reset, 275) + [GAME] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset) }; + +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: + return 275; + default: + return TAPPING_TERM; + } +} diff --git a/keyboards/handwired/ortho5x14/keymaps/2u/keymap.c b/keyboards/handwired/ortho5x14/keymaps/2u/keymap.c index 70a96fac9d..d785825332 100644 --- a/keyboards/handwired/ortho5x14/keymaps/2u/keymap.c +++ b/keyboards/handwired/ortho5x14/keymaps/2u/keymap.c @@ -343,9 +343,9 @@ qk_tap_dance_action_t tap_dance_actions[] = { [TD_PGUP_HOME] = ACTION_TAP_DANCE_DOUBLE(KC_PGUP, KC_HOME), [TD_PGDN_END] = ACTION_TAP_DANCE_DOUBLE(KC_PGDN, KC_END), - [TD_Q_LrALT] = ACTION_TAP_DANCE_DUAL_ROLE(KC_Q, _ALT), - [TD_R_LrKey] = ACTION_TAP_DANCE_DUAL_ROLE(KC_R, _RAISE), - [TD_T_LrMS] = ACTION_TAP_DANCE_DUAL_ROLE(KC_T, _MOUSE), + [TD_Q_LrALT] = ACTION_TAP_DANCE_LAYER_MOVE(KC_Q, _ALT), + [TD_R_LrKey] = ACTION_TAP_DANCE_LAYER_MOVE(KC_R, _RAISE), + [TD_T_LrMS] = ACTION_TAP_DANCE_LAYER_MOVE(KC_T, _MOUSE), [TD_SHIFT_CAPS] = ACTION_TAP_DANCE_FN_ADVANCED(NULL,lshift_finished, lshift_reset), [TD_SPC_ENT] = ACTION_TAP_DANCE_DOUBLE(KC_SPACE, KC_ENT), diff --git a/keyboards/handwired/ortho5x14/keymaps/split1/keymap.c b/keyboards/handwired/ortho5x14/keymaps/split1/keymap.c index b152503aaf..3734510f8c 100644 --- a/keyboards/handwired/ortho5x14/keymaps/split1/keymap.c +++ b/keyboards/handwired/ortho5x14/keymaps/split1/keymap.c @@ -279,9 +279,9 @@ qk_tap_dance_action_t tap_dance_actions[] = { [TD_PGUP_HOME] = ACTION_TAP_DANCE_DOUBLE(KC_PGUP, KC_HOME), [TD_PGDN_END] = ACTION_TAP_DANCE_DOUBLE(KC_PGUP, KC_END), - [TD_Q_LrALT] = ACTION_TAP_DANCE_DUAL_ROLE(KC_Q, _ALT), - [TD_R_LrKey] = ACTION_TAP_DANCE_DUAL_ROLE(KC_R, _RAISE), - [TD_T_LrMS] = ACTION_TAP_DANCE_DUAL_ROLE(KC_T, _MOUSE), + [TD_Q_LrALT] = ACTION_TAP_DANCE_LAYER_MOVE(KC_Q, _ALT), + [TD_R_LrKey] = ACTION_TAP_DANCE_LAYER_MOVE(KC_R, _RAISE), + [TD_T_LrMS] = ACTION_TAP_DANCE_LAYER_MOVE(KC_T, _MOUSE), [TD_SHIFT_CAPS] = ACTION_TAP_DANCE_FN_ADVANCED(NULL,lshift_finished, lshift_reset), [TD_SPC_ENT] = ACTION_TAP_DANCE_DOUBLE(KC_SPACE, KC_ENT), diff --git a/keyboards/jones/v03/keymaps/default_jp/config.h b/keyboards/jones/v03/keymaps/default_jp/config.h index aa06121c1c..7545003ac7 100644 --- a/keyboards/jones/v03/keymaps/default_jp/config.h +++ b/keyboards/jones/v03/keymaps/default_jp/config.h @@ -21,3 +21,4 @@ // time for long press #define TAPPING_TERM 200 +#define TAPPING_TERM_PER_KEY diff --git a/keyboards/jones/v03/keymaps/default_jp/keymap.c b/keyboards/jones/v03/keymaps/default_jp/keymap.c index 3f5b10208c..bdcb9e88b1 100644 --- a/keyboards/jones/v03/keymaps/default_jp/keymap.c +++ b/keyboards/jones/v03/keymaps/default_jp/keymap.c @@ -51,9 +51,18 @@ void ql_reset(qk_tap_dance_state_t *state, void *user_data); // Tap Dance definitions qk_tap_dance_action_t tap_dance_actions[] = { [TD_LSFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_LSFT, KC_CAPS), - [TD_ESC_NUM] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, ql_finished, ql_reset, 275), + [TD_ESC_NUM] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset), }; +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case TD(TD_ESC_NUM): + return 275; + default: + return TAPPING_TERM; + } +} + // Defines the keycodes used by our macros in process_record_user enum custom_keycodes { MAC = SAFE_RANGE, @@ -303,7 +312,7 @@ static tap ql_tap_state = { // Functions that control what our tap dance key does void ql_finished(qk_tap_dance_state_t *state, void *user_data) { ql_tap_state.state = cur_dance(state); - switch(state->keycode) { + switch(TAP_DANCE_KEYCODE(state)) { case TD(TD_ESC_NUM): // ESC key action switch (ql_tap_state.state) { case SINGLE_TAP: @@ -332,7 +341,7 @@ void ql_finished(qk_tap_dance_state_t *state, void *user_data) { } void ql_reset(qk_tap_dance_state_t *state, void *user_data) { - switch(state->keycode) { + switch(TAP_DANCE_KEYCODE(state)) { case TD(TD_ESC_NUM): // If the key was held down and now is released then switch off the layer if (ql_tap_state.state == TAP_HOLD) { diff --git a/keyboards/jones/v03_1/keymaps/default_ansi/config.h b/keyboards/jones/v03_1/keymaps/default_ansi/config.h index 0b51190bbe..ee7b09a90c 100644 --- a/keyboards/jones/v03_1/keymaps/default_ansi/config.h +++ b/keyboards/jones/v03_1/keymaps/default_ansi/config.h @@ -21,6 +21,7 @@ // time for long press #define TAPPING_TERM 200 +#define TAPPING_TERM_PER_KEY // music map for music-mode #define MUSIC_MAP diff --git a/keyboards/jones/v03_1/keymaps/default_ansi/keymap.c b/keyboards/jones/v03_1/keymaps/default_ansi/keymap.c index 5b1486bb86..e86204a3e3 100644 --- a/keyboards/jones/v03_1/keymaps/default_ansi/keymap.c +++ b/keyboards/jones/v03_1/keymaps/default_ansi/keymap.c @@ -50,9 +50,18 @@ void ql_reset(qk_tap_dance_state_t *state, void *user_data); // Tap Dance definitions qk_tap_dance_action_t tap_dance_actions[] = { [TD_LSFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_LSFT, KC_CAPS), - [TD_ESC_NUM] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, ql_finished, ql_reset, 275), + [TD_ESC_NUM] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset), }; +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case TD(TD_ESC_NUM): + return 275; + default: + return TAPPING_TERM; + } +} + // Defines the keycodes used by our macros in process_record_user enum custom_keycodes { MAC = SAFE_RANGE, @@ -322,7 +331,7 @@ static tap ql_tap_state = { // Functions that control what our tap dance key does void ql_finished(qk_tap_dance_state_t *state, void *user_data) { ql_tap_state.state = cur_dance(state); - switch(state->keycode) { + switch(TAP_DANCE_KEYCODE(state)) { case TD(TD_ESC_NUM): // ESC key action switch (ql_tap_state.state) { case SINGLE_TAP: @@ -351,7 +360,7 @@ void ql_finished(qk_tap_dance_state_t *state, void *user_data) { } void ql_reset(qk_tap_dance_state_t *state, void *user_data) { - switch(state->keycode) { + switch(TAP_DANCE_KEYCODE(state)) { case TD(TD_ESC_NUM): // If the key was held down and now is released then switch off the layer if (ql_tap_state.state == TAP_HOLD) { diff --git a/keyboards/jones/v03_1/keymaps/default_jp/config.h b/keyboards/jones/v03_1/keymaps/default_jp/config.h index 0b51190bbe..ee7b09a90c 100644 --- a/keyboards/jones/v03_1/keymaps/default_jp/config.h +++ b/keyboards/jones/v03_1/keymaps/default_jp/config.h @@ -21,6 +21,7 @@ // time for long press #define TAPPING_TERM 200 +#define TAPPING_TERM_PER_KEY // music map for music-mode #define MUSIC_MAP diff --git a/keyboards/jones/v03_1/keymaps/default_jp/keymap.c b/keyboards/jones/v03_1/keymaps/default_jp/keymap.c index 436586a721..9a2663f6d7 100644 --- a/keyboards/jones/v03_1/keymaps/default_jp/keymap.c +++ b/keyboards/jones/v03_1/keymaps/default_jp/keymap.c @@ -51,9 +51,18 @@ void ql_reset(qk_tap_dance_state_t *state, void *user_data); // Tap Dance definitions qk_tap_dance_action_t tap_dance_actions[] = { [TD_LSFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_LSFT, KC_CAPS), - [TD_ESC_NUM] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, ql_finished, ql_reset, 275), + [TD_ESC_NUM] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset), }; +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case TD(TD_ESC_NUM): + return 275; + default: + return TAPPING_TERM; + } +} + // Defines the keycodes used by our macros in process_record_user enum custom_keycodes { MAC = SAFE_RANGE, @@ -324,7 +333,7 @@ static tap ql_tap_state = { // Functions that control what our tap dance key does void ql_finished(qk_tap_dance_state_t *state, void *user_data) { ql_tap_state.state = cur_dance(state); - switch(state->keycode) { + switch(TAP_DANCE_KEYCODE(state)) { case TD(TD_ESC_NUM): // ESC key action switch (ql_tap_state.state) { case SINGLE_TAP: @@ -353,7 +362,7 @@ void ql_finished(qk_tap_dance_state_t *state, void *user_data) { } void ql_reset(qk_tap_dance_state_t *state, void *user_data) { - switch(state->keycode) { + switch(TAP_DANCE_KEYCODE(state)) { case TD(TD_ESC_NUM): // If the key was held down and now is released then switch off the layer if (ql_tap_state.state == TAP_HOLD) { diff --git a/keyboards/keyhive/navi10/keymaps/default/keymap.c b/keyboards/keyhive/navi10/keymaps/default/keymap.c index d1b3127760..c4295b1553 100644 --- a/keyboards/keyhive/navi10/keymaps/default/keymap.c +++ b/keyboards/keyhive/navi10/keymaps/default/keymap.c @@ -127,5 +127,5 @@ void tk_reset(qk_tap_dance_state_t *state, void *user_data){ //associate the tap dance key with its functionality qk_tap_dance_action_t tap_dance_actions[] = { - [TAPPY_KEY] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, tk_finished, tk_reset, 275) + [TAPPY_KEY] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, tk_finished, tk_reset) }; diff --git a/keyboards/keyhive/navi10/keymaps/devdev/keymap.c b/keyboards/keyhive/navi10/keymaps/devdev/keymap.c index b0d43ebc69..0fc20cbbd4 100644 --- a/keyboards/keyhive/navi10/keymaps/devdev/keymap.c +++ b/keyboards/keyhive/navi10/keymaps/devdev/keymap.c @@ -259,5 +259,5 @@ void tk_reset(qk_tap_dance_state_t *state, void *user_data){ //associate the tap dance key with its functionality qk_tap_dance_action_t tap_dance_actions[] = { - [TAPPY_KEY] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, tk_finished, tk_reset, TAPPING_TERM) + [TAPPY_KEY] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, tk_finished, tk_reset) }; diff --git a/keyboards/keyhive/navi10/keymaps/emdarcher/keymap.c b/keyboards/keyhive/navi10/keymaps/emdarcher/keymap.c index 40efed48d5..37850e28f3 100644 --- a/keyboards/keyhive/navi10/keymaps/emdarcher/keymap.c +++ b/keyboards/keyhive/navi10/keymaps/emdarcher/keymap.c @@ -178,5 +178,5 @@ void tk_reset(qk_tap_dance_state_t *state, void *user_data){ //associate the tap dance key with its functionality qk_tap_dance_action_t tap_dance_actions[] = { - [TAPPY_KEY] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, tk_finished, tk_reset, 275) + [TAPPY_KEY] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, tk_finished, tk_reset) }; diff --git a/keyboards/kprepublic/jj50/keymaps/archetype/config.h b/keyboards/kprepublic/jj50/keymaps/archetype/config.h index a12e070b4f..9604ac719c 100644 --- a/keyboards/kprepublic/jj50/keymaps/archetype/config.h +++ b/keyboards/kprepublic/jj50/keymaps/archetype/config.h @@ -6,4 +6,5 @@ #define AUTO_SHIFT_TIMEOUT 150 #define NO_AUTO_SHIFT_ALPHA #define TAPPING_TERM 150 +#define TAPPING_TERM_PER_KEY //#define BOOTMAGIC_KEY_SALT KC_LCTL diff --git a/keyboards/kprepublic/jj50/keymaps/archetype/keymap.c b/keyboards/kprepublic/jj50/keymaps/archetype/keymap.c index 82c615f135..ff59ef43e5 100644 --- a/keyboards/kprepublic/jj50/keymaps/archetype/keymap.c +++ b/keyboards/kprepublic/jj50/keymaps/archetype/keymap.c @@ -167,9 +167,18 @@ qk_tap_dance_action_t tap_dance_actions[] = { // Single tap = ) | Double tap = ] | Triple tap = } | Single hold = KC_LALT [TD_LALT_RBRC] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, right_brackets, right_brackets_reset), // Layer Switcher ESC - [TD_ESC_LAYER] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, layer_switcher, layer_switcher_reset, 100), + [TD_ESC_LAYER] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, layer_switcher, layer_switcher_reset), }; +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case TD(TD_ESC_LAYER): + return 100; + default: + return TAPPING_TERM; + } +} + const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/planck/keymaps/ariccb/config.h b/keyboards/planck/keymaps/ariccb/config.h index 322aa9277f..464a8cd5db 100644 --- a/keyboards/planck/keymaps/ariccb/config.h +++ b/keyboards/planck/keymaps/ariccb/config.h @@ -40,6 +40,7 @@ //#define MIDI_ADVANCED #define TAPPING_TERM 150 +#define TAPPING_TERM_PER_KEY #define IGNORE_MOD_TAP_INTERRUPT // #define IGNORE_MOD_TAP_INTERRUPT // #define HOLD_ON_OTHER_KEY_PRESS diff --git a/keyboards/planck/keymaps/ariccb/keymap.c b/keyboards/planck/keymaps/ariccb/keymap.c index c0b4e490fa..33d0a26273 100644 --- a/keyboards/planck/keymaps/ariccb/keymap.c +++ b/keyboards/planck/keymaps/ariccb/keymap.c @@ -379,9 +379,18 @@ void usl_reset(qk_tap_dance_state_t *state, void *user_data) { // Associate our tap dance key with its functionality qk_tap_dance_action_t tap_dance_actions[] = { - [UNDS_LOWER] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, usl_finished, usl_reset, 175) + [UNDS_LOWER] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, usl_finished, usl_reset) }; +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case TD(UNDS_LOWER): + return 175; + default: + return TAPPING_TERM; + } +} + bool process_record_user(uint16_t keycode, keyrecord_t *record) { if (!process_select_word(keycode, record, SELWORD)) { return false; } if (!process_caps_word(keycode, record)) { return false; } @@ -661,4 +670,4 @@ void process_combo_event(uint16_t combo_index, bool pressed) { } break; } -} \ No newline at end of file +} diff --git a/keyboards/planck/keymaps/rootiest/config.h b/keyboards/planck/keymaps/rootiest/config.h index fc1fc81761..5bb5a73a29 100644 --- a/keyboards/planck/keymaps/rootiest/config.h +++ b/keyboards/planck/keymaps/rootiest/config.h @@ -87,6 +87,7 @@ /* * TAP-DANCE options */ +#define TAPPING_TERM_PER_KEY // Control Tap-Dance time individually by key #define TAPPING_TERM 250 // Default time allowed before resetting a Tap-Dance combo #define ONESHOT_TAP_TOGGLE 5 /* Tapping this number of times holds the key until tapped once again. */ #define ONESHOT_TIMEOUT 5000 /* Time (in ms) before the one shot key is released */ diff --git a/keyboards/planck/keymaps/rootiest/keymap.c b/keyboards/planck/keymaps/rootiest/keymap.c index 634bf2829e..857d7a9e21 100644 --- a/keyboards/planck/keymaps/rootiest/keymap.c +++ b/keyboards/planck/keymaps/rootiest/keymap.c @@ -1656,13 +1656,26 @@ void sml_reset(qk_tap_dance_state_t* state, void* user_data) { sml_state.state = // Tap Dance definitions qk_tap_dance_action_t tap_dance_actions[] = { // Tap once for °, twice for ℉, thrice for ℃ - [TD_DEG_DEGF] = ACTION_TAP_DANCE_FN(send_degree_symbol), // - [TD_LSHFT_CAPS] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, scap_finished, scap_reset, 200), // - [TD_LCTL_STICKY] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, slctl_finished, slctl_reset, 200), // - [TD_LALT_STICKY] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, slalt_finished, slalt_reset, 200), // - [TD_SMILEY] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, sml_finished, sml_reset, 500), + [TD_DEG_DEGF] = ACTION_TAP_DANCE_FN(send_degree_symbol), // + [TD_LSHFT_CAPS] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, scap_finished, scap_reset), // + [TD_LCTL_STICKY] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, slctl_finished, slctl_reset), // + [TD_LALT_STICKY] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, slalt_finished, slalt_reset), // + [TD_SMILEY] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, sml_finished, sml_reset), }; +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case TD(TD_LSHFT_CAPS): + case TD(TD_LCTL_STICKY): + case TD(TD_LALT_STICKY): + return 200; + case TD(TD_SMILEY): + return 500; + default: + return TAPPING_TERM; + } +} + // Dip-Switch controls void dip_switch_update_user(uint8_t index, bool active) { switch (index) { diff --git a/keyboards/salicylic_acid3/7skb/keymaps/salicylic/keymap.c b/keyboards/salicylic_acid3/7skb/keymaps/salicylic/keymap.c index 4e1f2acd05..7221f8c612 100644 --- a/keyboards/salicylic_acid3/7skb/keymaps/salicylic/keymap.c +++ b/keyboards/salicylic_acid3/7skb/keymaps/salicylic/keymap.c @@ -32,8 +32,8 @@ enum tapdances{ }; qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESFL] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _FLOCK), - [TD_ESQW] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _QWERTY), + [TD_ESFL] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _FLOCK), + [TD_ESQW] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/salicylic_acid3/7splus/keymaps/salicylic/keymap.c b/keyboards/salicylic_acid3/7splus/keymaps/salicylic/keymap.c index 06d7972a2e..bea5978c50 100644 --- a/keyboards/salicylic_acid3/7splus/keymaps/salicylic/keymap.c +++ b/keyboards/salicylic_acid3/7splus/keymaps/salicylic/keymap.c @@ -39,8 +39,8 @@ enum tapdances{ }; qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESMS] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _MOUSE), - [TD_ESAR] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _QWERTY), + [TD_ESMS] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _MOUSE), + [TD_ESAR] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/salicylic_acid3/jisplit89/keymaps/salicylic/keymap.c b/keyboards/salicylic_acid3/jisplit89/keymaps/salicylic/keymap.c index 4e8addf6b7..eba01d4d65 100644 --- a/keyboards/salicylic_acid3/jisplit89/keymaps/salicylic/keymap.c +++ b/keyboards/salicylic_acid3/jisplit89/keymaps/salicylic/keymap.c @@ -39,8 +39,8 @@ enum tapdances{ }; qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESMS] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _MOUSE), - [TD_ESAR] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _QWERTY), + [TD_ESMS] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _MOUSE), + [TD_ESAR] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/salicylic_acid3/naked60/keymaps/salicylic/keymap.c b/keyboards/salicylic_acid3/naked60/keymaps/salicylic/keymap.c index 8a2dbf051f..b70e68da54 100644 --- a/keyboards/salicylic_acid3/naked60/keymaps/salicylic/keymap.c +++ b/keyboards/salicylic_acid3/naked60/keymaps/salicylic/keymap.c @@ -23,8 +23,8 @@ enum tapdances{ }; qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESFL] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _FLOCK), - [TD_ESQW] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _QWERTY), + [TD_ESFL] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _FLOCK), + [TD_ESQW] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/salicylic_acid3/naked60/keymaps/salicylic_with_nafuda/keymap.c b/keyboards/salicylic_acid3/naked60/keymaps/salicylic_with_nafuda/keymap.c index 4201d79a2e..c43803be35 100644 --- a/keyboards/salicylic_acid3/naked60/keymaps/salicylic_with_nafuda/keymap.c +++ b/keyboards/salicylic_acid3/naked60/keymaps/salicylic_with_nafuda/keymap.c @@ -34,8 +34,8 @@ enum tapdances{ }; qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESFL] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _FLOCK), - [TD_ESQW] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _QWERTY), + [TD_ESFL] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _FLOCK), + [TD_ESQW] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/salicylic_acid3/naked60/keymaps/salicylic_with_setta21/keymap.c b/keyboards/salicylic_acid3/naked60/keymaps/salicylic_with_setta21/keymap.c index 23786da915..6dd2ef8863 100644 --- a/keyboards/salicylic_acid3/naked60/keymaps/salicylic_with_setta21/keymap.c +++ b/keyboards/salicylic_acid3/naked60/keymaps/salicylic_with_setta21/keymap.c @@ -32,8 +32,8 @@ enum tapdances{ }; qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESFL] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _FLOCK), - [TD_ESQW] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _QWERTY), + [TD_ESFL] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _FLOCK), + [TD_ESQW] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/salicylic_acid3/naked64/keymaps/salicylic/keymap.c b/keyboards/salicylic_acid3/naked64/keymaps/salicylic/keymap.c index db46075c8f..ae7843341a 100644 --- a/keyboards/salicylic_acid3/naked64/keymaps/salicylic/keymap.c +++ b/keyboards/salicylic_acid3/naked64/keymaps/salicylic/keymap.c @@ -33,8 +33,8 @@ enum tapdances{ }; qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESFL] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _FLOCK), - [TD_ESQW] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _QWERTY), + [TD_ESFL] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _FLOCK), + [TD_ESQW] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/salicylic_acid3/naked64/keymaps/salicylic_with_setta21/keymap.c b/keyboards/salicylic_acid3/naked64/keymaps/salicylic_with_setta21/keymap.c index c91716cdd4..6e2dd0886f 100644 --- a/keyboards/salicylic_acid3/naked64/keymaps/salicylic_with_setta21/keymap.c +++ b/keyboards/salicylic_acid3/naked64/keymaps/salicylic_with_setta21/keymap.c @@ -38,8 +38,8 @@ enum tapdances{ }; qk_tap_dance_action_t tap_dance_actions[] = { - [TD_ESFL] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _FLOCK), - [TD_ESQW] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _QWERTY), + [TD_ESFL] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _FLOCK), + [TD_ESQW] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), }; #define LOWER MO(_LOWER) diff --git a/keyboards/thevankeyboards/minivan/keymaps/belak/keymap.c b/keyboards/thevankeyboards/minivan/keymaps/belak/keymap.c index 437b1881a4..0ed709747a 100644 --- a/keyboards/thevankeyboards/minivan/keymaps/belak/keymap.c +++ b/keyboards/thevankeyboards/minivan/keymaps/belak/keymap.c @@ -22,6 +22,9 @@ #define TD_LCTL TD(BE_TD_CTL) #define TD_LALT TD(BE_TD_ALT) +#define ACTION_TAP_DANCE_MOD_TAP(mod) \ + { .fn = {mod_tap_fn, NULL, mod_reset_fn}, .user_data = (void *)&((uint8_t){mod}), } + enum belak_td { BE_TD_GUI = 0, BE_TD_CTL, @@ -32,15 +35,9 @@ void mod_tap_fn(qk_tap_dance_state_t *state, void *user_data); void mod_reset_fn(qk_tap_dance_state_t *state, void *user_data); qk_tap_dance_action_t tap_dance_actions[] = { - [BE_TD_GUI] = ACTION_TAP_DANCE_FN_ADVANCED(mod_tap_fn, NULL, mod_reset_fn), - [BE_TD_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(mod_tap_fn, NULL, mod_reset_fn), - [BE_TD_ALT] = ACTION_TAP_DANCE_FN_ADVANCED(mod_tap_fn, NULL, mod_reset_fn), -}; - -uint16_t tap_dance_keys[] = { - [BE_TD_GUI] = KC_LGUI, - [BE_TD_CTL] = KC_LCTL, - [BE_TD_ALT] = KC_LALT, + [BE_TD_GUI] = ACTION_TAP_DANCE_MOD_TAP(KC_LGUI), + [BE_TD_CTL] = ACTION_TAP_DANCE_MOD_TAP(KC_LCTL), + [BE_TD_ALT] = ACTION_TAP_DANCE_MOD_TAP(KC_LALT), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { @@ -74,7 +71,8 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { void mod_tap_fn(qk_tap_dance_state_t *state, void *user_data) { switch (state->count) { case 1: - register_mods(MOD_BIT(tap_dance_keys[state->keycode - QK_TAP_DANCE])); + uint8_t *mod = (uint8_t *)user_data; + register_mods(MOD_BIT(*mod)); send_keyboard_report(); break; case 2: @@ -90,8 +88,9 @@ void mod_tap_fn(qk_tap_dance_state_t *state, void *user_data) { } void mod_reset_fn(qk_tap_dance_state_t *state, void *user_data) { + uint8_t *mod = (uint8_t *)user_data; layer_off(_L1); layer_off(_L2); - unregister_mods(MOD_BIT(tap_dance_keys[state->keycode - QK_TAP_DANCE])); + unregister_mods(MOD_BIT(*mod)); send_keyboard_report(); } diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index db8df5f870..3270a1b000 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -15,12 +15,8 @@ */ #include "quantum.h" -#ifndef NO_ACTION_ONESHOT -uint8_t get_oneshot_mods(void); -#endif - -static uint16_t last_td; -static int16_t highest_td = -1; +static uint16_t active_td; +static uint16_t last_tap_time; void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data) { qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; @@ -34,18 +30,14 @@ void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data) void qk_tap_dance_pair_finished(qk_tap_dance_state_t *state, void *user_data) { qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; - if (state->count == 1) { - register_code16(pair->kc1); - } else if (state->count == 2) { - register_code16(pair->kc2); - } + register_code16(pair->kc1); } void qk_tap_dance_pair_reset(qk_tap_dance_state_t *state, void *user_data) { qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; - wait_ms(TAP_CODE_DELAY); if (state->count == 1) { + wait_ms(TAP_CODE_DELAY); unregister_code16(pair->kc1); } else if (state->count == 2) { unregister_code16(pair->kc2); @@ -87,23 +79,40 @@ static inline void _process_tap_dance_action_fn(qk_tap_dance_state_t *state, voi } static inline void process_tap_dance_action_on_each_tap(qk_tap_dance_action_t *action) { + action->state.count++; + action->state.weak_mods = get_mods(); + action->state.weak_mods |= get_weak_mods(); +#ifndef NO_ACTION_ONESHOT + action->state.oneshot_mods = get_oneshot_mods(); +#endif _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_tap); } -static inline void process_tap_dance_action_on_dance_finished(qk_tap_dance_action_t *action) { - if (action->state.finished) return; - action->state.finished = true; - add_mods(action->state.oneshot_mods); - add_weak_mods(action->state.weak_mods); - send_keyboard_report(); - _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_dance_finished); -} - static inline void process_tap_dance_action_on_reset(qk_tap_dance_action_t *action) { _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_reset); - del_mods(action->state.oneshot_mods); del_weak_mods(action->state.weak_mods); +#ifndef NO_ACTION_ONESHOT + del_mods(action->state.oneshot_mods); +#endif send_keyboard_report(); + action->state = (const qk_tap_dance_state_t){0}; +} + +static inline void process_tap_dance_action_on_dance_finished(qk_tap_dance_action_t *action) { + if (!action->state.finished) { + action->state.finished = true; + add_weak_mods(action->state.weak_mods); +#ifndef NO_ACTION_ONESHOT + add_mods(action->state.oneshot_mods); +#endif + send_keyboard_report(); + _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_dance_finished); + } + active_td = 0; + if (!action->state.pressed) { + // There will not be a key release event, so reset now. + process_tap_dance_action_on_reset(action); + } } void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { @@ -111,51 +120,33 @@ void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { if (!record->event.pressed) return; - if (highest_td == -1) return; + if (!active_td || keycode == active_td) return; - for (int i = 0; i <= highest_td; i++) { - action = &tap_dance_actions[i]; - if (action->state.count) { - if (keycode == action->state.keycode && keycode == last_td) continue; - action->state.interrupted = true; - action->state.interrupting_keycode = keycode; - process_tap_dance_action_on_dance_finished(action); - reset_tap_dance(&action->state); + action = &tap_dance_actions[TD_INDEX(active_td)]; + action->state.interrupted = true; + action->state.interrupting_keycode = keycode; + process_tap_dance_action_on_dance_finished(action); - // Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with - // modifiers), but these weak mods should not affect the keypress which interrupted the tap dance. - clear_weak_mods(); - } - } + // Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with + // modifiers), but these weak mods should not affect the keypress which interrupted the tap dance. + clear_weak_mods(); } bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { - uint16_t idx = keycode - QK_TAP_DANCE; qk_tap_dance_action_t *action; switch (keycode) { case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: - if ((int16_t)idx > highest_td) highest_td = idx; - action = &tap_dance_actions[idx]; + action = &tap_dance_actions[TD_INDEX(keycode)]; action->state.pressed = record->event.pressed; if (record->event.pressed) { - action->state.keycode = keycode; - action->state.count++; - action->state.timer = timer_read(); -#ifndef NO_ACTION_ONESHOT - action->state.oneshot_mods = get_oneshot_mods(); -#else - action->state.oneshot_mods = 0; -#endif - action->state.weak_mods = get_mods(); - action->state.weak_mods |= get_weak_mods(); + last_tap_time = timer_read(); process_tap_dance_action_on_each_tap(action); - - last_td = keycode; + active_td = action->state.finished ? 0 : keycode; } else { - if (action->state.count && action->state.finished) { - reset_tap_dance(&action->state); + if (action->state.finished) { + process_tap_dance_action_on_reset(action); } } @@ -166,35 +157,17 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { } void tap_dance_task() { - if (highest_td == -1) return; - uint16_t tap_user_defined; + qk_tap_dance_action_t *action; - for (uint8_t i = 0; i <= highest_td; i++) { - qk_tap_dance_action_t *action = &tap_dance_actions[i]; - if (action->custom_tapping_term > 0) { - tap_user_defined = action->custom_tapping_term; - } else { - tap_user_defined = GET_TAPPING_TERM(action->state.keycode, &(keyrecord_t){}); - } - if (action->state.count && timer_elapsed(action->state.timer) > tap_user_defined) { - process_tap_dance_action_on_dance_finished(action); - reset_tap_dance(&action->state); - } + if (!active_td || timer_elapsed(last_tap_time) <= GET_TAPPING_TERM(active_td, &(keyrecord_t){})) return; + + action = &tap_dance_actions[TD_INDEX(active_td)]; + if (!action->state.interrupted) { + process_tap_dance_action_on_dance_finished(action); } } void reset_tap_dance(qk_tap_dance_state_t *state) { - qk_tap_dance_action_t *action; - - if (state->pressed) return; - - action = &tap_dance_actions[state->keycode - QK_TAP_DANCE]; - - process_tap_dance_action_on_reset(action); - - state->count = 0; - state->interrupted = false; - state->finished = false; - state->interrupting_keycode = 0; - last_td = 0; + active_td = 0; + process_tap_dance_action_on_reset((qk_tap_dance_action_t *)state); } diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h index d9ffb1e73d..d97900d96b 100644 --- a/quantum/process_keycode/process_tap_dance.h +++ b/quantum/process_keycode/process_tap_dance.h @@ -22,30 +22,27 @@ # include typedef struct { - uint8_t count; - uint8_t oneshot_mods; - uint8_t weak_mods; - uint16_t keycode; uint16_t interrupting_keycode; - uint16_t timer; - bool interrupted; - bool pressed; - bool finished; + uint8_t count; + uint8_t weak_mods; +# ifndef NO_ACTION_ONESHOT + uint8_t oneshot_mods; +# endif + bool pressed : 1; + bool finished : 1; + bool interrupted : 1; } qk_tap_dance_state_t; -# define TD(n) (QK_TAP_DANCE | ((n)&0xFF)) - typedef void (*qk_tap_dance_user_fn_t)(qk_tap_dance_state_t *state, void *user_data); typedef struct { + qk_tap_dance_state_t state; struct { qk_tap_dance_user_fn_t on_each_tap; qk_tap_dance_user_fn_t on_dance_finished; qk_tap_dance_user_fn_t on_reset; } fn; - qk_tap_dance_state_t state; - uint16_t custom_tapping_term; - void * user_data; + void *user_data; } qk_tap_dance_action_t; typedef struct { @@ -62,31 +59,31 @@ typedef struct { # define ACTION_TAP_DANCE_DOUBLE(kc1, kc2) \ { .fn = {qk_tap_dance_pair_on_each_tap, qk_tap_dance_pair_finished, qk_tap_dance_pair_reset}, .user_data = (void *)&((qk_tap_dance_pair_t){kc1, kc2}), } -# define ACTION_TAP_DANCE_DUAL_ROLE(kc, layer) \ +# define ACTION_TAP_DANCE_LAYER_MOVE(kc, layer) \ { .fn = {qk_tap_dance_dual_role_on_each_tap, qk_tap_dance_dual_role_finished, qk_tap_dance_dual_role_reset}, .user_data = (void *)&((qk_tap_dance_dual_role_t){kc, layer, layer_move}), } # define ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer) \ { .fn = {NULL, qk_tap_dance_dual_role_finished, qk_tap_dance_dual_role_reset}, .user_data = (void *)&((qk_tap_dance_dual_role_t){kc, layer, layer_invert}), } -# define ACTION_TAP_DANCE_LAYER_MOVE(kc, layer) ACTION_TAP_DANCE_DUAL_ROLE(kc, layer) - # define ACTION_TAP_DANCE_FN(user_fn) \ { .fn = {NULL, user_fn, 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, } -# define ACTION_TAP_DANCE_FN_ADVANCED_TIME(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, tap_specific_tapping_term) \ - { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset}, .user_data = NULL, .custom_tapping_term = tap_specific_tapping_term, } +# define TD(n) (QK_TAP_DANCE | TD_INDEX(n)) +# define TD_INDEX(code) ((code)&0xFF) +# define TAP_DANCE_KEYCODE(state) TD(((qk_tap_dance_action_t *)state) - tap_dance_actions) extern qk_tap_dance_action_t tap_dance_actions[]; +void reset_tap_dance(qk_tap_dance_state_t *state); + /* To be used internally */ void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record); bool process_tap_dance(uint16_t keycode, keyrecord_t *record); void tap_dance_task(void); -void reset_tap_dance(qk_tap_dance_state_t *state); void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data); void qk_tap_dance_pair_finished(qk_tap_dance_state_t *state, void *user_data); diff --git a/tests/tapdance/config.h b/tests/tapdance/config.h new file mode 100644 index 0000000000..6aada3efd3 --- /dev/null +++ b/tests/tapdance/config.h @@ -0,0 +1,19 @@ +/* Copyright 2022 Jouke Witteveen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "test_common.h" diff --git a/tests/tapdance/examples.c b/tests/tapdance/examples.c new file mode 100644 index 0000000000..4a5be41b08 --- /dev/null +++ b/tests/tapdance/examples.c @@ -0,0 +1,199 @@ +/* Copyright 2022 Jouke Witteveen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "quantum.h" +#include "examples.h" + +// Example code from the tap dance documentation, adapted for testing + +// clang-format off + +// Example 1 + +void dance_egg(qk_tap_dance_state_t *state, void *user_data) { + if (state->count >= 100) { + // SEND_STRING("Safety dance!"); + tap_code(KC_C); + reset_tap_dance(state); + } +} + + +// Example 2 + +void dance_flsh_each(qk_tap_dance_state_t *state, void *user_data) { + switch (state->count) { + case 1: + register_code(KC_3); + break; + case 2: + register_code(KC_2); + break; + case 3: + register_code(KC_1); + break; + case 4: + unregister_code(KC_3); + // wait_ms(50); + unregister_code(KC_2); + // wait_ms(50); + unregister_code(KC_1); + } +} + +void dance_flsh_finished(qk_tap_dance_state_t *state, void *user_data) { + if (state->count >= 4) { + // reset_keyboard(); + tap_code(KC_R); + } +} + +void dance_flsh_reset(qk_tap_dance_state_t *state, void *user_data) { + unregister_code(KC_1); + // wait_ms(50); + unregister_code(KC_2); + // wait_ms(50); + unregister_code(KC_3); +} + + +// Example 3 + +typedef struct { + uint16_t tap; + uint16_t hold; + uint16_t held; +} tap_dance_tap_hold_t; + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + qk_tap_dance_action_t *action; + + switch (keycode) { + case TD(CT_CLN): + action = &tap_dance_actions[TD_INDEX(keycode)]; + if (!record->event.pressed && action->state.count && !action->state.finished) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; + tap_code16(tap_hold->tap); + } + } + return true; +} + +void tap_dance_tap_hold_finished(qk_tap_dance_state_t *state, void *user_data) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + + if (state->pressed) { + if (state->count == 1 +#ifndef PERMISSIVE_HOLD + && !state->interrupted +#endif + ) { + register_code16(tap_hold->hold); + tap_hold->held = tap_hold->hold; + } else { + register_code16(tap_hold->tap); + tap_hold->held = tap_hold->tap; + } + } +} + +void tap_dance_tap_hold_reset(qk_tap_dance_state_t *state, void *user_data) { + tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + + if (tap_hold->held) { + unregister_code16(tap_hold->held); + tap_hold->held = 0; + } +} + +#define ACTION_TAP_DANCE_TAP_HOLD(tap, hold) \ + { .fn = {NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, .user_data = (void *)&((tap_dance_tap_hold_t){tap, hold, 0}), } + + +// Example 4 + +typedef enum { + TD_NONE, + TD_UNKNOWN, + TD_SINGLE_TAP, + TD_SINGLE_HOLD, + TD_DOUBLE_TAP, + TD_DOUBLE_HOLD, + TD_DOUBLE_SINGLE_TAP, + TD_TRIPLE_TAP, + TD_TRIPLE_HOLD +} td_state_t; + +typedef struct { + bool is_press_action; + td_state_t state; +} td_tap_t; + +td_state_t cur_dance(qk_tap_dance_state_t *state) { + if (state->count == 1) { + if (state->interrupted || !state->pressed) return TD_SINGLE_TAP; + else return TD_SINGLE_HOLD; + } else if (state->count == 2) { + if (state->interrupted) return TD_DOUBLE_SINGLE_TAP; + else if (state->pressed) return TD_DOUBLE_HOLD; + else return TD_DOUBLE_TAP; + } + + if (state->count == 3) { + if (state->interrupted || !state->pressed) return TD_TRIPLE_TAP; + else return TD_TRIPLE_HOLD; + } else return TD_UNKNOWN; +} + +static td_tap_t xtap_state = { + .is_press_action = true, + .state = TD_NONE +}; + +void x_finished(qk_tap_dance_state_t *state, void *user_data) { + xtap_state.state = cur_dance(state); + switch (xtap_state.state) { + case TD_SINGLE_TAP: register_code(KC_X); break; + case TD_SINGLE_HOLD: register_code(KC_LCTL); break; + case TD_DOUBLE_TAP: register_code(KC_ESC); break; + case TD_DOUBLE_HOLD: register_code(KC_LALT); break; + case TD_DOUBLE_SINGLE_TAP: tap_code(KC_X); register_code(KC_X); + default: break; // Not present in documentation + } +} + +void x_reset(qk_tap_dance_state_t *state, void *user_data) { + switch (xtap_state.state) { + case TD_SINGLE_TAP: unregister_code(KC_X); break; + case TD_SINGLE_HOLD: unregister_code(KC_LCTL); break; + case TD_DOUBLE_TAP: unregister_code(KC_ESC); break; + case TD_DOUBLE_HOLD: unregister_code(KC_LALT); + case TD_DOUBLE_SINGLE_TAP: unregister_code(KC_X); + default: break; // Not present in documentation + } + xtap_state.state = TD_NONE; +} + + +qk_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) +}; + +// clang-format on diff --git a/tests/tapdance/examples.h b/tests/tapdance/examples.h new file mode 100644 index 0000000000..2622af6b2f --- /dev/null +++ b/tests/tapdance/examples.h @@ -0,0 +1,33 @@ +/* Copyright 2022 Jouke Witteveen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + TD_ESC_CAPS, + CT_EGG, + CT_FLSH, + CT_CLN, + X_CTL, +}; + +#ifdef __cplusplus +} +#endif diff --git a/tests/tapdance/test.mk b/tests/tapdance/test.mk new file mode 100644 index 0000000000..041d9b4dc9 --- /dev/null +++ b/tests/tapdance/test.mk @@ -0,0 +1,22 @@ +# Copyright 2022 Jouke Witteveen +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# -------------------------------------------------------------------------------- +# Keep this file, even if it is empty, as a marker that this folder contains tests +# -------------------------------------------------------------------------------- + +TAP_DANCE_ENABLE = yes + +SRC += examples.c diff --git a/tests/tapdance/test_examples.cpp b/tests/tapdance/test_examples.cpp new file mode 100644 index 0000000000..e67e6cb907 --- /dev/null +++ b/tests/tapdance/test_examples.cpp @@ -0,0 +1,319 @@ +/* Copyright 2022 Jouke Witteveen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "keyboard_report_util.hpp" +#include "keycode.h" +#include "test_common.hpp" +#include "action_tapping.h" +#include "test_keymap_key.hpp" +#include "examples.h" + +using testing::_; +using testing::InSequence; + +class TapDance : public TestFixture {}; + +TEST_F(TapDance, DoubleTap) { + TestDriver driver; + InSequence s; + auto key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)}; + + set_keymap({key_esc_caps}); + + /* The tap dance key does nothing on the first press */ + key_esc_caps.press(); + run_one_scan_loop(); + key_esc_caps.release(); + EXPECT_NO_REPORT(driver); + + /* We get the key press and the release on timeout */ + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_ESC)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Double tap gets us the second key */ + tap_key(key_esc_caps); + EXPECT_NO_REPORT(driver); + key_esc_caps.press(); + EXPECT_REPORT(driver, (KC_CAPS)); + run_one_scan_loop(); + key_esc_caps.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, DoubleTapWithMod) { + TestDriver driver; + InSequence s; + auto key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)}; + auto key_shift = KeymapKey{0, 2, 0, KC_LSFT}; + + set_keymap({key_esc_caps, key_shift}); + + /* The tap dance key does nothing on the first press */ + key_shift.press(); + EXPECT_REPORT(driver, (KC_LSFT)); + run_one_scan_loop(); + key_esc_caps.press(); + run_one_scan_loop(); + + key_esc_caps.release(); + key_shift.release(); + EXPECT_EMPTY_REPORT(driver); + + /* We get the key press and the release */ + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_REPORT(driver, (KC_LSFT, KC_ESC)); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Double tap gets us the second key */ + key_shift.press(); + EXPECT_REPORT(driver, (KC_LSFT)); + run_one_scan_loop(); + tap_key(key_esc_caps); + EXPECT_NO_REPORT(driver); + key_shift.release(); + key_esc_caps.press(); + EXPECT_REPORT(driver, (KC_LSFT, KC_CAPS)); + run_one_scan_loop(); + key_esc_caps.release(); + EXPECT_REPORT(driver, (KC_LSFT)); + run_one_scan_loop(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, DoubleTapInterrupted) { + TestDriver driver; + InSequence s; + auto key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)}; + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({key_esc_caps, regular_key}); + + /* Interrupted double tap */ + tap_key(key_esc_caps); + regular_key.press(); + /* Immediate tap of the first key */ + EXPECT_REPORT(driver, (KC_ESC)); + EXPECT_EMPTY_REPORT(driver); + /* Followed by the interrupting key */ + EXPECT_REPORT(driver, (KC_A)); + run_one_scan_loop(); + regular_key.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Second tap after being interrupted acts as a single tap */ + key_esc_caps.press(); + run_one_scan_loop(); + key_esc_caps.release(); + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_ESC)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, DanceFn) { + TestDriver driver; + InSequence s; + auto key_egg = KeymapKey(0, 1, 0, TD(CT_EGG)); + + set_keymap({key_egg}); + + /* 99 taps do nothing */ + for (int i = 0; i < 99; i++) { + run_one_scan_loop(); + key_egg.press(); + run_one_scan_loop(); + key_egg.release(); + } + idle_for(TAPPING_TERM); + EXPECT_NO_REPORT(driver); + run_one_scan_loop(); + + /* 100 taps trigger the action */ + for (int i = 0; i < 100; i++) { + run_one_scan_loop(); + key_egg.press(); + run_one_scan_loop(); + key_egg.release(); + } + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_C)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* 250 taps act the same as 100 taps */ + /* Taps are counted in an uint8_t, so the count overflows after 255 taps */ + for (int i = 0; i < 250; i++) { + run_one_scan_loop(); + key_egg.press(); + run_one_scan_loop(); + key_egg.release(); + } + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_C)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, DanceFnAdvanced) { + TestDriver driver; + InSequence s; + auto key_flsh = KeymapKey(0, 1, 0, TD(CT_FLSH)); + + set_keymap({key_flsh}); + + /* Three taps don't trigger a reset */ + EXPECT_REPORT(driver, (KC_3)); + EXPECT_REPORT(driver, (KC_3, KC_2)); + EXPECT_REPORT(driver, (KC_3, KC_2, KC_1)); + for (int i = 0; i < 3; i++) { + run_one_scan_loop(); + key_flsh.press(); + run_one_scan_loop(); + key_flsh.release(); + } + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_3, KC_2)); + EXPECT_REPORT(driver, (KC_3)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Four taps trigger a reset */ + EXPECT_REPORT(driver, (KC_3)); + EXPECT_REPORT(driver, (KC_3, KC_2)); + EXPECT_REPORT(driver, (KC_3, KC_2, KC_1)); + EXPECT_REPORT(driver, (KC_2, KC_1)); + EXPECT_REPORT(driver, (KC_1)); + EXPECT_EMPTY_REPORT(driver); + for (int i = 0; i < 4; i++) { + run_one_scan_loop(); + key_flsh.press(); + run_one_scan_loop(); + key_flsh.release(); + } + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_R)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, TapHold) { + TestDriver driver; + InSequence s; + auto key_cln = KeymapKey{0, 1, 0, TD(CT_CLN)}; + + set_keymap({key_cln}); + + /* Short taps fire on release */ + key_cln.press(); + run_one_scan_loop(); + key_cln.release(); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_REPORT(driver, (KC_LSFT, KC_SCLN)); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Holds immediate following a tap apply to the tap key */ + key_cln.press(); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_REPORT(driver, (KC_LSFT, KC_SCLN)); + idle_for(TAPPING_TERM * 2); + key_cln.release(); + EXPECT_REPORT(driver, (KC_LSFT)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Holds trigger the hold key */ + key_cln.press(); + idle_for(TAPPING_TERM); + run_one_scan_loop(); + EXPECT_REPORT(driver, (KC_SCLN)); + run_one_scan_loop(); + key_cln.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(TapDance, QuadFunction) { + TestDriver driver; + InSequence s; + auto key_quad = KeymapKey{0, 1, 0, TD(X_CTL)}; + auto regular_key = KeymapKey(0, 2, 0, KC_A); + + set_keymap({key_quad, regular_key}); + + /* Single tap */ + key_quad.press(); + run_one_scan_loop(); + key_quad.release(); + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_X)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Single hold */ + key_quad.press(); + run_one_scan_loop(); + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_LCTL)); + run_one_scan_loop(); + key_quad.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Double tap */ + tap_key(key_quad); + key_quad.press(); + run_one_scan_loop(); + key_quad.release(); + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_ESC)); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Double tap and hold */ + tap_key(key_quad); + key_quad.press(); + run_one_scan_loop(); + idle_for(TAPPING_TERM); + EXPECT_REPORT(driver, (KC_LALT)); + run_one_scan_loop(); + key_quad.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); + + /* Double single tap */ + tap_key(key_quad); + tap_key(key_quad); + regular_key.press(); + EXPECT_REPORT(driver, (KC_X)); + EXPECT_EMPTY_REPORT(driver); + EXPECT_REPORT(driver, (KC_X)); + EXPECT_EMPTY_REPORT(driver); + EXPECT_REPORT(driver, (KC_A)); + run_one_scan_loop(); + regular_key.release(); + EXPECT_EMPTY_REPORT(driver); + run_one_scan_loop(); +} diff --git a/users/edvorakjp/edvorakjp_tap_dance.c b/users/edvorakjp/edvorakjp_tap_dance.c index cee10de693..69fcbac1ca 100644 --- a/users/edvorakjp/edvorakjp_tap_dance.c +++ b/users/edvorakjp/edvorakjp_tap_dance.c @@ -64,6 +64,15 @@ void td_raise_reset(qk_tap_dance_state_t *state, void *user_data) { } qk_tap_dance_action_t tap_dance_actions[] = { - [TD_EDVORAKJP_LOWER] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, td_lower_finished, td_lower_reset, 150), - [TD_EDVORAKJP_RAISE] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, td_raise_finished, td_raise_reset, 150), + [TD_EDVORAKJP_LOWER] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, td_lower_finished, td_lower_reset), + [TD_EDVORAKJP_RAISE] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, td_raise_finished, td_raise_reset), }; + +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: + return 150; + default: + return TAPPING_TERM; + } +} diff --git a/users/gourdo1/gourdo1.c b/users/gourdo1/gourdo1.c index b964729be9..4e741ccc91 100644 --- a/users/gourdo1/gourdo1.c +++ b/users/gourdo1/gourdo1.c @@ -45,7 +45,7 @@ qk_tap_dance_action_t tap_dance_actions[] = { [TD_LSFT_CAPSLOCK] = ACTION_TAP_DANCE_DOUBLE(KC_LSFT, KC_CAPS), [TD_LSFT_CAPS_WIN] = ACTION_TAP_DANCE_FN_ADVANCED(dance_LSFT_each_tap, NULL, dance_LSFT_reset), // Tap once for Escape, twice to reset to base layer - [TD_ESC_BASELYR] = ACTION_TAP_DANCE_DUAL_ROLE(KC_ESC, _BASE), + [TD_ESC_BASELYR] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _BASE), }; #endif // TD_LSFT_CAPSLOCK_ENABLE diff --git a/users/mnil/config.h b/users/mnil/config.h index 3547785ff7..b471b9a818 100644 --- a/users/mnil/config.h +++ b/users/mnil/config.h @@ -19,3 +19,4 @@ #define MK_3_SPEED #define MK_MOMENTARY_ACCEL #define PERMISSIVE_HOLD +#define TAPPING_TERM 250 diff --git a/users/mnil/mnil.c b/users/mnil/mnil.c index d5bd0ef0bb..00da6086ef 100644 --- a/users/mnil/mnil.c +++ b/users/mnil/mnil.c @@ -140,7 +140,7 @@ void aa_reset(qk_tap_dance_state_t *state, void *user_data) { // clang-format off qk_tap_dance_action_t tap_dance_actions[] = { - [AAE] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, ae_finished, ae_reset, 250), - [OAA] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, aa_finished, aa_reset, 250) + [AAE] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ae_finished, ae_reset), + [OAA] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, aa_finished, aa_reset) }; // clang-format on diff --git a/users/ninjonas/config.h b/users/ninjonas/config.h index 025dbb541a..565e40e841 100644 --- a/users/ninjonas/config.h +++ b/users/ninjonas/config.h @@ -2,6 +2,7 @@ #undef TAPPING_TERM #define TAPPING_TERM 200 #endif +#define TAPPING_TERM_PER_KEY // Mouse Settings: Smoothing out mouse movement on keypress #ifndef MOUSEKEY_INTERVAL @@ -18,4 +19,4 @@ #undef COMBO_TERM #define COMBO_COUNT 5 #define COMBO_TERM 60 -#endif \ No newline at end of file +#endif diff --git a/users/ninjonas/tap_dances.c b/users/ninjonas/tap_dances.c index 63e4d3ba47..3e4cec9133 100644 --- a/users/ninjonas/tap_dances.c +++ b/users/ninjonas/tap_dances.c @@ -107,6 +107,16 @@ qk_tap_dance_action_t tap_dance_actions[] = { [TD_GUI_GUISPC] = ACTION_TAP_DANCE_DOUBLE(KC_LGUI, LGUI(KC_SPC)), // Advanced Tap Dances - [TD_COPY_PASTE_APP] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, copy_paste_app_finished, copy_paste_app_reset, 300), - [TD_Y_NUMPAD] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, y_numpad_finished, y_numpad_reset, 300), -}; \ No newline at end of file + [TD_COPY_PASTE_APP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, copy_paste_app_finished, copy_paste_app_reset), + [TD_Y_NUMPAD] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, y_numpad_finished, y_numpad_reset), +}; + +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case TD(TD_COPY_PASTE_APP): + case TD(TD_Y_NUMPAD): + return 300; + default: + return TAPPING_TERM; + } +}