Browse Source

feat(behaviors): Add `global-quick-tap`

xmkb
Andrew Rae 3 years ago committed by GitHub
parent
commit
b5efc7a7eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      app/dts/bindings/behaviors/zmk,behavior-hold-tap.yaml
  2. 33
      app/src/behaviors/behavior_hold_tap.c
  3. 6
      app/tests/hold-tap/balanced/8-global-quick-tap/1-basic/events.patterns
  4. 24
      app/tests/hold-tap/balanced/8-global-quick-tap/1-basic/keycode_events.snapshot
  5. 25
      app/tests/hold-tap/balanced/8-global-quick-tap/1-basic/native_posix.keymap
  6. 6
      app/tests/hold-tap/balanced/8-global-quick-tap/2-double-hold/events.patterns
  7. 12
      app/tests/hold-tap/balanced/8-global-quick-tap/2-double-hold/keycode_events.snapshot
  8. 20
      app/tests/hold-tap/balanced/8-global-quick-tap/2-double-hold/native_posix.keymap
  9. 29
      app/tests/hold-tap/balanced/8-global-quick-tap/behavior_keymap.dtsi
  10. 6
      app/tests/hold-tap/hold-preferred/8-global-quick-tap/1-basic/events.patterns
  11. 24
      app/tests/hold-tap/hold-preferred/8-global-quick-tap/1-basic/keycode_events.snapshot
  12. 25
      app/tests/hold-tap/hold-preferred/8-global-quick-tap/1-basic/native_posix.keymap
  13. 6
      app/tests/hold-tap/hold-preferred/8-global-quick-tap/2-double-hold/events.patterns
  14. 12
      app/tests/hold-tap/hold-preferred/8-global-quick-tap/2-double-hold/keycode_events.snapshot
  15. 20
      app/tests/hold-tap/hold-preferred/8-global-quick-tap/2-double-hold/native_posix.keymap
  16. 29
      app/tests/hold-tap/hold-preferred/8-global-quick-tap/behavior_keymap.dtsi
  17. 6
      app/tests/hold-tap/tap-preferred/8-global-quick-tap/1-basic/events.patterns
  18. 24
      app/tests/hold-tap/tap-preferred/8-global-quick-tap/1-basic/keycode_events.snapshot
  19. 25
      app/tests/hold-tap/tap-preferred/8-global-quick-tap/1-basic/native_posix.keymap
  20. 6
      app/tests/hold-tap/tap-preferred/8-global-quick-tap/2-double-hold/events.patterns
  21. 12
      app/tests/hold-tap/tap-preferred/8-global-quick-tap/2-double-hold/keycode_events.snapshot
  22. 20
      app/tests/hold-tap/tap-preferred/8-global-quick-tap/2-double-hold/native_posix.keymap
  23. 29
      app/tests/hold-tap/tap-preferred/8-global-quick-tap/behavior_keymap.dtsi
  24. 6
      app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/1-basic/events.patterns
  25. 24
      app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/1-basic/keycode_events.snapshot
  26. 25
      app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/1-basic/native_posix.keymap
  27. 6
      app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/2-double-hold/events.patterns
  28. 12
      app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/2-double-hold/keycode_events.snapshot
  29. 20
      app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/2-double-hold/native_posix.keymap
  30. 29
      app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/behavior_keymap.dtsi
  31. 23
      docs/docs/behaviors/hold-tap.md

2
app/dts/bindings/behaviors/zmk,behavior-hold-tap.yaml

@ -20,6 +20,8 @@ properties: @@ -20,6 +20,8 @@ properties:
default: -1
quick_tap_ms: # deprecated
type: int
global-quick-tap:
type: boolean
flavor:
type: string
required: false

33
app/src/behaviors/behavior_hold_tap.c

