diff --git a/config/boards/shields/leko_pona/leko_pona.keymap b/config/boards/shields/leko_pona/leko_pona.keymap index 59fa667..51705a2 100644 --- a/config/boards/shields/leko_pona/leko_pona.keymap +++ b/config/boards/shields/leko_pona/leko_pona.keymap @@ -2,6 +2,8 @@ #include #include +#include "../zmk-nodefree-config/helper.h" + #define L0 0 #define L1 1 #define L2 2 diff --git a/config/west.yml b/config/west.yml index 379d291..a7b0cdf 100644 --- a/config/west.yml +++ b/config/west.yml @@ -1,11 +1,11 @@ manifest: remotes: - - name: zmkfirmware - url-base: https://github.com/zmkfirmware + - name: urob + url-base: https://github.com/urob projects: - name: zmk - remote: zmkfirmware - revision: main + remote: urob + revision: fix-mod-morph import: app/west.yml self: path: config diff --git a/zmk-nodefree-config/.gitattributes b/zmk-nodefree-config/.gitattributes new file mode 100644 index 0000000..9b2b33d --- /dev/null +++ b/zmk-nodefree-config/.gitattributes @@ -0,0 +1,2 @@ +*.dtsi linguist-language=C++ +*.keymap linguist-language=C++ diff --git a/zmk-nodefree-config/README.md b/zmk-nodefree-config/README.md new file mode 100644 index 0000000..5d4b634 --- /dev/null +++ b/zmk-nodefree-config/README.md @@ -0,0 +1,403 @@ +# zmk-nodefree-config + +ZMK lets user customize their keyboard layout by providing a Devicetree file +(`.keymap`). The specific syntax requirements of the Devicetree file format can, +however, make this process a bit daunting for new users. + +This repository provides simple convenience macros that simplify the configuration for +many common use cases. It results in a "node-free" user configuration with a more +streamlined syntax. Check out [example.keymap](example.keymap) to see it in action. + +See [changelog](#changelog) for latest changes. + +## Overview + +The following convenience macros are provided: +1. `ZMK_BEHAVIOR` creates behaviors such as hold-taps, tap-dances or + ZMK macros [\[doc\]](#zmk_behavior) +2. `ZMK_LAYER` adds layers to the keymap [\[doc\]](#zmk_layer) +3. `ZMK_COMBO` defines combos [\[doc\]](#zmk_combo) +4. `ZMK_CONDITIONAL_LAYER` sets up "tri-layer" conditions [\[doc\]](#zmk_conditional_layer) +5. `ZMK_UNICODE_SINGLE` and `ZMK_UNICODE_PAIR` create unicode characters [\[doc\]](#zmk_unicode) +6. `international_chars` provides character definitions for some non-English languages + [\[doc\]](#international-characters) +7. `keypos_def` provides human-readable key position shortcuts for some popular + keyboards that simplify the configuration of combos and positional hold-taps + [\[doc\]](#key-position-helpers) + +## Quickstart + +1. Copy this repository into the root folder of your zmk-config. The + folder structure should look as follows: + ``` + zmk-config + ├── config + │ ├── your.keyboard.conf + │ ├── your_keyboard.keymap + │ └── ... + ├── zmk-nodefree-config + │ ├── helper.h + │ ├── ... + ``` +2. Source `helper.h` near the top of your `.keymap` file: + ```C++ + #include "../zmk-nodefree-config/helper.h" + ``` +3. Customize your keyboard's `.keymap` file. See [example.keymap](example.keymap) or [my + personal zmk-config](https://github.com/urob/zmk-config/blob/main/config/base.keymap) + for a complete configuration, and read the documentation below for details. + +## Configuration details + +### ZMK\_BEHAVIOR + +`ZMK_BEHAVIOR` can be used to create any of the following ZMK behaviors: caps-word, +hold-tap, key-repeat, macro, mod-morph, sticky-key or tap-dance + +**Syntax:** `ZMK_BEHAVIOR(name, type, specification)` +* `name`: a unique string chosen by the user (e.g., `my_behavior`). The new behavior can + be added to the keymap using `&name` (e.g., `&my_behavior`). +* `type`: the behavior to be created. It must be one of the following: + `caps_word`, `hold_tap`, `key_repeat`, `macro`, `mod_morph`, `sticky_key` or + `tap_dance`. Note that two-word types are separated by underscores (`_`). +* `specification`: the custom behavior code. It should contain the + body of the corresponding [ZMK behavior configuration](https://zmk.dev/docs/config/behaviors) + without the `label`, `#binding-cells` and `compatible` properties and without the + surrounding node-specification. + +#### Example 1: Creating a custom "homerow mod" tap-hold behavior + +```C++ +ZMK_BEHAVIOR(hrm, hold_tap, + flavor = "balanced"; + tapping-term-ms = <280>; + quick-tap-ms = <125>; + global-quick-tap; + bindings = <&kp>, <&kp>; +) +``` + +This creates a custom "homerow mod" that can be added to the keymap using `&hrm`. For example, +`&hrm LSHIFT T` creates a key that yields `T` on tap and `LSHIFT` on hold. + +#### Example 2: Creating a custom tap-dance key + +```C++ +ZMK_BEHAVIOR(ss_cw, tap_dance, + tapping-term-ms = <200>; + bindings = <&sk LSHFT>, <&caps_word>; +) +``` +This behavior yields sticky-shift on tap and caps-word on double tap. It can be added to +the keymap using `&ss_cw`. + +#### Example 3: Creating a custom "win-sleep" macro + +```C++ +ZMK_BEHAVIOR(win_sleep, macro, + wait-ms = <100>; + tap-ms = <5>; + bindings = <&kp LG(X) &kp U &kp S>; +) +``` + +This creates a "Windows sleep macro" that can be added to the keymap using `&win_sleep`. + +### ZMK\_LAYER + +`ZMK_LAYER` adds new keymap layers to the configuration. + +**Syntax:** `ZMK_LAYER(name, layout)` +* `name`: a unique identifier string chosen by the user (usually there is no reason to reference this elsewhere) +* `layout`: the layout specification using the same syntax as the `bindings` + property of the [ZMK keymap configuration](https://zmk.dev/docs/config/keymap) + +Multiple layers can be added with repeated calls of `ZMK_LAYER`. They will be ordered +in the same order in which they are created, with the first-specified layer being +the "lowest" one ([see here for details](https://zmk.dev/docs/features/keymaps#layers)). + +#### Example usage +```C++ +ZMK_KEYMAP(default_layer, + // ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ + &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT + // ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &hrm LGUI A &hrm LALT R &hrm LCTRL S &hrm LSHFT T &kp G &kp M &hrm RSHFT N &hrm LCTRL E &hrm LALT I &hrm LGUI O + // ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &kp COMMA &kp DOT &kp SEMI + // ╰─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &kp ESC < NAV SPACE &kp TAB &kp RET &ss_cw &bs_del_num + // ╰─────────────┴──── ────────┴─────────────╯ ╰─────────────┴─────────────┴─────────────╯ +) + +``` + +### ZMK\_COMBO + +`ZMK_COMBO` defines new combos. + +**Syntax:** `ZMK_COMBO(name, bindings, keypos, layers)` +* `name`: a unique identifier string chosen by the user (usually there is not reason to reference this elsewhere) +* `binding`: the binding triggered by the combo (this can be any stock or previously defined behavior) +* `keypos`: a list of 2 or more key positions that trigger the combo (e.g., `12 + 13`). Note that the mapping from key positions to keys depends on your keyboard. To facilitate + the combo setup and increase portability, one can use key-position helpers instead. + See [below](#key-position-helpers) on how to use them. +* `layers`: a list of layers for which the combo is active (e.g., `0 1` for the first + two layers). If set to `ALL` the combo is active on all layers. + +By default, the timeout for combos created with `ZMK_COMBO` is 30ms. If `COMBO_TERM` is +reset prior to calling `ZMK_COMBO`, the new value of `COMBO_TERM` is used instead. +Alternatively, one can use `ZMK_COMBO_ADV` which allows to specify the combo-timeout directly +as 5th argument. + +Note: with older ZMK versions, using different combo-timeouts would result in keys +getting stuck. If this is an issue, try updating to the latest ZMK version. + +#### Example: copy and paste combos + +```C++ +#undef COMBO_TERM +#define COMBO_TERM 50 +ZMK_COMBO(copy, &kp LC(C), 12 13, ALL) +ZMK_COMBO(paste, &kp LC(V), 13 14, ALL) +``` +This sets the combo timeout to 50ms, and then creates two combos which both are +active on all layers. The first combo is triggered when the +12th and 13th keys are pressed jointly within the `COMBO_TERM`, sending Ctrl + C. The +second combo is triggered when the 13th and 14th keys are pressed jointly, sending +Ctrl + V. + +### ZMK\_CONDITIONAL\_LAYER + +This sets up tri-layer conditions. + +**Syntax:** `ZMK_CONDITIONAL_LAYER(if_layers, then_layers)` +* `if_layers`: a list of layers which trigger the `then_layer` if simultaneously active +* `then_layer`: the layer which is activated when the if-condition is met. Due to ZMK's + layering model, it should generally have a higher number than the `if_layers` + +For instance, this triggers "layer 3" if layers "1" and "2" are +simultaneously active. +```C++ +ZMK_CONDITIONAL_LAYER(1 2, 3) +``` +Mind that ZMK's layer numbering starts at 0. One can use layer +definitions, as demonstrated in [example.keymap](example.keymap), to simplify life. + +### ZMK\_UNICODE + +There are two macros that create new unicode characters that +can be added to the keymap. `ZMK_UNICODE_SINGLE` creates single unicode characters such +as , and `ZMK_UNICODE_PAIR` creates pairs of shifted/unshifted unicode +characters that are useful for specifying international characters such as +ä/Ä or δ/Δ. + + +**Syntax:** `ZMK_UNICODE_SINGLE(name, L0, L1, L2, L3)` +* `name:` a unique string chosen by the user (e.g., `my_char`). The unicode character can + be added to the keymap using `&name` (e.g., `&my_char`) +* `L0` to `L3`: a 4-digit sequence defining the unicode string using standard [ZMK key + codes](https://zmk.dev/docs/codes/keyboard-keypad) + +**Syntax:** `ZMK_UNICODE_PAIR(name, L0, L1, L2, L3, U0, U1, U2, U3)` +* `name:` a unique string chosen by the user (e.g., `my_char`). The unicode character can + be added to the keymap using `&name` (e.g., `&my_char`) +* `L0` to `L3`: a 4-digit sequence defining the unshifted unicode string +* `U0` to `U3`: a 4-digit sequence defining the shifted unicode string (which is send when + holding Shift while pressing &name) + +Note: 5-digit unicode characters are currently not supported (but would be easy to add +if there is interest). + +#### Example 1: Euro sign (€) + +```C++ +ZMK_UNICODE_SINGLE(euro_sign, N2, N0, A, C) +``` +This creates a Euro character that can be added to the keymap using `&euro_sign`. + +#### Example 2: German umlauts (ä/Ä, ö/Ö, ü/Ü) + +```C++ +// name unshifted shifted +ZMK_UNICODE_PAIR( de_ae, N0, N0, E, N4, N0, N0, C, N4 ) +ZMK_UNICODE_PAIR( de_oe, N0, N0, F, N6, N0, N0, D, N6 ) +ZMK_UNICODE_PAIR( de_ue, N0, N0, F, C, N0, N0, D, C ) +``` +The creates "umlaut" pairs that can be added to the keymap using `&de_ae`, `&de_oe` and `&de_ue`. + +#### Dependencies for unicode + + +* `ZMK_UNICODE_PAIR` requires ZMK patched with [PR + #1412](https://github.com/zmkfirmware/zmk/pull/1412). If you need help maintaining + your own ZMK repository, check out [this + guide](https://gist.github.com/urob/68a1e206b2356a01b876ed02d3f542c7). If you don't + want to maintain your own ZMK repository, you can use ZMK's [beta + testing](https://zmk.dev/docs/features/beta-testing) feature to configure Github + Actions to build against a patched remote branch of ZMK. To do so, replace the + contents of `west.yml` in your `zmk-config/config` directory with the following + contents, which adds the required patch: + ``` + manifest: + remotes: + - name: urob + url-base: https://github.com/urob + projects: + - name: zmk + remote: urob + revision: fix-mod-morph + import: app/west.yml + self: + path: config + ``` +* The input of unicode characters differs across operating systems. By + default, `ZMK_UNICODE` is configured for Windows (using WinCompose). To set it up + for another OS, set the variable + `HOST_OS` **before** sourcing `helper.h`. + + For Linux use: + ```C++ + #define HOST_OS 1 // set to 1 for Linux, default is 0 (Windows) + #include helper.h + ``` + For macOS/Windows-Alt-Codes use: + ```C++ + #define HOST_OS 2 // set to 2 for macOS/Windows-Alt-Codes, default is 0 (Windows) + #include helper.h + ``` + This will send unicode characters using the OS's default input channels. + For non-default input channels or for other operating systems, one can instead set the + variables `OS_UNICODE_LEAD` and `OS_UNICODE_TRAIL` to the character sequences that + initialize/terminate the unicode input.[^1] + +* On Windows and macOS there are additional requirements for unicode input to work. On + Windows, one must install [WinCompose](https://github.com/samhocevar/wincompose) for + full support (or use Win-Alt-Codes for limited support in select software). On + macOS one must enable unicode input in the system preferences. + +### International characters + +There are pre-defined definitions for international characters for a few +languages --- currently German, Greek and Swedish (contributions are welcome)[^2]. These can be +loaded by sourcing the corresponding files; e.g.: +```C++ +#include "../zmk-nodefree-config/international_chars/german.dtsi" +#include "../zmk-nodefree-config/international_chars/greek.dtsi" +#include "../zmk-nodefree-config/international_chars/swedish.dtsi" +``` +Once sourced, international characters can be added to the +keymap using, e.g., `&de_ae`, `&el_alpha` or `&sv_ao` +(each language has its own prefix; see the language files for a complete list of available characters). + +**Dependencies:** These definitions make use of unicode in the background, +see the unicode section above for [prerequisites](#dependencies-for-unicode). + +**Note:** Windows-Alt-Codes use different keycode sequences than the +usual unicode sequences, requiring different definitions. Currently, they are +pre-defined for German: +```C++ +#include "../zmk-nodefree-config/international_chars/german_alt.dtsi" +``` + +### Key-position helpers + +Certain configuration options such as combos and positional hold-taps are based on the +physical position of keys on the keyboard. This can be cumbersome and reduces +portability of configuration files across keyboards with different layouts. + +To increase portability and ease of use, this repo provides optional key-position +helpers for some popular keyboard layouts (48-key boards such as Planck, 42-key +boards such as Corne, 36-key boards and 34-key boards). + +These key-position helpers provide a map from the physical key positions to human-readable shortcuts. +All shortcuts are of the following form: +* `L/R` for **L**eft/**R**ight hand +* `T/M/B/H` for **T**op/**M**iddle/**B**ottom and t**H**umb row. +* `0/1/2/3/4` for the finger position starting from the inside (`0` is the inner + index-finger column, `1` is the home position of the index finger, ..., `4` is the home + position of the pinkie) + +For instance, the shortcuts layout for a 36-key board looks as follows: +``` +╭─────────────────────┬─────────────────────╮ +│ LT4 LT3 LT2 LT1 LT0 │ RT0 RT1 RT2 RT3 RT4 │ +│ LM4 LM3 LM2 LM1 LM0 │ RM0 RM1 RM2 RM3 RM4 │ +│ LB4 LB3 LB2 LB1 LB0 │ RB0 RB1 RB2 RB3 RB4 │ +╰───────╮ LH2 LH1 LH0 │ RH0 RH1 RH2 ╭───────╯ + ╰─────────────┴─────────────╯ +``` +Schematics for all supported keyboards can be found in the corresponding +definition files in the `keypos_def` folder. + +To use these key-position helpers, source the definition file for your keyboard +into your `.keymap` file. E.g., for a 36-key board, use: +```C++ +#include "../zmk-nodefree-config/keypos_def/keypos_36keys.h" +``` + +#### Example 1: Defining combos using key-position helpers + +```C++ +ZMK_COMBO(copy, &kp LC(C), LB2 LB3, ALL) +ZMK_COMBO(paste, &kp LC(V), LB1 LB2, ALL) +``` + +This defines a "copy"-combo for the middle + ring finger on the left bottom row, and a +"paste"-combo for the index + middle finger on the left bottom row. Both combos are active on all layers. + +#### Example 2: Home-row mods with positional hold-taps + +Here we use ZMK's [positional +hold-tap](https://zmk.dev/docs/behaviors/hold-tap#positional-hold-tap-and-hold-trigger-key-positions) +feature to make home-row mods only trigger with "opposite hand" keys. Using +key-position helpers makes this straightforward: + +```C++ +#define KEYS_L LT0 LT1 LT2 LT3 LT4 LM0 LM1 LM2 LM3 LM4 LB0 LB1 LB2 LB3 LB4 // left-hand keys +#define KEYS_R RT0 RT1 RT2 RT3 RT4 RM0 RM1 RM2 RM3 RM4 RB0 RB1 RB2 RB3 RB4 // right-hand keys +#define THUMBS LH2 LH1 LH0 RH0 RH1 RH2 // thumb keys + +ZMK_BEHAVIOR(hml, hold_tap, // left-hand HRMs + flavor = "balanced"; + tapping-term-ms = <280>; + quick-tap-ms = <125>; + global-quick-tap; + bindings = <&kp>, <&kp>; + hold-trigger-key-positions = ; +) + +ZMK_BEHAVIOR(hmr, hold_tap, // right-hand HRMs + flavor = "balanced"; + tapping-term-ms = <280>; + quick-tap-ms = <125>; + global-quick-tap; + bindings = <&kp>, <&kp>; + hold-trigger-key-positions = ; +) +``` + +## Changelog + +* **9/11/2022:** Support for Windows-Alt-Codes +* **8/05/2022:** New combo macro `ZMK_COMBO_ADV` for "advanced" combo setups. Compared + to the regular `ZMK_COMBO` macro, it takes the combo-timeout as fifth argument. + Moreover, if `COMBO_HOOK` is defined, it includes its definition as additional + options. For example, to use the new global-quick-tap for combos option introduced in + [PR #1387](https://github.com/andrewjrae/zmk/tree/min-prior-ms), one would set + `#define COMBO_HOOK global-quick-tap-ms = <125>;` before calling `ZMK_COMBO_ADV`. + See [my personal combo + setup](https://github.com/urob/zmk-config/blob/main/config/combos.dtsi) for examples. +* **7/31/2022:** Switch unicode dependency from PR #1114 to + [PR #1412](https://github.com/zmkfirmware/zmk/pull/1412) + + +[^1]: The default for Windows is `OS_UNICODE_LEAD` set to tap Right Alt + followed by U and `OS_UNICODE_TRAIL` set to tap Return. + The default for Linux is `OS_UNICODE_LEAD` set to tap Shift + + Ctrl + U and `OS_UNICODE_TRAIL` set to tap Space. + The default for macOS is `OS_UNICODE_LEAD` set to hold Left Alt + and `OS_UNICODE_TRAIL` set to release Left Alt. + +[^2]: Swedish language support was added by discord user "captainwoot". diff --git a/zmk-nodefree-config/example.keymap b/zmk-nodefree-config/example.keymap new file mode 100644 index 0000000..d776bf2 --- /dev/null +++ b/zmk-nodefree-config/example.keymap @@ -0,0 +1,109 @@ +#include +#include + +#include "../zmk-nodefree-config/helper.h" +#include "../zmk-nodefree-config/keypos_def/keypos_36keys.h" +#include "../zmk-nodefree-config/international_chars/german.dtsi" + +/* layer and key shortcuts */ + +#define ___ &trans + +#define DEF 0 // layer shortcuts, must match order in which they are defined below +#define NAV 1 +#define NUM 2 +#define GER 3 + +/* custom behaviors */ + +// homerow mods +ZMK_BEHAVIOR(hrm, hold_tap, + flavor = "balanced"; + tapping-term-ms = <280>; + quick-tap-ms = <125>; + global-quick-tap; + bindings = <&kp>, <&kp>; +) + +// tap: sticky shift | double tap: capsword +ZMK_BEHAVIOR(ss_cw, tap_dance, + tapping-term-ms = <200>; + bindings = <&sk LSHFT>, <&caps_word>; +) + +// tap: backspace | shift + tap: delete | hold: num layer +ZMK_BEHAVIOR(bs_del_num, mod_morph, + bindings = << NUM BSPC>, <&kp DEL>; + mods = <(MOD_LSFT|MOD_RSFT)>; +) + +// windows sleep macro +ZMK_BEHAVIOR(win_sleep, macro, + wait-ms = <100>; + tap-ms = <5>; + bindings = <&kp LG(X) &kp U &kp S>; +) + +// euro sign +ZMK_UNICODE_SINGLE(euro_sign, N2, N0, A, C) // € + +// replace a/o/u/s with German umlauts when NAV and NUM are held jointly +ZMK_CONDITIONAL_LAYER(NAV NUM, GER) + +// combos +#undef COMBO_TERM +#define COMBO_TERM 40 // timeout of 40ms (default is 30ms if omitted) +ZMK_COMBO(combo_sleep, &win_sleep, RT3 RT4, NAV) // custom sleep macro, only active on NAV layer +ZMK_COMBO(combo_copy, &kp LC(C), LB2 LB3, ALL) // Ctrl + C, active on all layers +ZMK_COMBO(combo_paste, &kp LC(V), LB1 LB2, ALL) // Ctrl + V, active on all layers + +/* keymap */ + +ZMK_LAYER(default_layer, + // ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ + &kp Q &kp W &kp F &kp P &kp B &kp J &kp L &kp U &kp Y &kp SQT + // ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &hrm LGUI A &hrm LALT R &hrm LCTRL S &hrm LSHFT T &kp G &kp M &hrm RSHFT N &hrm LCTRL E &hrm LALT I &hrm LGUI O + // ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &kp Z &kp X &kp C &kp D &kp V &kp K &kp H &kp COMMA &kp DOT &kp SEMI + // ╰─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &kp ESC < NAV SPACE &kp TAB &kp RET &bs_del_num &ss_cw + // ╰─────────────┴─────────────┴─────────────╯ ╰─────────────┴─────────────┴─────────────╯ +) + +ZMK_LAYER(nav_layer, + // ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ + ___ ___ ___ ___ ___ &kp PG_UP &kp HOME &kp UP &kp END &kp INS + // ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &sk LGUI &sk LALT &sk LCTRL &sk LSHFT ___ &kp PG_DN &kp LEFT &kp DOWN &kp RIGHT &kp DEL + // ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + ___ ___ ___ ___ ___ ___ &kp LC(X) &kp LC(INS) &kp LS(INS) ___ + // ╰─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + ___ ___ ___ ___ ___ ___ + // ╰─────────────┴─────────────┴─────────────╯ ╰─────────────┴─────────────┴─────────────╯ +) + +ZMK_LAYER(num_layer, + // ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ + &kp ESC &kp N7 &kp N8 &kp N9 &kp STAR ___ ___ ___ ___ ___ + // ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &kp TAB &kp N4 &kp N5 &kp N6 &kp PLUS ___ &sk RSHFT &sk LCTRL &sk LALT &sk LGUI + // ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &kp RET &kp N1 &kp N2 &kp N3 &kp FSLH ___ ___ ___ ___ ___ + // ╰─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &kp DOT &kp N0 &kp MINUS ___ ___ ___ + // ╰─────────────┴─────────────┴─────────────╯ ╰─────────────┴─────────────┴─────────────╯ +) + +ZMK_LAYER(german_layer, + // ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ ╭─────────────┬─────────────┬─────────────┬─────────────┬─────────────╮ + ___ ___ ___ ___ ___ ___ ___ &de_ue ___ ___ + // ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + &de_ae ___ &de_eszett ___ ___ ___ ___ &euro_sign ___ &de_oe + // ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ + // ╰─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ ├─────────────┼─────────────┼─────────────┼─────────────┼─────────────┤ + ___ ___ ___ ___ ___ ___ + // ╰─────────────┴─────────────┴─────────────╯ ╰─────────────┴─────────────┴─────────────╯ +) + diff --git a/zmk-nodefree-config/helper.h b/zmk-nodefree-config/helper.h new file mode 100644 index 0000000..06b1822 --- /dev/null +++ b/zmk-nodefree-config/helper.h @@ -0,0 +1,152 @@ +/* + * helper.h + * + * Convenience macros simplifying ZMK's keymap configuration. + * See https://github.com/urob/zmk-nodefree-config for documentation. + */ + +#pragma once + +#define ZMK_HELPER_STRINGIFY(x) #x + +/* ZMK_BEHAVIOR */ + +#define ZMK_BEHAVIOR_CORE_caps_word compatible = "zmk,behavior-caps-word"; #binding-cells = <0> +#define ZMK_BEHAVIOR_CORE_hold_tap compatible = "zmk,behavior-hold-tap"; #binding-cells = <2> +#define ZMK_BEHAVIOR_CORE_key_repeat compatible = "zmk,behavior-key-repeat"; #binding-cells = <0> +#define ZMK_BEHAVIOR_CORE_macro compatible = "zmk,behavior-macro"; #binding-cells = <0> +#define ZMK_BEHAVIOR_CORE_mod_morph compatible = "zmk,behavior-mod-morph"; #binding-cells = <0> +#define ZMK_BEHAVIOR_CORE_sticky_key compatible = "zmk,behavior-sticky-key"; #binding-cells = <1> +#define ZMK_BEHAVIOR_CORE_tap_dance compatible = "zmk,behavior-tap-dance"; #binding-cells = <0> + +#define ZMK_BEHAVIOR(name, type, ...) \ + / { \ + behaviors { \ + name: name { \ + label = ZMK_HELPER_STRINGIFY(ZB_ ## name); \ + ZMK_BEHAVIOR_CORE_ ## type; \ + __VA_ARGS__ \ + }; \ + }; \ + }; + +/* ZMK_LAYER */ + +#define ZMK_LAYER(name, layout) \ + / { \ + keymap { \ + compatible = "zmk,keymap"; \ + name { \ + bindings = ; \ + }; \ + }; \ + }; + +/* ZMK_COMBOS */ + +#define ALL 0xff +#if !defined COMBO_TERM + #define COMBO_TERM 30 +#endif + +#define ZMK_COMBO(name, combo_bindings, keypos, combo_layers) \ + / { \ + combos { \ + compatible = "zmk,combos"; \ + combo_ ## name { \ + timeout-ms = ; \ + bindings = ; \ + key-positions = ; \ + layers = ; \ + }; \ + }; \ + }; + +#if !defined COMBO_HOOK + #define COMBO_HOOK +#endif +#define ZMK_COMBO_ADV(name, combo_bindings, keypos, combo_layers, combo_timeout) \ + / { \ + combos { \ + compatible = "zmk,combos"; \ + combo_ ## name { \ + timeout-ms = ; \ + bindings = ; \ + key-positions = ; \ + layers = ; \ + COMBO_HOOK \ + }; \ + }; \ + }; + +/* ZMK_CONDITIONAL_LAYER */ + +#define ZMK_CONDITIONAL_LAYER(if_layers, then_layer) \ + / { \ + conditional_layers { \ + compatible = "zmk,conditional-layers"; \ + tri_layer { \ + if-layers = ; \ + then-layer = ; \ + }; \ + }; \ + }; + +/* ZMK_UNICODE */ + +#if !defined OS_UNICODE_LEAD + #if HOST_OS == 2 + #define OS_UNICODE_LEAD ¯o_press &kp LALT // macOS/Windows-Alt-Codes + #elif HOST_OS == 1 + #define OS_UNICODE_LEAD ¯o_tap &kp LS(LC(U)) // Linux + #else + #define OS_UNICODE_LEAD ¯o_tap &kp RALT &kp U // Windows + WinCompose (default) + #endif +#endif +#if !defined OS_UNICODE_TRAIL + #if HOST_OS == 2 + #define OS_UNICODE_TRAIL ¯o_release &kp LALT // macOS/Windows-Alt-Codes + #elif HOST_OS == 1 + #define OS_UNICODE_TRAIL ¯o_tap &kp SPACE // Linux + #else + #define OS_UNICODE_TRAIL ¯o_tap &kp RET // Windows + WinCompose (default) + #endif +#endif + +#define UC_MACRO(name, unicode_bindings) \ + / { \ + macros { \ + name: name { \ + compatible = "zmk,behavior-macro"; \ + label = ZMK_HELPER_STRINGIFY(UC_MACRO_ ## name); \ + wait-ms = <0>; \ + tap-ms = <0>; \ + #binding-cells = <0>; \ + bindings = , <¯o_tap unicode_bindings>, ; \ + }; \ + }; \ + }; + +#define UC_MODMORPH(name, uc_binding, shifted_uc_binding) \ + / { \ + behaviors { \ + name: name { \ + compatible = "zmk,behavior-mod-morph"; \ + label = ZMK_HELPER_STRINGIFY(UC_MORPH_ ## name); \ + #binding-cells = <0>; \ + bindings = , ; \ + mods = <(MOD_LSFT|MOD_RSFT)>; \ + masked-mods = <(MOD_LSFT|MOD_RSFT)>; \ + }; \ + }; \ + }; + +#define ZMK_UNICODE_SINGLE(name, L0, L1, L2, L3) \ + UC_MACRO(name ## _lower, &kp L0 &kp L1 &kp L2 &kp L3) \ + UC_MODMORPH(name, &name ## _lower, &none) + +#define ZMK_UNICODE_PAIR(name, L0, L1, L2, L3, U0, U1, U2, U3) \ + UC_MACRO(name ## _lower, &kp L0 &kp L1 &kp L2 &kp L3) \ + UC_MACRO(name ## _upper, &kp U0 &kp U1 &kp U2 &kp U3) \ + UC_MODMORPH(name, &name ## _lower, &name ## _upper) + diff --git a/zmk-nodefree-config/international_chars/german.dtsi b/zmk-nodefree-config/international_chars/german.dtsi new file mode 100644 index 0000000..637dd0f --- /dev/null +++ b/zmk-nodefree-config/international_chars/german.dtsi @@ -0,0 +1,6 @@ +/* German umlauts */ +ZMK_UNICODE_PAIR( de_ae, N0, N0, E, N4, N0, N0, C, N4) +ZMK_UNICODE_PAIR( de_oe, N0, N0, F, N6, N0, N0, D, N6) +ZMK_UNICODE_PAIR( de_ue, N0, N0, F, C, N0, N0, D, C) +ZMK_UNICODE_SINGLE( de_eszett, N0, N0, D, F) + diff --git a/zmk-nodefree-config/international_chars/german_alt.dtsi b/zmk-nodefree-config/international_chars/german_alt.dtsi new file mode 100644 index 0000000..696e988 --- /dev/null +++ b/zmk-nodefree-config/international_chars/german_alt.dtsi @@ -0,0 +1,5 @@ +/* German umlauts for Windows-Alt-Codes */ +ZMK_UNICODE_PAIR( de_ae, KP_N0, KP_N2, KP_N2, KP_N8, KP_N0, KP_N1, KP_N9, KP_N6) +ZMK_UNICODE_PAIR( de_oe, KP_N0, KP_N2, KP_N4, KP_N6, KP_N0, KP_N2, KP_N1, KP_N4) +ZMK_UNICODE_PAIR( de_ue, KP_N0, KP_N2, KP_N5, KP_N2, KP_N0, KP_N2, KP_N2, KP_N0) +ZMK_UNICODE_SINGLE( de_eszett, KP_N0, KP_N2, KP_N2, KP_N3) diff --git a/zmk-nodefree-config/international_chars/greek.dtsi b/zmk-nodefree-config/international_chars/greek.dtsi new file mode 100644 index 0000000..662e12e --- /dev/null +++ b/zmk-nodefree-config/international_chars/greek.dtsi @@ -0,0 +1,26 @@ +/* Greek letters */ +ZMK_UNICODE_PAIR(el_alpha, N0, N3, B, N1, N0, N3, N9, N1) +ZMK_UNICODE_PAIR(el_beta, N0, N3, B, N2, N0, N3, N9, N2) +ZMK_UNICODE_PAIR(el_gamma, N0, N3, B, N3, N0, N3, N9, N3) +ZMK_UNICODE_PAIR(el_delta, N0, N3, B, N4, N0, N3, N9, N4) +ZMK_UNICODE_PAIR(el_epsilon, N0, N3, F, N5, N0, N3, N9, N5) // varepsilon = 03B5 +ZMK_UNICODE_PAIR(el_zeta, N0, N3, B, N6, N0, N3, N9, N6) +ZMK_UNICODE_PAIR(el_eta, N0, N3, B, N7, N0, N3, N9, N7) +ZMK_UNICODE_PAIR(el_theta, N0, N3, B, N8, N0, N3, N9, N8) +ZMK_UNICODE_PAIR(el_iota, N0, N3, B, N9, N0, N3, N9, N9) +ZMK_UNICODE_PAIR(el_kappa, N0, N3, B, A, N0, N3, N9, A) +ZMK_UNICODE_PAIR(el_lambda, N0, N3, B, B, N0, N3, N9, B) +ZMK_UNICODE_PAIR(el_mu, N0, N3, B, C, N0, N3, N9, C) +ZMK_UNICODE_PAIR(el_nu, N0, N3, B, D, N0, N3, N9, D) +ZMK_UNICODE_PAIR(el_xi, N0, N3, B, E, N0, N3, N9, E) +ZMK_UNICODE_PAIR(el_omikron, N0, N3, B, F, N0, N3, N9, F) +ZMK_UNICODE_PAIR(el_pi, N0, N3, C, N0, N0, N3, A, N0) +ZMK_UNICODE_PAIR(el_rho, N0, N3, C, N1, N0, N3, A, N1) +ZMK_UNICODE_PAIR(el_sigma, N0, N3, C, N3, N0, N3, A, N3) +ZMK_UNICODE_PAIR(el_tau, N0, N3, C, N4, N0, N3, A, N4) +ZMK_UNICODE_PAIR(el_upsilon, N0, N3, C, N5, N0, N3, A, N5) +ZMK_UNICODE_PAIR(el_phi, N0, N3, C, N6, N0, N3, A, N6) // varphi = 03C6 +ZMK_UNICODE_PAIR(el_chi, N0, N3, C, N7, N0, N3, A, N7) +ZMK_UNICODE_PAIR(el_psi, N0, N3, C, N8, N0, N3, A, N8) +ZMK_UNICODE_PAIR(el_omega, N0, N3, C, N9, N0, N3, A, N9) + diff --git a/zmk-nodefree-config/international_chars/swedish.dtsi b/zmk-nodefree-config/international_chars/swedish.dtsi new file mode 100644 index 0000000..804702a --- /dev/null +++ b/zmk-nodefree-config/international_chars/swedish.dtsi @@ -0,0 +1,5 @@ +/* Swedish letters */ +ZMK_UNICODE_PAIR(sv_ae, N0, N0, E, N4, N0, N0, C, N4) +ZMK_UNICODE_PAIR(sv_ao, N0, N0, E, N5, N0, N0, C, N5) +ZMK_UNICODE_PAIR(sv_oe, N0, N0, F, N6, N0, N0, D, N6) + diff --git a/zmk-nodefree-config/keypos_def/keypos_34keys.h b/zmk-nodefree-config/keypos_def/keypos_34keys.h new file mode 100644 index 0000000..f7f1a6e --- /dev/null +++ b/zmk-nodefree-config/keypos_def/keypos_34keys.h @@ -0,0 +1,53 @@ +/* 34 KEY MATRIX / LAYOUT MAPPING + + ╭────────────────────┬────────────────────╮ ╭─────────────────────┬─────────────────────╮ + │ 0 1 2 3 4 │ 5 6 7 8 9 │ │ LT4 LT3 LT2 LT1 LT0 │ RT0 RT1 RT2 RT3 RT4 │ + │ 10 11 12 13 14 │ 15 16 17 18 19 │ │ LM4 LM3 LM2 LM1 LM0 │ RM0 RM1 RM2 RM3 RM4 │ + │ 20 21 22 23 24 │ 25 26 27 28 29 │ │ LB4 LB3 LB2 LB1 LB0 │ RB0 RB1 RB2 RB3 RB4 │ + ╰───────────╮ 30 31 │ 32 33 ╭───────────╯ ╰───────────╮ LH1 LH0 │ RH0 RH1 ╭───────────╯ + ╰────────┴────────╯ ╰─────────┴─────────╯ */ + +#pragma once + +#define LT0 4 // left-top row +#define LT1 3 +#define LT2 2 +#define LT3 1 +#define LT4 0 + +#define RT0 5 // right-top row +#define RT1 6 +#define RT2 7 +#define RT3 8 +#define RT4 9 + +#define LM0 14 // left-middle row +#define LM1 13 +#define LM2 12 +#define LM3 11 +#define LM4 10 + +#define RM0 15 // right-middle row +#define RM1 16 +#define RM2 17 +#define RM3 18 +#define RM4 19 + +#define LB0 24 // left-bottom row +#define LB1 23 +#define LB2 22 +#define LB3 21 +#define LB4 20 + +#define RB0 25 // right-bottom row +#define RB1 26 +#define RB2 27 +#define RB3 28 +#define RB4 29 + +#define LH0 31 // left thumb keys +#define LH1 30 + +#define RH0 32 // right thumb keys +#define RH1 33 + diff --git a/zmk-nodefree-config/keypos_def/keypos_36keys.h b/zmk-nodefree-config/keypos_def/keypos_36keys.h new file mode 100644 index 0000000..6bf73c1 --- /dev/null +++ b/zmk-nodefree-config/keypos_def/keypos_36keys.h @@ -0,0 +1,55 @@ +/* 36 KEY MATRIX / LAYOUT MAPPING + + ╭────────────────────┬────────────────────╮ ╭─────────────────────┬─────────────────────╮ + │ 0 1 2 3 4 │ 5 6 7 8 9 │ │ LT4 LT3 LT2 LT1 LT0 │ RT0 RT1 RT2 RT3 RT4 │ + │ 10 11 12 13 14 │ 15 16 17 18 19 │ │ LM4 LM3 LM2 LM1 LM0 │ RM0 RM1 RM2 RM3 RM4 │ + │ 20 21 22 23 24 │ 25 26 27 28 29 │ │ LB4 LB3 LB2 LB1 LB0 │ RB0 RB1 RB2 RB3 RB4 │ + ╰───────╮ 30 31 32 │ 33 34 35 ╭───────╯ ╰───────╮ LH2 LH1 LH0 │ RH0 RH1 RH2 ╭───────╯ + ╰────────────┴────────────╯ ╰─────────────┴─────────────╯ */ + +#pragma once + +#define LT0 4 // left-top row +#define LT1 3 +#define LT2 2 +#define LT3 1 +#define LT4 0 + +#define RT0 5 // right-top row +#define RT1 6 +#define RT2 7 +#define RT3 8 +#define RT4 9 + +#define LM0 14 // left-middle row +#define LM1 13 +#define LM2 12 +#define LM3 11 +#define LM4 10 + +#define RM0 15 // right-middle row +#define RM1 16 +#define RM2 17 +#define RM3 18 +#define RM4 19 + +#define LB0 24 // left-bottom row +#define LB1 23 +#define LB2 22 +#define LB3 21 +#define LB4 20 + +#define RB0 25 // right-bottom row +#define RB1 26 +#define RB2 27 +#define RB3 28 +#define RB4 29 + +#define LH0 32 // left thumb keys +#define LH1 31 +#define LH2 30 + +#define RH0 33 // right thumb keys +#define RH1 34 +#define RH2 35 + diff --git a/zmk-nodefree-config/keypos_def/keypos_42keys.h b/zmk-nodefree-config/keypos_def/keypos_42keys.h new file mode 100644 index 0000000..f2ad1aa --- /dev/null +++ b/zmk-nodefree-config/keypos_def/keypos_42keys.h @@ -0,0 +1,61 @@ +/* 42 KEY MATRIX / LAYOUT MAPPING + + ╭────────────────────────┬────────────────────────╮ ╭─────────────────────────┬─────────────────────────╮ + │ 0 1 2 3 4 5 │ 6 7 8 9 10 11 │ │ LT5 LT4 LT3 LT2 LT1 LT0 │ RT0 RT1 RT2 RT3 RT4 RT5 │ + │ 12 13 14 15 16 17 │ 18 19 20 21 22 23 │ │ LM5 LM4 LM3 LM2 LM1 LM0 │ RM0 RM1 RM2 RM3 RM4 RM5 │ + │ 24 25 26 27 28 29 │ 30 31 32 33 34 35 │ │ LB5 LB4 LB3 LB2 LB1 LB0 │ RB0 RB1 RB2 RB3 RB4 RB5 │ + ╰───────────╮ 36 37 38 │ 39 40 41 ╭───────────╯ ╰───────────╮ LH2 LH1 LH0 │ RH0 RH1 RH2 ╭───────────╯ + ╰────────────┴────────────╯ ╰─────────────┴─────────────╯ */ + +#pragma once + +#define LT0 5 // left-top row +#define LT1 4 +#define LT2 3 +#define LT3 2 +#define LT4 1 +#define LT5 0 + +#define RT0 6 // right-top row +#define RT1 7 +#define RT2 8 +#define RT3 9 +#define RT4 10 +#define RT5 11 + +#define LM0 17 // left-middle row +#define LM1 16 +#define LM2 15 +#define LM3 14 +#define LM4 13 +#define LM5 12 + +#define RM0 18 // right-middle row +#define RM1 19 +#define RM2 20 +#define RM3 21 +#define RM4 22 +#define RM5 23 + +#define LB0 29 // left-bottom row +#define LB1 28 +#define LB2 27 +#define LB3 26 +#define LB4 25 +#define LB5 24 + +#define RB0 30 // right-bottom row +#define RB1 31 +#define RB2 32 +#define RB3 33 +#define RB4 34 +#define RB5 35 + +#define LH0 38 // left thumb keys +#define LH1 37 +#define LH2 36 + +#define RH0 39 // right thumb keys +#define RH1 40 +#define RH2 41 + diff --git a/zmk-nodefree-config/keypos_def/keypos_48keys.h b/zmk-nodefree-config/keypos_def/keypos_48keys.h new file mode 100644 index 0000000..3c171f0 --- /dev/null +++ b/zmk-nodefree-config/keypos_def/keypos_48keys.h @@ -0,0 +1,67 @@ +/* 48 KEY MATRIX / LAYOUT MAPPING + + ╭────────────────────────┬────────────────────────╮ ╭─────────────────────────┬─────────────────────────╮ + │ 0 1 2 3 4 5 │ 6 7 8 9 10 11 │ │ LT5 LT4 LT3 LT2 LT1 LT0 │ RT0 RT1 RT2 RT3 RT4 RT5 │ + │ 12 13 14 15 16 17 │ 18 19 20 21 22 23 │ │ LM5 LM4 LM3 LM2 LM1 LM0 │ RM0 RM1 RM2 RM3 RM4 RM5 │ + │ 24 25 26 27 28 29 │ 30 31 32 33 34 35 │ │ LB5 LB4 LB3 LB2 LB1 LB0 │ RB0 RB1 RB2 RB3 RB4 RB5 │ + │ 36 37 38 39 40 41 │ 42 43 44 45 46 47 │ │ LH5 LH4 LH3 LH2 LH1 LH0 │ RH0 RH1 RH2 RH3 RH4 RH5 │ + ╰────────────────────────┴────────────────────────╯ ╰─────────────────────────┴─────────────────────────╯ */ + +#pragma once + +#define LT0 5 // left-top row +#define LT1 4 +#define LT2 3 +#define LT3 2 +#define LT4 1 +#define LT5 0 + +#define RT0 6 // right-top row +#define RT1 7 +#define RT2 8 +#define RT3 9 +#define RT4 10 +#define RT5 11 + +#define LM0 17 // left-middle row +#define LM1 16 +#define LM2 15 +#define LM3 14 +#define LM4 13 +#define LM5 12 + +#define RM0 18 // right-middle row +#define RM1 19 +#define RM2 20 +#define RM3 21 +#define RM4 22 +#define RM5 23 + +#define LB0 29 // left-bottom row +#define LB1 28 +#define LB2 27 +#define LB3 26 +#define LB4 25 +#define LB5 24 + +#define RB0 30 // right-bottom row +#define RB1 31 +#define RB2 32 +#define RB3 33 +#define RB4 34 +#define RB5 35 + +#define LH0 41 // left thumb keys +#define LH1 40 +#define LH2 39 +#define LH3 38 +#define LH4 37 +#define LH5 36 + +#define RH0 42 // right thumb keys +#define RH1 43 +#define RH2 44 +#define RH3 45 +#define RH4 46 +#define RH5 47 + diff --git a/zmk-nodefree-config/keypos_def/keypos_48keys_wide.h b/zmk-nodefree-config/keypos_def/keypos_48keys_wide.h new file mode 100644 index 0000000..73d7e30 --- /dev/null +++ b/zmk-nodefree-config/keypos_def/keypos_48keys_wide.h @@ -0,0 +1,59 @@ +/* 48 KEY MATRIX / WIDE LAYOUT MAPPING + + ╭────────────────────────┬────────────────────────╮ ╭─────────────────────────┬─────────────────────────╮ + │ 0 1 2 3 4 5 │ 6 7 8 9 10 11 │ │ LT4 LT3 LT2 LT1 LT0 --- │ --- RT0 RT1 RT2 RT3 RT4 │ + │ 12 13 14 15 16 17 │ 18 19 20 21 22 23 │ │ LM4 LM3 LM2 LM1 LM0 --- │ --- RM0 RM1 RM2 RM3 RM4 │ + │ 24 25 26 27 28 29 │ 30 31 32 33 34 35 │ │ LB4 LB3 LB2 LB1 LB0 --- │ --- RB0 RB1 RB2 RB3 RB4 │ + │ 36 37 38 39 40 41 │ 42 43 44 45 46 47 │ │ LH4 LH3 LH2 LH1 LH0 --- │ --- RH0 RH1 RH2 RH3 RH4 │ + ╰────────────────────────┴────────────────────────╯ ╰─────────────────────────┴─────────────────────────╯ */ + +#pragma once + +#define LT0 4 // left-top row +#define LT1 3 +#define LT2 2 +#define LT3 1 +#define LT4 0 + +#define RT0 7 // right-top row +#define RT1 8 +#define RT2 9 +#define RT3 10 +#define RT4 11 + +#define LM0 16 // left-middle row +#define LM1 15 +#define LM2 14 +#define LM3 13 +#define LM4 12 + +#define RM0 19 // right-middle row +#define RM1 20 +#define RM2 21 +#define RM3 22 +#define RM4 23 + +#define LB0 28 // left-bottom row +#define LB1 27 +#define LB2 26 +#define LB3 25 +#define LB4 24 + +#define RB0 31 // right-bottom row +#define RB1 32 +#define RB2 33 +#define RB3 34 +#define RB4 35 + +#define LH0 40 // left thumb keys +#define LH1 39 +#define LH2 38 +#define LH3 37 +#define LH4 36 + +#define RH0 43 // right thumb keys +#define RH1 44 +#define RH2 45 +#define RH3 46 +#define RH4 47 +