diff options
| author | Pascal Getreuer <50221757+getreuer@users.noreply.github.com> | 2022-05-13 23:00:32 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-14 16:00:32 +1000 |
| commit | 68b16bba68d79c63bbd649a5be58756e937c96da (patch) | |
| tree | be0d3d672c13c080f4da58687b5b1a261f465911 /quantum/process_keycode/process_caps_word.c | |
| parent | 90eef4cd153cdc1b00d973e6abb029828f656294 (diff) | |
[Core] Add Caps Word feature to core (#16588)
Co-authored-by: precondition <57645186+precondition@users.noreply.github.com>
Co-authored-by: Drashna Jaelre <drashna@live.com>
Diffstat (limited to 'quantum/process_keycode/process_caps_word.c')
| -rw-r--r-- | quantum/process_keycode/process_caps_word.c | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/quantum/process_keycode/process_caps_word.c b/quantum/process_keycode/process_caps_word.c new file mode 100644 index 0000000000..15238f04a1 --- /dev/null +++ b/quantum/process_keycode/process_caps_word.c | |||
| @@ -0,0 +1,160 @@ | |||
| 1 | // Copyright 2021-2022 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 "process_caps_word.h" | ||
| 16 | |||
| 17 | bool process_caps_word(uint16_t keycode, keyrecord_t* record) { | ||
| 18 | if (keycode == CAPSWRD) { // Pressing CAPSWRD toggles Caps Word. | ||
| 19 | if (record->event.pressed) { | ||
| 20 | caps_word_toggle(); | ||
| 21 | } | ||
| 22 | return false; | ||
| 23 | } | ||
| 24 | |||
| 25 | #ifndef NO_ACTION_ONESHOT | ||
| 26 | const uint8_t mods = get_mods() | get_oneshot_mods(); | ||
| 27 | #else | ||
| 28 | const uint8_t mods = get_mods(); | ||
| 29 | #endif // NO_ACTION_ONESHOT | ||
| 30 | |||
| 31 | if (!is_caps_word_on()) { | ||
| 32 | // The following optionally turns on Caps Word by holding left and | ||
| 33 | // right shifts or by double tapping left shift. This way Caps Word | ||
| 34 | // may be used without needing a dedicated key and also without | ||
| 35 | // needing combos or tap dance. | ||
| 36 | |||
| 37 | #ifdef BOTH_SHIFTS_TURNS_ON_CAPS_WORD | ||
| 38 | // Many keyboards enable the Command feature by default, which also | ||
| 39 | // uses left+right shift. It can be configured to use a different | ||
| 40 | // key combination by defining IS_COMMAND(). We make a non-fatal | ||
| 41 | // warning if Command is enabled but IS_COMMAND() is *not* defined. | ||
| 42 | # if defined(COMMAND_ENABLE) && !defined(IS_COMMAND) | ||
| 43 | # pragma message "BOTH_SHIFTS_TURNS_ON_CAPS_WORD and Command should not be enabled at the same time, since both use the Left Shift + Right Shift key combination. Please disable Command, or ensure that `IS_COMMAND` is not set to (get_mods() == MOD_MASK_SHIFT)." | ||
| 44 | # else | ||
| 45 | if (mods == MOD_MASK_SHIFT | ||
| 46 | # ifdef COMMAND_ENABLE | ||
| 47 | // Don't activate Caps Word at the same time as Command. | ||
| 48 | && !(IS_COMMAND()) | ||
| 49 | # endif // COMMAND_ENABLE | ||
| 50 | ) { | ||
| 51 | caps_word_on(); | ||
| 52 | } | ||
| 53 | # endif // defined(COMMAND_ENABLE) && !defined(IS_COMMAND) | ||
| 54 | #endif // BOTH_SHIFTS_TURNS_ON_CAPS_WORD | ||
| 55 | |||
| 56 | #ifdef DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD | ||
| 57 | // Double tapping left shift turns on Caps Word. | ||
| 58 | // | ||
| 59 | // NOTE: This works with KC_LSFT and one-shot left shift. It | ||
| 60 | // wouldn't make sense with mod-tap or Space Cadet shift since | ||
| 61 | // double tapping would of course trigger the tapping action. | ||
| 62 | if (record->event.pressed) { | ||
| 63 | static bool tapped = false; | ||
| 64 | static uint16_t timer = 0; | ||
| 65 | if (keycode == KC_LSFT || keycode == OSM(MOD_LSFT)) { | ||
| 66 | if (tapped && !timer_expired(record->event.time, timer)) { | ||
| 67 | // Left shift was double tapped, activate Caps Word. | ||
| 68 | caps_word_on(); | ||
| 69 | } | ||
| 70 | tapped = true; | ||
| 71 | timer = record->event.time + GET_TAPPING_TERM(keycode, record); | ||
| 72 | } else { | ||
| 73 | tapped = false; // Reset when any other key is pressed. | ||
| 74 | } | ||
| 75 | } | ||
| 76 | #endif // DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD | ||
| 77 | |||
| 78 | return true; | ||
| 79 | } | ||
| 80 | |||
| 81 | #if CAPS_WORD_IDLE_TIMEOUT > 0 | ||
| 82 | caps_word_reset_idle_timer(); | ||
| 83 | #endif // CAPS_WORD_IDLE_TIMEOUT > 0 | ||
| 84 | |||
| 85 | // From here on, we only take action on press events. | ||
| 86 | if (!record->event.pressed) { | ||
| 87 | return true; | ||
| 88 | } | ||
| 89 | |||
| 90 | if (!(mods & ~MOD_MASK_SHIFT)) { | ||
| 91 | switch (keycode) { | ||
| 92 | // Ignore MO, TO, TG, TT, and OSL layer switch keys. | ||
| 93 | case QK_MOMENTARY ... QK_MOMENTARY_MAX: | ||
| 94 | case QK_TO ... QK_TO_MAX: | ||
| 95 | case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX: | ||
| 96 | case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: | ||
| 97 | case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: | ||
| 98 | return true; | ||
| 99 | |||
| 100 | #ifndef NO_ACTION_TAPPING | ||
| 101 | case QK_MOD_TAP ... QK_MOD_TAP_MAX: | ||
| 102 | if (record->tap.count == 0) { | ||
| 103 | // Deactivate if a mod becomes active through holding | ||
| 104 | // a mod-tap key. | ||
| 105 | caps_word_off(); | ||
| 106 | return true; | ||
| 107 | } | ||
| 108 | keycode &= 0xff; | ||
| 109 | break; | ||
| 110 | |||
| 111 | # ifndef NO_ACTION_LAYER | ||
| 112 | case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: | ||
| 113 | # endif // NO_ACTION_LAYER | ||
| 114 | if (record->tap.count == 0) { | ||
| 115 | return true; | ||
| 116 | } | ||
| 117 | keycode &= 0xff; | ||
| 118 | break; | ||
| 119 | #endif // NO_ACTION_TAPPING | ||
| 120 | |||
| 121 | #ifdef SWAP_HANDS_ENABLE | ||
| 122 | case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: | ||
| 123 | if (keycode > 0x56F0 || record->tap.count == 0) { | ||
| 124 | return true; | ||
| 125 | } | ||
| 126 | keycode &= 0xff; | ||
| 127 | break; | ||
| 128 | #endif // SWAP_HANDS_ENABLE | ||
| 129 | } | ||
| 130 | |||
| 131 | clear_weak_mods(); | ||
| 132 | if (caps_word_press_user(keycode)) { | ||
| 133 | send_keyboard_report(); | ||
| 134 | return true; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | caps_word_off(); | ||
| 139 | return true; | ||
| 140 | } | ||
| 141 | |||
| 142 | __attribute__((weak)) bool caps_word_press_user(uint16_t keycode) { | ||
| 143 | switch (keycode) { | ||
| 144 | // Keycodes that continue Caps Word, with shift applied. | ||
| 145 | case KC_A ... KC_Z: | ||
| 146 | case KC_MINS: | ||
| 147 | add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key. | ||
| 148 | return true; | ||
| 149 | |||
| 150 | // Keycodes that continue Caps Word, without shifting. | ||
| 151 | case KC_1 ... KC_0: | ||
| 152 | case KC_BSPC: | ||
| 153 | case KC_DEL: | ||
| 154 | case KC_UNDS: | ||
| 155 | return true; | ||
| 156 | |||
| 157 | default: | ||
| 158 | return false; // Deactivate Caps Word. | ||
| 159 | } | ||
| 160 | } | ||
