Hold while undecided
This commit is contained in:
parent
b276a3bfb0
commit
c3f74bbcd1
7 changed files with 108 additions and 0 deletions
|
@ -31,6 +31,8 @@ properties:
|
|||
- "balanced"
|
||||
- "tap-preferred"
|
||||
- "tap-unless-interrupted"
|
||||
hold-while-undecided:
|
||||
type: boolean
|
||||
retro-tap:
|
||||
type: boolean
|
||||
hold-trigger-key-positions:
|
||||
|
|
|
@ -59,6 +59,7 @@ struct behavior_hold_tap_config {
|
|||
int quick_tap_ms;
|
||||
bool global_quick_tap;
|
||||
enum flavor flavor;
|
||||
bool hold_while_undecided;
|
||||
bool retro_tap;
|
||||
bool hold_trigger_on_release;
|
||||
int32_t hold_trigger_key_positions_len;
|
||||
|
@ -67,10 +68,12 @@ struct behavior_hold_tap_config {
|
|||
|
||||
// this data is specific for each hold-tap
|
||||
struct active_hold_tap {
|
||||
bool first_press;
|
||||
int32_t position;
|
||||
uint32_t param_hold;
|
||||
uint32_t param_tap;
|
||||
int64_t timestamp;
|
||||
bool hold_released;
|
||||
enum status status;
|
||||
const struct behavior_hold_tap_config *config;
|
||||
struct k_work_delayable work;
|
||||
|
@ -219,11 +222,13 @@ static struct active_hold_tap *store_hold_tap(uint32_t position, uint32_t param_
|
|||
if (active_hold_taps[i].position != ZMK_BHV_HOLD_TAP_POSITION_NOT_USED) {
|
||||
continue;
|
||||
}
|
||||
active_hold_taps[i].first_press = true;
|
||||
active_hold_taps[i].position = position;
|
||||
active_hold_taps[i].status = STATUS_UNDECIDED;
|
||||
active_hold_taps[i].config = config;
|
||||
active_hold_taps[i].param_hold = param_hold;
|
||||
active_hold_taps[i].param_tap = param_tap;
|
||||
active_hold_taps[i].hold_released = false;
|
||||
active_hold_taps[i].timestamp = timestamp;
|
||||
active_hold_taps[i].position_of_first_other_key_pressed = -1;
|
||||
return &active_hold_taps[i];
|
||||
|
@ -434,6 +439,28 @@ static void decide_positional_hold(struct active_hold_tap *hold_tap) {
|
|||
hold_tap->status = STATUS_TAP;
|
||||
}
|
||||
|
||||
static void release_hold_binding(struct active_hold_tap *hold_tap,
|
||||
enum decision_moment decision_moment) {
|
||||
bool keyTap =
|
||||
!(hold_tap->status == STATUS_HOLD_TIMER || hold_tap->status == STATUS_HOLD_INTERRUPT);
|
||||
bool holdRelease =
|
||||
(hold_tap->status == STATUS_HOLD_TIMER || hold_tap->status == STATUS_HOLD_INTERRUPT) &&
|
||||
decision_moment == HT_KEY_UP;
|
||||
|
||||
if (holdRelease || keyTap) {
|
||||
LOG_DBG("Releasing hold behavior");
|
||||
struct zmk_behavior_binding_event event = {
|
||||
.position = hold_tap->position,
|
||||
.timestamp = hold_tap->timestamp,
|
||||
};
|
||||
struct zmk_behavior_binding binding = {0};
|
||||
binding.behavior_dev = hold_tap->config->hold_behavior_dev;
|
||||
binding.param1 = hold_tap->param_hold;
|
||||
behavior_keymap_binding_released(&binding, event);
|
||||
hold_tap->hold_released = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void decide_hold_tap(struct active_hold_tap *hold_tap,
|
||||
enum decision_moment decision_moment) {
|
||||
if (hold_tap->status != STATUS_UNDECIDED) {
|
||||
|
@ -473,6 +500,10 @@ static void decide_hold_tap(struct active_hold_tap *hold_tap,
|
|||
status_str(hold_tap->status), flavor_str(hold_tap->config->flavor),
|
||||
decision_moment_str(decision_moment));
|
||||
undecided_hold_tap = NULL;
|
||||
|
||||
if (hold_tap->config->hold_while_undecided && !hold_tap->hold_released) {
|
||||
release_hold_binding(hold_tap, decision_moment);
|
||||
}
|
||||
press_binding(hold_tap);
|
||||
release_captured_events();
|
||||
}
|
||||
|
@ -530,6 +561,12 @@ static int on_hold_tap_binding_pressed(struct zmk_behavior_binding *binding,
|
|||
|
||||
if (is_quick_tap(hold_tap)) {
|
||||
decide_hold_tap(hold_tap, HT_QUICK_TAP);
|
||||
} else if (cfg->hold_while_undecided) {
|
||||
struct zmk_behavior_binding hold_binding = {0};
|
||||
hold_binding.behavior_dev = cfg->hold_behavior_dev;
|
||||
hold_binding.param1 = binding->param1;
|
||||
LOG_DBG("%d hold behavior pressed while undecided", event.position);
|
||||
behavior_keymap_binding_pressed(&hold_binding, event);
|
||||
}
|
||||
|
||||
// if this behavior was queued we have to adjust the timer to only
|
||||
|
@ -556,6 +593,9 @@ static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding,
|
|||
}
|
||||
|
||||
decide_hold_tap(hold_tap, HT_KEY_UP);
|
||||
if (hold_tap->config->hold_while_undecided && !hold_tap->hold_released) {
|
||||
release_hold_binding(hold_tap, HT_KEY_UP);
|
||||
}
|
||||
decide_retro_tap(hold_tap);
|
||||
release_binding(hold_tap);
|
||||
|
||||
|
@ -652,6 +692,11 @@ static int keycode_state_changed_listener(const zmk_event_t *eh) {
|
|||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
if (undecided_hold_tap->first_press && undecided_hold_tap->config->hold_while_undecided) {
|
||||
undecided_hold_tap->first_press = false;
|
||||
return ZMK_EV_EVENT_BUBBLE;
|
||||
}
|
||||
|
||||
// only key-up events will bubble through position_state_changed_listener
|
||||
// if a undecided_hold_tap is active.
|
||||
LOG_DBG("%d capturing 0x%02X %s event", undecided_hold_tap->position, ev->keycode,
|
||||
|
@ -705,6 +750,7 @@ static int behavior_hold_tap_init(const struct device *dev) {
|
|||
.quick_tap_ms = DT_INST_PROP(n, quick_tap_ms), \
|
||||
.global_quick_tap = DT_INST_PROP(n, global_quick_tap), \
|
||||
.flavor = DT_ENUM_IDX(DT_DRV_INST(n), flavor), \
|
||||
.hold_while_undecided = DT_INST_PROP(n, hold_while_undecided), \
|
||||
.retro_tap = DT_INST_PROP(n, retro_tap), \
|
||||
.hold_trigger_on_release = DT_INST_PROP(n, hold_trigger_on_release), \
|
||||
.hold_trigger_key_positions = DT_INST_PROP(n, hold_trigger_key_positions), \
|
||||
|
|
29
app/tests/hold-tap/balanced/behavior_keymap_hwu.dtsi
Normal file
29
app/tests/hold-tap/balanced/behavior_keymap_hwu.dtsi
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
|
||||
/ {
|
||||
behaviors {
|
||||
ht_bal: behavior_hold_tap_balanced {
|
||||
compatible = "zmk,behavior-hold-tap";
|
||||
label = "HOLD_TAP_BALANCED";
|
||||
#binding-cells = <2>;
|
||||
flavor = "balanced";
|
||||
tapping-term-ms = <300>;
|
||||
quick-tap-ms = <200>;
|
||||
bindings = <&kp>, <&kp>;
|
||||
hold-while-undecided;
|
||||
};
|
||||
};
|
||||
|
||||
keymap {
|
||||
compatible = "zmk,keymap";
|
||||
label ="Default keymap";
|
||||
|
||||
default_layer {
|
||||
bindings = <
|
||||
&ht_bal LEFT_SHIFT A &ht_bal LEFT_CONTROL B
|
||||
&kp D &kp RIGHT_CONTROL>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
s/.*hid_listener_keycode/kp/p
|
||||
s/.*mo_keymap_binding/mo/p
|
||||
s/.*on_hold_tap_binding/ht_binding/p
|
||||
s/.*decide_hold_tap/ht_decide/p
|
|
@ -0,0 +1,8 @@
|
|||
ht_binding_pressed: 0 new undecided hold_tap
|
||||
ht_binding_pressed: 0 hold behavior pressed while undecided
|
||||
kp_pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_decide: 0 decided tap (balanced decision moment key-up)
|
||||
kp_released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
|
||||
ht_binding_released: 0 cleaning up hold-tap
|
|
@ -0,0 +1,11 @@
|
|||
#include <dt-bindings/zmk/keys.h>
|
||||
#include <behaviors.dtsi>
|
||||
#include <dt-bindings/zmk/kscan_mock.h>
|
||||
#include "../behavior_keymap_hwu.dtsi"
|
||||
|
||||
&kscan {
|
||||
events = <
|
||||
ZMK_MOCK_PRESS(0,0,10)
|
||||
ZMK_MOCK_RELEASE(0,0,10)
|
||||
>;
|
||||
};
|
|
@ -84,6 +84,14 @@ For example, if you press `&mt LEFT_SHIFT A` and then release it without pressin
|
|||
};
|
||||
```
|
||||
|
||||
#### `hold-while-undecided`
|
||||
|
||||
If enabled, the hold behavior will immediately be held, and will release before the tap behavior is sent. With modifiers (excluding the windows key) this will not affect typing, and is useful for mod + clicking.
|
||||
|
||||
:::note Alt behavior
|
||||
In some applications, pressing the alt key by itself will have its own behavior like activate a menu.
|
||||
:::
|
||||
|
||||
#### Positional hold-tap and `hold-trigger-key-positions`
|
||||
|
||||
Including `hold-trigger-key-positions` in your hold-tap definition turns on the positional hold-tap feature. With positional hold-tap enabled, if you press any key **NOT** listed in `hold-trigger-key-positions` before `tapping-term-ms` expires, it will produce a tap.
|
||||
|
|
Loading…
Add table
Reference in a new issue