From d9393b86842b7ef143259b5f771ae7969f98cbb4 Mon Sep 17 00:00:00 2001 From: Isaac Elenbaas Date: Thu, 25 Nov 2021 07:12:14 -0500 Subject: Add Retro Shift (Auto Shift for Tap Hold via Retro Tapping) and Custom Auto Shifts (#11059) * Add Retro Shift and Custom Auto Shifts * Fix compilation errors with no RETRO_SHIFT value --- quantum/action.c | 19 +- quantum/action_tapping.c | 106 ++++++- quantum/process_keycode/process_auto_shift.c | 419 +++++++++++++++++++++------ quantum/process_keycode/process_auto_shift.h | 22 +- 4 files changed, 455 insertions(+), 111 deletions(-) (limited to 'quantum') diff --git a/quantum/action.c b/quantum/action.c index ceaaa551f5..5e81efb671 100644 --- a/quantum/action.c +++ b/quantum/action.c @@ -45,10 +45,14 @@ along with this program. If not, see . int tp_buttons; -#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) +#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) int retro_tapping_counter = 0; #endif +#if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING) +# include "process_auto_shift.h" +#endif + #ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY __attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { return false; } #endif @@ -69,7 +73,7 @@ void action_exec(keyevent_t event) { dprint("EVENT: "); debug_event(event); dprintln(); -#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) +#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) retro_tapping_counter++; #endif } @@ -106,6 +110,11 @@ void action_exec(keyevent_t event) { #endif #ifndef NO_ACTION_TAPPING +# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) + if (event.pressed) { + retroshift_poll_time(&event); + } +# endif if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { action_tapping_process(record); } @@ -730,7 +739,7 @@ void process_action(keyrecord_t *record, action_t action) { #endif #ifndef NO_ACTION_TAPPING -# if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) +# if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) if (!is_tap_action(action)) { retro_tapping_counter = 0; } else { @@ -747,7 +756,11 @@ void process_action(keyrecord_t *record, action_t action) { get_retro_tapping(get_event_keycode(record->event, false), record) && # endif retro_tapping_counter == 2) { +# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) + process_auto_shift(action.layer_tap.code, record); +# else tap_code(action.layer_tap.code); +# endif } retro_tapping_counter = 0; } diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c index 60e56fb811..0586fad421 100644 --- a/quantum/action_tapping.c +++ b/quantum/action_tapping.c @@ -44,6 +44,10 @@ __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *re __attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { return false; } # endif +# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) +# include "process_auto_shift.h" +# endif + static keyrecord_t tapping_key = {}; static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {}; static uint8_t waiting_buffer_head = 0; @@ -107,12 +111,29 @@ void action_tapping_process(keyrecord_t record) { /* return true when key event is processed or consumed. */ bool process_tapping(keyrecord_t *keyp) { keyevent_t event = keyp->event; +# if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(TAPPING_TERM_PER_KEY) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(TAPPING_FORCE_HOLD_PER_KEY) || defined(HOLD_ON_OTHER_KEYPRESS_PER_KEY) + uint16_t tapping_keycode = get_record_keycode(&tapping_key, false); +# endif // if tapping if (IS_TAPPING_PRESSED()) { - if (WITHIN_TAPPING_TERM(event)) { + // clang-format off + if (WITHIN_TAPPING_TERM(event) +# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) + || ( +# ifdef RETRO_TAPPING_PER_KEY + get_retro_tapping(tapping_keycode, keyp) && +# endif + (RETRO_SHIFT + 0) != 0 && TIMER_DIFF_16(event.time, tapping_key.event.time) < (RETRO_SHIFT + 0) + ) +# endif + ) { + // clang-format on if (tapping_key.tap.count == 0) { if (IS_TAPPING_RECORD(keyp) && !event.pressed) { +# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) + retroshift_swap_times(); +# endif // first tap! debug("Tapping: First tap(0->1).\n"); tapping_key.tap.count = 1; @@ -128,22 +149,70 @@ bool process_tapping(keyrecord_t *keyp) { * This can register the key before settlement of tapping, * useful for long TAPPING_TERM but may prevent fast typing. */ -# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) - else if ((( + // clang-format off +# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) + else if ( + ( + ( + ( # ifdef TAPPING_TERM_PER_KEY - get_tapping_term(get_record_keycode(&tapping_key, false), keyp) + get_tapping_term(tapping_keycode, keyp) # else - TAPPING_TERM + TAPPING_TERM # endif - >= 500) + >= 500 + ) # ifdef PERMISSIVE_HOLD_PER_KEY - || get_permissive_hold(get_record_keycode(&tapping_key, false), keyp) + || get_permissive_hold(tapping_keycode, keyp) # elif defined(PERMISSIVE_HOLD) - || true + || true +# endif + ) && IS_RELEASED(event) && waiting_buffer_typed(event) + ) + // Causes nested taps to not wait past TAPPING_TERM/RETRO_SHIFT + // unnecessarily and fixes them for Layer Taps. +# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) + || ( +# ifdef RETRO_TAPPING_PER_KEY + get_retro_tapping(tapping_keycode, keyp) && +# endif + ( + // Rolled over the two keys. + ( + ( + false +# if defined(HOLD_ON_OTHER_KEYPRESS) || defined(HOLD_ON_OTHER_KEYPRESS_PER_KEY) + || ( + IS_LT(tapping_keycode) +# ifdef HOLD_ON_OTHER_KEYPRESS_PER_KEY + && get_hold_on_other_keypress(tapping_keycode, keyp) +# endif + ) +# endif +# if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY) + || ( + IS_MT(tapping_keycode) +# ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY + && !get_ignore_mod_tap_interrupt(tapping_keycode, keyp) +# endif + ) +# endif + ) && tapping_key.tap.interrupted == true + ) + // Makes Retro Shift ignore [IGNORE_MOD_TAP_INTERRUPT's + // effects on nested taps for MTs and the default + // behavior of LTs] below TAPPING_TERM or RETRO_SHIFT. + || ( + IS_RETRO(tapping_keycode) + && (event.key.col != tapping_key.event.key.col || event.key.row != tapping_key.event.key.row) + && IS_RELEASED(event) && waiting_buffer_typed(event) + ) + ) + ) # endif - ) && - IS_RELEASED(event) && waiting_buffer_typed(event)) { + ) { + // clang-format on debug("Tapping: End. No tap. Interfered by typing key\n"); process_record(&tapping_key); tapping_key = (keyrecord_t){}; @@ -181,7 +250,7 @@ bool process_tapping(keyrecord_t *keyp) { tapping_key.tap.interrupted = true; # if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) # if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) - if (get_hold_on_other_key_press(get_record_keycode(&tapping_key, false), keyp)) + if (get_hold_on_other_key_press(tapping_keycode, keyp)) # endif { debug("Tapping: End. No tap. Interfered by pressed key\n"); @@ -284,14 +353,25 @@ bool process_tapping(keyrecord_t *keyp) { } } } else if (IS_TAPPING_RELEASED()) { - if (WITHIN_TAPPING_TERM(event)) { + // clang-format off + if (WITHIN_TAPPING_TERM(event) +# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) + || ( +# ifdef RETRO_TAPPING_PER_KEY + get_retro_tapping(tapping_keycode, keyp) && +# endif + (RETRO_SHIFT + 0) != 0 && TIMER_DIFF_16(event.time, tapping_key.event.time) < (RETRO_SHIFT + 0) + ) +# endif + ) { + // clang-format on if (event.pressed) { if (IS_TAPPING_RECORD(keyp)) { //# ifndef TAPPING_FORCE_HOLD # if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY) if ( # ifdef TAPPING_FORCE_HOLD_PER_KEY - !get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) && + !get_tapping_force_hold(tapping_keycode, keyp) && # endif !tapping_key.tap.interrupted && tapping_key.tap.count > 0) { // sequential tap. diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c index 4d928edb57..bbc6367ff1 100644 --- a/quantum/process_keycode/process_auto_shift.c +++ b/quantum/process_keycode/process_auto_shift.c @@ -18,7 +18,6 @@ # include # include - # include "process_auto_shift.h" # ifndef AUTO_SHIFT_DISABLED_AT_STARTUP @@ -27,11 +26,25 @@ # define AUTO_SHIFT_STARTUP_STATE false /* disabled */ # endif -static uint16_t autoshift_time = 0; -static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT; -static uint16_t autoshift_lastkey = KC_NO; +// Stores the last Auto Shift key's up or down time, for evaluation or keyrepeat. +static uint16_t autoshift_time = 0; +# if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING) +// Stores the last key's up or down time, to replace autoshift_time so that Tap Hold times are accurate. +static uint16_t retroshift_time = 0; +// Stores a possibly Retro Shift key's up or down time, as retroshift_time needs +// to be set before the Retro Shift key is evaluated if it is interrupted by an +// Auto Shifted key. +static uint16_t last_retroshift_time; +# endif +static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT; +static uint16_t autoshift_lastkey = KC_NO; +static keyrecord_t autoshift_lastrecord; +// Keys take 8 bits if modifiers are excluded. This records the shift state +// when pressed for each key, so that can be passed to the release function +// and it knows which key needs to be released (if shifted is different base). +static uint16_t autoshift_shift_states[((1 << 8) + 15) / 16]; static struct { - // Whether autoshift is enabled. + // Whether Auto Shift is enabled. bool enabled : 1; // Whether the last auto-shifted key was released after the timeout. This // is used to replicate the last key for a tap-then-hold. @@ -40,43 +53,157 @@ static struct { bool in_progress : 1; // Whether the auto-shifted keypress has been registered. bool holding_shift : 1; -} autoshift_flags = {AUTO_SHIFT_STARTUP_STATE, false, false, false}; + // Whether the user is holding a shift and we removed it. + bool cancelling_lshift : 1; + bool cancelling_rshift : 1; + // clang-format wants to remove the true for some reason. + // clang-format off +} autoshift_flags = {AUTO_SHIFT_STARTUP_STATE, false, false, false, false, false}; +// clang-format on + +/** \brief Called on physical press, returns whether key should be added to Auto Shift */ +__attribute__((weak)) bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { return false; } + +/** \brief Called on physical press, returns whether is Auto Shift key */ +__attribute__((weak)) bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { +# ifndef NO_AUTO_SHIFT_ALPHA + case AUTO_SHIFT_ALPHA: +# endif +# ifndef NO_AUTO_SHIFT_NUMERIC + case AUTO_SHIFT_NUMERIC: +# endif +# ifndef NO_AUTO_SHIFT_SPECIAL + case AUTO_SHIFT_SPECIAL: +# endif + return true; + } + return get_custom_auto_shifted_key(keycode, record); +} + +/** \brief Called to check whether defines should apply if PER_KEY is set for it */ +__attribute__((weak)) bool get_auto_shift_repeat(uint16_t keycode, keyrecord_t *record) { return true; } +__attribute__((weak)) bool get_auto_shift_no_auto_repeat(uint16_t keycode, keyrecord_t *record) { return true; } + +/** \brief Called when an Auto Shift key needs to be pressed */ +__attribute__((weak)) void autoshift_press_user(uint16_t keycode, bool shifted, keyrecord_t *record) { + if (shifted) { + add_weak_mods(MOD_BIT(KC_LSFT)); + } + register_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode); +} + +/** \brief Called when an Auto Shift key needs to be released */ +__attribute__((weak)) void autoshift_release_user(uint16_t keycode, bool shifted, keyrecord_t *record) { unregister_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode); } + +/** \brief Sets the shift state to use when keyrepeating, required by custom shifts */ +void set_autoshift_shift_state(uint16_t keycode, bool shifted) { + keycode = keycode & 0xFF; + if (shifted) { + autoshift_shift_states[keycode / 16] |= (uint16_t)1 << keycode % 16; + } else { + autoshift_shift_states[keycode / 16] &= ~((uint16_t)1 << keycode % 16); + } +} + +/** \brief Gets the shift state to use when keyrepeating, required by custom shifts */ +bool get_autoshift_shift_state(uint16_t keycode) { + keycode = keycode & 0xFF; + return (autoshift_shift_states[keycode / 16] & (uint16_t)1 << keycode % 16) != (uint16_t)0; +} + +/** \brief Restores the shift key if it was cancelled by Auto Shift */ +static void autoshift_flush_shift(void) { + autoshift_flags.holding_shift = false; + del_weak_mods(MOD_BIT(KC_LSFT)); + if (autoshift_flags.cancelling_lshift) { + autoshift_flags.cancelling_lshift = false; + add_mods(MOD_BIT(KC_LSFT)); + } + if (autoshift_flags.cancelling_rshift) { + autoshift_flags.cancelling_rshift = false; + add_mods(MOD_BIT(KC_RSFT)); + } + send_keyboard_report(); +} /** \brief Record the press of an autoshiftable key * * \return Whether the record should be further processed. */ static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record) { - if (!autoshift_flags.enabled) { - return true; - } - + // clang-format off + if ((get_mods() +# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) + | get_oneshot_mods() +# endif + ) & (~MOD_BIT(KC_LSFT)) + ) { + // clang-format on + // Prevents keyrepeating unshifted value of key after using it in a key combo. + autoshift_lastkey = KC_NO; # ifndef AUTO_SHIFT_MODIFIERS - if (get_mods()) { - return true; - } + // We can't return true here anymore because custom unshifted values are + // possible and there's no good way to tell whether the press returned + // true upon release. + set_autoshift_shift_state(keycode, false); + autoshift_press_user(keycode, false, record); +# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) + set_oneshot_mods(get_oneshot_mods() & (~MOD_BIT(KC_LSFT))); + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); +# endif + return false; # endif -# ifdef AUTO_SHIFT_REPEAT - const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); -# ifndef AUTO_SHIFT_NO_AUTO_REPEAT - if (!autoshift_flags.lastshifted) { + } + + // Store record to be sent to user functions if there's no release record then. + autoshift_lastrecord = *record; + autoshift_lastrecord.event.pressed = false; + autoshift_lastrecord.event.time = 0; + // clang-format off +# if defined(AUTO_SHIFT_REPEAT) || defined(AUTO_SHIFT_REPEAT_PER_KEY) + if (keycode == autoshift_lastkey && +# ifdef AUTO_SHIFT_REPEAT_PER_KEY + get_auto_shift_repeat(autoshift_lastkey, record) && # endif - if (elapsed < TAPPING_TERM && keycode == autoshift_lastkey) { - // Allow a tap-then-hold for keyrepeat. - if (!autoshift_flags.lastshifted) { - register_code(autoshift_lastkey); - } else { - // Simulate pressing the shift key. - add_weak_mods(MOD_BIT(KC_LEFT_SHIFT)); - register_code(autoshift_lastkey); - } - return false; +# if !defined(AUTO_SHIFT_NO_AUTO_REPEAT) || defined(AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY) + ( + !autoshift_flags.lastshifted +# ifdef AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY + || get_auto_shift_no_auto_repeat(autoshift_lastkey, record) +# endif + ) && +# endif + TIMER_DIFF_16(now, autoshift_time) < +# ifdef TAPPING_TERM_PER_KEY + get_tapping_term(autoshift_lastkey, record) +# else + TAPPING_TERM +# endif + ) { + // clang-format on + // Allow a tap-then-hold for keyrepeat. + if (get_mods() & MOD_BIT(KC_LSFT)) { + autoshift_flags.cancelling_lshift = true; + del_mods(MOD_BIT(KC_LSFT)); } -# ifndef AUTO_SHIFT_NO_AUTO_REPEAT + if (get_mods() & MOD_BIT(KC_RSFT)) { + autoshift_flags.cancelling_rshift = true; + del_mods(MOD_BIT(KC_RSFT)); + } + // autoshift_shift_state doesn't need to be changed. + autoshift_press_user(autoshift_lastkey, autoshift_flags.lastshifted, record); + return false; } -# endif # endif + // Use physical shift state of press event to be more like normal typing. +# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) + autoshift_flags.lastshifted = (get_mods() | get_oneshot_mods()) & MOD_BIT(KC_LSFT); + set_oneshot_mods(get_oneshot_mods() & (~MOD_BIT(KC_LSFT))); +# else + autoshift_flags.lastshifted = get_mods() & MOD_BIT(KC_LSFT); +# endif // Record the keycode so we can simulate it later. autoshift_lastkey = keycode; autoshift_time = now; @@ -90,51 +217,70 @@ static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record) /** \brief Registers an autoshiftable key under the right conditions * - * If the autoshift delay has elapsed, register a shift and the key. + * If autoshift_timeout has elapsed, register a shift and the key. * - * If the autoshift key is released before the delay has elapsed, register the + * If the Auto Shift key is released before the delay has elapsed, register the * key without a shift. + * + * Called on key down with keycode=KC_NO, auto-shifted key up, and timeout. */ -static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) { - // Called on key down with KC_NO, auto-shifted key up, and timeout. - if (autoshift_flags.in_progress) { +static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger, keyrecord_t *record) { + if (autoshift_flags.in_progress && (keycode == autoshift_lastkey || keycode == KC_NO)) { // Process the auto-shiftable key. autoshift_flags.in_progress = false; - - // Time since the initial press was recorded. - const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); - if (elapsed < autoshift_timeout) { - register_code(autoshift_lastkey); - autoshift_flags.lastshifted = false; - } else { - // Simulate pressing the shift key. - add_weak_mods(MOD_BIT(KC_LEFT_SHIFT)); - register_code(autoshift_lastkey); - autoshift_flags.lastshifted = true; -# if defined(AUTO_SHIFT_REPEAT) && !defined(AUTO_SHIFT_NO_AUTO_REPEAT) - if (matrix_trigger) { - // Prevents release. - return; - } + // clang-format off + autoshift_flags.lastshifted = + autoshift_flags.lastshifted + || TIMER_DIFF_16(now, autoshift_time) >= +# ifdef AUTO_SHIFT_TIMEOUT_PER_KEY + get_autoshift_timeout(autoshift_lastkey, record) +# else + autoshift_timeout # endif + ; + // clang-format on + set_autoshift_shift_state(autoshift_lastkey, autoshift_flags.lastshifted); + if (get_mods() & MOD_BIT(KC_LSFT)) { + autoshift_flags.cancelling_lshift = true; + del_mods(MOD_BIT(KC_LSFT)); + } + if (get_mods() & MOD_BIT(KC_RSFT)) { + autoshift_flags.cancelling_rshift = true; + del_mods(MOD_BIT(KC_RSFT)); } + autoshift_press_user(autoshift_lastkey, autoshift_flags.lastshifted, record); + // clang-format off +# if (defined(AUTO_SHIFT_REPEAT) || defined(AUTO_SHIFT_REPEAT_PER_KEY)) && (!defined(AUTO_SHIFT_NO_AUTO_REPEAT) || defined(AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY)) + if (matrix_trigger +# ifdef AUTO_SHIFT_REPEAT_PER_KEY + && get_auto_shift_repeat(autoshift_lastkey, record) +# endif +# ifdef AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY + && !get_auto_shift_no_auto_repeat(autoshift_lastkey, record) +# endif + ) { + // Prevents release. + return; + } +# endif + // clang-format on # if TAP_CODE_DELAY > 0 wait_ms(TAP_CODE_DELAY); # endif - unregister_code(autoshift_lastkey); - del_weak_mods(MOD_BIT(KC_LEFT_SHIFT)); + + autoshift_release_user(autoshift_lastkey, autoshift_flags.lastshifted, record); + autoshift_flush_shift(); } else { // Release after keyrepeat. - unregister_code(keycode); + autoshift_release_user(keycode, get_autoshift_shift_state(keycode), record); if (keycode == autoshift_lastkey) { // This will only fire when the key was the last auto-shiftable - // pressed. That prevents aaaaBBBB then releasing a from unshifting - // later Bs (if B wasn't auto-shiftable). - del_weak_mods(MOD_BIT(KC_LEFT_SHIFT)); + // pressed. That prevents 'aaaaBBBB' then releasing a from unshifting + // later 'B's (if 'B' wasn't auto-shiftable). + autoshift_flush_shift(); } } - send_keyboard_report(); // del_weak_mods doesn't send one. // Roll the autoshift_time forward for detecting tap-and-hold. autoshift_time = now; } @@ -147,24 +293,29 @@ static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) { */ void autoshift_matrix_scan(void) { if (autoshift_flags.in_progress) { - const uint16_t now = timer_read(); - const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time); - if (elapsed >= autoshift_timeout) { - autoshift_end(autoshift_lastkey, now, true); + const uint16_t now = timer_read(); + if (TIMER_DIFF_16(now, autoshift_time) >= +# ifdef AUTO_SHIFT_TIMEOUT_PER_KEY + get_autoshift_timeout(autoshift_lastkey, &autoshift_lastrecord) +# else + autoshift_timeout +# endif + ) { + autoshift_end(autoshift_lastkey, now, true, &autoshift_lastrecord); } } } void autoshift_toggle(void) { autoshift_flags.enabled = !autoshift_flags.enabled; - del_weak_mods(MOD_BIT(KC_LEFT_SHIFT)); + autoshift_flush_shift(); } void autoshift_enable(void) { autoshift_flags.enabled = true; } void autoshift_disable(void) { autoshift_flags.enabled = false; - del_weak_mods(MOD_BIT(KC_LEFT_SHIFT)); + autoshift_flush_shift(); } # ifndef AUTO_SHIFT_NO_SETUP @@ -179,76 +330,158 @@ void autoshift_timer_report(void) { bool get_autoshift_state(void) { return autoshift_flags.enabled; } -uint16_t get_autoshift_timeout(void) { return autoshift_timeout; } +uint16_t get_generic_autoshift_timeout() { return autoshift_timeout; } +__attribute__((weak)) uint16_t get_autoshift_timeout(uint16_t keycode, keyrecord_t *record) { return autoshift_timeout; } void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; } bool process_auto_shift(uint16_t keycode, keyrecord_t *record) { // Note that record->event.time isn't reliable, see: // https://github.com/qmk/qmk_firmware/pull/9826#issuecomment-733559550 - const uint16_t now = timer_read(); + // clang-format off + const uint16_t now = +# if !defined(RETRO_SHIFT) || defined(NO_ACTION_TAPPING) + timer_read() +# else + (record->event.pressed) ? retroshift_time : timer_read() +# endif + ; + // clang-format on if (record->event.pressed) { if (autoshift_flags.in_progress) { - // Evaluate previous key if there is one. Doing this elsewhere is - // more complicated and easier to break. - autoshift_end(KC_NO, now, false); + // Evaluate previous key if there is one. + autoshift_end(KC_NO, now, false, &autoshift_lastrecord); } - // For pressing another key while keyrepeating shifted autoshift. - del_weak_mods(MOD_BIT(KC_LEFT_SHIFT)); switch (keycode) { case KC_ASTG: autoshift_toggle(); - return true; + break; case KC_ASON: autoshift_enable(); - return true; + break; case KC_ASOFF: autoshift_disable(); - return true; + break; # ifndef AUTO_SHIFT_NO_SETUP case KC_ASUP: autoshift_timeout += 5; - return true; + break; case KC_ASDN: autoshift_timeout -= 5; - return true; - + break; case KC_ASRP: autoshift_timer_report(); - return true; + break; +# endif + } + // If Retro Shift is disabled, possible custom actions shouldn't happen. + // clang-format off + if (IS_RETRO(keycode) +# if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING) + // Not tapped or #defines mean that rolls should use hold action. + && ( + record->tap.count == 0 +# ifdef RETRO_TAPPING_PER_KEY + || !get_retro_tapping(keycode, record) +# endif + || (record->tap.interrupted && (IS_LT(keycode) +# if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) +# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY + ? get_hold_on_other_key_press(keycode, record) +# else + ? true +# endif +# else + ? false +# endif +# if defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY) +# ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY + : !get_ignore_mod_tap_interrupt(keycode, record) +# else + : false +# endif +# else + : true +# endif + )) + ) +# endif + ) { + // clang-format on + autoshift_lastkey = KC_NO; + return true; + } + } else { + if (keycode == KC_LSFT) { + autoshift_flags.cancelling_lshift = false; + } else if (keycode == KC_RSFT) { + autoshift_flags.cancelling_rshift = false; + } + // Same as above (for pressed), additional checks are not needed because + // tap.count gets set to 0 in process_action + // clang-format off + else if (IS_RETRO(keycode) +# if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING) + && ( + record->tap.count == 0 +# ifdef RETRO_TAPPING_PER_KEY + || !get_retro_tapping(keycode, record) +# endif + ) # endif + ) { + // Fixes modifiers not being applied to rolls with AUTO_SHIFT_MODIFIERS set. +# if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY) + if (autoshift_flags.in_progress +# ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY + && !get_ignore_mod_tap_interrupt(keycode, record) +# endif + ) { + autoshift_end(KC_NO, now, false, &autoshift_lastrecord); + } +# endif + // clang-format on + return true; } } + + if (!autoshift_flags.enabled) { + return true; + } if (get_auto_shifted_key(keycode, record)) { if (record->event.pressed) { return autoshift_press(keycode, now, record); } else { - autoshift_end(keycode, now, false); + autoshift_end(keycode, now, false, record); return false; } } + + // Prevent keyrepeating of older keys upon non-AS key event. + // Not commented at above returns but they serve the same function. + if (record->event.pressed) { + autoshift_lastkey = KC_NO; + } return true; } -__attribute__((weak)) bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { - switch (keycode) { -# ifndef NO_AUTO_SHIFT_ALPHA - case KC_A ... KC_Z: -# endif -# ifndef NO_AUTO_SHIFT_NUMERIC - case KC_1 ... KC_0: -# endif -# ifndef NO_AUTO_SHIFT_SPECIAL - case KC_TAB: - case KC_MINUS ... KC_SLASH: - case KC_NONUS_BACKSLASH: -# endif - return true; +# if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING) +// Called to record time before possible delays by action_tapping_process. +void retroshift_poll_time(keyevent_t *event) { + last_retroshift_time = retroshift_time; + retroshift_time = timer_read(); +} +// Used to swap the times of Retro Shifted key and Auto Shift key that interrupted it. +void retroshift_swap_times() { + if (last_retroshift_time != 0 && autoshift_flags.in_progress) { + uint16_t temp = retroshift_time; + retroshift_time = last_retroshift_time; + last_retroshift_time = temp; } - return false; } +# endif #endif diff --git a/quantum/process_keycode/process_auto_shift.h b/quantum/process_keycode/process_auto_shift.h index 00a9ab036f..ac6a143746 100644 --- a/quantum/process_keycode/process_auto_shift.h +++ b/quantum/process_keycode/process_auto_shift.h @@ -22,13 +22,31 @@ # define AUTO_SHIFT_TIMEOUT 175 #endif +#define IS_LT(kc) ((kc) >= QK_LAYER_TAP && (kc) <= QK_LAYER_TAP_MAX) +#define IS_MT(kc) ((kc) >= QK_MOD_TAP && (kc) <= QK_MOD_TAP_MAX) +#define IS_RETRO(kc) (IS_MT(kc) || IS_LT(kc)) +#define DO_GET_AUTOSHIFT_TIMEOUT(keycode, record, ...) record +// clang-format off +#define AUTO_SHIFT_ALPHA KC_A ... KC_Z +#define AUTO_SHIFT_NUMERIC KC_1 ... KC_0 +#define AUTO_SHIFT_SPECIAL \ + KC_TAB: \ + case KC_MINUS ... KC_SLASH: \ + case KC_NONUS_BSLASH +// clang-format on + bool process_auto_shift(uint16_t keycode, keyrecord_t *record); +void retroshift_poll_time(keyevent_t *event); +void retroshift_swap_times(void); void autoshift_enable(void); void autoshift_disable(void); void autoshift_toggle(void); bool get_autoshift_state(void); -uint16_t get_autoshift_timeout(void); +uint16_t get_generic_autoshift_timeout(void); +// clang-format off +uint16_t (get_autoshift_timeout)(uint16_t keycode, keyrecord_t *record); void set_autoshift_timeout(uint16_t timeout); void autoshift_matrix_scan(void); -bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record); +bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record); +// clang-format on -- cgit v1.2.1