diff options
| -rw-r--r-- | quantum/process_keycode/process_caps_word.c | 32 | ||||
| -rw-r--r-- | tests/caps_word/test_caps_word.cpp | 159 | 
2 files changed, 185 insertions, 6 deletions
| diff --git a/quantum/process_keycode/process_caps_word.c b/quantum/process_keycode/process_caps_word.c index bc4369846c..1b9583196d 100644 --- a/quantum/process_keycode/process_caps_word.c +++ b/quantum/process_keycode/process_caps_word.c @@ -101,14 +101,34 @@ bool process_caps_word(uint16_t keycode, keyrecord_t* record) {                  return true;  #ifndef NO_ACTION_TAPPING +            // Corresponding to mod keys above, a held mod-tap is handled as: +            // * For shift mods, pass KC_LSFT or KC_RSFT to +            //   caps_word_press_user() to determine whether to continue. +            // * For Shift + AltGr (MOD_RSFT | MOD_RALT), pass RSFT(KC_RALT). +            // * AltGr (MOD_RALT) is ignored. +            // * Otherwise stop Caps Word.              case QK_MOD_TAP ... QK_MOD_TAP_MAX: -                if (record->tap.count == 0) { -                    // Deactivate if a mod becomes active through holding -                    // a mod-tap key. -                    caps_word_off(); -                    return true; +                if (record->tap.count == 0) { // Mod-tap key is held. +                    const uint8_t mods = (keycode >> 8) & 0x1f; +                    switch (mods) { +                        case MOD_LSFT: +                            keycode = KC_LSFT; +                            break; +                        case MOD_RSFT: +                            keycode = KC_RSFT; +                            break; +                        case MOD_RSFT | MOD_RALT: +                            keycode = RSFT(KC_RALT); +                            break; +                        case MOD_RALT: +                            return true; +                        default: +                            caps_word_off(); +                            return true; +                    } +                } else { +                    keycode &= 0xff;                  } -                keycode &= 0xff;                  break;  #    ifndef NO_ACTION_LAYER diff --git a/tests/caps_word/test_caps_word.cpp b/tests/caps_word/test_caps_word.cpp index 0af4b0175d..3f59ed3744 100644 --- a/tests/caps_word/test_caps_word.cpp +++ b/tests/caps_word/test_caps_word.cpp @@ -25,10 +25,47 @@ using ::testing::AnyOf;  using ::testing::InSequence;  using ::testing::TestParamInfo; +namespace { + +bool press_user_default(uint16_t keycode) { +    switch (keycode) { +        // Keycodes that continue Caps Word, with shift applied. +        case KC_A ... KC_Z: +        case KC_MINS: +            add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key. +            return true; + +        // Keycodes that continue Caps Word, without shifting. +        case KC_1 ... KC_0: +        case KC_BSPC: +        case KC_DEL: +        case KC_UNDS: +            return true; + +        default: +            return false; // Deactivate Caps Word. +    } +} + +uint16_t passed_keycode; +bool     press_user_save_passed_keycode(uint16_t keycode) { +    passed_keycode = keycode; +    return true; +} + +bool (*press_user_fun)(uint16_t) = press_user_default; + +extern "C" { +bool caps_word_press_user(uint16_t keycode) { +    return press_user_fun(keycode); +} +} // extern "C" +  class CapsWord : public TestFixture {     public:      void SetUp() override {          caps_word_off(); +        press_user_fun = press_user_default;      }  }; @@ -226,6 +263,126 @@ TEST_F(CapsWord, ShiftsAltGrSymbols) {      testing::Mock::VerifyAndClearExpectations(&driver);  } +// Tests typing "AltGr + A" using a mod-tap key. +TEST_F(CapsWord, ShiftsModTapAltGrSymbols) { +    TestDriver driver; +    KeymapKey  key_a(0, 0, 0, KC_A); +    KeymapKey  key_altgr_t(0, 1, 0, RALT_T(KC_B)); +    set_keymap({key_a, key_altgr_t}); + +    // Allow any number of reports with no keys or only modifiers. +    // clang-format off +    EXPECT_CALL(driver, send_keyboard_mock(AnyOf( +                KeyboardReport(), +                KeyboardReport(KC_RALT), +                KeyboardReport(KC_LSFT, KC_RALT)))) +        .Times(AnyNumber()); +    // Expect "Shift + AltGr + A". +    EXPECT_REPORT(driver, (KC_LSFT, KC_RALT, KC_A)); +    // clang-format on + +    // Turn on Caps Word and type "AltGr + A". +    caps_word_on(); + +    key_altgr_t.press(); +    idle_for(TAPPING_TERM + 1); +    tap_key(key_a); +    run_one_scan_loop(); +    key_altgr_t.release(); + +    EXPECT_TRUE(is_caps_word_on()); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +struct CapsWordPressUserParams { +    std::string name; +    uint16_t    keycode; +    uint16_t    delay_ms; +    uint16_t    expected_passed_keycode; +    bool        continues_caps_word; + +    static const std::string& GetName(const TestParamInfo<CapsWordPressUserParams>& info) { +        return info.param.name; +    } +}; + +class CapsWordPressUser : public ::testing::WithParamInterface<CapsWordPressUserParams>, public CapsWord { +    void SetUp() override { +        caps_word_on(); +        passed_keycode = KC_NO; +        press_user_fun = press_user_save_passed_keycode; +    } +}; + +// Tests keycodes passed to caps_word_press_user() function for various keys. +TEST_P(CapsWordPressUser, KeyCode) { +    TestDriver driver; +    KeymapKey  key(0, 0, 0, GetParam().keycode); +    set_keymap({key}); + +    EXPECT_ANY_REPORT(driver).Times(AnyNumber()); +    tap_key(key, GetParam().delay_ms); + +    EXPECT_EQ(passed_keycode, GetParam().expected_passed_keycode); +    EXPECT_EQ(is_caps_word_on(), GetParam().continues_caps_word); +    clear_oneshot_mods(); +    testing::Mock::VerifyAndClearExpectations(&driver); +} + +const uint16_t LT_1_KC_A = LT(1, KC_A); +// clang-format off +INSTANTIATE_TEST_CASE_P( +    PressUser, +    CapsWordPressUser, +    ::testing::Values( +        CapsWordPressUserParams{ +            "KC_A", KC_A, 1, KC_A, true}, +        CapsWordPressUserParams{ +            "KC_HASH", KC_HASH, 1, KC_HASH, true}, +        CapsWordPressUserParams{ +            "KC_LSFT", KC_LSFT, 1, KC_LSFT, true}, +        CapsWordPressUserParams{ +            "KC_RSFT", KC_RSFT, 1, KC_RSFT, true}, +        CapsWordPressUserParams{ +            "LSFT_T_tapped", LSFT_T(KC_A), 1, KC_A, true}, +        CapsWordPressUserParams{ +            "LSFT_T_held", LSFT_T(KC_A), TAPPING_TERM + 1, KC_LSFT, true}, +        CapsWordPressUserParams{ +            "RSFT_T_held", RSFT_T(KC_A), TAPPING_TERM + 1, KC_RSFT, true}, +        CapsWordPressUserParams{ +            "RSA_T_held", RSA_T(KC_A), TAPPING_TERM + 1, RSFT(KC_RALT), true}, +        // Holding a mod-tap other than Shift or AltGr stops Caps Word. +        CapsWordPressUserParams{ +            "LCTL_T_held", LCTL_T(KC_A), TAPPING_TERM + 1, KC_NO, false}, +        CapsWordPressUserParams{ +            "LALT_T_held", LALT_T(KC_A), TAPPING_TERM + 1, KC_NO, false}, +        CapsWordPressUserParams{ +            "LGUI_T_held", LGUI_T(KC_A), TAPPING_TERM + 1, KC_NO, false}, +        // Layer keys are ignored and continue Caps Word. +        CapsWordPressUserParams{ +            "MO", MO(1), 1, KC_NO, true}, +        CapsWordPressUserParams{ +            "TO", TO(1), 1, KC_NO, true}, +        CapsWordPressUserParams{ +            "TG", TG(1), 1, KC_NO, true}, +        CapsWordPressUserParams{ +            "TT", TT(1), 1, KC_NO, true}, +        CapsWordPressUserParams{ +            "OSL", OSL(1), 1, KC_NO, true}, +        CapsWordPressUserParams{ +            "LT_held", LT_1_KC_A, TAPPING_TERM + 1, KC_NO, true}, +        // AltGr keys are ignored and continue Caps Word. +        CapsWordPressUserParams{ +            "KC_RALT", KC_RALT, 1, KC_NO, true}, +        CapsWordPressUserParams{ +            "OSM_MOD_RALT", OSM(MOD_RALT), 1, KC_NO, true}, +        CapsWordPressUserParams{ +            "RALT_T_held", RALT_T(KC_A), TAPPING_TERM + 1, KC_NO, true} +        ), +    CapsWordPressUserParams::GetName +    ); +// clang-format on +  struct CapsWordBothShiftsParams {      std::string name;      uint16_t    left_shift_keycode; @@ -435,3 +592,5 @@ INSTANTIATE_TEST_CASE_P(      CapsWordDoubleTapShiftParams::GetName      );  // clang-format on + +} // namespace | 
