run formatters

This commit is contained in:
urob 2024-04-08 21:55:45 -04:00
parent 3aed3a0bd6
commit c7107524a3
6 changed files with 304 additions and 296 deletions

2
.prettierrc Normal file
View file

@ -0,0 +1,2 @@
proseWrap: "always"
editorconfig: true

268
readme.md
View file

@ -1,8 +1,8 @@
# urob's zmk-config # urob's zmk-config
This is my personal [ZMK firmware](https://github.com/zmkfirmware/zmk/) configuration. This is my personal [ZMK firmware](https://github.com/zmkfirmware/zmk/)
It consists of a 34-keys base layout that is re-used for various boards, including my configuration. It consists of a 34-keys base layout that is re-used for various
Corneish Zen and my Planck. boards, including my Corneish Zen and my Planck.
This branch is updated for the latest ZMK using Zephyr 3.5. A legacy version This branch is updated for the latest ZMK using Zephyr 3.5. A legacy version
compatible with Zephyr 3.0 is available compatible with Zephyr 3.0 is available
@ -18,13 +18,16 @@ compatible with Zephyr 3.0 is available
- ["timeless" homerow mods](#timeless-homerow-mods) - ["timeless" homerow mods](#timeless-homerow-mods)
- combos replacing the symbol layer - combos replacing the symbol layer
- smart numbers and smart mouse layers that automatically toggle off when done - smart numbers and smart mouse layers that automatically toggle off when done
- sticky shift on right thumb, double-tap (or shift + tap)[^1] activates caps-word - sticky shift on right thumb, double-tap (or shift + tap)[^1] activates
caps-word
- arrow-cluster doubles as <kbd>home</kbd>, <kbd>end</kbd>, <kbd>begin/end of - arrow-cluster doubles as <kbd>home</kbd>, <kbd>end</kbd>, <kbd>begin/end of
document</kbd> on long-press document</kbd> on long-press
- more intuitive shift-actions: <kbd>, ;</kbd>, <kbd>. :</kbd> and <kbd>? !</kbd> - more intuitive shift-actions: <kbd>, ;</kbd>, <kbd>. :</kbd> and <kbd>?
!</kbd>
- <kbd>shift</kbd> + <kbd>space</kbd> morphs into <kbd>dot</kbd> - <kbd>shift</kbd> + <kbd>space</kbd> morphs into <kbd>dot</kbd>
<kbd>space</kbd><kbd>sticky-shift</kbd> <kbd>space</kbd><kbd>sticky-shift</kbd>
- "Greek" layer for mathematical typesetting (activated as sticky-layer via a combo) - "Greek" layer for mathematical typesetting (activated as sticky-layer via a
combo)
- modified Github Actions workflow that recognizes git-submodules - modified Github Actions workflow that recognizes git-submodules
- automated - automated
[build-scripts](https://github.com/urob/zmk-config/tree/main/scripts#readme) [build-scripts](https://github.com/urob/zmk-config/tree/main/scripts#readme)
@ -34,13 +37,13 @@ compatible with Zephyr 3.0 is available
## Timeless homerow mods ## Timeless homerow mods
[Homerow mods](https://precondition.github.io/home-row-mods) (aka "HRMs") can [Homerow mods](https://precondition.github.io/home-row-mods) (aka "HRMs") can be
be a game changer -- at least in theory. In practice, they require some finicky a game changer -- at least in theory. In practice, they require some finicky
timing: In its most naive implementation, in order to produce a "mod", they timing: In its most naive implementation, in order to produce a "mod", they must
must be held *longer* than `tapping-term-ms`. In order to produce a "tap", they be held _longer_ than `tapping-term-ms`. In order to produce a "tap", they must
must be held *less* than `tapping-term-ms`. This requires very consistent be held _less_ than `tapping-term-ms`. This requires very consistent typing
typing speeds that, alas, I do not possess. Hence my quest for a "timer-less" speeds that, alas, I do not possess. Hence my quest for a "timer-less" HRM
HRM setup.[^2] setup.[^2]
After months of tweaking, I eventually ended up with a HRM setup that is After months of tweaking, I eventually ended up with a HRM setup that is
essentially timer-less, resulting in virtually no misfires. Yet it provides a essentially timer-less, resulting in virtually no misfires. Yet it provides a
@ -50,54 +53,53 @@ Let's suppose for a moment we set `tapping-term-ms` to something ridiculously
large, say 5 seconds. This makes the configuration timer-less of sorts. But it large, say 5 seconds. This makes the configuration timer-less of sorts. But it
has two problems: (1) To activate a mod we will have to hold the HRM keys for has two problems: (1) To activate a mod we will have to hold the HRM keys for
what feels like eternity. (2) During regular typing, there are delays between what feels like eternity. (2) During regular typing, there are delays between
the press of a key and the time it appears on the screen.[^3] Enter two my favorite the press of a key and the time it appears on the screen.[^3] Enter two my
ZMK features: favorite ZMK features:
* To address the first problem, I use ZMK's `balanced` flavor, which produces a
- To address the first problem, I use ZMK's `balanced` flavor, which produces a
"hold" if another key is both pressed and released within the tapping-term. "hold" if another key is both pressed and released within the tapping-term.
Because that is exactly what I normally do with HRMs, there is virtually Because that is exactly what I normally do with HRMs, there is virtually never
never a need to wait past my long tapping term (see below for two a need to wait past my long tapping term (see below for two exceptions).
exceptions). - To address the typing delay, I use ZMK's `require-prior-idle-ms` property,
* To address the typing delay, I use ZMK's `require-prior-idle-ms` property, which which immediately resolves a HRM as "tap" when it is pressed shortly _after_
immediately resolves a HRM as "tap" when it is pressed shortly *after*
another key has been tapped. This all but completely eliminates the delay. another key has been tapped. This all but completely eliminates the delay.
This is great but there are still a few rough edges: This is great but there are still a few rough edges:
* When rolling keys, I sometimes unintentionally end up with "nested" key - When rolling keys, I sometimes unintentionally end up with "nested" key
sequences: `key 1` down, `key 2` down and up, `key 1` up. Because of the sequences: `key 1` down, `key 2` down and up, `key 1` up. Because of the
`balanced` flavor, this would falsely register `key 1` as a mod. As a remedy, `balanced` flavor, this would falsely register `key 1` as a mod. As a remedy,
I use ZMK's `positional hold-tap` feature to force HRMs to always resolve as I use ZMK's `positional hold-tap` feature to force HRMs to always resolve as
"tap" when the *next* key is on the same side of the keyboard. Problem "tap" when the _next_ key is on the same side of the keyboard. Problem solved.
solved. - ... or at least almost. By default, positional-hold-tap performs the
* ... or at least almost. By default, positional-hold-tap positional check when the next key is _pressed_. This is not ideal, because it
performs the positional check when the next key is *pressed*. This is not prevents combining multiple modifiers on the same hand. To fix this, I use the
ideal, because it prevents combining multiple modifiers on the same hand. To `hold-trigger-on-release` setting, which delays the positional-hold-tap
fix this, I use the `hold-trigger-on-release` setting, which delays the decision until the next key's _release_. With the setting, multiple mods can
positional-hold-tap decision until the next key's *release*. With the setting, be combined when held, while I still get the benefit from positional-hold-tap
multiple mods can be combined when held, while I still get the benefit from when keys are tapped.
positional-hold-tap when keys are tapped. - So far, nothing of the configuration depends on the duration of
* So far, nothing of the configuration depends on the duration of
`tapping-term-ms`. In practice, there are two reasons why I don't set it to `tapping-term-ms`. In practice, there are two reasons why I don't set it to
infinity: infinity:
1. Sometimes, in rare circumstances, I want to combine a mod with a 1. Sometimes, in rare circumstances, I want to combine a mod with a alpha-key
alpha-key *on the same hand* (e.g., when using the mouse with the other _on the same hand_ (e.g., when using the mouse with the other hand). My
hand). My positional hold-tap configuration prevents this *within* the positional hold-tap configuration prevents this _within_ the tapping term.
tapping term. By setting the tapping term to something large but not crazy By setting the tapping term to something large but not crazy large (I use
large (I use 280ms), I can still use same-hand `mod` + `alpha` shortcuts by 280ms), I can still use same-hand `mod` + `alpha` shortcuts by holding the
holding the mod for just a little while before tapping the alpha-key. mod for just a little while before tapping the alpha-key.
2. Sometimes, I want to press a modifier without another key (e.g., on 2. Sometimes, I want to press a modifier without another key (e.g., on
Windows, tapping `Win` opens the search menu). Because the `balanced` Windows, tapping `Win` opens the search menu). Because the `balanced`
flavour only kicks in when another key is pressed, this also requires flavour only kicks in when another key is pressed, this also requires
waiting past `tapping-term-ms`. waiting past `tapping-term-ms`.
* Finally, it is worth noting that this setup works best in combination with a - Finally, it is worth noting that this setup works best in combination with a
dedicated shift for capitalization during normal typing (I like sticky-shift dedicated shift for capitalization during normal typing (I like sticky-shift
on a home-thumb). This is because shifting alphas is the one scenario where on a home-thumb). This is because shifting alphas is the one scenario where
pressing a mod may conflict with `require-prior-idle-ms`, which may result in pressing a mod may conflict with `require-prior-idle-ms`, which may result in
false negatives when typing fast. false negatives when typing fast.
Here's my configuration (I use a bunch of [helper Here's my configuration (I use a bunch of
macros](https://github.com/urob/zmk-nodefree-config) to simplify the syntax, but they [helper macros](https://github.com/urob/zmk-nodefree-config) to simplify the
are not necessary): syntax, but they are not necessary):
```C++ ```C++
/* use helper macros to define left and right hand keys */ /* use helper macros to define left and right hand keys */
@ -131,57 +133,61 @@ ZMK_BEHAVIOR(hmr, hold_tap,
### Required firmware ### Required firmware
After a recent round of patches, the above configuration now works with After a recent round of patches, the above configuration now works with upstream
upstream ZMK. ZMK.
Other parts of my configuration still require a few PRs that aren't yet in Other parts of my configuration still require a few PRs that aren't yet in
upstream ZMK. My personal [ZMK fork](https://github.com/urob/zmk) includes all upstream ZMK. My personal [ZMK fork](https://github.com/urob/zmk) includes all
PRs needed to compile my configuration. If you prefer to maintain your own fork PRs needed to compile my configuration. If you prefer to maintain your own fork
with a custom selection of PRs, you might find this [ZMK-centric introduction with a custom selection of PRs, you might find this
to Git](https://gist.github.com/urob/68a1e206b2356a01b876ed02d3f542c7) helpful. [ZMK-centric introduction to Git](https://gist.github.com/urob/68a1e206b2356a01b876ed02d3f542c7)
helpful.
### Troubleshooting ### Troubleshooting
Hopefully, the above configuration "just works". If it doesn't, here's a Hopefully, the above configuration "just works". If it doesn't, here's a few
few smaller (and larger) things to try. smaller (and larger) things to try.
* **Noticeable delay when tapping HRMs:** Increase `require-prior-idle-ms`. As a rule of thumb, - **Noticeable delay when tapping HRMs:** Increase `require-prior-idle-ms`. As a
you want to set it to at least `10500/x` where `x` is your (relaxed) WPM for English prose.[^4] rule of thumb, you want to set it to at least `10500/x` where `x` is your
* **False negatives (same-hand):** Reduce `tapping-term-ms` (or disable (relaxed) WPM for English prose.[^4]
- **False negatives (same-hand):** Reduce `tapping-term-ms` (or disable
`hold-trigger-key-positions`) `hold-trigger-key-positions`)
* **False negatives (cross-hand):** Reduce `require-prior-idle-ms` (or set flavor - **False negatives (cross-hand):** Reduce `require-prior-idle-ms` (or set
to `hold-preferred` -- to continue using `hold-trigger-on-release`, you must flavor to `hold-preferred` -- to continue using `hold-trigger-on-release`, you
also [patch must also
ZMK](https://github.com/celejewski/zmk/commit/d7a8482712d87963e59b74238667346221199293) [patch ZMK](https://github.com/celejewski/zmk/commit/d7a8482712d87963e59b74238667346221199293)
or use [an already patched branch](https://github.com/urob/zmk)) or use [an already patched branch](https://github.com/urob/zmk))
* **False positives (same-hand):** Increase `tapping-term-ms` - **False positives (same-hand):** Increase `tapping-term-ms`
* **False positives (cross-hand):** Increase `require-prior-idle-ms` (or set - **False positives (cross-hand):** Increase `require-prior-idle-ms` (or set
flavor to `tap-preferred`, which requires holding HRMs past tapping term to flavor to `tap-preferred`, which requires holding HRMs past tapping term to
activate) activate)
## Using combos instead of a symbol layer ## Using combos instead of a symbol layer
I am a big fan of combos for all sort of things. In terms of comfort, I much prefer them I am a big fan of combos for all sort of things. In terms of comfort, I much
over accessing layers that involve lateral thumb movements to be activated, especially prefer them over accessing layers that involve lateral thumb movements to be
when switching between different layers in rapid succession. activated, especially when switching between different layers in rapid
succession.
One common concern about overloading the layout with combos is that they lead to One common concern about overloading the layout with combos is that they lead to
misfires. Fortunately, the above-mentioned `require-prior-idle-ms` option also works misfires. Fortunately, the above-mentioned `require-prior-idle-ms` option also
for combos, which in my experience all but completely eliminates the problem -- even works for combos, which in my experience all but completely eliminates the
when rolling keys on the home row! problem -- even when rolling keys on the home row!
My combo layout aims to place the most used symbols in easy-to-access My combo layout aims to place the most used symbols in easy-to-access locations
locations while also making them easy to remember. Specifically: while also making them easy to remember. Specifically:
- the top vertical-combo row matches the symbols on a standard numbers row - the top vertical-combo row matches the symbols on a standard numbers row
(except `+` and `&` being swapped) (except `+` and `&` being swapped)
- the bottom vertical-combo row is symmetric to the top row (subscript `_` - the bottom vertical-combo row is symmetric to the top row (subscript `_`
aligns with superscript `^`; minus `-` aligns with `+`; division `/` aligns aligns with superscript `^`; minus `-` aligns with `+`; division `/` aligns
with multiplication `*`; logical-or `|` aligns with logical-and `&`) with multiplication `*`; logical-or `|` aligns with logical-and `&`)
- parenthesis, braces, brackets are set up symmetrically as horizontal combos with `<`, - parenthesis, braces, brackets are set up symmetrically as horizontal combos
`>`, `{` and `}` being accessed from the Navigation layer (or when combined with `Shift`) with `<`, `>`, `{` and `}` being accessed from the Navigation layer (or when
- left-hand side combos for `tap`, `esc`, `enter`, `cut` (on <kbd>X</kbd> + <kbd>D</kbd>), combined with `Shift`)
`copy` and `paste` that go well with right-handed mouse usage - left-hand side combos for `tap`, `esc`, `enter`, `cut` (on <kbd>X</kbd> +
<kbd>D</kbd>), `copy` and `paste` that go well with right-handed mouse usage
- <kbd>L</kbd> + <kbd>Y</kbd> switches to the Greek layer for a single key - <kbd>L</kbd> + <kbd>Y</kbd> switches to the Greek layer for a single key
press, <kbd>L</kbd> + <kbd>U</kbd> + <kbd>Y</kbd> activates one-shot shift in press, <kbd>L</kbd> + <kbd>U</kbd> + <kbd>Y</kbd> activates one-shot shift in
addition addition
@ -193,49 +199,51 @@ locations while also making them easy to remember. Specifically:
Inspired by Jonas Hietala's Inspired by Jonas Hietala's
[Numword](https://www.jonashietala.se/blog/2021/06/03/the-t-34-keyboard-layout/#where-are-the-digits) [Numword](https://www.jonashietala.se/blog/2021/06/03/the-t-34-keyboard-layout/#where-are-the-digits)
for QMK, I implemented my own version of [Smart-layers for for QMK, I implemented my own version of
ZMK](https://github.com/zmkfirmware/zmk/pull/1451). It is triggered via a [Smart-layers for ZMK](https://github.com/zmkfirmware/zmk/pull/1451). It is
single tap on "Smart-Num". Numword continues to be activated as long as I triggered via a single tap on "Smart-Num". Numword continues to be activated as
type numbers, and deactivates automatically on any other keypress (holding it activates long as I type numbers, and deactivates automatically on any other keypress
a non-sticky num layer). (holding it activates a non-sticky num layer).
After using Numword for more than a year now, I have been overall very happy with it. When After using Numword for more than a year now, I have been overall very happy
typing single digits, it effectively is a sticky-layer but with the added advantage that with it. When typing single digits, it effectively is a sticky-layer but with
I can also use it to type multiple digits. the added advantage that I can also use it to type multiple digits.
The main downside is that if a sequence of numbers is *immediately* followed by any of the
letters on which my numpad is located (WFPRSTXCD), then the automatic deactivation won't
work. But this is rare -- most number sequences are terminated by `space`, `return` or some form
of punctuation/delimination. To deal with the rare cases where they aren't, there is a
`CANCEL` key on the navigation-layer that deactivates Numword, Capsword and Smart-mouse.
(It also toggles off when pressing `Numword` again, but I find it cognitively easier to
have a dedicated "off-switch" than keeping track of which modes are currently active.)
The main downside is that if a sequence of numbers is _immediately_ followed by
any of the letters on which my numpad is located (WFPRSTXCD), then the automatic
deactivation won't work. But this is rare -- most number sequences are
terminated by `space`, `return` or some form of punctuation/delimination. To
deal with the rare cases where they aren't, there is a `CANCEL` key on the
navigation-layer that deactivates Numword, Capsword and Smart-mouse. (It also
toggles off when pressing `Numword` again, but I find it cognitively easier to
have a dedicated "off-switch" than keeping track of which modes are currently
active.)
##### Smart-Mouse ##### Smart-Mouse
Similarly to Numword, I have a smart-mouse layer (activated by comboing Similarly to Numword, I have a smart-mouse layer (activated by comboing
<kbd>W</kbd> + <kbd>P</kbd>), which replaces the navigation cluster with <kbd>W</kbd> + <kbd>P</kbd>), which replaces the navigation cluster with scroll
scroll and mouse-movements, and replaces the right thumbs with mouse buttons. and mouse-movements, and replaces the right thumbs with mouse buttons. Pressing
Pressing any other key automatically deactivates the layer. any other key automatically deactivates the layer.
##### Capsword ##### Capsword
My right thumb triggers three variations of shift: Tapping yields My right thumb triggers three variations of shift: Tapping yields sticky-shift
sticky-shift (used to capitalize alphas), holding activates a regular shift, and (used to capitalize alphas), holding activates a regular shift, and
double-tapping (or equivalently shift + tap) activates ZMK's Caps-word behavior. double-tapping (or equivalently shift + tap) activates ZMK's Caps-word behavior.
One minor technical detail: While it would be possible to implement the double-tap functionality One minor technical detail: While it would be possible to implement the
as a tap-dance, this would add a delay when using single taps. To avoid the delays, I double-tap functionality as a tap-dance, this would add a delay when using
instead implemented the double-tap functionality as a mod-morph. single taps. To avoid the delays, I instead implemented the double-tap
functionality as a mod-morph.
##### Multi-purpose Navigation cluster ##### Multi-purpose Navigation cluster
To economize on keys, I am using hold-taps on my navigation cluster, which yield `home`, `end`, To economize on keys, I am using hold-taps on my navigation cluster, which yield
`begin/end of document`, and `delete word forward/backward` on long-presses. The exact `home`, `end`, `begin/end of document`, and `delete word forward/backward` on
implementation is tweaked so that `Ctrl` is silently absorbed in combination with `home` and `end` long-presses. The exact implementation is tweaked so that `Ctrl` is silently
to avoid accidental document-wide operations (which are accessible via the dedicated `begin/end absorbed in combination with `home` and `end` to avoid accidental document-wide
document keys`.) operations (which are accessible via the dedicated `begin/end document keys`.)
##### Swapper ##### Swapper
@ -245,19 +253,20 @@ one-handed Alt-Tab switcher (`PWin` and `NWin`).
##### Repeat ##### Repeat
I recently switched to 25g-chocs on one of my keyboards. I already was very happy with I recently switched to 25g-chocs on one of my keyboards. I already was very
my combos prior to that (even with heavy-ish MX-switches). But with the light chocs, I happy with my combos prior to that (even with heavy-ish MX-switches). But with
find that I can now even use them for regular typing. While I haven't yet tried the light chocs, I find that I can now even use them for regular typing. While I
placing alphas on combos, I am currently experimenting with a `repeat` combo on haven't yet tried placing alphas on combos, I am currently experimenting with a
my home row that I use to reduce SFUs when typing double-letter words. `repeat` combo on my home row that I use to reduce SFUs when typing
double-letter words.
## Issues and workarounds ## Issues and workarounds
Since I switched from QMK to ZMK I have been very impressed with how easy it is Since I switched from QMK to ZMK I have been very impressed with how easy it is
to set up relatively complex layouts in ZMK. For the most parts I don't miss to set up relatively complex layouts in ZMK. For the most parts I don't miss any
any functionality (to the contrary, I found that ZMK supports many features functionality (to the contrary, I found that ZMK supports many features natively
natively that would require complex user-space implementations in QMK). Below that would require complex user-space implementations in QMK). Below are a few
are a few remaining issues: remaining issues:
- ZMK does not yet support "tap-only" combos - ZMK does not yet support "tap-only" combos
([#544](https://github.com/zmkfirmware/zmk/issues/544)), requiring a brief ([#544](https://github.com/zmkfirmware/zmk/issues/544)), requiring a brief
@ -267,8 +276,8 @@ are a few remaining issues:
dynamically adding/removing mods doesn't work well). Having a native solution dynamically adding/removing mods doesn't work well). Having a native solution
akin to QMK's "COMBO_MUST_TAP" property would be fantastic. akin to QMK's "COMBO_MUST_TAP" property would be fantastic.
- Another item on my wishlist are adaptive keys - Another item on my wishlist are adaptive keys
([#1624](https://github.com/zmkfirmware/zmk/issues/1624)). This would open ([#1624](https://github.com/zmkfirmware/zmk/issues/1624)). This would open the
the door for things like <kbd>space</kbd><kbd>space</kbd> becoming door for things like <kbd>space</kbd><kbd>space</kbd> becoming
<kbd>.</kbd><kbd>space</kbd><kbd>sticky-shift</kbd>. (Using tap-dance isn't <kbd>.</kbd><kbd>space</kbd><kbd>sticky-shift</kbd>. (Using tap-dance isn't
really an option here due to the delay it adds) really an option here due to the delay it adds)
- A minor thing is that ZMK doesn't yet support any keys on the - A minor thing is that ZMK doesn't yet support any keys on the
@ -278,29 +287,34 @@ are a few remaining issues:
- Very minor: `&bootloader` doesn't work with stm32 boards like the Planck - Very minor: `&bootloader` doesn't work with stm32 boards like the Planck
([#1086](https://github.com/zmkfirmware/zmk/issues/1086)) ([#1086](https://github.com/zmkfirmware/zmk/issues/1086))
[^1]: Really what's happening is that `Shift` + my right home-thumb morph into [^1]:
Really what's happening is that `Shift` + my right home-thumb morph into
caps-word. This gives me two separate ways of activating it: (1) Holding the caps-word. This gives me two separate ways of activating it: (1) Holding the
homerow-mod shift on my left index-finger and then pressing my right home-thumb, which homerow-mod shift on my left index-finger and then pressing my right
is my new preferred way. Or, (2) double-tapping the right home-thumb, which also works home-thumb, which is my new preferred way. Or, (2) double-tapping the right
because the first tap yields sticky-shift, activating the mod-morph upon the second home-thumb, which also works because the first tap yields sticky-shift,
tap. But even when only activating via double-tapping, this implementation is advantageous activating the mod-morph upon the second tap. But even when only activating
compared to using tap-dance as it does not create any delay when single-tapping the key. via double-tapping, this implementation is advantageous compared to using
tap-dance as it does not create any delay when single-tapping the key.
[^2]: I call it "timer-less", because the large tapping-term makes the behavior [^2]:
I call it "timer-less", because the large tapping-term makes the behavior
insensitive to the precise timings. One may say that there is still the insensitive to the precise timings. One may say that there is still the
`require-prior-idle` timeout. However, with both a large tapping-term and `require-prior-idle` timeout. However, with both a large tapping-term and
positional-hold-taps, the behavior is *not* actually sensitive to the positional-hold-taps, the behavior is _not_ actually sensitive to the
`require-prior-idle` timing: All it does is reduce the delay in typing; i.e., variations `require-prior-idle` timing: All it does is reduce the delay in typing;
in typing speed won't affect *what* is being typed but merely *how fast* it appears on i.e., variations in typing speed won't affect _what_ is being typed but
the screen. merely _how fast_ it appears on the screen.
[^3]: The delay is determined by how quickly a key is released and is not [^3]:
directly related to the tapping-term. But regardless of its length, most The delay is determined by how quickly a key is released and is not directly
people still find it noticable and disruptive. related to the tapping-term. But regardless of its length, most people still
find it noticable and disruptive.
[^4]: E.g, if your WPM is 70 or larger, then the default of 150ms (=10500/70) [^4]:
E.g, if your WPM is 70 or larger, then the default of 150ms (=10500/70)
should work well. The rule of thumb is based on an average character length should work well. The rule of thumb is based on an average character length
of 4.7 for English words. Taking into account 1 extra tap for `space`, this of 4.7 for English words. Taking into account 1 extra tap for `space`, this
yields a minimum `require-prior-idle-ms` of (60 * 1000) / (5.7 * x) ≈ 10500 / x yields a minimum `require-prior-idle-ms` of (60 _ 1000) / (5.7 _ x) ≈ 10500
milliseconds. The approximation errs on the safe side, / x milliseconds. The approximation errs on the safe side, as in practice
as in practice home row taps tend to be faster than average. home row taps tend to be faster than average.

View file

@ -1,22 +1,23 @@
This folder contains scripts that automate installing and building using a This folder contains scripts that automate installing and building using a local
local toolchain. The scripts provide an alternative to using [Github toolchain. The scripts provide an alternative to using
Actions](https://zmk.dev/docs/user-setup#installing-the-firmware) and the [Github Actions](https://zmk.dev/docs/user-setup#installing-the-firmware) and
[developer toolchain](https://zmk.dev/docs/development/setup). the [developer toolchain](https://zmk.dev/docs/development/setup).
If the zmk-config repo contains a `combos.dtsi` file, the script will also automatically If the zmk-config repo contains a `combos.dtsi` file, the script will also
update the `MAX_COMBOS_PER_KEY` and `MAX_KEYS_PER_COMBO` settings for all boards, automatically update the `MAX_COMBOS_PER_KEY` and `MAX_KEYS_PER_COMBO` settings
depending on the combos specified in `combos.dtsi`. for all boards, depending on the combos specified in `combos.dtsi`.
## Build steps ## Build steps
### 1. Clone the ZMK repository ### 1. Clone the ZMK repository
Clone the ZMK repository and checkout the branch that you want to build Clone the ZMK repository and checkout the branch that you want to build against.
against. By default the build script will look for the ZMK repo in `~/zmk`. By default the build script will look for the ZMK repo in `~/zmk`. Other
Other locations can be specified with `--host-zmk-dir` or by changing the locations can be specified with `--host-zmk-dir` or by changing the default
default location for `HOST_ZMK_DIR` inside the script. location for `HOST_ZMK_DIR` inside the script.
For example, to build against my `main-3.2` branch, run: For example, to build against my `main-3.2` branch, run:
```bash ```bash
cd "$HOME" cd "$HOME"
git clone https://github.com/urob/zmk git clone https://github.com/urob/zmk
@ -25,11 +26,12 @@ git checkout main-3.2
### 2. Clone your zmk-config repository ### 2. Clone your zmk-config repository
By default the build script will look for the zmk-config repo in By default the build script will look for the zmk-config repo in `~/zmk-config`.
`~/zmk-config`. Other locations can be specified with `--host-config-dir` or by Other locations can be specified with `--host-config-dir` or by changing the
changing the default location for `HOST_CONFIG_DIR` inside the script. default location for `HOST_CONFIG_DIR` inside the script.
For example, to use my zmk-config repo, run: For example, to use my zmk-config repo, run:
```bash ```bash
cd "$HOME" cd "$HOME"
git clone https://github.com/urob/zmk-config git clone https://github.com/urob/zmk-config
@ -41,47 +43,48 @@ The build script can be used to install either a "docker" or a "local"
toolchain. If unsure, I recommend using the docker toolchain. Depending on your toolchain. If unsure, I recommend using the docker toolchain. Depending on your
installation choice, do **one** of the following: installation choice, do **one** of the following:
1. Install Docker or Podman (recommended) and, if using Podman, configure the docker 1. Install Docker or Podman (recommended) and, if using Podman, configure the
registry. On Debian or Ubuntu, you can do both by running: docker registry. On Debian or Ubuntu, you can do both by running:
```bash ```bash
sudo apt-get install podman sudo apt-get install podman
echo 'unqualified-search-registries = ["docker.io"]' > $XDG_CONFIG_HOME/containers/registries.conf echo 'unqualified-search-registries = ["docker.io"]' > $XDG_CONFIG_HOME/containers/registries.conf
``` ```
2. Install a local [developer 2. Install a local
toolchain](https://zmk.dev/docs/development/setup). The [developer toolchain](https://zmk.dev/docs/development/setup). The
`zmk_local_install.sh` script in this repository automates the process for `zmk_local_install.sh` script in this repository automates the process for
Debian-based operating system. Debian-based operating system.
### 4. Run the build script ### 4. Run the build script
Run the `zmk_build.sh` script to build your boards. By default, the script will Run the `zmk_build.sh` script to build your boards. By default, the script will
build all boards specified in `build.yaml` in your `zmk-config` repo. The default can be build all boards specified in `build.yaml` in your `zmk-config` repo. The
overwritten with the `-b` option. default can be overwritten with the `-b` option.
If using docker/podman, the script will pull down the required dependencies the first If using docker/podman, the script will pull down the required dependencies the
time it is used. The script will automatically detect whether the build first time it is used. The script will automatically detect whether the build
requirement have changed, and will only re-download the dependencies if needed. requirement have changed, and will only re-download the dependencies if needed.
In order to easily switch between multiple ZMK branches that have different In order to easily switch between multiple ZMK branches that have different
build requirements, one can specify the desired Zephyr version using the `-v` build requirements, one can specify the desired Zephyr version using the `-v`
option. Docker container and volumes are index by the Zephyr version, so option. Docker container and volumes are index by the Zephyr version, so
switching between Zephyr version won't require re-downloading new dependencies. switching between Zephyr version won't require re-downloading new dependencies.
In order to force re-installing all build requirements, pass the `-c` option, which will In order to force re-installing all build requirements, pass the `-c` option,
wipe out the Docker container and volume. which will wipe out the Docker container and volume.
By default the script will copy the firmware into the `OUTPUT_DIR` folder By default the script will copy the firmware into the `OUTPUT_DIR` folder
specified in the script. Other locations can be specified using the specified in the script. Other locations can be specified using the
`--output-dir` argument. `--output-dir` argument.
To switch between Docker and Podman, set the `DOCKER_BIN` variable in the To switch between Docker and Podman, set the `DOCKER_BIN` variable in the script
script (defaults to `podman`). If using Docker and the user is not in the (defaults to `podman`). If using Docker and the user is not in the docker-group,
docker-group, then one can use Docker in sudo-mode by using the `-s` flag for then one can use Docker in sudo-mode by using the `-s` flag for the script. If
the script. If using Podman, running in rootless mode is recommended. using Podman, running in rootless mode is recommended.
One can pass custom options to `west` by preluding them with `--`. One can pass custom options to `west` by preluding them with `--`.
For example, to build my boards using Zephyr version 3.2 in sudo mode and pass For example, to build my boards using Zephyr version 3.2 in sudo mode and pass
the "pristine" option to west, run: the "pristine" option to west, run:
```bash ```bash
zmk_build.sh -s -v 3.2 -- -p zmk_build.sh -s -v 3.2 -- -p
``` ```
@ -91,11 +94,12 @@ See the script for a full set of options.
## Developing interactively using Docker ## Developing interactively using Docker
The docker container can be entered interactively using with all the necessary The docker container can be entered interactively using with all the necessary
mounts using: The script shares a build environment with the build script mounts using: The script shares a build environment with the build script (again
(again indexed by Zephyr versions). indexed by Zephyr versions).
For example, to start an interactive Docker session in sudo mode using Zephyr For example, to start an interactive Docker session in sudo mode using Zephyr
version 3.2, run: version 3.2, run:
```bash ```bash
zmk_run_docker.sh -s -v 3.2 zmk_run_docker.sh -s -v 3.2
``` ```

View file

@ -103,24 +103,23 @@ DOCKER_BIN="$SUDO podman"
cd "$HOST_CONFIG_DIR" cd "$HOST_CONFIG_DIR"
if [[ -f config/combos.dtsi ]] if [[ -f config/combos.dtsi ]]; then
# update maximum combos per key # update maximum combos per key
then count=$(
count=$( \ tail -n +10 config/combos.dtsi |
tail -n +10 config/combos.dtsi | \ grep -Eo '[LR][TMBH][0-9]' |
grep -Eo '[LR][TMBH][0-9]' | \ sort | uniq -c | sort -nr |
sort | uniq -c | sort -nr | \ awk 'NR==1{print $1}'
awk 'NR==1{print $1}' \
) )
sed -Ei "/CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY/s/=.+/=$count/" config/*.conf sed -Ei "/CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY/s/=.+/=$count/" config/*.conf
echo "Setting MAX_COMBOS_PER_KEY to $count" echo "Setting MAX_COMBOS_PER_KEY to $count"
# update maximum keys per combo # update maximum keys per combo
count=$( \ count=$(
tail -n +10 config/combos.dtsi | \ tail -n +10 config/combos.dtsi |
grep -o -n '[LR][TMBH][0-9]' | \ grep -o -n '[LR][TMBH][0-9]' |
cut -d : -f 1 | uniq -c | sort -nr | \ cut -d : -f 1 | uniq -c | sort -nr |
awk 'NR==1{print $1}' \ awk 'NR==1{print $1}'
) )
sed -Ei "/CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO/s/=.+/=$count/" config/*.conf sed -Ei "/CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO/s/=.+/=$count/" config/*.conf
echo "Setting MAX_KEYS_PER_COMBO to $count" echo "Setting MAX_KEYS_PER_COMBO to $count"
@ -130,8 +129,7 @@ fi
# | BUILD THE FIRMWARE | # | BUILD THE FIRMWARE |
# +--------------------+ # +--------------------+
if [[ $RUNWITH_DOCKER = true ]] if [[ $RUNWITH_DOCKER = true ]]; then
then
echo "Build mode: docker" echo "Build mode: docker"
# DOCKER_CMD="$DOCKER_BIN run --name zmk-$ZEPHYR_VERSION --rm \ # DOCKER_CMD="$DOCKER_BIN run --name zmk-$ZEPHYR_VERSION --rm \
DOCKER_CMD="$DOCKER_BIN run --rm \ DOCKER_CMD="$DOCKER_BIN run --rm \
@ -143,8 +141,7 @@ then
--mount type=volume,source=zmk-zephyr-tools-$ZEPHYR_VERSION,target=$DOCKER_ZMK_DIR/tools" --mount type=volume,source=zmk-zephyr-tools-$ZEPHYR_VERSION,target=$DOCKER_ZMK_DIR/tools"
# Reset volumes # Reset volumes
if [[ $CLEAR_CACHE = true ]] if [[ $CLEAR_CACHE = true ]]; then
then
$DOCKER_BIN volume rm $($DOCKER_BIN volume ls -q | grep "^zmk-.*-$ZEPHYR_VERSION$") $DOCKER_BIN volume rm $($DOCKER_BIN volume ls -q | grep "^zmk-.*-$ZEPHYR_VERSION$")
fi fi
@ -177,12 +174,10 @@ compile_board () {
[[ $MULTITHREAD = "true" ]] && echo -e "$(tput setaf 2)Building $1... $(tput sgr0)" [[ $MULTITHREAD = "true" ]] && echo -e "$(tput setaf 2)Building $1... $(tput sgr0)"
$DOCKER_PREFIX west build -d "build/$BUILD_DIR" -b $1 $WEST_OPTS \ $DOCKER_PREFIX west build -d "build/$BUILD_DIR" -b $1 $WEST_OPTS \
-- -DZMK_CONFIG="$CONFIG_DIR" -Wno-dev >"$LOGFILE" 2>&1 -- -DZMK_CONFIG="$CONFIG_DIR" -Wno-dev >"$LOGFILE" 2>&1
if [[ $? -eq 0 ]] if [[ $? -eq 0 ]]; then
then
[[ $MULTITHREAD = "true" ]] || echo "$(tput setaf 2)done$(tput sgr0)" [[ $MULTITHREAD = "true" ]] || echo "$(tput setaf 2)done$(tput sgr0)"
echo "Build log saved to \"$LOGFILE\"." echo "Build log saved to \"$LOGFILE\"."
if [[ -f $HOST_ZMK_DIR/app/build/$BUILD_DIR/zephyr/zmk.uf2 ]] if [[ -f $HOST_ZMK_DIR/app/build/$BUILD_DIR/zephyr/zmk.uf2 ]]; then
then
TYPE="uf2" TYPE="uf2"
else else
TYPE="bin" TYPE="bin"
@ -200,8 +195,7 @@ compile_board () {
cd "$HOST_ZMK_DIR/app" cd "$HOST_ZMK_DIR/app"
if [[ $MULTITHREAD = "true" ]]; then if [[ $MULTITHREAD = "true" ]]; then
i=1 i=1
for board in $(echo $BOARDS | sed 's/,/ /g') for board in $(echo $BOARDS | sed 's/,/ /g'); do
do
compile_board $board & compile_board $board &
eval "T${i}=\${!}" eval "T${i}=\${!}"
eval "B${i}=\$board" # Store the board name in a corresponding variable eval "B${i}=\$board" # Store the board name in a corresponding variable
@ -209,16 +203,14 @@ if [[ $MULTITHREAD = "true" ]]; then
done done
echo "Starting $(($i - 1)) background threads:" echo "Starting $(($i - 1)) background threads:"
for ((x=1; x<i; x++)) for ((x = 1; x < i; x++)); do
do
pid="T$x" pid="T$x"
wait "${!pid}" wait "${!pid}"
board="B$x" # Retrieve the board name from the corresponding variable board="B$x" # Retrieve the board name from the corresponding variable
echo -e "$(tput setaf 3)Thread $x with PID ${!pid} has finished: ${!board}$(tput sgr0)" echo -e "$(tput setaf 3)Thread $x with PID ${!pid} has finished: ${!board}$(tput sgr0)"
done done
else else
for board in $(echo $BOARDS | sed 's/,/ /g') for board in $(echo $BOARDS | sed 's/,/ /g'); do
do
compile_board $board compile_board $board
done done
fi fi

View file

@ -55,4 +55,3 @@ pip3 install --user -r zephyr/scripts/requirements.txt # see above
sudo apt-get install --yes --no-install-recommends npm sudo apt-get install --yes --no-install-recommends npm
cd ~/zmk/docs cd ~/zmk/docs
npm ci npm ci

View file

@ -76,8 +76,7 @@ DOCKER_CMD="$SUDO docker run --name zmk-$ZEPHYR_VERSION --rm \
--mount type=volume,source=zmk-zephyr-tools-$ZEPHYR_VERSION,target=$DOCKER_ZMK_DIR/tools" --mount type=volume,source=zmk-zephyr-tools-$ZEPHYR_VERSION,target=$DOCKER_ZMK_DIR/tools"
# Reset volumes # Reset volumes
if [[ $CLEAR_CACHE = true ]] if [[ $CLEAR_CACHE = true ]]; then
then
$SUDO docker volume rm $(sudo docker volume ls -q | grep "^zmk-.*-$ZEPHYR_VERSION$") $SUDO docker volume rm $(sudo docker volume ls -q | grep "^zmk-.*-$ZEPHYR_VERSION$")
fi fi
@ -91,11 +90,9 @@ $DOCKER_CMD -w "$DOCKER_ZMK_DIR" "$DOCKER_IMG" /bin/bash -c " \
&& west update" && west update"
# Install docosaurus # Install docosaurus
if [[ $DOC_TOOLS = true ]] if [[ $DOC_TOOLS = true ]]; then
then
$DOCKER_CMD -w "$DOCKER_ZMK_DIR/docs" "$DOCKER_IMG" npm ci $DOCKER_CMD -w "$DOCKER_ZMK_DIR/docs" "$DOCKER_IMG" npm ci
fi fi
# Start interactive shell # Start interactive shell
$DOCKER_CMD -w "$DOCKER_ZMK_DIR" -it "$DOCKER_IMG" /bin/bash $DOCKER_CMD -w "$DOCKER_ZMK_DIR" -it "$DOCKER_IMG" /bin/bash