kmk_firmware
kmk_firmware copied to clipboard
How to have a combo type several keys
For backstory to this issue see Issue #573, which is closed and the question is a bit different now.
As noted in the docs, the output of a combo can only be another key.
So if you want to have your combo do multiple things (something other than a single keypress), you must utilize make_key
, and create a virtual key which executes those things. Then you can call that new key as the output of your combo.
However, from what I can tell, there is no way to have make_key
output multiple characters as HID output. You can only have it print those characters to this console.
So the behavior of chording or steno based keyboards seems to not currently be possible.
examples of desired behavior: having a U+Y combo type "you", or having a W+U combo type "wouldn't"
The documentation currently uses a print
statement in the example make_key
code. However, changing this to send_string
, or simple_key_sequence
also prints the output to the console, instead of "typing" it as if the user typed it.
I am not sure if this is a bug or simply expected behavior and I am expecting too much out of the current feature. If it is expected behavior, is there a way to implement multiple keystrokes as a result of combos going forward?
The only thing that should print to the console should be print as we don't have any print statements in send_string
or simple_key_sequence
. What code is triggering that print, as I took a look through the code path, and that doesn't seem like it should be possible.
doesn't come out in the regular way a print statement does, maybe there's a better terminology to use, but it does show up. Obviously the real problem is that it doesn't type anything though.
I'm removing the combo stuff for simplicity here:
code.py
print("Starting")
import board
from kb import KMKKeyboard
from kmk.keys import KC, make_key
from kmk.handlers.sequences import simple_key_sequence
from kmk.handlers.sequences import send_string
keyboard = KMKKeyboard()
keyboard.debug_enabled = True
make_key(
names=('MYKEY',),
on_press=lambda *args: send_string("PRESSED"),
)
keyboard.keymap = [
[
KC.MYKEY, KC.B,\
KC.C, KC.D,\
],
]
if __name__ == '__main__':
keyboard.go()
this is what shows up on the console:
143139 kmk.kmk_keyboard: MatrixChange(ic=0, pressed=True)
143145 kmk.keys: TRNS: Key(code=1008, has_modifiers=None)
143149 kmk.kmk_keyboard: KeyResolution(key=Key(code=1007, has_modifiers=None))
143158 kmk.keys: P: Key(code=19, has_modifiers=None)
143165 kmk.keys: R: Key(code=21, has_modifiers=None)
143172 kmk.keys: E: Key(code=8, has_modifiers=None)
143179 kmk.keys: S: Key(code=22, has_modifiers=None)
143188 kmk.kmk_keyboard: coordkeys_pressed={0: Key(code=1007, has_modifiers=None)}
143191 kmk.kmk_keyboard: keys_pressed=set()
143256 kmk.kmk_keyboard: MatrixChange(ic=0, pressed=False)
143260 kmk.kmk_keyboard: KeyResolution(key=Key(code=1007, has_modifiers=None))
143266 kmk.kmk_keyboard: coordkeys_pressed={}
143269 kmk.kmk_keyboard: keys_pressed=set()
if you's like I can post the code with it in a combo as well, but in that case it doesn't come out in the console like this either. It just registers the keypresses.
combos.combos = [
Chord((KC.A, KC.B), send_string("PRESSED")),
]
oh ok so the result of a combo doesn't have to be another key? The docs should probably be updated to reflect that, right now they say:
"The result of a combo is another key being pressed/released; if the desired action isn't covered by KMK keys: create your own with make_key and attach corresponding handlers."
I think the example you just showed should go right on the combos page, would make things a lot simpler
The docs are correct: the result has to be a key instance. send_string
returns a key instance. If anything, I'd argue that send_string(*args)
should be refactored to something like KC.SEQUENCE(*args)
.
OK, but I doubt the average user knows send_string returns a key instance, and the docs point you really explicitly to using make_key. IMO it very strongly implies that the result of a combo needs to be "KC.___"
I think maybe the KC.SEQUENCE thing you are describing fixes that issue, just in a different way, but that is a little beyond my understanding of the codebase.
I think simply adding the example you showed here, even without any further explanation, would make it all very clear. But if you wanted to add further explanation, I'd say the make_key stuff should go further below, under a section called something like "more advanced actions with combos", etc
maybe I could make the changes I am describing, submit it as a PR, and you could decide if you like it better or not
As I mentioned in https://github.com/KMKfw/kmk_firmware/issues/573#issuecomment-1241536195 and https://github.com/KMKfw/kmk_firmware/issues/573#issuecomment-1241538208 last night (why was this issue split into two in the first place? I have no way of merging issues in GitHub or I'd merge this back into the other one), Sequences are barely maintained easter eggs at this point. I would welcome pull requests to help clean it up, but anyway, the way to know key objects are returned by send_string
is by reading the underlying implementations linked in those comments. KMK's core is written in the same language and style as user keymaps on purpose: it's meant to be a system that invites you to tinker, all the way down to the HID loop.
To that end, PRs are, always, welcome if a better/more consistent/more obvious way is possible, or if you have an idea for documentation to clarify things!
I split it bc the other one was closed and I thought people had stopped responding. Also the question shifted quite a bit from no longer dealing with lambda, I thought maybe you wanted a different issue opened since it has shifted so much. I didn't get a notification about your comments there after I opened this one, but now that I see them, yes you do seem to answer my question there as well.
I'm a little confused by the animosity with which this whole subject has been met. My initial issue was fixed by correcting the example code, which seems positive. All I'm suggesting now is adding like three lines to the docs to make it clear for users how to easily use the features you have implemented.
Sorry - it's not intentional or malicious animosity on my part, and knowing xs and kdb, likewise for them. It's been a rough week (month... summer...) in my personal life, and I chime in on issues in my limited spare time an on a volunteer-hobby basis. My terseness or bluntness should not be taken for anger, just a reflection of a frazzled human trying to find paths to resolution in a public forum across many tickets and PRs.
As I closed the last comment:
To that end, PRs are, always, welcome if a better/more consistent/more obvious way is possible, or if you have an idea for documentation to clarify things!
np, I really appreciate the clarification tbh. I would definitely like to contribute to the docs. I am probably not a strong enough python programmer to contribute to the actual code base just yet, but I am good at explaining things both succinctly & clearly, so docs is perfect.
I did already start working on that - however I noticed some things to fix on kmkfw.io, and then went to fix them in the repo and they were already fixed there. For instance the OneShot page currently says "keyboard.modules.append(modtap)", and in the repo that's already fixed. Is the site not directly linked to the repo?
It's not hooked up to CI, I know that much. @kdb424 and @daysgobye maintain the link between the repo (which is community owned and controlled) and the site (which, while I personally hold the domain name, I've allowed Boardsource, the company, to maintain the contents, since they put in the work to do it as part of their rollout of their recent boards and of the PEG configurator)
If one of them can figure out doc auto-publishing, that'd probably help, but there's kinda.... weird steps to get there right now. Growing pains, eh? 😃
I'll pretend like I have any clue how that works for sure. We'll figure it out as time goes on.
This topic seems like it's been solved other than docs, which is not what this issue is even about. A Docs sync is in the works, so closing for now.