owned this note
owned this note
Published
Linked with GitHub
# LAYOUT Replacement
This document discusses what the system that will supplant `LAYOUT` macros will look like.
# Background
QMK currently stores keymaps as a 3-dimensional array. The outer arrays are layers, the middle arrays are matrix rows, and the inner arrays are matrix columns. It looks like this:
```c
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = {
{KC_1, KC_2, KC_3},
{KC_Q, KC_W, KC_E}
},
[1] = {
{KC_F1, KC_F2, KC_F3},
{KC_TRNS, KC_TRNS, KC_TRNS}
}
}
```
By convention we represent this 3-dimenional array as 2 dimensions using a preprocessor macro called `LAYOUT()`. This gives us an elegant way to handle unused matrix locations and to have keymaps that match the physical key layout even when the matrix wiring does not.
A typical LAYOUT macro:
```c
#define LAYOUT( \
k00, k01, k02, \
k10, k11, k12 \
) { \
{ k00, k01, k02 },
{ k10, k11, k12 }
}
```
A typical LAYOUT macro in use:
```c
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT(
KC_1, KC_2, KC_3,
KC_Q, KC_W, KC_E
),
[1] = LAYOUT(
KC_F1, KC_F2, KC_F3,
KC_TRNS, KC_TRNS, KC_TRNS
)
}
```
### Limitations of LAYOUT Macros
These preprocessor macros work great for many situations, but we have a number of pain points we continually encounter:
* Keyboard maintainers often don't understand them
* Sharing keymaps between keyboards with differing number and/or order of keys is difficult at best
* Anti-patterns like `LAYOUT_kc()` abuse the preprocessor macros
* Boards with a lot of layout options can require dozens of `LAYOUT()` macros
# The Solution: Matrix data in info.json
Currently layout data in `info.json` looks like this:
```json
{
"LAYOUT_all": [
{"x": 0, "y": 0, "w": 1, "label": "1"},
{"x": 1, "y": 0, "w": 1, "label": "2"},
{"x": 2, "y": 0, "w": 1, "label": "3"},
{"x": 0, "y": 1, "w": 1, "label": "Q"},
{"x": 1, "y": 1, "w": 1, "label": "W"},
{"x": 2, "y": 1, "w": 1, "label": "E"}
]
```
This solution extends this with a new key, `matrix`, containing a 2 item array of the form:
[<row>, <column>]
The resulting layout in `info.json` will look like this:
```json
{
"LAYOUT_all": [
{"x": 0, "y": 0, "w": 1, "label": "1", "matrix": [0, 0]},
{"x": 1, "y": 0, "w": 1, "label": "2", "matrix": [1, 0]},
{"x": 2, "y": 0, "w": 1, "label": "3", "matrix": [2, 0]},
{"x": 0, "y": 1, "w": 1, "label": "Q", "matrix": [0, 1]},
{"x": 1, "y": 1, "w": 1, "label": "W", "matrix": [1, 1]},
{"x": 2, "y": 1, "w": 1, "label": "E", "matrix": [2, 1]}
]
}
```
# Further Improvement: Standarize and require label names
By standardizing and requiring label names we will be able to evolve the JSON keymap format. Currently JSON keymaps are tied to `LAYOUT()` arrays, so it's difficult to share them between keyboards. If we defined them by location, much in the same way scancodes represent a key's location and not its function, we could share keymap between different keyboards.
Currently, our largest board is the 15x15 donutcables/scrabblepad, with 225 keys. If we define 255 labels we can represent them with an 8 bit number, should the need arise. Then we can associate these labels with standard locations on a keyboard and keyboard designers can map their keys to whatever makes the most sense.
I've setup a skeleton layout that is currently incomplete with some example layouts filled in below:
http://www.keyboard-layout-editor.com/#/gists/b8b68d084402233c9018704c8a698b60
# Next Step: Layout Options
Most keyboards that support multiple layouts use `LAYOUT_all` for the name of the layout that supports every possible key on the keyboard. They then define alternative layouts for other build configurations, for example it's common to see `LAYOUT_60_ansi` and `LAYOUT_60_iso` supported by the same board, with `LAYOUT_all` being a superset of the two. When a keyboard has multiple build options that may be combined in unique ways the number of required layouts begins to bloom.
Layout options will be a new addition to `info.json` that allow keyboard maintainers to define replacements for existing layout sections. It will specify the keys to replace using a list of label names, and a list of layout objects to put in its place. Layout options will always be applied to `LAYOUT`. If a keyboard does not have a `LAYOUT` macro it will not be able to use layout options.
The `LAYOUT` macro may contain every possible switch position (the way `LAYOUT_all` is used today) or it may only have the most common build configuration and use layout option to expose keys that aren't available in the base layout. The choice of which pattern to use is up to the keyboard maintainer(s).
Example:
```json
{
"keyboard": "handwired/test",
"layouts": {
"LAYOUT": {"layout": [
{"x": 0, "y": 0, "w": 1, "label": "1", "matrix": [0, 0]},
{"x": 1, "y": 0, "w": 1, "label": "2", "matrix": [1, 0]},
{"x": 2, "y": 0, "w": 1, "label": "3", "matrix": [2, 0]},
{"x": 0, "y": 1, "w": 1, "label": "Q", "matrix": [0, 1]},
{"x": 1, "y": 1, "w": 1, "label": "W", "matrix": [1, 1]},
{"x": 2, "y": 1, "w": 1, "label": "E", "matrix": [2, 1]}
]}
},
"layout_options": {
"top_row": {
"display": "Top Row",
"options": [
"1key_top": {
"display": "2U key on top",
"replaces": ["1", "2", "3"],
"layout": [
{"x": 0.5, "y": 0, "w": 2, "label": "2", "matrix": [1, 0]}
]
},
"2key_top": {
"display": "1.5U keys on top",
"replaces": ["1", "2", "3"],
"layout": [
{"x": 0, "y": 0, "w": 1.5, "label": "2", "matrix": [0, 0]},
{"x": 1.5, "y": 1, "w": 1.5, "label": "2", "matrix": [2, 0]}
]
},
]
},
"bottom_row": {
"display": "Bottom Row",
"options": [
"1key_bottom": {
"display": "2U key on bottom",
"replaces": ["Q", "W", "E"],
"layout": [
{"x": 1, "y": 1, "w": 2, "label": "Q", "matrix": [1, 1]}
]
},
"2key_bottom": {
"display": "1.5U keys on bottom",
"replaces": ["Q", "W", "E"],
"layout": [
{"x": 0, "y": 1, "w": 1.5, "label": "Q", "matrix": [0, 1]},
{"x": 1.5, "y": 1, "w": 1.5, "label": "E", "matrix": [2, 1]}
]
},
]
}
}
}
```
## KLE2JSON improvements
To facilitate creating these layout options we should enhance the converter so that it can process a superset of the [VIA KLE format](https://caniusevia.com/docs/layouts/). I've put together an example KLE that will work with both VIA and this proposed `qmk kle2json` improvement:
<http://www.keyboard-layout-editor.com/#/gists/70aaa4bed76d0b2f67fd165641239552>
To the existing fields that VIA uses I have added two more:
* Center Left: Label
* Front Left: Layout Option Name
We will combine these bits with the options that VIA specifies to generate a full info.json for a given keyeboard.
## FAQs
#### Will this work with C keymaps?
Yes, every layout defined in `info.json` will have `LAYOUT()` macros generated automatically. They will be removed from the `<keyboard>.h` file. In the [example above](#next-step-layout-options) a C keymap would be able to use `LAYOUT_1key_top`, `LAYOUT_2key_bottom`, or `LAYOUT_1key_top_2key_bottom`, and we will build the proper LAYOUT macro for it.
#### Can I still define pre-processor macros in <keyboard>.h?
Yes, but they won't be available to configurator or JSON keymaps.