qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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 }