zmk icon indicating copy to clipboard operation
zmk copied to clipboard

Add fire-hold-as-tap option to behavior-hold-tap

Open michaeltyson opened this issue 9 months ago • 5 comments

Add fire-hold-as-tap option to behavior-hold-tap

This introduces a new optional setting for behavior-hold-tap called fire-hold-as-tap.

When enabled, the hold behavior will send both a press and immediate release immediately when the hold condition is met, instead of sending a press on hold and a release on key-up. This prevents operating system key repeat behavior for hold actions that are intended to be momentary.

This is especially useful for sending keyboard shortcuts on hold, when key repeats would be undesirable or harmful.

The option is fully backwards compatible and defaults to off.

Feature Added

  • New boolean property: fire-hold-as-tap for behavior_hold_tap
  • Allows safe assignment of normal keys or simple behaviors to the hold side of hold-tap bindings
  • Eliminates OS key repeats by sending both press + release immediately on hold condition

Testing Performed

Tested locally on:

  • Wireless Corne using config repo with modified ZMK fork
  • Multiple hold-tap keys with fire-hold-as-tap set true and false
  • Long key holds confirmed: no OS-level repeat triggered
  • Tap behavior unaffected

Example Usage

ht: hold_tap {
    compatible = "zmk,behavior-hold-tap";
    #binding-cells = <2>;
    bindings = <&kp>, <&kp>;
    tapping-term-ms = <300>;
    quick-tap-ms = <150>;
    flavor = "tap-preferred";
    fire-hold-as-tap;
};

Additional Notes

This was implemented as an additional internal state (STATUS_HOLD_TAP_SENT) to avoid sending redundant key releases and keep the existing state machine clean.

If maintainers prefer, this could be split into a separate behavior (&tap-on-hold), but integrating it as an optional extension of hold-tap seems to align better with the extensibility approach of the current design.

michaeltyson avatar May 12 '25 09:05 michaeltyson

Hey, thanks for the PR. I'm not sure I see the need for this, as this functionality should be accomplishable by putting a macro inside of a hold-tap. If it isn't, then I would consider that a bug in need of tackling. I would be supportive of a PR demonstrating putting a macro inside of a hold-tap in our docs, though.

nmunnich avatar May 12 '25 10:05 nmunnich

Wow, super quick response!

Yeah, I can see that it could probably be achievable using macros – the problem that I had was that I just couldn’t figure it out on my own, and it seemed like a lot of finagling was required to get it working, especially within Nick Coutsos’ editor.

Setting it up with a macro seemed unnecessarily complex for what is a very simple requirement: send a single key/shortcut on hold without triggering OS repeats.

This PR offers a minimal, backwards-compatible option inside an existing core behavior. It makes this common case trivial to configure, without requiring extra behaviour definitions or macros.

What that said, I am not at all familiar with the ideology behind the current hold tap behaviour, so feel free to disregard if this is at odds with the intended use. But it did make my use case a lot simpler to achieve.

Thanks for your time!

michaeltyson avatar May 12 '25 11:05 michaeltyson

Yeah, I can see that it could probably be achievable using macros – the problem that I had was that I just couldn’t figure it out on my own, and it seemed like a lot of finagling was required to get it working, especially within Nick Coutsos’ editor.

Yeah, this is what a note in the docs should have helped with. I don't really consider it unnecessarily complex to use a macro for this - I would consider it an elegant solution to make a &ktap behaviour using macro-one-param, and then simply use that in place of &kt in the hold-tap definition. This also allows to avoid issues that some OS may have where they expect a certain delay between taps and releases, something that macros can configure but is lacking here.

What that said, I am not at all familiar with the ideology behind the current hold tap behaviour, so feel free to disregard if this is at odds with the intended use. But it did make my use case a lot simpler to achieve.

Philosophically speaking, ignoring that this functionality already exists in ZMK, I consider hold-taps a "decision-maker" between different branches that the execution can take. Also allowing them to output more than one behaviour on a press or a release would confuse their purpose and I would fear the potential edge cases that can follow from that. Others may see the issue differently, though.

Practically speaking, I think that this PR would need approval from Pete, which considering his review backlog, pacing, and the higher priorities of other issues would mean it likely wouldn't get looked at for a very long time.

I think I would encourage you as an individual to take the macro solution to the problem, to avoid needing to maintain a branch. I do hope that my comments haven't discouraged you from making further contributions in the future. If you have further issues with setting up your keymap or missing functionality, perhaps it might be an idea to go to the ZMK discord and ask for advice there, and if your use case isn't solved then a PR or a module would certainly be welcomed.

nmunnich avatar May 12 '25 13:05 nmunnich

Sure thing, no problem. I'll take a crack at it.

michaeltyson avatar May 12 '25 23:05 michaeltyson

FWIW, I've been trying for an hour to get the macro behaviour to work with no luck—making this PR and updating my keymap to use it took 20 minutes. Just from a user's perspective, the macro equivalent is appallingly inaccessible. I'm giving up and using my own fork from here on out.

If this were my project, I'd be going with this PR, as it's drastically simpler.

michaeltyson avatar May 13 '25 00:05 michaeltyson