From f3245ecfb7e16eb06bbf4624e71874b3386f4f81 Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Fri, 27 Jun 2025 05:37:01 -0400 Subject: [PATCH 01/17] Use less tap dance memory. Use dynamically allocated sparse array for tap dance state, dynamically allocate tap dance state when needed and free it when the tap dance is done. --- docs/ChangeLog/20250824.md | 23 +++++ docs/features/tap_dance.md | 8 +- quantum/process_keycode/process_tap_dance.c | 101 +++++++++++++------- quantum/process_keycode/process_tap_dance.h | 10 +- tests/tap_dance/examples.c | 4 +- 5 files changed, 104 insertions(+), 42 deletions(-) create mode 100644 docs/ChangeLog/20250824.md diff --git a/docs/ChangeLog/20250824.md b/docs/ChangeLog/20250824.md new file mode 100644 index 00000000000..d8a9b0ecd83 --- /dev/null +++ b/docs/ChangeLog/20250824.md @@ -0,0 +1,23 @@ +# QMK Breaking Changes - 2025 August 24 Changelog + +## Notable Features + +### Tap Dance ([#25415](https://github.com/qmk/qmk_firmware/pull/25415)) + +Tap dance state has been removed from `tap_dance_action_t`. Instead, tap dance +state can now be obtained from a method `tap_dance_get_state(int tap_dance_idx)`. + +`action.state->xxxxx` should generally be replaced with: + +```c +tap_dance_state_t* state; +... +state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode)); +... +state->xxxxx +``` + +Eg. [Example 3 in the tap dance documentation](../tap_dance#example-3). + +Tap dance functions that obtain the state as a parameter to one of the callback +methods are unaffected. They will continue to work without changes. diff --git a/docs/features/tap_dance.md b/docs/features/tap_dance.md index d533e41aaaf..8f6887c96e4 100644 --- a/docs/features/tap_dance.md +++ b/docs/features/tap_dance.md @@ -209,11 +209,13 @@ tap_dance_action_t tap_dance_actions[] = { bool process_record_user(uint16_t keycode, keyrecord_t *record) { tap_dance_action_t *action; + tap_dance_state_t* state; switch (keycode) { - case TD(CT_CLN): // list all tap dance keycodes with tap-hold configurations - action = &tap_dance_actions[QK_TAP_DANCE_GET_INDEX(keycode)]; - if (!record->event.pressed && action->state.count && !action->state.finished) { + case TD(CT_CLN): + action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(keycode)); + state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode)); + if (!record->event.pressed && state->count && !state->finished) { tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; tap_code16(tap_hold->tap); } diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index 11df62763dd..c657ddfaa16 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -24,8 +24,33 @@ #include "keymap_introspection.h" static uint16_t active_td; + +// Pointer to array of state pointers. Because the size of the +// array depends on the number of tap dances, it is discovered +// via introspection and is unknown at compile time. It will +// be allocated when first used. +static tap_dance_state_t **tap_dance_states = NULL; + static uint16_t last_tap_time; +tap_dance_state_t *tap_dance_get_state(int tap_dance_idx) { + if (tap_dance_idx >= tap_dance_count()) { + return NULL; + } + if (tap_dance_states == NULL) { + // Dynamic allocation of array of NULL pointers to tap dance states + // This is never freed, this array is initialized once and used forever + tap_dance_states = calloc(tap_dance_count(), sizeof(tap_dance_state_t *)); + } + if (tap_dance_states[tap_dance_idx] == NULL) { + // Dynamic allocation of struct for the tap dance state. + // This memory will be freed when the tap dance is complete + tap_dance_states[tap_dance_idx] = calloc(1, sizeof(tap_dance_state_t)); + tap_dance_states[tap_dance_idx]->index = tap_dance_idx; + } + return tap_dance_states[tap_dance_idx]; +} + void tap_dance_pair_on_each_tap(tap_dance_state_t *state, void *user_data) { tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data; @@ -86,58 +111,62 @@ static inline void _process_tap_dance_action_fn(tap_dance_state_t *state, void * } } -static inline void process_tap_dance_action_on_each_tap(tap_dance_action_t *action) { - action->state.count++; - action->state.weak_mods = get_mods(); - action->state.weak_mods |= get_weak_mods(); +static inline void process_tap_dance_action_on_each_tap(tap_dance_action_t *action, tap_dance_state_t *state) { + state->count++; + state->weak_mods = get_mods(); + state->weak_mods |= get_weak_mods(); #ifndef NO_ACTION_ONESHOT - action->state.oneshot_mods = get_oneshot_mods(); + state->oneshot_mods = get_oneshot_mods(); #endif - _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_tap); + _process_tap_dance_action_fn(state, action->user_data, action->fn.on_each_tap); } -static inline void process_tap_dance_action_on_each_release(tap_dance_action_t *action) { - _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_release); +static inline void process_tap_dance_action_on_each_release(tap_dance_action_t *action, tap_dance_state_t *state) { + _process_tap_dance_action_fn(state, action->user_data, action->fn.on_each_release); } -static inline void process_tap_dance_action_on_reset(tap_dance_action_t *action) { - _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_reset); - del_weak_mods(action->state.weak_mods); +static inline void process_tap_dance_action_on_reset(tap_dance_action_t *action, tap_dance_state_t *state) { + _process_tap_dance_action_fn(state, action->user_data, action->fn.on_reset); + del_weak_mods(state->weak_mods); #ifndef NO_ACTION_ONESHOT - del_mods(action->state.oneshot_mods); + del_mods(state->oneshot_mods); #endif send_keyboard_report(); - action->state = (const tap_dance_state_t){0}; + uint8_t tap_dance_idx = state->index; + free(tap_dance_states[tap_dance_idx]); + tap_dance_states[tap_dance_idx] = NULL; } -static inline void process_tap_dance_action_on_dance_finished(tap_dance_action_t *action) { - if (!action->state.finished) { - action->state.finished = true; - add_weak_mods(action->state.weak_mods); +static inline void process_tap_dance_action_on_dance_finished(tap_dance_action_t *action, tap_dance_state_t *state) { + if (!state->finished) { + state->finished = true; + add_weak_mods(state->weak_mods); #ifndef NO_ACTION_ONESHOT - add_mods(action->state.oneshot_mods); + add_mods(state->oneshot_mods); #endif send_keyboard_report(); - _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_dance_finished); + _process_tap_dance_action_fn(state, action->user_data, action->fn.on_dance_finished); } active_td = 0; - if (!action->state.pressed) { + if (!state->pressed) { // There will not be a key release event, so reset now. - process_tap_dance_action_on_reset(action); + process_tap_dance_action_on_reset(action, state); } } bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { tap_dance_action_t *action; + tap_dance_state_t *state; if (!record->event.pressed) return false; if (!active_td || keycode == active_td) return false; - action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)); - action->state.interrupted = true; - action->state.interrupting_keycode = keycode; - process_tap_dance_action_on_dance_finished(action); + action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)); + state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); + state->interrupted = true; + state->interrupting_keycode = keycode; + process_tap_dance_action_on_dance_finished(action, state); // 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. @@ -153,6 +182,7 @@ bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { int td_index; tap_dance_action_t *action; + tap_dance_state_t *state; switch (keycode) { case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: @@ -161,16 +191,17 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { return false; } action = tap_dance_get(td_index); + state = tap_dance_get_state(td_index); - action->state.pressed = record->event.pressed; + state->pressed = record->event.pressed; if (record->event.pressed) { last_tap_time = timer_read(); - process_tap_dance_action_on_each_tap(action); - active_td = action->state.finished ? 0 : keycode; + process_tap_dance_action_on_each_tap(action, state); + active_td = state->finished ? 0 : keycode; } else { - process_tap_dance_action_on_each_release(action); - if (action->state.finished) { - process_tap_dance_action_on_reset(action); + process_tap_dance_action_on_each_release(action, state); + if (state->finished) { + process_tap_dance_action_on_reset(action, state); if (active_td == keycode) { active_td = 0; } @@ -185,16 +216,18 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { void tap_dance_task(void) { tap_dance_action_t *action; + tap_dance_state_t *state; if (!active_td || timer_elapsed(last_tap_time) <= GET_TAPPING_TERM(active_td, &(keyrecord_t){})) return; action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)); - if (!action->state.interrupted) { - process_tap_dance_action_on_dance_finished(action); + state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); + if (!state->interrupted) { + process_tap_dance_action_on_dance_finished(action, state); } } void reset_tap_dance(tap_dance_state_t *state) { active_td = 0; - process_tap_dance_action_on_reset((tap_dance_action_t *)state); + process_tap_dance_action_on_reset(tap_dance_get(state->index), state); } diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h index 5cccbdf439a..2d04fa985a5 100644 --- a/quantum/process_keycode/process_tap_dance.h +++ b/quantum/process_keycode/process_tap_dance.h @@ -28,15 +28,15 @@ typedef struct { #ifndef NO_ACTION_ONESHOT uint8_t oneshot_mods; #endif - bool pressed : 1; - bool finished : 1; - bool interrupted : 1; + bool pressed : 1; + bool finished : 1; + bool interrupted : 1; + uint8_t index; } tap_dance_state_t; typedef void (*tap_dance_user_fn_t)(tap_dance_state_t *state, void *user_data); typedef struct tap_dance_action_t { - tap_dance_state_t state; struct { tap_dance_user_fn_t on_each_tap; tap_dance_user_fn_t on_dance_finished; @@ -80,6 +80,8 @@ typedef struct { void reset_tap_dance(tap_dance_state_t *state); +tap_dance_state_t *tap_dance_get_state(int tap_dance_idx); + /* To be used internally */ bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record); diff --git a/tests/tap_dance/examples.c b/tests/tap_dance/examples.c index 4b6bdb20908..1d28ba24d90 100644 --- a/tests/tap_dance/examples.c +++ b/tests/tap_dance/examples.c @@ -81,11 +81,13 @@ typedef struct { bool process_record_user(uint16_t keycode, keyrecord_t *record) { tap_dance_action_t *action; + tap_dance_state_t* state; switch (keycode) { case TD(CT_CLN): action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(keycode)); - if (!record->event.pressed && action->state.count && !action->state.finished) { + state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode)); + if (!record->event.pressed && state->count && !state->finished) { tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; tap_code16(tap_hold->tap); } From 2809c68d030b5b73ad4a5fb5e59ceeb2333bfdec Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Fri, 27 Jun 2025 16:08:08 -0400 Subject: [PATCH 02/17] new approach --- docs/ChangeLog/20250824.md | 23 ---------- docs/ChangeLog/20250831/pr25415.md | 3 ++ quantum/process_keycode/process_tap_dance.c | 51 ++++++++++++--------- quantum/process_keycode/process_tap_dance.h | 10 ++-- 4 files changed, 38 insertions(+), 49 deletions(-) delete mode 100644 docs/ChangeLog/20250824.md create mode 100644 docs/ChangeLog/20250831/pr25415.md diff --git a/docs/ChangeLog/20250824.md b/docs/ChangeLog/20250824.md deleted file mode 100644 index d8a9b0ecd83..00000000000 --- a/docs/ChangeLog/20250824.md +++ /dev/null @@ -1,23 +0,0 @@ -# QMK Breaking Changes - 2025 August 24 Changelog - -## Notable Features - -### Tap Dance ([#25415](https://github.com/qmk/qmk_firmware/pull/25415)) - -Tap dance state has been removed from `tap_dance_action_t`. Instead, tap dance -state can now be obtained from a method `tap_dance_get_state(int tap_dance_idx)`. - -`action.state->xxxxx` should generally be replaced with: - -```c -tap_dance_state_t* state; -... -state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode)); -... -state->xxxxx -``` - -Eg. [Example 3 in the tap dance documentation](../tap_dance#example-3). - -Tap dance functions that obtain the state as a parameter to one of the callback -methods are unaffected. They will continue to work without changes. diff --git a/docs/ChangeLog/20250831/pr25415.md b/docs/ChangeLog/20250831/pr25415.md new file mode 100644 index 00000000000..e2cac6cc8ca --- /dev/null +++ b/docs/ChangeLog/20250831/pr25415.md @@ -0,0 +1,3 @@ +# Tap dance state removed from `tap_dance_action_t` + +Code that accessed the tap dance state as a field in the tap dance action should now call `tap_dance_get_state(int tap_dance_idx)` instead. diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index c657ddfaa16..04b710a2e89 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -25,30 +25,40 @@ static uint16_t active_td; -// Pointer to array of state pointers. Because the size of the -// array depends on the number of tap dances, it is discovered -// via introspection and is unknown at compile time. It will -// be allocated when first used. -static tap_dance_state_t **tap_dance_states = NULL; +#ifndef TAP_DANCE_MAX_SIMULTANEOUS +# define TAP_DANCE_MAX_SIMULTANEOUS 3 +#endif + +static tap_dance_state_t tap_dance_states[TAP_DANCE_MAX_SIMULTANEOUS]; static uint16_t last_tap_time; -tap_dance_state_t *tap_dance_get_state(int tap_dance_idx) { +tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx) { + uint8_t i; + uint16_t keycode = QK_TAP_DANCE + tap_dance_idx; if (tap_dance_idx >= tap_dance_count()) { return NULL; } - if (tap_dance_states == NULL) { - // Dynamic allocation of array of NULL pointers to tap dance states - // This is never freed, this array is initialized once and used forever - tap_dance_states = calloc(tap_dance_count(), sizeof(tap_dance_state_t *)); + // Search for a state already used for this case + for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { + if (tap_dance_states[i].keycode == keycode) { + return &tap_dance_states[i]; + } } - if (tap_dance_states[tap_dance_idx] == NULL) { - // Dynamic allocation of struct for the tap dance state. - // This memory will be freed when the tap dance is complete - tap_dance_states[tap_dance_idx] = calloc(1, sizeof(tap_dance_state_t)); - tap_dance_states[tap_dance_idx]->index = tap_dance_idx; + // Search for the first free state + for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { + if (tap_dance_states[i].keycode == 0) { + tap_dance_states[i].keycode = keycode; + return &tap_dance_states[i]; + } } - return tap_dance_states[tap_dance_idx]; + // No states are free, reuse the last state. + // The undefined behavior that causes is + // better than returning null. All the tap dances + // don't get processed correctly, but the keyboard + // doesn't crash when more than the max tap dance + // keys are held at the same time. + return &tap_dance_states[TAP_DANCE_MAX_SIMULTANEOUS - 1]; } void tap_dance_pair_on_each_tap(tap_dance_state_t *state, void *user_data) { @@ -132,9 +142,8 @@ static inline void process_tap_dance_action_on_reset(tap_dance_action_t *action, del_mods(state->oneshot_mods); #endif send_keyboard_report(); - uint8_t tap_dance_idx = state->index; - free(tap_dance_states[tap_dance_idx]); - tap_dance_states[tap_dance_idx] = NULL; + // Clear the tap dance state and mark it as unused + memset(state, 0, sizeof(tap_dance_state_t)); } static inline void process_tap_dance_action_on_dance_finished(tap_dance_action_t *action, tap_dance_state_t *state) { @@ -180,7 +189,7 @@ bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { } bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { - int td_index; + uint8_t td_index; tap_dance_action_t *action; tap_dance_state_t *state; @@ -229,5 +238,5 @@ void tap_dance_task(void) { void reset_tap_dance(tap_dance_state_t *state) { active_td = 0; - process_tap_dance_action_on_reset(tap_dance_get(state->index), state); + process_tap_dance_action_on_reset(tap_dance_get(QK_TAP_DANCE_GET_INDEX(state->keycode)), state); } diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h index 2d04fa985a5..2fd0d1d2e63 100644 --- a/quantum/process_keycode/process_tap_dance.h +++ b/quantum/process_keycode/process_tap_dance.h @@ -28,10 +28,10 @@ typedef struct { #ifndef NO_ACTION_ONESHOT uint8_t oneshot_mods; #endif - bool pressed : 1; - bool finished : 1; - bool interrupted : 1; - uint8_t index; + bool pressed : 1; + bool finished : 1; + bool interrupted : 1; + uint16_t keycode; } tap_dance_state_t; typedef void (*tap_dance_user_fn_t)(tap_dance_state_t *state, void *user_data); @@ -80,7 +80,7 @@ typedef struct { void reset_tap_dance(tap_dance_state_t *state); -tap_dance_state_t *tap_dance_get_state(int tap_dance_idx); +tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx); /* To be used internally */ From a10feeba4d47399ebbd07b0f86047c3230f8b75c Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Fri, 27 Jun 2025 18:10:27 -0400 Subject: [PATCH 03/17] Use null, check for null --- quantum/process_keycode/process_tap_dance.c | 26 ++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index 04b710a2e89..260d1a246ea 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -39,26 +39,21 @@ tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx) { if (tap_dance_idx >= tap_dance_count()) { return NULL; } - // Search for a state already used for this case + // Search for a state already used for this keycode for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { if (tap_dance_states[i].keycode == keycode) { return &tap_dance_states[i]; } } - // Search for the first free state + // Search for the first available state for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { if (tap_dance_states[i].keycode == 0) { tap_dance_states[i].keycode = keycode; return &tap_dance_states[i]; } } - // No states are free, reuse the last state. - // The undefined behavior that causes is - // better than returning null. All the tap dances - // don't get processed correctly, but the keyboard - // doesn't crash when more than the max tap dance - // keys are held at the same time. - return &tap_dance_states[TAP_DANCE_MAX_SIMULTANEOUS - 1]; + // No states are available, tap dance won't happen + return NULL; } void tap_dance_pair_on_each_tap(tap_dance_state_t *state, void *user_data) { @@ -171,8 +166,11 @@ bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { if (!active_td || keycode == active_td) return false; - action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)); - state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); + action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)); + state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); + if (state == NULL) { + return false; + } state->interrupted = true; state->interrupting_keycode = keycode; process_tap_dance_action_on_dance_finished(action, state); @@ -201,7 +199,9 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { } action = tap_dance_get(td_index); state = tap_dance_get_state(td_index); - + if (state == NULL) { + return false; + } state->pressed = record->event.pressed; if (record->event.pressed) { last_tap_time = timer_read(); @@ -231,7 +231,7 @@ void tap_dance_task(void) { action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)); state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); - if (!state->interrupted) { + if (state != NULL && !state->interrupted) { process_tap_dance_action_on_dance_finished(action, state); } } From 39ab15a52fcc0758a693bc0405bdcf5304830250 Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Fri, 27 Jun 2025 18:26:20 -0400 Subject: [PATCH 04/17] Reformat with docker --- quantum/process_keycode/process_tap_dance.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index 260d1a246ea..d4417924544 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -160,7 +160,7 @@ static inline void process_tap_dance_action_on_dance_finished(tap_dance_action_t bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { tap_dance_action_t *action; - tap_dance_state_t *state; + tap_dance_state_t * state; if (!record->event.pressed) return false; @@ -189,7 +189,7 @@ bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { uint8_t td_index; tap_dance_action_t *action; - tap_dance_state_t *state; + tap_dance_state_t * state; switch (keycode) { case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: @@ -225,7 +225,7 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { void tap_dance_task(void) { tap_dance_action_t *action; - tap_dance_state_t *state; + tap_dance_state_t * state; if (!active_td || timer_elapsed(last_tap_time) <= GET_TAPPING_TERM(active_td, &(keyrecord_t){})) return; From 425a4007ce645c2b3a7d1dc275983a973bd04c06 Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Fri, 27 Jun 2025 18:58:49 -0400 Subject: [PATCH 05/17] Use uint8 with idx rather than uint16 with keycode in state --- docs/features/tap_dance.md | 2 +- quantum/process_keycode/process_tap_dance.c | 10 +++++----- quantum/process_keycode/process_tap_dance.h | 9 +++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/features/tap_dance.md b/docs/features/tap_dance.md index 8f6887c96e4..1841fd95c65 100644 --- a/docs/features/tap_dance.md +++ b/docs/features/tap_dance.md @@ -46,7 +46,7 @@ Well, that's the bulk of it! You should now be able to work through the examples Let's go over the three functions mentioned in `ACTION_TAP_DANCE_FN_ADVANCED` in a little more detail. They all receive the same two 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. +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, `½()` 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. diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index d4417924544..0899ce459cc 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -35,20 +35,20 @@ static uint16_t last_tap_time; tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx) { uint8_t i; - uint16_t keycode = QK_TAP_DANCE + tap_dance_idx; if (tap_dance_idx >= tap_dance_count()) { return NULL; } // Search for a state already used for this keycode for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { - if (tap_dance_states[i].keycode == keycode) { + if (tap_dance_states[i].in_use && tap_dance_states[i].index == tap_dance_idx) { return &tap_dance_states[i]; } } // Search for the first available state for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { - if (tap_dance_states[i].keycode == 0) { - tap_dance_states[i].keycode = keycode; + if (!tap_dance_states[i].in_use) { + tap_dance_states[i].index = tap_dance_idx; + tap_dance_states[i].in_use = true; return &tap_dance_states[i]; } } @@ -238,5 +238,5 @@ void tap_dance_task(void) { void reset_tap_dance(tap_dance_state_t *state) { active_td = 0; - process_tap_dance_action_on_reset(tap_dance_get(QK_TAP_DANCE_GET_INDEX(state->keycode)), state); + process_tap_dance_action_on_reset(tap_dance_get(state->index), state); } diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h index 2fd0d1d2e63..5a972cee5ab 100644 --- a/quantum/process_keycode/process_tap_dance.h +++ b/quantum/process_keycode/process_tap_dance.h @@ -28,10 +28,11 @@ typedef struct { #ifndef NO_ACTION_ONESHOT uint8_t oneshot_mods; #endif - bool pressed : 1; - bool finished : 1; - bool interrupted : 1; - uint16_t keycode; + bool pressed : 1; + bool finished : 1; + bool interrupted : 1; + bool in_use : 1; + uint8_t index; } tap_dance_state_t; typedef void (*tap_dance_user_fn_t)(tap_dance_state_t *state, void *user_data); From d5ee94e0b931a0a6f74e2d25b23dfb831c4a03da Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Fri, 27 Jun 2025 19:01:00 -0400 Subject: [PATCH 06/17] fix accidental change --- docs/features/tap_dance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/tap_dance.md b/docs/features/tap_dance.md index 1841fd95c65..8f6887c96e4 100644 --- a/docs/features/tap_dance.md +++ b/docs/features/tap_dance.md @@ -46,7 +46,7 @@ Well, that's the bulk of it! You should now be able to work through the examples Let's go over the three functions mentioned in `ACTION_TAP_DANCE_FN_ADVANCED` in a little more detail. They all receive the same two 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, `½()` periodically checks whether `TAPPING_TERM` has passed since the last key press and finishes a tap dance if that is the case. +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. From 693507d759c0596b08537374cb7b3758a1ec349e Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Fri, 27 Jun 2025 19:05:26 -0400 Subject: [PATCH 07/17] reformat --- quantum/process_keycode/process_tap_dance.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index 0899ce459cc..3b5c9576800 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -34,7 +34,7 @@ static tap_dance_state_t tap_dance_states[TAP_DANCE_MAX_SIMULTANEOUS]; static uint16_t last_tap_time; tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx) { - uint8_t i; + uint8_t i; if (tap_dance_idx >= tap_dance_count()) { return NULL; } @@ -47,7 +47,7 @@ tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx) { // Search for the first available state for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { if (!tap_dance_states[i].in_use) { - tap_dance_states[i].index = tap_dance_idx; + tap_dance_states[i].index = tap_dance_idx; tap_dance_states[i].in_use = true; return &tap_dance_states[i]; } From 24fae67788cf798ed3916fc6dc6ee01822bb8100 Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Sat, 28 Jun 2025 05:46:41 -0400 Subject: [PATCH 08/17] Add null check --- docs/ChangeLog/20250831/pr25415.md | 2 +- docs/features/tap_dance.md | 2 +- tests/tap_dance/examples.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ChangeLog/20250831/pr25415.md b/docs/ChangeLog/20250831/pr25415.md index e2cac6cc8ca..ffa29e2ec4b 100644 --- a/docs/ChangeLog/20250831/pr25415.md +++ b/docs/ChangeLog/20250831/pr25415.md @@ -1,3 +1,3 @@ # Tap dance state removed from `tap_dance_action_t` -Code that accessed the tap dance state as a field in the tap dance action should now call `tap_dance_get_state(int tap_dance_idx)` instead. +Code that accessed the tap dance state as a field in the tap dance action should now call `tap_dance_get_state(int tap_dance_idx)` instead. That function may return `NULL` if many tap dance keys are held together. Add a `NULL` check before using the returned state. diff --git a/docs/features/tap_dance.md b/docs/features/tap_dance.md index 8f6887c96e4..f0e5ddb1742 100644 --- a/docs/features/tap_dance.md +++ b/docs/features/tap_dance.md @@ -215,7 +215,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { case TD(CT_CLN): action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(keycode)); state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode)); - if (!record->event.pressed && state->count && !state->finished) { + if (!record->event.pressed && state != NULL && state->count && !state->finished) { tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; tap_code16(tap_hold->tap); } diff --git a/tests/tap_dance/examples.c b/tests/tap_dance/examples.c index 1d28ba24d90..6aaf0082323 100644 --- a/tests/tap_dance/examples.c +++ b/tests/tap_dance/examples.c @@ -87,7 +87,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { case TD(CT_CLN): action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(keycode)); state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode)); - if (!record->event.pressed && state->count && !state->finished) { + if (!record->event.pressed && state != NULL && state->count && !state->finished) { tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; tap_code16(tap_hold->tap); } From 1958e4d63d54cf57770cce404cfdc4e6e1b233cc Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Sun, 29 Jun 2025 15:40:02 -0400 Subject: [PATCH 09/17] Tapdance config in progmem --- .../ChangeLog/20250831/pr-tapdance-progmem.md | 80 ++++++++++++ docs/features/tap_dance.md | 85 ++++++++----- .../rev1_rgb/keymaps/2moons_rgb/keymap.c | 11 +- .../emptystring/nqg/keymaps/default/keymap.c | 16 ++- .../h0oni/deskpad/keymaps/default/keymap.c | 34 +++-- .../elemental75/keymaps/default/keymap.c | 2 +- .../keymaps/default_tapdance/keymap.c | 12 +- .../concertina/64key/keymaps/default/keymap.c | 12 +- .../myskeeb/keymaps/default/keymap.c | 6 +- .../onekey/keymaps/backlight/keymap.c | 2 +- .../handwired/onekey/keymaps/oled/keymap.c | 2 +- .../handwired/ortho5x14/keymaps/2u/keymap.c | 116 ++++++++++++++---- .../ortho5x14/keymaps/default/keymap.c | 74 ++++++++--- .../ortho5x14/keymaps/split1/keymap.c | 111 +++++++++++++---- .../space_oddity/keymaps/default/keymap.c | 12 +- .../jones/v03/keymaps/default_jp/keymap.c | 12 +- .../jones/v03_1/keymaps/default_ansi/keymap.c | 12 +- .../jones/v03_1/keymaps/default_jp/keymap.c | 12 +- keyboards/karn/keymaps/colemak/keymap.c | 16 ++- .../keyhive/navi10/keymaps/default/keymap.c | 12 +- .../ave/ortho/keymaps/default/keymap.c | 12 +- .../ave/staggered/keymaps/default/keymap.c | 12 +- .../treadstone32/keymaps/default/keymap.c | 16 ++- .../matrix/noah/keymaps/blockader/keymap.c | 16 ++- .../mechwild/bde/lefty/keymaps/fancy/keymap.c | 2 +- keyboards/numatreus/keymaps/like_jis/keymap.c | 16 ++- .../nomu30/keymaps/center_sprit/keymap.c | 14 ++- .../ajisai74/keymaps/jis/keymap.c | 14 ++- .../spaceman/2_milk/keymaps/emoji/keymap.c | 2 +- .../liminal/keymaps/default/keymap.c | 12 +- .../liminal/keymaps/default_iso/keymap.c | 12 +- .../schwann/keymaps/default/keymap.c | 2 +- .../diverge3/keymaps/default/keymap.c | 2 +- .../bigseries/1key/keymaps/leddance/keymap.c | 2 +- .../work_board/keymaps/default/keymap.c | 2 +- .../wsk/gothic50/keymaps/default/keymap.c | 16 ++- .../wsk/gothic70/keymaps/default/keymap.c | 17 ++- quantum/keymap_introspection.c | 2 +- quantum/process_keycode/process_tap_dance.c | 79 ++++++------ quantum/process_keycode/process_tap_dance.h | 22 ++-- tests/tap_dance/examples.c | 44 ++++--- .../tap_dance_layers/tap_dance_defs.c | 11 +- .../tap_dance_layers/tap_dance_defs.h | 9 +- 43 files changed, 725 insertions(+), 250 deletions(-) create mode 100644 docs/ChangeLog/20250831/pr-tapdance-progmem.md diff --git a/docs/ChangeLog/20250831/pr-tapdance-progmem.md b/docs/ChangeLog/20250831/pr-tapdance-progmem.md new file mode 100644 index 00000000000..78608a4eecc --- /dev/null +++ b/docs/ChangeLog/20250831/pr-tapdance-progmem.md @@ -0,0 +1,80 @@ +# Tap dance actions moved to PROGEM + +Tap dance actions are moved to PROGMEM so that they no longer use RAM. + +## Changes for tap dance action array + +Every layout that uses tap dances will need to change + +```c +tap_dance_action_t tap_dance_actions[] = { + +``` + +to + +```c +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + +``` + +## New syntax for `ACTION_TAP_DANCE_DOUBLE` + +`ACTION_TAP_DANCE_DOUBLE` no longer accepts two keycode literals, now it needs a variable containing the key code pair. + + +```c +tap_dance_action_t tap_dance_actions[] = { + [TD_DEL_BSPC] = ACTION_TAP_DANCE_DOUBLE(KC_DEL, KC_BSPC), + [TD_ESC_GRAVE] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_GRAVE), +}; +``` + +to + +```c +enum { + P_DEL_BSPC, + P_ESC_GRAVE, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_DEL_BSPC] = {KC_DEL, KC_BSPC}, + [P_ESC_GRAVE] = {KC_ESC, KC_GRAVE}, +}; + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_DEL_BSPC] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DEL_BSPC]), + [TD_ESC_GRAVE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_ESC_GRAVE]), +}; + +``` + +## New syntax for `ACTION_TAP_DANCE_LAYER_MOVE` and `ACTION_TAP_DANCE_LAYER_TOGGLE` + + +```c +tap_dance_action_t tap_dance_actions[] = { + [TD_Q_ALT] = ACTION_TAP_DANCE_LAYER_MOVE(KC_Q, _ALT), + [TD_R_RAISE] = ACTION_TAP_DANCE_LAYER_TOGGLE(KC_R, _RAISE), +}; +``` + +to + +```c +enum { + DR_Q_ALT, + DR_R_RAISE, +}; + +const tap_dance_dual_role_t tap_dance_dual_roles[] PROGMEM = { + [DR_Q_ALT] = DUAL_ROLE_TAP_DANCE_LAYER_MOVE(KC_Q, _ALT), + [DR_R_RAISE] = DUAL_ROLE_TAP_DANCE_LAYER_TOGGLE(KC_R, _RAISE), +}; + +tap_dance_action_t tap_dance_actions[] = { + [TD_Q_ALT] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[DR_Q_ALT]), + [TD_R_RAISE] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[DR_R_RAISE]), +}; +``` diff --git a/docs/features/tap_dance.md b/docs/features/tap_dance.md index f0e5ddb1742..730bdd164e6 100644 --- a/docs/features/tap_dance.md +++ b/docs/features/tap_dance.md @@ -21,16 +21,17 @@ The `TAPPING_TERM` time is the maximum time allowed between taps of your Tap Dan 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: +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 several 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). -* `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_DOUBLE(pair)`: Define keycode pairs in a separate progmem array. With the pair `{kc1, kc2}`, it 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_DUAL_ROLE(dual_role)`: Define dual roles in a separate progmem array. Using the following: + * `DUAL_ROLE_TAP_DANCE_LAYER_MOVE(kc, layer)`: Sends the `kc` keycode when tapped once, or moves to `layer`. (this functions like the `TO` layer keycode). + * `DUAL_ROLE_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: Sends the `kc` keycode when tapped once, or toggles the state of `layer`. (this functions like the `TG` layer keycode). * `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the final tap count of the tap dance action. * `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: Calls the first specified function - defined in the user keymap - on every tap, the second function when the dance action finishes (like the previous option), and the last function when the tap dance action resets. * `ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(on_each_tap_fn, on_each_release_fn, on_dance_finished_fn, on_dance_reset_fn)`: This macro is identical to `ACTION_TAP_DANCE_FN_ADVANCED` with the addition of `on_each_release_fn` which is invoked every time the key for the tap dance is released. It is worth noting that `on_each_release_fn` will still be called even when the key is released after the dance finishes (e.g. if the key is released after being pressed and held for longer than the `TAPPING_TERM`). -The first option is enough for a lot of cases, that just want dual roles. For example, `ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT)` will result in `Space` being sent on single-tap, `Enter` otherwise. +The first option is enough for a lot of cases that just want dual roles. ::: warning Keep in mind that only [basic keycodes](../keycodes_basic) are supported here. Custom keycodes are not supported. @@ -60,15 +61,25 @@ Here's a simple example for a single definition: 2. In your `keymap.c` file, define the variables and definitions, then add to your keymap: ```c -// Tap Dance declarations +// Tap dance pair declarations +enum { + P_ESC_CAPS, +}; + +// Tap dance pair definitions +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_ESC_CAPS] = {KC_ESC, KC_CAPS}, +}; + +// Tap dance declarations enum { TD_ESC_CAPS, }; -// Tap Dance definitions -tap_dance_action_t tap_dance_actions[] = { +// Tap dance action definitions +const tap_dance_action_t tap_dance_actions[] PROGMEM = { // Tap once for Escape, twice for Caps Lock - [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), + [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_ESC_CAPS]), }; // Add tap dance item to your keymap in place of a keycode @@ -105,7 +116,7 @@ void dance_egg(tap_dance_state_t *state, void *user_data) { } } -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [CT_EGG] = ACTION_TAP_DANCE_FN(dance_egg), }; ``` @@ -152,8 +163,8 @@ void dance_flsh_reset(tap_dance_state_t *state, void *user_data) { } // All tap dances now put together. Example 2 is "CT_FLSH" -tap_dance_action_t tap_dance_actions[] = { - [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_ESC_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) }; @@ -167,11 +178,17 @@ With a little effort, powerful tap-hold configurations can be implemented as tap typedef struct { uint16_t tap; uint16_t hold; - uint16_t held; } tap_dance_tap_hold_t; +typedef struct { + uint16_t held; +} tap_dance_tap_hold_state_t; + +static tap_dance_tap_hold_state_t tap_dance_tap_hold_states[TAP_DANCE_MAX_SIMULTANEOUS]; + void tap_dance_tap_hold_finished(tap_dance_state_t *state, void *user_data) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + tap_dance_tap_hold_t tap_hold; + memcpy_P(&tap_hold, user_data, sizeof(tap_dance_tap_hold_t)); if (state->pressed) { if (state->count == 1 @@ -179,45 +196,45 @@ void tap_dance_tap_hold_finished(tap_dance_state_t *state, void *user_data) { && !state->interrupted #endif ) { - register_code16(tap_hold->hold); - tap_hold->held = tap_hold->hold; + register_code16(tap_hold.hold); + tap_dance_tap_hold_states[state->state_idx].held = tap_hold.hold; } else { - register_code16(tap_hold->tap); - tap_hold->held = tap_hold->tap; + register_code16(tap_hold.tap); + tap_dance_tap_hold_states[state->state_idx].held = tap_hold.tap; } } } void tap_dance_tap_hold_reset(tap_dance_state_t *state, void *user_data) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + tap_dance_tap_hold_t tap_hold; + memcpy_P(&tap_hold, user_data, sizeof(tap_dance_tap_hold_t)); - if (tap_hold->held) { - unregister_code16(tap_hold->held); - tap_hold->held = 0; + if (tap_dance_tap_hold_states[state->state_idx].held) { + unregister_code16(tap_dance_tap_hold_states[state->state_idx].held); + tap_dance_tap_hold_states[state->state_idx].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}), \ - } +#define ACTION_TAP_DANCE_TAP_HOLD(tap_hold) {{NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, (void *)&(tap_hold)} -tap_dance_action_t tap_dance_actions[] = { - [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN), +const tap_dance_tap_hold_t d_ct_cln PROGMEM = {KC_COLN, KC_SCLN}; + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(d_ct_cln), }; bool process_record_user(uint16_t keycode, keyrecord_t *record) { tap_dance_action_t *action; tap_dance_state_t* state; + tap_dance_tap_hold_t tap_hold; switch (keycode) { case TD(CT_CLN): action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(keycode)); state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode)); if (!record->event.pressed && state != NULL && state->count && !state->finished) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; - tap_code16(tap_hold->tap); + memcpy_P(&tap_hold, (tap_dance_tap_hold_t *)action->user_data, sizeof(tap_dance_tap_hold_t)); + tap_code16(tap_hold.tap); } } return true; @@ -356,7 +373,7 @@ void x_reset(tap_dance_state_t *state, void *user_data) { xtap_state.state = TD_NONE; } -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [X_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset) }; ``` @@ -452,7 +469,7 @@ void altlp_reset(tap_dance_state_t *state, void *user_data) { } // Define `ACTION_TAP_DANCE_FN_ADVANCED()` for each tapdance keycode, passing in `finished` and `reset` functions -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [ALT_LP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, altlp_finished, altlp_reset) }; ``` @@ -546,7 +563,7 @@ void ql_reset(tap_dance_state_t *state, void *user_data) { } // Associate our tap dance key with its functionality -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [QUOT_LAYR] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset) }; 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 bd7923b36bd..b7a20455c5a 100644 --- a/keyboards/basekeys/slice/rev1_rgb/keymaps/2moons_rgb/keymap.c +++ b/keyboards/basekeys/slice/rev1_rgb/keymaps/2moons_rgb/keymap.c @@ -38,9 +38,14 @@ enum tapdances{ TD_ESQW, }; -tap_dance_action_t tap_dance_actions[] = { - [TD_ESFL] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _FLOCK), - [TD_ESQW] = ACTION_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), +const tap_dance_dual_role_t tap_dance_dual_roles[] PROGMEM = { + [TD_ESFL] = DUAL_ROLE_TAP_DANCE_LAYER_MOVE(KC_ESC, _FLOCK), + [TD_ESQW] = DUAL_ROLE_TAP_DANCE_LAYER_MOVE(KC_ESC, _QWERTY), +}; + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_ESFL] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[TD_ESFL]), + [TD_ESQW] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[TD_ESQW]), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/emptystring/nqg/keymaps/default/keymap.c b/keyboards/emptystring/nqg/keymaps/default/keymap.c index 9b87225f19d..0a8cc7f4167 100644 --- a/keyboards/emptystring/nqg/keymaps/default/keymap.c +++ b/keyboards/emptystring/nqg/keymaps/default/keymap.c @@ -28,12 +28,18 @@ enum tapdances{ #define KC_DSTP LGUI(KC_D) #define KC_ESLO LT(_LOWER, KC_ESC) +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [TD_SCCL] = {KC_SCLN, KC_QUOT}, + [TD_ENSL] = {KC_SLSH, KC_ENT}, + [TD_N0BS] = {KC_0, KC_BSLS}, + [TD_RPPI] = {KC_RPRN, KC_PIPE}, +}; -tap_dance_action_t tap_dance_actions[] = { - [TD_SCCL] = ACTION_TAP_DANCE_DOUBLE(KC_SCLN, KC_QUOT), - [TD_ENSL] = ACTION_TAP_DANCE_DOUBLE(KC_SLSH, KC_ENT), - [TD_N0BS] = ACTION_TAP_DANCE_DOUBLE(KC_0, KC_BSLS), - [TD_RPPI] = ACTION_TAP_DANCE_DOUBLE(KC_RPRN, KC_PIPE), +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_SCCL] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[TD_SCCL]), + [TD_ENSL] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[TD_ENSL]), + [TD_N0BS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[TD_N0BS]), + [TD_RPPI] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[TD_RPPI]), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/h0oni/deskpad/keymaps/default/keymap.c b/keyboards/h0oni/deskpad/keymaps/default/keymap.c index eea54cf6b19..63b25413113 100644 --- a/keyboards/h0oni/deskpad/keymaps/default/keymap.c +++ b/keyboards/h0oni/deskpad/keymaps/default/keymap.c @@ -112,13 +112,29 @@ void cvxa_reset(tap_dance_state_t *state, void *user_data) { cvxa_state.state = TD_NONE; } +enum { + P_CUT_REDO, + P_PLAY_PAUSE_MUTE, + P_MNXT_RIGHT, + P_MPRV_LEFT, + P_SEARCH_REFRESH, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_CUT_REDO] = {C(KC_Z), S(C(KC_Z))}, + [P_PLAY_PAUSE_MUTE] = {KC_MPLY, KC_MUTE}, + [P_MNXT_RIGHT] = {KC_MNXT, KC_RIGHT}, + [P_MPRV_LEFT] = {KC_MPRV, KC_LEFT}, + [P_SEARCH_REFRESH] = {KC_WREF, KC_WSCH}, +}; + // Tap Dance definitions -tap_dance_action_t tap_dance_actions[] = { - [TD_CUT_REDO] = ACTION_TAP_DANCE_DOUBLE(C(KC_Z), S(C(KC_Z))), - [TD_PLAY_PAUSE_MUTE] = ACTION_TAP_DANCE_DOUBLE(KC_MPLY, KC_MUTE), - [TD_MNXT_RIGHT] = ACTION_TAP_DANCE_DOUBLE(KC_MNXT, KC_RIGHT), - [TD_MPRV_LEFT] = ACTION_TAP_DANCE_DOUBLE(KC_MPRV, KC_LEFT), - [TD_SEARCH_REFRESH] = ACTION_TAP_DANCE_DOUBLE(KC_WREF, KC_WSCH), +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_CUT_REDO] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_CUT_REDO]), + [TD_PLAY_PAUSE_MUTE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_PLAY_PAUSE_MUTE]), + [TD_MNXT_RIGHT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_MNXT_RIGHT]), + [TD_MPRV_LEFT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_MPRV_LEFT]), + [TD_SEARCH_REFRESH] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SEARCH_REFRESH]), [QUAD_LAYER_SWITCH] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, layer_finished, layer_reset), [QUAD_CVXA] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, cvxa_finished, cvxa_reset) }; @@ -167,7 +183,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) { break; } return true; -}; +}; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { @@ -182,8 +198,8 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [2] = LAYOUT( A(KC_F4), SGUI(KC_S), KC_MYCM, LCA(KC_DEL), KC_CALC, TD(QUAD_LAYER_SWITCH) ), - + [3] = LAYOUT( C(KC_SLSH), VALORANT, VSCODE, DISCORD, LSA(KC_A), TD(QUAD_LAYER_SWITCH) ), -}; \ No newline at end of file +}; diff --git a/keyboards/halokeys/elemental75/keymaps/default/keymap.c b/keyboards/halokeys/elemental75/keymaps/default/keymap.c index 7e63089210f..1c65a54ca61 100644 --- a/keyboards/halokeys/elemental75/keymaps/default/keymap.c +++ b/keyboards/halokeys/elemental75/keymaps/default/keymap.c @@ -47,7 +47,7 @@ void dance_cln_finished(tap_dance_state_t *state, void *user_data) { } /* All tap dance functions would go here. Only showing this one. */ -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_PLAY_FORWARD_BACK] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, dance_cln_finished, NULL), }; diff --git a/keyboards/handwired/2x5keypad/keymaps/default_tapdance/keymap.c b/keyboards/handwired/2x5keypad/keymaps/default_tapdance/keymap.c index 9fddc59cd75..4a1538440c3 100644 --- a/keyboards/handwired/2x5keypad/keymaps/default_tapdance/keymap.c +++ b/keyboards/handwired/2x5keypad/keymaps/default_tapdance/keymap.c @@ -90,7 +90,7 @@ void dance_u_u(tap_dance_state_t *state, void *user_data) } /* Define the tap dance actions for the french characters */ -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [A_Q] = ACTION_TAP_DANCE_FN(dance_a_q), [E_Q] = ACTION_TAP_DANCE_FN(dance_e_q), [E_U] = ACTION_TAP_DANCE_FN(dance_e_u), @@ -101,21 +101,21 @@ tap_dance_action_t tap_dance_actions[] = { [O_C] = ACTION_TAP_DANCE_FN(dance_o_c), [U_U] = ACTION_TAP_DANCE_FN(dance_u_u) }; - + const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { - [NORMAL_LAYER] = LAYOUT(TO(1), WIN_TAB, KC_HOME, KC_UP, KC_END, + [NORMAL_LAYER] = LAYOUT(TO(1), WIN_TAB, KC_HOME, KC_UP, KC_END, WIN_LOCK, KC_MUTE, KC_LEFT, KC_DOWN, KC_RGHT), - [MEDIA_LAYER] = LAYOUT(TO(2), KC_CALC, KC_MPRV, KC_MNXT, KC_VOLU, + [MEDIA_LAYER] = LAYOUT(TO(2), KC_CALC, KC_MPRV, KC_MNXT, KC_VOLU, KC_TRNS, KC_TRNS, KC_MSTP, KC_MPLY, KC_VOLD), - [TBD_LAYER2] = LAYOUT(TO(3), KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, + [TBD_LAYER2] = LAYOUT(TO(3), KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS), - [FRENCH_LAYER] = LAYOUT(TO(0), TD(A_Q), TD(E_Q), TD(E_U), TD(E_E), + [FRENCH_LAYER] = LAYOUT(TO(0), TD(A_Q), TD(E_Q), TD(E_U), TD(E_E), KC_TRNS, TD(A_Y), TD(I_I), TD(O_C), TD(U_U)) }; diff --git a/keyboards/handwired/concertina/64key/keymaps/default/keymap.c b/keyboards/handwired/concertina/64key/keymaps/default/keymap.c index c9f63844400..8487a208bc8 100644 --- a/keyboards/handwired/concertina/64key/keymaps/default/keymap.c +++ b/keyboards/handwired/concertina/64key/keymaps/default/keymap.c @@ -29,8 +29,16 @@ enum taps { PNX, // Play/pause; next track. }; -tap_dance_action_t tap_dance_actions[] = { - [PNX] = ACTION_TAP_DANCE_DOUBLE(KC_MEDIA_PLAY_PAUSE, KC_MEDIA_NEXT_TRACK), +enum { + P_PNX, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_PNX] = {KC_MEDIA_PLAY_PAUSE, KC_MEDIA_NEXT_TRACK}, +}; + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [PNX] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_PNX]), }; // Shorthand: diff --git a/keyboards/handwired/myskeeb/keymaps/default/keymap.c b/keyboards/handwired/myskeeb/keymaps/default/keymap.c index d3a8ba20e8d..42ee7f6352f 100644 --- a/keyboards/handwired/myskeeb/keymaps/default/keymap.c +++ b/keyboards/handwired/myskeeb/keymaps/default/keymap.c @@ -53,9 +53,9 @@ void tilded(tap_dance_state_t *state, void *user_data) { else tap_code16(KC_AT); } -} - -tap_dance_action_t tap_dance_actions[] = { +} + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [OP_QT] = ACTION_TAP_DANCE_FN(tri_open), [CL_QT] = ACTION_TAP_DANCE_FN(tri_close), [TD_DQ] = ACTION_TAP_DANCE_FN(dquote), diff --git a/keyboards/handwired/onekey/keymaps/backlight/keymap.c b/keyboards/handwired/onekey/keymaps/backlight/keymap.c index 746e4619de5..075dd1cae86 100644 --- a/keyboards/handwired/onekey/keymaps/backlight/keymap.c +++ b/keyboards/handwired/onekey/keymaps/backlight/keymap.c @@ -30,7 +30,7 @@ void dance_cln_reset(tap_dance_state_t *state, void *user_data) { } } -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_BL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, dance_cln_finished, dance_cln_reset) }; diff --git a/keyboards/handwired/onekey/keymaps/oled/keymap.c b/keyboards/handwired/onekey/keymaps/oled/keymap.c index 120ab80fa5e..c6d046a8801 100644 --- a/keyboards/handwired/onekey/keymaps/oled/keymap.c +++ b/keyboards/handwired/onekey/keymaps/oled/keymap.c @@ -155,7 +155,7 @@ static void dance_oled_finished(tap_dance_state_t *state, void *user_data) { } } -tap_dance_action_t tap_dance_actions[] = {[TD_OLED] = ACTION_TAP_DANCE_FN(dance_oled_finished)}; +const tap_dance_action_t tap_dance_actions[] PROGMEM = {[TD_OLED] = ACTION_TAP_DANCE_FN(dance_oled_finished)}; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {LAYOUT_ortho_1x1(TD(TD_OLED))}; diff --git a/keyboards/handwired/ortho5x14/keymaps/2u/keymap.c b/keyboards/handwired/ortho5x14/keymaps/2u/keymap.c index 752eda0fde7..a6aa36c35d6 100644 --- a/keyboards/handwired/ortho5x14/keymaps/2u/keymap.c +++ b/keyboards/handwired/ortho5x14/keymaps/2u/keymap.c @@ -315,40 +315,110 @@ void lspace_reset (tap_dance_state_t *state, void *user_data) { //TD_RSPACE +enum { + P_DEL_BSPC, + P_ESC_GRAVE, + P_TAB_TILDE, + + P_9_LPRN, + P_0_RPRN, + + P_MINS_UNDS, + P_EQL_PLUS, + + P_LBRC_LCBR, + P_RBRC_RCBR, + + P_SCLN_COLN, + P_QUOT_DQT, + + P_COMM_LABK, + P_DOT_RABK, + P_SLSH_QUES, + P_BSLS_PIPE, + + P_PGUP_HOME, + P_PGDN_END, + + P_SPC_ENT, + P_SPC_BKSPC, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_DEL_BSPC] = {KC_DEL, KC_BSPC}, + [P_ESC_GRAVE] = {KC_ESC, KC_GRAVE}, + [P_TAB_TILDE] = {KC_TAB, KC_TILDE}, + + [P_9_LPRN] = {KC_9, KC_LPRN}, + [P_0_RPRN] = {KC_0, KC_RPRN}, + + [P_MINS_UNDS] = {KC_MINS, KC_UNDS}, + [P_EQL_PLUS] = {KC_EQL, KC_PLUS}, + + [P_LBRC_LCBR] = {KC_LBRC, KC_LCBR}, + [P_RBRC_RCBR] = {KC_RBRC, KC_RCBR}, + + [P_SCLN_COLN] = {KC_SCLN, KC_COLN}, + [P_QUOT_DQT] = {KC_QUOT, KC_DQT}, + + [P_COMM_LABK] = {KC_COMM, KC_LABK}, + [P_DOT_RABK] = {KC_DOT, KC_RABK}, + [P_SLSH_QUES] = {KC_SLSH, KC_QUES}, + [P_BSLS_PIPE] = {KC_BSLS, KC_PIPE}, + + [P_PGUP_HOME] = {KC_PGUP, KC_HOME}, + [P_PGDN_END] = {KC_PGUP, KC_END}, + + [P_SPC_ENT] = {KC_SPACE, KC_ENT}, + [P_SPC_BKSPC] = {KC_SPACE, KC_BSPC}, +}; + + +enum { + DR_Q_LrALT, + DR_R_LrKey, + DR_T_LrMS, +}; + +const tap_dance_dual_role_t tap_dance_dual_roles[] PROGMEM = { + [DR_Q_LrALT] = DUAL_ROLE_TAP_DANCE_LAYER_MOVE(KC_Q, _ALT), + [DR_R_LrKey] = DUAL_ROLE_TAP_DANCE_LAYER_MOVE(KC_R, _RAISE), + [DR_T_LrMS] = DUAL_ROLE_TAP_DANCE_LAYER_MOVE(KC_T, _MOUSE), +}; //Tap Dance Definitions -tap_dance_action_t tap_dance_actions[] = { - [TD_DEL_BSPC] = ACTION_TAP_DANCE_DOUBLE(KC_DEL, KC_BSPC), - [TD_ESC_GRAVE] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_GRAVE), - [TD_TAB_TILDE] = ACTION_TAP_DANCE_DOUBLE(KC_TAB, KC_TILDE), +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_DEL_BSPC] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DEL_BSPC]), + [TD_ESC_GRAVE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_ESC_GRAVE]), + [TD_TAB_TILDE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_TAB_TILDE]), - [TD_9_LPRN] = ACTION_TAP_DANCE_DOUBLE(KC_9, KC_LPRN), - [TD_0_RPRN] = ACTION_TAP_DANCE_DOUBLE(KC_0, KC_RPRN), + [TD_9_LPRN] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_9_LPRN]), + [TD_0_RPRN] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_0_RPRN]), - [TD_MINS_UNDS] = ACTION_TAP_DANCE_DOUBLE(KC_MINS, KC_UNDS), - [TD_EQL_PLUS] = ACTION_TAP_DANCE_DOUBLE(KC_EQL, KC_PLUS), + [TD_MINS_UNDS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_MINS_UNDS]), + [TD_EQL_PLUS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_EQL_PLUS]), - [TD_LBRC_LCBR] = ACTION_TAP_DANCE_DOUBLE(KC_LBRC, KC_LCBR), - [TD_RBRC_RCBR] = ACTION_TAP_DANCE_DOUBLE(KC_RBRC, KC_RCBR), + [TD_LBRC_LCBR] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_LBRC_LCBR]), + [TD_RBRC_RCBR] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_RBRC_RCBR]), - [TD_SCLN_COLN] = ACTION_TAP_DANCE_DOUBLE(KC_SCLN, KC_COLN), - [TD_QUOT_DQT] = ACTION_TAP_DANCE_DOUBLE(KC_QUOT, KC_DQT), + [TD_SCLN_COLN] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SCLN_COLN]), + [TD_QUOT_DQT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_QUOT_DQT]), - [TD_COMM_LABK] = ACTION_TAP_DANCE_DOUBLE(KC_COMM, KC_LABK), - [TD_DOT_RABK] = ACTION_TAP_DANCE_DOUBLE(KC_DOT, KC_RABK), - [TD_SLSH_QUES] = ACTION_TAP_DANCE_DOUBLE(KC_SLSH, KC_QUES), - [TD_BSLS_PIPE] = ACTION_TAP_DANCE_DOUBLE(KC_BSLS, KC_PIPE), + [TD_COMM_LABK] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_COMM_LABK]), + [TD_DOT_RABK] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DOT_RABK]), + [TD_SLSH_QUES] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SLSH_QUES]), + [TD_BSLS_PIPE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_BSLS_PIPE]), - [TD_PGUP_HOME] = ACTION_TAP_DANCE_DOUBLE(KC_PGUP, KC_HOME), - [TD_PGDN_END] = ACTION_TAP_DANCE_DOUBLE(KC_PGDN, KC_END), + [TD_PGUP_HOME] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_PGUP_HOME]), + [TD_PGDN_END] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_PGDN_END]), - [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_Q_LrALT] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[DR_Q_LrALT]), + [TD_R_LrKey] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[DR_R_LrKey]), + [TD_T_LrMS] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[DR_T_LrMS]), [TD_SHIFT_CAPS] = ACTION_TAP_DANCE_FN_ADVANCED(NULL,lshift_finished, lshift_reset), - [TD_SPC_ENT] = ACTION_TAP_DANCE_DOUBLE(KC_SPACE, KC_ENT), - [TD_SPC_BKSPC] = ACTION_TAP_DANCE_DOUBLE(KC_SPACE, KC_BSPC), + [TD_SPC_ENT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SPC_ENT]), + [TD_SPC_BKSPC] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SPC_BKSPC]), [TD_LSPACE] = ACTION_TAP_DANCE_FN_ADVANCED(NULL,lspace_finished,lspace_reset), [ALT_OSL1] = ACTION_TAP_DANCE_FN_ADVANCED(NULL,alt_finished, alt_reset), diff --git a/keyboards/handwired/ortho5x14/keymaps/default/keymap.c b/keyboards/handwired/ortho5x14/keymaps/default/keymap.c index a2f7befd9fc..43b61a5bb42 100644 --- a/keyboards/handwired/ortho5x14/keymaps/default/keymap.c +++ b/keyboards/handwired/ortho5x14/keymaps/default/keymap.c @@ -72,27 +72,71 @@ void dance_onshot_lsft(tap_dance_state_t *state, void *user_data) { } } +enum { + P_DEL_BSPC, + P_ESC_GRAVE, + P_TAB_TILDE, + + P_MINS_UNDS, + P_EQL_PLUS, + + P_LBRC_LCBR, + P_RBRC_RCBR, + + P_SCLN_COLN, + P_QUOT_DQT, + + P_COMM_LABK, + P_DOT_RABK, + P_SLSH_QUES, + P_BSLS_PIPE, + + P_3_F3, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_DEL_BSPC] = {KC_DEL, KC_BSPC}, + [P_ESC_GRAVE] = {KC_ESC, KC_GRAVE}, + [P_TAB_TILDE] = {KC_TAB, KC_TILDE}, + + [P_MINS_UNDS] = {KC_MINS, KC_UNDS}, + [P_EQL_PLUS] = {KC_EQL, KC_PLUS}, + + [P_LBRC_LCBR] = {KC_LBRC, KC_LCBR}, + [P_RBRC_RCBR] = {KC_RBRC, KC_RCBR}, + + [P_SCLN_COLN] = {KC_SCLN, KC_COLN}, + [P_QUOT_DQT] = {KC_QUOT, KC_DQT}, + + [P_COMM_LABK] = {KC_COMM, KC_LABK}, + [P_DOT_RABK] = {KC_DOT, KC_RABK}, + [P_SLSH_QUES] = {KC_SLSH, KC_QUES}, + [P_BSLS_PIPE] = {KC_BSLS, KC_PIPE}, + + [P_3_F3] = {KC_3, KC_F3}, +}; + //Tap Dance Definitions -tap_dance_action_t tap_dance_actions[] = { - [TD_DEL_BSPC] = ACTION_TAP_DANCE_DOUBLE(KC_DEL, KC_BSPC), - [TD_ESC_GRAVE] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_GRAVE), - [TD_TAB_TILDE] = ACTION_TAP_DANCE_DOUBLE(KC_TAB, KC_TILDE), +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_DEL_BSPC] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DEL_BSPC]), + [TD_ESC_GRAVE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_ESC_GRAVE]), + [TD_TAB_TILDE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_TAB_TILDE]), - [TD_MINS_UNDS] = ACTION_TAP_DANCE_DOUBLE(KC_MINS, KC_UNDS), - [TD_EQL_PLUS] = ACTION_TAP_DANCE_DOUBLE(KC_EQL, KC_PLUS), + [TD_MINS_UNDS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_MINS_UNDS]), + [TD_EQL_PLUS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_EQL_PLUS]), - [TD_LBRC_LCBR] = ACTION_TAP_DANCE_DOUBLE(KC_LBRC, KC_LCBR), - [TD_RBRC_RCBR] = ACTION_TAP_DANCE_DOUBLE(KC_RBRC, KC_RCBR), + [TD_LBRC_LCBR] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_LBRC_LCBR]), + [TD_RBRC_RCBR] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_RBRC_RCBR]), - [TD_SCLN_COLN] = ACTION_TAP_DANCE_DOUBLE(KC_SCLN, KC_COLN), - [TD_QUOT_DQT] = ACTION_TAP_DANCE_DOUBLE(KC_QUOT, KC_DQT), + [TD_SCLN_COLN] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SCLN_COLN]), + [TD_QUOT_DQT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_QUOT_DQT]), - [TD_COMM_LABK] = ACTION_TAP_DANCE_DOUBLE(KC_COMM, KC_LABK), - [TD_DOT_RABK] = ACTION_TAP_DANCE_DOUBLE(KC_DOT, KC_RABK), - [TD_SLSH_QUES] = ACTION_TAP_DANCE_DOUBLE(KC_SLSH, KC_QUES), - [TD_BSLS_PIPE] = ACTION_TAP_DANCE_DOUBLE(KC_BSLS, KC_PIPE), + [TD_COMM_LABK] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_COMM_LABK]), + [TD_DOT_RABK] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DOT_RABK]), + [TD_SLSH_QUES] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SLSH_QUES]), + [TD_BSLS_PIPE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_BSLS_PIPE]), - [TD_3_F3] = ACTION_TAP_DANCE_DOUBLE(KC_3, KC_F3), + [TD_3_F3] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_3_F3]), [TD_SHIFT_CAPS] = ACTION_TAP_DANCE_FN(dance_onshot_lsft) }; diff --git a/keyboards/handwired/ortho5x14/keymaps/split1/keymap.c b/keyboards/handwired/ortho5x14/keymaps/split1/keymap.c index a5222eecfaa..4ca9b380a36 100644 --- a/keyboards/handwired/ortho5x14/keymaps/split1/keymap.c +++ b/keyboards/handwired/ortho5x14/keymaps/split1/keymap.c @@ -250,45 +250,110 @@ void lshift_reset (tap_dance_state_t *state, void *user_data) { lshifttap_state.state = 0; } +enum { + P_DEL_BSPC, + P_ESC_GRAVE, + P_TAB_TILDE, + P_9_LPRN, + P_0_RPRN, + + P_MINS_UNDS, + P_EQL_PLUS, + + P_LBRC_LCBR, + P_RBRC_RCBR, + + P_SCLN_COLN, + P_QUOT_DQT, + + P_COMM_LABK, + P_DOT_RABK, + P_SLSH_QUES, + P_BSLS_PIPE, + + P_PGUP_HOME, + P_PGDN_END, + + P_SPC_ENT, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_DEL_BSPC] = {KC_DEL, KC_BSPC}, + [P_ESC_GRAVE] = {KC_ESC, KC_GRAVE}, + [P_TAB_TILDE] = {KC_TAB, KC_TILDE}, + + [P_9_LPRN] = {KC_9, KC_LPRN}, + [P_0_RPRN] = {KC_0, KC_RPRN}, + + [P_MINS_UNDS] = {KC_MINS, KC_UNDS}, + [P_EQL_PLUS] = {KC_EQL, KC_PLUS}, + + [P_LBRC_LCBR] = {KC_LBRC, KC_LCBR}, + [P_RBRC_RCBR] = {KC_RBRC, KC_RCBR}, + + [P_SCLN_COLN] = {KC_SCLN, KC_COLN}, + [P_QUOT_DQT] = {KC_QUOT, KC_DQT}, + + [P_COMM_LABK] = {KC_COMM, KC_LABK}, + [P_DOT_RABK] = {KC_DOT, KC_RABK}, + [P_SLSH_QUES] = {KC_SLSH, KC_QUES}, + [P_BSLS_PIPE] = {KC_BSLS, KC_PIPE}, + + [P_PGUP_HOME] = {KC_PGUP, KC_HOME}, + [P_PGDN_END] = {KC_PGUP, KC_END}, + + [P_SPC_ENT] = {KC_SPACE, KC_ENT}, +}; + +enum { + DR_Q_LrALT, + DR_R_LrKey, + DR_T_LrMS, +}; + +const tap_dance_dual_role_t tap_dance_dual_roles[] PROGMEM = { + [DR_Q_LrALT] = DUAL_ROLE_TAP_DANCE_LAYER_MOVE(KC_Q, _ALT), + [DR_R_LrKey] = DUAL_ROLE_TAP_DANCE_LAYER_MOVE(KC_R, _RAISE), + [DR_T_LrMS] = DUAL_ROLE_TAP_DANCE_LAYER_MOVE(KC_T, _MOUSE), +}; //Tap Dance Definitions -tap_dance_action_t tap_dance_actions[] = { - [TD_DEL_BSPC] = ACTION_TAP_DANCE_DOUBLE(KC_DEL, KC_BSPC), - [TD_ESC_GRAVE] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_GRAVE), - [TD_TAB_TILDE] = ACTION_TAP_DANCE_DOUBLE(KC_TAB, KC_TILDE), +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_DEL_BSPC] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DEL_BSPC]), + [TD_ESC_GRAVE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_ESC_GRAVE]), + [TD_TAB_TILDE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_TAB_TILDE]), - [TD_9_LPRN] = ACTION_TAP_DANCE_DOUBLE(KC_9, KC_LPRN), - [TD_0_RPRN] = ACTION_TAP_DANCE_DOUBLE(KC_0, KC_RPRN), + [TD_9_LPRN] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_9_LPRN]), + [TD_0_RPRN] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_0_RPRN]), - [TD_MINS_UNDS] = ACTION_TAP_DANCE_DOUBLE(KC_MINS, KC_UNDS), - [TD_EQL_PLUS] = ACTION_TAP_DANCE_DOUBLE(KC_EQL, KC_PLUS), + [TD_MINS_UNDS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_MINS_UNDS]), + [TD_EQL_PLUS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_EQL_PLUS]), - [TD_LBRC_LCBR] = ACTION_TAP_DANCE_DOUBLE(KC_LBRC, KC_LCBR), - [TD_RBRC_RCBR] = ACTION_TAP_DANCE_DOUBLE(KC_RBRC, KC_RCBR), + [TD_LBRC_LCBR] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_LBRC_LCBR]), + [TD_RBRC_RCBR] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_RBRC_RCBR]), - [TD_SCLN_COLN] = ACTION_TAP_DANCE_DOUBLE(KC_SCLN, KC_COLN), - [TD_QUOT_DQT] = ACTION_TAP_DANCE_DOUBLE(KC_QUOT, KC_DQT), + [TD_SCLN_COLN] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SCLN_COLN]), + [TD_QUOT_DQT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_QUOT_DQT]), - [TD_COMM_LABK] = ACTION_TAP_DANCE_DOUBLE(KC_COMM, KC_LABK), - [TD_DOT_RABK] = ACTION_TAP_DANCE_DOUBLE(KC_DOT, KC_RABK), - [TD_SLSH_QUES] = ACTION_TAP_DANCE_DOUBLE(KC_SLSH, KC_QUES), - [TD_BSLS_PIPE] = ACTION_TAP_DANCE_DOUBLE(KC_BSLS, KC_PIPE), + [TD_COMM_LABK] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_COMM_LABK]), + [TD_DOT_RABK] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DOT_RABK]), + [TD_SLSH_QUES] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SLSH_QUES]), + [TD_BSLS_PIPE] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_BSLS_PIPE]), - [TD_PGUP_HOME] = ACTION_TAP_DANCE_DOUBLE(KC_PGUP, KC_HOME), - [TD_PGDN_END] = ACTION_TAP_DANCE_DOUBLE(KC_PGUP, KC_END), + [TD_PGUP_HOME] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_PGUP_HOME]), + [TD_PGDN_END] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_PGDN_END]), - [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_Q_LrALT] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[DR_Q_LrALT]), + [TD_R_LrKey] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[DR_R_LrKey]), + [TD_T_LrMS] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[DR_T_LrMS]), [TD_SHIFT_CAPS] = ACTION_TAP_DANCE_FN_ADVANCED(NULL,lshift_finished, lshift_reset), - [TD_SPC_ENT] = ACTION_TAP_DANCE_DOUBLE(KC_SPACE, KC_ENT), + [TD_SPC_ENT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SPC_ENT]), [ALT_OSL1] = ACTION_TAP_DANCE_FN_ADVANCED(NULL,alt_finished, alt_reset), [CTL_OSL1] = ACTION_TAP_DANCE_FN_ADVANCED(NULL,ctl_finished, ctl_reset), [TD_LayerDn] = ACTION_TAP_DANCE_FN_ADVANCED(NULL,layerDown_finished, layerDown_reset) - }; // Fillers to make layering more clear diff --git a/keyboards/handwired/space_oddity/keymaps/default/keymap.c b/keyboards/handwired/space_oddity/keymaps/default/keymap.c index c278953f464..62d2b39265b 100644 --- a/keyboards/handwired/space_oddity/keymaps/default/keymap.c +++ b/keyboards/handwired/space_oddity/keymaps/default/keymap.c @@ -34,10 +34,16 @@ enum { }; -// Tap dance actions - double tap for Caps Lock. -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { - [SFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_LSFT, KC_CAPS), + [SFT_CAPS] = {KC_LSFT, KC_CAPS}, + +}; + +// Tap dance actions - double tap for Caps Lock. +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + + [SFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[SFT_CAPS]), }; diff --git a/keyboards/jones/v03/keymaps/default_jp/keymap.c b/keyboards/jones/v03/keymaps/default_jp/keymap.c index b38b01c7488..445c48c8461 100644 --- a/keyboards/jones/v03/keymaps/default_jp/keymap.c +++ b/keyboards/jones/v03/keymaps/default_jp/keymap.c @@ -48,9 +48,17 @@ uint8_t cur_dance(tap_dance_state_t *state); void ql_finished(tap_dance_state_t *state, void *user_data); void ql_reset(tap_dance_state_t *state, void *user_data); +enum { + P_LSFT_CAPS, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_LSFT_CAPS] = {KC_LSFT, KC_CAPS}, +}; + // Tap Dance definitions -tap_dance_action_t tap_dance_actions[] = { - [TD_LSFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_LSFT, KC_CAPS), +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_LSFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_LSFT_CAPS]), [TD_ESC_NUM] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset), }; diff --git a/keyboards/jones/v03_1/keymaps/default_ansi/keymap.c b/keyboards/jones/v03_1/keymaps/default_ansi/keymap.c index b60e26d961a..7136ea7206c 100644 --- a/keyboards/jones/v03_1/keymaps/default_ansi/keymap.c +++ b/keyboards/jones/v03_1/keymaps/default_ansi/keymap.c @@ -47,9 +47,17 @@ uint8_t cur_dance(tap_dance_state_t *state); void ql_finished(tap_dance_state_t *state, void *user_data); void ql_reset(tap_dance_state_t *state, void *user_data); +enum { + P_LSFT_CAPS, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_LSFT_CAPS] = {KC_LSFT, KC_CAPS}, +}; + // Tap Dance definitions -tap_dance_action_t tap_dance_actions[] = { - [TD_LSFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_LSFT, KC_CAPS), +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_LSFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_LSFT_CAPS]), [TD_ESC_NUM] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset), }; diff --git a/keyboards/jones/v03_1/keymaps/default_jp/keymap.c b/keyboards/jones/v03_1/keymaps/default_jp/keymap.c index 78fb06aad18..4acbb1ccdd0 100644 --- a/keyboards/jones/v03_1/keymaps/default_jp/keymap.c +++ b/keyboards/jones/v03_1/keymaps/default_jp/keymap.c @@ -48,9 +48,17 @@ uint8_t cur_dance(tap_dance_state_t *state); void ql_finished(tap_dance_state_t *state, void *user_data); void ql_reset(tap_dance_state_t *state, void *user_data); +enum { + P_LSFT_CAPS, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_LSFT_CAPS] = {KC_LSFT, KC_CAPS}, +}; + // Tap Dance definitions -tap_dance_action_t tap_dance_actions[] = { - [TD_LSFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_LSFT, KC_CAPS), +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_LSFT_CAPS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_LSFT_CAPS]), [TD_ESC_NUM] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset), }; diff --git a/keyboards/karn/keymaps/colemak/keymap.c b/keyboards/karn/keymaps/colemak/keymap.c index 13f23170e1e..974695265e2 100644 --- a/keyboards/karn/keymaps/colemak/keymap.c +++ b/keyboards/karn/keymaps/colemak/keymap.c @@ -9,11 +9,21 @@ enum { TD_F2_F12, }; +enum { + P_F1_F11, + P_F2_F12, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_F1_F11] = {KC_F1, KC_F11}, + [P_F2_F12] = {KC_F2, KC_F12}, +}; + // Tap Dance definitions -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { // Tap once for F1, twice for F11 - [TD_F1_F11] = ACTION_TAP_DANCE_DOUBLE(KC_F1, KC_F11), - [TD_F2_F12] = ACTION_TAP_DANCE_DOUBLE(KC_F2, KC_F12), + [TD_F1_F11] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_F1_F11]), + [TD_F2_F12] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_F2_F12]), }; bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { diff --git a/keyboards/keyhive/navi10/keymaps/default/keymap.c b/keyboards/keyhive/navi10/keymaps/default/keymap.c index 2a9de350478..2866e5aaa1b 100644 --- a/keyboards/keyhive/navi10/keymaps/default/keymap.c +++ b/keyboards/keyhive/navi10/keymaps/default/keymap.c @@ -48,19 +48,19 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [0] = LAYOUT(/* Base */ TD(TAPPY_KEY),KC_HOME, KC_PGUP, KC_DEL, KC_END, KC_PGDN, - + KC_UP, KC_LEFT, KC_DOWN, KC_RIGHT), [_FN0] = LAYOUT(/* function layer */ KC_TRNS, KC_PAUS, KC_VOLU, KC_ENTER, KC_SCRL, KC_VOLD, - + KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS), [_ML1] = LAYOUT(/* media function layer, toggled on a single tap */ - KC_TRNS, KC_TRNS, KC_VOLU, + KC_TRNS, KC_TRNS, KC_VOLU, KC_MUTE, KC_TRNS, KC_VOLD, - + KC_SPC, KC_MRWD, KC_MPLY, KC_MFFD), }; @@ -122,10 +122,10 @@ void tk_reset(tap_dance_state_t *state, void *user_data){ layer_off(_FN0); } //reset the state - tk_tap_state.state = 0; + tk_tap_state.state = 0; } //associate the tap dance key with its functionality -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TAPPY_KEY] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, tk_finished, tk_reset) }; diff --git a/keyboards/kingly_keys/ave/ortho/keymaps/default/keymap.c b/keyboards/kingly_keys/ave/ortho/keymaps/default/keymap.c index f6b4d9cac58..ff0f61b5932 100644 --- a/keyboards/kingly_keys/ave/ortho/keymaps/default/keymap.c +++ b/keyboards/kingly_keys/ave/ortho/keymaps/default/keymap.c @@ -39,10 +39,18 @@ void dance_rst_reset (tap_dance_state_t *state, void *user_data) { // *Line_Note } } +enum { + P_DBQT, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_DBQT] = {KC_QUOTE, KC_DQT}, +}; + //Tap Dance Functions: -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_RST] = ACTION_TAP_DANCE_FN_ADVANCED (NULL, NULL, dance_rst_reset), // References "dance_rst_reset" (*Line_Note.001) - [TD_DBQT] = ACTION_TAP_DANCE_DOUBLE (KC_QUOTE, KC_DQT) + [TD_DBQT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DBQT]), }; diff --git a/keyboards/kingly_keys/ave/staggered/keymaps/default/keymap.c b/keyboards/kingly_keys/ave/staggered/keymaps/default/keymap.c index 8f6d14dee06..549fec681d1 100644 --- a/keyboards/kingly_keys/ave/staggered/keymaps/default/keymap.c +++ b/keyboards/kingly_keys/ave/staggered/keymaps/default/keymap.c @@ -39,10 +39,18 @@ void dance_rst_reset (tap_dance_state_t *state, void *user_data) { // *Line_Note } } +enum { + P_DBQT, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_DBQT] = {KC_QUOTE, KC_DQT}, +}; + //Tap Dance Functions: -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_RST] = ACTION_TAP_DANCE_FN_ADVANCED (NULL, NULL, dance_rst_reset), // References "dance_rst_reset" (*Line_Note.001) - [TD_DBQT] = ACTION_TAP_DANCE_DOUBLE (KC_QUOTE, KC_DQT) + [TD_DBQT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DBQT]), }; diff --git a/keyboards/marksard/treadstone32/keymaps/default/keymap.c b/keyboards/marksard/treadstone32/keymaps/default/keymap.c index 1373e819f82..ebdb01cf674 100644 --- a/keyboards/marksard/treadstone32/keymaps/default/keymap.c +++ b/keyboards/marksard/treadstone32/keymaps/default/keymap.c @@ -55,9 +55,19 @@ enum custom_keycodes { // #define KC_CODO TD(TD_CODO) // #define KC_SLRO TD(TD_SLRO) -// tap_dance_action_t tap_dance_actions[] = { -// [TD_CODO] = ACTION_TAP_DANCE_DOUBLE(KC_COMM, KC_DOT), -// [TD_SLRO] = ACTION_TAP_DANCE_DOUBLE(KC_SLSH, JP_BSLS), +// enum { +// P_CODO, +// P_SLRO, +// }; + +// const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { +// [P_CODO] = {KC_COMM, KC_DOT}, +// [P_SLRO] = {KC_SLSH, JP_BSLS}, +// }; + +// const tap_dance_action_t tap_dance_actions[] PROGMEM = { +// [TD_CODO] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_CODO]), +// [TD_SLRO] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SLRO]), // }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/matrix/noah/keymaps/blockader/keymap.c b/keyboards/matrix/noah/keymaps/blockader/keymap.c index b6caf43f91f..7ba7c10dfcf 100644 --- a/keyboards/matrix/noah/keymaps/blockader/keymap.c +++ b/keyboards/matrix/noah/keymaps/blockader/keymap.c @@ -32,9 +32,19 @@ enum{ DANCE_PGUP_TOP, }; -tap_dance_action_t tap_dance_actions[] = { - [DANCE_PGDN_BOTTOM] = ACTION_TAP_DANCE_DOUBLE(KC_PGDN, LGUI(KC_DOWN)), - [DANCE_PGUP_TOP] = ACTION_TAP_DANCE_DOUBLE(KC_PGUP, LGUI(KC_UP)), +enum { + P_DANCE_PGDN_BOTTOM, + P_DANCE_PGUP_TOP, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_DANCE_PGDN_BOTTOM] = {KC_PGDN, LGUI(KC_DOWN)}, + [P_DANCE_PGUP_TOP] = {KC_PGUP, LGUI(KC_UP)}, +}; + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [DANCE_PGDN_BOTTOM] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DANCE_PGDN_BOTTOM]), + [DANCE_PGUP_TOP] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DANCE_PGUP_TOP]), }; #define KEY_DANCE(a) TD(a) diff --git a/keyboards/mechwild/bde/lefty/keymaps/fancy/keymap.c b/keyboards/mechwild/bde/lefty/keymaps/fancy/keymap.c index 6fd2ad4cda8..c330238ca08 100644 --- a/keyboards/mechwild/bde/lefty/keymaps/fancy/keymap.c +++ b/keyboards/mechwild/bde/lefty/keymaps/fancy/keymap.c @@ -113,7 +113,7 @@ void left_enter_reset(tap_dance_state_t *state, void *user_data) { left_enter_tap_state.state = 0; } -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [left_enter] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, left_enter_finished, left_enter_reset) }; diff --git a/keyboards/numatreus/keymaps/like_jis/keymap.c b/keyboards/numatreus/keymaps/like_jis/keymap.c index 6fc526610a6..d2f49625340 100644 --- a/keyboards/numatreus/keymaps/like_jis/keymap.c +++ b/keyboards/numatreus/keymaps/like_jis/keymap.c @@ -56,9 +56,19 @@ enum tapdances{ #define KC_CODO TD(TD_CODO) // #define KC_MNUB TD(TD_MNUB) -tap_dance_action_t tap_dance_actions[] = { - [TD_CODO] = ACTION_TAP_DANCE_DOUBLE(KC_COMM, KC_DOT), - // [TD_MNUB] = ACTION_TAP_DANCE_DOUBLE(KC_MINS, LSFT(JP_BSLS)), +enum { + P_CODO, + // P_MNUB, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_CODO] = {KC_COMM, KC_DOT}, + // [P_MNUB] = {KC_MINS, LSFT(JP_BSLS)}, +}; + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_CODO] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_CODO]), + // [TD_MNUB] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_MNUB]), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { diff --git a/keyboards/recompile_keys/nomu30/keymaps/center_sprit/keymap.c b/keyboards/recompile_keys/nomu30/keymaps/center_sprit/keymap.c index 16d320f045c..63b4c5febc3 100644 --- a/keyboards/recompile_keys/nomu30/keymaps/center_sprit/keymap.c +++ b/keyboards/recompile_keys/nomu30/keymaps/center_sprit/keymap.c @@ -54,9 +54,17 @@ enum tapdances{ // Tap dance #define KC_CODO TD(TD_CODO) -tap_dance_action_t tap_dance_actions[] = { - [TD_CODO] = ACTION_TAP_DANCE_DOUBLE(KC_COMM, KC_DOT), - }; +enum { + P_CODO, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_CODO] = {KC_COMM, KC_DOT}, +}; + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_CODO] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_CODO]), +}; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [_BASE] = LAYOUT( diff --git a/keyboards/salicylic_acid3/ajisai74/keymaps/jis/keymap.c b/keyboards/salicylic_acid3/ajisai74/keymaps/jis/keymap.c index 5fa8be40244..12a6449c518 100644 --- a/keyboards/salicylic_acid3/ajisai74/keymaps/jis/keymap.c +++ b/keyboards/salicylic_acid3/ajisai74/keymaps/jis/keymap.c @@ -30,8 +30,16 @@ enum tapdances{ TD_ENT = 0, }; -tap_dance_action_t tap_dance_actions[] = { - [TD_ENT] = ACTION_TAP_DANCE_DOUBLE(KC_ENT, KC_ENT), +enum { + P_ENT, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_ENT] = {KC_ENT, KC_ENT}, +}; + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_ENT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_ENT]), }; const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { @@ -59,7 +67,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { //|--------+--------+--------+--------+--------+--------| |--------+--------+--------+--------+--------+--------+--------+--------+--------| _______,_______,_______,_______,_______,_______, _______, _______, _______, _______, _______, _______, _______, _______, KC_END, //|--------+--------+--------+--------+--------+--------| |--------+--------+--------+--------+--------+--------+--------+--------+--------| - _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ //|-----------------------------------------------------| |--------------------------------------------------------------------------------' ) }; diff --git a/keyboards/spaceman/2_milk/keymaps/emoji/keymap.c b/keyboards/spaceman/2_milk/keymaps/emoji/keymap.c index 26ac20ad5ce..92e5f8b3703 100644 --- a/keyboards/spaceman/2_milk/keymaps/emoji/keymap.c +++ b/keyboards/spaceman/2_milk/keymaps/emoji/keymap.c @@ -40,7 +40,7 @@ void dance_key_two (tap_dance_state_t *state, void *user_data) { } } -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_KEY_1] = ACTION_TAP_DANCE_FN(dance_key_one), [TD_KEY_2] = ACTION_TAP_DANCE_FN(dance_key_two), }; diff --git a/keyboards/the_royal/liminal/keymaps/default/keymap.c b/keyboards/the_royal/liminal/keymaps/default/keymap.c index 5dedb8b0add..e0353032cc8 100644 --- a/keyboards/the_royal/liminal/keymaps/default/keymap.c +++ b/keyboards/the_royal/liminal/keymaps/default/keymap.c @@ -21,10 +21,18 @@ void dance_rst_reset (tap_dance_state_t *state, void *user_data) { } } +enum { + P_DBQT, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_DBQT] = {KC_QUOTE, KC_DQT}, +}; + //All tap dance functions would go here. Only showing this one. -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_RST] = ACTION_TAP_DANCE_FN_ADVANCED (NULL, NULL, dance_rst_reset), - [TD_DBQT] = ACTION_TAP_DANCE_DOUBLE (KC_QUOTE, KC_DQT) + [TD_DBQT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DBQT]), }; enum custom_keycodes { diff --git a/keyboards/the_royal/liminal/keymaps/default_iso/keymap.c b/keyboards/the_royal/liminal/keymaps/default_iso/keymap.c index 43b2fa22561..919cb2c8bd5 100644 --- a/keyboards/the_royal/liminal/keymaps/default_iso/keymap.c +++ b/keyboards/the_royal/liminal/keymaps/default_iso/keymap.c @@ -21,10 +21,18 @@ void dance_rst_reset (tap_dance_state_t *state, void *user_data) { } } +enum { + P_DBQT, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_DBQT] = {KC_QUOTE, KC_DQT}, +}; + //All tap dance functions would go here. Only showing this one. -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_RST] = ACTION_TAP_DANCE_FN_ADVANCED (NULL, NULL, dance_rst_reset), - [TD_DBQT] = ACTION_TAP_DANCE_DOUBLE (KC_QUOTE, KC_DQT) + [TD_DBQT] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_DBQT]), }; enum custom_keycodes { diff --git a/keyboards/the_royal/schwann/keymaps/default/keymap.c b/keyboards/the_royal/schwann/keymaps/default/keymap.c index c42f9f2759c..088d0c46e02 100644 --- a/keyboards/the_royal/schwann/keymaps/default/keymap.c +++ b/keyboards/the_royal/schwann/keymaps/default/keymap.c @@ -26,7 +26,7 @@ void dance_rst_reset (tap_dance_state_t *state, void *user_data) { } //All tap dance functions would go here. Only showing this one. -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_RST] = ACTION_TAP_DANCE_FN_ADVANCED (NULL, NULL, dance_rst_reset) }; diff --git a/keyboards/unikeyboard/diverge3/keymaps/default/keymap.c b/keyboards/unikeyboard/diverge3/keymaps/default/keymap.c index dc5c261f599..7eef490c05e 100644 --- a/keyboards/unikeyboard/diverge3/keymaps/default/keymap.c +++ b/keyboards/unikeyboard/diverge3/keymaps/default/keymap.c @@ -79,7 +79,7 @@ void se_reset (tap_dance_state_t *state, void *user_data) { se_tap_state.state = 0; } -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [SE_TAP_DANCE] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, se_finished, se_reset) }; diff --git a/keyboards/woodkeys/bigseries/1key/keymaps/leddance/keymap.c b/keyboards/woodkeys/bigseries/1key/keymaps/leddance/keymap.c index 0bbd8beab50..24e0ce5fba4 100755 --- a/keyboards/woodkeys/bigseries/1key/keymaps/leddance/keymap.c +++ b/keyboards/woodkeys/bigseries/1key/keymaps/leddance/keymap.c @@ -59,7 +59,7 @@ void dance_toggle (tap_dance_state_t *state, void *user_data) { } //Tap Dance Definitions -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_TOGGLE] = ACTION_TAP_DANCE_FN(dance_toggle) // Other declarations would go here, separated by commas, if you have them }; diff --git a/keyboards/work_louder/work_board/keymaps/default/keymap.c b/keyboards/work_louder/work_board/keymaps/default/keymap.c index a1e48867286..ed2f320f25c 100644 --- a/keyboards/work_louder/work_board/keymaps/default/keymap.c +++ b/keyboards/work_louder/work_board/keymaps/default/keymap.c @@ -79,6 +79,6 @@ void dance_enc_reset(tap_dance_state_t *state, void *user_data) { } // Tap Dance definitions -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [ENC_TAP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, dance_enc_finished, dance_enc_reset), }; diff --git a/keyboards/wsk/gothic50/keymaps/default/keymap.c b/keyboards/wsk/gothic50/keymaps/default/keymap.c index 828ccb7d4bf..20d41040a15 100644 --- a/keyboards/wsk/gothic50/keymaps/default/keymap.c +++ b/keyboards/wsk/gothic50/keymaps/default/keymap.c @@ -50,8 +50,16 @@ layer_state_t layer_state_set_user(layer_state_t state) return state; } -// Tap Dance Definitions -tap_dance_action_t tap_dance_actions[] = { - // double tap for caps - [TD_SCAPS] = ACTION_TAP_DANCE_DOUBLE(KC_LSFT, KC_CAPS) +enum { + P_SCAPS, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_SCAPS] = {KC_LSFT, KC_CAPS}, +}; + +// Tap Dance Definitions +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + // double tap for caps + [TD_SCAPS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SCAPS]), }; diff --git a/keyboards/wsk/gothic70/keymaps/default/keymap.c b/keyboards/wsk/gothic70/keymaps/default/keymap.c index 39b8ef8f6b0..274d227601d 100644 --- a/keyboards/wsk/gothic70/keymaps/default/keymap.c +++ b/keyboards/wsk/gothic70/keymaps/default/keymap.c @@ -61,9 +61,16 @@ layer_state_t layer_state_set_user(layer_state_t state) return state; } - -// Tap Dance Definitions -tap_dance_action_t tap_dance_actions[] = { - // Tap once for - [TD_SCAPS] = ACTION_TAP_DANCE_DOUBLE(KC_LSFT, KC_CAPS), +enum { + P_SCAPS, +}; + +const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { + [P_SCAPS] = {KC_LSFT, KC_CAPS}, +}; + +// Tap Dance Definition +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + // Tap once for + [TD_SCAPS] = ACTION_TAP_DANCE_DOUBLE(tap_dance_pairs[P_SCAPS]), }; diff --git a/quantum/keymap_introspection.c b/quantum/keymap_introspection.c index 99fd3f929e9..e512ab8f103 100644 --- a/quantum/keymap_introspection.c +++ b/quantum/keymap_introspection.c @@ -140,7 +140,7 @@ tap_dance_action_t* tap_dance_get_raw(uint16_t tap_dance_idx) { if (tap_dance_idx >= tap_dance_count_raw()) { return NULL; } - return &tap_dance_actions[tap_dance_idx]; + return (tap_dance_action_t*)&tap_dance_actions[tap_dance_idx]; } __attribute__((weak)) tap_dance_action_t* tap_dance_get(uint16_t tap_dance_idx) { diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index 3b5c9576800..a3eef3a1641 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -25,10 +25,6 @@ static uint16_t active_td; -#ifndef TAP_DANCE_MAX_SIMULTANEOUS -# define TAP_DANCE_MAX_SIMULTANEOUS 3 -#endif - static tap_dance_state_t tap_dance_states[TAP_DANCE_MAX_SIMULTANEOUS]; static uint16_t last_tap_time; @@ -40,14 +36,15 @@ tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx) { } // Search for a state already used for this keycode for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { - if (tap_dance_states[i].in_use && tap_dance_states[i].index == tap_dance_idx) { + if (tap_dance_states[i].in_use && tap_dance_states[i].tap_dance_idx == tap_dance_idx) { return &tap_dance_states[i]; } } // Search for the first available state for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { if (!tap_dance_states[i].in_use) { - tap_dance_states[i].index = tap_dance_idx; + tap_dance_states[i].tap_dance_idx = tap_dance_idx; + tap_dance_states[i].state_idx = i; tap_dance_states[i].in_use = true; return &tap_dance_states[i]; } @@ -57,56 +54,62 @@ tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx) { } void tap_dance_pair_on_each_tap(tap_dance_state_t *state, void *user_data) { - tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data; + tap_dance_pair_t pair; + memcpy_P(&pair, user_data, sizeof(tap_dance_pair_t)); if (state->count == 2) { - register_code16(pair->kc2); + register_code16(pair.kc2); state->finished = true; } } void tap_dance_pair_finished(tap_dance_state_t *state, void *user_data) { - tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data; + tap_dance_pair_t pair; + memcpy_P(&pair, user_data, sizeof(tap_dance_pair_t)); - register_code16(pair->kc1); + register_code16(pair.kc1); } void tap_dance_pair_reset(tap_dance_state_t *state, void *user_data) { - tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data; + tap_dance_pair_t pair; + memcpy_P(&pair, user_data, sizeof(tap_dance_pair_t)); if (state->count == 1) { wait_ms(TAP_CODE_DELAY); - unregister_code16(pair->kc1); + unregister_code16(pair.kc1); } else if (state->count == 2) { - unregister_code16(pair->kc2); + unregister_code16(pair.kc2); } } void tap_dance_dual_role_on_each_tap(tap_dance_state_t *state, void *user_data) { - tap_dance_dual_role_t *pair = (tap_dance_dual_role_t *)user_data; + tap_dance_dual_role_t pair; + memcpy_P(&pair, user_data, sizeof(tap_dance_dual_role_t)); if (state->count == 2) { - layer_move(pair->layer); + layer_move(pair.layer); state->finished = true; } } void tap_dance_dual_role_finished(tap_dance_state_t *state, void *user_data) { - tap_dance_dual_role_t *pair = (tap_dance_dual_role_t *)user_data; + tap_dance_dual_role_t pair; + memcpy_P(&pair, user_data, sizeof(tap_dance_dual_role_t)); if (state->count == 1) { - register_code16(pair->kc); + register_code16(pair.kc); } else if (state->count == 2) { - pair->layer_function(pair->layer); + pair.layer_function(pair.layer); } } void tap_dance_dual_role_reset(tap_dance_state_t *state, void *user_data) { - tap_dance_dual_role_t *pair = (tap_dance_dual_role_t *)user_data; + tap_dance_dual_role_t pair; + memcpy_P(&pair, user_data, sizeof(tap_dance_dual_role_t)); if (state->count == 1) { wait_ms(TAP_CODE_DELAY); - unregister_code16(pair->kc); + unregister_code16(pair.kc); } } @@ -159,21 +162,21 @@ static inline void process_tap_dance_action_on_dance_finished(tap_dance_action_t } bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { - tap_dance_action_t *action; - tap_dance_state_t * state; + tap_dance_action_t action; + tap_dance_state_t *state; if (!record->event.pressed) return false; if (!active_td || keycode == active_td) return false; - action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)); - state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); + memcpy_P(&action, tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)), sizeof(tap_dance_action_t)); + state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); if (state == NULL) { return false; } state->interrupted = true; state->interrupting_keycode = keycode; - process_tap_dance_action_on_dance_finished(action, state); + process_tap_dance_action_on_dance_finished(&action, state); // 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. @@ -187,9 +190,9 @@ bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { } bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { - uint8_t td_index; - tap_dance_action_t *action; - tap_dance_state_t * state; + uint8_t td_index; + tap_dance_action_t action; + tap_dance_state_t *state; switch (keycode) { case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: @@ -197,20 +200,20 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { if (td_index >= tap_dance_count()) { return false; } - action = tap_dance_get(td_index); - state = tap_dance_get_state(td_index); + memcpy_P(&action, tap_dance_get(td_index), sizeof(tap_dance_action_t)); + state = tap_dance_get_state(td_index); if (state == NULL) { return false; } state->pressed = record->event.pressed; if (record->event.pressed) { last_tap_time = timer_read(); - process_tap_dance_action_on_each_tap(action, state); + process_tap_dance_action_on_each_tap(&action, state); active_td = state->finished ? 0 : keycode; } else { - process_tap_dance_action_on_each_release(action, state); + process_tap_dance_action_on_each_release(&action, state); if (state->finished) { - process_tap_dance_action_on_reset(action, state); + process_tap_dance_action_on_reset(&action, state); if (active_td == keycode) { active_td = 0; } @@ -224,19 +227,19 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { } void tap_dance_task(void) { - tap_dance_action_t *action; - tap_dance_state_t * state; + tap_dance_action_t action; + tap_dance_state_t *state; if (!active_td || timer_elapsed(last_tap_time) <= GET_TAPPING_TERM(active_td, &(keyrecord_t){})) return; - action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)); + memcpy_P(&action, tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)), sizeof(tap_dance_action_t)); state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); if (state != NULL && !state->interrupted) { - process_tap_dance_action_on_dance_finished(action, state); + process_tap_dance_action_on_dance_finished(&action, state); } } void reset_tap_dance(tap_dance_state_t *state) { active_td = 0; - process_tap_dance_action_on_reset(tap_dance_get(state->index), state); + process_tap_dance_action_on_reset(tap_dance_get(state->tap_dance_idx), state); } diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h index 5a972cee5ab..e0417a4617d 100644 --- a/quantum/process_keycode/process_tap_dance.h +++ b/quantum/process_keycode/process_tap_dance.h @@ -21,6 +21,10 @@ #include "action.h" #include "quantum_keycodes.h" +#ifndef TAP_DANCE_MAX_SIMULTANEOUS +# define TAP_DANCE_MAX_SIMULTANEOUS 3 +#endif + typedef struct { uint16_t interrupting_keycode; uint8_t count; @@ -32,7 +36,8 @@ typedef struct { bool finished : 1; bool interrupted : 1; bool in_use : 1; - uint8_t index; + uint8_t tap_dance_idx; + uint8_t state_idx; } tap_dance_state_t; typedef void (*tap_dance_user_fn_t)(tap_dance_state_t *state, void *user_data); @@ -58,14 +63,17 @@ typedef struct { void (*layer_function)(uint8_t); } tap_dance_dual_role_t; -#define ACTION_TAP_DANCE_DOUBLE(kc1, kc2) \ - { .fn = {tap_dance_pair_on_each_tap, tap_dance_pair_finished, tap_dance_pair_reset, NULL}, .user_data = (void *)&((tap_dance_pair_t){kc1, kc2}), } +#define ACTION_TAP_DANCE_DOUBLE(pair) \ + { .fn = {tap_dance_pair_on_each_tap, tap_dance_pair_finished, tap_dance_pair_reset, NULL}, .user_data = (void *)&(pair), } -#define ACTION_TAP_DANCE_LAYER_MOVE(kc, layer) \ - { .fn = {tap_dance_dual_role_on_each_tap, tap_dance_dual_role_finished, tap_dance_dual_role_reset, NULL}, .user_data = (void *)&((tap_dance_dual_role_t){kc, layer, layer_move}), } +#define DUAL_ROLE_TAP_DANCE_LAYER_MOVE(kc, layer) \ + {kc, layer, layer_move} -#define ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer) \ - { .fn = {NULL, tap_dance_dual_role_finished, tap_dance_dual_role_reset, NULL}, .user_data = (void *)&((tap_dance_dual_role_t){kc, layer, layer_invert}), } +#define DUAL_ROLE_TAP_DANCE_LAYER_TOGGLE(kc, layer) \ + {kc, layer, layer_invert} + +#define ACTION_TAP_DANCE_DUAL_ROLE(dual_role) \ + { .fn = {tap_dance_dual_role_on_each_tap, tap_dance_dual_role_finished, tap_dance_dual_role_reset, NULL}, .user_data = (void *)&(dual_role), } #define ACTION_TAP_DANCE_FN(user_fn) \ { .fn = {NULL, user_fn, NULL, NULL}, .user_data = NULL, } diff --git a/tests/tap_dance/examples.c b/tests/tap_dance/examples.c index 6aaf0082323..6e6e9394c38 100644 --- a/tests/tap_dance/examples.c +++ b/tests/tap_dance/examples.c @@ -76,27 +76,34 @@ void dance_flsh_reset(tap_dance_state_t *state, void *user_data) { typedef struct { uint16_t tap; uint16_t hold; - uint16_t held; } tap_dance_tap_hold_t; +typedef struct { + uint16_t held; +} tap_dance_tap_hold_state_t; + +static tap_dance_tap_hold_state_t tap_dance_tap_hold_states[TAP_DANCE_MAX_SIMULTANEOUS]; + bool process_record_user(uint16_t keycode, keyrecord_t *record) { tap_dance_action_t *action; tap_dance_state_t* state; + tap_dance_tap_hold_t tap_hold; switch (keycode) { case TD(CT_CLN): action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(keycode)); state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode)); if (!record->event.pressed && state != NULL && state->count && !state->finished) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data; - tap_code16(tap_hold->tap); + memcpy_P(&tap_hold, (tap_dance_tap_hold_t *)action->user_data, sizeof(tap_dance_tap_hold_t)); + tap_code16(tap_hold.tap); } } return true; } void tap_dance_tap_hold_finished(tap_dance_state_t *state, void *user_data) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + tap_dance_tap_hold_t tap_hold; + memcpy_P(&tap_hold, user_data, sizeof(tap_dance_tap_hold_t)); if (state->pressed) { if (state->count == 1 @@ -104,27 +111,28 @@ void tap_dance_tap_hold_finished(tap_dance_state_t *state, void *user_data) { && !state->interrupted #endif ) { - register_code16(tap_hold->hold); - tap_hold->held = tap_hold->hold; + register_code16(tap_hold.hold); + tap_dance_tap_hold_states[state->state_idx].held = tap_hold.hold; } else { - register_code16(tap_hold->tap); - tap_hold->held = tap_hold->tap; + register_code16(tap_hold.tap); + tap_dance_tap_hold_states[state->state_idx].held = tap_hold.tap; } } } void tap_dance_tap_hold_reset(tap_dance_state_t *state, void *user_data) { - tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data; + tap_dance_tap_hold_t tap_hold; + memcpy_P(&tap_hold, user_data, sizeof(tap_dance_tap_hold_t)); - if (tap_hold->held) { - unregister_code16(tap_hold->held); - tap_hold->held = 0; + if (tap_dance_tap_hold_states[state->state_idx].held) { + unregister_code16(tap_dance_tap_hold_states[state->state_idx].held); + tap_dance_tap_hold_states[state->state_idx].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}), } +#define ACTION_TAP_DANCE_TAP_HOLD(tap_hold) {{NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, (void *)&(tap_hold)} +const tap_dance_tap_hold_t d_ct_cln PROGMEM = {KC_COLN, KC_SCLN}; // Example 4 @@ -211,11 +219,13 @@ static void release_reset(tap_dance_state_t *state, void *user_data) { tap_code16(KC_R); } -tap_dance_action_t tap_dance_actions[] = { - [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS), +const tap_dance_pair_t p_esc_caps PROGMEM = {KC_ESC, KC_CAPS}; + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(p_esc_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), + [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(d_ct_cln), [X_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset), [TD_RELEASE] = ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(release_press, release_unpress, release_finished, release_reset), [TD_RELEASE_AND_FINISH] = ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(release_press, release_unpress_mark_finished, release_finished, release_reset), diff --git a/tests/tap_dance/tap_dance_layers/tap_dance_defs.c b/tests/tap_dance/tap_dance_layers/tap_dance_defs.c index fbe37f7ed01..0652709b7de 100644 --- a/tests/tap_dance/tap_dance_layers/tap_dance_defs.c +++ b/tests/tap_dance/tap_dance_layers/tap_dance_defs.c @@ -90,8 +90,13 @@ static void lt_app_reset(tap_dance_state_t *state, void *user_data) { } } -tap_dance_action_t tap_dance_actions[] = { - [TD_L_MOVE] = ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1), - [TD_L_TOGG] = ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1), +const tap_dance_dual_role_t dual_roles[] PROGMEM = { + [DR_L_MOVE] = DUAL_ROLE_TAP_DANCE_LAYER_MOVE(KC_APP, 1), + [DR_L_TOGG] = DUAL_ROLE_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1), +}; + +const tap_dance_action_t tap_dance_actions[] PROGMEM = { + [TD_L_MOVE] = ACTION_TAP_DANCE_DUAL_ROLE(dual_roles[DR_L_MOVE]), + [TD_L_TOGG] = ACTION_TAP_DANCE_DUAL_ROLE(dual_roles[DR_L_TOGG]), [TD_LT_APP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, lt_app_finished, lt_app_reset), }; diff --git a/tests/tap_dance/tap_dance_layers/tap_dance_defs.h b/tests/tap_dance/tap_dance_layers/tap_dance_defs.h index 37cab0c2cbf..0b8b610946e 100644 --- a/tests/tap_dance/tap_dance_layers/tap_dance_defs.h +++ b/tests/tap_dance/tap_dance_layers/tap_dance_defs.h @@ -18,9 +18,14 @@ enum custom_keycodes { SLOW_CD, }; +enum tap_dance_dual_roles { + DR_L_MOVE, // move to layer one + DR_L_TOGG, // toggle layer one +}; + enum tap_dance_ids { - TD_L_MOVE, // ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1) - TD_L_TOGG, // ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1) + TD_L_MOVE, // move to layer one + TD_L_TOGG, // toggle layer one TD_LT_APP, // similar to LT(1, KC_APP) with KC_RCTL on tap+hold or double tap }; From 14eab13c1948b750ecea73db7cf795bdee440323 Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Mon, 30 Jun 2025 16:31:16 -0400 Subject: [PATCH 10/17] explain configuration and state, illustrate callback timings --- .../tap-dance-doube-hold-interrupted.svg | 267 ++++++++++++++++ docs/features/tap-dance-doube-hold.svg | 215 +++++++++++++ .../tap-dance-doube-tap-interrupted.svg | 286 ++++++++++++++++++ docs/features/tap-dance-doube-tap.svg | 219 ++++++++++++++ docs/features/tap_dance.md | 87 +++++- tests/tap_dance/examples.c | 10 +- 6 files changed, 1071 insertions(+), 13 deletions(-) create mode 100644 docs/features/tap-dance-doube-hold-interrupted.svg create mode 100644 docs/features/tap-dance-doube-hold.svg create mode 100644 docs/features/tap-dance-doube-tap-interrupted.svg create mode 100644 docs/features/tap-dance-doube-tap.svg diff --git a/docs/features/tap-dance-doube-hold-interrupted.svg b/docs/features/tap-dance-doube-hold-interrupted.svg new file mode 100644 index 00000000000..6dd9370e537 --- /dev/null +++ b/docs/features/tap-dance-doube-hold-interrupted.svg @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + Time + + Key up + Firstpress + 2ndpress + End ofTappingTerm + Key down + on_each_tap(count:1, pressed:true) + on_each_release(count:1, pressed:false) + on_each_tap(count:2, pressed:true) + on_each_release(count:2, pressed:false) + on_reset(count:2, pressed:false) + + + + Other keyinterrupting + on_dance_finished(count:2, interrupted:true) + + diff --git a/docs/features/tap-dance-doube-hold.svg b/docs/features/tap-dance-doube-hold.svg new file mode 100644 index 00000000000..df7801a0887 --- /dev/null +++ b/docs/features/tap-dance-doube-hold.svg @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + Time + + Key up + Firstpress + 2ndpress + End ofTappingTerm + Key down + on_each_tap(count:1, pressed:true) + on_each_release(count:1, pressed:false) + on_each_tap(count:2, pressed:true) + on_each_release(count:2, pressed:false) + on_dance_finished(count:2, pressed:true) + on_reset(count:2, pressed:false) + + + diff --git a/docs/features/tap-dance-doube-tap-interrupted.svg b/docs/features/tap-dance-doube-tap-interrupted.svg new file mode 100644 index 00000000000..ee8bc889007 --- /dev/null +++ b/docs/features/tap-dance-doube-tap-interrupted.svg @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + Time + + Key up + Firstpress + 2ndpress + Other keyinterrupting + End ofTappingTerm + Key down + on_each_tap(count:1, pressed:true) + on_each_release(count:1, pressed:false) + on_each_tap(count:2, pressed:true) + on_each_release(count:2, pressed:false) + on_dance_finished(count:2, interrupted:true) + on_reset(count:2, pressed:false) + + + diff --git a/docs/features/tap-dance-doube-tap.svg b/docs/features/tap-dance-doube-tap.svg new file mode 100644 index 00000000000..aa3ba3537d5 --- /dev/null +++ b/docs/features/tap-dance-doube-tap.svg @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + Time + + Key up + Firstpress + 2ndpress + End ofTappingTerm + Key down + on_each_tap(count:1, pressed:true) + on_each_release(count:1, pressed:false) + on_each_tap(count:2, pressed:true) + on_each_release(count:2, pressed:false) + on_dance_finished(count:2, pressed:false) + on_reset(count:2, pressed:false) + + + diff --git a/docs/features/tap_dance.md b/docs/features/tap_dance.md index 730bdd164e6..f599678c3a8 100644 --- a/docs/features/tap_dance.md +++ b/docs/features/tap_dance.md @@ -15,23 +15,26 @@ Optionally, you might want to set a custom `TAPPING_TERM` time by adding somethi ```c #define TAPPING_TERM 175 #define TAPPING_TERM_PER_KEY +#define TAP_DANCE_MAX_SIMULTANEOUS 3 ``` 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#tapping_term), which may be needed because `TAPPING_TERM` affects not just tap-dance keys. +`TAP_DANCE_MAX_SIMULTANEOUS` controls how many tap dance keys can be held at the same time and register keycodes. Increasing this value will use more RAM because each active tap dance needs a significant amount of state in active memory. + 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 several possible options: -* `ACTION_TAP_DANCE_DOUBLE(pair)`: Define keycode pairs in a separate progmem array. With the pair `{kc1, kc2}`, it 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_DUAL_ROLE(dual_role)`: Define dual roles in a separate progmem array. Using the following: +* `ACTION_TAP_DANCE_DOUBLE(pair)`: Define keycode pairs in a separate `PROGMEM` array. With the pair `{kc1, kc2}`, it 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_DUAL_ROLE(dual_role)`: Define dual roles in a separate `PROGMEM` array. Using the following: * `DUAL_ROLE_TAP_DANCE_LAYER_MOVE(kc, layer)`: Sends the `kc` keycode when tapped once, or moves to `layer`. (this functions like the `TO` layer keycode). * `DUAL_ROLE_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: Sends the `kc` keycode when tapped once, or toggles the state of `layer`. (this functions like the `TG` layer keycode). * `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the final tap count of the tap dance action. * `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: Calls the first specified function - defined in the user keymap - on every tap, the second function when the dance action finishes (like the previous option), and the last function when the tap dance action resets. * `ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(on_each_tap_fn, on_each_release_fn, on_dance_finished_fn, on_dance_reset_fn)`: This macro is identical to `ACTION_TAP_DANCE_FN_ADVANCED` with the addition of `on_each_release_fn` which is invoked every time the key for the tap dance is released. It is worth noting that `on_each_release_fn` will still be called even when the key is released after the dance finishes (e.g. if the key is released after being pressed and held for longer than the `TAPPING_TERM`). -The first option is enough for a lot of cases that just want dual roles. +The first option is enough for a lot of cases that just want keys that do something different when double tapped. ::: warning Keep in mind that only [basic keycodes](../keycodes_basic) are supported here. Custom keycodes are not supported. @@ -41,12 +44,50 @@ Similar to the first option, the second and third option are good for simple lay 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. +## Custom tap dances {#custom} + +There are four callback functions that can be used when implementing custom tap dances: + +- `on_each_tap(tap_dance_state_t *state, void *user_data)`; +- `on_dance_finished(tap_dance_state_t *state, void *user_data)`; +- `on_reset(tap_dance_state_t *state, void *user_data)`; +- `on_each_release(tap_dance_state_t *state, void *user_data)`; + +Here are some illustrations of when those functions are called during tap dances: + +### Double tap + +![](tap-dance-doube-tap.svg) + + +### Interrupted double tap + +![](tap-dance-doube-tap-interrupted.svg) + + +### Double hold + +![](tap-dance-doube-hold.svg) + + +### Interrupted double hold + +![](tap-dance-doube-hold-interrupted.svg) + +### Skipping the finished callback + +It is possible to end a tap dance immediately, skipping `on_dance_finished()`, but not `on_dance_reset_fn`, by calling `reset_tap_dance(state)`. Use this if your tap dance code has completely handled the tap dance in an earlier callback and the logic in the finished callback should not get executed. + +### Configuration data + +Your custom tap dance functions can accept `user_data` with configuration. This may consist of key codes, method pointers, or boolean flags used by your custom logic. So that it doesn't constantly take up RAM, it should be marked as PROGMEM. `memcpy_P()` should be used to read the `user_data` pointer into working memory in each of your callbacks. See [example 3](#example-3) below. + +### Mutable state + +Your custom tap dance may need to store data about the state of the current dance. Because `user_data` should be kept in immutable flash memory, it is not appropriate to use it for mutable state. Instead, define a static array of your state of size `TAP_DANCE_MAX_SIMULTANEOUS`. From the callback functions, use `state->state_idx` to get the index into this array which should be used. See [example 3](#example-3) below. + ## Implementation Details {#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! - -Let's go over the three functions mentioned in `ACTION_TAP_DANCE_FN_ADVANCED` in a little more detail. They all receive the same two 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. @@ -61,7 +102,7 @@ Here's a simple example for a single definition: 2. In your `keymap.c` file, define the variables and definitions, then add to your keymap: ```c -// Tap dance pair declarations +// Enumerate all tap dance pairs enum { P_ESC_CAPS, }; @@ -71,7 +112,7 @@ const tap_dance_pair_t tap_dance_pairs[] PROGMEM = { [P_ESC_CAPS] = {KC_ESC, KC_CAPS}, }; -// Tap dance declarations +// Enumerate all tap dances enum { TD_ESC_CAPS, }; @@ -186,6 +227,23 @@ typedef struct { static tap_dance_tap_hold_state_t tap_dance_tap_hold_states[TAP_DANCE_MAX_SIMULTANEOUS]; +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + tap_dance_action_t *action; + tap_dance_state_t* state; + tap_dance_tap_hold_t tap_hold; + + switch (keycode) { + case TD(CT_CLN): + action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(keycode)); + state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode)); + if (!record->event.pressed && state != NULL && state->count && !state->finished) { + memcpy_P(&tap_hold, (tap_dance_tap_hold_t *)action->user_data, sizeof(tap_dance_tap_hold_t)); + tap_code16(tap_hold.tap); + } + } + return true; +} + void tap_dance_tap_hold_finished(tap_dance_state_t *state, void *user_data) { tap_dance_tap_hold_t tap_hold; memcpy_P(&tap_hold, user_data, sizeof(tap_dance_tap_hold_t)); @@ -217,10 +275,17 @@ void tap_dance_tap_hold_reset(tap_dance_state_t *state, void *user_data) { #define ACTION_TAP_DANCE_TAP_HOLD(tap_hold) {{NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, (void *)&(tap_hold)} -const tap_dance_tap_hold_t d_ct_cln PROGMEM = {KC_COLN, KC_SCLN}; +enum { + TH_CT_CLN, +}; + +const tap_dance_tap_hold_t tap_dance_tap_holds[] PROGMEM = { + [TH_CT_CLN] = {KC_COLN, KC_SCLN}, +}; + const tap_dance_action_t tap_dance_actions[] PROGMEM = { - [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(d_ct_cln), + [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(tap_dance_tap_holds[TH_CT_CLN]), }; bool process_record_user(uint16_t keycode, keyrecord_t *record) { diff --git a/tests/tap_dance/examples.c b/tests/tap_dance/examples.c index 6e6e9394c38..68d1b495b76 100644 --- a/tests/tap_dance/examples.c +++ b/tests/tap_dance/examples.c @@ -132,7 +132,13 @@ void tap_dance_tap_hold_reset(tap_dance_state_t *state, void *user_data) { #define ACTION_TAP_DANCE_TAP_HOLD(tap_hold) {{NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, (void *)&(tap_hold)} -const tap_dance_tap_hold_t d_ct_cln PROGMEM = {KC_COLN, KC_SCLN}; +enum { + TH_CT_CLN, +}; + +const tap_dance_tap_hold_t tap_dance_tap_holds[] PROGMEM = { + [TH_CT_CLN] = {KC_COLN, KC_SCLN}, +}; // Example 4 @@ -225,7 +231,7 @@ const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(p_esc_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(d_ct_cln), + [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(tap_dance_tap_holds[TH_CT_CLN]), [X_CTL] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset), [TD_RELEASE] = ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(release_press, release_unpress, release_finished, release_reset), [TD_RELEASE_AND_FINISH] = ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(release_press, release_unpress_mark_finished, release_finished, release_reset), From b9e9fd057a77adef481362274afcde46fc6ac46b Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Mon, 30 Jun 2025 16:35:48 -0400 Subject: [PATCH 11/17] format --- quantum/process_keycode/process_tap_dance.c | 8 ++++---- quantum/process_keycode/process_tap_dance.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c index a3eef3a1641..efca3837629 100644 --- a/quantum/process_keycode/process_tap_dance.c +++ b/quantum/process_keycode/process_tap_dance.c @@ -43,9 +43,9 @@ tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx) { // Search for the first available state for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { if (!tap_dance_states[i].in_use) { - tap_dance_states[i].tap_dance_idx = tap_dance_idx; - tap_dance_states[i].state_idx = i; - tap_dance_states[i].in_use = true; + tap_dance_states[i].tap_dance_idx = tap_dance_idx; + tap_dance_states[i].state_idx = i; + tap_dance_states[i].in_use = true; return &tap_dance_states[i]; } } @@ -233,7 +233,7 @@ void tap_dance_task(void) { if (!active_td || timer_elapsed(last_tap_time) <= GET_TAPPING_TERM(active_td, &(keyrecord_t){})) return; memcpy_P(&action, tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)), sizeof(tap_dance_action_t)); - state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); + state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); if (state != NULL && !state->interrupted) { process_tap_dance_action_on_dance_finished(&action, state); } diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h index e0417a4617d..05f7b78c847 100644 --- a/quantum/process_keycode/process_tap_dance.h +++ b/quantum/process_keycode/process_tap_dance.h @@ -67,10 +67,10 @@ typedef struct { { .fn = {tap_dance_pair_on_each_tap, tap_dance_pair_finished, tap_dance_pair_reset, NULL}, .user_data = (void *)&(pair), } #define DUAL_ROLE_TAP_DANCE_LAYER_MOVE(kc, layer) \ - {kc, layer, layer_move} + { kc, layer, layer_move } #define DUAL_ROLE_TAP_DANCE_LAYER_TOGGLE(kc, layer) \ - {kc, layer, layer_invert} + { kc, layer, layer_invert } #define ACTION_TAP_DANCE_DUAL_ROLE(dual_role) \ { .fn = {tap_dance_dual_role_on_each_tap, tap_dance_dual_role_finished, tap_dance_dual_role_reset, NULL}, .user_data = (void *)&(dual_role), } From 7db9e83b6fea9268eef3cf009c8a9afbf9e3f811 Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Mon, 30 Jun 2025 19:48:35 -0400 Subject: [PATCH 12/17] Rename changlog file to reflect PR --- docs/ChangeLog/20250831/{pr-tapdance-progmem.md => pr25439.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/ChangeLog/20250831/{pr-tapdance-progmem.md => pr25439.md} (100%) diff --git a/docs/ChangeLog/20250831/pr-tapdance-progmem.md b/docs/ChangeLog/20250831/pr25439.md similarity index 100% rename from docs/ChangeLog/20250831/pr-tapdance-progmem.md rename to docs/ChangeLog/20250831/pr25439.md From 8af5d7243d06a599b45d2e96c307f24174c66bb4 Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Tue, 1 Jul 2025 04:52:36 -0400 Subject: [PATCH 13/17] add missing progmem --- docs/ChangeLog/20250831/pr25439.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ChangeLog/20250831/pr25439.md b/docs/ChangeLog/20250831/pr25439.md index 78608a4eecc..c92a2b849a1 100644 --- a/docs/ChangeLog/20250831/pr25439.md +++ b/docs/ChangeLog/20250831/pr25439.md @@ -73,7 +73,7 @@ const tap_dance_dual_role_t tap_dance_dual_roles[] PROGMEM = { [DR_R_RAISE] = DUAL_ROLE_TAP_DANCE_LAYER_TOGGLE(KC_R, _RAISE), }; -tap_dance_action_t tap_dance_actions[] = { +const tap_dance_action_t tap_dance_actions[] PROGMEM = { [TD_Q_ALT] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[DR_Q_ALT]), [TD_R_RAISE] = ACTION_TAP_DANCE_DUAL_ROLE(tap_dance_dual_roles[DR_R_RAISE]), }; From a1a0dbf01f51f3e5f55fa8a932ef833b24960e41 Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Tue, 1 Jul 2025 05:10:58 -0400 Subject: [PATCH 14/17] Add interrupting event processing to timeline --- .../tap-dance-doube-hold-interrupted.svg | 16 ++++++++++++-- .../tap-dance-doube-tap-interrupted.svg | 22 ++++++++++++++----- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/docs/features/tap-dance-doube-hold-interrupted.svg b/docs/features/tap-dance-doube-hold-interrupted.svg index 6dd9370e537..ddc9d089969 100644 --- a/docs/features/tap-dance-doube-hold-interrupted.svg +++ b/docs/features/tap-dance-doube-hold-interrupted.svg @@ -25,8 +25,8 @@ inkscape:document-units="mm" showgrid="false" inkscape:zoom="1.8884048" - inkscape:cx="514.45539" - inkscape:cy="326.20125" + inkscape:cx="505.18829" + inkscape:cy="379.68554" inkscape:current-layer="svg5" /> @@ -259,6 +259,18 @@ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:FreeMono;-inkscape-font-specification:FreeMono;stroke-width:0.264583" x="90.397614" y="-79.312668">on_dance_finished(count:2, interrupted:true) + Interrupting key event processing on_each_release(count:2, pressed:false) + x="155.74803" + y="17.118372">on_each_release(count:2, pressed:false) + Interrupting key event processing From 4b61904fcd871785e64a1c6cd4c7f7515ea54b41 Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Wed, 2 Jul 2025 20:15:41 -0400 Subject: [PATCH 15/17] changelog wording for clarity Co-authored-by: Daniel <1767914+iamdanielv@users.noreply.github.com> --- docs/ChangeLog/20250831/pr25439.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ChangeLog/20250831/pr25439.md b/docs/ChangeLog/20250831/pr25439.md index c92a2b849a1..a371693fe35 100644 --- a/docs/ChangeLog/20250831/pr25439.md +++ b/docs/ChangeLog/20250831/pr25439.md @@ -4,7 +4,7 @@ Tap dance actions are moved to PROGMEM so that they no longer use RAM. ## Changes for tap dance action array -Every layout that uses tap dances will need to change +Every layout that uses tap dances will need to change from ```c tap_dance_action_t tap_dance_actions[] = { From 0104bd2d27fd76fa2cf1535b329e05d9fbe42e1a Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Wed, 2 Jul 2025 20:15:48 -0400 Subject: [PATCH 16/17] changelog wording for clarity Co-authored-by: Daniel <1767914+iamdanielv@users.noreply.github.com> --- docs/ChangeLog/20250831/pr25439.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ChangeLog/20250831/pr25439.md b/docs/ChangeLog/20250831/pr25439.md index a371693fe35..16fe72f2ac3 100644 --- a/docs/ChangeLog/20250831/pr25439.md +++ b/docs/ChangeLog/20250831/pr25439.md @@ -20,7 +20,7 @@ const tap_dance_action_t tap_dance_actions[] PROGMEM = { ## New syntax for `ACTION_TAP_DANCE_DOUBLE` -`ACTION_TAP_DANCE_DOUBLE` no longer accepts two keycode literals, now it needs a variable containing the key code pair. +`ACTION_TAP_DANCE_DOUBLE` no longer accepts two keycode literals, now it needs a variable containing the key code pair. Change from ```c From 1fd811c91ac9e0b35cb977c0f799e44d6dfdb2e2 Mon Sep 17 00:00:00 2001 From: Stephen Ostermiller Date: Wed, 2 Jul 2025 20:15:55 -0400 Subject: [PATCH 17/17] changelog wording for clarity Co-authored-by: Daniel <1767914+iamdanielv@users.noreply.github.com> --- docs/ChangeLog/20250831/pr25439.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/ChangeLog/20250831/pr25439.md b/docs/ChangeLog/20250831/pr25439.md index 16fe72f2ac3..ccf328f7105 100644 --- a/docs/ChangeLog/20250831/pr25439.md +++ b/docs/ChangeLog/20250831/pr25439.md @@ -53,6 +53,8 @@ const tap_dance_action_t tap_dance_actions[] PROGMEM = { ## New syntax for `ACTION_TAP_DANCE_LAYER_MOVE` and `ACTION_TAP_DANCE_LAYER_TOGGLE` + Change from + ```c tap_dance_action_t tap_dance_actions[] = { [TD_Q_ALT] = ACTION_TAP_DANCE_LAYER_MOVE(KC_Q, _ALT),