It’s been a while since I looked closely at it, but it made sense at the time from an efficiency point of view. Maybe it simplified the math (for the machine, not for the humans). I should add that it also alternates between keys on the left side and right side. This last is so that a single index can be used to iterate through the keys on both sides.
(To be clear, I’m not the one who wrote the code in question, so I can’t give an authoritative answer.)
Here’s a better answer:
In the hardware, the row indexing is backwards from what it is in the firmware. So the
prog key is actually row 0, column 7. The keyscanner stores the states of the keyswitches in a bitfield for each half of the Model01 in hardware order, so the first bit corresponds to one of the thumb keys, and the second bit is the rightmost key on the top row. It’s a bit confusing that they’re not mirror images, but it does make this bit of code simpler.
The reason the keys are processed in this order (by decreasing column index instead of increasing) was easier to see before the idle key optimizations; the bit shift operations in
actOnHalfRow() obscure things a bit. Basically, we’re going through the bits in each byte (representing a row), but the bits in that byte are in the opposite order of the column index numbers that we need to use when calling
handleKeyswitchEvent(). We could change it so that we read the high bit instead of the low bit, do the bit shift in the other direction, and process the rows left to right, but that would have been a bigger change from the older code, for which that change was impractical.
Due to the design of the firmware, any scan order is equally good/bad.
The actual order mostly matters when debugging, e.g. to find out if scan order matters with respect to specific problems like the one that rumpel mentioned.
To be robust, and if possible, plugins should in general be written in a scan order agnostic way.
Indeed. Unfortunately, doing so is not straightforward. I took pains to do so with Qukeys, but in order to accomplish that, I had to break an abstraction barrier and explicitly send modified copies of previous key reports, rather than properly using the HID façade. Most of the core plugins don’t do what’s necessary to avoid scan order bugs, and neither does Kaleidoscope itself (when dealing with keys that use modifier flags, there are rollover issues).
Exactly, I am also currently dealing with such problems. Thereby, I am really, really happy to have a simulator with perfect simulated firmware timing. It was definitely worth spending all the time on its development, before I started working on my Papageno plugin.
The simulator helps a lot, especially when dealing with strange corner cases, e.g. the rollover issues you mentioned. For each such issue I write a little test case to make sure that nothing breaks, when I implement a workaround for the next one. That way I hope I will eventually eliminate all issues and I am pretty close. But the design is pretty brittle as I also had to break some abstraction.
Without a simulator, what I have in mind (and already working with QMK) would not be impossible (at least not in a reasonable amount of time). Not speaking about the time the simulator save by avoiding the upload and manual testing.
To me it appears, that the USB HID interface is the root of most issues. But I know, there is no other way to run a portable keyboard at this time. The way the mapping of key events via keycodes to keyboard reports works, is really a nightmare when dealing with complex tasks. By design there is no way to allow a greater number of plugins to co-operate safely.
Unfortunately, I didn’t come up with a better way to do this that is equally well performing, yet more safe and robust. You mentioned here and there that you are/have been working on an event-based firmware fork? Did you make any progress? And did you publish your approach somewhere (apart from the code)?
So, now that some time has passed, who is using which german keyboard layout and are you happy with it?