diff options
Diffstat (limited to 'quantum/process_keycode')
| -rw-r--r-- | quantum/process_keycode/process_auto_shift.c | 419 | ||||
| -rw-r--r-- | quantum/process_keycode/process_auto_shift.h | 22 | 
2 files changed, 346 insertions, 95 deletions
| 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 <stdbool.h>  #    include <stdio.h> -  #    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 | 
