I thought this looked familiar, and while it wasn’t the thread I was thinking of, someone posted about what may be a similar issue in 2018. I’m not sure how helpful those suggestions will be, but it might be worth checking out.
If that doesn’t help, I suspect the actual error may be helpful.
Funny you have that concern: I posted in another thread about my new in-progress plugin that only sends the individual keypresses if the chord isn’t triggered. I’ve been using it for a while, and it’s now the only way I type Right-Alt/AltGr and dash, and probably accounts for most of my escape and tab typing.
That said, I used to have tab bound to “AS”, and it took me several weeks to realize that the reason I occasionally lost focus when typing in a browser was because I was typing that sequence too fast and triggering tab instead; it turned out that I couldn’t reliably press the chord faster than the individual letters. The solution was to move it to “FR” instead; I’m really liking the vertical single-finger chords.
I really should try to clean that code up to be a real plugin one of these days…
Ah… not very polished. That was just pushing up some of my old config changes; in fact, yesterday I pulled down the newest Kaleidoscope, and it’s currently completely broken. It depended on key events being processed every cycle, which is no longer the case, so I’m currently in the process of cleaning it up to work in a more reliable way that will work with the new Kaleidoscope.
I hope to have that work done in the next couple days, and then I’m really hoping to take the time to make it a real plugin (mostly just making it configurable from the keyboard.ino file rather than in the plugin itself).
While you’re working on it, you might find the new(ish) Plugin Author’s Guide helpful, in addition to the event handler hooks documentation. And any suggestions for improvements to those docs are also welcome.
I’ll start a thread once it’s more ready, but I have managed to rebuild my chording plugin to work with up-to-date Kaleidoscope.
I changed the mechanism from tracking chords to tracking queued keypresses; that has some performance and flexibility implications, so it currently lives in SimpleChords in my layout. On the other hand, it’s dramatically simpler - I managed to write the whole thing just logging the actions to take (since a malfunctioning keyboard is a pain to work with), and got the vast majority of it right. The last few bits got a bit ugly, though; particularly around the fact that if you release a key, but the press was ABORTed, the released key is number 65535; that required switching to tracking the addr in a few places, making it a bit inconsistent.
But regardless of quality, after hammering on some edge cases, I’m running that code on my Atreus now. I want to make one more functional change to overlapping macros - right now, a macro is consumed as soon as it is pressed, but if it’s a subset of a larger macro, it should be postponed - e.g., if AS and ASD are both macros, right now pressing ASD will press the AS macro followed by D, while pressing DSA will press the ASD macro.
But since I don’t personally care about that edge case yet, I’m going to prioritize cleaning this up to a point that it’s usable by people other than me. Hopefully that’ll happen in the next week or two, I’ll get that done and announce this officially.
edit: I meant to add that there are some still some edge cases around overlapping chords inherent to the approach I’m taking, but for simple use cases, this should Just Work the way you expect it to.
Great job! It’s really nice to see someone writing plugins for the new event-drive Kaleidoscope, and wonderful to hear that it made things simpler! I’d like to offer a couple of suggestions you might want to look into for future improvements:
There is a KeyAddrEventQueue class in Kaleidoscope that’s used by a few core plugins (Qukeys and TapDance come to mind) that operate in a similar way to SimpleChords. It could help simplify your code and possibly deal with those pesky edge cases in a cleaner way.
It may be worth considering defining the chords based on KeyAddr instead of Key values, because the former takes half as much space, and is immune to keymap tweaks (“these two keys on the bottom row” vs “these two letters”).
You might want to consider setting the value of all the keys in a chord, if those keys are Keyboard HID keys or layer shifts. In those cases, it might be nice for the user to be able to keep the resulting value active as long as any key in the chord remains held. (In other cases, such as LockLayer(N), or a Macros key, you definitely don’t want it to be all of the keys.
Whatever decisions you make with this plugin, I’m eager to follow your progress.
Thanks for the thoughts! My goal right now is to get this usable ASAP, since I have a tendency to get distracted and take multi-month/year breaks (e.g., this plugin had been sitting around for almost a year). I’m hoping that if other people are using it, I’ll stay motivated to work on it. Or at least, once it’s out there, other people can also work on it, rather than having to start from scratch.
Also, I’m not sure that the event-driven Kaleidoscope actually made it simpler, so much as having to track events suggested a simpler way to do things. That said, the event driven approach feels much more natural to me, so I suspect it would also simplify my original approach. We’ll see how it turns out if I ever implement the original approach…
And responding to your three points:
Thanks for the tip! That sounds helpful.
Yeah, that’s on my mental list of things that should probably change - I started with Key because it was more obvious, but then had to use KeyAddrs to eat released keys. I do also want to allow chords to be different across layers, but I suspect the way to do that is add another chord parameter indicating what layer(s) it’s active on, not to depend on Key values being different across layers.
I’m not quite sure I follow. My best interpretation is that right now, I actually do track all keys held in a chord, in order to ABORT the release events - I don’t know if they’d cause any trouble, but it seems polite to not pass through a release event for a key whose press was ABORTed. So it actually would be pretty easy to release the virtual key representing the chord when the last key is released rather than the first; this way just seemed more natural to me.
Another interpretation is that the chord’s action (unlike MagicCombo) is just a key, which will remain held as long as the (entire) chord is pressed. So you could, say, map ZX to control; then pressing and holding ZX would be equivalent to holding control, and any other keys pressed during that time would be modified. Likewise, I currently have FV mapped to -, and holding it will result in standard key repeat events, allowing me to hold it to get lots of dashes.
And I’m glad to hear that there’s interest! As noted, that’ll hopefully motivate me to keep going with this, since I’m already at the point where it works for me day-to-day, which is the point where I tend to take a break.
Instead of tracking KeyAddr values to abort release events after a chord has been activated, you could just mask those keys at the time the chord is sent. Check out live_keys.mask(key_addr).