tap-dance: Restructure code and document in more detail (#16394)
This commit is contained in:
		
							parent
							
								
									b17324498e
								
							
						
					
					
						commit
						1706da9054
					
				
					 52 changed files with 970 additions and 244 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue