tap-dance: Restructure code and document in more detail (#16394)

This commit is contained in:
Jouke Witteveen 2022-06-13 22:12:55 +02:00 committed by GitHub
parent b17324498e
commit 1706da9054
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 970 additions and 244 deletions

View file

@ -42,7 +42,7 @@ VPATH += \
all: elf
VPATH += $(COMMON_VPATH)
VPATH += $(TEST_PATH) $(COMMON_VPATH)
PLATFORM:=TEST
PLATFORM_KEY:=test
BOOTLOADER_TYPE:=none

View file

@ -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[]`.

View file

@ -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番目の関数が呼び出され、タップダンスの実行をリセットするときに最後の関数が呼び出されます。

View file

@ -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] = {

View file

@ -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] = {

View file

@ -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

View file

@ -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) {

View file

@ -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.

View file

@ -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 {

View file

@ -7,6 +7,7 @@
#undef TAPPING_TERM
#define TAPPING_TERM 175
#define TAPPING_TERM_PER_KEY
#undef DEBOUNCE
#define DEBOUNCE 15

View file

@ -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;
}
}

View file

@ -27,3 +27,5 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define COMBO_ALLOW_ACTION_KEYS
#define COMBO_VARIABLE_LEN
#define TAPPING_TERM_PER_KEY

View file

@ -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;
}
}

View file

@ -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),

View file

@ -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),

View file

@ -21,3 +21,4 @@
// time for long press
#define TAPPING_TERM 200
#define TAPPING_TERM_PER_KEY

View file

@ -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) {

View file

@ -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

View file

@ -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) {

View file

@ -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

View file

@ -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) {

View file

@ -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)
};

View file

@ -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)
};

View file

@ -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)
};

View file

@ -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

View file

@ -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] = {

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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 */

View file

@ -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) {

View file

@ -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] = {

View file

@ -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] = {

View file

@ -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] = {

View file

@ -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] = {

View file

@ -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] = {

View file

@ -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] = {

View file

@ -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] = {

View file

@ -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)

View file

@ -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();
}

View file

@ -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);
}

View file

@ -22,30 +22,27 @@
# include <inttypes.h>
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);

19
tests/tapdance/config.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "test_common.h"

199
tests/tapdance/examples.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

33
tests/tapdance/examples.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
enum {
TD_ESC_CAPS,
CT_EGG,
CT_FLSH,
CT_CLN,
X_CTL,
};
#ifdef __cplusplus
}
#endif

22
tests/tapdance/test.mk Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
# --------------------------------------------------------------------------------
# Keep this file, even if it is empty, as a marker that this folder contains tests
# --------------------------------------------------------------------------------
TAP_DANCE_ENABLE = yes
SRC += examples.c

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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();
}

View file

@ -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;
}
}

View file

@ -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

View file

@ -19,3 +19,4 @@
#define MK_3_SPEED
#define MK_MOMENTARY_ACCEL
#define PERMISSIVE_HOLD
#define TAPPING_TERM 250

View file

@ -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

View file

@ -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
#endif

View file

@ -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),
};
[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;
}
}