feat(core): Support for 64 layers
Ability to use more than 32 layers by switching variable type from uint32_t to uint64_t.
- Add macro: WRITE_BIT64
- Update layer code to use BIT64 and WRITE_BIT64
- Tested and working on a Corne Mini with 55 layers
I am a first time contributor and I am unsure if this has any deeper implications than I realize, so I'm submitting it as a draft PR. Perhaps it will be useful if anyone else makes a layout with an absurd number of layers and wonders why it is not working.
PR check-list
- [ ] Branch has a clean commit history
- [ ] Additional tests are included, if changing behaviors/core code that is testable.
- [ ] Proper Copyright + License headers added to applicable files (Generally, we stick to "The ZMK Contributors" for copyrights to help avoid churn when files get edited)
- [ ] Pre-commit used to check formatting of files, commit messages, etc.
- [ ] Includes any necessary documentation changes.
I think that rather than uint64_t, the preferred approach would be to switch it to an array of uint8_t, with the array length automatically inferred from the keymap.
If you use sizes smaller than uint32_t, then it's probably going to get padded out to 4 byte alignment anyways, so an array of uint32_t is probably better than an array of uint8_t.
Zephyr provides a bit array structure that does this, though it also adds a spinlock, which isn't really necessary here since this all runs on the same thread. https://docs.zephyrproject.org/apidoc/latest/group__bitarray__apis.html
I probably wouldn't use the bitarray functions directly, since they have a bunch of extra stuff we don't need and would take up much more space than 4 extra bytes you're using here, but you could use its implementation for reference. For example, to define an array of the correct size, you might do something like
#define ZMK_LAYER_CNT (...)
#define ZMK_LAYER_BUNDLE_CNT \
DIV_ROUND_UP(DIV_ROUND_UP(ZMK_LAYER_CNT, 8), sizeof(uint32_t))
typedef uint32_t zmk_keymap_layers_state_t[ZMK_LAYER_BUNDLE_CNT];
and then to read/write layer bits, you could write functions that divide the index by sizeof(uint32_t) * 8 to get the index of the item in the array, and the remainder of the division is the bit within that item to use with BIT() and WRITE_BIT().