aboutsummaryrefslogtreecommitdiff
path: root/tmk_core/common
diff options
context:
space:
mode:
authorPete Sevander <pete.sevander@gmail.com>2021-08-06 02:44:57 +0300
committerGitHub <noreply@github.com>2021-08-06 09:44:57 +1000
commit7e983796e18e7401c062c158f23966aeb7b1405b (patch)
tree88d6897d7ca4f6103f03c1941dac6736ba477361 /tmk_core/common
parent07553b41f0a03ca6549c09cecf9cd3dec7332346 (diff)
downloadqmk_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.c37
-rw-r--r--tmk_core/common/action.h5
-rw-r--r--tmk_core/common/action_tapping.c41
-rw-r--r--tmk_core/common/action_tapping.h1
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
208void process_record_handler(keyrecord_t *record) { 214void 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 */
1012bool 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 */
997bool is_tap_action(action_t action) { 1030bool 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 */
62action_t action_for_key(uint8_t layer, keypos_t key); 65action_t action_for_key(uint8_t layer, keypos_t key);
66action_t action_for_keycode(uint16_t keycode);
63 67
64/* macro */ 68/* macro */
65const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt); 69const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt);
@@ -111,6 +115,7 @@ void clear_keyboard_but_mods(void);
111void clear_keyboard_but_mods_and_keys(void); 115void clear_keyboard_but_mods_and_keys(void);
112void layer_switch(uint8_t new_layer); 116void layer_switch(uint8_t new_layer);
113bool is_tap_key(keypos_t key); 117bool is_tap_key(keypos_t key);
118bool is_tap_record(keyrecord_t *record);
114bool is_tap_action(action_t action); 119bool 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
33uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache);
33uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache); 34uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
34void action_tapping_process(keyrecord_t record); 35void action_tapping_process(keyrecord_t record);
35 36