diff options
| -rw-r--r-- | quantum/action.c | 3 | ||||
| -rw-r--r-- | quantum/action_tapping.c | 105 | ||||
| -rw-r--r-- | quantum/action_tapping.h | 3 | ||||
| -rw-r--r-- | tests/tap_hold_configurations/flow_tap/config.h | 1 | ||||
| -rw-r--r-- | tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp | 331 |
5 files changed, 410 insertions, 33 deletions
diff --git a/quantum/action.c b/quantum/action.c index eb0dbc7022..dd82c9ec99 100644 --- a/quantum/action.c +++ b/quantum/action.c | |||
| @@ -281,6 +281,9 @@ void process_record(keyrecord_t *record) { | |||
| 281 | if (IS_NOEVENT(record->event)) { | 281 | if (IS_NOEVENT(record->event)) { |
| 282 | return; | 282 | return; |
| 283 | } | 283 | } |
| 284 | #ifdef FLOW_TAP_TERM | ||
| 285 | flow_tap_update_last_event(record); | ||
| 286 | #endif // FLOW_TAP_TERM | ||
| 284 | 287 | ||
| 285 | if (!process_record_quantum(record)) { | 288 | if (!process_record_quantum(record)) { |
| 286 | #ifndef NO_ACTION_ONESHOT | 289 | #ifndef NO_ACTION_ONESHOT |
diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c index 312c639169..3e391d1526 100644 --- a/quantum/action_tapping.c +++ b/quantum/action_tapping.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "action_tapping.h" | 6 | #include "action_tapping.h" |
| 7 | #include "action_util.h" | 7 | #include "action_util.h" |
| 8 | #include "keycode.h" | 8 | #include "keycode.h" |
| 9 | #include "quantum_keycodes.h" | ||
| 9 | #include "timer.h" | 10 | #include "timer.h" |
| 10 | 11 | ||
| 11 | #ifndef NO_ACTION_TAPPING | 12 | #ifndef NO_ACTION_TAPPING |
| @@ -102,10 +103,10 @@ __attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyreco | |||
| 102 | # endif | 103 | # endif |
| 103 | 104 | ||
| 104 | # if defined(FLOW_TAP_TERM) | 105 | # if defined(FLOW_TAP_TERM) |
| 105 | static uint32_t last_input = 0; | 106 | static uint32_t flow_tap_prev_time = 0; |
| 106 | static uint16_t prev_keycode = KC_NO; | 107 | static uint16_t flow_tap_prev_keycode = KC_NO; |
| 107 | 108 | ||
| 108 | uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t *record, uint16_t prev_keycode); | 109 | static bool flow_tap_key_if_within_term(keyrecord_t *record); |
| 109 | # endif // defined(FLOW_TAP_TERM) | 110 | # endif // defined(FLOW_TAP_TERM) |
| 110 | 111 | ||
| 111 | static keyrecord_t tapping_key = {}; | 112 | static keyrecord_t tapping_key = {}; |
| @@ -157,19 +158,6 @@ void action_tapping_process(keyrecord_t record) { | |||
| 157 | } | 158 | } |
| 158 | } | 159 | } |
| 159 | if (IS_EVENT(record.event)) { | 160 | if (IS_EVENT(record.event)) { |
| 160 | # if defined(FLOW_TAP_TERM) | ||
| 161 | const uint16_t keycode = get_record_keycode(&record, false); | ||
| 162 | // Track the previous key press. | ||
| 163 | if (record.event.pressed) { | ||
| 164 | prev_keycode = keycode; | ||
| 165 | } | ||
| 166 | // If there is no unsettled tap-hold key, update last input time. Ignore | ||
| 167 | // mod keys in this update to allow for chording multiple mods for | ||
| 168 | // hotkeys like "Ctrl+Shift+arrow". | ||
| 169 | if (IS_NOEVENT(tapping_key.event) && !IS_MODIFIER_KEYCODE(keycode)) { | ||
| 170 | last_input = timer_read32(); | ||
| 171 | } | ||
| 172 | # endif // defined(FLOW_TAP_TERM) | ||
| 173 | ac_dprintf("\n"); | 161 | ac_dprintf("\n"); |
| 174 | } | 162 | } |
| 175 | } | 163 | } |
| @@ -252,22 +240,8 @@ bool process_tapping(keyrecord_t *keyp) { | |||
| 252 | // into the "pressed" tapping key state | 240 | // into the "pressed" tapping key state |
| 253 | 241 | ||
| 254 | # if defined(FLOW_TAP_TERM) | 242 | # if defined(FLOW_TAP_TERM) |
| 255 | const uint16_t keycode = get_record_keycode(keyp, false); | 243 | if (flow_tap_key_if_within_term(keyp)) { |
| 256 | if (is_mt_or_lt(keycode)) { | 244 | return true; |
| 257 | const uint32_t idle_time = timer_elapsed32(last_input); | ||
| 258 | uint16_t term = get_flow_tap_term(keycode, keyp, prev_keycode); | ||
| 259 | if (term > 500) { | ||
| 260 | term = 500; | ||
| 261 | } | ||
| 262 | if (idle_time < 500 && idle_time < term) { | ||
| 263 | debug_event(keyp->event); | ||
| 264 | ac_dprintf(" within flow tap term (%u < %u) considered a tap\n", (int16_t)idle_time, term); | ||
| 265 | keyp->tap.count = 1; | ||
| 266 | registered_taps_add(keyp->event.key); | ||
| 267 | debug_registered_taps(); | ||
| 268 | process_record(keyp); | ||
| 269 | return true; | ||
| 270 | } | ||
| 271 | } | 245 | } |
| 272 | # endif // defined(FLOW_TAP_TERM) | 246 | # endif // defined(FLOW_TAP_TERM) |
| 273 | 247 | ||
| @@ -582,6 +556,13 @@ bool process_tapping(keyrecord_t *keyp) { | |||
| 582 | return true; | 556 | return true; |
| 583 | } else if (is_tap_record(keyp)) { | 557 | } else if (is_tap_record(keyp)) { |
| 584 | // Sequential tap can be interfered with other tap key. | 558 | // Sequential tap can be interfered with other tap key. |
| 559 | # if defined(FLOW_TAP_TERM) | ||
| 560 | if (flow_tap_key_if_within_term(keyp)) { | ||
| 561 | tapping_key = (keyrecord_t){0}; | ||
| 562 | debug_tapping_key(); | ||
| 563 | return true; | ||
| 564 | } | ||
| 565 | # endif // defined(FLOW_TAP_TERM) | ||
| 585 | ac_dprintf("Tapping: Start with interfering other tap.\n"); | 566 | ac_dprintf("Tapping: Start with interfering other tap.\n"); |
| 586 | tapping_key = *keyp; | 567 | tapping_key = *keyp; |
| 587 | waiting_buffer_scan_tap(); | 568 | waiting_buffer_scan_tap(); |
| @@ -809,6 +790,66 @@ static void waiting_buffer_process_regular(void) { | |||
| 809 | # endif // CHORDAL_HOLD | 790 | # endif // CHORDAL_HOLD |
| 810 | 791 | ||
| 811 | # ifdef FLOW_TAP_TERM | 792 | # ifdef FLOW_TAP_TERM |
| 793 | void flow_tap_update_last_event(keyrecord_t *record) { | ||
| 794 | // Don't update while a tap-hold key is unsettled. | ||
| 795 | if (waiting_buffer_tail != waiting_buffer_head || (tapping_key.event.pressed && tapping_key.tap.count == 0)) { | ||
| 796 | return; | ||
| 797 | } | ||
| 798 | const uint16_t keycode = get_record_keycode(record, false); | ||
| 799 | // Ignore releases of modifiers and held layer switches. | ||
| 800 | if (!record->event.pressed) { | ||
| 801 | switch (keycode) { | ||
| 802 | case MODIFIER_KEYCODE_RANGE: | ||
| 803 | case QK_MOMENTARY ... QK_MOMENTARY_MAX: | ||
| 804 | case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: | ||
| 805 | # ifndef NO_ACTION_ONESHOT // Ignore one-shot keys. | ||
| 806 | case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: | ||
| 807 | case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: | ||
| 808 | # endif // NO_ACTION_ONESHOT | ||
| 809 | # ifdef TRI_LAYER_ENABLE // Ignore Tri Layer keys. | ||
| 810 | case QK_TRI_LAYER_LOWER: | ||
| 811 | case QK_TRI_LAYER_UPPER: | ||
| 812 | # endif // TRI_LAYER_ENABLE | ||
| 813 | return; | ||
| 814 | case QK_MODS ... QK_MODS_MAX: | ||
| 815 | if (QK_MODS_GET_BASIC_KEYCODE(keycode) == KC_NO) { | ||
| 816 | return; | ||
| 817 | } | ||
| 818 | break; | ||
| 819 | case QK_MOD_TAP ... QK_MOD_TAP_MAX: | ||
| 820 | case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: | ||
| 821 | if (record->tap.count == 0) { | ||
| 822 | return; | ||
| 823 | } | ||
| 824 | break; | ||
| 825 | } | ||
| 826 | } | ||
| 827 | |||
| 828 | flow_tap_prev_keycode = keycode; | ||
| 829 | flow_tap_prev_time = timer_read32(); | ||
| 830 | } | ||
| 831 | |||
| 832 | static bool flow_tap_key_if_within_term(keyrecord_t *record) { | ||
| 833 | const uint16_t keycode = get_record_keycode(record, false); | ||
| 834 | if (is_mt_or_lt(keycode)) { | ||
| 835 | const uint32_t idle_time = timer_elapsed32(flow_tap_prev_time); | ||
| 836 | uint16_t term = get_flow_tap_term(keycode, record, flow_tap_prev_keycode); | ||
| 837 | if (term > 500) { | ||
| 838 | term = 500; | ||
| 839 | } | ||
| 840 | if (idle_time < 500 && idle_time < term) { | ||
| 841 | debug_event(record->event); | ||
| 842 | ac_dprintf(" within flow tap term (%u < %u) considered a tap\n", (int16_t)idle_time, term); | ||
| 843 | record->tap.count = 1; | ||
| 844 | registered_taps_add(record->event.key); | ||
| 845 | debug_registered_taps(); | ||
| 846 | process_record(record); | ||
| 847 | return true; | ||
| 848 | } | ||
| 849 | } | ||
| 850 | return false; | ||
| 851 | } | ||
| 852 | |||
| 812 | // By default, enable Flow Tap for the keys in the main alphas area and Space. | 853 | // By default, enable Flow Tap for the keys in the main alphas area and Space. |
| 813 | // This should work reasonably even if the layout is remapped on the host to an | 854 | // This should work reasonably even if the layout is remapped on the host to an |
| 814 | // alt layout or international layout (e.g. Dvorak or AZERTY), where these same | 855 | // alt layout or international layout (e.g. Dvorak or AZERTY), where these same |
diff --git a/quantum/action_tapping.h b/quantum/action_tapping.h index 2af000ad73..0cf4aa1200 100644 --- a/quantum/action_tapping.h +++ b/quantum/action_tapping.h | |||
| @@ -166,6 +166,9 @@ bool is_flow_tap_key(uint16_t keycode); | |||
| 166 | * @return Time in milliseconds. | 166 | * @return Time in milliseconds. |
| 167 | */ | 167 | */ |
| 168 | uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t *record, uint16_t prev_keycode); | 168 | uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t *record, uint16_t prev_keycode); |
| 169 | |||
| 170 | /** Updates the Flow Tap last key and timer. */ | ||
| 171 | void flow_tap_update_last_event(keyrecord_t *record); | ||
| 169 | #endif // FLOW_TAP_TERM | 172 | #endif // FLOW_TAP_TERM |
| 170 | 173 | ||
| 171 | #ifdef DYNAMIC_TAPPING_TERM_ENABLE | 174 | #ifdef DYNAMIC_TAPPING_TERM_ENABLE |
diff --git a/tests/tap_hold_configurations/flow_tap/config.h b/tests/tap_hold_configurations/flow_tap/config.h index a17d488214..d6f385d8d4 100644 --- a/tests/tap_hold_configurations/flow_tap/config.h +++ b/tests/tap_hold_configurations/flow_tap/config.h | |||
| @@ -20,3 +20,4 @@ | |||
| 20 | #include "test_common.h" | 20 | #include "test_common.h" |
| 21 | 21 | ||
| 22 | #define FLOW_TAP_TERM 150 | 22 | #define FLOW_TAP_TERM 150 |
| 23 | #define PERMISSIVE_HOLD | ||
diff --git a/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp b/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp index 7816fcb6da..a4233f0d57 100644 --- a/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp +++ b/tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp | |||
| @@ -25,7 +25,169 @@ using testing::InSequence; | |||
| 25 | 25 | ||
| 26 | class FlowTapTest : public TestFixture {}; | 26 | class FlowTapTest : public TestFixture {}; |
| 27 | 27 | ||
| 28 | TEST_F(FlowTapTest, short_flow_tap_settled_as_tapped) { | 28 | // Test an input of quick distinct taps. All should be settled as tapped. |
| 29 | TEST_F(FlowTapTest, distinct_taps) { | ||
| 30 | TestDriver driver; | ||
| 31 | InSequence s; | ||
| 32 | auto regular_key = KeymapKey(0, 0, 0, KC_A); | ||
| 33 | auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B)); | ||
| 34 | auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C)); | ||
| 35 | auto mod_tap_key3 = KeymapKey(0, 3, 0, ALT_T(KC_D)); | ||
| 36 | |||
| 37 | set_keymap({regular_key, mod_tap_key1, mod_tap_key2, mod_tap_key3}); | ||
| 38 | |||
| 39 | // Tap regular key. | ||
| 40 | EXPECT_REPORT(driver, (KC_A)); | ||
| 41 | EXPECT_EMPTY_REPORT(driver); | ||
| 42 | tap_key(regular_key, FLOW_TAP_TERM + 1); | ||
| 43 | VERIFY_AND_CLEAR(driver); | ||
| 44 | |||
| 45 | // Tap mod-tap 1. | ||
| 46 | EXPECT_REPORT(driver, (KC_B)); | ||
| 47 | mod_tap_key1.press(); | ||
| 48 | run_one_scan_loop(); | ||
| 49 | VERIFY_AND_CLEAR(driver); | ||
| 50 | |||
| 51 | EXPECT_EMPTY_REPORT(driver); | ||
| 52 | idle_for(FLOW_TAP_TERM + 1); | ||
| 53 | mod_tap_key1.release(); | ||
| 54 | run_one_scan_loop(); | ||
| 55 | VERIFY_AND_CLEAR(driver); | ||
| 56 | |||
| 57 | // Tap mod-tap 2. | ||
| 58 | EXPECT_REPORT(driver, (KC_C)); | ||
| 59 | mod_tap_key2.press(); | ||
| 60 | run_one_scan_loop(); | ||
| 61 | VERIFY_AND_CLEAR(driver); | ||
| 62 | |||
| 63 | EXPECT_EMPTY_REPORT(driver); | ||
| 64 | idle_for(FLOW_TAP_TERM + 1); | ||
| 65 | mod_tap_key2.release(); | ||
| 66 | run_one_scan_loop(); | ||
| 67 | VERIFY_AND_CLEAR(driver); | ||
| 68 | |||
| 69 | // Tap mod-tap 3. | ||
| 70 | EXPECT_REPORT(driver, (KC_D)); | ||
| 71 | mod_tap_key3.press(); | ||
| 72 | run_one_scan_loop(); | ||
| 73 | VERIFY_AND_CLEAR(driver); | ||
| 74 | |||
| 75 | EXPECT_EMPTY_REPORT(driver); | ||
| 76 | idle_for(FLOW_TAP_TERM + 1); | ||
| 77 | mod_tap_key3.release(); | ||
| 78 | idle_for(FLOW_TAP_TERM + 1); // Pause between taps. | ||
| 79 | VERIFY_AND_CLEAR(driver); | ||
| 80 | |||
| 81 | // Tap mod-tap 1. | ||
| 82 | EXPECT_NO_REPORT(driver); | ||
| 83 | mod_tap_key1.press(); | ||
| 84 | run_one_scan_loop(); | ||
| 85 | VERIFY_AND_CLEAR(driver); | ||
| 86 | |||
| 87 | EXPECT_REPORT(driver, (KC_B)); | ||
| 88 | EXPECT_EMPTY_REPORT(driver); | ||
| 89 | idle_for(FLOW_TAP_TERM + 1); | ||
| 90 | mod_tap_key1.release(); | ||
| 91 | run_one_scan_loop(); | ||
| 92 | VERIFY_AND_CLEAR(driver); | ||
| 93 | |||
| 94 | // Tap mod-tap 2. | ||
| 95 | EXPECT_REPORT(driver, (KC_C)); | ||
| 96 | mod_tap_key2.press(); | ||
| 97 | run_one_scan_loop(); | ||
| 98 | VERIFY_AND_CLEAR(driver); | ||
| 99 | |||
| 100 | EXPECT_EMPTY_REPORT(driver); | ||
| 101 | idle_for(FLOW_TAP_TERM + 1); | ||
| 102 | mod_tap_key2.release(); | ||
| 103 | idle_for(FLOW_TAP_TERM + 1); | ||
| 104 | VERIFY_AND_CLEAR(driver); | ||
| 105 | } | ||
| 106 | |||
| 107 | // By default, Flow Tap is disabled when mods other than Shift and AltGr are on. | ||
| 108 | TEST_F(FlowTapTest, hotkey_taps) { | ||
| 109 | TestDriver driver; | ||
| 110 | InSequence s; | ||
| 111 | auto ctrl_key = KeymapKey(0, 0, 0, KC_LCTL); | ||
| 112 | auto shft_key = KeymapKey(0, 1, 0, KC_LSFT); | ||
| 113 | auto alt_key = KeymapKey(0, 2, 0, KC_LALT); | ||
| 114 | auto gui_key = KeymapKey(0, 3, 0, KC_LGUI); | ||
| 115 | auto regular_key = KeymapKey(0, 4, 0, KC_A); | ||
| 116 | auto mod_tap_key = KeymapKey(0, 5, 0, RCTL_T(KC_B)); | ||
| 117 | |||
| 118 | set_keymap({ctrl_key, shft_key, alt_key, gui_key, regular_key, mod_tap_key}); | ||
| 119 | |||
| 120 | for (KeymapKey* mod_key : {&ctrl_key, &alt_key, &gui_key}) { | ||
| 121 | // Hold mod key. | ||
| 122 | EXPECT_REPORT(driver, (mod_key->code)); | ||
| 123 | mod_key->press(); | ||
| 124 | run_one_scan_loop(); | ||
| 125 | |||
| 126 | // Tap regular key. | ||
| 127 | EXPECT_REPORT(driver, (mod_key->code, KC_A)); | ||
| 128 | regular_key.press(); | ||
| 129 | run_one_scan_loop(); | ||
| 130 | VERIFY_AND_CLEAR(driver); | ||
| 131 | |||
| 132 | EXPECT_REPORT(driver, (mod_key->code)); | ||
| 133 | regular_key.release(); | ||
| 134 | run_one_scan_loop(); | ||
| 135 | VERIFY_AND_CLEAR(driver); | ||
| 136 | |||
| 137 | // Press mod-tap, where Flow Tap is disabled due to the held mod. | ||
| 138 | EXPECT_REPORT(driver, (mod_key->code, KC_RCTL)); | ||
| 139 | mod_tap_key.press(); | ||
| 140 | idle_for(TAPPING_TERM + 1); | ||
| 141 | VERIFY_AND_CLEAR(driver); | ||
| 142 | |||
| 143 | // Release mod-tap. | ||
| 144 | EXPECT_REPORT(driver, (mod_key->code)); | ||
| 145 | mod_tap_key.release(); | ||
| 146 | run_one_scan_loop(); | ||
| 147 | |||
| 148 | // Release mod key. | ||
| 149 | EXPECT_EMPTY_REPORT(driver); | ||
| 150 | mod_key->release(); | ||
| 151 | run_one_scan_loop(); | ||
| 152 | VERIFY_AND_CLEAR(driver); | ||
| 153 | } | ||
| 154 | |||
| 155 | // Hold Shift key. | ||
| 156 | EXPECT_REPORT(driver, (KC_LSFT)); | ||
| 157 | shft_key.press(); | ||
| 158 | run_one_scan_loop(); | ||
| 159 | |||
| 160 | // Tap regular key. | ||
| 161 | EXPECT_REPORT(driver, (KC_LSFT, KC_A)); | ||
| 162 | regular_key.press(); | ||
| 163 | run_one_scan_loop(); | ||
| 164 | VERIFY_AND_CLEAR(driver); | ||
| 165 | |||
| 166 | EXPECT_REPORT(driver, (KC_LSFT)); | ||
| 167 | regular_key.release(); | ||
| 168 | run_one_scan_loop(); | ||
| 169 | VERIFY_AND_CLEAR(driver); | ||
| 170 | |||
| 171 | // Press mod-tap, where Flow Tap applies to settle as tapped. | ||
| 172 | EXPECT_REPORT(driver, (KC_LSFT, KC_B)); | ||
| 173 | mod_tap_key.press(); | ||
| 174 | idle_for(TAPPING_TERM + 1); | ||
| 175 | VERIFY_AND_CLEAR(driver); | ||
| 176 | |||
| 177 | // Release mod-tap. | ||
| 178 | EXPECT_REPORT(driver, (KC_LSFT)); | ||
| 179 | mod_tap_key.release(); | ||
| 180 | run_one_scan_loop(); | ||
| 181 | |||
| 182 | // Release Shift key. | ||
| 183 | EXPECT_EMPTY_REPORT(driver); | ||
| 184 | shft_key.release(); | ||
| 185 | run_one_scan_loop(); | ||
| 186 | VERIFY_AND_CLEAR(driver); | ||
| 187 | } | ||
| 188 | |||
| 189 | // Test input with two mod-taps in a rolled press quickly after a regular key. | ||
| 190 | TEST_F(FlowTapTest, rolled_press) { | ||
| 29 | TestDriver driver; | 191 | TestDriver driver; |
| 30 | InSequence s; | 192 | InSequence s; |
| 31 | auto regular_key = KeymapKey(0, 0, 0, KC_A); | 193 | auto regular_key = KeymapKey(0, 0, 0, KC_A); |
| @@ -152,6 +314,53 @@ TEST_F(FlowTapTest, holding_multiple_mod_taps) { | |||
| 152 | VERIFY_AND_CLEAR(driver); | 314 | VERIFY_AND_CLEAR(driver); |
| 153 | } | 315 | } |
| 154 | 316 | ||
| 317 | TEST_F(FlowTapTest, holding_mod_tap_with_regular_mod) { | ||
| 318 | TestDriver driver; | ||
| 319 | InSequence s; | ||
| 320 | auto regular_key = KeymapKey(0, 0, 0, KC_A); | ||
| 321 | auto mod_key = KeymapKey(0, 1, 0, KC_LSFT); | ||
| 322 | auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_C)); | ||
| 323 | |||
| 324 | set_keymap({regular_key, mod_key, mod_tap_key}); | ||
| 325 | |||
| 326 | // Tap regular key. | ||
| 327 | EXPECT_REPORT(driver, (KC_A)); | ||
| 328 | EXPECT_EMPTY_REPORT(driver); | ||
| 329 | tap_key(regular_key); | ||
| 330 | VERIFY_AND_CLEAR(driver); | ||
| 331 | |||
| 332 | EXPECT_NO_REPORT(driver); | ||
| 333 | idle_for(FLOW_TAP_TERM + 1); | ||
| 334 | VERIFY_AND_CLEAR(driver); | ||
| 335 | |||
| 336 | // Press mod and mod-tap keys. | ||
| 337 | EXPECT_REPORT(driver, (KC_LSFT)); | ||
| 338 | mod_key.press(); | ||
| 339 | run_one_scan_loop(); | ||
| 340 | mod_tap_key.press(); | ||
| 341 | idle_for(TAPPING_TERM - 5); // Hold almost until tapping term. | ||
| 342 | VERIFY_AND_CLEAR(driver); | ||
| 343 | |||
| 344 | // Press regular key. | ||
| 345 | EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); | ||
| 346 | EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL, KC_A)); | ||
| 347 | regular_key.press(); | ||
| 348 | idle_for(10); | ||
| 349 | VERIFY_AND_CLEAR(driver); | ||
| 350 | |||
| 351 | // Release keys. | ||
| 352 | EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); | ||
| 353 | EXPECT_REPORT(driver, (KC_LCTL)); | ||
| 354 | EXPECT_EMPTY_REPORT(driver); | ||
| 355 | regular_key.release(); | ||
| 356 | run_one_scan_loop(); | ||
| 357 | mod_key.release(); | ||
| 358 | run_one_scan_loop(); | ||
| 359 | mod_tap_key.release(); | ||
| 360 | run_one_scan_loop(); | ||
| 361 | VERIFY_AND_CLEAR(driver); | ||
| 362 | } | ||
| 363 | |||
| 155 | TEST_F(FlowTapTest, layer_tap_key) { | 364 | TEST_F(FlowTapTest, layer_tap_key) { |
| 156 | TestDriver driver; | 365 | TestDriver driver; |
| 157 | InSequence s; | 366 | InSequence s; |
| @@ -216,6 +425,125 @@ TEST_F(FlowTapTest, layer_tap_key) { | |||
| 216 | VERIFY_AND_CLEAR(driver); | 425 | VERIFY_AND_CLEAR(driver); |
| 217 | } | 426 | } |
| 218 | 427 | ||
| 428 | TEST_F(FlowTapTest, layer_tap_ignored_with_disabled_key) { | ||
| 429 | TestDriver driver; | ||
| 430 | InSequence s; | ||
| 431 | auto no_key = KeymapKey(0, 0, 0, KC_NO); | ||
| 432 | auto regular_key = KeymapKey(1, 0, 0, KC_ESC); | ||
| 433 | auto layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A)); | ||
| 434 | auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_B)); | ||
| 435 | |||
| 436 | set_keymap({no_key, regular_key, layer_tap_key, mod_tap_key}); | ||
| 437 | |||
| 438 | EXPECT_REPORT(driver, (KC_ESC)); | ||
| 439 | EXPECT_EMPTY_REPORT(driver); | ||
| 440 | layer_tap_key.press(); | ||
| 441 | idle_for(TAPPING_TERM + 1); | ||
| 442 | tap_key(regular_key); | ||
| 443 | layer_tap_key.release(); | ||
| 444 | run_one_scan_loop(); | ||
| 445 | VERIFY_AND_CLEAR(driver); | ||
| 446 | |||
| 447 | EXPECT_REPORT(driver, (KC_LCTL)); | ||
| 448 | mod_tap_key.press(); | ||
| 449 | idle_for(TAPPING_TERM + 1); | ||
| 450 | VERIFY_AND_CLEAR(driver); | ||
| 451 | |||
| 452 | EXPECT_EMPTY_REPORT(driver); | ||
| 453 | mod_tap_key.release(); | ||
| 454 | run_one_scan_loop(); | ||
| 455 | VERIFY_AND_CLEAR(driver); | ||
| 456 | } | ||
| 457 | |||
| 458 | TEST_F(FlowTapTest, layer_tap_ignored_with_disabled_key_complex) { | ||
| 459 | TestDriver driver; | ||
| 460 | InSequence s; | ||
| 461 | auto regular_key1 = KeymapKey(0, 0, 0, KC_Q); | ||
| 462 | auto layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_SPC)); | ||
| 463 | auto mod_tap_key1 = KeymapKey(0, 2, 0, CTL_T(KC_T)); | ||
| 464 | // Place RALT_T(KC_I), where Flow Tap is enabled, in the same position on | ||
| 465 | // layer 0 as KC_RGHT, where Flow Tap is disabled. This tests that Flow Tap | ||
| 466 | // tracks the keycode from the correct layer. | ||
| 467 | auto mod_tap_key2 = KeymapKey(0, 3, 0, RALT_T(KC_I)); | ||
| 468 | auto regular_key2 = KeymapKey(1, 3, 0, KC_RGHT); | ||
| 469 | |||
| 470 | set_keymap({regular_key1, layer_tap_key, mod_tap_key1, mod_tap_key2, regular_key2}); | ||
| 471 | |||
| 472 | // Tap regular key 1. | ||
| 473 | EXPECT_REPORT(driver, (KC_Q)); | ||
| 474 | EXPECT_EMPTY_REPORT(driver); | ||
| 475 | tap_key(regular_key1); | ||
| 476 | idle_for(FLOW_TAP_TERM + 1); | ||
| 477 | VERIFY_AND_CLEAR(driver); | ||
| 478 | |||
| 479 | // Hold layer-tap key. | ||
| 480 | EXPECT_NO_REPORT(driver); | ||
| 481 | layer_tap_key.press(); | ||
| 482 | run_one_scan_loop(); | ||
| 483 | // idle_for(TAPPING_TERM + 1); | ||
| 484 | VERIFY_AND_CLEAR(driver); | ||
| 485 | |||
| 486 | // Tap regular key 2. | ||
| 487 | EXPECT_REPORT(driver, (KC_RGHT)); | ||
| 488 | EXPECT_EMPTY_REPORT(driver); | ||
| 489 | tap_key(regular_key2); | ||
| 490 | VERIFY_AND_CLEAR(driver); | ||
| 491 | |||
| 492 | // Release layer-tap key. | ||
| 493 | EXPECT_NO_REPORT(driver); | ||
| 494 | layer_tap_key.release(); | ||
| 495 | run_one_scan_loop(); | ||
| 496 | VERIFY_AND_CLEAR(driver); | ||
| 497 | |||
| 498 | // Quickly hold mod-tap key 1. | ||
| 499 | EXPECT_NO_REPORT(driver); | ||
| 500 | mod_tap_key1.press(); | ||
| 501 | run_one_scan_loop(); | ||
| 502 | VERIFY_AND_CLEAR(driver); | ||
| 503 | |||
| 504 | EXPECT_REPORT(driver, (KC_LCTL)); | ||
| 505 | EXPECT_REPORT(driver, (KC_LCTL, KC_Q)); | ||
| 506 | EXPECT_REPORT(driver, (KC_LCTL)); | ||
| 507 | tap_key(regular_key1); | ||
| 508 | VERIFY_AND_CLEAR(driver); | ||
| 509 | |||
| 510 | EXPECT_EMPTY_REPORT(driver); | ||
| 511 | mod_tap_key1.release(); | ||
| 512 | run_one_scan_loop(); | ||
| 513 | VERIFY_AND_CLEAR(driver); | ||
| 514 | } | ||
| 515 | |||
| 516 | TEST_F(FlowTapTest, layer_tap_ignored_with_enabled_key) { | ||
| 517 | TestDriver driver; | ||
| 518 | InSequence s; | ||
| 519 | auto no_key = KeymapKey(0, 0, 0, KC_NO); | ||
| 520 | auto regular_key = KeymapKey(1, 0, 0, KC_C); | ||
| 521 | auto layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A)); | ||
| 522 | auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_B)); | ||
| 523 | |||
| 524 | set_keymap({no_key, regular_key, layer_tap_key, mod_tap_key}); | ||
| 525 | |||
| 526 | EXPECT_REPORT(driver, (KC_C)); | ||
| 527 | EXPECT_EMPTY_REPORT(driver); | ||
| 528 | layer_tap_key.press(); | ||
| 529 | idle_for(TAPPING_TERM + 1); | ||
| 530 | tap_key(regular_key); | ||
| 531 | layer_tap_key.release(); | ||
| 532 | run_one_scan_loop(); | ||
| 533 | VERIFY_AND_CLEAR(driver); | ||
| 534 | |||
| 535 | EXPECT_REPORT(driver, (KC_B)); | ||
| 536 | mod_tap_key.press(); | ||
| 537 | run_one_scan_loop(); | ||
| 538 | VERIFY_AND_CLEAR(driver); | ||
| 539 | |||
| 540 | EXPECT_EMPTY_REPORT(driver); | ||
| 541 | idle_for(TAPPING_TERM + 1); | ||
| 542 | mod_tap_key.release(); | ||
| 543 | run_one_scan_loop(); | ||
| 544 | VERIFY_AND_CLEAR(driver); | ||
| 545 | } | ||
| 546 | |||
| 219 | TEST_F(FlowTapTest, combo_key) { | 547 | TEST_F(FlowTapTest, combo_key) { |
| 220 | TestDriver driver; | 548 | TestDriver driver; |
| 221 | InSequence s; | 549 | InSequence s; |
| @@ -275,6 +603,7 @@ TEST_F(FlowTapTest, oneshot_mod_key) { | |||
| 275 | // Nested press of OSM and regular keys. | 603 | // Nested press of OSM and regular keys. |
| 276 | EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); | 604 | EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); |
| 277 | EXPECT_REPORT(driver, (KC_LSFT, KC_A)); | 605 | EXPECT_REPORT(driver, (KC_LSFT, KC_A)); |
| 606 | EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); | ||
| 278 | EXPECT_EMPTY_REPORT(driver); | 607 | EXPECT_EMPTY_REPORT(driver); |
| 279 | osm_key.press(); | 608 | osm_key.press(); |
| 280 | run_one_scan_loop(); | 609 | run_one_scan_loop(); |
