qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

process_caps_word.c (9834B)


      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 #include "process_auto_shift.h"
     17 #include "process_space_cadet.h"
     18 #include "caps_word.h"
     19 #include "keycodes.h"
     20 #include "quantum_keycodes.h"
     21 #include "modifiers.h"
     22 #include "timer.h"
     23 #include "action_tapping.h"
     24 #include "action_util.h"
     25 
     26 #ifdef CAPS_WORD_INVERT_ON_SHIFT
     27 static uint8_t held_mods = 0;
     28 
     29 static bool handle_shift(uint16_t keycode, keyrecord_t* record) {
     30     switch (keycode) {
     31         case OSM(MOD_LSFT):
     32             keycode = KC_LSFT;
     33             break;
     34         case OSM(MOD_RSFT):
     35             keycode = KC_RSFT;
     36             break;
     37 
     38 #    ifndef NO_ACTION_TAPPING
     39         case QK_MOD_TAP ... QK_MOD_TAP_MAX:
     40             if (record->tap.count == 0) { // Mod-tap key is held.
     41                 switch (QK_MOD_TAP_GET_MODS(keycode)) {
     42                     case MOD_LSFT:
     43                         keycode = KC_LSFT;
     44                         break;
     45                     case MOD_RSFT:
     46                         keycode = KC_RSFT;
     47                         break;
     48                 }
     49             }
     50 #    endif // NO_ACTION_TAPPING
     51     }
     52 
     53     if (keycode == KC_LSFT || keycode == KC_RSFT) {
     54         const uint8_t mod = MOD_BIT(keycode);
     55 
     56         if (is_caps_word_on()) {
     57             if (record->event.pressed) {
     58                 held_mods |= mod;
     59             } else {
     60                 held_mods &= ~mod;
     61             }
     62             return false;
     63         } else if ((held_mods & mod) != 0) {
     64             held_mods &= ~mod;
     65             del_mods(mod);
     66             return record->event.pressed;
     67         }
     68     }
     69 
     70     return true;
     71 }
     72 #endif // CAPS_WORD_INVERT_ON_SHIFT
     73 
     74 bool process_caps_word(uint16_t keycode, keyrecord_t* record) {
     75     if (keycode == QK_CAPS_WORD_TOGGLE) {
     76         if (record->event.pressed) {
     77             caps_word_toggle();
     78         }
     79         return false;
     80     }
     81 #ifdef CAPS_WORD_INVERT_ON_SHIFT
     82     if (!handle_shift(keycode, record)) {
     83         return false;
     84     }
     85 #endif // CAPS_WORD_INVERT_ON_SHIFT
     86 
     87 #ifndef NO_ACTION_ONESHOT
     88     const uint8_t mods = get_mods() | get_oneshot_mods();
     89 #else
     90     const uint8_t mods = get_mods();
     91 #endif // NO_ACTION_ONESHOT
     92 
     93     if (!is_caps_word_on()) {
     94         // The following optionally turns on Caps Word by holding left and
     95         // right shifts or by double tapping left shift. This way Caps Word
     96         // may be used without needing a dedicated key and also without
     97         // needing combos or tap dance.
     98 
     99 #ifdef BOTH_SHIFTS_TURNS_ON_CAPS_WORD
    100         // Many keyboards enable the Command feature by default, which also
    101         // uses left+right shift. It can be configured to use a different
    102         // key combination by defining IS_COMMAND(). We make a non-fatal
    103         // warning if Command is enabled but IS_COMMAND() is *not* defined.
    104 #    if defined(COMMAND_ENABLE) && !defined(IS_COMMAND)
    105 #        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)."
    106 #    else
    107         if (mods == MOD_MASK_SHIFT
    108 #        ifdef COMMAND_ENABLE
    109             // Don't activate Caps Word at the same time as Command.
    110             && !(IS_COMMAND())
    111 #        endif // COMMAND_ENABLE
    112         ) {
    113             caps_word_on();
    114 #        ifdef SPACE_CADET_ENABLE
    115             reset_space_cadet();
    116 #        endif // SPACE_CADET_ENABLE
    117         }
    118 #    endif     // defined(COMMAND_ENABLE) && !defined(IS_COMMAND)
    119 #endif         // BOTH_SHIFTS_TURNS_ON_CAPS_WORD
    120 
    121 #ifdef DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD
    122         // Double tapping left shift turns on Caps Word.
    123         //
    124         // NOTE: This works with KC_LSFT and one-shot left shift. It
    125         // wouldn't make sense with mod-tap or Space Cadet shift since
    126         // double tapping would of course trigger the tapping action.
    127         if (record->event.pressed) {
    128             static bool     tapped = false;
    129             static uint16_t timer  = 0;
    130             if (keycode == KC_LSFT || keycode == OSM(MOD_LSFT)) {
    131                 if (tapped && !timer_expired(record->event.time, timer)) {
    132                     // Left shift was double tapped, activate Caps Word.
    133                     caps_word_on();
    134                 }
    135                 tapped = true;
    136                 timer  = record->event.time + GET_TAPPING_TERM(keycode, record);
    137             } else {
    138                 tapped = false; // Reset when any other key is pressed.
    139             }
    140         }
    141 #endif // DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD
    142 
    143         return true;
    144     }
    145 
    146 #if CAPS_WORD_IDLE_TIMEOUT > 0
    147     caps_word_reset_idle_timer();
    148 #endif // CAPS_WORD_IDLE_TIMEOUT > 0
    149 
    150     // From here on, we only take action on press events.
    151     if (!record->event.pressed) {
    152         return true;
    153     }
    154 
    155     if (!(mods & ~(MOD_MASK_SHIFT | MOD_BIT(KC_RALT)))) {
    156         switch (keycode) {
    157             // Ignore MO, TO, TG, TT, and OSL layer switch keys.
    158             case QK_MOMENTARY ... QK_MOMENTARY_MAX:
    159             case QK_TO ... QK_TO_MAX:
    160             case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
    161             case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
    162             case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
    163 #ifdef TRI_LAYER_ENABLE // Ignore Tri Layer keys.
    164             case QK_TRI_LAYER_LOWER ... QK_TRI_LAYER_UPPER:
    165 #endif                   // TRI_LAYER_ENABLE
    166 #ifdef LAYER_LOCK_ENABLE // Ignore Layer Lock key.
    167             case QK_LAYER_LOCK:
    168 #endif // LAYER_LOCK_ENABLE
    169        // Ignore AltGr.
    170             case KC_RALT:
    171             case OSM(MOD_RALT):
    172                 return true;
    173 
    174 #ifndef NO_ACTION_TAPPING
    175             // Corresponding to mod keys above, a held mod-tap is handled as:
    176             // * For shift mods, pass KC_LSFT or KC_RSFT to
    177             //   caps_word_press_user() to determine whether to continue.
    178             // * For Shift + AltGr (MOD_RSFT | MOD_RALT), pass RSFT(KC_RALT).
    179             // * AltGr (MOD_RALT) is ignored.
    180             // * Otherwise stop Caps Word.
    181             case QK_MOD_TAP ... QK_MOD_TAP_MAX:
    182                 if (record->tap.count == 0) { // Mod-tap key is held.
    183                     const uint8_t mods = QK_MOD_TAP_GET_MODS(keycode);
    184                     switch (mods) {
    185 #    ifndef CAPS_WORD_INVERT_ON_SHIFT
    186                         case MOD_LSFT:
    187                             keycode = KC_LSFT;
    188                             break;
    189                         case MOD_RSFT:
    190                             keycode = KC_RSFT;
    191                             break;
    192 #    endif // CAPS_WORD_INVERT_ON_SHIFT
    193                         case MOD_RSFT | MOD_RALT:
    194                             keycode = RSFT(KC_RALT);
    195                             break;
    196                         case MOD_RALT:
    197                             return true;
    198                         default:
    199                             caps_word_off();
    200 #    ifdef CAPS_WORD_INVERT_ON_SHIFT
    201                             add_mods(held_mods);
    202 #    endif // CAPS_WORD_INVERT_ON_SHIFT
    203                             return true;
    204                     }
    205                 } else {
    206                     keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode);
    207                 }
    208                 break;
    209 
    210 #    ifndef NO_ACTION_LAYER
    211             case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
    212 #    endif // NO_ACTION_LAYER
    213                 if (record->tap.count == 0) {
    214                     return true;
    215                 }
    216                 keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);
    217                 break;
    218 #endif // NO_ACTION_TAPPING
    219 
    220 #ifdef SWAP_HANDS_ENABLE
    221             case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
    222                 // Note: IS_SWAP_HANDS_KEYCODE() actually tests for the special action keycodes like SH_TOGG, SH_TT, ...,
    223                 // which currently overlap the SH_T(kc) range.
    224                 if (IS_SWAP_HANDS_KEYCODE(keycode)
    225 #    ifndef NO_ACTION_TAPPING
    226                     || record->tap.count == 0
    227 #    endif // NO_ACTION_TAPPING
    228                 ) {
    229                     return true;
    230                 }
    231                 keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode);
    232                 break;
    233 #endif // SWAP_HANDS_ENABLE
    234         }
    235 
    236 #ifdef AUTO_SHIFT_ENABLE
    237         del_weak_mods(get_autoshift_state() ? ~MOD_BIT(KC_LSFT) : 0xff);
    238 #else
    239         clear_weak_mods();
    240 #endif // AUTO_SHIFT_ENABLE
    241         if (caps_word_press_user(keycode)) {
    242 #ifdef CAPS_WORD_INVERT_ON_SHIFT
    243             if (held_mods) {
    244                 set_weak_mods(get_weak_mods() ^ MOD_BIT(KC_LSFT));
    245             }
    246 #endif // CAPS_WORD_INVERT_ON_SHIFT
    247             send_keyboard_report();
    248             return true;
    249         }
    250     }
    251 
    252     caps_word_off();
    253 #ifdef CAPS_WORD_INVERT_ON_SHIFT
    254     add_mods(held_mods);
    255 #endif // CAPS_WORD_INVERT_ON_SHIFT
    256     return true;
    257 }
    258 
    259 __attribute__((weak)) bool caps_word_press_user(uint16_t keycode) {
    260     switch (keycode) {
    261         // Keycodes that continue Caps Word, with shift applied.
    262         case KC_A ... KC_Z:
    263         case KC_MINS:
    264             add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key.
    265             return true;
    266 
    267         // Keycodes that continue Caps Word, without shifting.
    268         case KC_1 ... KC_0:
    269         case KC_BSPC:
    270         case KC_DEL:
    271         case KC_UNDS:
    272             return true;
    273 
    274         default:
    275             return false; // Deactivate Caps Word.
    276     }
    277 }