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>
This commit is contained in:
parent
07553b41f0
commit
7e983796e1
11 changed files with 845 additions and 169 deletions
|
@ -55,6 +55,8 @@ __attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrec
|
|||
__attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; }
|
||||
#endif
|
||||
|
||||
__attribute__((weak)) bool pre_process_record_quantum(keyrecord_t *record) { return true; }
|
||||
|
||||
#ifndef TAP_CODE_DELAY
|
||||
# define TAP_CODE_DELAY 0
|
||||
#endif
|
||||
|
@ -106,9 +108,13 @@ void action_exec(keyevent_t event) {
|
|||
#endif
|
||||
|
||||
#ifndef NO_ACTION_TAPPING
|
||||
action_tapping_process(record);
|
||||
if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) {
|
||||
action_tapping_process(record);
|
||||
}
|
||||
#else
|
||||
process_record(&record);
|
||||
if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) {
|
||||
process_record(&record);
|
||||
}
|
||||
if (!IS_NOEVENT(record.event)) {
|
||||
dprint("processed: ");
|
||||
debug_record(record);
|
||||
|
@ -206,7 +212,16 @@ void process_record(keyrecord_t *record) {
|
|||
}
|
||||
|
||||
void process_record_handler(keyrecord_t *record) {
|
||||
#ifdef COMBO_ENABLE
|
||||
action_t action;
|
||||
if (record->keycode) {
|
||||
action = action_for_keycode(record->keycode);
|
||||
} else {
|
||||
action = store_or_get_action(record->event.pressed, record->event.key);
|
||||
}
|
||||
#else
|
||||
action_t action = store_or_get_action(record->event.pressed, record->event.key);
|
||||
#endif
|
||||
dprint("ACTION: ");
|
||||
debug_action(action);
|
||||
#ifndef NO_ACTION_LAYER
|
||||
|
@ -990,6 +1005,24 @@ bool is_tap_key(keypos_t key) {
|
|||
return is_tap_action(action);
|
||||
}
|
||||
|
||||
/** \brief Utilities for actions. (FIXME: Needs better description)
|
||||
*
|
||||
* FIXME: Needs documentation.
|
||||
*/
|
||||
bool is_tap_record(keyrecord_t *record) {
|
||||
#ifdef COMBO_ENABLE
|
||||
action_t action;
|
||||
if (record->keycode) {
|
||||
action = action_for_keycode(record->keycode);
|
||||
} else {
|
||||
action = layer_switch_get_action(record->event.key);
|
||||
}
|
||||
#else
|
||||
action_t action = layer_switch_get_action(record->event.key);
|
||||
#endif
|
||||
return is_tap_action(action);
|
||||
}
|
||||
|
||||
/** \brief Utilities for actions. (FIXME: Needs better description)
|
||||
*
|
||||
* FIXME: Needs documentation.
|
||||
|
|
|
@ -53,6 +53,9 @@ typedef struct {
|
|||
#ifndef NO_ACTION_TAPPING
|
||||
tap_t tap;
|
||||
#endif
|
||||
#ifdef COMBO_ENABLE
|
||||
uint16_t keycode;
|
||||
#endif
|
||||
} keyrecord_t;
|
||||
|
||||
/* Execute action per keyevent */
|
||||
|
@ -60,6 +63,7 @@ void action_exec(keyevent_t event);
|
|||
|
||||
/* action for key */
|
||||
action_t action_for_key(uint8_t layer, keypos_t key);
|
||||
action_t action_for_keycode(uint16_t keycode);
|
||||
|
||||
/* macro */
|
||||
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);
|
|||
void clear_keyboard_but_mods_and_keys(void);
|
||||
void layer_switch(uint8_t new_layer);
|
||||
bool is_tap_key(keypos_t key);
|
||||
bool is_tap_record(keyrecord_t *record);
|
||||
bool is_tap_action(action_t action);
|
||||
|
||||
#ifndef NO_ACTION_TAPPING
|
||||
|
|
|
@ -18,11 +18,16 @@
|
|||
# define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
|
||||
# define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
|
||||
# define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))
|
||||
#ifndef COMBO_ENABLE
|
||||
# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)))
|
||||
#else
|
||||
# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)
|
||||
#endif
|
||||
|
||||
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; }
|
||||
|
||||
# ifdef TAPPING_TERM_PER_KEY
|
||||
# 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))
|
||||
# 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))
|
||||
# else
|
||||
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
|
||||
# endif
|
||||
|
@ -103,7 +108,7 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||
if (IS_TAPPING_PRESSED()) {
|
||||
if (WITHIN_TAPPING_TERM(event)) {
|
||||
if (tapping_key.tap.count == 0) {
|
||||
if (IS_TAPPING_KEY(event.key) && !event.pressed) {
|
||||
if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
|
||||
// first tap!
|
||||
debug("Tapping: First tap(0->1).\n");
|
||||
tapping_key.tap.count = 1;
|
||||
|
@ -122,14 +127,14 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||
# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY)
|
||||
else if (((
|
||||
# ifdef TAPPING_TERM_PER_KEY
|
||||
get_tapping_term(get_event_keycode(tapping_key.event, false), keyp)
|
||||
get_tapping_term(get_record_keycode(&tapping_key, false), keyp)
|
||||
# else
|
||||
TAPPING_TERM
|
||||
# endif
|
||||
>= 500)
|
||||
|
||||
# ifdef PERMISSIVE_HOLD_PER_KEY
|
||||
|| get_permissive_hold(get_event_keycode(tapping_key.event, false), keyp)
|
||||
|| get_permissive_hold(get_record_keycode(&tapping_key, false), keyp)
|
||||
# elif defined(PERMISSIVE_HOLD)
|
||||
|| true
|
||||
# endif
|
||||
|
@ -177,7 +182,7 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||
}
|
||||
// tap_count > 0
|
||||
else {
|
||||
if (IS_TAPPING_KEY(event.key) && !event.pressed) {
|
||||
if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
|
||||
debug("Tapping: Tap release(");
|
||||
debug_dec(tapping_key.tap.count);
|
||||
debug(")\n");
|
||||
|
@ -186,11 +191,15 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||
tapping_key = *keyp;
|
||||
debug_tapping_key();
|
||||
return true;
|
||||
} else if (is_tap_key(event.key) && event.pressed) {
|
||||
} else if (is_tap_record(keyp) && event.pressed) {
|
||||
if (tapping_key.tap.count > 1) {
|
||||
debug("Tapping: Start new tap with releasing last tap(>1).\n");
|
||||
// unregister key
|
||||
process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false});
|
||||
process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false,
|
||||
#ifdef COMBO_ENABLE
|
||||
.keycode = tapping_key.keycode,
|
||||
#endif
|
||||
});
|
||||
} else {
|
||||
debug("Tapping: Start while last tap(1).\n");
|
||||
}
|
||||
|
@ -218,17 +227,21 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||
debug_tapping_key();
|
||||
return false;
|
||||
} else {
|
||||
if (IS_TAPPING_KEY(event.key) && !event.pressed) {
|
||||
if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
|
||||
debug("Tapping: End. last timeout tap release(>0).");
|
||||
keyp->tap = tapping_key.tap;
|
||||
process_record(keyp);
|
||||
tapping_key = (keyrecord_t){};
|
||||
return true;
|
||||
} else if (is_tap_key(event.key) && event.pressed) {
|
||||
} else if (is_tap_record(keyp) && event.pressed) {
|
||||
if (tapping_key.tap.count > 1) {
|
||||
debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
|
||||
// unregister key
|
||||
process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false});
|
||||
process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false,
|
||||
#ifdef COMBO_ENABLE
|
||||
.keycode = tapping_key.keycode,
|
||||
#endif
|
||||
});
|
||||
} else {
|
||||
debug("Tapping: Start while last timeout tap(1).\n");
|
||||
}
|
||||
|
@ -248,12 +261,12 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||
} else if (IS_TAPPING_RELEASED()) {
|
||||
if (WITHIN_TAPPING_TERM(event)) {
|
||||
if (event.pressed) {
|
||||
if (IS_TAPPING_KEY(event.key)) {
|
||||
if (IS_TAPPING_RECORD(keyp)) {
|
||||
//# ifndef TAPPING_FORCE_HOLD
|
||||
# if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY)
|
||||
if (
|
||||
# ifdef TAPPING_FORCE_HOLD_PER_KEY
|
||||
!get_tapping_force_hold(get_event_keycode(tapping_key.event, false), keyp) &&
|
||||
!get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) &&
|
||||
# endif
|
||||
!tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
|
||||
// sequential tap.
|
||||
|
@ -271,7 +284,7 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||
// FIX: start new tap again
|
||||
tapping_key = *keyp;
|
||||
return true;
|
||||
} else if (is_tap_key(event.key)) {
|
||||
} else if (is_tap_record(keyp)) {
|
||||
// Sequential tap can be interfered with other tap key.
|
||||
debug("Tapping: Start with interfering other tap.\n");
|
||||
tapping_key = *keyp;
|
||||
|
@ -303,7 +316,7 @@ bool process_tapping(keyrecord_t *keyp) {
|
|||
}
|
||||
// not tapping state
|
||||
else {
|
||||
if (event.pressed && is_tap_key(event.key)) {
|
||||
if (event.pressed && is_tap_record(keyp)) {
|
||||
debug("Tapping: Start(Press tap key).\n");
|
||||
tapping_key = *keyp;
|
||||
process_record_tap_hint(&tapping_key);
|
||||
|
|
|
@ -30,6 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#define WAITING_BUFFER_SIZE 8
|
||||
|
||||
#ifndef NO_ACTION_TAPPING
|
||||
uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache);
|
||||
uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
|
||||
void action_tapping_process(keyrecord_t record);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue