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(®istered_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(®istered_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