I recently implemented some advanced features such as multi-key tap
dances, arbitrary leader key sequences, clusters, chords and more for the QMK firmware. All is based on Papageno, a portable C-library I
wrote especially for that purpose. Papageno provides compressed compile time static tree data structures for fast pattern recognition.
My QMK pull request comes with a description of some of the new features provided by Papageno. Just look at the markdown documentation files. Those familiar with QMK might also be interested in my personal QMK keymap that demonstrates how the Papageno features might be accessed from QMK through a dedicated API.
While I am waiting for my Model 01 to arrive, it would be nice to add all those features to Kaleidoscope.
An integration with Kaleidoscope shouldn’t be too difficult, I suppose. This is because I designed Papageno to be portable and with the aim to make it as easy as possible to couple it with other software/firmware, etc.
In QMK the Papageno subsystem works almost as a wrapper for the actual firmware. Papageno is hooked between matrix scan and the rest of the firmware. Matrix positions or keycodes are intercepted by the pattern recognition engine that triggers specific actions when a sequence of tokens (matrix positions or keycodes) matches a defined pattern, then e.g. emitting other keycodes or calling user callback functions. Otherwise, as soon as a matrix position/keycode sequence is recognized as non matching, the token sequence is immediately passed to the actual firmware that processes them in the same way as if they had just emerged during the most recent matrix scan.
How could that be added to Kaleidoscope? Would it be possible through a plugin alone or do I have to dig into the core part of the firmware? I definitely want to avoid overlooking any similar features that
are already part of Kaleidoscope, e.g. somewhere in the endless list of already existing
plugins.
My work was highly influenced by @algernon’s idea of tap dances. Great thanks for that nice feature!
Kaleidoscope has so called “loop hooks” instead of a matrix scan. Loop hooks run twice per scan cycle, once after scanning keys (but before handling them), and once more at the very end of the loop. If I remember correctly, matrix_scan_user corresponds to the first run of loop hooks. However, unlike in QMK, intercepting keycodes here isn’t easy, and not even possible in a keyboard-independent manner. Loop hooks can look at the matrix state, perhaps even change them, but they aren’t meant to do either.
Kaleidoscope also has “event handler hooks”, where you can hook into the event processing system (similar in spirit to QMK’s process_record_user), and handle key events as you see fit. These are the hooks designed to intercept key events, and possibly change the actions taken.
For the feature you want to implement, event handler hooks are the right place, in my opinion (and perhaps loop hooks, if you want any kind of timeout). You will have to keep a buffer of intercepted keys, which limits the length of the sequences, but other than that, such a hook should be all you need. You won’t need to touch the core firmware, it has enough hooks to allow you to do everything within a plugin.
In other words, if you create an event handler hook that intercepts key events and returns Key_NoKey until a decision about matching is made; runs custom action on match; and replays the intercepted keys on non-match - that should work.
Hope this helps. If you need further pointers, or explanations, ask away! Looking forward to the Kaleidoscope plugin, it sounds very interesting, can’t wait to try it out! =)
Thank you very much, @algernon, for pointing me in the right direction.
I had a look at the hooks and they indeed suit my requirements nicely, except for one thing. Is it safe to call handleKeyswitchEvent from within a loop hook? A rough glance at the implementation - very clearly arranged, by the way - did not exhibit any side effects. But I may be wrong
Anyway, a port from QMK seems to be pretty straightforward as apparently both, matrix positions and keycodes, that have been buffered by Papageno can be fed via handleKeyswitchEvent.
It is safe to call it from a loop hook, yes. There are a bunch of plugins that do that already. Just keep in mind that loop hooks run twice! You’ll have to figure out which run you want to call it at.
It should be possible to port QMK to the Model01, but it is a bit more involved than porting it to other keyboards. To the best of my knowledge, noone has tried that yet.
(I’d like to think that Kaleidoscope is close to being as featureful as QMK. I know we’re not there yet, and have a couple of bugs here and there, and so on. But that’s a goal I personally want to reach soon.)
Way more important than the exact number of features is the outstanding clarity and structuredness of K.scope in comparison to other firmwares. Its plugin concept avoids some of the disadvantages of other projects. Those known to me use pre-processor macros to configure the firmware, which leads to far less transparent code and also typically a more complex build system. For new, unexperienced users K.scopes approach is suppost to be a big advantage.
After having quite some experience with qmk, i would still rather hope to run K.scope on my other, currently qmk-driven keyboards, than vice versa. Even if that means to port my keymap and stuff.
But, to be fair, I am clearly a C++ guy which makes me slightly biased as other firmwares are written in plain C.
having just done some work on kaleidoscope i can say that it is a bit easier to work with than qmk. so i agree that i don’t see much advantage to a port unless something else comes up in the future.