From 68b16bba68d79c63bbd649a5be58756e937c96da Mon Sep 17 00:00:00 2001 From: Pascal Getreuer <50221757+getreuer@users.noreply.github.com> Date: Fri, 13 May 2022 23:00:32 -0700 Subject: [Core] Add Caps Word feature to core (#16588) Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> Co-authored-by: Drashna Jaelre --- quantum/process_keycode/process_caps_word.c | 160 ++++++++++++++++++++++++++++ quantum/process_keycode/process_caps_word.h | 37 +++++++ 2 files changed, 197 insertions(+) create mode 100644 quantum/process_keycode/process_caps_word.c create mode 100644 quantum/process_keycode/process_caps_word.h (limited to 'quantum/process_keycode') 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 @@ +// Copyright 2021-2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "process_caps_word.h" + +bool process_caps_word(uint16_t keycode, keyrecord_t* record) { + if (keycode == CAPSWRD) { // Pressing CAPSWRD toggles Caps Word. + if (record->event.pressed) { + caps_word_toggle(); + } + return false; + } + +#ifndef NO_ACTION_ONESHOT + const uint8_t mods = get_mods() | get_oneshot_mods(); +#else + const uint8_t mods = get_mods(); +#endif // NO_ACTION_ONESHOT + + if (!is_caps_word_on()) { + // The following optionally turns on Caps Word by holding left and + // right shifts or by double tapping left shift. This way Caps Word + // may be used without needing a dedicated key and also without + // needing combos or tap dance. + +#ifdef BOTH_SHIFTS_TURNS_ON_CAPS_WORD + // Many keyboards enable the Command feature by default, which also + // uses left+right shift. It can be configured to use a different + // key combination by defining IS_COMMAND(). We make a non-fatal + // warning if Command is enabled but IS_COMMAND() is *not* defined. +# if defined(COMMAND_ENABLE) && !defined(IS_COMMAND) +# 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)." +# else + if (mods == MOD_MASK_SHIFT +# ifdef COMMAND_ENABLE + // Don't activate Caps Word at the same time as Command. + && !(IS_COMMAND()) +# endif // COMMAND_ENABLE + ) { + caps_word_on(); + } +# endif // defined(COMMAND_ENABLE) && !defined(IS_COMMAND) +#endif // BOTH_SHIFTS_TURNS_ON_CAPS_WORD + +#ifdef DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD + // Double tapping left shift turns on Caps Word. + // + // NOTE: This works with KC_LSFT and one-shot left shift. It + // wouldn't make sense with mod-tap or Space Cadet shift since + // double tapping would of course trigger the tapping action. + if (record->event.pressed) { + static bool tapped = false; + static uint16_t timer = 0; + if (keycode == KC_LSFT || keycode == OSM(MOD_LSFT)) { + if (tapped && !timer_expired(record->event.time, timer)) { + // Left shift was double tapped, activate Caps Word. + caps_word_on(); + } + tapped = true; + timer = record->event.time + GET_TAPPING_TERM(keycode, record); + } else { + tapped = false; // Reset when any other key is pressed. + } + } +#endif // DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD + + return true; + } + +#if CAPS_WORD_IDLE_TIMEOUT > 0 + caps_word_reset_idle_timer(); +#endif // CAPS_WORD_IDLE_TIMEOUT > 0 + + // From here on, we only take action on press events. + if (!record->event.pressed) { + return true; + } + + if (!(mods & ~MOD_MASK_SHIFT)) { + switch (keycode) { + // Ignore MO, TO, TG, TT, and OSL layer switch keys. + case QK_MOMENTARY ... QK_MOMENTARY_MAX: + case QK_TO ... QK_TO_MAX: + case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX: + case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: + case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: + return true; + +#ifndef NO_ACTION_TAPPING + 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; + } + keycode &= 0xff; + break; + +# ifndef NO_ACTION_LAYER + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: +# endif // NO_ACTION_LAYER + if (record->tap.count == 0) { + return true; + } + keycode &= 0xff; + break; +#endif // NO_ACTION_TAPPING + +#ifdef SWAP_HANDS_ENABLE + case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: + if (keycode > 0x56F0 || record->tap.count == 0) { + return true; + } + keycode &= 0xff; + break; +#endif // SWAP_HANDS_ENABLE + } + + clear_weak_mods(); + if (caps_word_press_user(keycode)) { + send_keyboard_report(); + return true; + } + } + + caps_word_off(); + return true; +} + +__attribute__((weak)) bool caps_word_press_user(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. + } +} diff --git a/quantum/process_keycode/process_caps_word.h b/quantum/process_keycode/process_caps_word.h new file mode 100644 index 0000000000..f215bbc3a3 --- /dev/null +++ b/quantum/process_keycode/process_caps_word.h @@ -0,0 +1,37 @@ +// Copyright 2021-2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "quantum.h" +#include "caps_word.h" + +/** + * @brief Process handler for Caps Word feature. + * + * @param keycode Keycode registered by matrix press, per keymap + * @param record keyrecord_t structure + * @return true Continue processing keycodes, and send to host + * @return false Stop processing keycodes, and don't send to host + */ +bool process_caps_word(uint16_t keycode, keyrecord_t* record); + +/** + * @brief Weak function for user-level Caps Word press modification. + * + * @param keycode Keycode registered by matrix press, per keymap + * @return true Continue Caps Word + * @return false Stop Caps Word + */ +bool caps_word_press_user(uint16_t keycode); -- cgit v1.2.3