tap-dance: Restructure code and document in more detail (#16394)
This commit is contained in:
		| @@ -15,12 +15,8 @@ | ||||
|  */ | ||||
| #include "quantum.h" | ||||
|  | ||||
| #ifndef NO_ACTION_ONESHOT | ||||
| uint8_t get_oneshot_mods(void); | ||||
| #endif | ||||
|  | ||||
| static uint16_t last_td; | ||||
| static int16_t  highest_td = -1; | ||||
| static uint16_t active_td; | ||||
| static uint16_t last_tap_time; | ||||
|  | ||||
| void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data) { | ||||
|     qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; | ||||
| @@ -34,18 +30,14 @@ void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data) | ||||
| void qk_tap_dance_pair_finished(qk_tap_dance_state_t *state, void *user_data) { | ||||
|     qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; | ||||
|  | ||||
|     if (state->count == 1) { | ||||
|         register_code16(pair->kc1); | ||||
|     } else if (state->count == 2) { | ||||
|         register_code16(pair->kc2); | ||||
|     } | ||||
|     register_code16(pair->kc1); | ||||
| } | ||||
|  | ||||
| void qk_tap_dance_pair_reset(qk_tap_dance_state_t *state, void *user_data) { | ||||
|     qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; | ||||
|  | ||||
|     wait_ms(TAP_CODE_DELAY); | ||||
|     if (state->count == 1) { | ||||
|         wait_ms(TAP_CODE_DELAY); | ||||
|         unregister_code16(pair->kc1); | ||||
|     } else if (state->count == 2) { | ||||
|         unregister_code16(pair->kc2); | ||||
| @@ -87,23 +79,40 @@ static inline void _process_tap_dance_action_fn(qk_tap_dance_state_t *state, voi | ||||
| } | ||||
|  | ||||
| static inline void process_tap_dance_action_on_each_tap(qk_tap_dance_action_t *action) { | ||||
|     action->state.count++; | ||||
|     action->state.weak_mods = get_mods(); | ||||
|     action->state.weak_mods |= get_weak_mods(); | ||||
| #ifndef NO_ACTION_ONESHOT | ||||
|     action->state.oneshot_mods = get_oneshot_mods(); | ||||
| #endif | ||||
|     _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_tap); | ||||
| } | ||||
|  | ||||
| static inline void process_tap_dance_action_on_dance_finished(qk_tap_dance_action_t *action) { | ||||
|     if (action->state.finished) return; | ||||
|     action->state.finished = true; | ||||
|     add_mods(action->state.oneshot_mods); | ||||
|     add_weak_mods(action->state.weak_mods); | ||||
|     send_keyboard_report(); | ||||
|     _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_dance_finished); | ||||
| } | ||||
|  | ||||
| static inline void process_tap_dance_action_on_reset(qk_tap_dance_action_t *action) { | ||||
|     _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_reset); | ||||
|     del_mods(action->state.oneshot_mods); | ||||
|     del_weak_mods(action->state.weak_mods); | ||||
| #ifndef NO_ACTION_ONESHOT | ||||
|     del_mods(action->state.oneshot_mods); | ||||
| #endif | ||||
|     send_keyboard_report(); | ||||
|     action->state = (const qk_tap_dance_state_t){0}; | ||||
| } | ||||
|  | ||||
| static inline void process_tap_dance_action_on_dance_finished(qk_tap_dance_action_t *action) { | ||||
|     if (!action->state.finished) { | ||||
|         action->state.finished = true; | ||||
|         add_weak_mods(action->state.weak_mods); | ||||
| #ifndef NO_ACTION_ONESHOT | ||||
|         add_mods(action->state.oneshot_mods); | ||||
| #endif | ||||
|         send_keyboard_report(); | ||||
|         _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_dance_finished); | ||||
|     } | ||||
|     active_td = 0; | ||||
|     if (!action->state.pressed) { | ||||
|         // There will not be a key release event, so reset now. | ||||
|         process_tap_dance_action_on_reset(action); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { | ||||
| @@ -111,51 +120,33 @@ void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { | ||||
|  | ||||
|     if (!record->event.pressed) return; | ||||
|  | ||||
|     if (highest_td == -1) return; | ||||
|     if (!active_td || keycode == active_td) return; | ||||
|  | ||||
|     for (int i = 0; i <= highest_td; i++) { | ||||
|         action = &tap_dance_actions[i]; | ||||
|         if (action->state.count) { | ||||
|             if (keycode == action->state.keycode && keycode == last_td) continue; | ||||
|             action->state.interrupted          = true; | ||||
|             action->state.interrupting_keycode = keycode; | ||||
|             process_tap_dance_action_on_dance_finished(action); | ||||
|             reset_tap_dance(&action->state); | ||||
|     action                             = &tap_dance_actions[TD_INDEX(active_td)]; | ||||
|     action->state.interrupted          = true; | ||||
|     action->state.interrupting_keycode = keycode; | ||||
|     process_tap_dance_action_on_dance_finished(action); | ||||
|  | ||||
|             // Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with | ||||
|             // modifiers), but these weak mods should not affect the keypress which interrupted the tap dance. | ||||
|             clear_weak_mods(); | ||||
|         } | ||||
|     } | ||||
|     // Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with | ||||
|     // modifiers), but these weak mods should not affect the keypress which interrupted the tap dance. | ||||
|     clear_weak_mods(); | ||||
| } | ||||
|  | ||||
| bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { | ||||
|     uint16_t               idx = keycode - QK_TAP_DANCE; | ||||
|     qk_tap_dance_action_t *action; | ||||
|  | ||||
|     switch (keycode) { | ||||
|         case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: | ||||
|             if ((int16_t)idx > highest_td) highest_td = idx; | ||||
|             action = &tap_dance_actions[idx]; | ||||
|             action = &tap_dance_actions[TD_INDEX(keycode)]; | ||||
|  | ||||
|             action->state.pressed = record->event.pressed; | ||||
|             if (record->event.pressed) { | ||||
|                 action->state.keycode = keycode; | ||||
|                 action->state.count++; | ||||
|                 action->state.timer = timer_read(); | ||||
| #ifndef NO_ACTION_ONESHOT | ||||
|                 action->state.oneshot_mods = get_oneshot_mods(); | ||||
| #else | ||||
|                 action->state.oneshot_mods = 0; | ||||
| #endif | ||||
|                 action->state.weak_mods = get_mods(); | ||||
|                 action->state.weak_mods |= get_weak_mods(); | ||||
|                 last_tap_time = timer_read(); | ||||
|                 process_tap_dance_action_on_each_tap(action); | ||||
|  | ||||
|                 last_td = keycode; | ||||
|                 active_td = action->state.finished ? 0 : keycode; | ||||
|             } else { | ||||
|                 if (action->state.count && action->state.finished) { | ||||
|                     reset_tap_dance(&action->state); | ||||
|                 if (action->state.finished) { | ||||
|                     process_tap_dance_action_on_reset(action); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @@ -166,35 +157,17 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { | ||||
| } | ||||
|  | ||||
| void tap_dance_task() { | ||||
|     if (highest_td == -1) return; | ||||
|     uint16_t tap_user_defined; | ||||
|     qk_tap_dance_action_t *action; | ||||
|  | ||||
|     for (uint8_t i = 0; i <= highest_td; i++) { | ||||
|         qk_tap_dance_action_t *action = &tap_dance_actions[i]; | ||||
|         if (action->custom_tapping_term > 0) { | ||||
|             tap_user_defined = action->custom_tapping_term; | ||||
|         } else { | ||||
|             tap_user_defined = GET_TAPPING_TERM(action->state.keycode, &(keyrecord_t){}); | ||||
|         } | ||||
|         if (action->state.count && timer_elapsed(action->state.timer) > tap_user_defined) { | ||||
|             process_tap_dance_action_on_dance_finished(action); | ||||
|             reset_tap_dance(&action->state); | ||||
|         } | ||||
|     if (!active_td || timer_elapsed(last_tap_time) <= GET_TAPPING_TERM(active_td, &(keyrecord_t){})) return; | ||||
|  | ||||
|     action = &tap_dance_actions[TD_INDEX(active_td)]; | ||||
|     if (!action->state.interrupted) { | ||||
|         process_tap_dance_action_on_dance_finished(action); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void reset_tap_dance(qk_tap_dance_state_t *state) { | ||||
|     qk_tap_dance_action_t *action; | ||||
|  | ||||
|     if (state->pressed) return; | ||||
|  | ||||
|     action = &tap_dance_actions[state->keycode - QK_TAP_DANCE]; | ||||
|  | ||||
|     process_tap_dance_action_on_reset(action); | ||||
|  | ||||
|     state->count                = 0; | ||||
|     state->interrupted          = false; | ||||
|     state->finished             = false; | ||||
|     state->interrupting_keycode = 0; | ||||
|     last_td                     = 0; | ||||
|     active_td = 0; | ||||
|     process_tap_dance_action_on_reset((qk_tap_dance_action_t *)state); | ||||
| } | ||||
|   | ||||
| @@ -22,30 +22,27 @@ | ||||
| #    include <inttypes.h> | ||||
|  | ||||
| typedef struct { | ||||
|     uint8_t  count; | ||||
|     uint8_t  oneshot_mods; | ||||
|     uint8_t  weak_mods; | ||||
|     uint16_t keycode; | ||||
|     uint16_t interrupting_keycode; | ||||
|     uint16_t timer; | ||||
|     bool     interrupted; | ||||
|     bool     pressed; | ||||
|     bool     finished; | ||||
|     uint8_t  count; | ||||
|     uint8_t  weak_mods; | ||||
| #    ifndef NO_ACTION_ONESHOT | ||||
|     uint8_t oneshot_mods; | ||||
| #    endif | ||||
|     bool pressed : 1; | ||||
|     bool finished : 1; | ||||
|     bool interrupted : 1; | ||||
| } qk_tap_dance_state_t; | ||||
|  | ||||
| #    define TD(n) (QK_TAP_DANCE | ((n)&0xFF)) | ||||
|  | ||||
| typedef void (*qk_tap_dance_user_fn_t)(qk_tap_dance_state_t *state, void *user_data); | ||||
|  | ||||
| typedef struct { | ||||
|     qk_tap_dance_state_t state; | ||||
|     struct { | ||||
|         qk_tap_dance_user_fn_t on_each_tap; | ||||
|         qk_tap_dance_user_fn_t on_dance_finished; | ||||
|         qk_tap_dance_user_fn_t on_reset; | ||||
|     } fn; | ||||
|     qk_tap_dance_state_t state; | ||||
|     uint16_t             custom_tapping_term; | ||||
|     void *               user_data; | ||||
|     void *user_data; | ||||
| } qk_tap_dance_action_t; | ||||
|  | ||||
| typedef struct { | ||||
| @@ -62,31 +59,31 @@ typedef struct { | ||||
| #    define ACTION_TAP_DANCE_DOUBLE(kc1, kc2) \ | ||||
|         { .fn = {qk_tap_dance_pair_on_each_tap, qk_tap_dance_pair_finished, qk_tap_dance_pair_reset}, .user_data = (void *)&((qk_tap_dance_pair_t){kc1, kc2}), } | ||||
|  | ||||
| #    define ACTION_TAP_DANCE_DUAL_ROLE(kc, layer) \ | ||||
| #    define ACTION_TAP_DANCE_LAYER_MOVE(kc, layer) \ | ||||
|         { .fn = {qk_tap_dance_dual_role_on_each_tap, qk_tap_dance_dual_role_finished, qk_tap_dance_dual_role_reset}, .user_data = (void *)&((qk_tap_dance_dual_role_t){kc, layer, layer_move}), } | ||||
|  | ||||
| #    define ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer) \ | ||||
|         { .fn = {NULL, qk_tap_dance_dual_role_finished, qk_tap_dance_dual_role_reset}, .user_data = (void *)&((qk_tap_dance_dual_role_t){kc, layer, layer_invert}), } | ||||
|  | ||||
| #    define ACTION_TAP_DANCE_LAYER_MOVE(kc, layer) ACTION_TAP_DANCE_DUAL_ROLE(kc, layer) | ||||
|  | ||||
| #    define ACTION_TAP_DANCE_FN(user_fn) \ | ||||
|         { .fn = {NULL, user_fn, NULL}, .user_data = NULL, } | ||||
|  | ||||
| #    define ACTION_TAP_DANCE_FN_ADVANCED(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset) \ | ||||
|         { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset}, .user_data = NULL, } | ||||
|  | ||||
| #    define ACTION_TAP_DANCE_FN_ADVANCED_TIME(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, tap_specific_tapping_term) \ | ||||
|         { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset}, .user_data = NULL, .custom_tapping_term = tap_specific_tapping_term, } | ||||
| #    define TD(n) (QK_TAP_DANCE | TD_INDEX(n)) | ||||
| #    define TD_INDEX(code) ((code)&0xFF) | ||||
| #    define TAP_DANCE_KEYCODE(state) TD(((qk_tap_dance_action_t *)state) - tap_dance_actions) | ||||
|  | ||||
| extern qk_tap_dance_action_t tap_dance_actions[]; | ||||
|  | ||||
| void reset_tap_dance(qk_tap_dance_state_t *state); | ||||
|  | ||||
| /* To be used internally */ | ||||
|  | ||||
| void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record); | ||||
| bool process_tap_dance(uint16_t keycode, keyrecord_t *record); | ||||
| void tap_dance_task(void); | ||||
| void reset_tap_dance(qk_tap_dance_state_t *state); | ||||
|  | ||||
| void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data); | ||||
| void qk_tap_dance_pair_finished(qk_tap_dance_state_t *state, void *user_data); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user