aboutsummaryrefslogtreecommitdiff
path: root/quantum/process_keycode/process_tap_dance.c
diff options
context:
space:
mode:
authorGergely Nagy <algernon@madhouse-project.org>2016-08-17 13:04:50 +0200
committerGergely Nagy <algernon@madhouse-project.org>2016-08-17 15:05:58 +0200
commit29f64d7a93d941167c6c6e95f893ab84586b2205 (patch)
tree673ae25744608863609e022dbb0600c830893157 /quantum/process_keycode/process_tap_dance.c
parentd78058cc75a9b05a6885991506d5f807ebb2a9f9 (diff)
downloadqmk_firmware-29f64d7a93d941167c6c6e95f893ab84586b2205.tar.gz
qmk_firmware-29f64d7a93d941167c6c6e95f893ab84586b2205.zip
tap-dance: Major rework, to make it more reliable
This reworks how the tap-dance feature works: instead of one global state, we have a state for each tap-dance key, so we can cancel them when another tap-dance key is in flight. This fixes #527. Since we have a state for each key, we can avoid situation where a keyup would mess with our global state. This fixes #563. And while here, we also make sure to fire events only once, and this fixes #574. There is one breaking change, though: tap-dance debugging support was removed, because dumping the whole state would increase the firmware size too much. Any keymap that made use of this, will have to be updated (but there's no such keymap in the repo). Also, there's a nice trick used in this rework: we need to iterate through tap_dance_actions in a few places, to check for timeouts, and so on. For this, we'd need to know the size of the array. We can't discover that at compile-time, because tap-dance gets compiled separately. We'd like to avoid having to terminate the list with a sentinel value, because that would require updates to all keymaps that use the feature. So, we keep track of the highest tap-dance code seen so far, and iterate until that index. Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Diffstat (limited to 'quantum/process_keycode/process_tap_dance.c')
-rw-r--r--quantum/process_keycode/process_tap_dance.c121
1 files changed, 61 insertions, 60 deletions
diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c
index bab5c4dbd..e152f2350 100644
--- a/quantum/process_keycode/process_tap_dance.c
+++ b/quantum/process_keycode/process_tap_dance.c
@@ -1,19 +1,8 @@
1#include "quantum.h" 1#include "quantum.h"
2#include "action_tapping.h" 2#include "action_tapping.h"
3 3
4static qk_tap_dance_state_t qk_tap_dance_state; 4static uint16_t last_td;
5bool td_debug_enable = false; 5static int8_t highest_td = -1;
6
7#if CONSOLE_ENABLE
8#define td_debug(s) if (td_debug_enable) \
9 { \
10 xprintf ("D:tap_dance:%s:%s = { keycode = %d, count = %d, active = %d, pressed = %d }\n", __FUNCTION__, s, \
11 qk_tap_dance_state.keycode, qk_tap_dance_state.count, \
12 qk_tap_dance_state.active, qk_tap_dance_state.pressed); \
13 }
14#else
15#define td_debug(s)
16#endif
17 6
18void qk_tap_dance_pair_finished (qk_tap_dance_state_t *state, void *user_data) { 7void qk_tap_dance_pair_finished (qk_tap_dance_state_t *state, void *user_data) {
19 qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data; 8 qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data;
@@ -36,98 +25,110 @@ void qk_tap_dance_pair_reset (qk_tap_dance_state_t *state, void *user_data) {
36} 25}
37 26
38static inline void _process_tap_dance_action_fn (qk_tap_dance_state_t *state, 27static inline void _process_tap_dance_action_fn (qk_tap_dance_state_t *state,
39 void *user_data, 28 void *user_data,
40 qk_tap_dance_user_fn_t fn) 29 qk_tap_dance_user_fn_t fn)
41{ 30{
42 if (fn) { 31 if (fn) {
43 fn(state, user_data); 32 fn(state, user_data);
44 } 33 }
45} 34}
46 35
47static inline void process_tap_dance_action_on_each_tap (qk_tap_dance_action_t action) 36static inline void process_tap_dance_action_on_each_tap (qk_tap_dance_action_t *action)
48{ 37{
49 td_debug("trigger"); 38 _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_each_tap);
50 _process_tap_dance_action_fn (&qk_tap_dance_state, action.user_data, action.fn.on_each_tap);
51} 39}
52 40
53static inline void process_tap_dance_action_on_dance_finished (qk_tap_dance_action_t action) 41static inline void process_tap_dance_action_on_dance_finished (qk_tap_dance_action_t *action)
54{ 42{
55 td_debug("trigger"); 43 if (action->state.finished)
56 _process_tap_dance_action_fn (&qk_tap_dance_state, action.user_data, action.fn.on_dance_finished); 44 return;
45 action->state.finished = true;
46 _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_dance_finished);
57} 47}
58 48
59static inline void process_tap_dance_action_on_reset (qk_tap_dance_action_t action) 49static inline void process_tap_dance_action_on_reset (qk_tap_dance_action_t *action)
60{ 50{
61 td_debug("trigger") 51 _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_reset);
62 _process_tap_dance_action_fn (&qk_tap_dance_state, action.user_data, action.fn.on_reset);
63} 52}
64 53
65bool process_tap_dance(uint16_t keycode, keyrecord_t *record) { 54bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
66 bool r = true;
67 uint16_t idx = keycode - QK_TAP_DANCE; 55 uint16_t idx = keycode - QK_TAP_DANCE;
68 qk_tap_dance_action_t action; 56 qk_tap_dance_action_t *action;
57
58 if (last_td && last_td != keycode) {
59 (&tap_dance_actions[last_td - QK_TAP_DANCE])->state.interrupted = true;
60 }
69 61
70 switch(keycode) { 62 switch(keycode) {
71 case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: 63 case QK_TAP_DANCE ... QK_TAP_DANCE_MAX:
72 action = tap_dance_actions[idx]; 64 if ((int16_t)idx > highest_td)
73 65 highest_td = idx;
74 process_tap_dance_action_on_each_tap (action); 66 action = &tap_dance_actions[idx];
75 if (qk_tap_dance_state.keycode && qk_tap_dance_state.keycode != keycode) {
76 process_tap_dance_action_on_dance_finished (action);
77 } else if (qk_tap_dance_state.active && qk_tap_dance_state.pressed) {
78 reset_tap_dance (&qk_tap_dance_state);
79 } else {
80 r = false;
81 }
82 67
83 qk_tap_dance_state.active = true; 68 action->state.keycode = keycode;
84 qk_tap_dance_state.pressed = record->event.pressed; 69 action->state.pressed = record->event.pressed;
85 if (record->event.pressed) { 70 if (record->event.pressed) {
86 qk_tap_dance_state.keycode = keycode; 71 action->state.count++;
87 qk_tap_dance_state.timer = timer_read (); 72 action->state.timer = timer_read();
88 qk_tap_dance_state.count++; 73
74 if (last_td && last_td != keycode) {
75 qk_tap_dance_action_t *paction = &tap_dance_actions[last_td - QK_TAP_DANCE];
76 paction->state.interrupted = true;
77 process_tap_dance_action_on_dance_finished (paction);
78 reset_tap_dance (&paction->state);
79 }
89 } 80 }
81 last_td = keycode;
82
90 break; 83 break;
91 84
92 default: 85 default:
93 if (qk_tap_dance_state.keycode) { 86 if (!record->event.pressed)
94 // if we are here, the tap dance was interrupted by a different key 87 return true;
95 idx = qk_tap_dance_state.keycode - QK_TAP_DANCE; 88
96 action = tap_dance_actions[idx]; 89 if (highest_td == -1)
90 return true;
97 91
98 process_tap_dance_action_on_each_tap (action); 92 for (int i = 0; i <= highest_td; i++) {
93 action = &tap_dance_actions[i];
94 if (action->state.count == 0)
95 continue;
96 action->state.interrupted = true;
99 process_tap_dance_action_on_dance_finished (action); 97 process_tap_dance_action_on_dance_finished (action);
100 reset_tap_dance (&qk_tap_dance_state); 98 reset_tap_dance (&action->state);
101 qk_tap_dance_state.active = false;
102 } 99 }
103 break; 100 break;
104 } 101 }
105 102
106 return r; 103 return true;
107} 104}
108 105
109void matrix_scan_tap_dance () { 106void matrix_scan_tap_dance () {
110 if (qk_tap_dance_state.active && timer_elapsed (qk_tap_dance_state.timer) > TAPPING_TERM) { 107 if (highest_td == -1)
111 // if we are here, the tap dance was timed out 108 return;
112 uint16_t idx = qk_tap_dance_state.keycode - QK_TAP_DANCE; 109
113 qk_tap_dance_action_t action = tap_dance_actions[idx]; 110 for (int i = 0; i <= highest_td; i++) {
111 qk_tap_dance_action_t *action = &tap_dance_actions[i];
114 112
115 process_tap_dance_action_on_dance_finished (action); 113 if (action->state.count && timer_elapsed (action->state.timer) > TAPPING_TERM) {
116 reset_tap_dance (&qk_tap_dance_state); 114 process_tap_dance_action_on_dance_finished (action);
115 reset_tap_dance (&action->state);
116 }
117 } 117 }
118} 118}
119 119
120void reset_tap_dance (qk_tap_dance_state_t *state) { 120void reset_tap_dance (qk_tap_dance_state_t *state) {
121 uint16_t idx = state->keycode - QK_TAP_DANCE; 121 qk_tap_dance_action_t *action;
122 qk_tap_dance_action_t action;
123 122
124 if (state->pressed) 123 if (state->pressed)
125 return; 124 return;
126 125
127 action = tap_dance_actions[idx]; 126 action = &tap_dance_actions[state->keycode - QK_TAP_DANCE];
127
128 process_tap_dance_action_on_reset (action); 128 process_tap_dance_action_on_reset (action);
129 129
130 state->keycode = 0;
131 state->count = 0; 130 state->count = 0;
132 state->active = false; 131 state->interrupted = false;
132 state->finished = false;
133 last_td = 0;
133} 134}