keyboard stuff
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[Core] Speculative Hold option for mod-taps: hold mods instantly while unsettled. (#25572)

authored by

Pascal Getreuer and committed by
GitHub
efc5d633 2af9aac6

+3662 -1
+1
data/mappings/info_config.hjson
··· 224 224 "PERMISSIVE_HOLD_PER_KEY": {"info_key": "tapping.permissive_hold_per_key", "value_type": "flag"}, 225 225 "RETRO_TAPPING": {"info_key": "tapping.retro", "value_type": "flag"}, 226 226 "RETRO_TAPPING_PER_KEY": {"info_key": "tapping.retro_per_key", "value_type": "flag"}, 227 + "SPECULATIVE_HOLD": {"info_key": "tapping.speculative_hold", "value_type": "flag"}, 227 228 "TAP_CODE_DELAY": {"info_key": "qmk.tap_keycode_delay", "value_type": "int"}, 228 229 "TAP_HOLD_CAPS_DELAY": {"info_key": "qmk.tap_capslock_delay", "value_type": "int"}, 229 230 "TAPPING_TERM": {"info_key": "tapping.term", "value_type": "int"},
+33
docs/tap_hold.md
··· 779 779 780 780 [Auto Shift](features/auto_shift) has its own version of `retro tapping` called `retro shift`. It is extremely similar to `retro tapping`, but holding the key past `AUTO_SHIFT_TIMEOUT` results in the value it sends being shifted. Other configurations also affect it differently; see [here](features/auto_shift#retro-shift) for more information. 781 781 782 + ### Speculative Hold 783 + 784 + Speculative Hold makes mod-tap keys more responsive by applying the modifier instantly on keydown, before the tap-hold decision is made. This is especially useful for actions like Shift+Click with a mouse, which can feel laggy with standard mod-taps. 785 + 786 + The firmware holds the modifier speculatively. Once the key's behavior is settled: 787 + 788 + * If held, the modifier remains active as expected until the key is released. 789 + * If tapped, the speculative modifier is canceled just before the tapping keycode is sent. 790 + 791 + Speculative Hold applies the modifier early but does not change the underlying tap-hold decision logic. Speculative Hold is compatible to use in combination with any other tap-hold options. 792 + 793 + To enable Speculative Hold, add the following to your `config.h`: 794 + 795 + ```c 796 + #define SPECULATIVE_HOLD 797 + ``` 798 + 799 + By default, Speculative Hold applies to mod-taps using Shift, Ctrl, or Shift + Ctrl. You can override this behavior by defining the `get_speculative_hold()` callback in your keymap, for instance: 800 + 801 + ```c 802 + bool get_speculative_hold(uint16_t keycode, keyrecord_t* record) { 803 + switch (keycode) { // These keys may be speculatively held. 804 + case LCTL_T(KC_ESC): 805 + case LSFT_T(KC_Z): 806 + case RSFT_T(KC_SLSH): 807 + return true; 808 + } 809 + return false; // Disable otherwise. 810 + } 811 + ``` 812 + 813 + Some operating systems or applications assign actions to tapping a modifier key by itself, e.g., tapping GUI to open a start menu. Because Speculative Hold sends a lone modifier key press in some cases, it can falsely trigger these actions. To prevent this, set `DUMMY_MOD_NEUTRALIZER_KEYCODE` (and optionally `MODS_TO_NEUTRALIZE`) in your `config.h` in the same way as described above for [Retro Tapping](#retro-tapping). 814 + 782 815 ## Why do we include the key record for the per key functions? 783 816 784 817 One thing that you may notice is that we include the key record for all of the "per key" functions, and may be wondering why we do that.
+5
quantum/action.c
··· 281 281 if (IS_NOEVENT(record->event)) { 282 282 return; 283 283 } 284 + #ifdef SPECULATIVE_HOLD 285 + if (record->event.pressed) { 286 + speculative_key_settled(record); 287 + } 288 + #endif // SPECULATIVE_HOLD 284 289 #ifdef FLOW_TAP_TERM 285 290 flow_tap_update_last_event(record); 286 291 #endif // FLOW_TAP_TERM
+1 -1
quantum/action.h
··· 38 38 /* tapping count and state */ 39 39 typedef struct { 40 40 bool interrupted : 1; 41 - bool reserved2 : 1; 41 + bool speculated : 1; 42 42 bool reserved1 : 1; 43 43 bool reserved0 : 1; 44 44 uint8_t count : 4;
+171
quantum/action_tapping.c
··· 6 6 #include "action_tapping.h" 7 7 #include "action_util.h" 8 8 #include "keycode.h" 9 + #include "keycode_config.h" 9 10 #include "quantum_keycodes.h" 10 11 #include "timer.h" 12 + #include "wait.h" 11 13 12 14 #ifndef NO_ACTION_TAPPING 13 15 ··· 51 53 } 52 54 # endif 53 55 56 + # ifdef SPECULATIVE_HOLD 57 + typedef struct { 58 + keypos_t key; 59 + uint8_t mods; 60 + } speculative_key_t; 61 + # define SPECULATIVE_KEYS_SIZE 8 62 + static speculative_key_t speculative_keys[SPECULATIVE_KEYS_SIZE] = {}; 63 + static uint8_t num_speculative_keys = 0; 64 + static uint8_t prev_speculative_mods = 0; 65 + static uint8_t speculative_mods = 0; 66 + 67 + /** Handler to be called on incoming press events. */ 68 + static void speculative_key_press(keyrecord_t *record); 69 + # endif // SPECULATIVE_HOLD 70 + 54 71 # if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM) 55 72 # define REGISTERED_TAPS_SIZE 8 56 73 // Array of tap-hold keys that have been settled as tapped but not yet released. ··· 129 146 * FIXME: Needs doc 130 147 */ 131 148 void action_tapping_process(keyrecord_t record) { 149 + # ifdef SPECULATIVE_HOLD 150 + prev_speculative_mods = speculative_mods; 151 + if (record.event.pressed) { 152 + speculative_key_press(&record); 153 + } 154 + # endif // SPECULATIVE_HOLD 155 + 132 156 if (process_tapping(&record)) { 133 157 if (IS_EVENT(record.event)) { 134 158 ac_dprintf("processed: "); ··· 144 168 tapping_key = (keyrecord_t){0}; 145 169 } 146 170 } 171 + 172 + # ifdef SPECULATIVE_HOLD 173 + if (speculative_mods != prev_speculative_mods) { 174 + send_keyboard_report(); 175 + } 176 + # endif // SPECULATIVE_HOLD 147 177 148 178 // process waiting_buffer 149 179 if (IS_EVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) { ··· 707 737 } 708 738 } 709 739 } 740 + 741 + # ifdef SPECULATIVE_HOLD 742 + static void debug_speculative_keys(void) { 743 + ac_dprintf("mods = { "); 744 + for (int8_t i = 0; i < num_speculative_keys; ++i) { 745 + ac_dprintf("%02X ", speculative_keys[i].mods); 746 + } 747 + ac_dprintf("}, keys = { "); 748 + for (int8_t i = 0; i < num_speculative_keys; ++i) { 749 + ac_dprintf("%02X%02X ", speculative_keys[i].key.row, speculative_keys[i].key.col); 750 + } 751 + ac_dprintf("}\n"); 752 + } 753 + 754 + // Find key in speculative_keys. Returns num_speculative_keys if not found. 755 + static int8_t speculative_keys_find(keypos_t key) { 756 + uint8_t i; 757 + for (i = 0; i < num_speculative_keys; ++i) { 758 + if (KEYEQ(speculative_keys[i].key, key)) { 759 + break; 760 + } 761 + } 762 + return i; 763 + } 764 + 765 + static void speculative_key_press(keyrecord_t *record) { 766 + if (num_speculative_keys >= SPECULATIVE_KEYS_SIZE) { // Overflow! 767 + ac_dprintf("SPECULATIVE KEYS OVERFLOW: IGNORING EVENT\n"); 768 + return; // Don't trigger: speculative_keys is full. 769 + } 770 + if (speculative_keys_find(record->event.key) < num_speculative_keys) { 771 + return; // Don't trigger: key is already in speculative_keys. 772 + } 773 + 774 + const uint16_t keycode = get_record_keycode(record, false); 775 + if (!IS_QK_MOD_TAP(keycode)) { 776 + return; // Don't trigger: not a mod-tap key. 777 + } 778 + 779 + uint8_t mods = mod_config(QK_MOD_TAP_GET_MODS(keycode)); 780 + if ((mods & 0x10) != 0) { // Unpack 5-bit mods to 8-bit representation. 781 + mods <<= 4; 782 + } 783 + if ((~(get_mods() | speculative_mods) & mods) == 0) { 784 + return; // Don't trigger: mods are already active. 785 + } 786 + 787 + // Don't do Speculative Hold when there are non-speculated buffered events, 788 + // since that could result in sending keys out of order. 789 + for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { 790 + if (!waiting_buffer[i].tap.speculated) { 791 + return; 792 + } 793 + } 794 + 795 + if (get_speculative_hold(keycode, record)) { 796 + record->tap.speculated = true; 797 + speculative_mods |= mods; 798 + // Remember the keypos and mods associated with this key. 799 + speculative_keys[num_speculative_keys] = (speculative_key_t){ 800 + .key = record->event.key, 801 + .mods = mods, 802 + }; 803 + ++num_speculative_keys; 804 + 805 + ac_dprintf("Speculative Hold: "); 806 + debug_speculative_keys(); 807 + } 808 + } 809 + 810 + uint8_t get_speculative_mods(void) { 811 + return speculative_mods; 812 + } 813 + 814 + __attribute__((weak)) bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) { 815 + const uint8_t mods = mod_config(QK_MOD_TAP_GET_MODS(keycode)); 816 + return (mods & (MOD_LCTL | MOD_LSFT)) == mods; 817 + } 818 + 819 + void speculative_key_settled(keyrecord_t *record) { 820 + if (num_speculative_keys == 0) { 821 + return; // Early return when there are no active speculative keys. 822 + } 823 + 824 + uint8_t i = speculative_keys_find(record->event.key); 825 + 826 + const uint16_t keycode = get_record_keycode(record, false); 827 + if (IS_QK_MOD_TAP(keycode) && record->tap.count == 0) { // MT hold press. 828 + if (i < num_speculative_keys) { 829 + --num_speculative_keys; 830 + const uint8_t cleared_mods = speculative_keys[i].mods; 831 + 832 + if (num_speculative_keys) { 833 + speculative_mods &= ~cleared_mods; 834 + // Don't call send_keyboard_report() here; allow default 835 + // handling to reapply the mod before the next report. 836 + 837 + // Remove the ith entry from speculative_keys. 838 + for (uint8_t j = i; j < num_speculative_keys; ++j) { 839 + speculative_keys[j] = speculative_keys[j + 1]; 840 + } 841 + } else { 842 + speculative_mods = 0; 843 + } 844 + 845 + ac_dprintf("Speculative Hold: settled %02x, ", cleared_mods); 846 + debug_speculative_keys(); 847 + } 848 + } else { // Tap press event; cancel speculatively-held mod. 849 + if (i >= num_speculative_keys) { 850 + i = 0; 851 + } 852 + 853 + // Clear mods for the ith key and all keys that follow. 854 + uint8_t cleared_mods = 0; 855 + for (uint8_t j = i; j < num_speculative_keys; ++j) { 856 + cleared_mods |= speculative_keys[j].mods; 857 + } 858 + 859 + num_speculative_keys = i; // Remove ith and following entries. 860 + 861 + if ((prev_speculative_mods & cleared_mods) != 0) { 862 + # ifdef DUMMY_MOD_NEUTRALIZER_KEYCODE 863 + neutralize_flashing_modifiers(get_mods() | prev_speculative_mods); 864 + # endif // DUMMY_MOD_NEUTRALIZER_KEYCODE 865 + } 866 + 867 + if (num_speculative_keys) { 868 + speculative_mods &= ~cleared_mods; 869 + } else { 870 + speculative_mods = 0; 871 + } 872 + 873 + send_keyboard_report(); 874 + wait_ms(TAP_CODE_DELAY); 875 + 876 + ac_dprintf("Speculative Hold: canceled %02x, ", cleared_mods); 877 + debug_speculative_keys(); 878 + } 879 + } 880 + # endif // SPECULATIVE_HOLD 710 881 711 882 # if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM) 712 883 static void registered_taps_add(keypos_t key) {
+30
quantum/action_tapping.h
··· 46 46 bool get_retro_tapping(uint16_t keycode, keyrecord_t *record); 47 47 bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record); 48 48 49 + #ifdef SPECULATIVE_HOLD 50 + /** Gets the currently active speculative mods. */ 51 + uint8_t get_speculative_mods(void); 52 + 53 + /** 54 + * Callback to say if a mod-tap key may be speculatively held. 55 + * 56 + * By default, speculative hold is enabled for mod-tap keys where the mod is 57 + * Ctrl, Shift, and Ctrl+Shift for either hand. 58 + * 59 + * @param keycode Keycode of the mod-tap key. 60 + * @param record Record associated with the mod-tap press event. 61 + * @return True if the mod-tap key may be speculatively held. 62 + */ 63 + bool get_speculative_hold(uint16_t keycode, keyrecord_t *record); 64 + 65 + /** 66 + * Handler to be called on press events after tap-holds are settled. 67 + * 68 + * This function is to be called in process_record() in action.c, that is, just 69 + * after tap-hold events are settled as either tapped or held. When `record` 70 + * corresponds to a speculatively-held key, the speculative mod is cleared. 71 + * 72 + * @param record Record associated with the mod-tap press event. 73 + */ 74 + void speculative_key_settled(keyrecord_t *record); 75 + #else 76 + # define get_speculative_mods() 0 77 + #endif // SPECULATIVE_HOLD 78 + 49 79 #ifdef CHORDAL_HOLD 50 80 /** 51 81 * Callback to say when a key chord before the tapping term may be held.
+5
quantum/action_util.c
··· 19 19 #include "debug.h" 20 20 #include "action_util.h" 21 21 #include "action_layer.h" 22 + #include "action_tapping.h" 22 23 #include "timer.h" 23 24 #include "keycode_config.h" 24 25 #include <string.h> ··· 282 283 clear_oneshot_mods(); 283 284 } 284 285 } 286 + #endif 287 + 288 + #ifdef SPECULATIVE_HOLD 289 + mods |= get_speculative_mods(); 285 290 #endif 286 291 287 292 #ifdef KEY_OVERRIDE_ENABLE
+23
tests/tap_hold_configurations/speculative_hold/default/config.h
··· 1 + /* Copyright 2022 Vladislav Kucheriavykh 2 + * Copyright 2025 Google LLC 3 + * 4 + * This program is free software: you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation, either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 + */ 17 + 18 + #pragma once 19 + 20 + #include "test_common.h" 21 + 22 + #define SPECULATIVE_HOLD 23 + #define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_F24
+21
tests/tap_hold_configurations/speculative_hold/default/test.mk
··· 1 + # Copyright 2022 Vladislav Kucheriavykh 2 + # Copyright 2025 Google LLC 3 + # 4 + # This program is free software: you can redistribute it and/or modify 5 + # it under the terms of the GNU General Public License as published by 6 + # the Free Software Foundation, either version 2 of the License, or 7 + # (at your option) any later version. 8 + # 9 + # This program is distributed in the hope that it will be useful, 10 + # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + # GNU General Public License for more details. 13 + # 14 + # You should have received a copy of the GNU General Public License 15 + # along with this program. If not, see <http://www.gnu.org/licenses/>. 16 + 17 + KEY_OVERRIDE_ENABLE = yes 18 + MAGIC_ENABLE = yes 19 + 20 + INTROSPECTION_KEYMAP_C = test_keymap.c 21 +
+20
tests/tap_hold_configurations/speculative_hold/default/test_keymap.c
··· 1 + // Copyright 2025 Google LLC 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // https://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + #include "quantum.h" 16 + 17 + // Shift + Esc = Home 18 + const key_override_t home_esc_override = ko_make_basic(MOD_MASK_SHIFT, KC_ESC, KC_HOME); 19 + 20 + const key_override_t *key_overrides[] = {&home_esc_override};
+794
tests/tap_hold_configurations/speculative_hold/default/test_tap_hold.cpp
··· 1 + // Copyright 2025 Google LLC 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // https://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + #include <functional> 16 + 17 + #include "keyboard_report_util.hpp" 18 + #include "keycode.h" 19 + #include "test_common.hpp" 20 + #include "action_tapping.h" 21 + #include "test_fixture.hpp" 22 + #include "test_keymap_key.hpp" 23 + 24 + using testing::_; 25 + using testing::AnyNumber; 26 + using testing::InSequence; 27 + 28 + namespace { 29 + 30 + // Gets the unpacked 8-bit mods corresponding to a given mod-tap keycode. 31 + uint8_t unpack_mod_tap_mods(uint16_t keycode) { 32 + const uint8_t mods5 = QK_MOD_TAP_GET_MODS(keycode); 33 + return (mods5 & 0x10) != 0 ? (mods5 << 4) : mods5; 34 + } 35 + 36 + bool get_speculative_hold_all_keys(uint16_t keycode, keyrecord_t *record) { 37 + return true; // Enable Speculative Hold for all mod-tap keys. 38 + } 39 + 40 + bool process_record_user_default(uint16_t keycode, keyrecord_t *record) { 41 + return true; 42 + } 43 + 44 + // Indirection so that get_speculative_hold() and process_record_user() can be 45 + // replaced with other functions in the test cases below. 46 + std::function<bool(uint16_t, keyrecord_t *)> get_speculative_hold_fun = get_speculative_hold_all_keys; 47 + std::function<bool(uint16_t, keyrecord_t *)> process_record_user_fun = process_record_user_default; 48 + 49 + extern "C" bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) { 50 + return get_speculative_hold_fun(keycode, record); 51 + } 52 + 53 + extern "C" bool process_record_user(uint16_t keycode, keyrecord_t *record) { 54 + return process_record_user_fun(keycode, record); 55 + } 56 + 57 + class SpeculativeHoldDefault : public TestFixture { 58 + public: 59 + void SetUp() override { 60 + get_speculative_hold_fun = get_speculative_hold_all_keys; 61 + process_record_user_fun = process_record_user_default; 62 + } 63 + }; 64 + 65 + TEST_F(SpeculativeHoldDefault, tap_mod_tap) { 66 + TestDriver driver; 67 + InSequence s; 68 + static int process_record_user_calls = 0; 69 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); 70 + 71 + set_keymap({mod_tap_key}); 72 + 73 + process_record_user_fun = [](uint16_t keycode, keyrecord_t *record) { 74 + ++process_record_user_calls; 75 + return true; 76 + }; 77 + 78 + // Press mod-tap-hold key. Mod is held speculatively. 79 + EXPECT_REPORT(driver, (KC_LSFT)); 80 + mod_tap_key.press(); 81 + idle_for(10); 82 + VERIFY_AND_CLEAR(driver); 83 + EXPECT_EQ(get_speculative_mods(), MOD_BIT_LSHIFT); 84 + // Speculative mod holds and releases are made directly, bypassing regular 85 + // event processing. No calls have been made yet to process_record_user(). 86 + EXPECT_EQ(process_record_user_calls, 0); 87 + 88 + // Release mod-tap-hold key. 89 + EXPECT_EMPTY_REPORT(driver); // Speculative mod canceled. 90 + EXPECT_REPORT(driver, (KC_P)); 91 + EXPECT_EMPTY_REPORT(driver); 92 + mod_tap_key.release(); 93 + run_one_scan_loop(); 94 + VERIFY_AND_CLEAR(driver); 95 + // All mods are released. 96 + EXPECT_EQ(get_speculative_mods(), 0); 97 + EXPECT_EQ(get_mods(), 0); 98 + // Two calls have now been made, for pressing and releasing KC_P. 99 + EXPECT_EQ(process_record_user_calls, 2); 100 + 101 + // Idle for tapping term of mod tap hold key. 102 + idle_for(TAPPING_TERM - 10); 103 + VERIFY_AND_CLEAR(driver); 104 + } 105 + 106 + TEST_F(SpeculativeHoldDefault, tap_mod_tap_neutralized) { 107 + TestDriver driver; 108 + InSequence s; 109 + auto mod_tap_key = KeymapKey(0, 1, 0, GUI_T(KC_P)); 110 + 111 + set_keymap({mod_tap_key}); 112 + 113 + // Press mod-tap-hold key. Mod is held speculatively. 114 + EXPECT_REPORT(driver, (KC_LGUI)); 115 + mod_tap_key.press(); 116 + idle_for(10); 117 + VERIFY_AND_CLEAR(driver); 118 + 119 + // Release mod-tap-hold key. Speculative mod is neutralized and canceled. 120 + EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 121 + EXPECT_REPORT(driver, (KC_LGUI)); 122 + EXPECT_EMPTY_REPORT(driver); 123 + EXPECT_REPORT(driver, (KC_P)); 124 + EXPECT_EMPTY_REPORT(driver); 125 + mod_tap_key.release(); 126 + run_one_scan_loop(); 127 + VERIFY_AND_CLEAR(driver); 128 + 129 + // Idle for tapping term of mod tap hold key. 130 + idle_for(TAPPING_TERM - 10); 131 + VERIFY_AND_CLEAR(driver); 132 + } 133 + 134 + TEST_F(SpeculativeHoldDefault, hold_two_mod_taps) { 135 + TestDriver driver; 136 + InSequence s; 137 + auto mod_tap_key1 = KeymapKey(0, 1, 0, LCTL_T(KC_A)); 138 + auto mod_tap_key2 = KeymapKey(0, 2, 0, RALT_T(KC_B)); 139 + 140 + set_keymap({mod_tap_key1, mod_tap_key2}); 141 + 142 + // Press first mod-tap key. 143 + EXPECT_REPORT(driver, (KC_LCTL)); 144 + mod_tap_key1.press(); 145 + run_one_scan_loop(); 146 + VERIFY_AND_CLEAR(driver); 147 + EXPECT_EQ(get_speculative_mods(), MOD_BIT_LCTRL); 148 + 149 + // Press second mod-tap key. 150 + EXPECT_REPORT(driver, (KC_LCTL, KC_RALT)); 151 + mod_tap_key2.press(); 152 + run_one_scan_loop(); 153 + VERIFY_AND_CLEAR(driver); 154 + EXPECT_EQ(get_speculative_mods(), MOD_BIT_LCTRL | MOD_BIT_RALT); 155 + 156 + EXPECT_NO_REPORT(driver); 157 + idle_for(TAPPING_TERM + 1); 158 + VERIFY_AND_CLEAR(driver); 159 + EXPECT_EQ(get_speculative_mods(), 0); 160 + EXPECT_EQ(get_mods(), MOD_BIT_LCTRL | MOD_BIT_RALT); 161 + 162 + // Release first mod-tap key. 163 + EXPECT_REPORT(driver, (KC_RALT)); 164 + mod_tap_key1.release(); 165 + run_one_scan_loop(); 166 + VERIFY_AND_CLEAR(driver); 167 + 168 + // Release second mod-tap key. 169 + EXPECT_EMPTY_REPORT(driver); 170 + mod_tap_key2.release(); 171 + run_one_scan_loop(); 172 + VERIFY_AND_CLEAR(driver); 173 + } 174 + 175 + TEST_F(SpeculativeHoldDefault, two_mod_taps_same_mods) { 176 + TestDriver driver; 177 + InSequence s; 178 + auto mod_tap_key1 = KeymapKey(0, 1, 0, GUI_T(KC_A)); 179 + auto mod_tap_key2 = KeymapKey(0, 2, 0, GUI_T(KC_B)); 180 + 181 + set_keymap({mod_tap_key1, mod_tap_key2}); 182 + 183 + // Press first mod-tap key. 184 + EXPECT_REPORT(driver, (KC_LGUI)); 185 + mod_tap_key1.press(); 186 + run_one_scan_loop(); 187 + VERIFY_AND_CLEAR(driver); 188 + 189 + // Tap second mod-tap key. 190 + EXPECT_NO_REPORT(driver); 191 + mod_tap_key2.press(); 192 + run_one_scan_loop(); 193 + mod_tap_key2.release(); 194 + run_one_scan_loop(); 195 + VERIFY_AND_CLEAR(driver); 196 + 197 + // Release first mod-tap key. 198 + EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 199 + EXPECT_REPORT(driver, (KC_LGUI)); 200 + EXPECT_EMPTY_REPORT(driver); 201 + EXPECT_REPORT(driver, (KC_A)); 202 + EXPECT_REPORT(driver, (KC_A, KC_B)); 203 + EXPECT_REPORT(driver, (KC_A)); 204 + EXPECT_EMPTY_REPORT(driver); 205 + mod_tap_key1.release(); 206 + run_one_scan_loop(); 207 + VERIFY_AND_CLEAR(driver); 208 + } 209 + 210 + TEST_F(SpeculativeHoldDefault, respects_get_speculative_hold_callback) { 211 + TestDriver driver; 212 + InSequence s; 213 + auto mod_tap_key1 = KeymapKey(0, 0, 0, LSFT_T(KC_A)); 214 + auto mod_tap_key2 = KeymapKey(0, 1, 0, LSFT_T(KC_B)); 215 + auto mod_tap_key3 = KeymapKey(0, 2, 0, LCTL_T(KC_C)); 216 + auto mod_tap_key4 = KeymapKey(0, 3, 0, LCTL_T(KC_D)); 217 + auto mod_tap_key5 = KeymapKey(0, 4, 0, RSFT_T(KC_E)); 218 + auto mod_tap_key6 = KeymapKey(0, 5, 0, RSFT_T(KC_F)); 219 + 220 + set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3, mod_tap_key4, mod_tap_key5, mod_tap_key6}); 221 + 222 + // Enable Speculative Hold selectively for some of the keys. 223 + get_speculative_hold_fun = [](uint16_t keycode, keyrecord_t *record) { 224 + switch (keycode) { 225 + case LSFT_T(KC_B): 226 + case LCTL_T(KC_D): 227 + case RSFT_T(KC_F): 228 + return true; 229 + } 230 + return false; 231 + }; 232 + 233 + for (KeymapKey *mod_tap_key : {&mod_tap_key2, &mod_tap_key4, &mod_tap_key6}) { 234 + SCOPED_TRACE(std::string("mod_tap_key = ") + mod_tap_key->name); 235 + const uint8_t mods = unpack_mod_tap_mods(mod_tap_key->code); 236 + 237 + // Long press and release mod_tap_key. 238 + // For these keys where Speculative Hold is enabled, then the mod should 239 + // activate immediately on keydown. 240 + EXPECT_REPORT(driver, (KC_LCTL + biton(mods))); 241 + mod_tap_key->press(); 242 + run_one_scan_loop(); 243 + EXPECT_EQ(get_speculative_mods(), mods); 244 + EXPECT_EQ(get_mods(), 0); 245 + VERIFY_AND_CLEAR(driver); 246 + 247 + EXPECT_NO_REPORT(driver); 248 + idle_for(TAPPING_TERM + 1); 249 + EXPECT_EQ(get_speculative_mods(), 0); 250 + EXPECT_EQ(get_mods(), mods); 251 + VERIFY_AND_CLEAR(driver); 252 + 253 + EXPECT_EMPTY_REPORT(driver); 254 + mod_tap_key->release(); 255 + idle_for(TAPPING_TERM + 1); 256 + EXPECT_EQ(get_speculative_mods(), 0); 257 + EXPECT_EQ(get_mods(), 0); 258 + VERIFY_AND_CLEAR(driver); 259 + } 260 + 261 + for (KeymapKey *mod_tap_key : {&mod_tap_key1, &mod_tap_key3, &mod_tap_key5}) { 262 + SCOPED_TRACE(std::string("mod_tap_key = ") + mod_tap_key->name); 263 + const uint8_t mods = unpack_mod_tap_mods(mod_tap_key->code); 264 + 265 + // Long press and release mod_tap_key. 266 + // For these keys where Speculative Hold is disabled, the mod should 267 + // activate when the key has settled after the tapping term. 268 + EXPECT_NO_REPORT(driver); 269 + mod_tap_key->press(); 270 + run_one_scan_loop(); 271 + EXPECT_EQ(get_speculative_mods(), 0); 272 + EXPECT_EQ(get_mods(), 0); 273 + VERIFY_AND_CLEAR(driver); 274 + 275 + EXPECT_REPORT(driver, (KC_LCTL + biton(mods))); 276 + idle_for(TAPPING_TERM + 1); 277 + EXPECT_EQ(get_speculative_mods(), 0); 278 + EXPECT_EQ(get_mods(), mods); 279 + VERIFY_AND_CLEAR(driver); 280 + 281 + EXPECT_EMPTY_REPORT(driver); 282 + mod_tap_key->release(); 283 + idle_for(TAPPING_TERM + 1); 284 + EXPECT_EQ(get_speculative_mods(), 0); 285 + EXPECT_EQ(get_mods(), 0); 286 + VERIFY_AND_CLEAR(driver); 287 + } 288 + } 289 + 290 + TEST_F(SpeculativeHoldDefault, respects_magic_mod_config) { 291 + TestDriver driver; 292 + InSequence s; 293 + auto mod_tap_key = KeymapKey(0, 1, 0, CTL_T(KC_P)); 294 + 295 + set_keymap({mod_tap_key}); 296 + 297 + keymap_config.swap_lctl_lgui = true; 298 + 299 + // Press mod-tap-hold key. 300 + EXPECT_REPORT(driver, (KC_LGUI)); 301 + mod_tap_key.press(); 302 + run_one_scan_loop(); 303 + VERIFY_AND_CLEAR(driver); 304 + 305 + // Release mod-tap-hold key. 306 + EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 307 + EXPECT_REPORT(driver, (KC_LGUI)); 308 + EXPECT_EMPTY_REPORT(driver); 309 + EXPECT_REPORT(driver, (KC_P)); 310 + EXPECT_EMPTY_REPORT(driver); 311 + mod_tap_key.release(); 312 + idle_for(TAPPING_TERM + 1); 313 + VERIFY_AND_CLEAR(driver); 314 + 315 + keymap_config.swap_lctl_lgui = false; 316 + 317 + // Press mod-tap-hold key. 318 + EXPECT_REPORT(driver, (KC_LCTL)); 319 + mod_tap_key.press(); 320 + run_one_scan_loop(); 321 + VERIFY_AND_CLEAR(driver); 322 + 323 + // Release mod-tap-hold key. 324 + EXPECT_EMPTY_REPORT(driver); 325 + EXPECT_REPORT(driver, (KC_P)); 326 + EXPECT_EMPTY_REPORT(driver); 327 + mod_tap_key.release(); 328 + run_one_scan_loop(); 329 + VERIFY_AND_CLEAR(driver); 330 + } 331 + 332 + TEST_F(SpeculativeHoldDefault, key_overrides) { 333 + TestDriver driver; 334 + InSequence s; 335 + auto mod_tap_key = KeymapKey(0, 1, 0, LSFT_T(KC_A)); 336 + auto esc_key = KeymapKey(0, 3, 0, KC_ESC); 337 + 338 + set_keymap({mod_tap_key, esc_key}); 339 + 340 + // Press mod-tap Shift key. 341 + EXPECT_REPORT(driver, (KC_LSFT)); 342 + mod_tap_key.press(); 343 + run_one_scan_loop(); 344 + VERIFY_AND_CLEAR(driver); 345 + 346 + // Press Esc key. 347 + EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); 348 + EXPECT_REPORT(driver, (KC_HOME)); 349 + esc_key.press(); 350 + idle_for(TAPPING_TERM + 1); 351 + VERIFY_AND_CLEAR(driver); 352 + 353 + // Release Esc key. 354 + EXPECT_EMPTY_REPORT(driver).Times(AnyNumber()); 355 + EXPECT_REPORT(driver, (KC_LSFT)); 356 + esc_key.release(); 357 + run_one_scan_loop(); 358 + VERIFY_AND_CLEAR(driver); 359 + 360 + // Release mod-tap Shift key. 361 + EXPECT_EMPTY_REPORT(driver); 362 + mod_tap_key.release(); 363 + run_one_scan_loop(); 364 + VERIFY_AND_CLEAR(driver); 365 + } 366 + 367 + TEST_F(SpeculativeHoldDefault, tap_regular_key_while_mod_tap_key_is_held) { 368 + TestDriver driver; 369 + InSequence s; 370 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); 371 + auto regular_key = KeymapKey(0, 2, 0, KC_A); 372 + 373 + set_keymap({mod_tap_key, regular_key}); 374 + 375 + // Press mod-tap-hold key. 376 + EXPECT_REPORT(driver, (KC_LSFT)); 377 + mod_tap_key.press(); 378 + run_one_scan_loop(); 379 + VERIFY_AND_CLEAR(driver); 380 + 381 + // Tap regular key. 382 + EXPECT_NO_REPORT(driver); 383 + regular_key.press(); 384 + run_one_scan_loop(); 385 + regular_key.release(); 386 + run_one_scan_loop(); 387 + VERIFY_AND_CLEAR(driver); 388 + 389 + // Release mod-tap-hold key. 390 + EXPECT_EMPTY_REPORT(driver); 391 + EXPECT_REPORT(driver, (KC_P)); 392 + EXPECT_REPORT(driver, (KC_P, KC_A)); 393 + EXPECT_REPORT(driver, (KC_P)); 394 + EXPECT_EMPTY_REPORT(driver); 395 + mod_tap_key.release(); 396 + run_one_scan_loop(); 397 + VERIFY_AND_CLEAR(driver); 398 + 399 + // Idle for tapping term of mod tap hold key. 400 + idle_for(TAPPING_TERM - 3); 401 + VERIFY_AND_CLEAR(driver); 402 + } 403 + 404 + TEST_F(SpeculativeHoldDefault, tap_a_mod_tap_key_while_another_mod_tap_key_is_held) { 405 + TestDriver driver; 406 + InSequence s; 407 + auto first_mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); 408 + auto second_mod_tap_key = KeymapKey(0, 2, 0, RSFT_T(KC_A)); 409 + 410 + set_keymap({first_mod_tap_key, second_mod_tap_key}); 411 + 412 + // Press first mod-tap-hold key. 413 + EXPECT_REPORT(driver, (KC_LSFT)); 414 + first_mod_tap_key.press(); 415 + run_one_scan_loop(); 416 + VERIFY_AND_CLEAR(driver); 417 + 418 + // Press second tap-hold key. 419 + EXPECT_REPORT(driver, (KC_LSFT, KC_RSFT)); 420 + second_mod_tap_key.press(); 421 + run_one_scan_loop(); 422 + VERIFY_AND_CLEAR(driver); 423 + 424 + // Release second tap-hold key. 425 + EXPECT_NO_REPORT(driver); 426 + second_mod_tap_key.release(); 427 + run_one_scan_loop(); 428 + VERIFY_AND_CLEAR(driver); 429 + 430 + // Release first mod-tap-hold key. 431 + EXPECT_EMPTY_REPORT(driver); 432 + EXPECT_REPORT(driver, (KC_P)); 433 + EXPECT_REPORT(driver, (KC_P, KC_A)); 434 + EXPECT_REPORT(driver, (KC_P)); 435 + EXPECT_EMPTY_REPORT(driver); 436 + first_mod_tap_key.release(); 437 + run_one_scan_loop(); 438 + VERIFY_AND_CLEAR(driver); 439 + } 440 + 441 + TEST_F(SpeculativeHoldDefault, tap_mod_tap_key_two_times) { 442 + TestDriver driver; 443 + InSequence s; 444 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); 445 + 446 + set_keymap({mod_tap_key}); 447 + 448 + // Press mod-tap-hold key. 449 + EXPECT_REPORT(driver, (KC_LSFT)); 450 + mod_tap_key.press(); 451 + run_one_scan_loop(); 452 + VERIFY_AND_CLEAR(driver); 453 + 454 + // Release mod-tap-hold key. 455 + EXPECT_EMPTY_REPORT(driver); 456 + EXPECT_REPORT(driver, (KC_P)); 457 + EXPECT_EMPTY_REPORT(driver); 458 + mod_tap_key.release(); 459 + run_one_scan_loop(); 460 + VERIFY_AND_CLEAR(driver); 461 + 462 + // Press mod-tap-hold key again. 463 + EXPECT_REPORT(driver, (KC_P)); 464 + mod_tap_key.press(); 465 + idle_for(TAPPING_TERM); 466 + VERIFY_AND_CLEAR(driver); 467 + 468 + // Release mod-tap-hold key. 469 + EXPECT_EMPTY_REPORT(driver); 470 + mod_tap_key.release(); 471 + run_one_scan_loop(); 472 + VERIFY_AND_CLEAR(driver); 473 + } 474 + 475 + TEST_F(SpeculativeHoldDefault, tap_mod_tap_key_twice_and_hold_on_second_time) { 476 + TestDriver driver; 477 + InSequence s; 478 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); 479 + 480 + set_keymap({mod_tap_key}); 481 + 482 + // Press mod-tap-hold key. 483 + EXPECT_REPORT(driver, (KC_LSFT)); 484 + mod_tap_key.press(); 485 + run_one_scan_loop(); 486 + VERIFY_AND_CLEAR(driver); 487 + 488 + // Release mod-tap-hold key. 489 + EXPECT_EMPTY_REPORT(driver); 490 + EXPECT_REPORT(driver, (KC_P)); 491 + EXPECT_EMPTY_REPORT(driver); 492 + mod_tap_key.release(); 493 + run_one_scan_loop(); 494 + VERIFY_AND_CLEAR(driver); 495 + 496 + // Press mod-tap-hold key again. 497 + EXPECT_REPORT(driver, (KC_P)); 498 + mod_tap_key.press(); 499 + idle_for(TAPPING_TERM); 500 + VERIFY_AND_CLEAR(driver); 501 + 502 + // Release mod-tap-hold key. 503 + EXPECT_EMPTY_REPORT(driver); 504 + mod_tap_key.release(); 505 + run_one_scan_loop(); 506 + VERIFY_AND_CLEAR(driver); 507 + } 508 + 509 + TEST_F(SpeculativeHoldDefault, tap_and_hold_mod_tap_key) { 510 + TestDriver driver; 511 + InSequence s; 512 + static int process_record_user_calls = 0; 513 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); 514 + 515 + set_keymap({mod_tap_key}); 516 + 517 + process_record_user_fun = [](uint16_t keycode, keyrecord_t *record) { 518 + ++process_record_user_calls; 519 + return true; 520 + }; 521 + 522 + // Press mod-tap-hold key. 523 + EXPECT_REPORT(driver, (KC_LSFT)); 524 + mod_tap_key.press(); 525 + idle_for(TAPPING_TERM - 1); 526 + EXPECT_EQ(get_speculative_mods(), MOD_BIT_LSHIFT); 527 + EXPECT_EQ(get_mods(), 0); 528 + // Speculative mod holds and releases are made directly, bypassing regular 529 + // event processing. No calls have been made yet to process_record_user(). 530 + EXPECT_EQ(process_record_user_calls, 0); 531 + idle_for(2); 532 + // Now that the key has settled, one call has been made for the hold event. 533 + EXPECT_EQ(process_record_user_calls, 1); 534 + EXPECT_EQ(get_speculative_mods(), 0); 535 + EXPECT_EQ(get_mods(), MOD_BIT_LSHIFT); 536 + VERIFY_AND_CLEAR(driver); 537 + 538 + // Release mod-tap-hold key. 539 + EXPECT_EMPTY_REPORT(driver); 540 + mod_tap_key.release(); 541 + run_one_scan_loop(); 542 + EXPECT_EQ(process_record_user_calls, 2); 543 + VERIFY_AND_CLEAR(driver); 544 + } 545 + 546 + // Test with layer tap and speculative mod tap keys on the same layer, 547 + // rolling from LT to MT key: 548 + // "LT down, MT down, (wait out tapping term), LT up, MT up." 549 + TEST_F(SpeculativeHoldDefault, lt_mt_same_layer_roll) { 550 + TestDriver driver; 551 + InSequence s; 552 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 553 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B)); 554 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 555 + 556 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 557 + 558 + // Press layer tap key. 559 + EXPECT_NO_REPORT(driver); 560 + layer_tap_key.press(); 561 + run_one_scan_loop(); 562 + VERIFY_AND_CLEAR(driver); 563 + 564 + // Press mod-tap key, after flow tap term but within tapping term. The 565 + // speculative mod activates. 566 + EXPECT_REPORT(driver, (KC_LSFT)); 567 + mod_tap_key.press(); 568 + run_one_scan_loop(); 569 + VERIFY_AND_CLEAR(driver); 570 + 571 + // Wait for the layer tap key to settle. 572 + EXPECT_EMPTY_REPORT(driver); 573 + EXPECT_REPORT(driver, (KC_C)); 574 + idle_for(TAPPING_TERM); 575 + VERIFY_AND_CLEAR(driver); 576 + 577 + // Release keys. 578 + EXPECT_EMPTY_REPORT(driver); 579 + layer_tap_key.release(); 580 + run_one_scan_loop(); 581 + mod_tap_key.release(); 582 + run_one_scan_loop(); 583 + VERIFY_AND_CLEAR(driver); 584 + // All mods are released. 585 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 586 + } 587 + 588 + // Test with layer tap and speculative mod tap keys on the same layer, trying a 589 + // nested press: 590 + // "LT down, MT down, (wait out tapping term), MT up, LT up." 591 + TEST_F(SpeculativeHoldDefault, lt_mt_same_layer_nested_press) { 592 + TestDriver driver; 593 + InSequence s; 594 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 595 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B)); 596 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 597 + 598 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 599 + 600 + EXPECT_NO_REPORT(driver); 601 + layer_tap_key.press(); 602 + run_one_scan_loop(); 603 + VERIFY_AND_CLEAR(driver); 604 + 605 + EXPECT_REPORT(driver, (KC_LSFT)); 606 + mod_tap_key.press(); 607 + run_one_scan_loop(); 608 + VERIFY_AND_CLEAR(driver); 609 + 610 + EXPECT_EMPTY_REPORT(driver); 611 + EXPECT_REPORT(driver, (KC_C)); 612 + run_one_scan_loop(); 613 + idle_for(TAPPING_TERM); 614 + VERIFY_AND_CLEAR(driver); 615 + 616 + // Release keys: MT first, LT second. 617 + EXPECT_EMPTY_REPORT(driver); 618 + mod_tap_key.release(); 619 + run_one_scan_loop(); 620 + layer_tap_key.release(); 621 + run_one_scan_loop(); 622 + VERIFY_AND_CLEAR(driver); 623 + // All mods are released. 624 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 625 + } 626 + 627 + // Test with layer tap and speculative mod tap keys on the same layer, trying a 628 + // nested press with the MT first: 629 + // "MT down, LT down, (wait out tapping term), LT up, MT up." 630 + TEST_F(SpeculativeHoldDefault, mt_lt_same_layer_nested_press) { 631 + TestDriver driver; 632 + InSequence s; 633 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 634 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B)); 635 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 636 + 637 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 638 + 639 + EXPECT_REPORT(driver, (KC_LSFT)); 640 + mod_tap_key.press(); 641 + run_one_scan_loop(); 642 + 643 + EXPECT_NO_REPORT(driver); 644 + layer_tap_key.press(); 645 + idle_for(TAPPING_TERM + 1); 646 + VERIFY_AND_CLEAR(driver); 647 + 648 + EXPECT_EMPTY_REPORT(driver); 649 + layer_tap_key.release(); 650 + run_one_scan_loop(); 651 + mod_tap_key.release(); 652 + run_one_scan_loop(); 653 + VERIFY_AND_CLEAR(driver); 654 + // All mods are released. 655 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 656 + } 657 + 658 + // Test with a speculative mod tap key reached by a layer tap key, rolling from 659 + // LT to MT key: 660 + // "LT down, MT down, (wait out tapping term), LT up, MT up." 661 + TEST_F(SpeculativeHoldDefault, lt_mt_different_layer_roll) { 662 + TestDriver driver; 663 + InSequence s; 664 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 665 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 666 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 667 + auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C)); 668 + 669 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 670 + 671 + // Press layer tap key. 672 + EXPECT_NO_REPORT(driver); 673 + layer_tap_key.press(); 674 + run_one_scan_loop(); 675 + // Press mod tap key. 676 + mod_tap_key.press(); 677 + idle_for(TAPPING_TERM); 678 + VERIFY_AND_CLEAR(driver); 679 + 680 + // Release keys. 681 + EXPECT_REPORT(driver, (KC_LSFT)); 682 + layer_tap_key.release(); 683 + idle_for(TAPPING_TERM); 684 + VERIFY_AND_CLEAR(driver); 685 + 686 + EXPECT_EMPTY_REPORT(driver); 687 + mod_tap_key.release(); 688 + run_one_scan_loop(); 689 + VERIFY_AND_CLEAR(driver); 690 + // All mods are released. 691 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 692 + } 693 + 694 + // Test with a speculative mod tap key reached by a layer tap key, slowly 695 + // rolling from LT to MT key: 696 + // "LT down, (wait), MT down, (wait), LT up, MT up." 697 + TEST_F(SpeculativeHoldDefault, lt_mt_different_layer_slow_roll) { 698 + TestDriver driver; 699 + InSequence s; 700 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 701 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 702 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 703 + auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C)); 704 + 705 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 706 + 707 + EXPECT_NO_REPORT(driver); 708 + layer_tap_key.press(); 709 + idle_for(TAPPING_TERM + 1); 710 + 711 + EXPECT_REPORT(driver, (KC_LSFT)); 712 + mod_tap_key.press(); 713 + run_one_scan_loop(); 714 + VERIFY_AND_CLEAR(driver); 715 + 716 + EXPECT_EMPTY_REPORT(driver); 717 + EXPECT_REPORT(driver, (KC_B)); 718 + EXPECT_EMPTY_REPORT(driver); 719 + layer_tap_key.release(); 720 + run_one_scan_loop(); 721 + mod_tap_key.release(); 722 + run_one_scan_loop(); 723 + VERIFY_AND_CLEAR(driver); 724 + // All mods are released. 725 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 726 + } 727 + 728 + // Test with a speculative mod tap key reached by a layer tap key, trying a 729 + // nested press: 730 + // "LT down, MT down, (wait out tapping term), MT up, LT up." 731 + TEST_F(SpeculativeHoldDefault, lt_mt_different_layer_nested_press) { 732 + TestDriver driver; 733 + InSequence s; 734 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 735 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 736 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 737 + auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C)); 738 + 739 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 740 + 741 + EXPECT_NO_REPORT(driver); 742 + layer_tap_key.press(); 743 + run_one_scan_loop(); 744 + mod_tap_key.press(); 745 + idle_for(TAPPING_TERM); 746 + VERIFY_AND_CLEAR(driver); 747 + 748 + // Release keys. 749 + EXPECT_REPORT(driver, (KC_LSFT)); 750 + EXPECT_EMPTY_REPORT(driver); 751 + mod_tap_key.release(); 752 + run_one_scan_loop(); 753 + layer_tap_key.release(); 754 + run_one_scan_loop(); 755 + VERIFY_AND_CLEAR(driver); 756 + // All mods are released. 757 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 758 + } 759 + 760 + // Test with a speculative mod tap key reached by a layer tap key, trying a 761 + // slow nested press: 762 + // "LT down, (wait), MT down, MT up, LT up." 763 + TEST_F(SpeculativeHoldDefault, lt_mt_different_layer_slow_nested_press) { 764 + TestDriver driver; 765 + InSequence s; 766 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 767 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 768 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 769 + auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C)); 770 + 771 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 772 + 773 + EXPECT_NO_REPORT(driver); 774 + layer_tap_key.press(); 775 + idle_for(TAPPING_TERM + 1); 776 + 777 + EXPECT_REPORT(driver, (KC_LSFT)); 778 + mod_tap_key.press(); 779 + run_one_scan_loop(); 780 + VERIFY_AND_CLEAR(driver); 781 + 782 + EXPECT_EMPTY_REPORT(driver); 783 + EXPECT_REPORT(driver, (KC_C)); 784 + EXPECT_EMPTY_REPORT(driver); 785 + mod_tap_key.release(); 786 + run_one_scan_loop(); 787 + layer_tap_key.release(); 788 + run_one_scan_loop(); 789 + VERIFY_AND_CLEAR(driver); 790 + // All mods are released. 791 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 792 + } 793 + 794 + } // namespace
+25
tests/tap_hold_configurations/speculative_hold/flow_tap/config.h
··· 1 + /* Copyright 2022 Vladislav Kucheriavykh 2 + * Copyright 2025 Google LLC 3 + * 4 + * This program is free software: you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation, either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 + */ 17 + 18 + #pragma once 19 + 20 + #include "test_common.h" 21 + 22 + #define SPECULATIVE_HOLD 23 + #define FLOW_TAP_TERM 150 24 + #define PERMISSIVE_HOLD 25 + #define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_F24
+15
tests/tap_hold_configurations/speculative_hold/flow_tap/test.mk
··· 1 + # Copyright 2022 Vladislav Kucheriavykh 2 + # 3 + # This program is free software: you can redistribute it and/or modify 4 + # it under the terms of the GNU General Public License as published by 5 + # the Free Software Foundation, either version 2 of the License, or 6 + # (at your option) any later version. 7 + # 8 + # This program is distributed in the hope that it will be useful, 9 + # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + # GNU General Public License for more details. 12 + # 13 + # You should have received a copy of the GNU General Public License 14 + # along with this program. If not, see <http://www.gnu.org/licenses/>. 15 +
+1114
tests/tap_hold_configurations/speculative_hold/flow_tap/test_tap_hold.cpp
··· 1 + // Copyright 2025 Google LLC 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // https://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + #include "keyboard_report_util.hpp" 16 + #include "keycode.h" 17 + #include "test_common.hpp" 18 + #include "action_tapping.h" 19 + #include "test_fixture.hpp" 20 + #include "test_keymap_key.hpp" 21 + 22 + using testing::_; 23 + using testing::AnyNumber; 24 + using testing::InSequence; 25 + 26 + extern "C" bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) { 27 + return true; 28 + } 29 + 30 + class SpeculativeHoldFlowTapTest : public TestFixture {}; 31 + 32 + TEST_F(SpeculativeHoldFlowTapTest, tap_mod_tap) { 33 + TestDriver driver; 34 + InSequence s; 35 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); 36 + 37 + set_keymap({mod_tap_key}); 38 + 39 + // Press mod-tap-hold key. Mod is held speculatively. 40 + EXPECT_REPORT(driver, (KC_LSFT)); 41 + mod_tap_key.press(); 42 + idle_for(10); 43 + VERIFY_AND_CLEAR(driver); 44 + EXPECT_EQ(get_speculative_mods(), MOD_BIT_LSHIFT); 45 + 46 + // Release mod-tap-hold key. 47 + EXPECT_EMPTY_REPORT(driver); // Speculative mod canceled. 48 + EXPECT_REPORT(driver, (KC_P)); 49 + EXPECT_EMPTY_REPORT(driver); 50 + mod_tap_key.release(); 51 + run_one_scan_loop(); 52 + VERIFY_AND_CLEAR(driver); 53 + // All mods are released. 54 + EXPECT_EQ(get_speculative_mods(), 0); 55 + EXPECT_EQ(get_mods(), 0); 56 + 57 + // Idle for tapping term of mod tap hold key. 58 + idle_for(TAPPING_TERM - 10); 59 + VERIFY_AND_CLEAR(driver); 60 + } 61 + 62 + TEST_F(SpeculativeHoldFlowTapTest, hold_two_mod_taps) { 63 + TestDriver driver; 64 + InSequence s; 65 + auto mod_tap_key1 = KeymapKey(0, 1, 0, LCTL_T(KC_A)); 66 + auto mod_tap_key2 = KeymapKey(0, 2, 0, RALT_T(KC_B)); 67 + 68 + set_keymap({mod_tap_key1, mod_tap_key2}); 69 + 70 + // Press first mod-tap key. 71 + EXPECT_REPORT(driver, (KC_LCTL)); 72 + mod_tap_key1.press(); 73 + idle_for(FLOW_TAP_TERM + 1); 74 + VERIFY_AND_CLEAR(driver); 75 + EXPECT_EQ(get_speculative_mods(), MOD_BIT_LCTRL); 76 + 77 + // Press second mod-tap key. 78 + EXPECT_REPORT(driver, (KC_LCTL, KC_RALT)); 79 + mod_tap_key2.press(); 80 + run_one_scan_loop(); 81 + VERIFY_AND_CLEAR(driver); 82 + EXPECT_EQ(get_speculative_mods(), MOD_BIT_LCTRL | MOD_BIT_RALT); 83 + 84 + EXPECT_NO_REPORT(driver); 85 + idle_for(TAPPING_TERM + 1); 86 + VERIFY_AND_CLEAR(driver); 87 + EXPECT_EQ(get_speculative_mods(), 0); 88 + EXPECT_EQ(get_mods(), MOD_BIT_LCTRL | MOD_BIT_RALT); 89 + 90 + // Release first mod-tap key. 91 + EXPECT_REPORT(driver, (KC_RALT)); 92 + mod_tap_key1.release(); 93 + run_one_scan_loop(); 94 + VERIFY_AND_CLEAR(driver); 95 + 96 + // Release second mod-tap key. 97 + EXPECT_EMPTY_REPORT(driver); 98 + mod_tap_key2.release(); 99 + run_one_scan_loop(); 100 + VERIFY_AND_CLEAR(driver); 101 + } 102 + 103 + TEST_F(SpeculativeHoldFlowTapTest, two_mod_taps_same_mods) { 104 + TestDriver driver; 105 + InSequence s; 106 + auto mod_tap_key1 = KeymapKey(0, 1, 0, GUI_T(KC_A)); 107 + auto mod_tap_key2 = KeymapKey(0, 2, 0, GUI_T(KC_B)); 108 + 109 + set_keymap({mod_tap_key1, mod_tap_key2}); 110 + 111 + // Press first mod-tap key. 112 + EXPECT_REPORT(driver, (KC_LGUI)); 113 + mod_tap_key1.press(); 114 + idle_for(FLOW_TAP_TERM + 1); 115 + VERIFY_AND_CLEAR(driver); 116 + 117 + // Tap second mod-tap key. 118 + EXPECT_NO_REPORT(driver); 119 + mod_tap_key2.press(); 120 + run_one_scan_loop(); 121 + VERIFY_AND_CLEAR(driver); 122 + 123 + EXPECT_REPORT(driver, (KC_LGUI, KC_B)); 124 + EXPECT_REPORT(driver, (KC_LGUI)); 125 + mod_tap_key2.release(); 126 + run_one_scan_loop(); 127 + VERIFY_AND_CLEAR(driver); 128 + 129 + // Release first mod-tap key. 130 + EXPECT_EMPTY_REPORT(driver); 131 + mod_tap_key1.release(); 132 + run_one_scan_loop(); 133 + VERIFY_AND_CLEAR(driver); 134 + // All mods are released. 135 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 136 + } 137 + 138 + // Test an input of quick distinct taps. All should be settled as tapped. 139 + TEST_F(SpeculativeHoldFlowTapTest, distinct_taps) { 140 + TestDriver driver; 141 + InSequence s; 142 + auto regular_key = KeymapKey(0, 0, 0, KC_A); 143 + auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B)); 144 + auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C)); 145 + auto mod_tap_key3 = KeymapKey(0, 3, 0, ALT_T(KC_D)); 146 + 147 + set_keymap({regular_key, mod_tap_key1, mod_tap_key2, mod_tap_key3}); 148 + 149 + // Tap regular key. 150 + EXPECT_REPORT(driver, (KC_A)); 151 + EXPECT_EMPTY_REPORT(driver); 152 + tap_key(regular_key, FLOW_TAP_TERM + 1); 153 + VERIFY_AND_CLEAR(driver); 154 + 155 + // Tap mod-tap 1. 156 + EXPECT_REPORT(driver, (KC_B)); 157 + mod_tap_key1.press(); 158 + run_one_scan_loop(); 159 + VERIFY_AND_CLEAR(driver); 160 + 161 + EXPECT_EMPTY_REPORT(driver); 162 + idle_for(FLOW_TAP_TERM + 1); 163 + mod_tap_key1.release(); 164 + run_one_scan_loop(); 165 + VERIFY_AND_CLEAR(driver); 166 + 167 + // Tap mod-tap 2. 168 + EXPECT_REPORT(driver, (KC_C)); 169 + mod_tap_key2.press(); 170 + run_one_scan_loop(); 171 + VERIFY_AND_CLEAR(driver); 172 + 173 + EXPECT_EMPTY_REPORT(driver); 174 + idle_for(FLOW_TAP_TERM + 1); 175 + mod_tap_key2.release(); 176 + run_one_scan_loop(); 177 + VERIFY_AND_CLEAR(driver); 178 + 179 + // Tap mod-tap 3. 180 + EXPECT_REPORT(driver, (KC_D)); 181 + mod_tap_key3.press(); 182 + run_one_scan_loop(); 183 + VERIFY_AND_CLEAR(driver); 184 + 185 + EXPECT_EMPTY_REPORT(driver); 186 + idle_for(FLOW_TAP_TERM + 1); 187 + mod_tap_key3.release(); 188 + idle_for(FLOW_TAP_TERM + 1); // Pause between taps. 189 + VERIFY_AND_CLEAR(driver); 190 + 191 + // Tap mod-tap 1. 192 + EXPECT_REPORT(driver, (KC_LSFT)); 193 + mod_tap_key1.press(); 194 + run_one_scan_loop(); 195 + VERIFY_AND_CLEAR(driver); 196 + 197 + EXPECT_EMPTY_REPORT(driver); 198 + EXPECT_REPORT(driver, (KC_B)); 199 + EXPECT_EMPTY_REPORT(driver); 200 + idle_for(FLOW_TAP_TERM + 1); 201 + mod_tap_key1.release(); 202 + run_one_scan_loop(); 203 + VERIFY_AND_CLEAR(driver); 204 + 205 + // Tap mod-tap 2. 206 + EXPECT_REPORT(driver, (KC_C)); 207 + mod_tap_key2.press(); 208 + run_one_scan_loop(); 209 + VERIFY_AND_CLEAR(driver); 210 + 211 + EXPECT_EMPTY_REPORT(driver); 212 + idle_for(TAPPING_TERM + 1); 213 + mod_tap_key2.release(); 214 + idle_for(FLOW_TAP_TERM + 1); 215 + VERIFY_AND_CLEAR(driver); 216 + // All mods are released. 217 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 218 + } 219 + 220 + // By default, Flow Tap is disabled when mods other than Shift and AltGr are on. 221 + TEST_F(SpeculativeHoldFlowTapTest, hotkey_taps) { 222 + TestDriver driver; 223 + InSequence s; 224 + auto ctrl_key = KeymapKey(0, 0, 0, KC_LCTL); 225 + auto shft_key = KeymapKey(0, 1, 0, KC_LSFT); 226 + auto alt_key = KeymapKey(0, 2, 0, KC_LALT); 227 + auto gui_key = KeymapKey(0, 3, 0, KC_LGUI); 228 + auto regular_key = KeymapKey(0, 4, 0, KC_A); 229 + auto mod_tap_key = KeymapKey(0, 5, 0, RCTL_T(KC_B)); 230 + 231 + set_keymap({ctrl_key, shft_key, alt_key, gui_key, regular_key, mod_tap_key}); 232 + 233 + for (KeymapKey *mod_key : {&ctrl_key, &alt_key, &gui_key}) { 234 + // Hold mod key. 235 + EXPECT_REPORT(driver, (mod_key->code)); 236 + mod_key->press(); 237 + run_one_scan_loop(); 238 + 239 + // Tap regular key. 240 + EXPECT_REPORT(driver, (mod_key->code, KC_A)); 241 + regular_key.press(); 242 + run_one_scan_loop(); 243 + VERIFY_AND_CLEAR(driver); 244 + 245 + EXPECT_REPORT(driver, (mod_key->code)); 246 + regular_key.release(); 247 + run_one_scan_loop(); 248 + VERIFY_AND_CLEAR(driver); 249 + 250 + // Press mod-tap, where Flow Tap is disabled due to the held mod. 251 + EXPECT_REPORT(driver, (mod_key->code, KC_RCTL)); 252 + mod_tap_key.press(); 253 + idle_for(TAPPING_TERM + 1); 254 + VERIFY_AND_CLEAR(driver); 255 + 256 + // Release mod-tap. 257 + EXPECT_REPORT(driver, (mod_key->code)); 258 + mod_tap_key.release(); 259 + run_one_scan_loop(); 260 + 261 + // Release mod key. 262 + EXPECT_EMPTY_REPORT(driver); 263 + mod_key->release(); 264 + run_one_scan_loop(); 265 + VERIFY_AND_CLEAR(driver); 266 + } 267 + 268 + // Hold Shift key. 269 + EXPECT_REPORT(driver, (KC_LSFT)); 270 + shft_key.press(); 271 + run_one_scan_loop(); 272 + 273 + // Tap regular key. 274 + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); 275 + regular_key.press(); 276 + run_one_scan_loop(); 277 + VERIFY_AND_CLEAR(driver); 278 + 279 + EXPECT_REPORT(driver, (KC_LSFT)); 280 + regular_key.release(); 281 + run_one_scan_loop(); 282 + VERIFY_AND_CLEAR(driver); 283 + 284 + // Press mod-tap, where Flow Tap applies to settle as tapped. 285 + EXPECT_REPORT(driver, (KC_LSFT, KC_B)); 286 + mod_tap_key.press(); 287 + idle_for(TAPPING_TERM + 1); 288 + VERIFY_AND_CLEAR(driver); 289 + 290 + // Release mod-tap. 291 + EXPECT_REPORT(driver, (KC_LSFT)); 292 + mod_tap_key.release(); 293 + run_one_scan_loop(); 294 + 295 + // Release Shift key. 296 + EXPECT_EMPTY_REPORT(driver); 297 + shft_key.release(); 298 + run_one_scan_loop(); 299 + VERIFY_AND_CLEAR(driver); 300 + // All mods are released. 301 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 302 + } 303 + 304 + // Test input with two mod-taps in a rolled press quickly after a regular key. 305 + TEST_F(SpeculativeHoldFlowTapTest, rolled_press) { 306 + TestDriver driver; 307 + InSequence s; 308 + auto regular_key = KeymapKey(0, 0, 0, KC_A); 309 + auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B)); 310 + auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C)); 311 + 312 + set_keymap({regular_key, mod_tap_key1, mod_tap_key2}); 313 + 314 + // Tap regular key. 315 + EXPECT_REPORT(driver, (KC_A)); 316 + EXPECT_EMPTY_REPORT(driver); 317 + tap_key(regular_key); 318 + VERIFY_AND_CLEAR(driver); 319 + 320 + // Press mod-tap key 1 quickly after regular key. The mod-tap should settle 321 + // immediately as tapped, sending `KC_B`. 322 + EXPECT_REPORT(driver, (KC_B)); 323 + mod_tap_key1.press(); 324 + run_one_scan_loop(); 325 + VERIFY_AND_CLEAR(driver); 326 + 327 + // Press mod-tap key 2 quickly. 328 + EXPECT_REPORT(driver, (KC_B, KC_C)); 329 + mod_tap_key2.press(); 330 + run_one_scan_loop(); 331 + VERIFY_AND_CLEAR(driver); 332 + 333 + // Hold for longer than the tapping term. 334 + EXPECT_NO_REPORT(driver); 335 + idle_for(TAPPING_TERM + 1); 336 + VERIFY_AND_CLEAR(driver); 337 + 338 + // Release mod-tap keys. 339 + EXPECT_REPORT(driver, (KC_C)); 340 + EXPECT_EMPTY_REPORT(driver); 341 + mod_tap_key1.release(); 342 + run_one_scan_loop(); 343 + mod_tap_key2.release(); 344 + run_one_scan_loop(); 345 + VERIFY_AND_CLEAR(driver); 346 + // All mods are released. 347 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 348 + } 349 + 350 + TEST_F(SpeculativeHoldFlowTapTest, long_flow_tap_settled_as_held) { 351 + TestDriver driver; 352 + InSequence s; 353 + auto regular_key = KeymapKey(0, 0, 0, KC_A); 354 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B)); 355 + 356 + set_keymap({regular_key, mod_tap_key}); 357 + 358 + // Tap regular key. 359 + EXPECT_REPORT(driver, (KC_A)); 360 + EXPECT_EMPTY_REPORT(driver); 361 + tap_key(regular_key); 362 + VERIFY_AND_CLEAR(driver); 363 + 364 + EXPECT_NO_REPORT(driver); 365 + idle_for(FLOW_TAP_TERM + 1); 366 + VERIFY_AND_CLEAR(driver); 367 + 368 + // Press mod-tap key. 369 + EXPECT_REPORT(driver, (KC_LSFT)); 370 + mod_tap_key.press(); 371 + run_one_scan_loop(); 372 + VERIFY_AND_CLEAR(driver); 373 + 374 + // Hold for the tapping term. 375 + EXPECT_NO_REPORT(driver); 376 + idle_for(TAPPING_TERM); 377 + VERIFY_AND_CLEAR(driver); 378 + 379 + // Release mod-tap key. 380 + EXPECT_EMPTY_REPORT(driver); 381 + mod_tap_key.release(); 382 + run_one_scan_loop(); 383 + VERIFY_AND_CLEAR(driver); 384 + // All mods are released. 385 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 386 + } 387 + 388 + TEST_F(SpeculativeHoldFlowTapTest, holding_multiple_mod_taps) { 389 + TestDriver driver; 390 + InSequence s; 391 + auto regular_key = KeymapKey(0, 0, 0, KC_A); 392 + auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B)); 393 + auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C)); 394 + 395 + set_keymap({regular_key, mod_tap_key1, mod_tap_key2}); 396 + 397 + // Tap regular key. 398 + EXPECT_REPORT(driver, (KC_A)); 399 + EXPECT_EMPTY_REPORT(driver); 400 + tap_key(regular_key); 401 + VERIFY_AND_CLEAR(driver); 402 + 403 + EXPECT_NO_REPORT(driver); 404 + idle_for(FLOW_TAP_TERM + 1); 405 + VERIFY_AND_CLEAR(driver); 406 + 407 + // Press mod-tap keys. 408 + EXPECT_REPORT(driver, (KC_LSFT)); 409 + mod_tap_key1.press(); 410 + run_one_scan_loop(); 411 + VERIFY_AND_CLEAR(driver); 412 + 413 + EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); 414 + mod_tap_key2.press(); 415 + idle_for(TAPPING_TERM - 5); // Hold almost until tapping term. 416 + VERIFY_AND_CLEAR(driver); 417 + 418 + // Press regular key. 419 + EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL, KC_A)); 420 + regular_key.press(); 421 + idle_for(10); 422 + VERIFY_AND_CLEAR(driver); 423 + 424 + // Release keys. 425 + EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); 426 + EXPECT_REPORT(driver, (KC_LCTL)); 427 + EXPECT_EMPTY_REPORT(driver); 428 + regular_key.release(); 429 + run_one_scan_loop(); 430 + mod_tap_key1.release(); 431 + run_one_scan_loop(); 432 + mod_tap_key2.release(); 433 + run_one_scan_loop(); 434 + VERIFY_AND_CLEAR(driver); 435 + // All mods are released. 436 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 437 + } 438 + 439 + TEST_F(SpeculativeHoldFlowTapTest, holding_mod_tap_with_regular_mod) { 440 + TestDriver driver; 441 + InSequence s; 442 + auto regular_key = KeymapKey(0, 0, 0, KC_A); 443 + auto mod_key = KeymapKey(0, 1, 0, KC_LSFT); 444 + auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_C)); 445 + 446 + set_keymap({regular_key, mod_key, mod_tap_key}); 447 + 448 + // Tap regular key. 449 + EXPECT_REPORT(driver, (KC_A)); 450 + EXPECT_EMPTY_REPORT(driver); 451 + tap_key(regular_key); 452 + VERIFY_AND_CLEAR(driver); 453 + 454 + EXPECT_NO_REPORT(driver); 455 + idle_for(FLOW_TAP_TERM + 1); 456 + VERIFY_AND_CLEAR(driver); 457 + 458 + // Press mod and mod-tap keys. 459 + EXPECT_REPORT(driver, (KC_LSFT)); 460 + mod_key.press(); 461 + run_one_scan_loop(); 462 + VERIFY_AND_CLEAR(driver); 463 + 464 + EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); 465 + mod_tap_key.press(); 466 + idle_for(TAPPING_TERM - 5); // Hold almost until tapping term. 467 + VERIFY_AND_CLEAR(driver); 468 + 469 + // Press regular key. 470 + EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL, KC_A)); 471 + regular_key.press(); 472 + idle_for(10); 473 + VERIFY_AND_CLEAR(driver); 474 + 475 + // Release keys. 476 + EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); 477 + EXPECT_REPORT(driver, (KC_LCTL)); 478 + EXPECT_EMPTY_REPORT(driver); 479 + regular_key.release(); 480 + run_one_scan_loop(); 481 + mod_key.release(); 482 + run_one_scan_loop(); 483 + mod_tap_key.release(); 484 + run_one_scan_loop(); 485 + VERIFY_AND_CLEAR(driver); 486 + // All mods are released. 487 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 488 + } 489 + 490 + TEST_F(SpeculativeHoldFlowTapTest, layer_tap_ignored_with_disabled_key) { 491 + TestDriver driver; 492 + InSequence s; 493 + auto no_key = KeymapKey(0, 0, 0, KC_NO); 494 + auto regular_key = KeymapKey(1, 0, 0, KC_ESC); 495 + auto layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A)); 496 + auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_B)); 497 + 498 + set_keymap({no_key, regular_key, layer_tap_key, mod_tap_key}); 499 + 500 + EXPECT_REPORT(driver, (KC_ESC)); 501 + EXPECT_EMPTY_REPORT(driver); 502 + layer_tap_key.press(); 503 + idle_for(TAPPING_TERM + 1); 504 + tap_key(regular_key); 505 + layer_tap_key.release(); 506 + run_one_scan_loop(); 507 + VERIFY_AND_CLEAR(driver); 508 + 509 + EXPECT_REPORT(driver, (KC_LCTL)); 510 + mod_tap_key.press(); 511 + idle_for(TAPPING_TERM + 1); 512 + VERIFY_AND_CLEAR(driver); 513 + 514 + EXPECT_EMPTY_REPORT(driver); 515 + mod_tap_key.release(); 516 + run_one_scan_loop(); 517 + VERIFY_AND_CLEAR(driver); 518 + // All mods are released. 519 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 520 + } 521 + 522 + TEST_F(SpeculativeHoldFlowTapTest, layer_tap_ignored_with_disabled_key_complex) { 523 + TestDriver driver; 524 + InSequence s; 525 + auto regular_key1 = KeymapKey(0, 0, 0, KC_Q); 526 + auto layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_SPC)); 527 + auto mod_tap_key1 = KeymapKey(0, 2, 0, CTL_T(KC_T)); 528 + // Place RALT_T(KC_I), where Flow Tap is enabled, in the same position on 529 + // layer 0 as KC_RGHT, where Flow Tap is disabled. This tests that Flow Tap 530 + // tracks the keycode from the correct layer. 531 + auto mod_tap_key2 = KeymapKey(0, 3, 0, RALT_T(KC_I)); 532 + auto regular_key2 = KeymapKey(1, 3, 0, KC_RGHT); 533 + 534 + set_keymap({regular_key1, layer_tap_key, mod_tap_key1, mod_tap_key2, regular_key2}); 535 + 536 + // Tap regular key 1. 537 + EXPECT_REPORT(driver, (KC_Q)); 538 + EXPECT_EMPTY_REPORT(driver); 539 + tap_key(regular_key1); 540 + idle_for(FLOW_TAP_TERM + 1); 541 + VERIFY_AND_CLEAR(driver); 542 + 543 + // Hold layer-tap key. 544 + EXPECT_NO_REPORT(driver); 545 + layer_tap_key.press(); 546 + run_one_scan_loop(); 547 + VERIFY_AND_CLEAR(driver); 548 + 549 + // Tap regular key 2. 550 + EXPECT_REPORT(driver, (KC_RALT)); 551 + EXPECT_EMPTY_REPORT(driver); 552 + EXPECT_REPORT(driver, (KC_RGHT)); 553 + EXPECT_EMPTY_REPORT(driver); 554 + tap_key(regular_key2); 555 + VERIFY_AND_CLEAR(driver); 556 + 557 + // Release layer-tap key. 558 + EXPECT_NO_REPORT(driver); 559 + layer_tap_key.release(); 560 + run_one_scan_loop(); 561 + VERIFY_AND_CLEAR(driver); 562 + 563 + // Quickly hold mod-tap key 1. 564 + EXPECT_REPORT(driver, (KC_LCTL)); 565 + mod_tap_key1.press(); 566 + run_one_scan_loop(); 567 + VERIFY_AND_CLEAR(driver); 568 + 569 + EXPECT_REPORT(driver, (KC_LCTL, KC_Q)); 570 + EXPECT_REPORT(driver, (KC_LCTL)); 571 + tap_key(regular_key1); 572 + VERIFY_AND_CLEAR(driver); 573 + 574 + EXPECT_EMPTY_REPORT(driver); 575 + mod_tap_key1.release(); 576 + run_one_scan_loop(); 577 + VERIFY_AND_CLEAR(driver); 578 + // All mods are released. 579 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 580 + } 581 + 582 + TEST_F(SpeculativeHoldFlowTapTest, layer_tap_ignored_with_enabled_key) { 583 + TestDriver driver; 584 + InSequence s; 585 + auto no_key = KeymapKey(0, 0, 0, KC_NO); 586 + auto regular_key = KeymapKey(1, 0, 0, KC_C); 587 + auto layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A)); 588 + auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_B)); 589 + 590 + set_keymap({no_key, regular_key, layer_tap_key, mod_tap_key}); 591 + 592 + EXPECT_REPORT(driver, (KC_C)); 593 + EXPECT_EMPTY_REPORT(driver); 594 + layer_tap_key.press(); 595 + idle_for(TAPPING_TERM + 1); 596 + tap_key(regular_key); 597 + layer_tap_key.release(); 598 + run_one_scan_loop(); 599 + VERIFY_AND_CLEAR(driver); 600 + 601 + EXPECT_REPORT(driver, (KC_B)); 602 + mod_tap_key.press(); 603 + run_one_scan_loop(); 604 + VERIFY_AND_CLEAR(driver); 605 + 606 + EXPECT_EMPTY_REPORT(driver); 607 + idle_for(TAPPING_TERM + 1); 608 + mod_tap_key.release(); 609 + run_one_scan_loop(); 610 + VERIFY_AND_CLEAR(driver); 611 + // All mods are released. 612 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 613 + } 614 + 615 + TEST_F(SpeculativeHoldFlowTapTest, quick_tap) { 616 + TestDriver driver; 617 + InSequence s; 618 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_A)); 619 + 620 + set_keymap({mod_tap_key}); 621 + 622 + EXPECT_REPORT(driver, (KC_LSFT)); 623 + EXPECT_EMPTY_REPORT(driver); 624 + EXPECT_REPORT(driver, (KC_A)); 625 + EXPECT_EMPTY_REPORT(driver); 626 + tap_key(mod_tap_key); 627 + VERIFY_AND_CLEAR(driver); 628 + 629 + EXPECT_REPORT(driver, (KC_A)); 630 + mod_tap_key.press(); 631 + run_one_scan_loop(); 632 + VERIFY_AND_CLEAR(driver); 633 + 634 + EXPECT_NO_REPORT(driver); 635 + idle_for(TAPPING_TERM + 1); 636 + VERIFY_AND_CLEAR(driver); 637 + 638 + // Release mod-tap key. 639 + EXPECT_EMPTY_REPORT(driver); 640 + mod_tap_key.release(); 641 + run_one_scan_loop(); 642 + VERIFY_AND_CLEAR(driver); 643 + // All mods are released. 644 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 645 + } 646 + 647 + TEST_F(SpeculativeHoldFlowTapTest, rolling_mt_mt) { 648 + TestDriver driver; 649 + InSequence s; 650 + auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A)); 651 + auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B)); 652 + 653 + set_keymap({mod_tap_key1, mod_tap_key2}); 654 + 655 + EXPECT_NO_REPORT(driver); 656 + EXPECT_REPORT(driver, (KC_LSFT)); 657 + EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL)); 658 + idle_for(FLOW_TAP_TERM + 1); 659 + mod_tap_key1.press(); 660 + run_one_scan_loop(); 661 + mod_tap_key2.press(); 662 + run_one_scan_loop(); 663 + VERIFY_AND_CLEAR(driver); 664 + 665 + EXPECT_EMPTY_REPORT(driver); 666 + EXPECT_REPORT(driver, (KC_A)); 667 + EXPECT_REPORT(driver, (KC_A, KC_B)); 668 + EXPECT_REPORT(driver, (KC_B)); 669 + mod_tap_key1.release(); 670 + run_one_scan_loop(); 671 + VERIFY_AND_CLEAR(driver); 672 + 673 + // Hold for longer than the tapping term. 674 + EXPECT_NO_REPORT(driver); 675 + idle_for(TAPPING_TERM + 1); 676 + VERIFY_AND_CLEAR(driver); 677 + 678 + // Release mod-tap keys. 679 + EXPECT_EMPTY_REPORT(driver); 680 + mod_tap_key2.release(); 681 + run_one_scan_loop(); 682 + VERIFY_AND_CLEAR(driver); 683 + // All mods are released. 684 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 685 + } 686 + 687 + TEST_F(SpeculativeHoldFlowTapTest, rolling_lt_mt_regular) { 688 + TestDriver driver; 689 + InSequence s; 690 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 691 + auto mod_tap_key = KeymapKey(0, 1, 0, CTL_T(KC_B)); 692 + auto regular_key = KeymapKey(0, 2, 0, KC_C); 693 + 694 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 695 + 696 + EXPECT_REPORT(driver, (KC_LCTL)); 697 + idle_for(FLOW_TAP_TERM + 1); 698 + layer_tap_key.press(); 699 + run_one_scan_loop(); 700 + mod_tap_key.press(); 701 + run_one_scan_loop(); 702 + regular_key.press(); 703 + run_one_scan_loop(); 704 + VERIFY_AND_CLEAR(driver); 705 + 706 + EXPECT_EMPTY_REPORT(driver); 707 + EXPECT_REPORT(driver, (KC_A)); 708 + EXPECT_REPORT(driver, (KC_A, KC_B)); 709 + EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); 710 + EXPECT_REPORT(driver, (KC_B, KC_C)); 711 + layer_tap_key.release(); 712 + run_one_scan_loop(); 713 + VERIFY_AND_CLEAR(driver); 714 + 715 + // Hold for longer than the tapping term. 716 + EXPECT_NO_REPORT(driver); 717 + idle_for(TAPPING_TERM + 1); 718 + VERIFY_AND_CLEAR(driver); 719 + 720 + // Release mod-tap keys. 721 + EXPECT_REPORT(driver, (KC_C)); 722 + EXPECT_EMPTY_REPORT(driver); 723 + mod_tap_key.release(); 724 + run_one_scan_loop(); 725 + regular_key.release(); 726 + run_one_scan_loop(); 727 + VERIFY_AND_CLEAR(driver); 728 + // All mods are released. 729 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 730 + } 731 + 732 + TEST_F(SpeculativeHoldFlowTapTest, rolling_lt_regular_mt) { 733 + TestDriver driver; 734 + InSequence s; 735 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 736 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 737 + auto mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_C)); 738 + 739 + set_keymap({layer_tap_key, regular_key, mod_tap_key}); 740 + 741 + EXPECT_NO_REPORT(driver); 742 + idle_for(FLOW_TAP_TERM + 1); 743 + layer_tap_key.press(); 744 + run_one_scan_loop(); 745 + regular_key.press(); 746 + run_one_scan_loop(); 747 + mod_tap_key.press(); 748 + run_one_scan_loop(); 749 + VERIFY_AND_CLEAR(driver); 750 + 751 + EXPECT_REPORT(driver, (KC_A)); 752 + EXPECT_REPORT(driver, (KC_A, KC_B)); 753 + EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); 754 + EXPECT_REPORT(driver, (KC_B, KC_C)); 755 + layer_tap_key.release(); 756 + run_one_scan_loop(); 757 + VERIFY_AND_CLEAR(driver); 758 + 759 + // Hold for longer than the tapping term. 760 + EXPECT_NO_REPORT(driver); 761 + idle_for(TAPPING_TERM + 1); 762 + VERIFY_AND_CLEAR(driver); 763 + 764 + // Release mod-tap keys. 765 + EXPECT_REPORT(driver, (KC_C)); 766 + EXPECT_EMPTY_REPORT(driver); 767 + regular_key.release(); 768 + run_one_scan_loop(); 769 + mod_tap_key.release(); 770 + run_one_scan_loop(); 771 + VERIFY_AND_CLEAR(driver); 772 + // All mods are released. 773 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 774 + } 775 + 776 + TEST_F(SpeculativeHoldFlowTapTest, rolling_mt_mt_mt) { 777 + TestDriver driver; 778 + InSequence s; 779 + auto mod_tap_key1 = KeymapKey(0, 0, 0, CTL_T(KC_A)); 780 + auto mod_tap_key2 = KeymapKey(0, 1, 0, GUI_T(KC_B)); 781 + auto mod_tap_key3 = KeymapKey(0, 2, 0, ALT_T(KC_C)); 782 + 783 + set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3}); 784 + 785 + // Press mod-tap keys. 786 + EXPECT_REPORT(driver, (KC_LCTL)); 787 + EXPECT_REPORT(driver, (KC_LCTL, KC_LGUI)); 788 + EXPECT_REPORT(driver, (KC_LCTL, KC_LGUI, KC_LALT)); 789 + idle_for(FLOW_TAP_TERM + 1); 790 + mod_tap_key1.press(); 791 + run_one_scan_loop(); 792 + mod_tap_key2.press(); 793 + run_one_scan_loop(); 794 + mod_tap_key3.press(); 795 + run_one_scan_loop(); 796 + VERIFY_AND_CLEAR(driver); 797 + 798 + // Release first mod-tap key. 799 + EXPECT_EMPTY_REPORT(driver); 800 + EXPECT_REPORT(driver, (KC_A)); 801 + EXPECT_REPORT(driver, (KC_A, KC_B)); 802 + EXPECT_REPORT(driver, (KC_A, KC_B, KC_C)); 803 + EXPECT_REPORT(driver, (KC_B, KC_C)); 804 + mod_tap_key1.release(); 805 + run_one_scan_loop(); 806 + VERIFY_AND_CLEAR(driver); 807 + 808 + // Hold for longer than the tapping term. 809 + EXPECT_NO_REPORT(driver); 810 + idle_for(TAPPING_TERM + 1); 811 + VERIFY_AND_CLEAR(driver); 812 + 813 + // Release other mod-tap keys. 814 + EXPECT_REPORT(driver, (KC_C)); 815 + EXPECT_EMPTY_REPORT(driver); 816 + mod_tap_key2.release(); 817 + run_one_scan_loop(); 818 + mod_tap_key3.release(); 819 + run_one_scan_loop(); 820 + VERIFY_AND_CLEAR(driver); 821 + // All mods are released. 822 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 823 + } 824 + 825 + TEST_F(SpeculativeHoldFlowTapTest, roll_release_132) { 826 + TestDriver driver; 827 + InSequence s; 828 + auto mod_tap_key1 = KeymapKey(0, 0, 0, CTL_T(KC_A)); 829 + auto mod_tap_key2 = KeymapKey(0, 1, 0, GUI_T(KC_B)); 830 + auto mod_tap_key3 = KeymapKey(0, 2, 0, ALT_T(KC_C)); 831 + 832 + set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3}); 833 + 834 + // Press mod-tap keys. 835 + EXPECT_REPORT(driver, (KC_LCTL)); 836 + EXPECT_REPORT(driver, (KC_LCTL, KC_LGUI)); 837 + EXPECT_REPORT(driver, (KC_LCTL, KC_LGUI, KC_LALT)); 838 + idle_for(FLOW_TAP_TERM + 1); 839 + mod_tap_key1.press(); 840 + run_one_scan_loop(); 841 + mod_tap_key2.press(); 842 + idle_for(FLOW_TAP_TERM + 1); 843 + mod_tap_key3.press(); 844 + run_one_scan_loop(); 845 + VERIFY_AND_CLEAR(driver); 846 + 847 + // Release first mod-tap key. 848 + EXPECT_EMPTY_REPORT(driver); 849 + EXPECT_REPORT(driver, (KC_A)); 850 + EXPECT_REPORT(driver, (KC_A, KC_B)); 851 + EXPECT_REPORT(driver, (KC_B)); 852 + mod_tap_key1.release(); 853 + run_one_scan_loop(); 854 + VERIFY_AND_CLEAR(driver); 855 + 856 + // Release other mod-tap keys. 857 + EXPECT_REPORT(driver, (KC_B, KC_C)); 858 + EXPECT_REPORT(driver, (KC_B)); 859 + EXPECT_EMPTY_REPORT(driver); 860 + mod_tap_key3.release(); 861 + run_one_scan_loop(); 862 + mod_tap_key2.release(); 863 + run_one_scan_loop(); 864 + VERIFY_AND_CLEAR(driver); 865 + // All mods are released. 866 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 867 + } 868 + 869 + // Test with layer tap and speculative mod tap keys on the same layer, rolling 870 + // from LT to MT key: 871 + // "LT down, MT down, (wait out tapping term), LT up, MT up." 872 + TEST_F(SpeculativeHoldFlowTapTest, lt_mt_same_layer_roll) { 873 + TestDriver driver; 874 + InSequence s; 875 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 876 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B)); 877 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 878 + 879 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 880 + 881 + // Press layer tap key. 882 + EXPECT_NO_REPORT(driver); 883 + layer_tap_key.press(); 884 + idle_for(FLOW_TAP_TERM + 1); 885 + VERIFY_AND_CLEAR(driver); 886 + 887 + // Press mod-tap key, after flow tap term but within tapping term. The 888 + // speculative mod activates. 889 + EXPECT_REPORT(driver, (KC_LSFT)); 890 + mod_tap_key.press(); 891 + run_one_scan_loop(); 892 + VERIFY_AND_CLEAR(driver); 893 + 894 + // Wait for the layer tap key to settle. 895 + EXPECT_EMPTY_REPORT(driver); 896 + EXPECT_REPORT(driver, (KC_C)); 897 + idle_for(TAPPING_TERM - FLOW_TAP_TERM); 898 + VERIFY_AND_CLEAR(driver); 899 + 900 + // Release keys. 901 + EXPECT_EMPTY_REPORT(driver); 902 + layer_tap_key.release(); 903 + run_one_scan_loop(); 904 + mod_tap_key.release(); 905 + idle_for(FLOW_TAP_TERM + 1); 906 + VERIFY_AND_CLEAR(driver); 907 + // All mods are released. 908 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 909 + } 910 + 911 + // Test with layer tap and speculative mod tap keys on the same layer, trying 912 + // a nested press from LT to MT key: 913 + // "LT down, MT down, (wait out tapping term), MT up, LT up." 914 + TEST_F(SpeculativeHoldFlowTapTest, lt_mt_same_layer_nested_press) { 915 + TestDriver driver; 916 + InSequence s; 917 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 918 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B)); 919 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 920 + 921 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 922 + 923 + EXPECT_NO_REPORT(driver); 924 + layer_tap_key.press(); 925 + idle_for(FLOW_TAP_TERM + 1); 926 + VERIFY_AND_CLEAR(driver); 927 + 928 + EXPECT_REPORT(driver, (KC_LSFT)); 929 + mod_tap_key.press(); 930 + run_one_scan_loop(); 931 + VERIFY_AND_CLEAR(driver); 932 + 933 + EXPECT_EMPTY_REPORT(driver); 934 + EXPECT_REPORT(driver, (KC_C)); 935 + idle_for(TAPPING_TERM - FLOW_TAP_TERM); 936 + VERIFY_AND_CLEAR(driver); 937 + 938 + // Release keys: MT first, LT second. 939 + EXPECT_EMPTY_REPORT(driver); 940 + mod_tap_key.release(); 941 + run_one_scan_loop(); 942 + layer_tap_key.release(); 943 + idle_for(FLOW_TAP_TERM + 1); 944 + VERIFY_AND_CLEAR(driver); 945 + // All mods are released. 946 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 947 + } 948 + 949 + // Test with layer tap and speculative mod tap keys on the same layer, trying 950 + // a nested press with the MT first: 951 + // "MT down, LT down, (wait out tapping term), LT up, MT up." 952 + TEST_F(SpeculativeHoldFlowTapTest, mt_lt_same_layer_nested_press) { 953 + TestDriver driver; 954 + InSequence s; 955 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 956 + auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B)); 957 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 958 + 959 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 960 + 961 + EXPECT_REPORT(driver, (KC_LSFT)); 962 + mod_tap_key.press(); 963 + run_one_scan_loop(); 964 + 965 + EXPECT_NO_REPORT(driver); 966 + layer_tap_key.press(); 967 + idle_for(TAPPING_TERM + 1); 968 + VERIFY_AND_CLEAR(driver); 969 + 970 + EXPECT_EMPTY_REPORT(driver); 971 + layer_tap_key.release(); 972 + run_one_scan_loop(); 973 + mod_tap_key.release(); 974 + idle_for(FLOW_TAP_TERM + 1); 975 + VERIFY_AND_CLEAR(driver); 976 + // All mods are released. 977 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 978 + } 979 + 980 + // Test with a speculative mod tap key reached by a layer tap key, rolling from 981 + // LT to MT key: 982 + // "LT down, MT down, (wait out tapping term), LT up, MT up." 983 + TEST_F(SpeculativeHoldFlowTapTest, lt_mt_different_layer_roll) { 984 + TestDriver driver; 985 + InSequence s; 986 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 987 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 988 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 989 + auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C)); 990 + 991 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 992 + 993 + // Press layer tap key. 994 + EXPECT_NO_REPORT(driver); 995 + layer_tap_key.press(); 996 + idle_for(FLOW_TAP_TERM + 1); 997 + // Press mod tap key. 998 + mod_tap_key.press(); 999 + idle_for(TAPPING_TERM - FLOW_TAP_TERM); 1000 + VERIFY_AND_CLEAR(driver); 1001 + 1002 + // Release keys. 1003 + EXPECT_REPORT(driver, (KC_B)); 1004 + layer_tap_key.release(); 1005 + idle_for(TAPPING_TERM); 1006 + VERIFY_AND_CLEAR(driver); 1007 + 1008 + EXPECT_EMPTY_REPORT(driver); 1009 + mod_tap_key.release(); 1010 + idle_for(FLOW_TAP_TERM + 1); 1011 + VERIFY_AND_CLEAR(driver); 1012 + // All mods are released. 1013 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 1014 + } 1015 + 1016 + // Test with a speculative mod tap key reached by a layer tap key, slowly 1017 + // rolling from LT to MT key: 1018 + // "LT down, (wait), MT down, (wait), LT up, MT up." 1019 + TEST_F(SpeculativeHoldFlowTapTest, lt_mt_different_layer_slow_roll) { 1020 + TestDriver driver; 1021 + InSequence s; 1022 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 1023 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 1024 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 1025 + auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C)); 1026 + 1027 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 1028 + 1029 + EXPECT_NO_REPORT(driver); 1030 + layer_tap_key.press(); 1031 + idle_for(TAPPING_TERM + 1); 1032 + 1033 + EXPECT_REPORT(driver, (KC_LSFT)); 1034 + mod_tap_key.press(); 1035 + idle_for(FLOW_TAP_TERM + 1); 1036 + VERIFY_AND_CLEAR(driver); 1037 + 1038 + EXPECT_EMPTY_REPORT(driver); 1039 + EXPECT_REPORT(driver, (KC_B)); 1040 + EXPECT_EMPTY_REPORT(driver); 1041 + layer_tap_key.release(); 1042 + run_one_scan_loop(); 1043 + mod_tap_key.release(); 1044 + idle_for(FLOW_TAP_TERM + 1); 1045 + VERIFY_AND_CLEAR(driver); 1046 + // All mods are released. 1047 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 1048 + } 1049 + 1050 + // Test with a speculative mod tap key reached by a layer tap key, trying a 1051 + // nested press: 1052 + // "LT down, MT down, (wait out tapping term), MT up, LT up." 1053 + TEST_F(SpeculativeHoldFlowTapTest, lt_mt_different_layer_nested_press) { 1054 + TestDriver driver; 1055 + InSequence s; 1056 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 1057 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 1058 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 1059 + auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C)); 1060 + 1061 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 1062 + 1063 + EXPECT_NO_REPORT(driver); 1064 + layer_tap_key.press(); 1065 + idle_for(FLOW_TAP_TERM + 1); 1066 + mod_tap_key.press(); 1067 + idle_for(TAPPING_TERM - FLOW_TAP_TERM); 1068 + VERIFY_AND_CLEAR(driver); 1069 + 1070 + // Release keys. 1071 + EXPECT_REPORT(driver, (KC_C)); 1072 + EXPECT_EMPTY_REPORT(driver); 1073 + mod_tap_key.release(); 1074 + run_one_scan_loop(); 1075 + layer_tap_key.release(); 1076 + idle_for(FLOW_TAP_TERM + 1); 1077 + VERIFY_AND_CLEAR(driver); 1078 + // All mods are released. 1079 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 1080 + } 1081 + 1082 + // Test with a speculative mod tap key reached by a layer tap key, trying a 1083 + // slow nested press: 1084 + // "LT down, (wait), MT down, MT up, LT up." 1085 + TEST_F(SpeculativeHoldFlowTapTest, lt_mt_different_layer_slow_nested_press) { 1086 + TestDriver driver; 1087 + InSequence s; 1088 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 1089 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 1090 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 1091 + auto mod_tap_key = KeymapKey(1, 1, 0, SFT_T(KC_C)); 1092 + 1093 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 1094 + 1095 + EXPECT_NO_REPORT(driver); 1096 + layer_tap_key.press(); 1097 + idle_for(TAPPING_TERM + 1); 1098 + 1099 + EXPECT_REPORT(driver, (KC_LSFT)); 1100 + mod_tap_key.press(); 1101 + idle_for(FLOW_TAP_TERM + 1); 1102 + VERIFY_AND_CLEAR(driver); 1103 + 1104 + EXPECT_EMPTY_REPORT(driver); 1105 + EXPECT_REPORT(driver, (KC_C)); 1106 + EXPECT_EMPTY_REPORT(driver); 1107 + mod_tap_key.release(); 1108 + run_one_scan_loop(); 1109 + layer_tap_key.release(); 1110 + idle_for(FLOW_TAP_TERM + 1); 1111 + VERIFY_AND_CLEAR(driver); 1112 + // All mods are released. 1113 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 1114 + }
+30
tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/config.h
··· 1 + /* Copyright 2022 Isaac Elenbaas 2 + * Copyright 2025 Google LLC 3 + * 4 + * This program is free software: you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation, either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 + */ 17 + 18 + #pragma once 19 + 20 + #include "test_common.h" 21 + 22 + #define SPECULATIVE_HOLD 23 + #define PERMISSIVE_HOLD 24 + 25 + #define RETRO_SHIFT 2 * TAPPING_TERM 26 + // releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested 27 + #define AUTO_SHIFT_TIMEOUT TAPPING_TERM 28 + #define AUTO_SHIFT_MODIFIERS 29 + 30 + #define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_F24
+17
tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/test.mk
··· 1 + # Copyright 2022 Isaac Elenbaas 2 + # Copyright 2025 Google LLC 3 + # 4 + # This program is free software: you can redistribute it and/or modify 5 + # it under the terms of the GNU General Public License as published by 6 + # the Free Software Foundation, either version 2 of the License, or 7 + # (at your option) any later version. 8 + # 9 + # This program is distributed in the hope that it will be useful, 10 + # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + # GNU General Public License for more details. 13 + # 14 + # You should have received a copy of the GNU General Public License 15 + # along with this program. If not, see <http://www.gnu.org/licenses/>. 16 + 17 + AUTO_SHIFT_ENABLE = yes
+689
tests/tap_hold_configurations/speculative_hold/retro_shift_permissive_hold/test_retro_shift.cpp
··· 1 + // Copyright 2022 Isaac Elenbaas 2 + // Copyright 2025 Google LLC 3 + // 4 + // This program is free software: you can redistribute it and/or modify 5 + // it under the terms of the GNU General Public License as published by 6 + // the Free Software Foundation, either version 2 of the License, or 7 + // (at your option) any later version. 8 + // 9 + // This program is distributed in the hope that it will be useful, 10 + // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + // GNU General Public License for more details. 13 + // 14 + // You should have received a copy of the GNU General Public License 15 + // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 + 17 + #include "keyboard_report_util.hpp" 18 + #include "keycode.h" 19 + #include "test_common.hpp" 20 + #include "action_tapping.h" 21 + #include "test_fixture.hpp" 22 + #include "test_keymap_key.hpp" 23 + 24 + extern "C" { 25 + bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) { 26 + return true; 27 + } 28 + 29 + bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { 30 + return true; 31 + } 32 + } // extern "C" 33 + 34 + using testing::_; 35 + using testing::AnyNumber; 36 + using testing::AnyOf; 37 + using testing::InSequence; 38 + 39 + class RetroShiftPermissiveHold : public TestFixture {}; 40 + 41 + TEST_F(RetroShiftPermissiveHold, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) { 42 + TestDriver driver; 43 + InSequence s; 44 + auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P)); 45 + auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A); 46 + 47 + set_keymap({mod_tap_key, regular_key}); 48 + 49 + // Press mod-tap-hold key. 50 + EXPECT_REPORT(driver, (KC_LCTL)); 51 + mod_tap_key.press(); 52 + run_one_scan_loop(); 53 + VERIFY_AND_CLEAR(driver); 54 + 55 + // Press regular key. 56 + EXPECT_NO_REPORT(driver); 57 + regular_key.press(); 58 + run_one_scan_loop(); 59 + VERIFY_AND_CLEAR(driver); 60 + 61 + // Release regular key. 62 + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber()); 63 + EXPECT_REPORT(driver, (KC_LCTL, KC_A)); 64 + EXPECT_REPORT(driver, (KC_LCTL)); 65 + regular_key.release(); 66 + run_one_scan_loop(); 67 + VERIFY_AND_CLEAR(driver); 68 + 69 + // Release mod-tap-hold key. 70 + EXPECT_EMPTY_REPORT(driver); 71 + mod_tap_key.release(); 72 + run_one_scan_loop(); 73 + VERIFY_AND_CLEAR(driver); 74 + } 75 + 76 + TEST_F(RetroShiftPermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) { 77 + TestDriver driver; 78 + InSequence s; 79 + auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P)); 80 + auto mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, LALT_T(KC_A)); 81 + 82 + set_keymap({mod_tap_key, mod_tap_regular_key}); 83 + 84 + // Press mod-tap-hold key. 85 + EXPECT_REPORT(driver, (KC_LCTL)); 86 + mod_tap_key.press(); 87 + run_one_scan_loop(); 88 + VERIFY_AND_CLEAR(driver); 89 + 90 + // Press mod-tap-regular key. 91 + EXPECT_REPORT(driver, (KC_LCTL, KC_LALT)); 92 + mod_tap_regular_key.press(); 93 + run_one_scan_loop(); 94 + VERIFY_AND_CLEAR(driver); 95 + 96 + // Release mod-tap-regular key. 97 + EXPECT_REPORT(driver, (KC_LCTL)); 98 + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber()); 99 + EXPECT_REPORT(driver, (KC_LCTL, KC_A)); 100 + EXPECT_REPORT(driver, (KC_LCTL)); 101 + mod_tap_regular_key.release(); 102 + run_one_scan_loop(); 103 + VERIFY_AND_CLEAR(driver); 104 + 105 + // Release mod-tap-hold key. 106 + EXPECT_EMPTY_REPORT(driver); 107 + mod_tap_key.release(); 108 + run_one_scan_loop(); 109 + VERIFY_AND_CLEAR(driver); 110 + } 111 + 112 + TEST_F(RetroShiftPermissiveHold, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) { 113 + TestDriver driver; 114 + InSequence s; 115 + auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P)); 116 + auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A); 117 + 118 + set_keymap({mod_tap_key, regular_key}); 119 + 120 + // Press mod-tap-hold key. 121 + EXPECT_REPORT(driver, (KC_LCTL)); 122 + mod_tap_key.press(); 123 + run_one_scan_loop(); 124 + VERIFY_AND_CLEAR(driver); 125 + 126 + // Press regular key. 127 + EXPECT_NO_REPORT(driver); 128 + regular_key.press(); 129 + run_one_scan_loop(); 130 + VERIFY_AND_CLEAR(driver); 131 + 132 + // Release regular key. 133 + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber()); 134 + EXPECT_REPORT(driver, (KC_LCTL, KC_A)); 135 + EXPECT_REPORT(driver, (KC_LCTL)); 136 + regular_key.release(); 137 + run_one_scan_loop(); 138 + idle_for(TAPPING_TERM); 139 + VERIFY_AND_CLEAR(driver); 140 + 141 + // Release mod-tap-hold key. 142 + EXPECT_EMPTY_REPORT(driver); 143 + mod_tap_key.release(); 144 + run_one_scan_loop(); 145 + VERIFY_AND_CLEAR(driver); 146 + } 147 + 148 + TEST_F(RetroShiftPermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) { 149 + TestDriver driver; 150 + InSequence s; 151 + auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P)); 152 + auto mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, LALT_T(KC_A)); 153 + 154 + set_keymap({mod_tap_key, mod_tap_regular_key}); 155 + 156 + // Press mod-tap-hold key. 157 + EXPECT_REPORT(driver, (KC_LCTL)); 158 + mod_tap_key.press(); 159 + run_one_scan_loop(); 160 + VERIFY_AND_CLEAR(driver); 161 + 162 + // Press mod-tap-regular key. 163 + EXPECT_REPORT(driver, (KC_LCTL, KC_LALT)); 164 + mod_tap_regular_key.press(); 165 + run_one_scan_loop(); 166 + VERIFY_AND_CLEAR(driver); 167 + 168 + // Release mod-tap-regular key. 169 + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber()); 170 + EXPECT_REPORT(driver, (KC_LCTL, KC_A)); 171 + EXPECT_REPORT(driver, (KC_LCTL)); 172 + mod_tap_regular_key.release(); 173 + run_one_scan_loop(); 174 + idle_for(TAPPING_TERM); 175 + VERIFY_AND_CLEAR(driver); 176 + 177 + // Release mod-tap-hold key. 178 + EXPECT_EMPTY_REPORT(driver); 179 + mod_tap_key.release(); 180 + run_one_scan_loop(); 181 + VERIFY_AND_CLEAR(driver); 182 + } 183 + 184 + TEST_F(RetroShiftPermissiveHold, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) { 185 + TestDriver driver; 186 + InSequence s; 187 + auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P)); 188 + auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A); 189 + 190 + set_keymap({mod_tap_key, regular_key}); 191 + 192 + // Press mod-tap-hold key. 193 + EXPECT_REPORT(driver, (KC_LCTL)); 194 + mod_tap_key.press(); 195 + run_one_scan_loop(); 196 + VERIFY_AND_CLEAR(driver); 197 + 198 + // Press regular key. 199 + EXPECT_NO_REPORT(driver); 200 + regular_key.press(); 201 + run_one_scan_loop(); 202 + idle_for(AUTO_SHIFT_TIMEOUT); 203 + VERIFY_AND_CLEAR(driver); 204 + 205 + // Release regular key. 206 + // clang-format off 207 + EXPECT_CALL(driver, send_keyboard_mock(AnyOf( 208 + KeyboardReport(KC_LCTL, KC_LSFT), 209 + KeyboardReport(KC_LSFT), 210 + KeyboardReport(KC_LCTL)))) 211 + .Times(AnyNumber()); 212 + // clang-format on 213 + EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A)); 214 + // clang-format off 215 + EXPECT_CALL(driver, send_keyboard_mock(AnyOf( 216 + KeyboardReport(KC_LCTL, KC_LSFT), 217 + KeyboardReport(KC_LSFT)))) 218 + .Times(AnyNumber()); 219 + // clang-format on 220 + EXPECT_REPORT(driver, (KC_LCTL)); 221 + regular_key.release(); 222 + run_one_scan_loop(); 223 + VERIFY_AND_CLEAR(driver); 224 + 225 + // Release mod-tap-hold key. 226 + EXPECT_EMPTY_REPORT(driver); 227 + mod_tap_key.release(); 228 + run_one_scan_loop(); 229 + VERIFY_AND_CLEAR(driver); 230 + } 231 + 232 + TEST_F(RetroShiftPermissiveHold, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) { 233 + TestDriver driver; 234 + InSequence s; 235 + auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P)); 236 + auto mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, LALT_T(KC_A)); 237 + 238 + set_keymap({mod_tap_key, mod_tap_regular_key}); 239 + 240 + // Press mod-tap-hold key. 241 + EXPECT_REPORT(driver, (KC_LCTL)); 242 + mod_tap_key.press(); 243 + run_one_scan_loop(); 244 + VERIFY_AND_CLEAR(driver); 245 + 246 + // Press mod-tap-regular key. 247 + EXPECT_REPORT(driver, (KC_LCTL, KC_LALT)); 248 + mod_tap_regular_key.press(); 249 + run_one_scan_loop(); 250 + idle_for(AUTO_SHIFT_TIMEOUT); 251 + VERIFY_AND_CLEAR(driver); 252 + 253 + // Release mod-tap-regular key. 254 + // clang-format off 255 + EXPECT_CALL(driver, send_keyboard_mock(AnyOf( 256 + KeyboardReport(KC_LCTL, KC_LSFT), 257 + KeyboardReport(KC_LSFT), 258 + KeyboardReport(KC_LCTL)))) 259 + .Times(AnyNumber()); 260 + // clang-format on 261 + EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A)); 262 + // clang-format off 263 + EXPECT_CALL(driver, send_keyboard_mock(AnyOf( 264 + KeyboardReport(KC_LCTL, KC_LSFT), 265 + KeyboardReport(KC_LSFT)))) 266 + .Times(AnyNumber()); 267 + // clang-format on 268 + EXPECT_REPORT(driver, (KC_LCTL)); 269 + mod_tap_regular_key.release(); 270 + run_one_scan_loop(); 271 + idle_for(TAPPING_TERM); 272 + VERIFY_AND_CLEAR(driver); 273 + 274 + // Release mod-tap-hold key. 275 + EXPECT_EMPTY_REPORT(driver); 276 + mod_tap_key.release(); 277 + run_one_scan_loop(); 278 + VERIFY_AND_CLEAR(driver); 279 + } 280 + 281 + TEST_F(RetroShiftPermissiveHold, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) { 282 + TestDriver driver; 283 + InSequence s; 284 + auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P)); 285 + auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A); 286 + 287 + set_keymap({mod_tap_key, regular_key}); 288 + 289 + // Press mod-tap-hold key. 290 + EXPECT_REPORT(driver, (KC_LCTL)); 291 + mod_tap_key.press(); 292 + run_one_scan_loop(); 293 + VERIFY_AND_CLEAR(driver); 294 + 295 + // Press regular key. 296 + EXPECT_NO_REPORT(driver); 297 + regular_key.press(); 298 + run_one_scan_loop(); 299 + VERIFY_AND_CLEAR(driver); 300 + 301 + // Release mod-tap-hold key. 302 + EXPECT_EMPTY_REPORT(driver); 303 + EXPECT_REPORT(driver, (KC_P)); 304 + EXPECT_EMPTY_REPORT(driver); 305 + mod_tap_key.release(); 306 + run_one_scan_loop(); 307 + VERIFY_AND_CLEAR(driver); 308 + 309 + // Release regular key. 310 + EXPECT_REPORT(driver, (KC_A)); 311 + EXPECT_EMPTY_REPORT(driver); 312 + regular_key.release(); 313 + run_one_scan_loop(); 314 + VERIFY_AND_CLEAR(driver); 315 + } 316 + 317 + TEST_F(RetroShiftPermissiveHold, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) { 318 + TestDriver driver; 319 + InSequence s; 320 + auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P)); 321 + auto mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, LALT_T(KC_A)); 322 + 323 + set_keymap({mod_tap_key, mod_tap_regular_key}); 324 + 325 + // Press mod-tap-hold key. 326 + EXPECT_REPORT(driver, (KC_LCTL)); 327 + mod_tap_key.press(); 328 + run_one_scan_loop(); 329 + VERIFY_AND_CLEAR(driver); 330 + 331 + // Press mod-tap-regular key. 332 + EXPECT_REPORT(driver, (KC_LCTL, KC_LALT)); 333 + mod_tap_regular_key.press(); 334 + run_one_scan_loop(); 335 + VERIFY_AND_CLEAR(driver); 336 + 337 + // Release mod-tap-hold key. 338 + EXPECT_EMPTY_REPORT(driver); 339 + EXPECT_REPORT(driver, (KC_P)); 340 + EXPECT_EMPTY_REPORT(driver); 341 + mod_tap_key.release(); 342 + run_one_scan_loop(); 343 + VERIFY_AND_CLEAR(driver); 344 + 345 + // Release mod-tap-regular key. 346 + EXPECT_REPORT(driver, (KC_A)); 347 + EXPECT_EMPTY_REPORT(driver); 348 + mod_tap_regular_key.release(); 349 + run_one_scan_loop(); 350 + VERIFY_AND_CLEAR(driver); 351 + } 352 + 353 + TEST_F(RetroShiftPermissiveHold, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) { 354 + TestDriver driver; 355 + InSequence s; 356 + auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P)); 357 + auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A); 358 + 359 + set_keymap({mod_tap_key, regular_key}); 360 + 361 + // Press mod-tap-hold key. 362 + EXPECT_REPORT(driver, (KC_LCTL)); 363 + mod_tap_key.press(); 364 + run_one_scan_loop(); 365 + VERIFY_AND_CLEAR(driver); 366 + 367 + // Press regular key. 368 + EXPECT_NO_REPORT(driver); 369 + regular_key.press(); 370 + run_one_scan_loop(); 371 + VERIFY_AND_CLEAR(driver); 372 + 373 + // Release mod-tap-hold key. 374 + EXPECT_EMPTY_REPORT(driver); 375 + EXPECT_REPORT(driver, (KC_P)); 376 + EXPECT_EMPTY_REPORT(driver); 377 + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); 378 + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); 379 + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); 380 + EXPECT_EMPTY_REPORT(driver); 381 + mod_tap_key.release(); 382 + run_one_scan_loop(); 383 + idle_for(AUTO_SHIFT_TIMEOUT); 384 + VERIFY_AND_CLEAR(driver); 385 + 386 + // Release regular key. 387 + EXPECT_NO_REPORT(driver); 388 + regular_key.release(); 389 + run_one_scan_loop(); 390 + VERIFY_AND_CLEAR(driver); 391 + } 392 + 393 + TEST_F(RetroShiftPermissiveHold, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) { 394 + TestDriver driver; 395 + InSequence s; 396 + auto mod_tap_key = KeymapKey(0, 0, 0, LCTL_T(KC_P)); 397 + auto mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, LALT_T(KC_A)); 398 + 399 + set_keymap({mod_tap_key, mod_tap_regular_key}); 400 + 401 + // Press mod-tap-hold key. 402 + EXPECT_REPORT(driver, (KC_LCTL)); 403 + mod_tap_key.press(); 404 + run_one_scan_loop(); 405 + VERIFY_AND_CLEAR(driver); 406 + 407 + // Press mod-tap-regular key. 408 + EXPECT_REPORT(driver, (KC_LCTL, KC_LALT)); 409 + mod_tap_regular_key.press(); 410 + run_one_scan_loop(); 411 + VERIFY_AND_CLEAR(driver); 412 + 413 + // Release mod-tap-hold key. 414 + EXPECT_EMPTY_REPORT(driver); 415 + EXPECT_REPORT(driver, (KC_P)); 416 + EXPECT_EMPTY_REPORT(driver); 417 + mod_tap_key.release(); 418 + run_one_scan_loop(); 419 + VERIFY_AND_CLEAR(driver); 420 + 421 + // Release mod-tap-regular key. 422 + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); 423 + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); 424 + EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber()); 425 + EXPECT_EMPTY_REPORT(driver); 426 + idle_for(AUTO_SHIFT_TIMEOUT); 427 + mod_tap_regular_key.release(); 428 + run_one_scan_loop(); 429 + VERIFY_AND_CLEAR(driver); 430 + } 431 + 432 + // Test with layer tap and speculative mod tap keys on the same layer, rolling 433 + // from LT to MT key: 434 + // "LT down, MT down, (wait out tapping term), LT up, MT up." 435 + TEST_F(RetroShiftPermissiveHold, lt_mt_same_layer_roll) { 436 + TestDriver driver; 437 + InSequence s; 438 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 439 + auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B)); 440 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 441 + 442 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 443 + 444 + // Press layer tap key. 445 + EXPECT_NO_REPORT(driver); 446 + layer_tap_key.press(); 447 + run_one_scan_loop(); 448 + VERIFY_AND_CLEAR(driver); 449 + 450 + // Press mod-tap key, after flow tap term but within tapping term. The 451 + // speculative mod activates. 452 + EXPECT_REPORT(driver, (KC_LCTL)); 453 + mod_tap_key.press(); 454 + run_one_scan_loop(); 455 + VERIFY_AND_CLEAR(driver); 456 + 457 + // Wait for the layer tap key to settle. 458 + EXPECT_NO_REPORT(driver); 459 + idle_for(TAPPING_TERM); 460 + VERIFY_AND_CLEAR(driver); 461 + 462 + // Release keys. 463 + EXPECT_EMPTY_REPORT(driver); 464 + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); 465 + EXPECT_REPORT(driver, (KC_LSFT)); 466 + EXPECT_EMPTY_REPORT(driver); 467 + EXPECT_REPORT(driver, (KC_B)); 468 + EXPECT_EMPTY_REPORT(driver); 469 + layer_tap_key.release(); 470 + run_one_scan_loop(); 471 + mod_tap_key.release(); 472 + run_one_scan_loop(); 473 + VERIFY_AND_CLEAR(driver); 474 + // All mods are released. 475 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 476 + } 477 + 478 + // Test with layer tap and speculative mod tap keys on the same layer, trying 479 + // a nested press from LT to MT key: 480 + // "LT down, MT down, (wait out tapping term), MT up, LT up." 481 + TEST_F(RetroShiftPermissiveHold, lt_mt_same_layer_nested_press) { 482 + TestDriver driver; 483 + InSequence s; 484 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 485 + auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B)); 486 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 487 + 488 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 489 + 490 + EXPECT_NO_REPORT(driver); 491 + layer_tap_key.press(); 492 + run_one_scan_loop(); 493 + VERIFY_AND_CLEAR(driver); 494 + 495 + EXPECT_REPORT(driver, (KC_LCTL)); 496 + mod_tap_key.press(); 497 + run_one_scan_loop(); 498 + VERIFY_AND_CLEAR(driver); 499 + 500 + EXPECT_NO_REPORT(driver); 501 + run_one_scan_loop(); 502 + idle_for(TAPPING_TERM); 503 + VERIFY_AND_CLEAR(driver); 504 + 505 + // Release keys: MT first, LT second. 506 + EXPECT_EMPTY_REPORT(driver); 507 + EXPECT_REPORT(driver, (KC_LSFT, KC_C)); 508 + EXPECT_REPORT(driver, (KC_LSFT)); 509 + EXPECT_EMPTY_REPORT(driver); 510 + mod_tap_key.release(); 511 + run_one_scan_loop(); 512 + layer_tap_key.release(); 513 + run_one_scan_loop(); 514 + VERIFY_AND_CLEAR(driver); 515 + // All mods are released. 516 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 517 + } 518 + 519 + // Test with layer tap and speculative mod tap keys on the same layer, trying 520 + // a nested press with the MT first: 521 + // "MT down, LT down, (wait out tapping term), LT up, MT up." 522 + TEST_F(RetroShiftPermissiveHold, mt_lt_same_layer_nested_press) { 523 + TestDriver driver; 524 + InSequence s; 525 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 526 + auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B)); 527 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 528 + 529 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 530 + 531 + EXPECT_REPORT(driver, (KC_LCTL)); 532 + mod_tap_key.press(); 533 + run_one_scan_loop(); 534 + VERIFY_AND_CLEAR(driver); 535 + 536 + EXPECT_NO_REPORT(driver); 537 + layer_tap_key.press(); 538 + idle_for(TAPPING_TERM + 1); 539 + VERIFY_AND_CLEAR(driver); 540 + 541 + EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A)); 542 + EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT)); 543 + EXPECT_REPORT(driver, (KC_LCTL)); 544 + EXPECT_EMPTY_REPORT(driver); 545 + layer_tap_key.release(); 546 + run_one_scan_loop(); 547 + mod_tap_key.release(); 548 + run_one_scan_loop(); 549 + VERIFY_AND_CLEAR(driver); 550 + // All mods are released. 551 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 552 + } 553 + 554 + // Test with a speculative mod tap key reached by a layer tap key, rolling from 555 + // LT to MT key: 556 + // "LT down, MT down, (wait out tapping term), LT up, MT up." 557 + TEST_F(RetroShiftPermissiveHold, lt_mt_different_layer_roll) { 558 + TestDriver driver; 559 + InSequence s; 560 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 561 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 562 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 563 + auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C)); 564 + 565 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 566 + 567 + // Press layer tap key. 568 + EXPECT_NO_REPORT(driver); 569 + layer_tap_key.press(); 570 + run_one_scan_loop(); 571 + // Press mod tap key. 572 + mod_tap_key.press(); 573 + idle_for(TAPPING_TERM); 574 + VERIFY_AND_CLEAR(driver); 575 + 576 + // Release keys. 577 + EXPECT_REPORT(driver, (KC_A)); 578 + EXPECT_EMPTY_REPORT(driver); 579 + layer_tap_key.release(); 580 + idle_for(TAPPING_TERM); 581 + VERIFY_AND_CLEAR(driver); 582 + 583 + EXPECT_REPORT(driver, (KC_LSFT, KC_B)); 584 + EXPECT_REPORT(driver, (KC_LSFT)); 585 + EXPECT_EMPTY_REPORT(driver); 586 + mod_tap_key.release(); 587 + run_one_scan_loop(); 588 + VERIFY_AND_CLEAR(driver); 589 + // All mods are released. 590 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 591 + } 592 + 593 + // Test with a speculative mod tap key reached by a layer tap key, slowly 594 + // rolling from LT to MT key: 595 + // "LT down, (wait), MT down, (wait), LT up, MT up." 596 + TEST_F(RetroShiftPermissiveHold, lt_mt_different_layer_slow_roll) { 597 + TestDriver driver; 598 + InSequence s; 599 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 600 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 601 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 602 + auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C)); 603 + 604 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 605 + 606 + EXPECT_NO_REPORT(driver); 607 + layer_tap_key.press(); 608 + idle_for(TAPPING_TERM + 1); 609 + mod_tap_key.press(); 610 + run_one_scan_loop(); 611 + VERIFY_AND_CLEAR(driver); 612 + 613 + EXPECT_REPORT(driver, (KC_LSFT, KC_A)); 614 + EXPECT_REPORT(driver, (KC_LSFT)); 615 + EXPECT_EMPTY_REPORT(driver); 616 + EXPECT_REPORT(driver, (KC_B)); 617 + EXPECT_EMPTY_REPORT(driver); 618 + layer_tap_key.release(); 619 + run_one_scan_loop(); 620 + mod_tap_key.release(); 621 + run_one_scan_loop(); 622 + VERIFY_AND_CLEAR(driver); 623 + // All mods are released. 624 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 625 + } 626 + 627 + // Test with a speculative mod tap key reached by a layer tap key, try a nested 628 + // press: 629 + // "LT down, MT down, (wait out tapping term), MT up, LT up." 630 + TEST_F(RetroShiftPermissiveHold, lt_mt_different_layer_nested_press) { 631 + TestDriver driver; 632 + InSequence s; 633 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 634 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 635 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 636 + auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C)); 637 + 638 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 639 + 640 + EXPECT_NO_REPORT(driver); 641 + layer_tap_key.press(); 642 + run_one_scan_loop(); 643 + mod_tap_key.press(); 644 + idle_for(TAPPING_TERM); 645 + VERIFY_AND_CLEAR(driver); 646 + 647 + // Release keys. 648 + EXPECT_REPORT(driver, (KC_LSFT, KC_C)); 649 + EXPECT_REPORT(driver, (KC_LSFT)); 650 + EXPECT_EMPTY_REPORT(driver); 651 + mod_tap_key.release(); 652 + run_one_scan_loop(); 653 + layer_tap_key.release(); 654 + run_one_scan_loop(); 655 + VERIFY_AND_CLEAR(driver); 656 + // All mods are released. 657 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 658 + } 659 + 660 + // Test with a speculative mod tap key reached by a layer tap key, try a slow 661 + // nested press: 662 + // "LT down, (wait), MT down, MT up, LT up." 663 + TEST_F(RetroShiftPermissiveHold, lt_mt_different_layer_slow_nested_press) { 664 + TestDriver driver; 665 + InSequence s; 666 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 667 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 668 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 669 + auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C)); 670 + 671 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 672 + 673 + EXPECT_NO_REPORT(driver); 674 + layer_tap_key.press(); 675 + idle_for(TAPPING_TERM + 1); 676 + mod_tap_key.press(); 677 + run_one_scan_loop(); 678 + VERIFY_AND_CLEAR(driver); 679 + 680 + EXPECT_REPORT(driver, (KC_C)); 681 + EXPECT_EMPTY_REPORT(driver); 682 + mod_tap_key.release(); 683 + run_one_scan_loop(); 684 + layer_tap_key.release(); 685 + run_one_scan_loop(); 686 + VERIFY_AND_CLEAR(driver); 687 + // All mods are released. 688 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 689 + }
+24
tests/tap_hold_configurations/speculative_hold/retro_tapping/config.h
··· 1 + /* Copyright 2022 Vladislav Kucheriavykh 2 + * Copyright 2025 Google LLC 3 + * 4 + * This program is free software: you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation, either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 + */ 17 + 18 + #pragma once 19 + 20 + #include "test_common.h" 21 + 22 + #define SPECULATIVE_HOLD 23 + #define RETRO_TAPPING 24 + #define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_F24
+15
tests/tap_hold_configurations/speculative_hold/retro_tapping/test.mk
··· 1 + # Copyright 2022 Vladislav Kucheriavykh 2 + # Copyright 2025 Google LLC 3 + # 4 + # This program is free software: you can redistribute it and/or modify 5 + # it under the terms of the GNU General Public License as published by 6 + # the Free Software Foundation, either version 2 of the License, or 7 + # (at your option) any later version. 8 + # 9 + # This program is distributed in the hope that it will be useful, 10 + # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + # GNU General Public License for more details. 13 + # 14 + # You should have received a copy of the GNU General Public License 15 + # along with this program. If not, see <http://www.gnu.org/licenses/>.
+629
tests/tap_hold_configurations/speculative_hold/retro_tapping/test_tap_hold.cpp
··· 1 + // Copyright 2025 Google LLC 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // https://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + #include "keyboard_report_util.hpp" 16 + #include "keycode.h" 17 + #include "test_common.hpp" 18 + #include "action_tapping.h" 19 + #include "test_fixture.hpp" 20 + #include "test_keymap_key.hpp" 21 + 22 + using testing::_; 23 + using testing::InSequence; 24 + 25 + extern "C" bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) { 26 + return true; 27 + } 28 + 29 + class SpeculativeHoldRetroTappingTest : public TestFixture {}; 30 + 31 + TEST_F(SpeculativeHoldRetroTappingTest, roll_regular_to_lgui_mod) { 32 + TestDriver driver; 33 + InSequence s; 34 + auto mod_tap_key = KeymapKey(0, 1, 0, LGUI_T(KC_P)); 35 + auto regular_key = KeymapKey(0, 2, 0, KC_B); 36 + 37 + set_keymap({mod_tap_key, regular_key}); 38 + 39 + EXPECT_REPORT(driver, (KC_B)); 40 + regular_key.press(); 41 + run_one_scan_loop(); 42 + VERIFY_AND_CLEAR(driver); 43 + 44 + EXPECT_REPORT(driver, (KC_B, KC_LGUI)); 45 + mod_tap_key.press(); 46 + idle_for(TAPPING_TERM + 1); 47 + VERIFY_AND_CLEAR(driver); 48 + 49 + EXPECT_REPORT(driver, (KC_LGUI)); 50 + regular_key.release(); 51 + run_one_scan_loop(); 52 + VERIFY_AND_CLEAR(driver); 53 + 54 + // Neutralizer invoked by Speculative Hold. 55 + EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 56 + EXPECT_REPORT(driver, (KC_LGUI)); 57 + EXPECT_EMPTY_REPORT(driver); 58 + EXPECT_REPORT(driver, (KC_P)); 59 + EXPECT_EMPTY_REPORT(driver); 60 + mod_tap_key.release(); 61 + run_one_scan_loop(); 62 + VERIFY_AND_CLEAR(driver); 63 + } 64 + 65 + TEST_F(SpeculativeHoldRetroTappingTest, regular_to_mod_under_tap_term) { 66 + TestDriver driver; 67 + InSequence s; 68 + auto mod_tap_key = KeymapKey(0, 1, 0, LSFT_T(KC_A)); 69 + auto regular_key = KeymapKey(0, 2, 0, KC_B); 70 + 71 + set_keymap({mod_tap_key, regular_key}); 72 + 73 + EXPECT_REPORT(driver, (KC_B)); 74 + regular_key.press(); 75 + run_one_scan_loop(); 76 + VERIFY_AND_CLEAR(driver); 77 + 78 + EXPECT_REPORT(driver, (KC_B, KC_LSFT)); 79 + mod_tap_key.press(); 80 + run_one_scan_loop(); 81 + VERIFY_AND_CLEAR(driver); 82 + 83 + EXPECT_REPORT(driver, (KC_LSFT)); 84 + regular_key.release(); 85 + run_one_scan_loop(); 86 + VERIFY_AND_CLEAR(driver); 87 + 88 + EXPECT_EMPTY_REPORT(driver); 89 + EXPECT_REPORT(driver, (KC_A)); 90 + EXPECT_EMPTY_REPORT(driver); 91 + mod_tap_key.release(); 92 + run_one_scan_loop(); 93 + VERIFY_AND_CLEAR(driver); 94 + } 95 + 96 + TEST_F(SpeculativeHoldRetroTappingTest, mod_under_tap_term_to_regular) { 97 + TestDriver driver; 98 + InSequence s; 99 + auto mod_tap_key = KeymapKey(0, 1, 0, LGUI_T(KC_P)); 100 + auto regular_key = KeymapKey(0, 2, 0, KC_B); 101 + 102 + set_keymap({mod_tap_key, regular_key}); 103 + 104 + EXPECT_REPORT(driver, (KC_LGUI)); 105 + mod_tap_key.press(); 106 + run_one_scan_loop(); 107 + VERIFY_AND_CLEAR(driver); 108 + 109 + EXPECT_NO_REPORT(driver); 110 + regular_key.press(); 111 + run_one_scan_loop(); 112 + VERIFY_AND_CLEAR(driver); 113 + 114 + // Neutralizer invoked by Speculative Hold. 115 + EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 116 + EXPECT_REPORT(driver, (KC_LGUI)); 117 + EXPECT_EMPTY_REPORT(driver); 118 + EXPECT_REPORT(driver, (KC_P)); 119 + EXPECT_REPORT(driver, (KC_B, KC_P)); 120 + EXPECT_REPORT(driver, (KC_B)); 121 + mod_tap_key.release(); 122 + run_one_scan_loop(); 123 + VERIFY_AND_CLEAR(driver); 124 + 125 + EXPECT_EMPTY_REPORT(driver); 126 + regular_key.release(); 127 + run_one_scan_loop(); 128 + VERIFY_AND_CLEAR(driver); 129 + } 130 + 131 + TEST_F(SpeculativeHoldRetroTappingTest, mod_over_tap_term_to_regular) { 132 + TestDriver driver; 133 + InSequence s; 134 + auto mod_tap_key = KeymapKey(0, 1, 0, LSFT_T(KC_A)); 135 + auto regular_key = KeymapKey(0, 2, 0, KC_B); 136 + 137 + set_keymap({mod_tap_key, regular_key}); 138 + 139 + EXPECT_REPORT(driver, (KC_LSFT)); 140 + mod_tap_key.press(); 141 + idle_for(TAPPING_TERM); 142 + run_one_scan_loop(); 143 + VERIFY_AND_CLEAR(driver); 144 + 145 + EXPECT_REPORT(driver, (KC_LSFT, KC_B)); 146 + regular_key.press(); 147 + run_one_scan_loop(); 148 + VERIFY_AND_CLEAR(driver); 149 + 150 + EXPECT_REPORT(driver, (KC_B)); 151 + mod_tap_key.release(); 152 + run_one_scan_loop(); 153 + VERIFY_AND_CLEAR(driver); 154 + 155 + EXPECT_EMPTY_REPORT(driver); 156 + regular_key.release(); 157 + run_one_scan_loop(); 158 + VERIFY_AND_CLEAR(driver); 159 + } 160 + 161 + TEST_F(SpeculativeHoldRetroTappingTest, mod_under_tap_term_to_mod_under_tap_term) { 162 + TestDriver driver; 163 + InSequence s; 164 + auto mod_tap_lgui = KeymapKey(0, 1, 0, LGUI_T(KC_P)); 165 + auto mod_tap_lsft = KeymapKey(0, 2, 0, LSFT_T(KC_A)); 166 + 167 + set_keymap({mod_tap_lgui, mod_tap_lsft}); 168 + 169 + EXPECT_REPORT(driver, (KC_LSFT)); 170 + mod_tap_lsft.press(); 171 + run_one_scan_loop(); 172 + VERIFY_AND_CLEAR(driver); 173 + 174 + EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI)); 175 + mod_tap_lgui.press(); 176 + run_one_scan_loop(); 177 + VERIFY_AND_CLEAR(driver); 178 + 179 + EXPECT_EMPTY_REPORT(driver); 180 + EXPECT_REPORT(driver, (KC_A)); 181 + EXPECT_EMPTY_REPORT(driver); 182 + mod_tap_lsft.release(); 183 + run_one_scan_loop(); 184 + VERIFY_AND_CLEAR(driver); 185 + 186 + EXPECT_REPORT(driver, (KC_P)); 187 + EXPECT_EMPTY_REPORT(driver); 188 + mod_tap_lgui.release(); 189 + run_one_scan_loop(); 190 + VERIFY_AND_CLEAR(driver); 191 + } 192 + 193 + TEST_F(SpeculativeHoldRetroTappingTest, mod_over_tap_term_to_mod_under_tap_term) { 194 + TestDriver driver; 195 + InSequence s; 196 + auto mod_tap_lgui = KeymapKey(0, 1, 0, LGUI_T(KC_P)); 197 + auto mod_tap_lsft = KeymapKey(0, 2, 0, LSFT_T(KC_A)); 198 + 199 + set_keymap({mod_tap_lgui, mod_tap_lsft}); 200 + 201 + EXPECT_REPORT(driver, (KC_LSFT)); 202 + mod_tap_lsft.press(); 203 + idle_for(TAPPING_TERM); 204 + run_one_scan_loop(); 205 + VERIFY_AND_CLEAR(driver); 206 + 207 + EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI)); 208 + mod_tap_lgui.press(); 209 + run_one_scan_loop(); 210 + VERIFY_AND_CLEAR(driver); 211 + 212 + EXPECT_NO_REPORT(driver); 213 + mod_tap_lsft.release(); 214 + run_one_scan_loop(); 215 + VERIFY_AND_CLEAR(driver); 216 + 217 + EXPECT_REPORT(driver, (KC_LSFT)); 218 + EXPECT_REPORT(driver, (KC_LSFT, KC_P)); 219 + EXPECT_REPORT(driver, (KC_P)); 220 + EXPECT_EMPTY_REPORT(driver); 221 + mod_tap_lgui.release(); 222 + run_one_scan_loop(); 223 + VERIFY_AND_CLEAR(driver); 224 + } 225 + 226 + TEST_F(SpeculativeHoldRetroTappingTest, mod_under_tap_term_to_mod_over_tap_term) { 227 + TestDriver driver; 228 + InSequence s; 229 + auto mod_tap_lgui = KeymapKey(0, 1, 0, LGUI_T(KC_P)); 230 + auto mod_tap_lsft = KeymapKey(0, 2, 0, LSFT_T(KC_A)); 231 + 232 + set_keymap({mod_tap_lgui, mod_tap_lsft}); 233 + 234 + EXPECT_REPORT(driver, (KC_LSFT)); 235 + mod_tap_lsft.press(); 236 + run_one_scan_loop(); 237 + VERIFY_AND_CLEAR(driver); 238 + 239 + EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI)); 240 + mod_tap_lgui.press(); 241 + idle_for(TAPPING_TERM); 242 + run_one_scan_loop(); 243 + VERIFY_AND_CLEAR(driver); 244 + 245 + EXPECT_REPORT(driver, (KC_LGUI)); 246 + mod_tap_lsft.release(); 247 + run_one_scan_loop(); 248 + VERIFY_AND_CLEAR(driver); 249 + 250 + // Neutralizer invoked by Retro Tapping. 251 + EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 252 + EXPECT_REPORT(driver, (KC_LGUI)); 253 + EXPECT_EMPTY_REPORT(driver); 254 + EXPECT_REPORT(driver, (KC_LSFT)); 255 + EXPECT_REPORT(driver, (KC_P, KC_LSFT)); 256 + EXPECT_REPORT(driver, (KC_LSFT)); 257 + EXPECT_EMPTY_REPORT(driver); 258 + mod_tap_lgui.release(); 259 + run_one_scan_loop(); 260 + VERIFY_AND_CLEAR(driver); 261 + } 262 + 263 + TEST_F(SpeculativeHoldRetroTappingTest, mod_under_tap_term_to_mod_over_tap_term_offset) { 264 + TestDriver driver; 265 + InSequence s; 266 + auto mod_tap_lgui = KeymapKey(0, 1, 0, LGUI_T(KC_P)); 267 + auto mod_tap_lsft = KeymapKey(0, 2, 0, LSFT_T(KC_A)); 268 + 269 + set_keymap({mod_tap_lgui, mod_tap_lsft}); 270 + 271 + EXPECT_REPORT(driver, (KC_LSFT)); 272 + mod_tap_lsft.press(); 273 + run_one_scan_loop(); 274 + VERIFY_AND_CLEAR(driver); 275 + 276 + EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI)); 277 + mod_tap_lgui.press(); 278 + run_one_scan_loop(); 279 + VERIFY_AND_CLEAR(driver); 280 + 281 + EXPECT_EMPTY_REPORT(driver); 282 + EXPECT_REPORT(driver, (KC_A)); 283 + EXPECT_EMPTY_REPORT(driver); 284 + mod_tap_lsft.release(); 285 + run_one_scan_loop(); 286 + VERIFY_AND_CLEAR(driver); 287 + 288 + EXPECT_REPORT(driver, (KC_LGUI)); 289 + // Neutralizer invoked by Retro Tapping. 290 + EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 291 + EXPECT_REPORT(driver, (KC_LGUI)); 292 + EXPECT_EMPTY_REPORT(driver); 293 + EXPECT_REPORT(driver, (KC_P)); 294 + EXPECT_EMPTY_REPORT(driver); 295 + idle_for(TAPPING_TERM); 296 + mod_tap_lgui.release(); 297 + run_one_scan_loop(); 298 + VERIFY_AND_CLEAR(driver); 299 + } 300 + 301 + TEST_F(SpeculativeHoldRetroTappingTest, mod_over_tap_term_to_mod_over_tap_term) { 302 + TestDriver driver; 303 + InSequence s; 304 + auto mod_tap_lgui = KeymapKey(0, 1, 0, LGUI_T(KC_P)); 305 + auto mod_tap_lsft = KeymapKey(0, 2, 0, LSFT_T(KC_A)); 306 + 307 + set_keymap({mod_tap_lgui, mod_tap_lsft}); 308 + 309 + EXPECT_REPORT(driver, (KC_LSFT)); 310 + mod_tap_lsft.press(); 311 + idle_for(TAPPING_TERM); 312 + run_one_scan_loop(); 313 + VERIFY_AND_CLEAR(driver); 314 + 315 + EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI)); 316 + mod_tap_lgui.press(); 317 + idle_for(TAPPING_TERM); 318 + run_one_scan_loop(); 319 + VERIFY_AND_CLEAR(driver); 320 + 321 + EXPECT_REPORT(driver, (KC_LGUI)); 322 + mod_tap_lsft.release(); 323 + run_one_scan_loop(); 324 + VERIFY_AND_CLEAR(driver); 325 + 326 + // Neutralizer invoked by Retro Tapping. 327 + EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 328 + EXPECT_REPORT(driver, (KC_LGUI)); 329 + EXPECT_EMPTY_REPORT(driver); 330 + EXPECT_REPORT(driver, (KC_LSFT)); 331 + EXPECT_REPORT(driver, (KC_P, KC_LSFT)); 332 + EXPECT_REPORT(driver, (KC_LSFT)); 333 + EXPECT_EMPTY_REPORT(driver); 334 + mod_tap_lgui.release(); 335 + run_one_scan_loop(); 336 + VERIFY_AND_CLEAR(driver); 337 + } 338 + 339 + TEST_F(SpeculativeHoldRetroTappingTest, mod_to_mod_to_mod) { 340 + TestDriver driver; 341 + InSequence s; 342 + auto mod_tap_lalt = KeymapKey(0, 1, 0, LALT_T(KC_R)); 343 + auto mod_tap_lsft = KeymapKey(0, 2, 0, SFT_T(KC_A)); 344 + auto mod_tap_lctl = KeymapKey(0, 3, 0, LCTL_T(KC_C)); 345 + 346 + set_keymap({mod_tap_lalt, mod_tap_lsft, mod_tap_lctl}); 347 + 348 + EXPECT_REPORT(driver, (KC_LALT)); 349 + mod_tap_lalt.press(); 350 + idle_for(TAPPING_TERM); 351 + run_one_scan_loop(); 352 + VERIFY_AND_CLEAR(driver); 353 + 354 + EXPECT_REPORT(driver, (KC_LSFT, KC_LALT)); 355 + mod_tap_lsft.press(); 356 + idle_for(TAPPING_TERM); 357 + run_one_scan_loop(); 358 + VERIFY_AND_CLEAR(driver); 359 + 360 + EXPECT_REPORT(driver, (KC_LSFT)); 361 + mod_tap_lalt.release(); 362 + run_one_scan_loop(); 363 + VERIFY_AND_CLEAR(driver); 364 + 365 + EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT)); 366 + mod_tap_lctl.press(); 367 + idle_for(TAPPING_TERM); 368 + run_one_scan_loop(); 369 + VERIFY_AND_CLEAR(driver); 370 + 371 + EXPECT_REPORT(driver, (KC_LCTL)); 372 + mod_tap_lsft.release(); 373 + run_one_scan_loop(); 374 + VERIFY_AND_CLEAR(driver); 375 + 376 + EXPECT_EMPTY_REPORT(driver); 377 + EXPECT_REPORT(driver, (KC_LSFT)); 378 + EXPECT_REPORT(driver, (KC_C, KC_LSFT)); 379 + EXPECT_REPORT(driver, (KC_LSFT)); 380 + EXPECT_EMPTY_REPORT(driver); 381 + mod_tap_lctl.release(); 382 + run_one_scan_loop(); 383 + VERIFY_AND_CLEAR(driver); 384 + } 385 + 386 + // Test with layer tap and speculative mod tap keys on the same layer, rolling 387 + // from LT to MT key: 388 + // "LT down, MT down, (wait out tapping term), LT up, MT up." 389 + TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_same_layer_roll) { 390 + TestDriver driver; 391 + InSequence s; 392 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 393 + auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B)); 394 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 395 + 396 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 397 + 398 + // Press layer tap key. 399 + EXPECT_NO_REPORT(driver); 400 + layer_tap_key.press(); 401 + run_one_scan_loop(); 402 + VERIFY_AND_CLEAR(driver); 403 + 404 + // Press mod-tap key, after flow tap term but within tapping term. The 405 + // speculative mod activates. 406 + EXPECT_REPORT(driver, (KC_LCTL)); 407 + mod_tap_key.press(); 408 + run_one_scan_loop(); 409 + VERIFY_AND_CLEAR(driver); 410 + 411 + // Wait for the layer tap key to settle. 412 + EXPECT_EMPTY_REPORT(driver); 413 + EXPECT_REPORT(driver, (KC_C)); 414 + idle_for(TAPPING_TERM); 415 + VERIFY_AND_CLEAR(driver); 416 + 417 + // Release keys. 418 + EXPECT_EMPTY_REPORT(driver); 419 + layer_tap_key.release(); 420 + run_one_scan_loop(); 421 + mod_tap_key.release(); 422 + run_one_scan_loop(); 423 + VERIFY_AND_CLEAR(driver); 424 + // All mods are released. 425 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 426 + } 427 + 428 + // Test with layer tap and speculative mod tap keys on the same layer, trying a 429 + // nested press: 430 + // "LT down, MT down, (wait out tapping term), MT up, LT up." 431 + TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_same_layer_nested_press) { 432 + TestDriver driver; 433 + InSequence s; 434 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 435 + auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B)); 436 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 437 + 438 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 439 + 440 + EXPECT_NO_REPORT(driver); 441 + layer_tap_key.press(); 442 + run_one_scan_loop(); 443 + VERIFY_AND_CLEAR(driver); 444 + 445 + EXPECT_REPORT(driver, (KC_LCTL)); 446 + mod_tap_key.press(); 447 + run_one_scan_loop(); 448 + VERIFY_AND_CLEAR(driver); 449 + 450 + EXPECT_EMPTY_REPORT(driver); 451 + EXPECT_REPORT(driver, (KC_C)); 452 + idle_for(TAPPING_TERM); 453 + VERIFY_AND_CLEAR(driver); 454 + 455 + // Release keys: MT first, LT second. 456 + EXPECT_EMPTY_REPORT(driver); 457 + mod_tap_key.release(); 458 + run_one_scan_loop(); 459 + layer_tap_key.release(); 460 + run_one_scan_loop(); 461 + VERIFY_AND_CLEAR(driver); 462 + // All mods are released. 463 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 464 + } 465 + 466 + // Test with layer tap and speculative mod tap keys on the same layer, trying a 467 + // nested press with the MT first: 468 + // "MT down, LT down, (wait out tapping term), LT up, MT up." 469 + TEST_F(SpeculativeHoldRetroTappingTest, mt_lt_same_layer_nested_press) { 470 + TestDriver driver; 471 + InSequence s; 472 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 473 + auto mod_tap_key = KeymapKey(0, 1, 0, LCTL_T(KC_B)); 474 + auto regular_key = KeymapKey(1, 1, 0, KC_C); 475 + 476 + set_keymap({layer_tap_key, mod_tap_key, regular_key}); 477 + 478 + EXPECT_REPORT(driver, (KC_LCTL)); 479 + mod_tap_key.press(); 480 + run_one_scan_loop(); 481 + 482 + EXPECT_NO_REPORT(driver); 483 + layer_tap_key.press(); 484 + idle_for(TAPPING_TERM + 1); 485 + VERIFY_AND_CLEAR(driver); 486 + 487 + EXPECT_REPORT(driver, (KC_LCTL, KC_A)); 488 + EXPECT_REPORT(driver, (KC_LCTL)); 489 + EXPECT_EMPTY_REPORT(driver); 490 + layer_tap_key.release(); 491 + run_one_scan_loop(); 492 + mod_tap_key.release(); 493 + run_one_scan_loop(); 494 + VERIFY_AND_CLEAR(driver); 495 + // All mods are released. 496 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 497 + } 498 + 499 + // Test with a speculative mod tap key reached by a layer tap key, rolling from 500 + // LT to MT key: 501 + // "LT down, MT down, (wait out tapping term), LT up, MT up." 502 + TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_different_layer_roll) { 503 + TestDriver driver; 504 + InSequence s; 505 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 506 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 507 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 508 + auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C)); 509 + 510 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 511 + 512 + // Press layer tap key. 513 + EXPECT_NO_REPORT(driver); 514 + layer_tap_key.press(); 515 + run_one_scan_loop(); 516 + // Press mod tap key. 517 + mod_tap_key.press(); 518 + idle_for(TAPPING_TERM); 519 + VERIFY_AND_CLEAR(driver); 520 + 521 + // Release keys. 522 + EXPECT_REPORT(driver, (KC_LCTL)); 523 + layer_tap_key.release(); 524 + idle_for(TAPPING_TERM); 525 + VERIFY_AND_CLEAR(driver); 526 + 527 + EXPECT_EMPTY_REPORT(driver); 528 + mod_tap_key.release(); 529 + run_one_scan_loop(); 530 + VERIFY_AND_CLEAR(driver); 531 + // All mods are released. 532 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 533 + } 534 + 535 + // Test with a speculative mod tap key reached by a layer tap key, slowly 536 + // rolling from LT to MT key: 537 + // "LT down, (wait), MT down, (wait), LT up, MT up." 538 + TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_different_layer_slow_roll) { 539 + TestDriver driver; 540 + InSequence s; 541 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 542 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 543 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 544 + auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C)); 545 + 546 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 547 + 548 + EXPECT_REPORT(driver, (KC_LCTL)); 549 + layer_tap_key.press(); 550 + idle_for(TAPPING_TERM + 1); 551 + mod_tap_key.press(); 552 + run_one_scan_loop(); 553 + VERIFY_AND_CLEAR(driver); 554 + 555 + EXPECT_EMPTY_REPORT(driver); 556 + EXPECT_REPORT(driver, (KC_B)); 557 + EXPECT_EMPTY_REPORT(driver); 558 + layer_tap_key.release(); 559 + run_one_scan_loop(); 560 + mod_tap_key.release(); 561 + run_one_scan_loop(); 562 + VERIFY_AND_CLEAR(driver); 563 + // All mods are released. 564 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 565 + } 566 + 567 + // Test with a speculative mod tap key reached by a layer tap key, trying a 568 + // nested press: 569 + // "LT down, MT down, (wait out tapping term), MT up, LT up." 570 + TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_different_layer_nested_press) { 571 + TestDriver driver; 572 + InSequence s; 573 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 574 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 575 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 576 + auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C)); 577 + 578 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 579 + 580 + EXPECT_NO_REPORT(driver); 581 + layer_tap_key.press(); 582 + run_one_scan_loop(); 583 + mod_tap_key.press(); 584 + idle_for(TAPPING_TERM); 585 + VERIFY_AND_CLEAR(driver); 586 + 587 + // Release keys. 588 + EXPECT_REPORT(driver, (KC_LCTL)); 589 + EXPECT_EMPTY_REPORT(driver); 590 + mod_tap_key.release(); 591 + run_one_scan_loop(); 592 + layer_tap_key.release(); 593 + run_one_scan_loop(); 594 + VERIFY_AND_CLEAR(driver); 595 + // All mods are released. 596 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 597 + } 598 + 599 + // Test with a speculative mod tap key reached by a layer tap key, slowly making 600 + // a nested press from LT to MT key: 601 + // "LT down, (wait), MT down, MT up, LT up." 602 + TEST_F(SpeculativeHoldRetroTappingTest, lt_mt_different_layer_slow_nested_press) { 603 + TestDriver driver; 604 + InSequence s; 605 + auto layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A)); 606 + auto regular_key = KeymapKey(0, 1, 0, KC_B); 607 + auto placeholder_key = KeymapKey(1, 0, 0, KC_NO); 608 + auto mod_tap_key = KeymapKey(1, 1, 0, LCTL_T(KC_C)); 609 + 610 + set_keymap({layer_tap_key, regular_key, placeholder_key, mod_tap_key}); 611 + 612 + EXPECT_REPORT(driver, (KC_LCTL)); 613 + layer_tap_key.press(); 614 + idle_for(TAPPING_TERM + 1); 615 + mod_tap_key.press(); 616 + run_one_scan_loop(); 617 + VERIFY_AND_CLEAR(driver); 618 + 619 + EXPECT_EMPTY_REPORT(driver); 620 + EXPECT_REPORT(driver, (KC_C)); 621 + EXPECT_EMPTY_REPORT(driver); 622 + mod_tap_key.release(); 623 + run_one_scan_loop(); 624 + layer_tap_key.release(); 625 + run_one_scan_loop(); 626 + VERIFY_AND_CLEAR(driver); 627 + // All mods are released. 628 + EXPECT_EQ(get_mods() | get_speculative_mods(), 0); 629 + }