Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Configurable combo timing #407

Closed
LeonKowarschickKenbun opened this issue May 17, 2023 · 8 comments
Closed

Feature request: Configurable combo timing #407

LeonKowarschickKenbun opened this issue May 17, 2023 · 8 comments
Labels
enhancement New feature or request PRs welcome jtroo has no plans to work on this at present, but PRs are welcome

Comments

@LeonKowarschickKenbun
Copy link

Is your feature request related to a problem? Please describe.
I use combos quite excessively in my layout, both for certain symbols (äöüß), character sequences (=>, ->, ::) as well as things like backspace. However, currently, I seem to missfire my combos quite frequently. I've gotten used to very tight combo timings on my QMK and ZMK boards, so I avoid accidental triggers of the combos quite well there

Describe the solution you'd like
Having the combo timings, and potentially the general combo detection algorithm, be more configurable would be great.
When hacking around with my own implementation of combos a few years ago, I noticed that for certain combos, my timing would be very different from what it was for others, even varying in terms of the exact "inner" timings (time between first to last keypress, time between each press, time between first release vs last release, order of releases compared to keydown, etc). Having those be configurable on a per-combo level would of course be optimal, although I'd already be happy with a higher level of configuration for combos globally.

Describe alternatives you've considered

Additional context
Thanks for making Kanata, and thanks for supporting Combos, and even bigger thanks for having great error reporting!

@LeonKowarschickKenbun LeonKowarschickKenbun added the enhancement New feature or request label May 17, 2023
@gerhard-h
Copy link
Contributor

I use the the timings from tap-hold to make my chords tappy (tap-hold 1 140 @chordq q)

@prescientmoon
Copy link

Hi @gerhard-h, could you be more explicit about what the exact behaviour produced by your snippet is? I'm not sure I understand how the features interact together. What happens if q is the second key in the chord for example? Do you have to put the tap hold on all the members of the chord?

@jtroo
Copy link
Owner

jtroo commented May 18, 2023

The timing in kanata should be decently reliable, within +-1 real life ms for the configured delay, assuming it's not a macro of spamming keys as fast as possible, since that has a lot of overhead on the operating system side. However, if the system is older/weaker and CPU usage is very high, this might still have issues. You can see the implementation of the timing handling here:

fn handle_time_ticks(&mut self, tx: &Option<Sender<ServerMessage>>) -> Result<()> {

As for configuring chords more, one could add a timeout value per chord instead of the one per-group.

pub chords: &'a [(ChordKeys, &'a Action<'a, T>)],

Then one would need to process that accordingly here:

let active = queued

Finally, modify the chord parsing in this file to accept the new timeout per chord:

fn parse_chord_groups(exprs: &[&Spanned<Vec<SExpr>>], s: &mut ParsedState) -> Result<()> {

One could add a new, different defchords item that is parsed differently from the existing one, for backwards compatibility reasons, or change the parsing of the existing one somehow in a backwards-compatible way.

@gerhard-h
Copy link
Contributor

could you be more explicit about what the exact behaviour produced by your snippet is?

my goal was to only trigger the cord if both key are tapped but not hold, because q w shall become ! " when hold.

My original chord timer was 300 the a tap-hold timer of 140 made them tappier ( the defacto chord timer was now also 140).
So the tap-hold timer in (tap-hold 1 140 @chordq S-1) can be used to cancel a chord early.

I was happy with that, but after your question I realized: the obvious way to make a chord tappy is to reduce the chord timer.
I wonder if asymetric tap-hold timings could be usefull to role into the chord from one side but not from the other.

(defalias
  chordq (chord qw q) 
  chordw (chord qw w) 
)
(defchords qw 20
  (q      ) q
  (   w   ) w
  (q  w  ) @layerfix
)

now using aliases q (tap-hold 1 140 @chordq S-1) and w (tap-hold 140 140 @chordw S-2)
tap q: q
hold q: !
tap q w: @layerfix
hold q w: !" or "!

@jtroo jtroo added the PRs welcome jtroo has no plans to work on this at present, but PRs are welcome label Jul 17, 2023
@Vermoot
Copy link

Vermoot commented Nov 9, 2023

Seconding OP's request. I use some combos with few, close keys ((h e) / on Colemak-DH, index and middle finger) and some with more keys, more spread out ((w f e i) S-1).

You can imagine for a combo like (h e) / I need a pretty tight combo term (sorry, I'm using QMK terminology since I'm not yet super familiar with kanata) as to not trigger the combo when I type the word "he" (which is a roll). This tight combo term is easily doable with such close keys on two adjacent fingers.

For a combo like (w f e i) S-1 though, the keys are more spread out, and most importantly spread over my two hands. Hitting those four keys within the combo term requires it to be more forgiving (so, longer), which is not a problem because there's no way I could be accidentally hitting those four keys simultaneously by accident.

So I need a tight combo term for the first one and a longer term for the second one, but this is all on my base layer so as it stands I can't differentiate them from within my (defchords base 50 ... ) definition.

@Fred-Vatin
Copy link
Contributor

USE CASE

I have an actual case and the solution that would be required to make the things flawless in my opinion.

Check that sample dummy config:

(defcfg
  process-unmapped-keys yes
)

(defvar
  chords-timeout 35
)

(defsrc
  1
  s l
)

(defalias
  cs (chord test s)
  cl (chord test l)
)

(defchords test $chords-timeout
  (s   ) s
  (   l) l
  (s  l) del
)

(deflayer test
  lrld
  @cs @cl
)

With the actual implementation, the setting limitation to $chords-timeout produces two cases when you write the word else quickly.

  1. If $chords-timeout is too high (>50) then you won’t be able to write else sometimes because chord is triggered while pressing l then s in quick sequence.
  2. If $chords-timeout is too low (<50) then you might misfire your combo (del).

In my case I chose to set $chords-timeout to 35 to avoid triggering combo when not wanted (writing else fast). The downside being I need to be very accurate when wanting to trigger the combo.

I think this alternative between either misfire the combo or triggering it when not wanted could be solved by an idle timeout. Let’s call it input-chord-timeout. If no key were pressed during the input-chord-timeout then “play the chord” else ignore it. I think it could be a defcfg option or a third argument for defchords. I guess defcfg option would be enough.

This solution would solve the main topic. My previous config could be then:

(defcfg
  process-unmapped-keys yes
  input-chord-timeout 200
  
)

(defvar
  chords-timeout 100
)

(defsrc
  1
  s l
)

(defalias
  cs (chord test s)
  cl (chord test l)
)

(defchords test $chords-timeout
  (s   ) s
  (   l) l
  (s  l) del
)

(deflayer test
  lrld
  @cs @cl
)

$chords-timeout 100 would be confortable to trigger the chord while input-chord-timeout 200 would prevent user from “playing the chord” while writing fast.

@jtroo
Copy link
Owner

jtroo commented Mar 7, 2024

@Fred-Vatin You might be interested to have a look at this comment thread. The solution isn't as simple as input-chord-timeout but it is also more general-case and I think enables the use case in the similar manner.

@jtroo
Copy link
Owner

jtroo commented Apr 15, 2024

Adjacently fixed by #916

@jtroo jtroo closed this as not planned Won't fix, can't repro, duplicate, stale Apr 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request PRs welcome jtroo has no plans to work on this at present, but PRs are welcome
Projects
None yet
Development

No branches or pull requests

6 participants