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.

548 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(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(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
  151. else if (
  152. (
  153. IS_RELEASED(event) && waiting_buffer_typed(event)
  154. # ifdef PERMISSIVE_HOLD_PER_KEY
  155. && get_permissive_hold(tapping_keycode, &tapping_key)
  156. # elif defined(PERMISSIVE_HOLD)
  157. && true
  158. # endif
  159. )
  160. // Causes nested taps to not wait past TAPPING_TERM/RETRO_SHIFT
  161. // unnecessarily and fixes them for Layer Taps.
  162. # if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
  163. || (
  164. # ifdef RETRO_TAPPING_PER_KEY
  165. get_retro_tapping(tapping_keycode, &tapping_key) &&
  166. # endif
  167. (
  168. // Rolled over the two keys.
  169. (
  170. (
  171. false
  172. # if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
  173. || (
  174. IS_LT(tapping_keycode)
  175. # ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
  176. && get_hold_on_other_key_press(tapping_keycode, &tapping_key)
  177. # endif
  178. )
  179. # endif
  180. # if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
  181. || (
  182. IS_MT(tapping_keycode)
  183. # ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
  184. && !get_ignore_mod_tap_interrupt(tapping_keycode, &tapping_key)
  185. # endif
  186. )
  187. # endif
  188. ) && tapping_key.tap.interrupted == true
  189. )
  190. // Makes Retro Shift ignore [IGNORE_MOD_TAP_INTERRUPT's
  191. // effects on nested taps for MTs and the default
  192. // behavior of LTs] below TAPPING_TERM or RETRO_SHIFT.
  193. || (
  194. IS_RETRO(tapping_keycode)
  195. && (event.key.col != tapping_key.event.key.col || event.key.row != tapping_key.event.key.row)
  196. && IS_RELEASED(event) && waiting_buffer_typed(event)
  197. )
  198. )
  199. )
  200. # endif
  201. ) {
  202. // clang-format on
  203. debug("Tapping: End. No tap. Interfered by typing key\n");
  204. process_record(&tapping_key);
  205. tapping_key = (keyrecord_t){};
  206. debug_tapping_key();
  207. // enqueue
  208. return false;
  209. }
  210. # endif
  211. /* Process release event of a key pressed before tapping starts
  212. * Without this unexpected repeating will occur with having fast repeating setting
  213. * https://github.com/tmk/tmk_keyboard/issues/60
  214. */
  215. else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) {
  216. // Modifier should be retained till end of this tapping.
  217. action_t action = layer_switch_get_action(event.key);
  218. switch (action.kind.id) {
  219. case ACT_LMODS:
  220. case ACT_RMODS:
  221. if (action.key.mods && !action.key.code) return false;
  222. if (IS_MOD(action.key.code)) return false;
  223. break;
  224. case ACT_LMODS_TAP:
  225. case ACT_RMODS_TAP:
  226. if (action.key.mods && keyp->tap.count == 0) return false;
  227. if (IS_MOD(action.key.code)) return false;
  228. break;
  229. }
  230. // Release of key should be process immediately.
  231. debug("Tapping: release event of a key pressed before tapping\n");
  232. process_record(keyp);
  233. return true;
  234. } else {
  235. // set interrupted flag when other key preesed during tapping
  236. if (event.pressed) {
  237. tapping_key.tap.interrupted = true;
  238. # if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
  239. # if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
  240. if (get_hold_on_other_key_press(tapping_keycode, &tapping_key))
  241. # endif
  242. {
  243. debug("Tapping: End. No tap. Interfered by pressed key\n");
  244. process_record(&tapping_key);
  245. tapping_key = (keyrecord_t){};
  246. debug_tapping_key();
  247. // enqueue
  248. return false;
  249. }
  250. # endif
  251. }
  252. // enqueue
  253. return false;
  254. }
  255. }
  256. // tap_count > 0
  257. else {
  258. if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
  259. debug("Tapping: Tap release(");
  260. debug_dec(tapping_key.tap.count);
  261. debug(")\n");
  262. keyp->tap = tapping_key.tap;
  263. process_record(keyp);
  264. tapping_key = *keyp;
  265. debug_tapping_key();
  266. return true;
  267. } else if (is_tap_record(keyp) && event.pressed) {
  268. if (tapping_key.tap.count > 1) {
  269. debug("Tapping: Start new tap with releasing last tap(>1).\n");
  270. // unregister key
  271. process_record(&(keyrecord_t){
  272. .tap = tapping_key.tap,
  273. .event.key = tapping_key.event.key,
  274. .event.time = event.time,
  275. .event.pressed = false,
  276. # ifdef COMBO_ENABLE
  277. .keycode = tapping_key.keycode,
  278. # endif
  279. });
  280. } else {
  281. debug("Tapping: Start while last tap(1).\n");
  282. }
  283. tapping_key = *keyp;
  284. waiting_buffer_scan_tap();
  285. debug_tapping_key();
  286. return true;
  287. } else {
  288. if (!IS_NOEVENT(event)) {
  289. debug("Tapping: key event while last tap(>0).\n");
  290. }
  291. process_record(keyp);
  292. return true;
  293. }
  294. }
  295. }
  296. // after TAPPING_TERM
  297. else {
  298. if (tapping_key.tap.count == 0) {
  299. debug("Tapping: End. Timeout. Not tap(0): ");
  300. debug_event(event);
  301. debug("\n");
  302. process_record(&tapping_key);
  303. tapping_key = (keyrecord_t){};
  304. debug_tapping_key();
  305. return false;
  306. } else {
  307. if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
  308. debug("Tapping: End. last timeout tap release(>0).");
  309. keyp->tap = tapping_key.tap;
  310. process_record(keyp);
  311. tapping_key = (keyrecord_t){};
  312. return true;
  313. } else if (is_tap_record(keyp) && event.pressed) {
  314. if (tapping_key.tap.count > 1) {
  315. debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
  316. // unregister key
  317. process_record(&(keyrecord_t){
  318. .tap = tapping_key.tap,
  319. .event.key = tapping_key.event.key,
  320. .event.time = event.time,
  321. .event.pressed = false,
  322. # ifdef COMBO_ENABLE
  323. .keycode = tapping_key.keycode,
  324. # endif
  325. });
  326. } else {
  327. debug("Tapping: Start while last timeout tap(1).\n");
  328. }
  329. tapping_key = *keyp;
  330. waiting_buffer_scan_tap();
  331. debug_tapping_key();
  332. return true;
  333. } else {
  334. if (!IS_NOEVENT(event)) {
  335. debug("Tapping: key event while last timeout tap(>0).\n");
  336. }
  337. process_record(keyp);
  338. return true;
  339. }
  340. }
  341. }
  342. } else if (IS_TAPPING_RELEASED()) {
  343. // clang-format off
  344. if (WITHIN_TAPPING_TERM(event)
  345. # if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
  346. || (
  347. # ifdef RETRO_TAPPING_PER_KEY
  348. get_retro_tapping(tapping_keycode, &tapping_key) &&
  349. # endif
  350. (RETRO_SHIFT + 0) != 0 && TIMER_DIFF_16(event.time, tapping_key.event.time) < (RETRO_SHIFT + 0)
  351. )
  352. # endif
  353. ) {
  354. // clang-format on
  355. if (event.pressed) {
  356. if (IS_TAPPING_RECORD(keyp)) {
  357. //# ifndef TAPPING_FORCE_HOLD
  358. # if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY)
  359. if (
  360. # ifdef TAPPING_FORCE_HOLD_PER_KEY
  361. !get_tapping_force_hold(tapping_keycode, &tapping_key) &&
  362. # endif
  363. !tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
  364. // sequential tap.
  365. keyp->tap = tapping_key.tap;
  366. if (keyp->tap.count < 15) keyp->tap.count += 1;
  367. debug("Tapping: Tap press(");
  368. debug_dec(keyp->tap.count);
  369. debug(")\n");
  370. process_record(keyp);
  371. tapping_key = *keyp;
  372. debug_tapping_key();
  373. return true;
  374. }
  375. # endif
  376. // FIX: start new tap again
  377. tapping_key = *keyp;
  378. return true;
  379. } else if (is_tap_record(keyp)) {
  380. // Sequential tap can be interfered with other tap key.
  381. debug("Tapping: Start with interfering other tap.\n");
  382. tapping_key = *keyp;
  383. waiting_buffer_scan_tap();
  384. debug_tapping_key();
  385. return true;
  386. } else {
  387. // should none in buffer
  388. // FIX: interrupted when other key is pressed
  389. tapping_key.tap.interrupted = true;
  390. process_record(keyp);
  391. return true;
  392. }
  393. } else {
  394. if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n");
  395. process_record(keyp);
  396. return true;
  397. }
  398. } else {
  399. // FIX: process_action here?
  400. // timeout. no sequential tap.
  401. debug("Tapping: End(Timeout after releasing last tap): ");
  402. debug_event(event);
  403. debug("\n");
  404. tapping_key = (keyrecord_t){};
  405. debug_tapping_key();
  406. return false;
  407. }
  408. }
  409. // not tapping state
  410. else {
  411. if (event.pressed && is_tap_record(keyp)) {
  412. debug("Tapping: Start(Press tap key).\n");
  413. tapping_key = *keyp;
  414. process_record_tap_hint(&tapping_key);
  415. waiting_buffer_scan_tap();
  416. debug_tapping_key();
  417. return true;
  418. } else {
  419. process_record(keyp);
  420. return true;
  421. }
  422. }
  423. }
  424. /** \brief Waiting buffer enq
  425. *
  426. * FIXME: Needs docs
  427. */
  428. bool waiting_buffer_enq(keyrecord_t record) {
  429. if (IS_NOEVENT(record.event)) {
  430. return true;
  431. }
  432. if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) {
  433. debug("waiting_buffer_enq: Over flow.\n");
  434. return false;
  435. }
  436. waiting_buffer[waiting_buffer_head] = record;
  437. waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE;
  438. debug("waiting_buffer_enq: ");
  439. debug_waiting_buffer();
  440. return true;
  441. }
  442. /** \brief Waiting buffer clear
  443. *
  444. * FIXME: Needs docs
  445. */
  446. void waiting_buffer_clear(void) {
  447. waiting_buffer_head = 0;
  448. waiting_buffer_tail = 0;
  449. }
  450. /** \brief Waiting buffer typed
  451. *
  452. * FIXME: Needs docs
  453. */
  454. bool waiting_buffer_typed(keyevent_t event) {
  455. for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
  456. if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) {
  457. return true;
  458. }
  459. }
  460. return false;
  461. }
  462. /** \brief Waiting buffer has anykey pressed
  463. *
  464. * FIXME: Needs docs
  465. */
  466. __attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) {
  467. for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
  468. if (waiting_buffer[i].event.pressed) return true;
  469. }
  470. return false;
  471. }
  472. /** \brief Scan buffer for tapping
  473. *
  474. * FIXME: Needs docs
  475. */
  476. void waiting_buffer_scan_tap(void) {
  477. // tapping already is settled
  478. if (tapping_key.tap.count > 0) return;
  479. // invalid state: tapping_key released && tap.count == 0
  480. if (!tapping_key.event.pressed) return;
  481. for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
  482. if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && !waiting_buffer[i].event.pressed && WITHIN_TAPPING_TERM(waiting_buffer[i].event)) {
  483. tapping_key.tap.count = 1;
  484. waiting_buffer[i].tap.count = 1;
  485. process_record(&tapping_key);
  486. debug("waiting_buffer_scan_tap: found at [");
  487. debug_dec(i);
  488. debug("]\n");
  489. debug_waiting_buffer();
  490. return;
  491. }
  492. }
  493. }
  494. /** \brief Tapping key debug print
  495. *
  496. * FIXME: Needs docs
  497. */
  498. static void debug_tapping_key(void) {
  499. debug("TAPPING_KEY=");
  500. debug_record(tapping_key);
  501. debug("\n");
  502. }
  503. /** \brief Waiting buffer debug print
  504. *
  505. * FIXME: Needs docs
  506. */
  507. static void debug_waiting_buffer(void) {
  508. debug("{ ");
  509. for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {
  510. debug("[");
  511. debug_dec(i);
  512. debug("]=");
  513. debug_record(waiting_buffer[i]);
  514. debug(" ");
  515. }
  516. debug("}\n");
  517. }
  518. #endif