first of all: Thank you for developing such an awesome keyboard. I ordered my M01 some weeks ago and can’t wait to receive it and start typing.
Being new around here (and the open source community, FWIW) i hope you don’t consider it rude to jump right in with a large-ish proposal.
TL;DR: I propose applying Dependency Inversion to the Kaleidoscope core. Among other effects this would make the core and most plugins independent of Arduino libraries and hardware. This would allow unit-testing on a developer machine. Additionally it would probably simplify understanding Kaleidoscope and porting it to other hardware. Would you (the maintainers) be interested in such a change?
Here comes the detailed version:
To shorten the wait for my M01, i looked around Kaleidoscope a bit. As i need the M01 to work on PCs with a German keyboard layout i was hoping to contribute in that area. While trying to get started, i found no angle of attack fitting my preferred development process (Test Driven Development) and tools (Windows, modern IDEs, no available M01 for now).
Although i like many aspects of Kaleidoscope (plugin concept, clarity of client plugin code) its current implementation has implications that i would like to see mitigated:
- I found no simple way to use a modern IDE (i.e. MSVS) for enhanced navigation, refactoring, code-completion, integration of unit-tests, etc.
- I could not figure out how to apply Test Driven Development
- I see no possibility to unit-test code without running it on the target hardware
In my eyes the main source for the latter two is Kaleidoscopes direct dependency to the Arduino headers and to Arduino specific plugins. Here’s my proposal for improving upon this:
- Create abstractions of hardware aspects in the form of interfaces (just like Kaleidoscope-Hardware.h) within the core.
- Implement those interfaces in plugins (much like Kaleidoscope-Hardware-Model01.h, but implementing kaleidoscope::Hardware instead of incidentally having a similar set of methods).
- Ensure the core only depends on the aforementioned interfaces, not on the implementing plugins.
- Move all Arduino dependencies (especially #include <Arduino.h>) down to the implementing plugins or up to the construction root.
- Bind plugins to their implemented interfaces in the construction root (*.ino files), not within the core.
By applying this kind of Dependency Inversion to all Arduino specific details, the Kaleidoscope core would become independent of the Arduino libraries and hardware.
In case any of you are not familiar with Dependency Inversion: Currently the high-level core directly depends upon low-level plugins like “Kaleidoscope-Hardware-Model01.h”. By applying the above steps, the core (which does not include the construction root) no longer depends on any particular low-level plugin. Instead, the low level plugin depends on the core, by implementing the core interface defined in “Kaleidoscope-Hardware.h”. Thus, the dependencies have been inverted from core->plugin to plugin->core.
My personal goal with this: Allow unit testing the Kaleidoscope core and client plugins in general and Test-Driving their Development in particular
Additional benefits include:
- Understanding the Kaleidoscope core becomes simpler, as it provides simple interfaces instead of depending on complex implementation plugins.
- Porting to other Hardware (e.g. virtual devices, non-Arduino keyboards but also other Arduino-hardware) becomes simpler, as the core clearly defines a set of interfaces that need to be implemented.
Whew… that became much longer than i intended. I sure hope it is understandable and you don’t mind making my first appearance like this.
In anticipation of the more frequently raised questions here:
- I fear it would have some (hopefully minor) impact on the public API. For instance i suspect “extern HARDWARE_IMPLEMENTATION KeyboardHardware;” needed to become “extern Hardware* KeyboardHardware;” in order to allow applying the necessary polymorphism.
- Probably the compilers ability to inline would be impacted by the interfaces, which might or might not lead to a measurable performance impact. If so, using “static polymorphism” could be explored as an alternative.
Please let me know if you would welcome the described change or have any questions regarding my proposal.