gedankenexperimenter / Kaleidoscope

Firmware for the Keyboardio Model 01 and other keyboards with AVR or ARM MCUs.
http://keyboard.io
GNU General Public License v3.0
0 stars 1 forks source link

Reasons for using a pre-build script #4

Open gedankenexperimenter opened 6 years ago

gedankenexperimenter commented 6 years ago

There are a number of reasons to use a script (probably written in Perl) to generate the sketch file(s) for building the firmware, rather than relying on a strictly-C++ & preprocessor macro system. It does mean requiring Perl (or maybe Python) on the build machine, but to me that's a minor drawback.

Improved keymap readability

With a sketch builder, a user can write a keymap using single-character tokens for most of the keys, greatly improving readability of the keymap. For example:

keymap{
  Q  W  E  R  T  Y  U  I  O  P
  A  S  D  F  G  H  J  K  L  ;
  Z  X  C  V  B  N  M  ,  .
}

Some keys will still require multiple characters: e.g. space, enter, left_shift. These can be given abbreviations easily: spc, ret, sft to make the keymap easier to put on a grid.

Better plugin configuration (in keymap)

Some plugins already have (through Kaleidoscope-Ranges) a way to configure keys in the keymap, but others (e.g. Qukeys) store config data in separate arrays. This configuration requires defining keys with keymap coordinates, which is somewhat error-prone. If we could write in the keymap: qukey(A,left_shift) it would be much clearer and reduce the chances of configuring the wrong key, especially with single-byte key addressing. In this case, the pre-build script would parse the keymap, simply put Key_A in the sketch for that key, but also add the line defining the qukey elsewhere.

Dynamic (compile-time) allocation of ranges

Instead of using hard-coded ranges for special key flags in kaleidoscope::ranges, those ranges could be defined dynamically in the pre-build, possibly even with each plugin's configuration determining how big its range will be. For example, Qukeys could work by actually marking the key as such in the keymap, and storing a table with both the primary and alternate keycode elsewhere. The offset would be the index in the qukeys[] array, so it wouldn't need to search, and it would only need to request a range as big as the total number of qukeys defined in the keymap. (The drawback to this approach is that it doesn't leave room for expansion if Chrysalis allows redefinition of the keymap, but a minimum could be requested).

This would mean that any plugins that aren't used wouldn't waste any space in the Key structure.

More readable hook function calls in code

The C++ preprocessor approach certainly generates efficient code, but it's completely impenetrable to people who are not C++ template meta-programming experts. I find it virtually impossible to figure out what the code is that's generated, myself. With a fairly simple Perl script, it should be possible to generate the same "unrolled loop" (a misnomer, really), based on the plugins that are used, and the resulting code will be written to a file that will be plainly readable before the firmware is compiled, rather than being hidden behind several levels of incomprehensible macros.

More flexible hook function call ordering

For each hook that a plugin implements, it could have a configuration parameter, requesting a certain priority for that hook. For example, Qukeys should have its keyswitch event handler hook called as early as possible, before most other plugins process the event. Unshifter should have its hook run very late. With this system, it wouldn't be necessary for a user to carefully order the plugins he's using; he can just declare them all, and let the script put them in the proper order.

Furthermore, this would allow different hook functions from the same plugin to have different priorities (pre-scan would run early, but pre-report late, for example).

No need to explicitly use plugins

It might even be possible for the pre-build script to determine which plugins to use just by parsing the keymap. That should work for many plugins, anyway – maybe not for LED effects that are accessed by cycling through them via a macro.