test_tap_hold.cpp (11024B)
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_mods(uint16_t keycode, keyrecord_t *record) { 37 return true; // Enable Speculative Hold for all mod-tap keys. 38 } 39 40 // Indirection so that get_speculative_hold() can be 41 // replaced with other functions in the test cases below. 42 std::function<bool(uint16_t, keyrecord_t *)> get_speculative_hold_fun = get_speculative_hold_all_mods; 43 44 extern "C" bool get_speculative_hold(uint16_t keycode, keyrecord_t *record) { 45 return get_speculative_hold_fun(keycode, record); 46 } 47 48 class SpeculativeHoldAllMods : public TestFixture { 49 public: 50 void SetUp() override { 51 get_speculative_hold_fun = get_speculative_hold_all_mods; 52 } 53 }; 54 55 TEST_F(SpeculativeHoldAllMods, tap_mod_tap_neutralized) { 56 TestDriver driver; 57 InSequence s; 58 auto mod_tap_key = KeymapKey(0, 1, 0, GUI_T(KC_P)); 59 60 set_keymap({mod_tap_key}); 61 62 // Press mod-tap-hold key. Mod is held speculatively. 63 EXPECT_REPORT(driver, (KC_LGUI)); 64 mod_tap_key.press(); 65 idle_for(10); 66 VERIFY_AND_CLEAR(driver); 67 68 // Release mod-tap-hold key. Speculative mod is neutralized and canceled. 69 EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 70 EXPECT_REPORT(driver, (KC_LGUI)); 71 EXPECT_EMPTY_REPORT(driver); 72 EXPECT_REPORT(driver, (KC_P)); 73 EXPECT_EMPTY_REPORT(driver); 74 mod_tap_key.release(); 75 run_one_scan_loop(); 76 VERIFY_AND_CLEAR(driver); 77 78 // Idle for tapping term of mod tap hold key. 79 idle_for(TAPPING_TERM - 10); 80 VERIFY_AND_CLEAR(driver); 81 } 82 83 TEST_F(SpeculativeHoldAllMods, hold_two_mod_taps) { 84 TestDriver driver; 85 InSequence s; 86 auto mod_tap_key1 = KeymapKey(0, 1, 0, LCTL_T(KC_A)); 87 auto mod_tap_key2 = KeymapKey(0, 2, 0, RALT_T(KC_B)); 88 89 set_keymap({mod_tap_key1, mod_tap_key2}); 90 91 // Press first mod-tap key. 92 EXPECT_REPORT(driver, (KC_LCTL)); 93 mod_tap_key1.press(); 94 run_one_scan_loop(); 95 VERIFY_AND_CLEAR(driver); 96 EXPECT_EQ(get_speculative_mods(), MOD_BIT_LCTRL); 97 98 // Press second mod-tap key. 99 EXPECT_REPORT(driver, (KC_LCTL, KC_RALT)); 100 mod_tap_key2.press(); 101 run_one_scan_loop(); 102 VERIFY_AND_CLEAR(driver); 103 EXPECT_EQ(get_speculative_mods(), MOD_BIT_LCTRL | MOD_BIT_RALT); 104 105 EXPECT_NO_REPORT(driver); 106 idle_for(TAPPING_TERM + 1); 107 VERIFY_AND_CLEAR(driver); 108 EXPECT_EQ(get_speculative_mods(), 0); 109 EXPECT_EQ(get_mods(), MOD_BIT_LCTRL | MOD_BIT_RALT); 110 111 // Release first mod-tap key. 112 EXPECT_REPORT(driver, (KC_RALT)); 113 mod_tap_key1.release(); 114 run_one_scan_loop(); 115 VERIFY_AND_CLEAR(driver); 116 117 // Release second mod-tap key. 118 EXPECT_EMPTY_REPORT(driver); 119 mod_tap_key2.release(); 120 run_one_scan_loop(); 121 VERIFY_AND_CLEAR(driver); 122 } 123 124 TEST_F(SpeculativeHoldAllMods, two_mod_taps_same_mods) { 125 TestDriver driver; 126 InSequence s; 127 auto mod_tap_key1 = KeymapKey(0, 1, 0, GUI_T(KC_A)); 128 auto mod_tap_key2 = KeymapKey(0, 2, 0, GUI_T(KC_B)); 129 130 set_keymap({mod_tap_key1, mod_tap_key2}); 131 132 // Press first mod-tap key. 133 EXPECT_REPORT(driver, (KC_LGUI)); 134 mod_tap_key1.press(); 135 run_one_scan_loop(); 136 VERIFY_AND_CLEAR(driver); 137 138 // Tap second mod-tap key. 139 EXPECT_NO_REPORT(driver); 140 mod_tap_key2.press(); 141 run_one_scan_loop(); 142 mod_tap_key2.release(); 143 run_one_scan_loop(); 144 VERIFY_AND_CLEAR(driver); 145 146 // Release first mod-tap key. 147 EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 148 EXPECT_REPORT(driver, (KC_LGUI)); 149 EXPECT_EMPTY_REPORT(driver); 150 EXPECT_REPORT(driver, (KC_A)); 151 EXPECT_REPORT(driver, (KC_A, KC_B)); 152 EXPECT_REPORT(driver, (KC_A)); 153 EXPECT_EMPTY_REPORT(driver); 154 mod_tap_key1.release(); 155 run_one_scan_loop(); 156 VERIFY_AND_CLEAR(driver); 157 } 158 159 TEST_F(SpeculativeHoldAllMods, respects_get_speculative_hold_callback) { 160 TestDriver driver; 161 InSequence s; 162 auto mod_tap_key1 = KeymapKey(0, 0, 0, LSFT_T(KC_A)); 163 auto mod_tap_key2 = KeymapKey(0, 1, 0, LSFT_T(KC_B)); 164 auto mod_tap_key3 = KeymapKey(0, 2, 0, LCTL_T(KC_C)); 165 auto mod_tap_key4 = KeymapKey(0, 3, 0, LCTL_T(KC_D)); 166 auto mod_tap_key5 = KeymapKey(0, 4, 0, RSFT_T(KC_E)); 167 auto mod_tap_key6 = KeymapKey(0, 5, 0, RSFT_T(KC_F)); 168 169 set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3, mod_tap_key4, mod_tap_key5, mod_tap_key6}); 170 171 // Enable Speculative Hold selectively for some of the keys. 172 get_speculative_hold_fun = [](uint16_t keycode, keyrecord_t *record) { 173 switch (keycode) { 174 case LSFT_T(KC_B): 175 case LCTL_T(KC_D): 176 case RSFT_T(KC_F): 177 return true; 178 } 179 return false; 180 }; 181 182 for (KeymapKey *mod_tap_key : {&mod_tap_key2, &mod_tap_key4, &mod_tap_key6}) { 183 SCOPED_TRACE(std::string("mod_tap_key = ") + mod_tap_key->name); 184 const uint8_t mods = unpack_mod_tap_mods(mod_tap_key->code); 185 186 // Long press and release mod_tap_key. 187 // For these keys where Speculative Hold is enabled, then the mod should 188 // activate immediately on keydown. 189 EXPECT_REPORT(driver, (KC_LCTL + biton(mods))); 190 mod_tap_key->press(); 191 run_one_scan_loop(); 192 EXPECT_EQ(get_speculative_mods(), mods); 193 EXPECT_EQ(get_mods(), 0); 194 VERIFY_AND_CLEAR(driver); 195 196 EXPECT_NO_REPORT(driver); 197 idle_for(TAPPING_TERM + 1); 198 EXPECT_EQ(get_speculative_mods(), 0); 199 EXPECT_EQ(get_mods(), mods); 200 VERIFY_AND_CLEAR(driver); 201 202 EXPECT_EMPTY_REPORT(driver); 203 mod_tap_key->release(); 204 idle_for(TAPPING_TERM + 1); 205 EXPECT_EQ(get_speculative_mods(), 0); 206 EXPECT_EQ(get_mods(), 0); 207 VERIFY_AND_CLEAR(driver); 208 } 209 210 for (KeymapKey *mod_tap_key : {&mod_tap_key1, &mod_tap_key3, &mod_tap_key5}) { 211 SCOPED_TRACE(std::string("mod_tap_key = ") + mod_tap_key->name); 212 const uint8_t mods = unpack_mod_tap_mods(mod_tap_key->code); 213 214 // Long press and release mod_tap_key. 215 // For these keys where Speculative Hold is disabled, the mod should 216 // activate when the key has settled after the tapping term. 217 EXPECT_NO_REPORT(driver); 218 mod_tap_key->press(); 219 run_one_scan_loop(); 220 EXPECT_EQ(get_speculative_mods(), 0); 221 EXPECT_EQ(get_mods(), 0); 222 VERIFY_AND_CLEAR(driver); 223 224 EXPECT_REPORT(driver, (KC_LCTL + biton(mods))); 225 idle_for(TAPPING_TERM + 1); 226 EXPECT_EQ(get_speculative_mods(), 0); 227 EXPECT_EQ(get_mods(), mods); 228 VERIFY_AND_CLEAR(driver); 229 230 EXPECT_EMPTY_REPORT(driver); 231 mod_tap_key->release(); 232 idle_for(TAPPING_TERM + 1); 233 EXPECT_EQ(get_speculative_mods(), 0); 234 EXPECT_EQ(get_mods(), 0); 235 VERIFY_AND_CLEAR(driver); 236 } 237 } 238 239 TEST_F(SpeculativeHoldAllMods, respects_magic_mod_config) { 240 TestDriver driver; 241 InSequence s; 242 auto mod_tap_key = KeymapKey(0, 1, 0, CTL_T(KC_P)); 243 244 set_keymap({mod_tap_key}); 245 246 keymap_config.swap_lctl_lgui = true; 247 248 // Press mod-tap-hold key. 249 EXPECT_REPORT(driver, (KC_LGUI)); 250 mod_tap_key.press(); 251 run_one_scan_loop(); 252 VERIFY_AND_CLEAR(driver); 253 254 // Release mod-tap-hold key. 255 EXPECT_REPORT(driver, (KC_LGUI, DUMMY_MOD_NEUTRALIZER_KEYCODE)); 256 EXPECT_REPORT(driver, (KC_LGUI)); 257 EXPECT_EMPTY_REPORT(driver); 258 EXPECT_REPORT(driver, (KC_P)); 259 EXPECT_EMPTY_REPORT(driver); 260 mod_tap_key.release(); 261 idle_for(TAPPING_TERM + 1); 262 VERIFY_AND_CLEAR(driver); 263 264 keymap_config.swap_lctl_lgui = false; 265 266 // Press mod-tap-hold key. 267 EXPECT_REPORT(driver, (KC_LCTL)); 268 mod_tap_key.press(); 269 run_one_scan_loop(); 270 VERIFY_AND_CLEAR(driver); 271 272 // Release mod-tap-hold key. 273 EXPECT_EMPTY_REPORT(driver); 274 EXPECT_REPORT(driver, (KC_P)); 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(SpeculativeHoldAllMods, tap_a_mod_tap_key_while_another_mod_tap_key_is_held) { 282 TestDriver driver; 283 InSequence s; 284 auto first_mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); 285 auto second_mod_tap_key = KeymapKey(0, 2, 0, RSFT_T(KC_A)); 286 287 set_keymap({first_mod_tap_key, second_mod_tap_key}); 288 289 // Press first mod-tap-hold key. 290 EXPECT_REPORT(driver, (KC_LSFT)); 291 first_mod_tap_key.press(); 292 run_one_scan_loop(); 293 VERIFY_AND_CLEAR(driver); 294 295 // Press second tap-hold key. 296 EXPECT_REPORT(driver, (KC_LSFT, KC_RSFT)); 297 second_mod_tap_key.press(); 298 run_one_scan_loop(); 299 VERIFY_AND_CLEAR(driver); 300 301 // Release second tap-hold key. 302 EXPECT_NO_REPORT(driver); 303 second_mod_tap_key.release(); 304 run_one_scan_loop(); 305 VERIFY_AND_CLEAR(driver); 306 307 // Release first mod-tap-hold key. 308 EXPECT_EMPTY_REPORT(driver); 309 EXPECT_REPORT(driver, (KC_P)); 310 EXPECT_REPORT(driver, (KC_P, KC_A)); 311 EXPECT_REPORT(driver, (KC_P)); 312 EXPECT_EMPTY_REPORT(driver); 313 first_mod_tap_key.release(); 314 run_one_scan_loop(); 315 VERIFY_AND_CLEAR(driver); 316 } 317 318 TEST_F(SpeculativeHoldAllMods, tap_mod_tap_key_two_times) { 319 TestDriver driver; 320 InSequence s; 321 auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P)); 322 323 set_keymap({mod_tap_key}); 324 325 // Press mod-tap-hold key. 326 EXPECT_REPORT(driver, (KC_LSFT)); 327 mod_tap_key.press(); 328 run_one_scan_loop(); 329 VERIFY_AND_CLEAR(driver); 330 331 // Release mod-tap-hold key. 332 EXPECT_EMPTY_REPORT(driver); 333 EXPECT_REPORT(driver, (KC_P)); 334 EXPECT_EMPTY_REPORT(driver); 335 mod_tap_key.release(); 336 run_one_scan_loop(); 337 VERIFY_AND_CLEAR(driver); 338 339 // Press mod-tap-hold key again. 340 EXPECT_REPORT(driver, (KC_P)); 341 mod_tap_key.press(); 342 idle_for(TAPPING_TERM); 343 VERIFY_AND_CLEAR(driver); 344 345 // Release mod-tap-hold key. 346 EXPECT_EMPTY_REPORT(driver); 347 mod_tap_key.release(); 348 run_one_scan_loop(); 349 VERIFY_AND_CLEAR(driver); 350 } 351 352 } // namespace