I’m trying to understand some of the behaviour that I’m seeing from Kaleidosocope, and I’m at a loss.
I started out by thinking about a keymap with two keys defined as Key_LeftShift
(or any modifier). Looking at the code, it seems like holding both of those keys down, plus a letter key – e.g. lshift
+lshift
+A
– would result in a single erroneous HID report if one of those lshift
keys (whichever one gets handled last) was released, because it would call hid::releaseKey()
, clearing the bit for lshift
in the report, and possibly resulting in this output, despite the fact that a shift
key was being held the whole time:
AAAAAAaAAAAAAAA
[Note the a
]
I tried it out, and noticed that this doesn’t happen. I’m still not sure why, but I thought it might be because of repeat-rate limiting done by the OS. So I tried to slow down the keyboard by adding a single line at the end of Kaleidoscope.loop()
:
delay(1000);
I figured, with a guaranteed one-second gap between reports, the OS would certainly show what was happening. It didn’t. Instead, I saw very surprising things happen when I tapped multiple keys simultaneously. For example, when I very quickly rolled over j
, k
, and l
, I saw this:
jjjjjjjjjkkkkkkkkklllllllllllllllllllllllllllllllllllllll
I was expecting the first key to be detected would be in one report alone, so the repeated j
characters were not surprising, but on the next report after that, I thought I’d get the k
and the l
simultaneously, so whichever was detected first by the OS (k
, most likely, because its bit appears first in the key report) would appear only once, then get masked. Here’s what I expected:
jjjjjjjjjklllllllll
…or maybe just:
jjjjjjjjj
…because, by the next loop, the scanner will have had time to do a couple hundred scan cycles. (I’m also trying to figure out how the scanners work). If it worked that way, though, I guess I probably wouldn’t see any output unless I got lucky or held the keys down long enough.
Anyway, I’m just flummoxed. I added some serial debugging code to Keyboard.sendReportUnchecked()
, and it shows this happening (the bitfield is the byte from keyReport
including j
, k
, and l
; the decimal number is millis()
):
40313: 00100000
41317: 01100000
42321: 11100000
43325: 11000000
44328: 10000000
45333: 00000000
[Padded with zeroes for readability]
The first line shows a report with j
alone. Then, one second later, there’s a report with both j
& k
, but not l
. This is ~one full second after all three keys were released!
Then, one more second passes, and k
is added to the report. Then, almost three seconds and two full reports after the key was released, the j
is finally removed from the key report. Then k
, then, finally – five seconds after it was released – a report is sent with l
removed.
Now, I’m not complaining about any actual behaviour here; it’s utterly ridiculous to have a one-second delay added to the main loop, of course. But I’m trying to understand how this all works, and I’m stumped by this one. What’s going on here?