diff options
author | Pete Sevander <pete.sevander@gmail.com> | 2021-08-06 02:44:57 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-06 09:44:57 +1000 |
commit | 7e983796e18e7401c062c158f23966aeb7b1405b (patch) | |
tree | 88d6897d7ca4f6103f03c1941dac6736ba477361 /tmk_core/common | |
parent | 07553b41f0a03ca6549c09cecf9cd3dec7332346 (diff) | |
download | qmk_firmware-7e983796e18e7401c062c158f23966aeb7b1405b.tar.gz qmk_firmware-7e983796e18e7401c062c158f23966aeb7b1405b.zip |
Process combos earlier & overlapping combos (#8591)
* Combo processing improvements.
Now it is possible to use ModTap and LayerTap keys as part of combos.
Overlapping combos also don't trigger all the combos, just exactly the
one that you press.
New settings:
- COMBO_MUST_HOLD_MODS
- COMBO_MOD_TERM
- COMBO_TERM_PER_COMBO
- COMBO_MUST_HOLD_PER_COMBO
- COMBO_STRICT_TIMER
- COMBO_NO_TIMER
* Remove the size flags from combo_t struct boolean members.
This in the end actually saves space as the members are accessed so many
times. The amount of operations needed to access the bits uses more
memory than setting the size saves.
* Fix `process_combo_key_release` not called correctly with tap-only combos
* Fix not passing a pointer when NO_ACTION_TAPPING is defined.
* Docs for `COMBO_ONLY_FROM_LAYER`
* Update docs/feature_combo.md
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
* Update quantum/process_keycode/process_combo.c
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
* Add `EXTRA_SHORT_COMBOS` option.
Stuff combo's `disabled` and `active` flags into `state`. Possibly can
save some space.
* Add more examples and clarify things with dict management system.
- Simple examples now has a combo that has modifiers included.
- The slightly more advanced examples now are actually more advanced
instead of just `tap_code16(<modded-keycode>)`.
- Added a note that `COMBO_ACTION`s are not needed anymore as you can
just use custom keycodes.
- Added a note that the `g/keymap_combo.h` macros use the
`process_combo_event` function and that it is not usable in one's
keymap afterwards.
* Update docs/feature_combo.md
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
* Update docs/feature_combo.md
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
* Update docs/feature_combo.md
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
* Update docs/feature_combo.md
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
* Update docs/feature_combo.md
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
* Change "the" combo action example to "email" example.
* Update docs/feature_combo.md
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
* Fix sneaky infinite loop with `combo_disable()`
No need to call `dump_key_buffer` when disabling combos because the
buffer is either being dumped if a combo-key was pressed, or the buffer is empty
if a non-combo-key is pressed.
* Update docs/feature_combo.md
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
* Update docs/feature_combo.md
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
Co-authored-by: Drashna Jaelre <drashna@live.com>
Diffstat (limited to 'tmk_core/common')
-rw-r--r-- | tmk_core/common/action.c | 37 | ||||
-rw-r--r-- | tmk_core/common/action.h | 5 | ||||
-rw-r--r-- | tmk_core/common/action_tapping.c | 41 | ||||
-rw-r--r-- | tmk_core/common/action_tapping.h | 1 |
4 files changed, 68 insertions, 16 deletions
diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index bd41d28b6..d19fd2a04 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c | |||
@@ -55,6 +55,8 @@ __attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrec | |||
55 | __attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; } | 55 | __attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; } |
56 | #endif | 56 | #endif |
57 | 57 | ||
58 | __attribute__((weak)) bool pre_process_record_quantum(keyrecord_t *record) { return true; } | ||
59 | |||
58 | #ifndef TAP_CODE_DELAY | 60 | #ifndef TAP_CODE_DELAY |
59 | # define TAP_CODE_DELAY 0 | 61 | # define TAP_CODE_DELAY 0 |
60 | #endif | 62 | #endif |
@@ -106,9 +108,13 @@ void action_exec(keyevent_t event) { | |||
106 | #endif | 108 | #endif |
107 | 109 | ||
108 | #ifndef NO_ACTION_TAPPING | 110 | #ifndef NO_ACTION_TAPPING |
109 | action_tapping_process(record); | 111 | if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { |
112 | action_tapping_process(record); | ||
113 | } | ||
110 | #else | 114 | #else |
111 | process_record(&record); | 115 | if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { |
116 | process_record(&record); | ||
117 | } | ||
112 | if (!IS_NOEVENT(record.event)) { | 118 | if (!IS_NOEVENT(record.event)) { |
113 | dprint("processed: "); | 119 | dprint("processed: "); |
114 | debug_record(record); | 120 | debug_record(record); |
@@ -206,7 +212,16 @@ void process_record(keyrecord_t *record) { | |||
206 | } | 212 | } |
207 | 213 | ||
208 | void process_record_handler(keyrecord_t *record) { | 214 | void process_record_handler(keyrecord_t *record) { |
215 | #ifdef COMBO_ENABLE | ||
216 | action_t action; | ||
217 | if (record->keycode) { | ||
218 | action = action_for_keycode(record->keycode); | ||
219 | } else { | ||
220 | action = store_or_get_action(record->event.pressed, record->event.key); | ||
221 | } | ||
222 | #else | ||
209 | action_t action = store_or_get_action(record->event.pressed, record->event.key); | 223 | action_t action = store_or_get_action(record->event.pressed, record->event.key); |
224 | #endif | ||
210 | dprint("ACTION: "); | 225 | dprint("ACTION: "); |
211 | debug_action(action); | 226 | debug_action(action); |
212 | #ifndef NO_ACTION_LAYER | 227 | #ifndef NO_ACTION_LAYER |
@@ -994,6 +1009,24 @@ bool is_tap_key(keypos_t key) { | |||
994 | * | 1009 | * |
995 | * FIXME: Needs documentation. | 1010 | * FIXME: Needs documentation. |
996 | */ | 1011 | */ |
1012 | bool is_tap_record(keyrecord_t *record) { | ||
1013 | #ifdef COMBO_ENABLE | ||
1014 | action_t action; | ||
1015 | if (record->keycode) { | ||
1016 | action = action_for_keycode(record->keycode); | ||
1017 | } else { | ||
1018 | action = layer_switch_get_action(record->event.key); | ||
1019 | } | ||
1020 | #else | ||
1021 | action_t action = layer_switch_get_action(record->event.key); | ||
1022 | #endif | ||
1023 | return is_tap_action(action); | ||
1024 | } | ||
1025 | |||
1026 | /** \brief Utilities for actions. (FIXME: Needs better description) | ||
1027 | * | ||
1028 | * FIXME: Needs documentation. | ||
1029 | */ | ||
997 | bool is_tap_action(action_t action) { | 1030 | bool is_tap_action(action_t action) { |
998 | switch (action.kind.id) { | 1031 | switch (action.kind.id) { |
999 | case ACT_LMODS_TAP: | 1032 | case ACT_LMODS_TAP: |
diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h index 8cb4722c6..3d357b33b 100644 --- a/tmk_core/common/action.h +++ b/tmk_core/common/action.h | |||
@@ -53,6 +53,9 @@ typedef struct { | |||
53 | #ifndef NO_ACTION_TAPPING | 53 | #ifndef NO_ACTION_TAPPING |
54 | tap_t tap; | 54 | tap_t tap; |
55 | #endif | 55 | #endif |
56 | #ifdef COMBO_ENABLE | ||
57 | uint16_t keycode; | ||
58 | #endif | ||
56 | } keyrecord_t; | 59 | } keyrecord_t; |
57 | 60 | ||
58 | /* Execute action per keyevent */ | 61 | /* Execute action per keyevent */ |
@@ -60,6 +63,7 @@ void action_exec(keyevent_t event); | |||
60 | 63 | ||
61 | /* action for key */ | 64 | /* action for key */ |
62 | action_t action_for_key(uint8_t layer, keypos_t key); | 65 | action_t action_for_key(uint8_t layer, keypos_t key); |
66 | action_t action_for_keycode(uint16_t keycode); | ||
63 | 67 | ||
64 | /* macro */ | 68 | /* macro */ |
65 | const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt); | 69 | const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt); |
@@ -111,6 +115,7 @@ void clear_keyboard_but_mods(void); | |||
111 | void clear_keyboard_but_mods_and_keys(void); | 115 | void clear_keyboard_but_mods_and_keys(void); |
112 | void layer_switch(uint8_t new_layer); | 116 | void layer_switch(uint8_t new_layer); |
113 | bool is_tap_key(keypos_t key); | 117 | bool is_tap_key(keypos_t key); |
118 | bool is_tap_record(keyrecord_t *record); | ||
114 | bool is_tap_action(action_t action); | 119 | bool is_tap_action(action_t action); |
115 | 120 | ||
116 | #ifndef NO_ACTION_TAPPING | 121 | #ifndef NO_ACTION_TAPPING |
diff --git a/tmk_core/common/action_tapping.c b/tmk_core/common/action_tapping.c index 56044e096..1701ae471 100644 --- a/tmk_core/common/action_tapping.c +++ b/tmk_core/common/action_tapping.c | |||
@@ -18,11 +18,16 @@ | |||
18 | # define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed) | 18 | # define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed) |
19 | # define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed) | 19 | # define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed) |
20 | # define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k))) | 20 | # define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k))) |
21 | #ifndef COMBO_ENABLE | ||
22 | # define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key))) | ||
23 | #else | ||
24 | # define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode) | ||
25 | #endif | ||
21 | 26 | ||
22 | __attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; } | 27 | __attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; } |
23 | 28 | ||
24 | # ifdef TAPPING_TERM_PER_KEY | 29 | # ifdef TAPPING_TERM_PER_KEY |
25 | # define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_event_keycode(tapping_key.event, false), &tapping_key)) | 30 | # define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_record_keycode(&tapping_key, false), &tapping_key)) |
26 | # else | 31 | # else |
27 | # define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM) | 32 | # define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM) |
28 | # endif | 33 | # endif |
@@ -103,7 +108,7 @@ bool process_tapping(keyrecord_t *keyp) { | |||
103 | if (IS_TAPPING_PRESSED()) { | 108 | if (IS_TAPPING_PRESSED()) { |
104 | if (WITHIN_TAPPING_TERM(event)) { | 109 | if (WITHIN_TAPPING_TERM(event)) { |
105 | if (tapping_key.tap.count == 0) { | 110 | if (tapping_key.tap.count == 0) { |
106 | if (IS_TAPPING_KEY(event.key) && !event.pressed) { | 111 | if (IS_TAPPING_RECORD(keyp) && !event.pressed) { |
107 | // first tap! | 112 | // first tap! |
108 | debug("Tapping: First tap(0->1).\n"); | 113 | debug("Tapping: First tap(0->1).\n"); |
109 | tapping_key.tap.count = 1; | 114 | tapping_key.tap.count = 1; |
@@ -122,14 +127,14 @@ bool process_tapping(keyrecord_t *keyp) { | |||
122 | # if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) | 127 | # if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) |
123 | else if ((( | 128 | else if ((( |
124 | # ifdef TAPPING_TERM_PER_KEY | 129 | # ifdef TAPPING_TERM_PER_KEY |
125 | get_tapping_term(get_event_keycode(tapping_key.event, false), keyp) | 130 | get_tapping_term(get_record_keycode(&tapping_key, false), keyp) |
126 | # else | 131 | # else |
127 | TAPPING_TERM | 132 | TAPPING_TERM |
128 | # endif | 133 | # endif |
129 | >= 500) | 134 | >= 500) |
130 | 135 | ||
131 | # ifdef PERMISSIVE_HOLD_PER_KEY | 136 | # ifdef PERMISSIVE_HOLD_PER_KEY |
132 | || get_permissive_hold(get_event_keycode(tapping_key.event, false), keyp) | 137 | || get_permissive_hold(get_record_keycode(&tapping_key, false), keyp) |
133 | # elif defined(PERMISSIVE_HOLD) | 138 | # elif defined(PERMISSIVE_HOLD) |
134 | || true | 139 | || true |
135 | # endif | 140 | # endif |
@@ -177,7 +182,7 @@ bool process_tapping(keyrecord_t *keyp) { | |||
177 | } | 182 | } |
178 | // tap_count > 0 | 183 | // tap_count > 0 |
179 | else { | 184 | else { |
180 | if (IS_TAPPING_KEY(event.key) && !event.pressed) { | 185 | if (IS_TAPPING_RECORD(keyp) && !event.pressed) { |
181 | debug("Tapping: Tap release("); | 186 | debug("Tapping: Tap release("); |
182 | debug_dec(tapping_key.tap.count); | 187 | debug_dec(tapping_key.tap.count); |
183 | debug(")\n"); | 188 | debug(")\n"); |
@@ -186,11 +191,15 @@ bool process_tapping(keyrecord_t *keyp) { | |||
186 | tapping_key = *keyp; | 191 | tapping_key = *keyp; |
187 | debug_tapping_key(); | 192 | debug_tapping_key(); |
188 | return true; | 193 | return true; |
189 | } else if (is_tap_key(event.key) && event.pressed) { | 194 | } else if (is_tap_record(keyp) && event.pressed) { |
190 | if (tapping_key.tap.count > 1) { | 195 | if (tapping_key.tap.count > 1) { |
191 | debug("Tapping: Start new tap with releasing last tap(>1).\n"); | 196 | debug("Tapping: Start new tap with releasing last tap(>1).\n"); |
192 | // unregister key | 197 | // unregister key |
193 | process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false}); | 198 | process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false, |
199 | #ifdef COMBO_ENABLE | ||
200 | .keycode = tapping_key.keycode, | ||
201 | #endif | ||
202 | }); | ||
194 | } else { | 203 | } else { |
195 | debug("Tapping: Start while last tap(1).\n"); | 204 | debug("Tapping: Start while last tap(1).\n"); |
196 | } | 205 | } |
@@ -218,17 +227,21 @@ bool process_tapping(keyrecord_t *keyp) { | |||
218 | debug_tapping_key(); | 227 | debug_tapping_key(); |
219 | return false; | 228 | return false; |
220 | } else { | 229 | } else { |
221 | if (IS_TAPPING_KEY(event.key) && !event.pressed) { | 230 | if (IS_TAPPING_RECORD(keyp) && !event.pressed) { |
222 | debug("Tapping: End. last timeout tap release(>0)."); | 231 | debug("Tapping: End. last timeout tap release(>0)."); |
223 | keyp->tap = tapping_key.tap; | 232 | keyp->tap = tapping_key.tap; |
224 | process_record(keyp); | 233 | process_record(keyp); |
225 | tapping_key = (keyrecord_t){}; | 234 | tapping_key = (keyrecord_t){}; |
226 | return true; | 235 | return true; |
227 | } else if (is_tap_key(event.key) && event.pressed) { | 236 | } else if (is_tap_record(keyp) && event.pressed) { |
228 | if (tapping_key.tap.count > 1) { | 237 | if (tapping_key.tap.count > 1) { |
229 | debug("Tapping: Start new tap with releasing last timeout tap(>1).\n"); | 238 | debug("Tapping: Start new tap with releasing last timeout tap(>1).\n"); |
230 | // unregister key | 239 | // unregister key |
231 | process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false}); | 240 | process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false, |
241 | #ifdef COMBO_ENABLE | ||
242 | .keycode = tapping_key.keycode, | ||
243 | #endif | ||
244 | }); | ||
232 | } else { | 245 | } else { |
233 | debug("Tapping: Start while last timeout tap(1).\n"); | 246 | debug("Tapping: Start while last timeout tap(1).\n"); |
234 | } | 247 | } |
@@ -248,12 +261,12 @@ bool process_tapping(keyrecord_t *keyp) { | |||
248 | } else if (IS_TAPPING_RELEASED()) { | 261 | } else if (IS_TAPPING_RELEASED()) { |
249 | if (WITHIN_TAPPING_TERM(event)) { | 262 | if (WITHIN_TAPPING_TERM(event)) { |
250 | if (event.pressed) { | 263 | if (event.pressed) { |
251 | if (IS_TAPPING_KEY(event.key)) { | 264 | if (IS_TAPPING_RECORD(keyp)) { |
252 | //# ifndef TAPPING_FORCE_HOLD | 265 | //# ifndef TAPPING_FORCE_HOLD |
253 | # if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY) | 266 | # if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY) |
254 | if ( | 267 | if ( |
255 | # ifdef TAPPING_FORCE_HOLD_PER_KEY | 268 | # ifdef TAPPING_FORCE_HOLD_PER_KEY |
256 | !get_tapping_force_hold(get_event_keycode(tapping_key.event, false), keyp) && | 269 | !get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) && |
257 | # endif | 270 | # endif |
258 | !tapping_key.tap.interrupted && tapping_key.tap.count > 0) { | 271 | !tapping_key.tap.interrupted && tapping_key.tap.count > 0) { |
259 | // sequential tap. | 272 | // sequential tap. |
@@ -271,7 +284,7 @@ bool process_tapping(keyrecord_t *keyp) { | |||
271 | // FIX: start new tap again | 284 | // FIX: start new tap again |
272 | tapping_key = *keyp; | 285 | tapping_key = *keyp; |
273 | return true; | 286 | return true; |
274 | } else if (is_tap_key(event.key)) { | 287 | } else if (is_tap_record(keyp)) { |
275 | // Sequential tap can be interfered with other tap key. | 288 | // Sequential tap can be interfered with other tap key. |
276 | debug("Tapping: Start with interfering other tap.\n"); | 289 | debug("Tapping: Start with interfering other tap.\n"); |
277 | tapping_key = *keyp; | 290 | tapping_key = *keyp; |
@@ -303,7 +316,7 @@ bool process_tapping(keyrecord_t *keyp) { | |||
303 | } | 316 | } |
304 | // not tapping state | 317 | // not tapping state |
305 | else { | 318 | else { |
306 | if (event.pressed && is_tap_key(event.key)) { | 319 | if (event.pressed && is_tap_record(keyp)) { |
307 | debug("Tapping: Start(Press tap key).\n"); | 320 | debug("Tapping: Start(Press tap key).\n"); |
308 | tapping_key = *keyp; | 321 | tapping_key = *keyp; |
309 | process_record_tap_hint(&tapping_key); | 322 | process_record_tap_hint(&tapping_key); |
diff --git a/tmk_core/common/action_tapping.h b/tmk_core/common/action_tapping.h index 893ccb1ce..7de8049c7 100644 --- a/tmk_core/common/action_tapping.h +++ b/tmk_core/common/action_tapping.h | |||
@@ -30,6 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
30 | #define WAITING_BUFFER_SIZE 8 | 30 | #define WAITING_BUFFER_SIZE 8 |
31 | 31 | ||
32 | #ifndef NO_ACTION_TAPPING | 32 | #ifndef NO_ACTION_TAPPING |
33 | uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache); | ||
33 | uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache); | 34 | uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache); |
34 | void action_tapping_process(keyrecord_t record); | 35 | void action_tapping_process(keyrecord_t record); |
35 | 36 | ||