From 8e4d916ed177a82a3b7104b58be9bd6d477f1a9f Mon Sep 17 00:00:00 2001
From: Okke Formsma <okke@formsma.nl>
Date: Wed, 11 Nov 2020 21:55:36 +0100
Subject: [PATCH] feat: add retro tapping

closes #339
---
 app/src/behaviors/behavior_hold_tap.c         | 11 ++++---
 .../1-dn-up/events.patterns                   |  4 +++
 .../1-dn-up/keycode_events.snapshot           |  5 ++++
 .../1-dn-up/native_posix.keymap               | 11 +++++++
 .../3a-moddn-dn-modup-up/events.patterns      |  4 +++
 .../keycode_events.snapshot                   |  7 +++++
 .../3a-moddn-dn-modup-up/native_posix.keymap  | 13 +++++++++
 .../3c-kcdn-dn-kcup-up/events.patterns        |  4 +++
 .../keycode_events.snapshot                   |  7 +++++
 .../3c-kcdn-dn-kcup-up/native_posix.keymap    | 13 +++++++++
 .../4c-dn-kcdn-kcup-up/events.patterns        |  4 +++
 .../keycode_events.snapshot                   |  7 +++++
 .../4c-dn-kcdn-kcup-up/native_posix.keymap    | 14 +++++++++
 .../behavior_keymap.dtsi                      | 29 +++++++++++++++++++
 .../1-dn-up/events.patterns                   |  4 +++
 .../1-dn-up/keycode_events.snapshot           |  5 ++++
 .../1-dn-up/native_posix.keymap               | 11 +++++++
 .../3a-moddn-dn-modup-up/events.patterns      |  4 +++
 .../keycode_events.snapshot                   |  7 +++++
 .../3a-moddn-dn-modup-up/native_posix.keymap  | 13 +++++++++
 .../3c-kcdn-dn-kcup-up/events.patterns        |  4 +++
 .../keycode_events.snapshot                   |  7 +++++
 .../3c-kcdn-dn-kcup-up/native_posix.keymap    | 13 +++++++++
 .../4c-dn-kcdn-kcup-up/events.patterns        |  4 +++
 .../keycode_events.snapshot                   |  7 +++++
 .../4c-dn-kcdn-kcup-up/native_posix.keymap    | 14 +++++++++
 .../behavior_keymap.dtsi                      | 29 +++++++++++++++++++
 docs/docs/behavior/hold-tap.md                | 20 +++++++++++--
 28 files changed, 269 insertions(+), 6 deletions(-)
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/1-dn-up/events.patterns
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/1-dn-up/keycode_events.snapshot
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/1-dn-up/native_posix.keymap
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/events.patterns
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/keycode_events.snapshot
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/native_posix.keymap
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/events.patterns
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/keycode_events.snapshot
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/native_posix.keymap
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/events.patterns
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/keycode_events.snapshot
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/native_posix.keymap
 create mode 100644 app/tests/hold-tap/balanced-disable-timer/behavior_keymap.dtsi
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/events.patterns
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/keycode_events.snapshot
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/native_posix.keymap
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/events.patterns
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/keycode_events.snapshot
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/native_posix.keymap
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/events.patterns
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/keycode_events.snapshot
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/native_posix.keymap
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/events.patterns
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/keycode_events.snapshot
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/native_posix.keymap
 create mode 100644 app/tests/hold-tap/hold-preferred-disable-timer/behavior_keymap.dtsi

diff --git a/app/src/behaviors/behavior_hold_tap.c b/app/src/behaviors/behavior_hold_tap.c
index 1dc665dd..6e0b966d 100644
--- a/app/src/behaviors/behavior_hold_tap.c
+++ b/app/src/behaviors/behavior_hold_tap.c
@@ -45,6 +45,7 @@ struct behavior_hold_tap_config {
     int tapping_term_ms;
     struct behavior_hold_tap_behaviors *behaviors;
     enum flavor flavor;
+    bool ignore_timer;
 };
 
 // this data is specific for each hold-tap
