qmk_firmware

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

repeat_key.c (10597B)


      1 // Copyright 2022-2023 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 "repeat_key.h"
     16 #include "quantum_keycodes.h"
     17 
     18 // Variables saving the state of the last key press.
     19 static keyrecord_t last_record = {0};
     20 static uint8_t     last_mods   = 0;
     21 // Signed count of the number of times the last key has been repeated or
     22 // alternate repeated: it is 0 when a key is pressed normally, positive when
     23 // repeated, and negative when alternate repeated.
     24 static int8_t last_repeat_count = 0;
     25 // The repeat_count, but set to 0 outside of repeat_key_invoke() so that it is
     26 // nonzero only while a repeated key is being processed.
     27 static int8_t processing_repeat_count = 0;
     28 
     29 uint16_t get_last_keycode(void) {
     30     return last_record.keycode;
     31 }
     32 
     33 uint8_t get_last_mods(void) {
     34     return last_mods;
     35 }
     36 
     37 void set_last_keycode(uint16_t keycode) {
     38     set_last_record(keycode, &(keyrecord_t){
     39 #ifndef NO_ACTION_TAPPING
     40                                  .tap.interrupted = false,
     41                                  .tap.count       = 1,
     42 #endif
     43                              });
     44 }
     45 
     46 void set_last_mods(uint8_t mods) {
     47     last_mods = mods;
     48 }
     49 
     50 void set_last_record(uint16_t keycode, keyrecord_t* record) {
     51     last_record         = *record;
     52     last_record.keycode = keycode;
     53     last_repeat_count   = 0;
     54 }
     55 
     56 /** @brief Updates `last_repeat_count` in direction `dir`. */
     57 static void update_last_repeat_count(int8_t dir) {
     58     if (dir * last_repeat_count < 0) {
     59         last_repeat_count = dir;
     60     } else if (dir * last_repeat_count < 127) {
     61         last_repeat_count += dir;
     62     }
     63 }
     64 
     65 int8_t get_repeat_key_count(void) {
     66     return processing_repeat_count;
     67 }
     68 
     69 void repeat_key_invoke(const keyevent_t* event) {
     70     // It is possible (e.g. in rolled presses) that the last key changes while
     71     // the Repeat Key is pressed. To prevent stuck keys, it is important to
     72     // remember separately what key record was processed on press so that the
     73     // the corresponding record is generated on release.
     74     static keyrecord_t registered_record       = {0};
     75     static int8_t      registered_repeat_count = 0;
     76     // Since this function calls process_record(), it may recursively call
     77     // itself. We return early if `processing_repeat_count` is nonzero to
     78     // prevent infinite recursion.
     79     if (processing_repeat_count || !last_record.keycode) {
     80         return;
     81     }
     82 
     83     if (event->pressed) {
     84         update_last_repeat_count(1);
     85         // On press, apply the last mods state, stacking on top of current mods.
     86         register_weak_mods(last_mods);
     87         registered_record       = last_record;
     88         registered_repeat_count = last_repeat_count;
     89     }
     90 
     91     // Generate a keyrecord and plumb it into the event pipeline.
     92     registered_record.event = *event;
     93     processing_repeat_count = registered_repeat_count;
     94     process_record(&registered_record);
     95     processing_repeat_count = 0;
     96 
     97     // On release, restore the mods state.
     98     if (!event->pressed) {
     99         unregister_weak_mods(last_mods);
    100     }
    101 }
    102 
    103 #ifndef NO_ALT_REPEAT_KEY
    104 /**
    105  * @brief Find alternate keycode from a table of opposing keycode pairs.
    106  * @param table Array of pairs of basic keycodes, declared as PROGMEM.
    107  * @param table_size_bytes The size of the table in bytes.
    108  * @param target The basic keycode to find.
    109  * @return The alternate basic keycode, or KC_NO if none was found.
    110  *
    111  * @note The table keycodes and target must be basic keycodes.
    112  *
    113  * This helper is used several times below to define alternate keys. Given a
    114  * table of pairs of basic keycodes, the function finds the pair containing
    115  * `target` and returns the other keycode in the pair.
    116  */
    117 static uint8_t find_alt_keycode(const uint8_t (*table)[2], uint8_t table_size_bytes, uint8_t target) {
    118     const uint8_t* keycodes = (const uint8_t*)table;
    119     for (uint8_t i = 0; i < table_size_bytes; ++i) {
    120         if (target == pgm_read_byte(keycodes + i)) {
    121             // Xor (i ^ 1) the index to get the other element in the pair.
    122             return pgm_read_byte(keycodes + (i ^ 1));
    123         }
    124     }
    125     return KC_NO;
    126 }
    127 
    128 uint16_t get_alt_repeat_key_keycode(void) {
    129     uint16_t keycode = last_record.keycode;
    130     uint8_t  mods    = last_mods;
    131 
    132     // Call the user callback first to give it a chance to override the default
    133     // alternate key definitions that follow.
    134     uint16_t alt_keycode = get_alt_repeat_key_keycode_user(keycode, mods);
    135 
    136     if (alt_keycode != KC_TRANSPARENT) {
    137         return alt_keycode;
    138     }
    139 
    140     // Convert 8-bit mods to the 5-bit format used in keycodes. This is lossy:
    141     // if left and right handed mods were mixed, they all become right handed.
    142     mods = ((mods & 0xf0) ? /* set right hand bit */ 0x10 : 0)
    143            // Combine right and left hand mods.
    144            | (((mods >> 4) | mods) & 0xf);
    145 
    146     switch (keycode) {
    147         case QK_MODS ... QK_MODS_MAX: // Unpack modifier + basic key.
    148             mods |= QK_MODS_GET_MODS(keycode);
    149             keycode = QK_MODS_GET_BASIC_KEYCODE(keycode);
    150             break;
    151 
    152 #    ifndef NO_ACTION_TAPPING
    153         case QK_MOD_TAP ... QK_MOD_TAP_MAX:
    154             keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode);
    155             break;
    156 #        ifndef NO_ACTION_LAYER
    157         case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
    158             keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);
    159             break;
    160 #        endif // NO_ACTION_LAYER
    161 #    endif     // NO_ACTION_TAPPING
    162 
    163 #    ifdef SWAP_HANDS_ENABLE
    164         case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
    165             if (IS_SWAP_HANDS_KEYCODE(keycode)) {
    166                 return KC_NO;
    167             }
    168             keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode);
    169             break;
    170 #    endif // SWAP_HANDS_ENABLE
    171     }
    172 
    173     if (IS_QK_BASIC(keycode)) {
    174         if ((mods & (MOD_LCTL | MOD_LALT | MOD_LGUI))) {
    175             // The last key was pressed with a modifier other than Shift.
    176             // The following maps
    177             //   mod + F <-> mod + B
    178             // and a few others, supporting several core hotkeys used in
    179             // Emacs, Vim, less, and other programs.
    180             // clang-format off
    181             static const uint8_t pairs[][2] PROGMEM = {
    182                 {KC_F   , KC_B   },  // Forward / Backward.
    183                 {KC_D   , KC_U   },  // Down / Up.
    184                 {KC_N   , KC_P   },  // Next / Previous.
    185                 {KC_A   , KC_E   },  // Home / End.
    186                 {KC_O   , KC_I   },  // Older / Newer in Vim jump list.
    187             };
    188             // clang-format on
    189             alt_keycode = find_alt_keycode(pairs, sizeof(pairs), keycode);
    190         } else {
    191             // The last key was pressed with no mods or only Shift. The
    192             // following map a few more Vim hotkeys.
    193             // clang-format off
    194             static const uint8_t pairs[][2] PROGMEM = {
    195                 {KC_J   , KC_K   },  // Down / Up.
    196                 {KC_H   , KC_L   },  // Left / Right.
    197                 // These two lines map W and E to B, and B to W.
    198                 {KC_W   , KC_B   },  // Forward / Backward by word.
    199                 {KC_E   , KC_B   },  // Forward / Backward by word.
    200             };
    201             // clang-format on
    202             alt_keycode = find_alt_keycode(pairs, sizeof(pairs), keycode);
    203         }
    204 
    205         if (!alt_keycode) {
    206             // The following key pairs are considered with any mods.
    207             // clang-format off
    208             static const uint8_t pairs[][2] PROGMEM = {
    209                 {KC_LEFT, KC_RGHT},  // Left / Right Arrow.
    210                 {KC_UP  , KC_DOWN},  // Up / Down Arrow.
    211                 {KC_HOME, KC_END },  // Home / End.
    212                 {KC_PGUP, KC_PGDN},  // Page Up / Page Down.
    213                 {KC_BSPC, KC_DEL },  // Backspace / Delete.
    214                 {KC_LBRC, KC_RBRC},  // Brackets [ ] and { }.
    215 #ifdef EXTRAKEY_ENABLE
    216                 {KC_WBAK, KC_WFWD},  // Browser Back / Forward.
    217                 {KC_MNXT, KC_MPRV},  // Next / Previous Media Track.
    218                 {KC_MFFD, KC_MRWD},  // Fast Forward / Rewind Media.
    219                 {KC_VOLU, KC_VOLD},  // Volume Up / Down.
    220                 {KC_BRIU, KC_BRID},  // Brightness Up / Down.
    221 #endif  // EXTRAKEY_ENABLE
    222 #ifdef MOUSEKEY_ENABLE
    223                 {MS_LEFT, MS_RGHT},  // Mouse Cursor Left / Right.
    224                 {MS_UP,   MS_DOWN},  // Mouse Cursor Up / Down.
    225                 {MS_WHLL, MS_WHLR},  // Mouse Wheel Left / Right.
    226                 {MS_WHLU, MS_WHLD},  // Mouse Wheel Up / Down.
    227 #endif  // MOUSEKEY_ENABLE
    228             };
    229             // clang-format on
    230             alt_keycode = find_alt_keycode(pairs, sizeof(pairs), keycode);
    231         }
    232 
    233         if (alt_keycode) {
    234             // Combine basic keycode with mods.
    235             return (mods << 8) | alt_keycode;
    236         }
    237     }
    238 
    239     return KC_NO; // No alternate key found.
    240 }
    241 
    242 void alt_repeat_key_invoke(const keyevent_t* event) {
    243     static keyrecord_t registered_record       = {0};
    244     static int8_t      registered_repeat_count = 0;
    245     // Since this function calls process_record(), it may recursively call
    246     // itself. We return early if `processing_repeat_count` is nonzero to
    247     // prevent infinite recursion.
    248     if (processing_repeat_count) {
    249         return;
    250     }
    251 
    252     if (event->pressed) {
    253         registered_record = (keyrecord_t){
    254 #    ifndef NO_ACTION_TAPPING
    255             .tap.interrupted = false,
    256             .tap.count       = 0,
    257 #    endif
    258             .keycode = get_alt_repeat_key_keycode(),
    259         };
    260     }
    261 
    262     // Early return if there is no alternate key defined.
    263     if (!registered_record.keycode) {
    264         return;
    265     }
    266 
    267     if (event->pressed) {
    268         update_last_repeat_count(-1);
    269         registered_repeat_count = last_repeat_count;
    270     }
    271 
    272     // Generate a keyrecord and plumb it into the event pipeline.
    273     registered_record.event = *event;
    274     processing_repeat_count = registered_repeat_count;
    275     process_record(&registered_record);
    276     processing_repeat_count = 0;
    277 }
    278 
    279 // Default implementation of get_alt_repeat_key_keycode_user().
    280 __attribute__((weak)) uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) {
    281     return KC_TRANSPARENT;
    282 }
    283 #endif // NO_ALT_REPEAT_KEY