You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

550 lines
20 KiB

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>
2 years ago
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>
2 years ago
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>
2 years ago
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>
2 years ago
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>
2 years ago
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>
2 years ago
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>
2 years ago
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>
2 years ago
  1. #include <stdint.h>
  2. #include <stdbool.h>
  3. #ifdef DEBUG_ACTION
  4. # include "debug.h"
  5. #else
  6. # include "nodebug.h"
  7. #endif
  8. #include "action.h"
  9. #include "action_layer.h"
  10. #include "action_tapping.h"
  11. #include "keycode.h"
  12. #include "timer.h"
  13. #ifndef NO_ACTION_TAPPING
  14. # define IS_TAPPING() !IS_NOEVENT(tapping_key.event)
  15. # define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
  16. # define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
  17. # define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))
  18. # ifndef COMBO_ENABLE
  19. # define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)))
  20. # else
  21. # define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)
  22. # endif
  23. # 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))
  24. # ifdef DYNAMIC_TAPPING_TERM_ENABLE
  25. uint16_t g_tapping_term = TAPPING_TERM;
  26. # endif
  27. # ifdef TAPPING_TERM_PER_KEY
  28. __attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {
  29. # ifdef DYNAMIC_TAPPING_TERM_ENABLE
  30. return g_tapping_term;
  31. # else
  32. return TAPPING_TERM;
  33. # endif
  34. }
  35. # endif
  36. # ifdef TAPPING_FORCE_HOLD_PER_KEY
  37. __attribute__((weak)) bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record) {
  38. return false;
  39. }
  40. # endif
  41. # ifdef PERMISSIVE_HOLD_PER_KEY
  42. __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) {
  43. return false;
  44. }
  45. # endif
  46. # ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
  47. __attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) {
  48. return false;
  49. }
  50. # endif
  51. # if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
  52. # include "process_auto_shift.h"
  53. # endif
  54. static keyrecord_t tapping_key = {};
  55. static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
  56. static uint8_t waiting_buffer_head = 0;
  57. static uint8_t waiting_buffer_tail = 0;
  58. static bool process_tapping(keyrecord_t *record);
  59. static bool waiting_buffer_enq(keyrecord_t record);
  60. static void waiting_buffer_clear(void);
  61. static bool waiting_buffer_typed(keyevent_t event);
  62. static bool waiting_buffer_has_anykey_pressed(void);
  63. static void waiting_buffer_scan_tap(void);
  64. static void debug_tapping_key(void);
  65. static void debug_waiting_buffer(void);
  66. /** \brief Action Tapping Process
  67. *
  68. * FIXME: Needs doc
  69. */
  70. void action_tapping_process(keyrecord_t record) {
  71. if (process_tapping(&record)) {
  72. if (!IS_NOEVENT(record.event)) {
  73. debug("processed: ");
  74. debug_record(record);
  75. debug("\n");
  76. }
  77. } else {
  78. if (!waiting_buffer_enq(record)) {
  79. // clear all in case of overflow.
  80. debug("OVERFLOW: CLEAR ALL STATES\n");
  81. clear_keyboard();
  82. waiting_buffer_clear();
  83. tapping_key = (keyrecord_t){};
  84. }
  85. }
  86. // process waiting_buffer
  87. if (!IS_NOEVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {
  88. debug("---- action_exec: process waiting_buffer -----\n");
  89. }
  90. for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
  91. if (process_tapping(&waiting_buffer[waiting_buffer_tail])) {
  92. debug("processed: waiting_buffer[");
  93. debug_dec(waiting_buffer_tail);
  94. debug("] = ");
  95. debug_record(waiting_buffer[waiting_buffer_tail]);
  96. debug("\n\n");
  97. } else {
  98. break;
  99. }
  100. }
  101. if (!IS_NOEVENT(record.event)) {
  102. debug("\n");
  103. }
  104. }
  105. /** \brief Tapping
  106. *
  107. * Rule: Tap key is typed(pressed and released) within TAPPING_TERM.
  108. * (without interfering by typing other key)
  109. */
  110. /* return true when key event is processed or consumed. */
  111. bool process_tapping(keyrecord_t *keyp) {
  112. keyevent_t event = keyp->event;
  113. # if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(TAPPING_TERM_PER_KEY) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(TAPPING_FORCE_HOLD_PER_KEY) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
  114. uint16_t tapping_keycode = get_record_keycode(&tapping_key, false);
  115. # endif
  116. // if tapping
  117. if (IS_TAPPING_PRESSED()) {
  118. // clang-format off
  119. if (WITHIN_TAPPING_TERM(event)
  120. # if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
  121. || (
  122. # ifdef RETRO_TAPPING_PER_KEY
  123. get_retro_tapping(tapping_keycode, &tapping_key) &&
  124. # endif
  125. (RETRO_SHIFT + 0) != 0 && TIMER_DIFF_16(event.time, tapping_key.event.time) < (RETRO_SHIFT + 0)
  126. )
  127. # endif
  128. ) {
  129. // clang-format on
  130. if (tapping_key.tap.count == 0) {
  131. if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
  132. # if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
  133. retroshift_swap_times();
  134. # endif
  135. // first tap!
  136. debug("Tapping: First tap(0->1).\n");
  137. tapping_key.tap.count = 1;
  138. debug_tapping_key();
  139. process_record(&tapping_key);
  140. // copy tapping state
  141. keyp->tap = tapping_key.tap;
  142. // enqueue
  143. return false;
  144. }
  145. /* Process a key typed within TAPPING_TERM
  146. * This can register the key before settlement of tapping,
  147. * useful for long TAPPING_TERM but may prevent fast typing.
  148. */
  149. // clang-format off
  150. # if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
  151. else if (
  152. (
  153. (
  154. GET_TAPPING_TERM(tapping_keycode, &tapping_key) >= 500
  155. # ifdef PERMISSIVE_HOLD_PER_KEY
  156. || get_permissive_hold(tapping_keycode, &tapping_key)
  157. # elif defined(PERMISSIVE_HOLD)
  158. || true
  159. # endif
  160. ) && IS_RELEASED(event) && waiting_buffer_typed(event)
  161. )
  162. // Causes nested taps to not wait past TAPPING_TERM/RETRO_SHIFT
  163. // unnecessarily and fixes them for Layer Taps.
  164. # if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
  165. || (
  166. # ifdef RETRO_TAPPING_PER_KEY
  167. get_retro_tapping(tapping_keycode, &tapping_key) &&
  168. # endif
  169. (
  170. // Rolled over the two keys.
  171. (
  172. (
  173. false
  174. # if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
  175. || (
  176. IS_LT(tapping_keycode)
  177. # ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
  178. && get_hold_on_other_key_press(tapping_keycode, &tapping_key)
  179. # endif
  180. )
  181. # endif
  182. # if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
  183. || (
  184. IS_MT(tapping_keycode)
  185. # ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
  186. && !get_ignore_mod_tap_interrupt(tapping_keycode, &tapping_key)
  187. # endif
  188. )
  189. # endif
  190. ) && tapping_key.tap.interrupted == true
  191. )
  192. // Makes Retro Shift ignore [IGNORE_MOD_TAP_INTERRUPT's
  193. // effects on nested taps for MTs and the default
  194. // behavior of LTs] below TAPPING_TERM or RETRO_SHIFT.
  195. || (
  196. IS_RETRO(tapping_keycode)
  197. && (event.key.col != tapping_key.event.key.col || event.key.row != tapping_key.event.key.row)
  198. && IS_RELEASED(event) && waiting_buffer_typed(event)
  199. )
  200. )
  201. )
  202. # endif
  203. ) {
  204. // clang-format on
  205. debug("Tapping: End. No tap. Interfered by typing key\n");
  206. process_record(&tapping_key);
  207. tapping_key = (keyrecord_t){};
  208. debug_tapping_key();
  209. // enqueue
  210. return false;
  211. }
  212. # endif
  213. /* Process release event of a key pressed before tapping starts
  214. * Without this unexpected repeating will occur with having fast repeating setting
  215. * https://github.com/tmk/tmk_keyboard/issues/60
  216. */
  217. else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) {
  218. // Modifier should be retained till end of this tapping.
  219. action_t action = layer_switch_get_action(event.key);
  220. switch (action.kind.id) {
  221. case ACT_LMODS:
  222. case ACT_RMODS:
  223. if (action.key.mods && !action.key.code) return false;
  224. if (IS_MOD(action.key.code)) return false;
  225. break;
  226. case ACT_LMODS_TAP:
  227. case ACT_RMODS_TAP:
  228. if (action.key.mods && keyp->tap.count == 0) return false;
  229. if (IS_MOD(action.key.code)) return false;
  230. break;
  231. }
  232. // Release of key should be process immediately.
  233. debug("Tapping: release event of a key pressed before tapping\n");
  234. process_record(keyp);
  235. return true;
  236. } else {
  237. // set interrupted flag when other key preesed during tapping
  238. if (event.pressed) {
  239. tapping_key.tap.interrupted = true;
  240. # if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
  241. # if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
  242. if (get_hold_on_other_key_press(tapping_keycode, &tapping_key))
  243. # endif
  244. {
  245. debug("Tapping: End. No tap. Interfered by pressed key\n");
  246. process_record(&tapping_key);
  247. tapping_key = (keyrecord_t){};
  248. debug_tapping_key();
  249. // enqueue
  250. return false;
  251. }
  252. # endif
  253. }
  254. // enqueue
  255. return false;
  256. }
  257. }
  258. // tap_count > 0
  259. else {
  260. if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
  261. debug("Tapping: Tap release(");
  262. debug_dec(tapping_key.tap.count);
  263. debug(")\n");
  264. keyp->tap = tapping_key.tap;
  265. process_record(keyp);
  266. tapping_key = *keyp;
  267. debug_tapping_key();
  268. return true;
  269. } else if (is_tap_record(keyp) && event.pressed) {
  270. if (tapping_key.tap.count > 1) {
  271. debug("Tapping: Start new tap with releasing last tap(>1).\n");
  272. // unregister key
  273. process_record(&(keyrecord_t){
  274. .tap = tapping_key.tap,
  275. .event.key = tapping_key.event.key,
  276. .event.time = event.time,
  277. .event.pressed = false,
  278. # ifdef COMBO_ENABLE
  279. .keycode = tapping_key.keycode,
  280. # endif
  281. });
  282. } else {
  283. debug("Tapping: Start while last tap(1).\n");
  284. }
  285. tapping_key = *keyp;
  286. waiting_buffer_scan_tap();
  287. debug_tapping_key();
  288. return true;
  289. } else {
  290. if (!IS_NOEVENT(event)) {
  291. debug("Tapping: key event while last tap(>0).\n");
  292. }
  293. process_record(keyp);
  294. return true;
  295. }
  296. }
  297. }
  298. // after TAPPING_TERM
  299. else {
  300. if (tapping_key.tap.count == 0) {
  301. debug("Tapping: End. Timeout. Not tap(0): ");
  302. debug_event(event);
  303. debug("\n");
  304. process_record(&tapping_key);
  305. tapping_key = (keyrecord_t){};
  306. debug_tapping_key();
  307. return false;
  308. } else {
  309. if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
  310. debug("Tapping: End. last timeout tap release(>0).");
  311. keyp->tap = tapping_key.tap;
  312. process_record(keyp);
  313. tapping_key = (keyrecord_t){};
  314. return true;
  315. } else if (is_tap_record(keyp) && event.pressed) {
  316. if (tapping_key.tap.count > 1) {
  317. debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
  318. // unregister key
  319. process_record(&(keyrecord_t){
  320. .tap = tapping_key.tap,
  321. .event.key = tapping_key.event.key,
  322. .event.time = event.time,
  323. .event.pressed = false,
  324. # ifdef COMBO_ENABLE
  325. .keycode = tapping_key.keycode,
  326. # endif
  327. });
  328. } else {
  329. debug("Tapping: Start while last timeout tap(1).\n");
  330. }
  331. tapping_key = *keyp;
  332. waiting_buffer_scan_tap();
  333. debug_tapping_key();
  334. return true;
  335. } else {
  336. if (!IS_NOEVENT(event)) {
  337. debug("Tapping: key event while last timeout tap(>0).\n");
  338. }
  339. process_record(keyp);
  340. return true;
  341. }
  342. }
  343. }
  344. } else if (IS_TAPPING_RELEASED()) {
  345. // clang-format off
  346. if (WITHIN_TAPPING_TERM(event)
  347. # if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
  348. || (
  349. # ifdef RETRO_TAPPING_PER_KEY
  350. get_retro_tapping(tapping_keycode, &tapping_key) &&
  351. # endif
  352. (RETRO_SHIFT + 0) != 0 && TIMER_DIFF_16(event.time, tapping_key.event.time) < (RETRO_SHIFT + 0)
  353. )
  354. # endif
  355. ) {
  356. // clang-format on
  357. if (event.pressed) {
  358. if (IS_TAPPING_RECORD(keyp)) {
  359. //# ifndef TAPPING_FORCE_HOLD
  360. # if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY)
  361. if (
  362. # ifdef TAPPING_FORCE_HOLD_PER_KEY
  363. !get_tapping_force_hold(tapping_keycode, &tapping_key) &&
  364. # endif
  365. !tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
  366. // sequential tap.
  367. keyp->tap = tapping_key.tap;
  368. if (keyp->tap.count < 15) keyp->tap.count += 1;
  369. debug("Tapping: Tap press(");
  370. debug_dec(keyp->tap.count);
  371. debug(")\n");
  372. process_record(keyp);
  373. tapping_key = *keyp;
  374. debug_tapping_key();
  375. return true;
  376. }
  377. # endif
  378. // FIX: start new tap again
  379. tapping_key = *keyp;
  380. return true;
  381. } else if (is_tap_record(keyp)) {
  382. // Sequential tap can be interfered with other tap key.
  383. debug("Tapping: Start with interfering other tap.\n");
  384. tapping_key = *keyp;
  385. waiting_buffer_scan_tap();
  386. debug_tapping_key();
  387. return true;
  388. } else {
  389. // should none in buffer
  390. // FIX: interrupted when other key is pressed
  391. tapping_key.tap.interrupted = true;
  392. process_record(keyp);
  393. return true;
  394. }
  395. } else {
  396. if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n");
  397. process_record(keyp);
  398. return true;
  399. }
  400. } else {
  401. // FIX: process_action here?
  402. // timeout. no sequential tap.
  403. debug("Tapping: End(Timeout after releasing last tap): ");
  404. debug_event(event);
  405. debug("\n");
  406. tapping_key = (keyrecord_t){};
  407. debug_tapping_key();
  408. return false;
  409. }
  410. }
  411. // not tapping state
  412. else {
  413. if (event.pressed && is_tap_record(keyp)) {
  414. debug("Tapping: Start(Press tap key).\n");
  415. tapping_key = *keyp;
  416. process_record_tap_hint(&tapping_key);
  417. waiting_buffer_scan_tap();
  418. debug_tapping_key();
  419. return true;
  420. } else {
  421. process_record(keyp);
  422. return true;
  423. }
  424. }
  425. }
  426. /** \brief Waiting buffer enq
  427. *
  428. * FIXME: Needs docs
  429. */
  430. bool waiting_buffer_enq(keyrecord_t record) {
  431. if (IS_NOEVENT(record.event)) {
  432. return true;
  433. }
  434. if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) {
  435. debug("waiting_buffer_enq: Over flow.\n");
  436. return false;
  437. }
  438. waiting_buffer[waiting_buffer_head] = record;
  439. waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE;
  440. debug("waiting_buffer_enq: ");
  441. debug_waiting_buffer();
  442. return true;
  443. }
  444. /** \brief Waiting buffer clear
  445. *
  446. * FIXME: Needs docs
  447. */
  448. void waiting_buffer_clear(void) {
  449. waiting_buffer_head = 0;
  450. waiting_buffer_tail = 0;
  451. }
  452. /** \brief Waiting buffer typed
  453. *
  454. * FIXME: Needs docs
  455. */
  456. bool waiting_buffer_typed(keyevent_t event) {
  457. for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
  458. if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) {
  459. return true;
  460. }
  461. }
  462. return false;
  463. }
  464. /** \brief Waiting buffer has anykey pressed
  465. *
  466. * FIXME: Needs docs
  467. */
  468. __attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) {
  469. for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
  470. if (waiting_buffer[i].event.pressed) return true;
  471. }
  472. return false;
  473. }
  474. /** \brief Scan buffer for tapping
  475. *
  476. * FIXME: Needs docs
  477. */
  478. void waiting_buffer_scan_tap(void) {
  479. // tapping already is settled
  480. if (tapping_key.tap.count > 0) return;
  481. // invalid state: tapping_key released && tap.count == 0
  482. if (!tapping_key.event.pressed) return;
  483. for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
  484. if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && !waiting_buffer[i].event.pressed && WITHIN_TAPPING_TERM(waiting_buffer[i].event)) {
  485. tapping_key.tap.count = 1;
  486. waiting_buffer[i].tap.count = 1;
  487. process_record(&tapping_key);
  488. debug("waiting_buffer_scan_tap: found at [");
  489. debug_dec(i);
  490. debug("]\n");
  491. debug_waiting_buffer();
  492. return;
  493. }
  494. }
  495. }
  496. /** \brief Tapping key debug print
  497. *
  498. * FIXME: Needs docs
  499. */
  500. static void debug_tapping_key(void) {
  501. debug("TAPPING_KEY=");
  502. debug_record(tapping_key);
  503. debug("\n");
  504. }
  505. /** \brief Waiting buffer debug print
  506. *
  507. * FIXME: Needs docs
  508. */
  509. static void debug_waiting_buffer(void) {
  510. debug("{ ");
  511. for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
  512. debug("[");
  513. debug_dec(i);
  514. debug("]=");
  515. debug_record(waiting_buffer[i]);
  516. debug(" ");
  517. }
  518. debug("}\n");
  519. }
  520. #endif