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