@@ -234,8 +235,8 @@ static void decide_hold_preferred(struct active_hold_tap *hold_tap, enum decisio
         hold_tap->is_hold = 0;
         hold_tap->is_decided = true;
         break;
-    case HT_OTHER_KEY_DOWN:
     case HT_TIMER_EVENT:
+    case HT_OTHER_KEY_DOWN:
         hold_tap->is_hold = 1;
         hold_tap->is_decided = true;
         break;
@@ -345,7 +346,8 @@ static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding,
     // If these events were queued, the timer event may be queued too late or not at all.
     // We insert a timer event before the TH_KEY_UP event to verify.
     int work_cancel_result = k_delayed_work_cancel(&hold_tap->work);
-    if (event.timestamp > (hold_tap->timestamp + hold_tap->config->tapping_term_ms)) {
+    if (hold_tap->config->tapping_term_ms > 0 &&
+        event.timestamp > (hold_tap->timestamp + hold_tap->config->tapping_term_ms)) {
         decide_hold_tap(hold_tap, HT_TIMER_EVENT);
     }
 
@@ -408,8 +410,9 @@ static int position_state_changed_listener(const struct zmk_event_header *eh) {
     // If these events were queued, the timer event may be queued too late or not at all.
     // We make a timer decision before the other key events are handled if the timer would
     // have run out.
-    if (ev->timestamp >
-        (undecided_hold_tap->timestamp + undecided_hold_tap->config->tapping_term_ms)) {
+    if (undecided_hold_tap->config->tapping_term_ms > 0 &&
+        ev->timestamp >
+            (undecided_hold_tap->timestamp + undecided_hold_tap->config->tapping_term_ms)) {
         decide_hold_tap(undecided_hold_tap, HT_TIMER_EVENT);
     }
 
diff --git a/app/tests/hold-tap/balanced-disable-timer/1-dn-up/events.patterns b/app/tests/hold-tap/balanced-disable-timer/1-dn-up/events.patterns
new file mode 100644
index 00000000..fdf2b15c
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/1-dn-up/events.patterns
@@ -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
\ No newline at end of file
diff --git a/app/tests/hold-tap/balanced-disable-timer/1-dn-up/keycode_events.snapshot b/app/tests/hold-tap/balanced-disable-timer/1-dn-up/keycode_events.snapshot
new file mode 100644
index 00000000..2eb64758
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/1-dn-up/keycode_events.snapshot
@@ -0,0 +1,5 @@
+ht_binding_pressed: 0 new undecided hold_tap
+ht_decide: 0 decided tap (hold-preferred event 0)
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
+ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced-disable-timer/1-dn-up/native_posix.keymap b/app/tests/hold-tap/balanced-disable-timer/1-dn-up/native_posix.keymap
new file mode 100644
index 00000000..10336ef3
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/1-dn-up/native_posix.keymap
@@ -0,0 +1,11 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+#include "../behavior_keymap.dtsi"
+
+&kscan {
+	events = <
+		ZMK_MOCK_PRESS(0,0,10) 
+		ZMK_MOCK_RELEASE(0,0,10) 
+	>;
+};
\ No newline at end of file
diff --git a/app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/events.patterns b/app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/events.patterns
new file mode 100644
index 00000000..fdf2b15c
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/events.patterns
@@ -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
\ No newline at end of file
diff --git a/app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/keycode_events.snapshot b/app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/keycode_events.snapshot
new file mode 100644
index 00000000..b7434c65
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/keycode_events.snapshot
@@ -0,0 +1,7 @@
+kp_pressed: usage_page 0x07 keycode 0xe4 mods 0x00
+ht_binding_pressed: 0 new undecided hold_tap
+ht_decide: 0 decided tap (hold-preferred event 0)
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe4 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
+ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/native_posix.keymap b/app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/native_posix.keymap
new file mode 100644
index 00000000..6f08689b
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/3a-moddn-dn-modup-up/native_posix.keymap
@@ -0,0 +1,13 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+#include "../behavior_keymap.dtsi"
+
+&kscan {
+	events = <
+		ZMK_MOCK_PRESS(1,1,10) /*ctrl*/
+		ZMK_MOCK_PRESS(0,0,100)  /*mt f-shift */
+		ZMK_MOCK_RELEASE(1,1,10)
+		ZMK_MOCK_RELEASE(0,0,10) 
+	>;
+};
\ No newline at end of file
diff --git a/app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/events.patterns b/app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/events.patterns
new file mode 100644
index 00000000..fdf2b15c
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/events.patterns
@@ -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
\ No newline at end of file
diff --git a/app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/keycode_events.snapshot
new file mode 100644
index 00000000..1254fedd
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/keycode_events.snapshot
@@ -0,0 +1,7 @@
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+ht_binding_pressed: 0 new undecided hold_tap
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+ht_decide: 0 decided tap (hold-preferred event 0)
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
+ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/native_posix.keymap b/app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/native_posix.keymap
new file mode 100644
index 00000000..77306cd0
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/3c-kcdn-dn-kcup-up/native_posix.keymap
@@ -0,0 +1,13 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+#include "../behavior_keymap.dtsi"
+
+&kscan {
+	events = <
+		ZMK_MOCK_PRESS(1,0,10) /*d*/
+		ZMK_MOCK_PRESS(0,0,100)  /*mt f-shift */
+		ZMK_MOCK_RELEASE(1,0,10)
+		ZMK_MOCK_RELEASE(0,0,10) 
+	>;
+};
\ No newline at end of file
diff --git a/app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/events.patterns b/app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/events.patterns
new file mode 100644
index 00000000..fdf2b15c
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/events.patterns
@@ -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
\ No newline at end of file
diff --git a/app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/keycode_events.snapshot
new file mode 100644
index 00000000..97cd07bf
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/keycode_events.snapshot
@@ -0,0 +1,7 @@
+ht_binding_pressed: 0 new undecided hold_tap
+ht_decide: 0 decided hold (hold-preferred event 1)
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
+ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/native_posix.keymap b/app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/native_posix.keymap
new file mode 100644
index 00000000..ce030af3
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/4c-dn-kcdn-kcup-up/native_posix.keymap
@@ -0,0 +1,14 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+#include "../behavior_keymap.dtsi"
+
+&kscan {
+	events = <
+		ZMK_MOCK_PRESS(0,0,10)
+		ZMK_MOCK_PRESS(1,0,10)
+		ZMK_MOCK_RELEASE(1,0,10)
+		ZMK_MOCK_RELEASE(0,0,10)
+		/* timer */ 
+	>;
+};
\ No newline at end of file
diff --git a/app/tests/hold-tap/balanced-disable-timer/behavior_keymap.dtsi b/app/tests/hold-tap/balanced-disable-timer/behavior_keymap.dtsi
new file mode 100644
index 00000000..b935ea13
--- /dev/null
+++ b/app/tests/hold-tap/balanced-disable-timer/behavior_keymap.dtsi
@@ -0,0 +1,29 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+
+
+/ {
+	behaviors {
+		ht_hold: behavior_hold_hold_tap {
+			compatible = "zmk,behavior-hold-tap";
+			label = "hold_hold_tap";
+			#binding-cells = <2>;
+			flavor = "hold-preferred";
+			tapping_term_ms = <0>;
+			bindings = <&kp>, <&kp>;
+		};
+	};
+
+	keymap {
+		compatible = "zmk,keymap";
+		label ="Default keymap";
+
+		default_layer {
+			bindings = <
+				&ht_hold LEFT_SHIFT F &ht_hold LEFT_CONTROL J
+				&kp D &kp RIGHT_CONTROL>;
+		};
+	};
+};
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/events.patterns b/app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/events.patterns
new file mode 100644
index 00000000..fdf2b15c
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/events.patterns
@@ -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
\ No newline at end of file
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/keycode_events.snapshot
new file mode 100644
index 00000000..2eb64758
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/keycode_events.snapshot
@@ -0,0 +1,5 @@
+ht_binding_pressed: 0 new undecided hold_tap
+ht_decide: 0 decided tap (hold-preferred event 0)
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
+ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/native_posix.keymap b/app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/native_posix.keymap
new file mode 100644
index 00000000..10336ef3
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/1-dn-up/native_posix.keymap
@@ -0,0 +1,11 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+#include "../behavior_keymap.dtsi"
+
+&kscan {
+	events = <
+		ZMK_MOCK_PRESS(0,0,10) 
+		ZMK_MOCK_RELEASE(0,0,10) 
+	>;
+};
\ No newline at end of file
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/events.patterns b/app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/events.patterns
new file mode 100644
index 00000000..fdf2b15c
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/events.patterns
@@ -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
\ No newline at end of file
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/keycode_events.snapshot
new file mode 100644
index 00000000..b7434c65
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/keycode_events.snapshot
@@ -0,0 +1,7 @@
+kp_pressed: usage_page 0x07 keycode 0xe4 mods 0x00
+ht_binding_pressed: 0 new undecided hold_tap
+ht_decide: 0 decided tap (hold-preferred event 0)
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe4 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
+ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/native_posix.keymap b/app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/native_posix.keymap
new file mode 100644
index 00000000..6f08689b
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/3a-moddn-dn-modup-up/native_posix.keymap
@@ -0,0 +1,13 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+#include "../behavior_keymap.dtsi"
+
+&kscan {
+	events = <
+		ZMK_MOCK_PRESS(1,1,10) /*ctrl*/
+		ZMK_MOCK_PRESS(0,0,100)  /*mt f-shift */
+		ZMK_MOCK_RELEASE(1,1,10)
+		ZMK_MOCK_RELEASE(0,0,10) 
+	>;
+};
\ No newline at end of file
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/events.patterns b/app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/events.patterns
new file mode 100644
index 00000000..fdf2b15c
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/events.patterns
@@ -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
\ No newline at end of file
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/keycode_events.snapshot
new file mode 100644
index 00000000..1254fedd
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/keycode_events.snapshot
@@ -0,0 +1,7 @@
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+ht_binding_pressed: 0 new undecided hold_tap
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+ht_decide: 0 decided tap (hold-preferred event 0)
+kp_pressed: usage_page 0x07 keycode 0x09 mods 0x00
+kp_released: usage_page 0x07 keycode 0x09 mods 0x00
+ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/native_posix.keymap b/app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/native_posix.keymap
new file mode 100644
index 00000000..77306cd0
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/3c-kcdn-dn-kcup-up/native_posix.keymap
@@ -0,0 +1,13 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+#include "../behavior_keymap.dtsi"
+
+&kscan {
+	events = <
+		ZMK_MOCK_PRESS(1,0,10) /*d*/
+		ZMK_MOCK_PRESS(0,0,100)  /*mt f-shift */
+		ZMK_MOCK_RELEASE(1,0,10)
+		ZMK_MOCK_RELEASE(0,0,10) 
+	>;
+};
\ No newline at end of file
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/events.patterns b/app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/events.patterns
new file mode 100644
index 00000000..fdf2b15c
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/events.patterns
@@ -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
\ No newline at end of file
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/keycode_events.snapshot b/app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/keycode_events.snapshot
new file mode 100644
index 00000000..97cd07bf
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/keycode_events.snapshot
@@ -0,0 +1,7 @@
+ht_binding_pressed: 0 new undecided hold_tap
+ht_decide: 0 decided hold (hold-preferred event 1)
+kp_pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+kp_pressed: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0x07 mods 0x00
+kp_released: usage_page 0x07 keycode 0xe1 mods 0x00
+ht_binding_released: 0 cleaning up hold-tap
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/native_posix.keymap b/app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/native_posix.keymap
new file mode 100644
index 00000000..ce030af3
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/4c-dn-kcdn-kcup-up/native_posix.keymap
@@ -0,0 +1,14 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+#include "../behavior_keymap.dtsi"
+
+&kscan {
+	events = <
+		ZMK_MOCK_PRESS(0,0,10)
+		ZMK_MOCK_PRESS(1,0,10)
+		ZMK_MOCK_RELEASE(1,0,10)
+		ZMK_MOCK_RELEASE(0,0,10)
+		/* timer */ 
+	>;
+};
\ No newline at end of file
diff --git a/app/tests/hold-tap/hold-preferred-disable-timer/behavior_keymap.dtsi b/app/tests/hold-tap/hold-preferred-disable-timer/behavior_keymap.dtsi
new file mode 100644
index 00000000..b935ea13
--- /dev/null
+++ b/app/tests/hold-tap/hold-preferred-disable-timer/behavior_keymap.dtsi
@@ -0,0 +1,29 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+
+
+/ {
+	behaviors {
+		ht_hold: behavior_hold_hold_tap {
+			compatible = "zmk,behavior-hold-tap";
+			label = "hold_hold_tap";
+			#binding-cells = <2>;
+			flavor = "hold-preferred";
+			tapping_term_ms = <0>;
+			bindings = <&kp>, <&kp>;
+		};
+	};
+
+	keymap {
+		compatible = "zmk,keymap";
+		label ="Default keymap";
+
+		default_layer {
+			bindings = <
+				&ht_hold LEFT_SHIFT F &ht_hold LEFT_CONTROL J
+				&kp D &kp RIGHT_CONTROL>;
+		};
+	};
+};
diff --git a/docs/docs/behavior/hold-tap.md b/docs/docs/behavior/hold-tap.md
index 7ae5fc07..841103a7 100644
--- a/docs/docs/behavior/hold-tap.md
+++ b/docs/docs/behavior/hold-tap.md
@@ -21,6 +21,14 @@ By default, the hold-tap is configured to also select the 'hold' functionality i
 
 We call this the 'hold-preferred' flavor of hold-taps. While this flavor may work very well for a ctrl/escape key, it's not very well suited for home-row mods or layer-taps. That's why there are two more flavors to choose from: 'tap-preferred' and 'balanced'.
 
+#### Flavors
+
+- The 'hold-preferred' flavor triggers the hold behavior when the tapping_term_ms has expired or another key is pressed.
+- The 'balanced' flavor will trigger the hold behavior when the tapping_term_ms has expired or another key is pressed and released.
+- The 'tap-preferred' flavor triggers the hold behavior when the tapping_term_ms has expired. It triggers the tap behavior when another key is pressed.
+
+When the hold-tap key is released and the hold behavior has not been triggered, the tap behavior will trigger.
+
 ![Hold-tap comparison](../assets/hold-tap/comparison.png)
 
 ### Basic usage
@@ -29,7 +37,7 @@ For basic usage, please see [mod-tap](./mod-tap.md) and [layer-tap](./layers.md)
 
 ### Advanced Configuration
 
-A code example which configures a mod-tap setting that works with homerow mods:
+This example configures a hold-tap setting that works well with homerow mods:
 
 ```
 #include <behaviors.dtsi>
@@ -62,8 +70,16 @@ A code example which configures a mod-tap setting that works with homerow mods:
 
 If this config does not work for you, try the flavor "tap-preferred" and a short tapping_term_ms such as 120ms.
 
-If you want to use a tap-hold with a keycode from a different code page, you have to define another behavior with another "bindings" parameter.For example, if you want to use SHIFT and volume up, define the bindings like `bindings = <&kp>, <&kp>;`. Only single-argument behaviors are supported at the moment.
+#### Disabling the timer
+
+If you set the tapping_term_ms to `0`, the timer is disabled for this hold-tap:
+
+- The 'hold-preferred' flavor with tapping_term_ms `0` triggers the hold behavior when another key is pressed.
+- The 'balanced' flavor with tapping_term_ms `0` triggers the hold behavior when another key is pressed and released.
+- The 'tap-preferred' flavor with tapping_term_ms `0` never triggers the hold behavior (so don't set the tapping_term for these to 0)!
 
 #### Comparison to QMK
 
 The hold-preferred flavor works similar to the `HOLD_ON_OTHER_KEY_PRESS` setting in QMK. The 'balanced' flavor is similar to the `PERMISSIVE_HOLD` setting, and the `tap-preferred` flavor is similar to `IGNORE_MOD_TAP_INTERRUPT`.
+
+Setting the `tapping_term_ms` to `0` is equivalent to `RETRO_TAPPING` in QMK.