From 85ef39b09d031798c13d35b9d80c4fa462d335e8 Mon Sep 17 00:00:00 2001 From: Jens Kadenbach Date: Mon, 20 Jul 2026 18:05:50 +0200 Subject: [PATCH] Add audax_osx ErgoDox layout for macOS software neo2 neo2 for macOS via the installed software neo2 keyboard layout instead of firmware emulation. The base layer and Mod3 send positional NE_* scancodes that the OS neo2 layout translates; Mod4 (cursor and numpad) is a firmware layer so its keys auto-repeat. Keeps the audax_osx_neo2 key arrangement, home-row mods, macOS shortcuts and media/function layer. The right Mod3 key taps to the neo2 sharp-s; a dedicated right-hand Mod4 key and the Enter thumb both reach the Mod4 layer. --- layouts/community/ergodox/audax_osx/config.h | 6 + layouts/community/ergodox/audax_osx/keymap.c | 259 ++++++++++++++++++ layouts/community/ergodox/audax_osx/layers.h | 10 + layouts/community/ergodox/audax_osx/readme.md | 86 ++++++ 4 files changed, 361 insertions(+) create mode 100644 layouts/community/ergodox/audax_osx/config.h create mode 100644 layouts/community/ergodox/audax_osx/keymap.c create mode 100644 layouts/community/ergodox/audax_osx/layers.h create mode 100644 layouts/community/ergodox/audax_osx/readme.md diff --git a/layouts/community/ergodox/audax_osx/config.h b/layouts/community/ergodox/audax_osx/config.h new file mode 100644 index 0000000000..09cfa11486 --- /dev/null +++ b/layouts/community/ergodox/audax_osx/config.h @@ -0,0 +1,6 @@ +#pragma once + +// Tap/hold window for the home-row mods (LCTL_T/LALT_T/LGUI_T) and the +// RALT_T(KC_ENT) Mod4 key. +#undef TAPPING_TERM +#define TAPPING_TERM 200 diff --git a/layouts/community/ergodox/audax_osx/keymap.c b/layouts/community/ergodox/audax_osx/keymap.c new file mode 100644 index 0000000000..7cd8667aeb --- /dev/null +++ b/layouts/community/ergodox/audax_osx/keymap.c @@ -0,0 +1,259 @@ +#include QMK_KEYBOARD_H +#include "layers.h" +#include "keymap_extras/keymap_neo2.h" + +/* + * audax_osx - neo2 for macOS via the *software* neo2 keyboard layout. + * + * Unlike the sibling `audax_osx_neo2` layout (which emulates neo2 in firmware + * on top of a US/ABC-Extended macOS layout), this layout expects the neo2 + * layout to be installed on the Mac itself: + * + * - neo2-layout-osx (.keylayout / .bundle) provides base, shift and Mod3. + * + * The base/Mod3 keys send *positional* scancodes (the NE_* aliases from + * keymap_extras/keymap_neo2.h); the OS neo2 layout produces those characters. + * See readme.md for the macOS/Karabiner setup. + * + * --------------------------------------------------------------------------- + * neo2 layer switching: + * + * Mod3 (layer 3, symbols): held on both Mod3 keys, which send '#' (NE_L3R / + * KC_NUHS); the OS neo2 .keylayout turns '#' into Mod3. The canonical + * neo2 Mod3-left is CapsLock, but macOS intercepts CapsLock, so it does + * NOT act as Mod3 unless Karabiner remaps caps_lock -> '#'. Sending '#' + * from both keys works out of the box. + * + * Mod4 (layer 4, cursor + numpad): provided as a FIRMWARE layer (NEO_4), + * held via the right-thumb Enter key (NEO_ENT_M4). Kept in firmware - + * not the OS neo2 Mod4 - so the cursor keys auto-repeat when held, like + * the Linux `audax` layout. Mod4 special characters (ordinals, GBP, ...) + * are omitted; add them to NEO_4 if you need them. + * --------------------------------------------------------------------------- + */ + +// Custom keycodes. +enum custom_keycodes { + // Right Mod3 key: hold = Mod3 (sends NE_L3R / '#'), tap = ss (neo2 native). + NEO_L3R_SS = SAFE_RANGE, + // Right-thumb Enter: tap = Enter, hold = the firmware Mod4 layer (NEO_4). + NEO_ENT_M4 +}; + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + /* NEO_1: neo2 base layer (positional scancodes) + * + * The character legend below is what the *OS neo2 layout* produces for each + * key; the firmware only sends the matching physical scancode. + * + * ,--------------------------------------------------. ,--------------------------------------------------. + * | ESC | 1/° | 2/§ | 3/ | 4/» | 5/« | MPLY | | MNXT | 6/$ | 7/€ | 8/„ | 9/“ | 0/” | -/— | + * |--------+------+------+------+------+-------------| |------+------+------+------+------+------+--------| + * | TAB | X | V | L | C | W | FKEYS| | RCTL | K | H | G | F | Q | Y | + * |--------+------+------+------+------+------| | | |------+------+------+------+------+--------| + * | Mod3-L | CTL/U| ALT/I| GUI/A| E | O |------| |------| S | N | R | T | D | #/ß M3R| + * |--------+------+------+------+------+------| LALT | | MOD4 |------+------+------+------+------+--------| + * | LSHIFT | Ü | Ö | Ä | P | Z | | | | B | M | , | . | J | SFT/ESC| + * `--------+------+------+------+------+-------------' `-------------+------+------+------+------+--------' + * | Home | PGUP | PGDN | End | S(G(a)) | Left | Down | Up | Right| RGUI | + * `----------------------------------' `----------------------------------' + * ,-------------. ,-------------. + * |G(TAB)|G(S(T))| |A(G(Lft))|A(G(Rgt))| + * ,------|------|------| |------+------+------. + * | | | Ins | |A(G(Home))| MO | | + * | Space|Bksp |------| |------|FKEYS | Enter| + * | | | LCTL | | | |/Mod4 | + * `--------------------' `--------------------' + */ + [NEO_1] = LAYOUT_ergodox( + // left hand side - main + KC_ESC, NE_1, NE_2, NE_3, NE_4, NE_5, KC_MPLY, + KC_TAB, NE_X, NE_V, NE_L, NE_C, NE_W, MO(FKEYS), + NE_L3R, LCTL_T(NE_U), LALT_T(NE_I), LGUI_T(NE_A), NE_E, NE_O, /* --- */ // Mod3-left: send '#' like Mod3-right (macOS intercepts CapsLock, so NE_L3L is unreliable) + KC_LSFT, NE_UDIA, NE_ODIA, NE_ADIA, NE_P, NE_Z, KC_LALT, + KC_HOME, KC_PGUP, KC_PGDN, KC_END, LSFT(LGUI(NE_A)), /* --- */ /* --- */ + + // left hand side - thumb cluster + /* --- */ LGUI(KC_TAB), LGUI(LSFT(KC_TAB)), + /* KC_SPACE */ /* KC_BSPC */ KC_INS, + KC_SPACE, KC_BSPC, KC_LCTL, + + // right hand side - main + KC_MNXT, NE_6, NE_7, NE_8, NE_9, NE_0, NE_MINS, + KC_RCTL, NE_K, NE_H, NE_G, NE_F, NE_Q, NE_Y, + /* --- */ NE_S, NE_N, NE_R, NE_T, NE_D, NEO_L3R_SS, + MO(NEO_4), NE_B, NE_M, NE_COMM, NE_DOT, NE_J, RSFT_T(KC_ESC), + /* --- */ /* --- */ KC_LEFT, KC_DOWN, KC_UP, KC_RIGHT, KC_RGUI, + + // right hand side - thumb cluster + LALT(LGUI(KC_LEFT)), LALT(LGUI(KC_RIGHT)), /* --- */ + LALT(LGUI(KC_HOME)), /* --- */ /* --- */ + MO(FKEYS), KC_SPACE, NEO_ENT_M4 + ), + + /* NEO_4: Mod4 - cursor block (left) + numpad (right) + * + * Firmware layer with real keycodes so the cursor keys auto-repeat when held. + * Held via the right-thumb Enter key. neo2 Mod4 special characters (ordinals, + * GBP, inverted !/?, ...) are omitted as KC_NO. + * + * ,--------------------------------------------------. ,--------------------------------------------------. + * | ---- | ---- | ---- | ---- | ---- | ---- | | | | ---- | Tab | / | * | - | ---- | + * |--------+------+------+------+------+-------------| |------+------+------+------+------+------+--------| + * | | PgUp | Bsp | Up | Del | PgDn | | | | ---- | 7 | 8 | 9 | + | ---- | + * |--------+------+------+------+------+------| | | |------+------+------+------+------+--------| + * | | Home | Left | Down | Right| End |------| |------| ---- | 4 | 5 | 6 | , | . | + * |--------+------+------+------+------+------| | | |------+------+------+------+------+--------| + * | | Esc | Tab | Ins |Return| ---- | | | | ---- | 1 | 2 | 3 | ---- | | + * `--------+------+------+------+------+-------------' `-------------+------+------+------+------+--------' + * | | | | | | | 0 | | | | | + * `----------------------------------' `----------------------------------' + * ,-------------. ,-------------. + * | | | | | | + * ,------|------|------| |------+------+------. + * | | | | | | | | + * | | Del |------| |------| | | + * | | | | | | | | + * `--------------------' `--------------------' + */ + [NEO_4] = LAYOUT_ergodox( + // left hand side - main + KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, _______, + _______, KC_PGUP, KC_BSPC, KC_UP, KC_DELETE, KC_PGDN, _______, + _______, KC_HOME, KC_LEFT, KC_DOWN, KC_RIGHT, KC_END, /* --- */ + _______, KC_ESCAPE, KC_TAB, KC_INSERT, KC_ENTER, KC_NO, _______, + _______, _______, _______, _______, _______, /* --- */ /* --- */ + + // left hand side - thumb cluster + /* --- */ _______, _______, + /* --- */ /* --- */ _______, + _______, KC_DEL, _______, + + // right hand side - main + _______, KC_NO, KC_TAB, KC_KP_SLASH, KC_KP_ASTERISK, KC_KP_MINUS, KC_NO, + _______, KC_NO, KC_KP_7, KC_KP_8, KC_KP_9, KC_KP_PLUS, KC_NO, + /* --- */ KC_NO, KC_KP_4, KC_KP_5, KC_KP_6, KC_KP_COMMA, KC_KP_DOT, + _______, KC_NO, KC_KP_1, KC_KP_2, KC_KP_3, KC_NO, _______, + /* --- */ /* --- */ _______, KC_KP_0, _______, _______, _______, + + // right hand side - thumb cluster + _______, _______, /* --- */ + _______, /* --- */ /* --- */ + _______, _______, _______ + ), + + /* FKEYS: Function and media keys + * + * ,--------------------------------------------------. ,--------------------------------------------------. + * | Prev | F1 | F2 | F3 | F4 | F5 | F11 | | F12 | F6 | F7 | F8 | F9 | F10 | | + * |--------+------+------+------+------+-------------| |------+------+------+------+------+------+--------| + * | Play | | | | | | | | | | | | | | | + * |--------+------+------+------+------+------| | | |------+------+------+------+------+--------| + * | Next | | | | | |------| |------| | | | | | | + * |--------+------+------+------+------+------| | | |------+------+------+------+------+--------| + * | | | | | | | | | | | | |VolDn | Mute | VolUp | + * `--------+------+------+------+------+-------------' `-------------+------+------+------+------+--------' + * | | | | | | | | | | | | + * `----------------------------------' `----------------------------------' + */ + [FKEYS] = LAYOUT_ergodox( + // left hand side - main + KC_MEDIA_REWIND, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F11, + KC_MEDIA_PLAY_PAUSE, _______, _______, _______, _______, _______, _______, + KC_MEDIA_FAST_FORWARD, _______, _______, _______, _______, _______, /* --- */ + _______, _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, _______, /* --- */ /* --- */ + + // left hand side - thumb cluster + /* --- */ _______, _______, + /* --- */ /* --- */ _______, + _______, _______, _______, + + // right hand side - main + KC_F12, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, _______, + _______, _______, _______, _______, _______, _______, _______, + /* --- */ _______, _______, _______, _______, _______, _______, + _______, _______, _______, _______, KC_AUDIO_VOL_DOWN, KC_AUDIO_MUTE, KC_AUDIO_VOL_UP, + /* --- */ /* --- */ _______, _______, _______, _______, _______, + + // right hand side - thumb cluster + _______, _______, /* --- */ + _______, /* --- */ /* --- */ + _______, _______, _______ + ), +}; + +// Tracks the tap/hold state of the right Mod3 key so it can send ss on a tap +// while acting as the neo2 Mod3 modifier on a hold. +static bool l3r_held = false; +static bool l3r_used_as_mod3 = false; + +// Tracks the tap/hold state of the right-thumb Enter/Mod4 key: a pure tap emits +// Enter, a hold turns on the NEO_4 (Mod4) layer. +static bool ent_held = false; +static bool ent_used = false; + +// Runs for each key down or up event. +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + switch (keycode) { + case NEO_L3R_SS: + if (record->event.pressed) { + // Engage neo2 Mod3 (the '#' / non-US-# scancode) for as long as held. + register_code(NE_L3R); + l3r_held = true; + l3r_used_as_mod3 = false; + } else { + unregister_code(NE_L3R); + l3r_held = false; + // Nothing else was pressed while held -> it was a tap: send ss. + if (!l3r_used_as_mod3) { + tap_code(NE_SS); + } + } + return false; + case NEO_ENT_M4: + if (record->event.pressed) { + // Hold = neo2 Mod4 as a firmware layer, so its cursor keys auto-repeat. + // (Enter+Backspace = Delete too: NEO_4 maps that thumb key to KC_DEL.) + layer_on(NEO_4); + ent_held = true; + ent_used = false; + } else { + layer_off(NEO_4); + ent_held = false; + // Nothing else was pressed while held -> it was a tap: send Enter. + if (!ent_used) { + tap_code(KC_ENT); + } + } + return false; + default: + break; + } + + // A separate keypress while a hold-key is held means that key is acting as a + // modifier/layer, so its own release must not emit its tap code. + if (record->event.pressed) { + if (l3r_held) { + l3r_used_as_mod3 = true; + } + if (ent_held) { + ent_used = true; + } + } + return true; +} + +// Runs constantly in the background, in a loop. Lights an LED on the FKEYS layer. +void matrix_scan_user(void) { + uint8_t layer = get_highest_layer(layer_state); + + ergodox_board_led_off(); + ergodox_right_led_1_off(); + ergodox_right_led_2_off(); + ergodox_right_led_3_off(); + + if (layer == FKEYS) { + ergodox_right_led_1_on(); + } +} diff --git a/layouts/community/ergodox/audax_osx/layers.h b/layouts/community/ergodox/audax_osx/layers.h new file mode 100644 index 0000000000..f67416a496 --- /dev/null +++ b/layouts/community/ergodox/audax_osx/layers.h @@ -0,0 +1,10 @@ +#pragma once + +// Software-neo2 layout: the OS-side neo2 keyboard layout owns the base layer, +// shift and Mod3 (layer 3). Mod4 (layer 4) is kept as a firmware layer so its +// cursor keys auto-repeat when held. +enum layers { + NEO_1, // layer 0: neo2 base (positional scancodes) + NEO_4, // layer 1: neo2 Mod4 - cursor + numpad (firmware, real keycodes) + FKEYS // layer 2: media and function keys (not part of neo2) +}; diff --git a/layouts/community/ergodox/audax_osx/readme.md b/layouts/community/ergodox/audax_osx/readme.md new file mode 100644 index 0000000000..7039e49c25 --- /dev/null +++ b/layouts/community/ergodox/audax_osx/readme.md @@ -0,0 +1,86 @@ +# audax_osx - neo2 for macOS via the software neo2 layout + Karabiner-Elements + +This is a neo2 ErgoDox layout for macOS that relies on the **software** neo2 +keyboard layout installed on the Mac, rather than emulating neo2 in firmware. + +It is the software-neo2 counterpart of the sibling `audax_osx_neo2` layout. The +key arrangement, thumb clusters, home-row mods and macOS shortcuts are the same; +the difference is who produces the neo2 characters: + +| | `audax_osx_neo2` (old) | `audax_osx` (this) | +| --- | --- | --- | +| macOS input source | US / ABC Extended | **neo2** (installed on the Mac) | +| neo2 letters | faked in firmware | produced by the OS | +| umlauts, ß, €, „ … | Option dead-key macros in firmware | produced by the OS | +| layer 3 (symbols) | firmware layer | produced by the OS | +| layer 4 (cursor/numpad) | firmware layer | firmware layer (so its keys auto-repeat) | +| firmware sends | US scancodes + macros | **positional neo2 scancodes** (`NE_*`) | + +Base layer, shift and Mod3 live in the OS neo2 layout, so the firmware stays +small: a neo2 base layer, a Mod4 cursor/numpad layer, a media/function layer, +and two small custom keys. + +## Required macOS setup + +1. Install the neo2 macOS keyboard layout (`.keylayout` / `.bundle`): + + and select it as the active input source. It provides the base layer, shift + and **Mod3**. +2. Optional: install [Karabiner-Elements](https://karabiner-elements.pqrs.org/) + and import only if you + want the OS-side neo2 **layer 6** (math symbols). Mod4 (cursor/numpad) is + handled by the firmware in this layout, so Karabiner is not required for it. + +References: [Neo auf dem Apple Macintosh einrichten](https://old-wiki.neo-layout.org/neowiki/wiki/Neo%20auf%20dem%20Apple%20Macintosh%20einrichten). + +## Layer switching + +**Mod3 (layer 3, symbols)** is handled by the OS neo2 `.keylayout`. Both Mod3 +keys send `#` (`KC_NUHS`), which the `.keylayout` treats as Mod3. + +| Role | Keycode | Sends | Notes | +| --- | --- | --- | --- | +| Mod3-left | `NE_L3R` | `#` (`KC_NUHS`) | held = Mod3 | +| Mod3-right | `NEO_L3R_SS` | `#` (`KC_NUHS`) | held = Mod3, **tap = ß** | + +**Why both Mod3 keys send `#`:** the canonical neo2 Mod3-left is CapsLock, but +macOS intercepts CapsLock and it does not register as a live Mod3 modifier +unless Karabiner explicitly remaps `caps_lock` to the `#` key. Sending `#` from +both keys makes the left Mod3 work out of the box. If you prefer true CapsLock +semantics, add that Karabiner remap and use `NE_L3L` for the left Mod3 key. + +**Mod4 (layer 4, cursor + numpad)** is a firmware layer (`NEO_4`), held via the +right-thumb Enter key. It is kept in firmware - not the OS neo2 Mod4 - so the +cursor keys auto-repeat when held, matching the Linux `audax` layout. The neo2 +Mod4 special characters are omitted; add them to `NEO_4` in `keymap.c` if wanted. + +**Layer 6** (Mod3+Mod4 math symbols) is not available, because Mod4 is not an OS +modifier in this layout. Enable it through Karabiner if you need it. + +## Behaviour worth knowing + +- **Right Mod3 key (tap = ß):** in neo2 the right-hand Mod3 key is a pure + modifier. To keep the muscle memory from `audax_osx_neo2`, tapping it alone + sends `ß` (neo2's native `NE_SS` position); holding it acts as Mod3 + (`NEO_L3R_SS` in `keymap.c`). +- **Enter thumb key (tap = Enter, hold = Mod4, +Backspace = Delete):** the + right-thumb Enter is the custom `NEO_ENT_M4` key. A tap types Enter; a hold + turns on the `NEO_4` firmware layer (cursor + numpad, all auto-repeating). On + that layer the Backspace thumb key is `KC_DEL`, so holding Enter and pressing + Backspace produces Delete - the Enter+Backspace convenience from the Linux + `audax` layout. +- **Dedicated Mod4 key:** the right inner bottom key (row 4, the big 1.5u key by + the thumb cluster) is `MO(NEO_4)` - a real momentary Mod4. Hold it with the + right hand and work the cursor block on the left. The Enter thumb is a second + Mod4 (on hold). Left Option remains on the left outer Alt key. +- **Home-row mods:** left home row is `LCTL_T`/`LALT_T`/`LGUI_T` on `U`/`I`/`A`. +- **`FKEYS` layer** (media + F-keys) is firmware-only; it is not part of neo2 and + lights the first right-hand LED while active. + +## Building + +``` +qmk compile -kb ergodox_ez -km audax_osx +``` + +(Use the appropriate ErgoDox keyboard target for your board.)