|
|
|
@ -16,48 +16,149 @@ |
|
|
|
|
|
|
|
|
|
#ifdef AUTO_SHIFT_ENABLE |
|
|
|
|
|
|
|
|
|
# include <stdbool.h> |
|
|
|
|
# include <stdio.h> |
|
|
|
|
|
|
|
|
|
# include "process_auto_shift.h" |
|
|
|
|
|
|
|
|
|
static bool autoshift_enabled = true; |
|
|
|
|
static uint16_t autoshift_time = 0; |
|
|
|
|
static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT; |
|
|
|
|
static uint16_t autoshift_lastkey = KC_NO; |
|
|
|
|
static struct { |
|
|
|
|
// Whether autoshift 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.
|
|
|
|
|
bool lastshifted : 1; |
|
|
|
|
// Whether an auto-shiftable key has been pressed but not processed.
|
|
|
|
|
bool in_progress : 1; |
|
|
|
|
// Whether the auto-shifted keypress has been registered.
|
|
|
|
|
bool holding_shift : 1; |
|
|
|
|
} autoshift_flags = {true, false, false, false}; |
|
|
|
|
|
|
|
|
|
/** \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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
# ifndef AUTO_SHIFT_MODIFIERS |
|
|
|
|
if (get_mods() & (~MOD_BIT(KC_LSFT))) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
# 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) { |
|
|
|
|
# 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_LSFT)); |
|
|
|
|
register_code(autoshift_lastkey); |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
# ifndef AUTO_SHIFT_NO_AUTO_REPEAT |
|
|
|
|
} |
|
|
|
|
# endif |
|
|
|
|
# endif |
|
|
|
|
|
|
|
|
|
void autoshift_flush(void) { |
|
|
|
|
if (autoshift_lastkey != KC_NO) { |
|
|
|
|
uint16_t elapsed = timer_elapsed(autoshift_time); |
|
|
|
|
// Record the keycode so we can simulate it later.
|
|
|
|
|
autoshift_lastkey = keycode; |
|
|
|
|
autoshift_time = now; |
|
|
|
|
autoshift_flags.in_progress = true; |
|
|
|
|
|
|
|
|
|
if (elapsed > autoshift_timeout) { |
|
|
|
|
tap_code16(LSFT(autoshift_lastkey)); |
|
|
|
|
# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) |
|
|
|
|
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); |
|
|
|
|
# endif |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** \brief Registers an autoshiftable key under the right conditions
|
|
|
|
|
* |
|
|
|
|
* If the autoshift delay has elapsed, register a shift and the key. |
|
|
|
|
* |
|
|
|
|
* If the autoshift key is released before the delay has elapsed, register the |
|
|
|
|
* key without a shift. |
|
|
|
|
*/ |
|
|
|
|
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) { |
|
|
|
|
// 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 { |
|
|
|
|
tap_code(autoshift_lastkey); |
|
|
|
|
// Simulate pressing the shift key.
|
|
|
|
|
add_weak_mods(MOD_BIT(KC_LSFT)); |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
# endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
autoshift_time = 0; |
|
|
|
|
autoshift_lastkey = KC_NO; |
|
|
|
|
# if TAP_CODE_DELAY > 0 |
|
|
|
|
wait_ms(TAP_CODE_DELAY); |
|
|
|
|
# endif |
|
|
|
|
unregister_code(autoshift_lastkey); |
|
|
|
|
del_weak_mods(MOD_BIT(KC_LSFT)); |
|
|
|
|
} else { |
|
|
|
|
// Release after keyrepeat.
|
|
|
|
|
unregister_code(keycode); |
|
|
|
|
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_LSFT)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
send_keyboard_report(); // del_weak_mods doesn't send one.
|
|
|
|
|
// Roll the autoshift_time forward for detecting tap-and-hold.
|
|
|
|
|
autoshift_time = now; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void autoshift_on(uint16_t keycode) { |
|
|
|
|
autoshift_time = timer_read(); |
|
|
|
|
autoshift_lastkey = keycode; |
|
|
|
|
/** \brief Simulates auto-shifted key releases when timeout is hit
|
|
|
|
|
* |
|
|
|
|
* Can be called from \c matrix_scan_user so that auto-shifted keys are sent |
|
|
|
|
* immediately after the timeout has expired, rather than waiting for the key |
|
|
|
|
* to be released. |
|
|
|
|
*/ |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void autoshift_toggle(void) { |
|
|
|
|
if (autoshift_enabled) { |
|
|
|
|
autoshift_enabled = false; |
|
|
|
|
autoshift_flush(); |
|
|
|
|
} else { |
|
|
|
|
autoshift_enabled = true; |
|
|
|
|
} |
|
|
|
|
autoshift_flags.enabled = !autoshift_flags.enabled; |
|
|
|
|
del_weak_mods(MOD_BIT(KC_LSFT)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void autoshift_enable(void) { autoshift_enabled = true; } |
|
|
|
|
void autoshift_enable(void) { autoshift_flags.enabled = true; } |
|
|
|
|
|
|
|
|
|
void autoshift_disable(void) { |
|
|
|
|
autoshift_enabled = false; |
|
|
|
|
autoshift_flush(); |
|
|
|
|
autoshift_flags.enabled = false; |
|
|
|
|
del_weak_mods(MOD_BIT(KC_LSFT)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
# ifndef AUTO_SHIFT_NO_SETUP |
|
|
|
@ -70,19 +171,30 @@ void autoshift_timer_report(void) { |
|
|
|
|
} |
|
|
|
|
# endif |
|
|
|
|
|
|
|
|
|
bool get_autoshift_state(void) { return autoshift_enabled; } |
|
|
|
|
bool get_autoshift_state(void) { return autoshift_flags.enabled; } |
|
|
|
|
|
|
|
|
|
uint16_t get_autoshift_timeout(void) { 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(); |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
// For pressing another key while keyrepeating shifted autoshift.
|
|
|
|
|
del_weak_mods(MOD_BIT(KC_LSFT)); |
|
|
|
|
|
|
|
|
|
switch (keycode) { |
|
|
|
|
case KC_ASTG: |
|
|
|
|
autoshift_toggle(); |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
case KC_ASON: |
|
|
|
|
autoshift_enable(); |
|
|
|
|
return true; |
|
|
|
@ -102,41 +214,28 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) { |
|
|
|
|
autoshift_timer_report(); |
|
|
|
|
return true; |
|
|
|
|
# endif |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (keycode) { |
|
|
|
|
# ifndef NO_AUTO_SHIFT_ALPHA |
|
|
|
|
case KC_A ... KC_Z: |
|
|
|
|
case KC_A ... KC_Z: |
|
|
|
|
# endif |
|
|
|
|
# ifndef NO_AUTO_SHIFT_NUMERIC |
|
|
|
|
case KC_1 ... KC_0: |
|
|
|
|
case KC_1 ... KC_0: |
|
|
|
|
# endif |
|
|
|
|
# ifndef NO_AUTO_SHIFT_SPECIAL |
|
|
|
|
case KC_TAB: |
|
|
|
|
case KC_MINUS ... KC_SLASH: |
|
|
|
|
case KC_NONUS_BSLASH: |
|
|
|
|
# endif |
|
|
|
|
autoshift_flush(); |
|
|
|
|
if (!autoshift_enabled) return true; |
|
|
|
|
|
|
|
|
|
# ifndef AUTO_SHIFT_MODIFIERS |
|
|
|
|
if (get_mods()) { |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
# endif |
|
|
|
|
autoshift_on(keycode); |
|
|
|
|
|
|
|
|
|
// We need some extra handling here for OSL edge cases
|
|
|
|
|
# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING) |
|
|
|
|
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); |
|
|
|
|
case KC_TAB: |
|
|
|
|
case KC_MINUS ... KC_SLASH: |
|
|
|
|
case KC_NONUS_BSLASH: |
|
|
|
|
# endif |
|
|
|
|
if (record->event.pressed) { |
|
|
|
|
return autoshift_press(keycode, now, record); |
|
|
|
|
} else { |
|
|
|
|
autoshift_end(keycode, now, false); |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
autoshift_flush(); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
autoshift_flush(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|