feat: add retro tapping

closes #339
This commit is contained in:
Okke Formsma 2020-11-11 21:55:36 +01:00
parent d5cc504b3a
commit 8e4d916ed1
28 changed files with 269 additions and 6 deletions

View file

@ -45,6 +45,7 @@ struct behavior_hold_tap_config {
int tapping_term_ms; int tapping_term_ms;
struct behavior_hold_tap_behaviors *behaviors; struct behavior_hold_tap_behaviors *behaviors;
enum flavor flavor; enum flavor flavor;
bool ignore_timer;
}; };
// this data is specific for each hold-tap // 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_hold = 0;
hold_tap->is_decided = true; hold_tap->is_decided = true;
break; break;
case HT_OTHER_KEY_DOWN:
case HT_TIMER_EVENT: case HT_TIMER_EVENT:
case HT_OTHER_KEY_DOWN:
hold_tap->is_hold = 1; hold_tap->is_hold = 1;
hold_tap->is_decided = true; hold_tap->is_decided = true;
break; 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. // 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. // We insert a timer event before the TH_KEY_UP event to verify.
int work_cancel_result = k_delayed_work_cancel(&hold_tap->work); 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); 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. // 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 // We make a timer decision before the other key events are handled if the timer would
// have run out. // have run out.
if (ev->timestamp > if (undecided_hold_tap->config->tapping_term_ms > 0 &&
(undecided_hold_tap->timestamp + undecided_hold_tap->config->tapping_term_ms)) { ev->timestamp >
(undecided_hold_tap->timestamp + undecided_hold_tap->config->tapping_term_ms)) {
decide_hold_tap(undecided_hold_tap, HT_TIMER_EVENT); decide_hold_tap(undecided_hold_tap, HT_TIMER_EVENT);
} }

View file

@ -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

View file

@ -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

View file

@ -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)
>;
};

View file

@ -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

View file

@ -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

View file

@ -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)
>;
};

View file

@ -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

View file

@ -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

View file

@ -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)
>;
};

View file

@ -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

View file

@ -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

View file

@ -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 */
>;
};

View file

@ -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>;
};
};
};

View file

@ -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

View file

@ -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

View file

@ -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)
>;
};

View file

@ -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

View file

@ -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

View file

@ -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)
>;
};

View file

@ -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

View file

@ -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

View file

@ -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)
>;
};

View file

@ -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

View file

@ -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

View file

@ -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 */
>;
};

View file

@ -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>;
};
};
};

View file

@ -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'. 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) ![Hold-tap comparison](../assets/hold-tap/comparison.png)
### Basic usage ### Basic usage
@ -29,7 +37,7 @@ For basic usage, please see [mod-tap](./mod-tap.md) and [layer-tap](./layers.md)
### Advanced Configuration ### 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> #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 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 #### 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`. 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.