@ -57,6 +57,7 @@ struct behavior_hold_tap_config { @@ -57,6 +57,7 @@ struct behavior_hold_tap_config {
char *hold_behavior_dev;
char *tap_behavior_dev;
int quick_tap_ms;
bool global_quick_tap;
enum flavor flavor;
bool retro_tap;
int32_t hold_trigger_key_positions_len;
@ -88,22 +89,33 @@ struct active_hold_tap active_hold_taps[ZMK_BHV_HOLD_TAP_MAX_HELD] = {}; @@ -88,22 +89,33 @@ struct active_hold_tap active_hold_taps[ZMK_BHV_HOLD_TAP_MAX_HELD] = {};
// We capture most position_state_changed events and some modifiers_state_changed events.
const zmk_event_t *captured_events[ZMK_BHV_HOLD_TAP_MAX_CAPTURED_EVENTS] = {};
// Keep track of which key was tapped most recently for 'quick_tap_ms'
// Keep track of which key was tapped most recently for the standard, if it is a hold-tap
// a position, will be given, if not it will just be INT32_MIN
struct last_tapped {
int32_t position;
int64_t tap_deadline;
int64_t timestamp;
};
struct last_tapped last_tapped;
struct last_tapped last_tapped = {INT32_MIN, INT64_MIN};
static void store_last_tapped(struct active_hold_tap *hold_tap) {
static void store_last_tapped(int64_t timestamp) {
if (timestamp > last_tapped.timestamp) {
last_tapped.position = INT32_MIN;
last_tapped.timestamp = timestamp;
}
}
static void store_last_hold_tapped(struct active_hold_tap *hold_tap) {
last_tapped.position = hold_tap->position;
last_tapped.tap_deadline = hold_tap->timestamp + hold_tap->config->quick_tap_ms;
last_tapped.timestamp = hold_tap->timestamp;
}
static bool is_quick_tap(struct active_hold_tap *hold_tap) {
return last_tapped.position == hold_tap->position &&
last_tapped.tap_deadline > hold_tap->timestamp;
if (hold_tap->config->global_quick_tap || last_tapped.position == hold_tap->position) {
return (last_tapped.timestamp + hold_tap->config->quick_tap_ms) > hold_tap->timestamp;
} else {
return false;
}
}
static int capture_event(const zmk_event_t *event) {
@ -362,7 +374,7 @@ static int press_binding(struct active_hold_tap *hold_tap) { @@ -362,7 +374,7 @@ static int press_binding(struct active_hold_tap *hold_tap) {
} else {
binding.behavior_dev = hold_tap->config->tap_behavior_dev;
binding.param1 = hold_tap->param_tap;
store_last_tapped(hold_tap);
store_last_hold_tapped(hold_tap);
}
return behavior_keymap_binding_pressed(&binding, event);
}
@ -619,6 +631,10 @@ static int keycode_state_changed_listener(const zmk_event_t *eh) { @@ -619,6 +631,10 @@ static int keycode_state_changed_listener(const zmk_event_t *eh) {
// we want to catch layer-up events too... how?
struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh);
if (ev->state && !is_mod(ev->usage_page, ev->keycode)) {
store_last_tapped(ev->timestamp);
}
if (undecided_hold_tap == NULL) {
// LOG_DBG("0x%02X bubble (no undecided hold_tap active)", ev->keycode);
return ZMK_EV_EVENT_BUBBLE;
@ -680,6 +696,7 @@ static int behavior_hold_tap_init(const struct device *dev) { @@ -680,6 +696,7 @@ static int behavior_hold_tap_init(const struct device *dev) {
.hold_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 0)), \
.tap_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 1)), \
.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), \
.retro_tap = DT_INST_PROP(n, retro_tap), \
.hold_trigger_key_positions = DT_INST_PROP(n, hold_trigger_key_positions), \

6
app/tests/hold-tap/balanced/8-global-quick-tap/1-basic/events.patterns

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
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
s/.*update_hold_status_for_retro_tap/update_hold_status_for_retro_tap/p
s/.*decide_retro_tap/decide_retro_tap/p

24
app/tests/hold-tap/balanced/8-global-quick-tap/1-basic/keycode_events.snapshot

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (balanced decision moment key-up)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (balanced decision moment quick-tap)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-timer (balanced decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (balanced decision moment quick-tap)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

25
app/tests/hold-tap/balanced/8-global-quick-tap/1-basic/native_posix.keymap

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
/* tap */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
/* normal quick tap */
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_RELEASE(0,0,400)
/* hold */
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,400)
/* global quick tap */
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

6
app/tests/hold-tap/balanced/8-global-quick-tap/2-double-hold/events.patterns

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
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
s/.*update_hold_status_for_retro_tap/update_hold_status_for_retro_tap/p
s/.*decide_retro_tap/decide_retro_tap/p

12
app/tests/hold-tap/balanced/8-global-quick-tap/2-double-hold/keycode_events.snapshot

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-timer (balanced decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 1 new undecided hold_tap
ht_decide: 1 decided hold-timer (balanced decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
kp_released: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 1 cleaning up hold-tap

20
app/tests/hold-tap/balanced/8-global-quick-tap/2-double-hold/native_posix.keymap

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
/* hold the first mod tap */
ZMK_MOCK_PRESS(0,0,400)
/* hold the second mod tap */
ZMK_MOCK_PRESS(0,1,400)
/* press the normal key */
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
/* release the hold taps */
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
>;
};

29
app/tests/hold-tap/balanced/8-global-quick-tap/behavior_keymap.dtsi

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
behaviors {
ht_bal: behavior_balanced {
compatible = "zmk,behavior-hold-tap";
label = "MOD_TAP";
#binding-cells = <2>;
flavor = "balanced";
tapping-term-ms = <300>;
quick-tap-ms = <300>;
bindings = <&kp>, <&kp>;
global-quick-tap;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&ht_bal LEFT_SHIFT F &ht_bal LEFT_CONTROL C
&kp D &none>;
};
};
};

6
app/tests/hold-tap/hold-preferred/8-global-quick-tap/1-basic/events.patterns

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
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
s/.*update_hold_status_for_retro_tap/update_hold_status_for_retro_tap/p
s/.*decide_retro_tap/decide_retro_tap/p

24
app/tests/hold-tap/hold-preferred/8-global-quick-tap/1-basic/keycode_events.snapshot

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (hold-preferred decision moment key-up)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (hold-preferred decision moment quick-tap)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-interrupt (hold-preferred decision moment other-key-down)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (hold-preferred decision moment quick-tap)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

25
app/tests/hold-tap/hold-preferred/8-global-quick-tap/1-basic/native_posix.keymap

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
/* tap */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
/* normal quick tap */
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_RELEASE(0,0,400)
/* hold */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,400)
/* global quick tap */
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

6
app/tests/hold-tap/hold-preferred/8-global-quick-tap/2-double-hold/events.patterns

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
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
s/.*update_hold_status_for_retro_tap/update_hold_status_for_retro_tap/p
s/.*decide_retro_tap/decide_retro_tap/p

12
app/tests/hold-tap/hold-preferred/8-global-quick-tap/2-double-hold/keycode_events.snapshot

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-timer (hold-preferred decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 1 new undecided hold_tap
ht_decide: 1 decided hold-timer (hold-preferred decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
kp_released: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 1 cleaning up hold-tap

20
app/tests/hold-tap/hold-preferred/8-global-quick-tap/2-double-hold/native_posix.keymap

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
/* hold the first mod tap */
ZMK_MOCK_PRESS(0,0,400)
/* hold the second mod tap */
ZMK_MOCK_PRESS(0,1,400)
/* press the normal key */
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
/* release the hold taps */
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
>;
};

29
app/tests/hold-tap/hold-preferred/8-global-quick-tap/behavior_keymap.dtsi

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
behaviors {
hp: behavior_hold_preferred {
compatible = "zmk,behavior-hold-tap";
label = "MOD_TAP";
#binding-cells = <2>;
flavor = "hold-preferred";
tapping-term-ms = <300>;
quick-tap-ms = <300>;
bindings = <&kp>, <&kp>;
global-quick-tap;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&hp LEFT_SHIFT F &hp LEFT_CONTROL G
&kp D &none>;
};
};
};

6
app/tests/hold-tap/tap-preferred/8-global-quick-tap/1-basic/events.patterns

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
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
s/.*update_hold_status_for_retro_tap/update_hold_status_for_retro_tap/p
s/.*decide_retro_tap/decide_retro_tap/p

24
app/tests/hold-tap/tap-preferred/8-global-quick-tap/1-basic/keycode_events.snapshot

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-preferred decision moment key-up)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-preferred decision moment quick-tap)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-timer (tap-preferred decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-preferred decision moment quick-tap)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

