process_tap_dance.c (8449B)
1 /* Copyright 2016 Jack Humbert 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 2 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include "process_tap_dance.h" 18 #include "quantum.h" 19 #include "action_layer.h" 20 #include "action_tapping.h" 21 #include "action_util.h" 22 #include "timer.h" 23 #include "wait.h" 24 #include "keymap_introspection.h" 25 26 static uint16_t active_td; 27 28 #ifndef TAP_DANCE_MAX_SIMULTANEOUS 29 # define TAP_DANCE_MAX_SIMULTANEOUS 3 30 #endif 31 32 static tap_dance_state_t tap_dance_states[TAP_DANCE_MAX_SIMULTANEOUS]; 33 34 static uint16_t last_tap_time; 35 36 static tap_dance_state_t *tap_dance_get_or_allocate_state(uint8_t tap_dance_idx, bool allocate) { 37 uint8_t i; 38 if (tap_dance_idx >= tap_dance_count()) { 39 return NULL; 40 } 41 // Search for a state already used for this keycode 42 for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { 43 if (tap_dance_states[i].in_use && tap_dance_states[i].index == tap_dance_idx) { 44 return &tap_dance_states[i]; 45 } 46 } 47 // No existing state found; bail out if new state allocation is not allowed 48 if (!allocate) { 49 return NULL; 50 } 51 // Search for the first available state 52 for (i = 0; i < TAP_DANCE_MAX_SIMULTANEOUS; i++) { 53 if (!tap_dance_states[i].in_use) { 54 tap_dance_states[i].index = tap_dance_idx; 55 tap_dance_states[i].in_use = true; 56 return &tap_dance_states[i]; 57 } 58 } 59 // No states are available, tap dance won't happen 60 return NULL; 61 } 62 63 tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx) { 64 return tap_dance_get_or_allocate_state(tap_dance_idx, false); 65 } 66 67 void tap_dance_pair_on_each_tap(tap_dance_state_t *state, void *user_data) { 68 tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data; 69 70 if (state->count == 2) { 71 register_code16(pair->kc2); 72 state->finished = true; 73 } 74 } 75 76 void tap_dance_pair_finished(tap_dance_state_t *state, void *user_data) { 77 tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data; 78 79 register_code16(pair->kc1); 80 } 81 82 void tap_dance_pair_reset(tap_dance_state_t *state, void *user_data) { 83 tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data; 84 85 if (state->count == 1) { 86 wait_ms(TAP_CODE_DELAY); 87 unregister_code16(pair->kc1); 88 } else if (state->count == 2) { 89 unregister_code16(pair->kc2); 90 } 91 } 92 93 void tap_dance_dual_role_on_each_tap(tap_dance_state_t *state, void *user_data) { 94 tap_dance_dual_role_t *pair = (tap_dance_dual_role_t *)user_data; 95 96 if (state->count == 2) { 97 layer_move(pair->layer); 98 state->finished = true; 99 } 100 } 101 102 void tap_dance_dual_role_finished(tap_dance_state_t *state, void *user_data) { 103 tap_dance_dual_role_t *pair = (tap_dance_dual_role_t *)user_data; 104 105 if (state->count == 1) { 106 register_code16(pair->kc); 107 } else if (state->count == 2) { 108 pair->layer_function(pair->layer); 109 } 110 } 111 112 void tap_dance_dual_role_reset(tap_dance_state_t *state, void *user_data) { 113 tap_dance_dual_role_t *pair = (tap_dance_dual_role_t *)user_data; 114 115 if (state->count == 1) { 116 wait_ms(TAP_CODE_DELAY); 117 unregister_code16(pair->kc); 118 } 119 } 120 121 static inline void _process_tap_dance_action_fn(tap_dance_state_t *state, void *user_data, tap_dance_user_fn_t fn) { 122 if (fn) { 123 fn(state, user_data); 124 } 125 } 126 127 static inline void process_tap_dance_action_on_each_tap(tap_dance_action_t *action, tap_dance_state_t *state) { 128 state->count++; 129 state->weak_mods = get_mods(); 130 state->weak_mods |= get_weak_mods(); 131 #ifndef NO_ACTION_ONESHOT 132 state->oneshot_mods = get_oneshot_mods(); 133 #endif 134 _process_tap_dance_action_fn(state, action->user_data, action->fn.on_each_tap); 135 } 136 137 static inline void process_tap_dance_action_on_each_release(tap_dance_action_t *action, tap_dance_state_t *state) { 138 _process_tap_dance_action_fn(state, action->user_data, action->fn.on_each_release); 139 } 140 141 static inline void process_tap_dance_action_on_reset(tap_dance_action_t *action, tap_dance_state_t *state) { 142 _process_tap_dance_action_fn(state, action->user_data, action->fn.on_reset); 143 del_weak_mods(state->weak_mods); 144 #ifndef NO_ACTION_ONESHOT 145 del_mods(state->oneshot_mods); 146 #endif 147 send_keyboard_report(); 148 // Clear the tap dance state and mark it as unused 149 memset(state, 0, sizeof(tap_dance_state_t)); 150 } 151 152 static inline void process_tap_dance_action_on_dance_finished(tap_dance_action_t *action, tap_dance_state_t *state) { 153 if (!state->finished) { 154 state->finished = true; 155 add_weak_mods(state->weak_mods); 156 #ifndef NO_ACTION_ONESHOT 157 add_mods(state->oneshot_mods); 158 #endif 159 send_keyboard_report(); 160 _process_tap_dance_action_fn(state, action->user_data, action->fn.on_dance_finished); 161 } 162 active_td = 0; 163 if (!state->pressed) { 164 // There will not be a key release event, so reset now. 165 process_tap_dance_action_on_reset(action, state); 166 } 167 } 168 169 bool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) { 170 tap_dance_action_t *action; 171 tap_dance_state_t *state; 172 173 if (!record->event.pressed) return false; 174 175 if (!active_td || keycode == active_td) return false; 176 177 action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)); 178 state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); 179 if (state == NULL) { 180 return false; 181 } 182 state->interrupted = true; 183 state->interrupting_keycode = keycode; 184 process_tap_dance_action_on_dance_finished(action, state); 185 186 // Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with 187 // modifiers), but these weak mods should not affect the keypress which interrupted the tap dance. 188 clear_weak_mods(); 189 190 // Signal that a tap dance has been finished due to being interrupted, 191 // therefore the keymap lookup for the currently processed event needs to 192 // be repeated with the current layer state that might have been updated by 193 // the finished tap dance. 194 return true; 195 } 196 197 bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { 198 uint8_t td_index; 199 tap_dance_action_t *action; 200 tap_dance_state_t *state; 201 202 switch (keycode) { 203 case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: 204 td_index = QK_TAP_DANCE_GET_INDEX(keycode); 205 if (td_index >= tap_dance_count()) { 206 return false; 207 } 208 action = tap_dance_get(td_index); 209 state = tap_dance_get_or_allocate_state(td_index, record->event.pressed); 210 if (state == NULL) { 211 return false; 212 } 213 state->pressed = record->event.pressed; 214 if (record->event.pressed) { 215 last_tap_time = timer_read(); 216 process_tap_dance_action_on_each_tap(action, state); 217 active_td = state->finished ? 0 : keycode; 218 } else { 219 process_tap_dance_action_on_each_release(action, state); 220 if (state->finished) { 221 process_tap_dance_action_on_reset(action, state); 222 if (active_td == keycode) { 223 active_td = 0; 224 } 225 } 226 } 227 228 break; 229 } 230 231 return true; 232 } 233 234 void tap_dance_task(void) { 235 tap_dance_action_t *action; 236 tap_dance_state_t *state; 237 238 if (!active_td || timer_elapsed(last_tap_time) <= GET_TAPPING_TERM(active_td, &(keyrecord_t){})) return; 239 240 action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td)); 241 state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(active_td)); 242 if (state != NULL && !state->interrupted) { 243 process_tap_dance_action_on_dance_finished(action, state); 244 } 245 } 246 247 void reset_tap_dance(tap_dance_state_t *state) { 248 active_td = 0; 249 process_tap_dance_action_on_reset(tap_dance_get(state->index), state); 250 }