From 805f5cb72bb04c10327b40ed0c41e3848ad9ca75 Mon Sep 17 00:00:00 2001 From: Zsolt Parragi Date: Wed, 13 May 2020 23:36:55 +0200 Subject: [PATCH] One shot support for swap hands (#8590) This commits add the SH_OS keycode, which works similarly to one shot layers: * while pressed, the keyboard is swapped * if no keys were pressed while it was pressed, the next key press is swapped SH_OS also supports chaining with one shot layers: OSL(x) + SH_OS + key interprets the key press on the oneshot layer. The ONESHOT_TIMEOUT setting used by one shot keys and layers is also used by oneshot swap hands. In the above chaining scenario the timeout of the oneshot layer is reset when swap hands is activated. Resolves #2682 --- docs/feature_swap_hands.md | 1 + docs/keycodes.md | 1 + quantum/quantum_keycodes.h | 1 + tmk_core/common/action.c | 27 +++++++++++++++++- tmk_core/common/action_code.h | 2 ++ tmk_core/common/action_util.c | 54 +++++++++++++++++++++++++++++++++++ tmk_core/common/action_util.h | 8 ++++++ 7 files changed, 93 insertions(+), 1 deletion(-) diff --git a/docs/feature_swap_hands.md b/docs/feature_swap_hands.md index 09e01d50d3c0b41c5c86d6c2ccc9e2869bfb9597..009477d2033429679189a72b2b9660d6036f2ef8 100644 --- a/docs/feature_swap_hands.md +++ b/docs/feature_swap_hands.md @@ -28,3 +28,4 @@ Note that the array indices are reversed same as the matrix and the values are o |`SH_MOFF` |Momentarily turns off swap. | |`SH_TG` |Toggles swap on and off with every key press. | |`SH_TT` |Toggles with a tap; momentary when held. | +|`SH_OS` |One shot swap hands: toggles while pressed or until next key press. | diff --git a/docs/keycodes.md b/docs/keycodes.md index 18fd811184074602cb6f507171af095cee3af5c3..40a46964a815de8be67b85f353803dde80864a99 100644 --- a/docs/keycodes.md +++ b/docs/keycodes.md @@ -531,6 +531,7 @@ See also: [Swap Hands](feature_swap_hands.md) |`SH_MOFF` |Momentarily turns off swap. | |`SH_TG` |Toggles swap on and off with every key press. | |`SH_TT` |Toggles with a tap; momentary when held. | +|`SH_OS` |One shot swap hands: toggle while pressed or until next key press. | ## Unicode Support :id=unicode-support diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index 0958c4f4eb008bf898d33df8c99c2ea18c58a955..d8f1fa4bbbc2ec2924ad725c821e834b477539ab 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -794,6 +794,7 @@ enum quantum_keycodes { # define SH_T(kc) (QK_SWAP_HANDS | (kc)) # define SH_TG (QK_SWAP_HANDS | OP_SH_TOGGLE) # define SH_TT (QK_SWAP_HANDS | OP_SH_TAP_TOGGLE) +# define SH_OS (QK_SWAP_HANDS | OP_SH_ONESHOT) # define SH_MON (QK_SWAP_HANDS | OP_SH_ON_OFF) # define SH_MOFF (QK_SWAP_HANDS | OP_SH_OFF_ON) # define SH_ON (QK_SWAP_HANDS | OP_SH_ON) diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index e5e9e270525ea8fc5d809291f1b7288dac7b3fc3..3b1268dc940bea9a97f82deb7db366c75b62877f 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -98,6 +98,11 @@ void action_exec(keyevent_t event) { if (has_oneshot_mods_timed_out()) { clear_oneshot_mods(); } +# ifdef SWAP_HANDS_ENABLE + if (has_oneshot_swaphands_timed_out()) { + clear_oneshot_swaphands(); + } +# endif # endif #endif @@ -165,6 +170,8 @@ void process_record_tap_hint(keyrecord_t *record) { # ifdef SWAP_HANDS_ENABLE case ACT_SWAP_HANDS: switch (action.swap.code) { + case OP_SH_ONESHOT: + break; case OP_SH_TAP_TOGGLE: default: swap_hands = !swap_hands; @@ -224,7 +231,11 @@ void process_action(keyrecord_t *record, action_t action) { #ifndef NO_ACTION_ONESHOT bool do_release_oneshot = false; // notice we only clear the one shot layer if the pressed key is not a modifier. - if (is_oneshot_layer_active() && event.pressed && !IS_MOD(action.key.code)) { + if (is_oneshot_layer_active() && event.pressed && !IS_MOD(action.key.code) +# ifdef SWAP_HANDS_ENABLE + && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT) +# endif + ) { clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); do_release_oneshot = !is_oneshot_layer_active(); } @@ -593,6 +604,14 @@ void process_action(keyrecord_t *record, action_t action) { swap_hands = false; } break; + case OP_SH_ONESHOT: + if (event.pressed) { + set_oneshot_swaphands(); + } else { + release_oneshot_swaphands(); + } + break; + # ifndef NO_ACTION_TAPPING case OP_SH_TAP_TOGGLE: /* tap toggle */ @@ -681,6 +700,12 @@ void process_action(keyrecord_t *record, action_t action) { # endif #endif +#ifdef SWAP_HANDS_ENABLE + if (event.pressed && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)) { + use_oneshot_swaphands(); + } +#endif + #ifndef NO_ACTION_ONESHOT /* Because we switch layers after a oneshot event, we need to release the * key before we leave the layer or no key up event will be generated. diff --git a/tmk_core/common/action_code.h b/tmk_core/common/action_code.h index f80b7a782e9f55a3fb61d55c4df93395a80ea48a..eea554ff21625bf19e9af438b3ae31b60e90ac4b 100644 --- a/tmk_core/common/action_code.h +++ b/tmk_core/common/action_code.h @@ -294,11 +294,13 @@ enum swap_hands_param_tap_op { OP_SH_OFF_ON, OP_SH_OFF, OP_SH_ON, + OP_SH_ONESHOT, }; #define ACTION_SWAP_HANDS() ACTION_SWAP_HANDS_ON_OFF() #define ACTION_SWAP_HANDS_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TOGGLE) #define ACTION_SWAP_HANDS_TAP_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TAP_TOGGLE) +#define ACTION_SWAP_HANDS_ONESHOT() ACTION(ACT_SWAP_HANDS, OP_SH_ONESHOT) #define ACTION_SWAP_HANDS_TAP_KEY(key) ACTION(ACT_SWAP_HANDS, key) #define ACTION_SWAP_HANDS_ON_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_ON_OFF) #define ACTION_SWAP_HANDS_OFF_ON() ACTION(ACT_SWAP_HANDS, OP_SH_OFF_ON) diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c index 335aa36e62a14f737e49784f7b3ff49f35d0a529..371acfa610b56c75ea6912c68c523bf32d89fdbd 100644 --- a/tmk_core/common/action_util.c +++ b/tmk_core/common/action_util.c @@ -83,9 +83,63 @@ static int8_t oneshot_layer_data = 0; inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; } inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; } +# ifdef SWAP_HANDS_ENABLE +enum { + SHO_OFF, + SHO_ACTIVE, // Swap hands button was pressed, and we didn't send any swapped keys yet + SHO_PRESSED, // Swap hands button is currently pressed + SHO_USED, // Swap hands button is still pressed, and we already sent swapped keys +} swap_hands_oneshot = SHO_OFF; +# endif + # if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) static uint16_t oneshot_layer_time = 0; inline bool has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); } +# ifdef SWAP_HANDS_ENABLE +static uint16_t oneshot_swaphands_time = 0; +inline bool has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= ONESHOT_TIMEOUT && !(swap_hands_oneshot >= SHO_PRESSED); } +# endif +# endif + +# ifdef SWAP_HANDS_ENABLE + +void set_oneshot_swaphands(void) { + swap_hands_oneshot = SHO_PRESSED; + swap_hands = true; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_swaphands_time = timer_read(); + if (oneshot_layer_time != 0) { + oneshot_layer_time = oneshot_swaphands_time; + } +# endif +} + +void release_oneshot_swaphands(void) { + if (swap_hands_oneshot == SHO_PRESSED) { + swap_hands_oneshot = SHO_ACTIVE; + } + if (swap_hands_oneshot == SHO_USED) { + clear_oneshot_swaphands(); + } +} + +void use_oneshot_swaphands(void) { + if (swap_hands_oneshot == SHO_PRESSED) { + swap_hands_oneshot = SHO_USED; + } + if (swap_hands_oneshot == SHO_ACTIVE) { + clear_oneshot_swaphands(); + } +} + +void clear_oneshot_swaphands(void) { + swap_hands_oneshot = SHO_OFF; + swap_hands = false; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_swaphands_time = 0; +# endif +} + # endif /** \brief Set oneshot layer diff --git a/tmk_core/common/action_util.h b/tmk_core/common/action_util.h index 1ce03ed0e44a3a26b4b0e35bf0cafd25d669e28c..5dd8393da48efaf22e13b4765a9544d390359077 100644 --- a/tmk_core/common/action_util.h +++ b/tmk_core/common/action_util.h @@ -77,6 +77,7 @@ void reset_oneshot_layer(void); bool is_oneshot_layer_active(void); uint8_t get_oneshot_layer_state(void); bool has_oneshot_layer_timed_out(void); +bool has_oneshot_swaphands_timed_out(void); void oneshot_locked_mods_changed_user(uint8_t mods); void oneshot_locked_mods_changed_kb(uint8_t mods); @@ -88,6 +89,13 @@ void oneshot_layer_changed_kb(uint8_t layer); /* inspect */ uint8_t has_anymod(void); +#ifdef SWAP_HANDS_ENABLE +void set_oneshot_swaphands(void); +void release_oneshot_swaphands(void); +void use_oneshot_swaphands(void); +void clear_oneshot_swaphands(void); +#endif + #ifdef __cplusplus } #endif