25
app/tests/hold-tap/tap-preferred/8-global-quick-tap/1-basic/native_posix.keymap

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
/* tap */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
/* normal quick tap */
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_RELEASE(0,0,400)
/* hold */
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,400)
/* global quick tap */
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

6
app/tests/hold-tap/tap-preferred/8-global-quick-tap/2-double-hold/events.patterns

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
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
s/.*update_hold_status_for_retro_tap/update_hold_status_for_retro_tap/p
s/.*decide_retro_tap/decide_retro_tap/p

12
app/tests/hold-tap/tap-preferred/8-global-quick-tap/2-double-hold/keycode_events.snapshot

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided hold-timer (tap-preferred decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 1 new undecided hold_tap
ht_decide: 1 decided hold-timer (tap-preferred decision moment timer)
kp_pressed: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0xe1 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
kp_released: usage_page 0x07 keycode 0xe0 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 1 cleaning up hold-tap

20
app/tests/hold-tap/tap-preferred/8-global-quick-tap/2-double-hold/native_posix.keymap

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
/* hold the first mod tap */
ZMK_MOCK_PRESS(0,0,400)
/* hold the second mod tap */
ZMK_MOCK_PRESS(0,1,400)
/* press the normal key */
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
/* release the hold taps */
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
>;
};

