feat(behaviors): sticky keys no longer sticky after being held
Sticky keys that are pressed down for longer than `no-sticky-after-hold-ms` will immediately deactivate upon release. If the property `no-sticky-after-hold-ms` does not exist, the value of `release-after-ms` will be used as a fallback.
This commit is contained in:
parent
0a4b1a6533
commit
95185e384d
9 changed files with 142 additions and 0 deletions
|
@ -14,6 +14,8 @@ properties:
|
|||
release-after-ms:
|
||||
type: int
|
||||
required: true
|
||||
no-sticky-after-hold-ms:
|
||||
type: int
|
||||
quick-release:
|
||||
type: boolean
|
||||
ignore-modifiers:
|
||||
|
|
|
@ -30,6 +30,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
|
||||
struct behavior_sticky_key_config {
|
||||
uint32_t release_after_ms;
|
||||
uint32_t no_sticky_after_hold_ms;
|
||||
bool quick_release;
|
||||
bool ignore_modifiers;
|
||||
struct zmk_behavior_binding behavior;
|
||||
|
@ -41,6 +42,7 @@ struct active_sticky_key {
|
|||
uint32_t param2;
|
||||
const struct behavior_sticky_key_config *config;
|
||||
// timer data.
|
||||
int64_t no_sticky_after_hold_at;
|
||||
bool timer_started;
|
||||
bool timer_cancelled;
|
||||
int64_t release_at;
|
||||
|
@ -66,6 +68,7 @@ static struct active_sticky_key *store_sticky_key(uint32_t position, uint32_t pa
|
|||
sticky_key->param2 = param2;
|
||||
sticky_key->config = config;
|
||||
sticky_key->release_at = 0;
|
||||
sticky_key->no_sticky_after_hold_at = 0;
|
||||
sticky_key->timer_cancelled = false;
|
||||
sticky_key->timer_started = false;
|
||||
sticky_key->modified_key_usage_page = 0;
|
||||
|
@ -144,6 +147,8 @@ static int on_sticky_key_binding_pressed(struct zmk_behavior_binding *binding,
|
|||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
||||
sticky_key->no_sticky_after_hold_at = event.timestamp + cfg->no_sticky_after_hold_ms;
|
||||
|
||||
press_sticky_key_behavior(sticky_key, event.timestamp);
|
||||
LOG_DBG("%d new sticky_key", event.position);
|
||||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
|
@ -162,6 +167,11 @@ static int on_sticky_key_binding_released(struct zmk_behavior_binding *binding,
|
|||
return release_sticky_key_behavior(sticky_key, event.timestamp);
|
||||
}
|
||||
|
||||
if (sticky_key->no_sticky_after_hold_at < event.timestamp) {
|
||||
LOG_DBG("Sticky key %d was held for longer than no-sticky-after-hold-ms.", event.position);
|
||||
return release_sticky_key_behavior(sticky_key, event.timestamp);
|
||||
}
|
||||
|
||||
// No other key was pressed. Start the timer.
|
||||
sticky_key->timer_started = true;
|
||||
sticky_key->release_at = event.timestamp + sticky_key->config->release_after_ms;
|
||||
|
@ -290,6 +300,8 @@ static struct behavior_sticky_key_data behavior_sticky_key_data;
|
|||
static struct behavior_sticky_key_config behavior_sticky_key_config_##n = { \
|
||||
.behavior = ZMK_KEYMAP_EXTRACT_BINDING(0, DT_DRV_INST(n)), \
|
||||
.release_after_ms = DT_INST_PROP(n, release_after_ms), \
|
||||
.no_sticky_after_hold_ms = \
|
||||
DT_INST_PROP_OR(n, no_sticky_after_hold_ms, DT_INST_PROP(n, release_after_ms)), \
|
||||
.ignore_modifiers = DT_INST_PROP(n, ignore_modifiers), \
|
||||
.quick_release = DT_INST_PROP(n, quick_release), \
|
||||
}; \
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
|
@ -0,0 +1,10 @@
|
|||
pressed: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1C implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1C implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x1C implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x1C implicit_mods 0x00 explicit_mods 0x00
|
|
@ -0,0 +1,49 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap.dtsi"
|
||||
|
||||
/* This test ensures that sticky keys remain active while being pressed. */
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&sk E &sl 1
|
||||
&kp A &kp B>;
|
||||
};
|
||||
|
||||
lower_layer {
|
||||
bindings = <
|
||||
&sk LEFT_CONTROL &kp X
|
||||
&kp Y &kp Z>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
// Hold &sk E past no-sticky-after-hold-ms
|
||||
ZMK_MOCK_PRESS(0,0,1200)
|
||||
// Tap &kp A twice
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
// &sk E should remain active before releasing
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
|
||||
// Hold &sl 1 past no-sticky-after-hold-ms
|
||||
ZMK_MOCK_PRESS(0,1,1200)
|
||||
// Tap &kp Y twice
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
// &sl 1 should remain active before releasing
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
>;
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
|
@ -0,0 +1,6 @@
|
|||
pressed: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
|
@ -0,0 +1,57 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap.dtsi"
|
||||
|
||||
/* This test ensures that sticky keys are deactivated immediately upon
|
||||
* release after being held for longer than `no-sticky-after-hold-ms`.
|
||||
*/
|
||||
|
||||
&sk {
|
||||
no-sticky-after-hold-ms = <200>;
|
||||
};
|
||||
|
||||
&sl {
|
||||
no-sticky-after-hold-ms = <200>;
|
||||
};
|
||||
|
||||
/ {
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&sk E &sl 1
|
||||
&kp A &kp B>;
|
||||
};
|
||||
|
||||
lower_layer {
|
||||
bindings = <
|
||||
&sk LEFT_CONTROL &kp X
|
||||
&kp Y &kp Z>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
// Hold &sk E past no-sticky-after-hold-ms
|
||||
ZMK_MOCK_PRESS(0,0,400)
|
||||
// Release &sk E
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
// &sk E should now be deactivated
|
||||
// Tap &kp A right after
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
|
||||
// Hold &sl 1 past no-sticky-after-hold-ms
|
||||
ZMK_MOCK_PRESS(0,1,400)
|
||||
// Release &sl 1
|
||||
ZMK_MOCK_RELEASE(0,1,10)
|
||||
// &sl 1 should now be deactivated
|
||||
// Tap &kp A right after
|
||||
ZMK_MOCK_PRESS(1,0,10)
|
||||
ZMK_MOCK_RELEASE(1,0,10)
|
||||
>;
|
||||
};
|
|
@ -30,6 +30,10 @@ You can use any keycode that works for `&kp` as parameter to `&sk`:
|
|||
|
||||
By default, sticky keys stay pressed for a second if you don't press any other key. You can configure this with the `release-after-ms` setting.
|
||||
|
||||
#### `no-sticky-after-hold-ms`
|
||||
|
||||
Sticky keys that are pressed for longer than `no-sticky-after-hold-ms` will function like regular keys.
|
||||
|
||||
#### `quick-release`
|
||||
|
||||
Some typists may find that using a sticky shift key interspersed with rapid typing results in two or more capitalized letters instead of one. This happens as the sticky key is active until the next key is released, under which other keys may be pressed and will receive the modifier. You can enable the `quick-release` setting to instead deactivate the sticky key on the next key being pressed, as opposed to released.
|
||||
|
|
Loading…
Add table
Reference in a new issue