diff options
Diffstat (limited to 'quantum/process_keycode/process_auto_shift.c')
| -rw-r--r-- | quantum/process_keycode/process_auto_shift.c | 199 |
1 files changed, 149 insertions, 50 deletions
diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c index b1267922c..a2d315408 100644 --- a/quantum/process_keycode/process_auto_shift.c +++ b/quantum/process_keycode/process_auto_shift.c | |||
| @@ -16,48 +16,149 @@ | |||
| 16 | 16 | ||
| 17 | #ifdef AUTO_SHIFT_ENABLE | 17 | #ifdef AUTO_SHIFT_ENABLE |
| 18 | 18 | ||
| 19 | # include <stdbool.h> | ||
| 19 | # include <stdio.h> | 20 | # include <stdio.h> |
| 20 | 21 | ||
| 21 | # include "process_auto_shift.h" | 22 | # include "process_auto_shift.h" |
| 22 | 23 | ||
| 23 | static bool autoshift_enabled = true; | ||
| 24 | static uint16_t autoshift_time = 0; | 24 | static uint16_t autoshift_time = 0; |
| 25 | static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT; | 25 | static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT; |
| 26 | static uint16_t autoshift_lastkey = KC_NO; | 26 | static uint16_t autoshift_lastkey = KC_NO; |
| 27 | static struct { | ||
| 28 | // Whether autoshift is enabled. | ||
| 29 | bool enabled : 1; | ||
| 30 | // Whether the last auto-shifted key was released after the timeout. This | ||
| 31 | // is used to replicate the last key for a tap-then-hold. | ||
| 32 | bool lastshifted : 1; | ||
| 33 | // Whether an auto-shiftable key has been pressed but not processed. | ||
| 34 | bool in_progress : 1; | ||
| 35 | // Whether the auto-shifted keypress has been registered. | ||
| 36 | bool holding_shift : 1; | ||
| 37 | } autoshift_flags = {true, false, false, false}; | ||
| 38 | |||
| 39 | /** \brief Record the press of an autoshiftable key | ||
| 40 | * | ||
| 41 | * \return Whether the record should be further processed. | ||
| 42 | */ | ||
| 43 | static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record) { | ||
| 44 | if (!autoshift_flags.enabled) { | ||
| 45 | return true; | ||
| 46 | } | ||
| 47 | |||
| 48 | # ifndef AUTO_SHIFT_MODIFIERS | ||
| 49 | if (get_mods() & (~MOD_BIT(KC_LSFT))) { | ||
| 50 | return true; | ||
| 51 | } | ||
| 52 | # endif | ||
| 53 | # ifdef AUTO_SHIFT_REPEAT | ||
| 54 | const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); | ||
| 55 | # ifndef AUTO_SHIFT_NO_AUTO_REPEAT | ||
| 56 | if (!autoshift_flags.lastshifted) { | ||
| 57 | # endif | ||
| 58 | if (elapsed < TAPPING_TERM && keycode == autoshift_lastkey) { | ||
| 59 | // Allow a tap-then-hold for keyrepeat. | ||
| 60 | if (!autoshift_flags.lastshifted) { | ||
| 61 | register_code(autoshift_lastkey); | ||
| 62 | } else { | ||
| 63 | // Simulate pressing the shift key. | ||
| 64 | add_weak_mods(MOD_BIT(KC_LSFT)); | ||
| 65 | register_code(autoshift_lastkey); | ||
| 66 | } | ||
| 67 | return false; | ||
| 68 | } | ||
| 69 | # ifndef AUTO_SHIFT_NO_AUTO_REPEAT | ||
| 70 | } | ||
| 71 | # endif | ||
| 72 | # endif | ||
| 27 | 73 | ||
| 28 | void autoshift_flush(void) { | 74 | // Record the keycode so we can simulate it later. |
| 29 | if (autoshift_lastkey != KC_NO) { | 75 | autoshift_lastkey = keycode; |
| 30 | uint16_t elapsed = timer_elapsed(autoshift_time); | 76 | autoshift_time = now; |
| 77 | autoshift_flags.in_progress = true; | ||
| 31 | 78 | ||
| 32 | if (elapsed > autoshift_timeout) { | 79 | # if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) |
| 33 | tap_code16(LSFT(autoshift_lastkey)); | 80 | clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); |
| 81 | # endif | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | |||
| 85 | /** \brief Registers an autoshiftable key under the right conditions | ||
| 86 | * | ||
| 87 | * If the autoshift delay has elapsed, register a shift and the key. | ||
| 88 | * | ||
| 89 | * If the autoshift key is released before the delay has elapsed, register the | ||
| 90 | * key without a shift. | ||
| 91 | */ | ||
| 92 | static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) { | ||
| 93 | // Called on key down with KC_NO, auto-shifted key up, and timeout. | ||
| 94 | if (autoshift_flags.in_progress) { | ||
| 95 | // Process the auto-shiftable key. | ||
| 96 | autoshift_flags.in_progress = false; | ||
| 97 | |||
| 98 | // Time since the initial press was recorded. | ||
| 99 | const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); | ||
| 100 | if (elapsed < autoshift_timeout) { | ||
| 101 | register_code(autoshift_lastkey); | ||
| 102 | autoshift_flags.lastshifted = false; | ||
| 34 | } else { | 103 | } else { |
| 35 | tap_code(autoshift_lastkey); | 104 | // Simulate pressing the shift key. |
| 105 | add_weak_mods(MOD_BIT(KC_LSFT)); | ||
| 106 | register_code(autoshift_lastkey); | ||
| 107 | autoshift_flags.lastshifted = true; | ||
| 108 | # if defined(AUTO_SHIFT_REPEAT) && !defined(AUTO_SHIFT_NO_AUTO_REPEAT) | ||
| 109 | if (matrix_trigger) { | ||
| 110 | // Prevents release. | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | # endif | ||
| 36 | } | 114 | } |
| 37 | 115 | ||
| 38 | autoshift_time = 0; | 116 | # if TAP_CODE_DELAY > 0 |
| 39 | autoshift_lastkey = KC_NO; | 117 | wait_ms(TAP_CODE_DELAY); |
| 118 | # endif | ||
| 119 | unregister_code(autoshift_lastkey); | ||
| 120 | del_weak_mods(MOD_BIT(KC_LSFT)); | ||
| 121 | } else { | ||
| 122 | // Release after keyrepeat. | ||
| 123 | unregister_code(keycode); | ||
| 124 | if (keycode == autoshift_lastkey) { | ||
| 125 | // This will only fire when the key was the last auto-shiftable | ||
| 126 | // pressed. That prevents aaaaBBBB then releasing a from unshifting | ||
| 127 | // later Bs (if B wasn't auto-shiftable). | ||
| 128 | del_weak_mods(MOD_BIT(KC_LSFT)); | ||
| 129 | } | ||
| 40 | } | 130 | } |
| 131 | send_keyboard_report(); // del_weak_mods doesn't send one. | ||
| 132 | // Roll the autoshift_time forward for detecting tap-and-hold. | ||
| 133 | autoshift_time = now; | ||
| 41 | } | 134 | } |
| 42 | 135 | ||
| 43 | void autoshift_on(uint16_t keycode) { | 136 | /** \brief Simulates auto-shifted key releases when timeout is hit |
| 44 | autoshift_time = timer_read(); | 137 | * |
| 45 | autoshift_lastkey = keycode; | 138 | * Can be called from \c matrix_scan_user so that auto-shifted keys are sent |
| 139 | * immediately after the timeout has expired, rather than waiting for the key | ||
| 140 | * to be released. | ||
| 141 | */ | ||
| 142 | void autoshift_matrix_scan(void) { | ||
| 143 | if (autoshift_flags.in_progress) { | ||
| 144 | const uint16_t now = timer_read(); | ||
| 145 | const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); | ||
| 146 | if (elapsed >= autoshift_timeout) { | ||
| 147 | autoshift_end(autoshift_lastkey, now, true); | ||
| 148 | } | ||
| 149 | } | ||
| 46 | } | 150 | } |
| 47 | 151 | ||
| 48 | void autoshift_toggle(void) { | 152 | void autoshift_toggle(void) { |
| 49 | if (autoshift_enabled) { | 153 | autoshift_flags.enabled = !autoshift_flags.enabled; |
| 50 | autoshift_enabled = false; | 154 | del_weak_mods(MOD_BIT(KC_LSFT)); |
| 51 | autoshift_flush(); | ||
| 52 | } else { | ||
| 53 | autoshift_enabled = true; | ||
| 54 | } | ||
| 55 | } | 155 | } |
| 56 | 156 | ||
| 57 | void autoshift_enable(void) { autoshift_enabled = true; } | 157 | void autoshift_enable(void) { autoshift_flags.enabled = true; } |
| 158 | |||
| 58 | void autoshift_disable(void) { | 159 | void autoshift_disable(void) { |
| 59 | autoshift_enabled = false; | 160 | autoshift_flags.enabled = false; |
| 60 | autoshift_flush(); | 161 | del_weak_mods(MOD_BIT(KC_LSFT)); |
| 61 | } | 162 | } |
| 62 | 163 | ||
| 63 | # ifndef AUTO_SHIFT_NO_SETUP | 164 | # ifndef AUTO_SHIFT_NO_SETUP |
| @@ -70,19 +171,30 @@ void autoshift_timer_report(void) { | |||
| 70 | } | 171 | } |
| 71 | # endif | 172 | # endif |
| 72 | 173 | ||
| 73 | bool get_autoshift_state(void) { return autoshift_enabled; } | 174 | bool get_autoshift_state(void) { return autoshift_flags.enabled; } |
| 74 | 175 | ||
| 75 | uint16_t get_autoshift_timeout(void) { return autoshift_timeout; } | 176 | uint16_t get_autoshift_timeout(void) { return autoshift_timeout; } |
| 76 | 177 | ||
| 77 | void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; } | 178 | void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; } |
| 78 | 179 | ||
| 79 | bool process_auto_shift(uint16_t keycode, keyrecord_t *record) { | 180 | bool process_auto_shift(uint16_t keycode, keyrecord_t *record) { |
| 181 | // Note that record->event.time isn't reliable, see: | ||
| 182 | // https://github.com/qmk/qmk_firmware/pull/9826#issuecomment-733559550 | ||
| 183 | const uint16_t now = timer_read(); | ||
| 184 | |||
| 80 | if (record->event.pressed) { | 185 | if (record->event.pressed) { |
| 186 | if (autoshift_flags.in_progress) { | ||
| 187 | // Evaluate previous key if there is one. Doing this elsewhere is | ||
| 188 | // more complicated and easier to break. | ||
| 189 | autoshift_end(KC_NO, now, false); | ||
| 190 | } | ||
| 191 | // For pressing another key while keyrepeating shifted autoshift. | ||
| 192 | del_weak_mods(MOD_BIT(KC_LSFT)); | ||
| 193 | |||
| 81 | switch (keycode) { | 194 | switch (keycode) { |
| 82 | case KC_ASTG: | 195 | case KC_ASTG: |
| 83 | autoshift_toggle(); | 196 | autoshift_toggle(); |
| 84 | return true; | 197 | return true; |
| 85 | |||
| 86 | case KC_ASON: | 198 | case KC_ASON: |
| 87 | autoshift_enable(); | 199 | autoshift_enable(); |
| 88 | return true; | 200 | return true; |
| @@ -102,41 +214,28 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) { | |||
| 102 | autoshift_timer_report(); | 214 | autoshift_timer_report(); |
| 103 | return true; | 215 | return true; |
| 104 | # endif | 216 | # endif |
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | switch (keycode) { | ||
| 105 | # ifndef NO_AUTO_SHIFT_ALPHA | 221 | # ifndef NO_AUTO_SHIFT_ALPHA |
| 106 | case KC_A ... KC_Z: | 222 | case KC_A ... KC_Z: |
| 107 | # endif | 223 | # endif |
| 108 | # ifndef NO_AUTO_SHIFT_NUMERIC | 224 | # ifndef NO_AUTO_SHIFT_NUMERIC |
| 109 | case KC_1 ... KC_0: | 225 | case KC_1 ... KC_0: |
| 110 | # endif | 226 | # endif |
| 111 | # ifndef NO_AUTO_SHIFT_SPECIAL | 227 | # ifndef NO_AUTO_SHIFT_SPECIAL |
| 112 | case KC_TAB: | 228 | case KC_TAB: |
| 113 | case KC_MINUS ... KC_SLASH: | 229 | case KC_MINUS ... KC_SLASH: |
| 114 | case KC_NONUS_BSLASH: | 230 | case KC_NONUS_BSLASH: |
| 115 | # endif | ||
| 116 | autoshift_flush(); | ||
| 117 | if (!autoshift_enabled) return true; | ||
| 118 | |||
| 119 | # ifndef AUTO_SHIFT_MODIFIERS | ||
| 120 | if (get_mods()) { | ||
| 121 | return true; | ||
| 122 | } | ||
| 123 | # endif | ||
| 124 | autoshift_on(keycode); | ||
| 125 | |||
| 126 | // We need some extra handling here for OSL edge cases | ||
| 127 | # if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) | ||
| 128 | clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); | ||
| 129 | # endif | 231 | # endif |
| 232 | if (record->event.pressed) { | ||
| 233 | return autoshift_press(keycode, now, record); | ||
| 234 | } else { | ||
| 235 | autoshift_end(keycode, now, false); | ||
| 130 | return false; | 236 | return false; |
| 131 | 237 | } | |
| 132 | default: | ||
| 133 | autoshift_flush(); | ||
| 134 | return true; | ||
| 135 | } | ||
| 136 | } else { | ||
| 137 | autoshift_flush(); | ||
| 138 | } | 238 | } |
| 139 | |||
| 140 | return true; | 239 | return true; |
| 141 | } | 240 | } |
| 142 | 241 | ||