29
app/tests/hold-tap/tap-preferred/8-global-quick-tap/behavior_keymap.dtsi

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
behaviors {
tp: behavior_tap_preferred {
compatible = "zmk,behavior-hold-tap";
label = "MOD_TAP";
#binding-cells = <2>;
flavor = "tap-preferred";
tapping-term-ms = <300>;
quick-tap-ms = <300>;
bindings = <&kp>, <&kp>;
global-quick-tap;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&tp LEFT_SHIFT F &tp LEFT_CONTROL C
&kp D &none>;
};
};
};

6
app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/1-basic/events.patterns

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
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
s/.*update_hold_status_for_retro_tap/update_hold_status_for_retro_tap/p
s/.*decide_retro_tap/decide_retro_tap/p

24
app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/1-basic/keycode_events.snapshot

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-unless-interrupted decision moment key-up)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-unless-interrupted decision moment quick-tap)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-unless-interrupted decision moment timer)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-unless-interrupted decision moment quick-tap)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap

25
app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/1-basic/native_posix.keymap

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
/* tap */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
/* normal quick tap */
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_RELEASE(0,0,400)
/* hold */
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,400)
/* global quick tap */
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_PRESS(0,0,400)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

6
app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/2-double-hold/events.patterns

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
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
s/.*update_hold_status_for_retro_tap/update_hold_status_for_retro_tap/p
s/.*decide_retro_tap/decide_retro_tap/p

12
app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/2-double-hold/keycode_events.snapshot

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
ht_binding_pressed: 0 new undecided hold_tap
ht_decide: 0 decided tap (tap-unless-interrupted decision moment timer)
kp_pressed: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_pressed: 1 new undecided hold_tap
ht_decide: 1 decided tap (tap-unless-interrupted decision moment timer)
kp_pressed: usage_page 0x07 keycode 0x0d implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 0 cleaning up hold-tap
kp_released: usage_page 0x07 keycode 0x0d implicit_mods 0x00 explicit_mods 0x00
ht_binding_released: 1 cleaning up hold-tap

20
app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/2-double-hold/native_posix.keymap

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
/* hold the first mod tap */
ZMK_MOCK_PRESS(0,0,400)
/* hold the second mod tap */
ZMK_MOCK_PRESS(0,1,400)
/* press the normal key */
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
/* release the hold taps */
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
>;
};

29
app/tests/hold-tap/tap-unless-interrupted/6-global-quick-tap/behavior_keymap.dtsi

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
behaviors {
ht_tui: behavior_hold_tap_tap_unless_interrupted {
compatible = "zmk,behavior-hold-tap";
label = "hold_tap_tap_unless_interrupted";
#binding-cells = <2>;
flavor = "tap-unless-interrupted";
tapping-term-ms = <300>;
quick-tap-ms = <300>;
bindings = <&kp>, <&kp>;
global-quick-tap;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&ht_tui LEFT_SHIFT F &ht_tui LEFT_CONTROL J
&kp D &kp RIGHT_CONTROL>;
};
};
};

23
docs/docs/behaviors/hold-tap.md

@ -48,6 +48,29 @@ If you press a tapped hold-tap again within `quick-tap-ms` milliseconds, it will @@ -48,6 +48,29 @@ If you press a tapped hold-tap again within `quick-tap-ms` milliseconds, it will
In QMK, unlike ZMK, this functionality is enabled by default, and you turn it off using `TAPPING_FORCE_HOLD`.
#### `global-quick-tap`
If global quick tap is enabled, then `quick-tap-ms` will apply not only when the given hold-tap is tapped but for any key tap before it. This effectively disables the hold tap when typing quickly, which can be quite useful for home row mods. It can also have the effect of removing the input delay when typing quickly.
For example, the following hold-tap configuration enables global quick tap with a 125 millisecond term.
```
gqt: global-quick-tap {
compatible = "zmk,behavior-hold-tap";
label = "GLOBAL_QUICK_TAP";
#binding-cells = <2>;
flavor = "tap-preferred";
tapping-term-ms = <200>;
quick-tap-ms = <125>;
global-quick-tap;
bindings = <&kp>, <&kp>;
};
```
If you press `&kp A` and then `&gqt LEFT_SHIFT B` **within** 125 ms, then `ab` will be output. Importantly, `b` will be output immediately since it was within the `quick-tap-ms`. This quick-tap behavior will work for any key press, whether it is within a behavior like hold-tap, or a simple `&kp`. This means the `&gqt LEFT_SHIFT B` binding will only have its underlying hold-tap behavior if it is pressed 125 ms **after** a key press.
Note that the higher the `quick-tap-ms` the harder it will be to use the hold behavior, making this less applicable for something like capitalizing letter while typing normally. However, if the hold behavior isn't used during fast typing, then it can be an effective way to mitigate misfires.
#### `retro-tap`
If retro tap is enabled, the tap behavior is triggered when releasing the hold-tap key if no other key was pressed in the meantime.

Loading…
Cancel
Save