├── config ├── charybdis_left.conf ├── charybdis_zmk.yml ├── west.yml ├── charybdis_right.conf ├── charybdis.conf ├── combos.dtsi ├── charybdis-layouts.dtsi ├── behaviors.dtsi ├── charybdis.json ├── macros.dtsi └── charybdis.keymap ├── boards └── shields │ ├── charybdis-dongle │ ├── charybdis_left.conf │ ├── charybdis_right.conf │ ├── Kconfig.shield │ ├── Kconfig.defconfig │ ├── charybdis_dongle.conf │ ├── charybdis_dongle.overlay │ ├── charybdis_left.overlay │ ├── charybdis_right.overlay │ ├── charybdis.dtsi │ └── charybdis_pmw3610.dtsi │ └── charybdis-bt │ ├── Kconfig.shield │ ├── Kconfig.defconfig │ ├── charybdis_left.conf │ ├── charybdis_right.conf │ ├── charybdis_left.overlay │ ├── charybdis_right.overlay │ ├── charybdis.dtsi │ └── charybdis_pmw3610.dtsi ├── .gitignore ├── zephyr └── module.yml ├── .github └── workflows │ ├── build.yml │ ├── user_config_build.yaml │ └── draw_keymaps.yaml ├── keymap-drawer ├── config.yaml └── charybdis.yaml ├── scripts └── convert_keymap.py └── README.md /config/charybdis_left.conf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /boards/shields/charybdis-dongle/charybdis_left.conf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /boards/shields/charybdis-dongle/charybdis_right.conf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | boards/shields/charybdis/charybdis.keymap 2 | drivers/ -------------------------------------------------------------------------------- /zephyr/module.yml: -------------------------------------------------------------------------------- 1 | build: 2 | settings: 3 | board_root: . 4 | -------------------------------------------------------------------------------- /boards/shields/charybdis-bt/Kconfig.shield: -------------------------------------------------------------------------------- 1 | config SHIELD_CHARYBDIS_LEFT 2 | def_bool $(shields_list_contains,charybdis_left) 3 | 4 | config SHIELD_CHARYBDIS_RIGHT 5 | def_bool $(shields_list_contains,charybdis_right) -------------------------------------------------------------------------------- /boards/shields/charybdis-dongle/Kconfig.shield: -------------------------------------------------------------------------------- 1 | config SHIELD_CHARYBDIS_DONGLE 2 | def_bool $(shields_list_contains,charybdis_dongle) 3 | 4 | config SHIELD_CHARYBDIS_LEFT 5 | def_bool $(shields_list_contains,charybdis_left) 6 | 7 | config SHIELD_CHARYBDIS_RIGHT 8 | def_bool $(shields_list_contains,charybdis_right) -------------------------------------------------------------------------------- /boards/shields/charybdis-bt/Kconfig.defconfig: -------------------------------------------------------------------------------- 1 | if SHIELD_CHARYBDIS_RIGHT 2 | 3 | config ZMK_KEYBOARD_NAME 4 | default "Charybdis" 5 | 6 | config ZMK_SPLIT_BLE_ROLE_CENTRAL 7 | default y 8 | 9 | endif 10 | 11 | if SHIELD_CHARYBDIS_LEFT || SHIELD_CHARYBDIS_RIGHT 12 | 13 | config ZMK_SPLIT 14 | default y 15 | 16 | endif -------------------------------------------------------------------------------- /boards/shields/charybdis-bt/charybdis_left.conf: -------------------------------------------------------------------------------- 1 | ## ZMK Studio - Move this to config/charybdis.conf when dongle works with ZMK Studio 2 | ## https://zmk.dev/docs/features/studio 3 | CONFIG_ZMK_STUDIO=y 4 | ## https://zmk.dev/docs/config/studio 5 | CONFIG_ZMK_STUDIO_LOCKING=y 6 | CONFIG_ZMK_STUDIO_LOCK_ON_DISCONNECT=y 7 | CONFIG_ZMK_STUDIO_TRANSPORT_BLE_PREF_LATENCY=5 -------------------------------------------------------------------------------- /boards/shields/charybdis-dongle/Kconfig.defconfig: -------------------------------------------------------------------------------- 1 | if SHIELD_CHARYBDIS_DONGLE 2 | 3 | config ZMK_KEYBOARD_NAME 4 | default "Charybdis" 5 | 6 | config ZMK_SPLIT_ROLE_CENTRAL 7 | default y 8 | 9 | endif 10 | 11 | if SHIELD_CHARYBDIS_LEFT || SHIELD_CHARYBDIS_RIGHT || SHIELD_CHARYBDIS_DONGLE 12 | 13 | config ZMK_SPLIT 14 | default y 15 | 16 | endif -------------------------------------------------------------------------------- /config/charybdis_zmk.yml: -------------------------------------------------------------------------------- 1 | file_format: "1" 2 | id: charybdis 3 | name: Charybdis 4 | type: shield 5 | url: https://github.com/Bastardkb/Charybdis/ 6 | requires: [pro_micro] 7 | features: 8 | - Keys 9 | - QWERTY 10 | - Colemak DH 11 | - Mouse keys pointer 12 | - Trackball pointer 13 | - Dongle/Bluetooth/USB 14 | siblings: 15 | - charybdis_left 16 | - charybdis_right 17 | - charybdis_dongle 18 | -------------------------------------------------------------------------------- /boards/shields/charybdis-dongle/charybdis_dongle.conf: -------------------------------------------------------------------------------- 1 | ## Split keyboards 2 | ## https://zmk.dev/docs/config/system#split-keyboards 3 | CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS=2 4 | CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y 5 | CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY=y 6 | 7 | CONFIG_ZMK_SLEEP=n ## Uncomment this if there are connectivity issues 8 | 9 | ## Display Configuratoin 10 | ## https://zmk.dev/docs/config/displays 11 | # CONFIG_ZMK_DISPLAY=n 12 | 13 | # ## Track words per minute 14 | # CONFIG_ZMK_WPM=y -------------------------------------------------------------------------------- /boards/shields/charybdis-bt/charybdis_right.conf: -------------------------------------------------------------------------------- 1 | ## Split keyboards 2 | ## https://zmk.dev/docs/config/system#split-keyboards 3 | CONFIG_ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS=1 4 | CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y 5 | CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY=y 6 | 7 | ## ZMK Studio - Move this to config/charybdis.conf when dongle works with ZMK Studio 8 | ## https://zmk.dev/docs/features/studio 9 | CONFIG_ZMK_STUDIO=y 10 | ## https://zmk.dev/docs/config/studio 11 | CONFIG_ZMK_STUDIO_LOCKING=y 12 | CONFIG_ZMK_STUDIO_LOCK_ON_DISCONNECT=y 13 | CONFIG_ZMK_STUDIO_TRANSPORT_BLE_PREF_LATENCY=5 -------------------------------------------------------------------------------- /config/west.yml: -------------------------------------------------------------------------------- 1 | manifest: 2 | remotes: 3 | - name: zmkfirmware 4 | url-base: https://github.com/zmkfirmware 5 | - name: badjeff 6 | url-base: https://github.com/badjeff 7 | projects: 8 | - name: zmk 9 | remote: zmkfirmware 10 | revision: main 11 | import: app/west.yml 12 | - name: zmk-pmw3610-driver 13 | remote: badjeff 14 | revision: main 15 | - name: zmk-split-peripheral-input-relay 16 | remote: badjeff 17 | revision: main 18 | - name: zmk-input-behavior-listener 19 | remote: badjeff 20 | revision: main 21 | self: 22 | path: config -------------------------------------------------------------------------------- /boards/shields/charybdis-dongle/charybdis_dongle.overlay: -------------------------------------------------------------------------------- 1 | #include "charybdis.dtsi" 2 | 3 | / { 4 | chosen { 5 | zmk,kscan = &mock_kscan; 6 | }; 7 | 8 | mock_kscan: kscan_1 { 9 | compatible = "zmk,kscan-mock"; 10 | columns = <0>; 11 | rows = <0>; 12 | events = <0>; 13 | }; 14 | }; 15 | 16 | / { 17 | vtrackball: virtual_input_trackball { 18 | compatible = "zmk,virtual-input"; 19 | }; 20 | 21 | input_relay_config_trackball { 22 | compatible = "zmk,split-peripheral-input-relay"; 23 | device = <&vtrackball>; 24 | relay-channel = <111>; 25 | }; 26 | }; -------------------------------------------------------------------------------- /config/charybdis_right.conf: -------------------------------------------------------------------------------- 1 | ## PMW3610 Driver 2 | ## https://github.com/badjeff/zmk-pmw3610-driver 3 | CONFIG_SPI=y 4 | CONFIG_PMW3610=y 5 | #CONFIG_SPI_LOG_LEVEL_DBG=y 6 | 7 | ## https://github.com/badjeff/zmk-pmw3610-driver/blob/main/src/pmw3610.c 8 | CONFIG_PMW3610_SMART_ALGORITHM=y 9 | 10 | CONFIG_PMW3610_SWAP_XY=y 11 | CONFIG_PMW3610_INVERT_X=y 12 | CONFIG_PMW3610_INVERT_Y=y 13 | CONFIG_PMW3610_REPORT_INTERVAL_MIN=12 14 | 15 | CONFIG_PMW3610_REST1_SAMPLE_TIME_MS=100 16 | CONFIG_PMW3610_REST2_SAMPLE_TIME_MS=200 17 | CONFIG_PMW3610_REST3_SAMPLE_TIME_MS=300 18 | CONFIG_PMW3610_RUN_DOWNSHIFT_TIME_MS=2000 19 | CONFIG_PMW3610_REST1_DOWNSHIFT_TIME_MS=3000 20 | CONFIG_PMW3610_REST2_DOWNSHIFT_TIME_MS=30000 -------------------------------------------------------------------------------- /boards/shields/charybdis-dongle/charybdis_left.overlay: -------------------------------------------------------------------------------- 1 | #include "charybdis.dtsi" 2 | #include "charybdis_pmw3610.dtsi" 3 | 4 | &kscan0 { 5 | compatible = "zmk,kscan-gpio-matrix"; 6 | 7 | col-gpios 8 | = <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 9 | , <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 10 | , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 11 | , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 12 | , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 13 | , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 14 | ; 15 | 16 | row-gpios 17 | = <&pro_micro 18 GPIO_ACTIVE_HIGH> 18 | , <&pro_micro 5 GPIO_ACTIVE_HIGH> 19 | , <&pro_micro 4 GPIO_ACTIVE_HIGH> 20 | , <&pro_micro 9 GPIO_ACTIVE_HIGH> 21 | ; 22 | }; 23 | 24 | / { 25 | vtrackball: virtual_input_trackball { 26 | compatible = "zmk,virtual-input"; 27 | }; 28 | }; -------------------------------------------------------------------------------- /boards/shields/charybdis-bt/charybdis_left.overlay: -------------------------------------------------------------------------------- 1 | #include "charybdis.dtsi" 2 | #include "charybdis_pmw3610.dtsi" 3 | 4 | &kscan0 { 5 | compatible = "zmk,kscan-gpio-matrix"; 6 | 7 | col-gpios 8 | = <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 9 | , <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 10 | , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 11 | , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 12 | , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 13 | , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 14 | ; 15 | 16 | row-gpios 17 | = <&pro_micro 18 GPIO_ACTIVE_HIGH> 18 | , <&pro_micro 5 GPIO_ACTIVE_HIGH> 19 | , <&pro_micro 4 GPIO_ACTIVE_HIGH> 20 | , <&pro_micro 9 GPIO_ACTIVE_HIGH> 21 | ; 22 | }; 23 | 24 | // https://github.com/badjeff/zmk-pmw3610-driver 25 | / { 26 | trackball_listener { 27 | compatible = "zmk,input-listener"; 28 | device = <&trackball>; 29 | }; 30 | }; -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: ZMK Firmware 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | schedule: 7 | - cron: '0 0 1 */2 *' # run every two months to keep artifacts from expiring 8 | 9 | jobs: 10 | convert-and-store-keymap: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-python@v5 18 | with: 19 | python-version: "3.x" 20 | - run: python scripts/convert_keymap.py -c q2c --in-path "$GITHUB_WORKSPACE/config/charybdis.keymap" 21 | 22 | - uses: actions/upload-artifact@v4 23 | with: 24 | name: keymap_files 25 | path: ${{ github.workspace }}/config/*.keymap 26 | 27 | build: 28 | needs: convert-and-store-keymap 29 | uses: ./.github/workflows/user_config_build.yaml 30 | # uses: zmkfirmware/zmk/.github/workflows/build-user-config.yml@main 31 | 32 | keymap_images: 33 | needs: build 34 | permissions: 35 | contents: write 36 | uses: ./.github/workflows/draw_keymaps.yaml 37 | # credit to https://github.com/caksoylar/keymap-drawer 38 | -------------------------------------------------------------------------------- /boards/shields/charybdis-bt/charybdis_right.overlay: -------------------------------------------------------------------------------- 1 | #include "charybdis.dtsi" 2 | #include "charybdis_pmw3610.dtsi" 3 | 4 | // The matrix transform is 6 columns over because the left half is 6 columns wide. 5 | &default_transform { 6 | col-offset = <6>; 7 | }; 8 | 9 | &five_column_transform { 10 | col-offset = <5>; 11 | }; 12 | 13 | &kscan0 { 14 | compatible = "zmk,kscan-gpio-matrix"; 15 | 16 | col-gpios 17 | = <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 18 | , <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 19 | , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 20 | , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 21 | , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 22 | , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 23 | ; 24 | 25 | row-gpios 26 | = <&pro_micro 18 GPIO_ACTIVE_HIGH> 27 | , <&pro_micro 5 GPIO_ACTIVE_HIGH> 28 | , <&pro_micro 4 GPIO_ACTIVE_HIGH> 29 | , <&pro_micro 9 GPIO_ACTIVE_HIGH> 30 | ; 31 | }; 32 | 33 | &spi0 { 34 | status = "okay"; 35 | }; 36 | 37 | &trackball { 38 | status = "okay"; 39 | }; 40 | 41 | // https://github.com/badjeff/zmk-pmw3610-driver 42 | / { 43 | trackball_listener { 44 | compatible = "zmk,input-listener"; 45 | device = <&trackball>; 46 | }; 47 | }; -------------------------------------------------------------------------------- /config/charybdis.conf: -------------------------------------------------------------------------------- 1 | ## Bluetooth 2 | ## https://zmk.dev/docs/troubleshooting/connection-issues#unreliableweak-connection 3 | CONFIG_BT_CTLR_TX_PWR_PLUS_8=y 4 | ## https://zmk.dev/docs/config/system#bluetooth 5 | CONFIG_ZMK_BLE=y 6 | ## https://zmk.dev/docs/config/bluetooth#kconfig 7 | CONFIG_ZMK_BLE_EXPERIMENTAL_FEATURES=y 8 | 9 | ## Power Management Config 10 | ## https://zmk.dev/docs/config/power 11 | CONFIG_ZMK_SLEEP=y 12 | # CONFIG_ZMK_IDLE_TIMEOUT=30000 13 | # CONFIG_ZMK_IDLE_SLEEP_TIMEOUT=600000 14 | CONFIG_ZMK_EXT_POWER=y 15 | ## https://docs.zephyrproject.org/2.7.5/reference/kconfig/CONFIG_PM_DEVICE.html 16 | CONFIG_PM_DEVICE=y 17 | 18 | ## Battery Level 19 | ## https://zmk.dev/docs/config/battery 20 | CONFIG_ZMK_BATTERY_REPORTING=y 21 | 22 | ## Mouse Emulation 23 | ## https://zmk.dev/docs/keymaps/behaviors/mouse-emulation 24 | CONFIG_ZMK_MOUSE=y 25 | 26 | ## Include input subsystem and drivers in the system config 27 | ## https://github.com/badjeff/zmk-pmw3610-driver 28 | ## https://github.com/badjeff/zmk-split-peripheral-input-relay 29 | CONFIG_INPUT=y 30 | 31 | ## https://zmk.dev/docs/keymaps/behaviors/macros#behavior-queue-limit 32 | CONFIG_ZMK_BEHAVIORS_QUEUE_SIZE=512 33 | 34 | ## https://github.com/zmkfirmware/zmk/pull/2477 35 | # CONFIG_ZMK_MOUSE_SMOOTH_SCROLLING=y -------------------------------------------------------------------------------- /boards/shields/charybdis-dongle/charybdis_right.overlay: -------------------------------------------------------------------------------- 1 | #include "charybdis.dtsi" 2 | #include "charybdis_pmw3610.dtsi" 3 | 4 | // The matrix transform is 6 columns over because the left half is 6 columns wide. 5 | &default_transform { 6 | col-offset = <6>; 7 | }; 8 | 9 | &five_column_transform { 10 | col-offset = <5>; 11 | }; 12 | 13 | &kscan0 { 14 | compatible = "zmk,kscan-gpio-matrix"; 15 | 16 | col-gpios 17 | = <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 18 | , <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 19 | , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 20 | , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 21 | , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 22 | , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> 23 | ; 24 | 25 | row-gpios 26 | = <&pro_micro 18 GPIO_ACTIVE_HIGH> 27 | , <&pro_micro 5 GPIO_ACTIVE_HIGH> 28 | , <&pro_micro 4 GPIO_ACTIVE_HIGH> 29 | , <&pro_micro 9 GPIO_ACTIVE_HIGH> 30 | ; 31 | }; 32 | 33 | &spi0 { 34 | status = "okay"; 35 | }; 36 | 37 | &trackball { 38 | status = "okay"; 39 | }; 40 | 41 | / { 42 | vtrackball: virtual_input_trackball { 43 | compatible = "zmk,virtual-input"; 44 | }; 45 | 46 | input_relay_config_trackball { 47 | compatible = "zmk,split-peripheral-input-relay"; 48 | device = <&trackball>; 49 | relay-channel = <111>; 50 | }; 51 | }; -------------------------------------------------------------------------------- /config/combos.dtsi: -------------------------------------------------------------------------------- 1 | / { 2 | combos { 3 | compatible = "zmk,combos"; 4 | 5 | CapsWord { 6 | bindings = <&caps_word>; 7 | key-positions = <17 18>; 8 | layers = <0>; 9 | }; 10 | 11 | MiddleClick { 12 | bindings = <&mkp MCLK>; 13 | key-positions = <28 37>; 14 | }; 15 | 16 | RightClick { 17 | bindings = <&mkp RCLK>; 18 | key-positions = <16 37>; 19 | }; 20 | 21 | RightParen { 22 | bindings = <&kp RIGHT_PARENTHESIS>; 23 | key-positions = <20 21>; 24 | layers = <3>; 25 | }; 26 | 27 | RightBracket { 28 | bindings = <&kp RIGHT_BRACKET>; 29 | key-positions = <21 22>; 30 | layers = <3>; 31 | }; 32 | 33 | RightBrace { 34 | bindings = <&kp RIGHT_BRACE>; 35 | key-positions = <8 9>; 36 | layers = <3>; 37 | }; 38 | 39 | RightGreaterThan { 40 | bindings = <&kp GREATER_THAN>; 41 | key-positions = <32 33>; 42 | layers = <3>; 43 | }; 44 | 45 | BASE_or_EXTRAS { 46 | bindings = <&td_bore>; 47 | key-positions = <38 39>; 48 | }; 49 | 50 | // GameLayer { 51 | // bindings = <&tog 4>; 52 | // key-positions = <8 9>; 53 | // }; 54 | 55 | // PhotoLayer { 56 | // bindings = <&tog 5>; 57 | // key-positions = <2 3>; 58 | // }; 59 | }; 60 | }; -------------------------------------------------------------------------------- /boards/shields/charybdis-bt/charybdis.dtsi: -------------------------------------------------------------------------------- 1 | #include 2 | #include "charybdis-layouts.dtsi" 3 | 4 | / { 5 | // chosen { 6 | // zmk,kscan = &kscan0; 7 | // zmk,matrix_transform = &default_transform; 8 | // }; 9 | chosen { 10 | zmk,kscan = &kscan0; 11 | zmk,physical-layout = &charybdis_physical_layout; 12 | }; 13 | 14 | vbatt: vbatt { 15 | compatible = "zmk,battery-nrf-vddh"; 16 | }; 17 | 18 | default_transform: keymap_transform_0 { 19 | compatible = "zmk,matrix-transform"; 20 | columns = <12>; 21 | rows = <4>; 22 | 23 | map = < 24 | RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,11) RC(0,10) RC(0,9) RC(0,8) RC(0,7) RC(0,6) 25 | RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,11) RC(1,10) RC(1,9) RC(1,8) RC(1,7) RC(1,6) 26 | RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,11) RC(2,10) RC(2,9) RC(2,8) RC(2,7) RC(2,6) 27 | RC(3,3) RC(3,4) RC(3,1) RC(3,7) RC(3,9) 28 | >; 29 | }; 30 | 31 | five_column_transform: keymap_transform_1 { 32 | compatible = "zmk,matrix-transform"; 33 | columns = <10>; 34 | rows = <4>; 35 | 36 | map = < 37 | RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,10) RC(0,9) RC(0,8) RC(0,7) RC(0,6) 38 | RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,10) RC(1,9) RC(1,8) RC(1,7) RC(1,6) 39 | RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,10) RC(2,9) RC(2,8) RC(2,7) RC(2,6) 40 | RC(3,3) RC(3,4) RC(3,5) RC(3,10) RC(3,9) 41 | >; 42 | }; 43 | 44 | kscan0: kscan { 45 | wakeup-source; 46 | diode-direction = "col2row"; 47 | }; 48 | }; 49 | -------------------------------------------------------------------------------- /boards/shields/charybdis-dongle/charybdis.dtsi: -------------------------------------------------------------------------------- 1 | #include 2 | #include "charybdis-layouts.dtsi" 3 | 4 | / { 5 | chosen { 6 | zmk,kscan = &kscan0; 7 | zmk,matrix_transform = &default_transform; 8 | }; 9 | 10 | // ZMK Studio 11 | // chosen { 12 | // zmk,kscan = &kscan0; 13 | // zmk,physical-layout = &charybdis_physical_layout; 14 | // }; 15 | 16 | vbatt: vbatt { 17 | compatible = "zmk,battery-nrf-vddh"; 18 | }; 19 | 20 | default_transform: keymap_transform_0 { 21 | compatible = "zmk,matrix-transform"; 22 | columns = <12>; 23 | rows = <4>; 24 | 25 | map = < 26 | RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,11) RC(0,10) RC(0,9) RC(0,8) RC(0,7) RC(0,6) 27 | RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,11) RC(1,10) RC(1,9) RC(1,8) RC(1,7) RC(1,6) 28 | RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,11) RC(2,10) RC(2,9) RC(2,8) RC(2,7) RC(2,6) 29 | RC(3,3) RC(3,4) RC(3,1) RC(3,7) RC(3,9) 30 | >; 31 | }; 32 | 33 | five_column_transform: keymap_transform_1 { 34 | compatible = "zmk,matrix-transform"; 35 | columns = <10>; 36 | rows = <4>; 37 | 38 | map = < 39 | RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,10) RC(0,9) RC(0,8) RC(0,7) RC(0,6) 40 | RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,10) RC(1,9) RC(1,8) RC(1,7) RC(1,6) 41 | RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,10) RC(2,9) RC(2,8) RC(2,7) RC(2,6) 42 | RC(3,3) RC(3,4) RC(3,1) RC(3,6) RC(3,8) 43 | >; 44 | }; 45 | 46 | kscan0: kscan { 47 | wakeup-source; 48 | diode-direction = "row2col"; 49 | }; 50 | }; -------------------------------------------------------------------------------- /boards/shields/charybdis-bt/charybdis_pmw3610.dtsi: -------------------------------------------------------------------------------- 1 | #define INPUT_EV_KEY 0x01 /**< Key event */ 2 | #define INPUT_EV_REL 0x02 /**< Relative coordinate event */ 3 | #define INPUT_EV_ABS 0x03 /**< Absolute coordinate event */ 4 | #define INPUT_EV_MSC 0x04 /**< Miscellaneous event */ 5 | #define INPUT_EV_VENDOR_START 0xf0 /**< Vendor specific event start */ 6 | #define INPUT_EV_VENDOR_STOP 0xff /**< Vendor specific event stop */ 7 | 8 | #define INPUT_REL_X 0x00 /**< Relative X coordinate */ 9 | #define INPUT_REL_Y 0x01 /**< Relative Y coordinate */ 10 | #define INPUT_REL_WHEEL 0x08 /**< Relative wheel coordinate */ 11 | #define INPUT_REL_HWHEEL 0x06 /**< Relative horizontal wheel coordinate */ 12 | #define INPUT_REL_MISC 0x09 /**< Relative misc coordinate */ 13 | 14 | &pinctrl { 15 | spi0_default: spi0_default { 16 | group1 { 17 | psels = , 18 | , 19 | ; 20 | }; 21 | }; 22 | 23 | spi0_sleep: spi0_sleep { 24 | group1 { 25 | psels = , 26 | , 27 | ; 28 | low-power-enable; 29 | }; 30 | }; 31 | }; 32 | 33 | &spi0 { 34 | status = "disabled"; 35 | compatible = "nordic,nrf-spim"; 36 | pinctrl-0 = <&spi0_default>; 37 | pinctrl-1 = <&spi0_sleep>; 38 | pinctrl-names = "default", "sleep"; 39 | cs-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; 40 | 41 | trackball: trackball@0 { 42 | status = "disabled"; 43 | compatible = "pixart,pmw3610"; 44 | reg = <0>; 45 | spi-max-frequency = <2000000>; 46 | irq-gpios = <&gpio0 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; 47 | cpi = <1000>; 48 | evt-type = ; 49 | x-input-code = ; 50 | y-input-code = ; 51 | }; 52 | }; -------------------------------------------------------------------------------- /boards/shields/charybdis-dongle/charybdis_pmw3610.dtsi: -------------------------------------------------------------------------------- 1 | #define INPUT_EV_KEY 0x01 /**< Key event */ 2 | #define INPUT_EV_REL 0x02 /**< Relative coordinate event */ 3 | #define INPUT_EV_ABS 0x03 /**< Absolute coordinate event */ 4 | #define INPUT_EV_MSC 0x04 /**< Miscellaneous event */ 5 | #define INPUT_EV_VENDOR_START 0xf0 /**< Vendor specific event start */ 6 | #define INPUT_EV_VENDOR_STOP 0xff /**< Vendor specific event stop */ 7 | 8 | #define INPUT_REL_X 0x00 /**< Relative X coordinate */ 9 | #define INPUT_REL_Y 0x01 /**< Relative Y coordinate */ 10 | #define INPUT_REL_WHEEL 0x08 /**< Relative wheel coordinate */ 11 | #define INPUT_REL_HWHEEL 0x06 /**< Relative horizontal wheel coordinate */ 12 | #define INPUT_REL_MISC 0x09 /**< Relative misc coordinate */ 13 | 14 | &pinctrl { 15 | spi0_default: spi0_default { 16 | group1 { 17 | psels = , 18 | , 19 | ; 20 | }; 21 | }; 22 | 23 | spi0_sleep: spi0_sleep { 24 | group1 { 25 | psels = , 26 | , 27 | ; 28 | low-power-enable; 29 | }; 30 | }; 31 | }; 32 | 33 | &spi0 { 34 | status = "disabled"; 35 | compatible = "nordic,nrf-spim"; 36 | pinctrl-0 = <&spi0_default>; 37 | pinctrl-1 = <&spi0_sleep>; 38 | pinctrl-names = "default", "sleep"; 39 | cs-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; 40 | 41 | trackball: trackball@0 { 42 | status = "disabled"; 43 | compatible = "pixart,pmw3610"; 44 | reg = <0>; 45 | spi-max-frequency = <2000000>; 46 | irq-gpios = <&gpio0 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; 47 | cpi = <1000>; 48 | evt-type = ; 49 | x-input-code = ; 50 | y-input-code = ; 51 | }; 52 | }; -------------------------------------------------------------------------------- /config/charybdis-layouts.dtsi: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | / { 4 | charybdis_physical_layout: charybdis_physical_layout { 5 | compatible = "zmk,physical-layout"; 6 | display-name = "Charybdis 3x6"; 7 | 8 | kscan = <&kscan0>; 9 | transform = <&default_transform>; 10 | 11 | keys 12 | // Left Half - Main Keys 13 | // w h x y rot rx ry 14 | = <&key_physical_attrs 100 100 0 38 0 0 0> 15 | , <&key_physical_attrs 100 100 100 38 0 0 0> 16 | , <&key_physical_attrs 100 100 200 12 0 0 0> 17 | , <&key_physical_attrs 100 100 300 0 0 0 0> 18 | , <&key_physical_attrs 100 100 400 12 0 0 0> 19 | , <&key_physical_attrs 100 100 500 12 0 0 0> 20 | , <&key_physical_attrs 100 100 900 12 0 0 0> 21 | , <&key_physical_attrs 100 100 1000 12 0 0 0> 22 | , <&key_physical_attrs 100 100 1100 0 0 0 0> 23 | , <&key_physical_attrs 100 100 1200 12 0 0 0> 24 | , <&key_physical_attrs 100 100 1300 38 0 0 0> 25 | , <&key_physical_attrs 100 100 1400 38 0 0 0> 26 | , <&key_physical_attrs 100 100 0 138 0 0 0> 27 | , <&key_physical_attrs 100 100 100 138 0 0 0> 28 | , <&key_physical_attrs 100 100 200 112 0 0 0> 29 | , <&key_physical_attrs 100 100 300 100 0 0 0> 30 | , <&key_physical_attrs 100 100 400 112 0 0 0> 31 | , <&key_physical_attrs 100 100 500 112 0 0 0> 32 | 33 | // Right Half - Main Keys 34 | // w h x y rot rx ry 35 | , <&key_physical_attrs 100 100 900 112 0 0 0> 36 | , <&key_physical_attrs 100 100 1000 112 0 0 0> 37 | , <&key_physical_attrs 100 100 1100 100 0 0 0> 38 | , <&key_physical_attrs 100 100 1200 112 0 0 0> 39 | , <&key_physical_attrs 100 100 1300 138 0 0 0> 40 | , <&key_physical_attrs 100 100 1400 138 0 0 0> 41 | , <&key_physical_attrs 100 100 0 238 0 0 0> 42 | , <&key_physical_attrs 100 100 100 238 0 0 0> 43 | , <&key_physical_attrs 100 100 200 212 0 0 0> 44 | , <&key_physical_attrs 100 100 300 200 0 0 0> 45 | , <&key_physical_attrs 100 100 400 212 0 0 0> 46 | , <&key_physical_attrs 100 100 500 212 0 0 0> 47 | , <&key_physical_attrs 100 100 900 212 0 0 0> 48 | , <&key_physical_attrs 100 100 1000 212 0 0 0> 49 | , <&key_physical_attrs 100 100 1100 200 0 0 0> 50 | , <&key_physical_attrs 100 100 1200 212 0 0 0> 51 | , <&key_physical_attrs 100 100 1300 238 0 0 0> 52 | , <&key_physical_attrs 100 100 1400 238 0 0 0> 53 | 54 | // Thumb Clusters 55 | // w h x y rot rx ry 56 | , <&key_physical_attrs 100 100 348 314 0 0 0> 57 | , <&key_physical_attrs 100 100 348 314 1500 398 790> 58 | , <&key_physical_attrs 100 100 348 320 3000 398 790> 59 | , <&key_physical_attrs 100 100 1052 320 (-3000) 1102 790> 60 | , <&key_physical_attrs 100 100 1052 314 (-1500) 1102 790> 61 | ; 62 | }; 63 | }; 64 | -------------------------------------------------------------------------------- /config/behaviors.dtsi: -------------------------------------------------------------------------------- 1 | / { 2 | behaviors { 3 | hm: homerow_mod { 4 | compatible = "zmk,behavior-hold-tap"; 5 | #binding-cells = <2>; 6 | flavor = "tap-preferred"; 7 | tapping-term-ms = <175>; // triggers the hold behavior when the tapping-term-ms has expired 8 | quick-tap-ms = <150>; // If you press a tapped hold-tap again within quick-tap-ms milliseconds of the first press, it will always trigger the tap behavior. 9 | require-prior-idle-ms = <185>; // like quick-tap-ms however it will apply for any non-modifier key pressed before it 10 | bindings = <&kp>, <&kp>; // hold, tap 11 | }; 12 | 13 | hm_right: homerow_mod_right { 14 | compatible = "zmk,behavior-hold-tap"; 15 | #binding-cells = <2>; 16 | flavor = "tap-preferred"; 17 | tapping-term-ms = <130>; 18 | // quick-tap-ms = <200>; 19 | // require-prior-idle-ms = <150>; 20 | bindings = <&kp>, <&kp>; 21 | hold-trigger-key-positions = <0 1 2 3 4 5 12 13 14 15 16 17 24 25 26 27 28 29 36 37 38>; 22 | }; 23 | 24 | hm_left: homerow_mod_left { 25 | compatible = "zmk,behavior-hold-tap"; 26 | #binding-cells = <2>; 27 | flavor = "tap-preferred"; 28 | tapping-term-ms = <130>; 29 | // quick-tap-ms = <200>; 30 | // require-prior-idle-ms = <150>; 31 | bindings = <&kp>, <&kp>; 32 | hold-trigger-key-positions = <6 7 8 9 10 11 18 19 20 21 22 23 30 31 32 33 34 35 39 40>; 33 | }; 34 | 35 | td_cursor: td_scroll_or_cursor { 36 | compatible = "zmk,behavior-tap-dance"; 37 | #binding-cells = <0>; 38 | bindings = << 9 ESC>, <&mo 8>; // hold, tap, double_tap 39 | }; 40 | 41 | td_space: td_space { 42 | compatible = "zmk,behavior-tap-dance"; 43 | #binding-cells = <0>; 44 | tapping-term-ms = <85>; 45 | bindings = << 2 SPACE>, <&kp SPACE>; // hold, tap, double_tap 46 | }; 47 | 48 | td_bs: td_bs { 49 | compatible = "zmk,behavior-tap-dance"; 50 | #binding-cells = <0>; 51 | tapping-term-ms = <85>; 52 | bindings = << 1 BACKSPACE>, <&kp BACKSPACE>; // hold, tap, double_tap 53 | }; 54 | 55 | httl: ht_two_layers { 56 | compatible = "zmk,behavior-hold-tap"; 57 | bindings = <&mo>, <&to>; 58 | #binding-cells = <2>; 59 | flavor = "balanced"; 60 | tapping-term-ms = <125>; 61 | }; 62 | 63 | td_layers: td_layer_swap { 64 | compatible = "zmk,behavior-tap-dance"; 65 | #binding-cells = <0>; 66 | bindings = <&to 0>, <&to 5>, <&to 4>; // tap, double_tap, tripple_tap 67 | }; 68 | 69 | td_clk_scrl: td_click_or_scroll { 70 | compatible = "zmk,behavior-tap-dance"; 71 | #binding-cells = <0>; 72 | tapping-term-ms = <250>; 73 | bindings = <&mkp RCLK>, <&mo 9>; // tap/hold, double_tap 74 | }; 75 | 76 | td_bore: BASE_or_EXTRA { 77 | compatible = "zmk,behavior-tap-dance"; 78 | #binding-cells = <0>; 79 | bindings = <&mo 6>, <&to 0>; 80 | }; 81 | 82 | td_grave: DoubleTap_Grave { 83 | compatible = "zmk,behavior-tap-dance"; 84 | #binding-cells = <0>; 85 | bindings = <&kp GRAVE>, <&grave_macro>; 86 | }; 87 | 88 | td_hyphen: DoubleTap_Hyphen { 89 | compatible = "zmk,behavior-tap-dance"; 90 | #binding-cells = <0>; 91 | bindings = <&kp MINUS>, <&minus_macro>; 92 | }; 93 | 94 | td_equals: DoubleTap_Equals { 95 | compatible = "zmk,behavior-tap-dance"; 96 | #binding-cells = <0>; 97 | bindings = <&kp EQUAL>, <&equals_macro>; 98 | }; 99 | 100 | td_home: DoubleTap_Home { 101 | compatible = "zmk,behavior-tap-dance"; 102 | #binding-cells = <0>; 103 | bindings = <&kp FSLH>, <&home_macro>; 104 | }; 105 | }; 106 | }; -------------------------------------------------------------------------------- /config/charybdis.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "charybdis", 3 | "name": "Charybdis", 4 | "layouts": { 5 | "default_transform": { 6 | "name": "default_transform", 7 | "layout": [ 8 | { "row": 0, "col": 0, "x": 0, "y": 0.360 }, 9 | { "row": 0, "col": 1, "x": 1, "y": 0.360 }, 10 | { "row": 0, "col": 2, "x": 2, "y": 0.110 }, 11 | { "row": 0, "col": 3, "x": 3, "y": -0.015 }, 12 | { "row": 0, "col": 4, "x": 4, "y": 0.110 }, 13 | { "row": 0, "col": 5, "x": 5, "y": 0.110 }, 14 | { "row": 0, "col": 7, "x": 9, "y": 0.110 }, 15 | { "row": 0, "col": 8, "x": 10, "y": 0.110 }, 16 | { "row": 0, "col": 9, "x": 11, "y": -0.015 }, 17 | { "row": 0, "col": 10, "x": 12, "y": 0.110 }, 18 | { "row": 0, "col": 11, "x": 13, "y": 0.360 }, 19 | { "row": 0, "col": 12, "x": 14, "y": 0.360 }, 20 | 21 | { "row": 1, "col": 0, "x": 0, "y": 1.360 }, 22 | { "row": 1, "col": 1, "x": 1, "y": 1.360 }, 23 | { "row": 1, "col": 2, "x": 2, "y": 1.110 }, 24 | { "row": 1, "col": 3, "x": 3, "y": 0.985 }, 25 | { "row": 1, "col": 4, "x": 4, "y": 1.110 }, 26 | { "row": 1, "col": 5, "x": 5, "y": 1.110 }, 27 | { "row": 1, "col": 7, "x": 9, "y": 1.110 }, 28 | { "row": 1, "col": 8, "x": 10, "y": 1.110 }, 29 | { "row": 1, "col": 9, "x": 11, "y": 0.985 }, 30 | { "row": 1, "col": 10, "x": 12, "y": 1.110 }, 31 | { "row": 1, "col": 11, "x": 13, "y": 1.360 }, 32 | { "row": 1, "col": 12, "x": 14, "y": 1.360 }, 33 | 34 | { "row": 2, "col": 0, "x": 0, "y": 2.360 }, 35 | { "row": 2, "col": 1, "x": 1, "y": 2.360 }, 36 | { "row": 2, "col": 2, "x": 2, "y": 2.110 }, 37 | { "row": 2, "col": 3, "x": 3, "y": 1.985 }, 38 | { "row": 2, "col": 4, "x": 4, "y": 2.110 }, 39 | { "row": 2, "col": 5, "x": 5, "y": 2.110 }, 40 | { "row": 2, "col": 7, "x": 9, "y": 2.110 }, 41 | { "row": 2, "col": 8, "x": 10, "y": 2.110 }, 42 | { "row": 2, "col": 9, "x": 11, "y": 1.985 }, 43 | { "row": 2, "col": 10, "x": 12, "y": 2.110 }, 44 | { "row": 2, "col": 11, "x": 13, "y": 2.360 }, 45 | { "row": 2, "col": 12, "x": 14, "y": 2.360 }, 46 | 47 | { "row": 3, "col": 3, "x": 3.48, "y": 3.13, "r": 0 }, 48 | { "row": 3, "col": 4, "x": 3.48, "y": 3.13, "r": 15, "rx": 3.98, "ry": 7.88 }, 49 | { "row": 3, "col": 5, "x": 3.48, "y": 3.18, "r": 30, "rx": 3.98, "ry": 7.88 }, 50 | { "row": 3, "col": 7, "x": 10.52, "y": 3.18, "r": -30, "rx": 11.02, "ry": 7.88 }, 51 | { "row": 3, "col": 8, "x": 10.52, "y": 3.13, "r": -15, "rx": 11.02, "ry": 7.88 } 52 | ] 53 | }, 54 | "five_column_transform": { 55 | "name": "five_column_transform", 56 | "layout": [ 57 | { "row": 0, "col": 1, "x": 0, "y": 0.575 }, 58 | { "row": 0, "col": 2, "x": 1, "y": 0.125 }, 59 | { "row": 0, "col": 3, "x": 2, "y": 0 }, 60 | { "row": 0, "col": 4, "x": 3, "y": 0.125 }, 61 | { "row": 0, "col": 5, "x": 4, "y": 0.125 }, 62 | { "row": 0, "col": 7, "x": 8, "y": 0.125 }, 63 | { "row": 0, "col": 8, "x": 9, "y": 0.125 }, 64 | { "row": 0, "col": 9, "x": 10, "y": 0 }, 65 | { "row": 0, "col": 10, "x": 11, "y": 0.125 }, 66 | { "row": 0, "col": 11, "x": 12, "y": 0.575 }, 67 | 68 | { "row": 1, "col": 1, "x": 0, "y": 1.575 }, 69 | { "row": 1, "col": 2, "x": 1, "y": 1.125 }, 70 | { "row": 1, "col": 3, "x": 2, "y": 1 }, 71 | { "row": 1, "col": 4, "x": 3, "y": 1.125 }, 72 | { "row": 1, "col": 5, "x": 4, "y": 1.125 }, 73 | { "row": 1, "col": 7, "x": 8, "y": 1.125 }, 74 | { "row": 1, "col": 8, "x": 9, "y": 1.125 }, 75 | { "row": 1, "col": 9, "x": 10, "y": 1 }, 76 | { "row": 1, "col": 10, "x": 11, "y": 1.125 }, 77 | { "row": 1, "col": 11, "x": 12, "y": 1.575 }, 78 | 79 | { "row": 2, "col": 1, "x": 0, "y": 2.575 }, 80 | { "row": 2, "col": 2, "x": 1, "y": 2.125 }, 81 | { "row": 2, "col": 3, "x": 2, "y": 2 }, 82 | { "row": 2, "col": 4, "x": 3, "y": 2.125 }, 83 | { "row": 2, "col": 5, "x": 4, "y": 2.125 }, 84 | { "row": 2, "col": 7, "x": 8, "y": 2.125 }, 85 | { "row": 2, "col": 8, "x": 9, "y": 2.125 }, 86 | { "row": 2, "col": 9, "x": 10, "y": 2 }, 87 | { "row": 2, "col": 10, "x": 11, "y": 2.125 }, 88 | { "row": 2, "col": 11, "x": 12, "y": 2.575 }, 89 | 90 | { "row": 3, "col": 3, "x": 2.48, "y": 3.125, "r": 0 }, 91 | { "row": 3, "col": 4, "x": 2.48, "y": 3.125, "r": 15, "rx": 2.98, "ry": 7.895 }, 92 | { "row": 3, "col": 5, "x": 2.48, "y": 3.125, "r": 30, "rx": 2.98, "ry": 7.895, "h": 1 }, 93 | { "row": 3, "col": 7, "x": 9.52, "y": 3.125, "r": -30, "rx": 10.02, "ry": 7.895, "h": 1 }, 94 | { "row": 3, "col": 8, "x": 9.52, "y": 3.125, "r": -15, "rx": 10.02, "ry": 7.895 } 95 | ] 96 | } 97 | }, 98 | "sensors": [] 99 | } -------------------------------------------------------------------------------- /keymap-drawer/config.yaml: -------------------------------------------------------------------------------- 1 | draw_config: 2 | key_w: 60 3 | key_h: 56 4 | split_gap: 30.0 5 | combo_w: 28 6 | combo_h: 26 7 | key_rx: 6.0 8 | key_ry: 6.0 9 | dark_mode: auto 10 | n_columns: 1 11 | separate_combo_diagrams: true 12 | combo_diagrams_scale: 2 13 | inner_pad_w: 2.0 14 | inner_pad_h: 2.0 15 | outer_pad_w: 30.0 16 | outer_pad_h: 56.0 17 | line_spacing: 1.2 18 | arc_radius: 6.0 19 | append_colon_to_layer_header: true 20 | small_pad: 2.0 21 | legend_rel_x: 0.0 22 | legend_rel_y: 0.0 23 | draw_key_sides: true 24 | key_side_pars: 25 | rel_x: 0.0 26 | rel_y: 4.0 27 | rel_w: 12.0 28 | rel_h: 12.0 29 | rx: 4.0 30 | ry: 4.0 31 | # svg_extra_style: " 32 | # @media (prefers-color-scheme: dark) { 33 | # svg.keymap { fill: #dca448; } 34 | # rect.key { fill: #1e1e1e; } 35 | # rect.key, rect.combo { stroke: #122532; } 36 | # rect.combo, rect.combo-separate { fill: #2e4857; } 37 | # rect.held, rect.combo.held { fill: #5b3131; } 38 | # text.label, text.footer { stroke: #331d36; 39 | # stroke-width: 0; } 40 | # text.trans { fill: #7e8184; } 41 | # path.combo { stroke: #7f7f7f; }" 42 | footer_text: "" 43 | shrink_wide_legends: 7 44 | style_layer_activators: true 45 | glyph_tap_size: 14 46 | glyph_hold_size: 12 47 | glyph_shifted_size: 10 48 | glyphs: {} 49 | parse_config: 50 | preprocess: true 51 | skip_binding_parsing: false 52 | raw_binding_map: {} 53 | sticky_label: sticky 54 | toggle_label: toggle 55 | tap_toggle_label: tap-toggle 56 | trans_legend: 57 | t: ▽ 58 | type: trans 59 | mark_alternate_layer_activators: false 60 | modifier_fn_map: 61 | left_ctrl: Ctl 62 | right_ctrl: Ctl 63 | left_shift: Sft 64 | right_shift: Sft 65 | left_alt: Alt 66 | right_alt: AGr 67 | left_gui: Gui 68 | right_gui: Gui 69 | keycode_combiner: "{mods}+ {key}" 70 | mod_combiner: "{mod_1}+{mod_2}" 71 | special_combinations: 72 | left_ctrl+left_alt+left_gui+left_shift: Hyper 73 | left_ctrl+left_alt+left_shift: Meh 74 | qmk_remove_keycode_prefix: 75 | - KC_ 76 | qmk_keycode_map: 77 | XXXXXXX: "" 78 | "NO": "" 79 | MINUS: "-" 80 | MINS: "-" 81 | EQUAL: "=" 82 | EQL: "=" 83 | LEFT_BRACKET: "[" 84 | LBRC: "[" 85 | RIGHT_BRACKET: "]" 86 | RBRC: "]" 87 | BACKSLASH: \ 88 | BSLS: \ 89 | NONUS_HASH: "#" 90 | NUHS: "#" 91 | SEMICOLON: ; 92 | SCLN: ; 93 | QUOTE: "'" 94 | QUOT: "'" 95 | GRAVE: "`" 96 | GRV: "`" 97 | COMMA: "," 98 | COMM: "," 99 | DOT: . 100 | SLASH: / 101 | SLSH: / 102 | TILDE: "~" 103 | TILD: "~" 104 | EXCLAIM: "!" 105 | EXLM: "!" 106 | AT: "@" 107 | HASH: "#" 108 | DOLLAR: $ 109 | DLR: $ 110 | PERCENT: "%" 111 | PERC: "%" 112 | CIRCUMFLEX: ^ 113 | CIRC: ^ 114 | AMPERSAND: "&" 115 | AMPR: "&" 116 | ASTERISK: "*" 117 | ASTR: "*" 118 | LEFT_PAREN: ( 119 | LPRN: ( 120 | RIGHT_PAREN: ) 121 | RPRN: ) 122 | UNDERSCORE: _ 123 | UNDS: _ 124 | PLUS: + 125 | LEFT_CURLY_BRACE: "{" 126 | LCBR: "{" 127 | RIGHT_CURLY_BRACE: "}" 128 | RCBR: "}" 129 | PIPE: "|" 130 | COLON: ":" 131 | COLN: ":" 132 | DOUBLE_QUOTE: '"' 133 | DQUO: '"' 134 | DQT: '"' 135 | LEFT_ANGLE_BRACKET: < 136 | LABK: < 137 | LT: < 138 | RIGHT_ANGLE_BRACKET: ">" 139 | RABK: ">" 140 | GT: ">" 141 | QUESTION: "?" 142 | QUES: "?" 143 | zmk_remove_keycode_prefix: [] 144 | zmk_keycode_map: 145 | EXCLAMATION: "!" 146 | EXCL: "!" 147 | AT_SIGN: "@" 148 | AT: "@" 149 | HASH: "#" 150 | POUND: "#" 151 | DOLLAR: $ 152 | DLLR: $ 153 | PERCENT: "%" 154 | PRCNT: "%" 155 | CARET: ^ 156 | AMPERSAND: "&" 157 | AMPS: "&" 158 | ASTERISK: "*" 159 | ASTRK: "*" 160 | STAR: "*" 161 | LEFT_PARENTHESIS: ( 162 | LPAR: ( 163 | RIGHT_PARENTHESIS: ) 164 | RPAR: ) 165 | EQUAL: "=" 166 | PLUS: + 167 | MINUS: "-" 168 | UNDERSCORE: _ 169 | UNDER: _ 170 | SLASH: / 171 | FSLH: / 172 | QUESTION: "?" 173 | QMARK: "?" 174 | BACKSLASH: \ 175 | BSLH: \ 176 | PIPE: "|" 177 | NON_US_BACKSLASH: \ 178 | PIPE2: "|" 179 | NON_US_BSLH: "|" 180 | SEMICOLON: ; 181 | SEMI: ; 182 | COLON: ":" 183 | SINGLE_QUOTE: "'" 184 | SQT: "'" 185 | APOSTROPHE: "'" 186 | APOS: "'" 187 | DOUBLE_QUOTES: '"' 188 | DQT: '"' 189 | COMMA: "," 190 | LESS_THAN: < 191 | LT: < 192 | PERIOD: . 193 | DOT: . 194 | GREATER_THAN: ">" 195 | GT: ">" 196 | LEFT_BRACKET: "[" 197 | LBKT: "[" 198 | LEFT_BRACE: "{" 199 | LBRC: "{" 200 | RIGHT_BRACKET: "]" 201 | RBKT: "]" 202 | RIGHT_BRACE: "}" 203 | RBRC: "}" 204 | GRAVE: "`" 205 | TILDE: "~" 206 | NON_US_HASH: "#" 207 | NUHS: "#" 208 | TILDE2: "~" 209 | LA(LC(T)): "SHELL" 210 | C_AC_SEARCH: "SEARCH" 211 | LG(E): "WINDOW" 212 | LC(LA(LEFT_SHIFT)): "MEH" 213 | LA(LC(LG(LEFT_SHIFT))): "HYPER" 214 | LC(F10): "ALL WIN" 215 | LS(LC(H)): "PANE ◀" 216 | LS(LC(J)): "PANE ▲" 217 | LS(LC(K)): "PANE ▼" 218 | LS(LC(L)): "PANE ▶" 219 | LEFT_ARROW: "LEFT" 220 | RIGHT_ARROW: "RIGHT" 221 | C_AC_UNDO: "UNDO" 222 | C_AC_CUT: "CUT" 223 | C_AC_COPY: "COPY" 224 | C_AC_PASTE: "PASTE" 225 | C_AL_COFFEE: "LOCK" 226 | PRINTSCREEN: "PRINT SCREEN" 227 | BACKSPACE: "BACK SPACE" 228 | LC(LA(E)): "FIT" 229 | LS(DELETE): "SHRED" 230 | LA(LC(LS(Z))): "TAG Z" 231 | LA(LC(LS(E))): "TAG E" 232 | LA(LC(LS(A))): "TAG A" 233 | LA(LC(LS(S))): "TAG S" 234 | LA(LC(LS(B))): "TAG B" 235 | LA(LS(A)): "SHOW TAGS" 236 | LA(KP_N1): "REJECT" 237 | LA(KP_N2): "PENDING" 238 | LA(KP_N3): "ACCEPT" 239 | F12: "DEV TOOLS" 240 | BRIGHTNESS INC: "BRIGHT ▲" 241 | BRIGHTNESS DEC: "BRIGHT ▼" 242 | zmk_combos: {} 243 | zmk_preamble: "#define KEYMAP_DRAWER" 244 | zmk_additional_includes: [] 245 | -------------------------------------------------------------------------------- /config/macros.dtsi: -------------------------------------------------------------------------------- 1 | / { 2 | macros { 3 | v_split: v_split { 4 | compatible = "zmk,behavior-macro"; 5 | #binding-cells = <0>; 6 | bindings = <¯o_tap &kp LC(B) &kp PRCNT>; 7 | }; 8 | 9 | h_split: h_split { 10 | compatible = "zmk,behavior-macro"; 11 | #binding-cells = <0>; 12 | bindings = <¯o_tap &kp LC(B) &kp DQT>; 13 | }; 14 | 15 | detach_session: detach_session { 16 | compatible = "zmk,behavior-macro"; 17 | #binding-cells = <0>; 18 | bindings = <&kp LC(B) &kp D>; 19 | }; 20 | 21 | sync_panes: sync_panes { 22 | compatible = "zmk,behavior-macro"; 23 | #binding-cells = <0>; 24 | bindings = <&kp LC(B) &kp SPACE>; 25 | }; 26 | 27 | next_pane: next_pane { 28 | compatible = "zmk,behavior-macro"; 29 | #binding-cells = <0>; 30 | bindings = <&kp LC(B) &kp O>; 31 | }; 32 | 33 | show_sessions: show_sessions { 34 | compatible = "zmk,behavior-macro"; 35 | #binding-cells = <0>; 36 | bindings = <&kp LC(B) &kp S>; 37 | }; 38 | 39 | create_or_attach: create_or_attach { 40 | compatible = "zmk,behavior-macro"; 41 | #binding-cells = <0>; 42 | bindings 43 | = <¯o_tap &kp T> 44 | , <¯o_tap &kp M> 45 | , <¯o_tap &kp U> 46 | , <¯o_tap &kp X> 47 | , <¯o_tap &kp SPC> 48 | , <¯o_tap &kp N> 49 | , <¯o_tap &kp E> 50 | , <¯o_tap &kp W> 51 | , <¯o_tap &kp MINUS> 52 | , <¯o_tap &kp S> 53 | , <¯o_tap &kp E> 54 | , <¯o_tap &kp S> 55 | , <¯o_tap &kp S> 56 | , <¯o_tap &kp I> 57 | , <¯o_tap &kp O> 58 | , <¯o_tap &kp N> 59 | , <¯o_tap &kp SPC> 60 | , <¯o_tap &kp MINUS> 61 | , <¯o_tap &kp A> 62 | , <¯o_tap &kp SPC> 63 | , <¯o_tap &kp MINUS> 64 | , <¯o_tap &kp S> 65 | , <¯o_tap &kp SPC> 66 | ; 67 | }; 68 | 69 | shrug: shrug { // shrug emoji ¯\_(ツ)_/¯ 70 | compatible = "zmk,behavior-macro"; 71 | #binding-cells = <0>; 72 | wait-ms = <10>; 73 | tap-ms = <20>; 74 | bindings 75 | // ¯ (Macros) 76 | = <¯o_press &kp LCTRL &kp LSHFT> 77 | , <¯o_tap &kp U> 78 | , <¯o_release &kp LCTRL &kp LSHFT> 79 | , <¯o_tap &kp N0 &kp N0 &kp A &kp F &kp SPC> 80 | // \ 81 | , <¯o_tap &kp BACKSLASH> 82 | , <¯o_tap &kp BACKSLASH> 83 | // _ 84 | , <¯o_tap &kp UNDER> 85 | // ( 86 | , <¯o_tap &kp LPAR> 87 | // ツ (Katakana Letter Tu) 88 | , <¯o_press &kp LCTRL &kp LSHFT> 89 | , <¯o_tap &kp U> 90 | , <¯o_release &kp LCTRL &kp LSHFT> 91 | , <¯o_tap &kp N3 &kp N0 &kp C &kp N4 &kp SPC> 92 | // ) 93 | , <¯o_tap &kp RPAR> 94 | // _ 95 | , <¯o_tap &kp UNDER> 96 | // / 97 | , <¯o_tap &kp FSLH> 98 | // ¯ (Macron) 99 | , <¯o_press &kp LCTRL &kp LSHFT> 100 | , <¯o_tap &kp U> 101 | , <¯o_release &kp LCTRL &kp LSHFT> 102 | , <¯o_tap &kp N0 &kp N0 &kp A &kp F &kp SPC> 103 | ; 104 | }; 105 | 106 | gcm: gcm { // git commit WIP 107 | compatible = "zmk,behavior-macro"; 108 | #binding-cells = <0>; 109 | wait-ms = <10>; 110 | tap-ms = <20>; 111 | bindings = < 112 | ¯o_tap &kp G &kp I &kp T &kp SPACE 113 | &kp C &kp O &kp M &kp M &kp I &kp T &kp SPACE 114 | &kp MINUS &kp A &kp M &kp SPACE 115 | &kp DQT &kp LS(W) &kp LS(I) &kp LS(P) &kp DQT 116 | >; 117 | }; 118 | 119 | lgtm: lgtm { // LGTM Approval 120 | compatible = "zmk,behavior-macro"; 121 | #binding-cells = <0>; 122 | wait-ms = <10>; 123 | tap-ms = <20>; 124 | bindings = < 125 | ¯o_tap 126 | &kp LS(L) &kp LS(G) &kp LS(T) &kp LS(M) 127 | &kp SPACE &kp MINUS &kp SPACE 128 | &kp LS(A) &kp P &kp P &kp R &kp O &kp V &kp E &kp D &kp EXCL 129 | >; 130 | }; 131 | 132 | sudo: sudo { // SUDO_BANG_BANG 133 | compatible = "zmk,behavior-macro"; 134 | #binding-cells = <0>; 135 | wait-ms = <10>; 136 | tap-ms = <20>; 137 | bindings 138 | = <¯o_tap &kp S &kp U &kp D &kp O> // "sudo" 139 | , <¯o_tap &kp SPACE> // space 140 | , <¯o_tap &kp EXCL &kp EXCL> // "!!" 141 | , <¯o_tap &kp RET> // Enter key 142 | ; 143 | }; 144 | 145 | clip_hist: clip_hist { // CLIPBOARD_HISTORY 146 | compatible = "zmk,behavior-macro"; 147 | #binding-cells = <0>; 148 | bindings 149 | = <¯o_press &kp LGUI> 150 | , <¯o_tap &kp V> 151 | , <¯o_release &kp LGUI> 152 | ; 153 | }; 154 | 155 | new_dir: new_dir { 156 | compatible = "zmk,behavior-macro"; 157 | #binding-cells = <0>; 158 | bindings 159 | = <¯o_press &kp LCTRL &kp LSHFT> 160 | , <¯o_tap &kp N> 161 | , <¯o_release &kp LCTRL &kp LSHFT> 162 | ; 163 | }; 164 | 165 | grave_macro: grave_macro { 166 | compatible = "zmk,behavior-macro"; 167 | #binding-cells = <0>; 168 | bindings 169 | = <¯o_tap &kp GRAVE> 170 | , <¯o_tap &kp GRAVE> 171 | , <¯o_tap &kp LEFT_ARROW> 172 | ; 173 | }; 174 | 175 | minus_macro: minus_macro { 176 | compatible = "zmk,behavior-macro"; 177 | #binding-cells = <0>; 178 | bindings 179 | = <¯o_tap &kp SPACE> 180 | , <¯o_tap &kp MINUS> 181 | , <¯o_tap &kp SPACE> 182 | ; 183 | }; 184 | 185 | equals_macro: equals_macro { 186 | compatible = "zmk,behavior-macro"; 187 | #binding-cells = <0>; 188 | bindings 189 | = <¯o_tap &kp SPACE> 190 | , <¯o_tap &kp EQUAL> 191 | , <¯o_tap &kp SPACE> 192 | ; 193 | }; 194 | 195 | home_macro: home_macro { 196 | compatible = "zmk,behavior-macro"; 197 | #binding-cells = <0>; 198 | bindings 199 | = <¯o_tap &kp TILDE> 200 | , <¯o_tap &kp FSLH> 201 | ; 202 | }; 203 | }; 204 | }; -------------------------------------------------------------------------------- /scripts/convert_keymap.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | import sys 4 | import argparse 5 | 6 | def main(): 7 | ##################################################################### 8 | # Define variables & write output keymap file 9 | ##################################################################### 10 | 11 | # Create the parser 12 | parser = argparse.ArgumentParser(description="A script that converts ZMK keymap files from QWERTY <|> Colemak DH") 13 | # Define flags and parameters 14 | parser.add_argument( 15 | '-c', '--convert', 16 | type=str, 17 | choices=['q2c', 'c2q'], 18 | default='q2c', 19 | help="Specify the conversion: 'q2c' will convert QWERTY to Colemak DH, 'c2q' will convert Colemak DH to QWERTY (default: 'q2c')" 20 | ) 21 | parser.add_argument( 22 | '--in-path', 23 | type=str, 24 | required=True, 25 | help="Path to the input keymap file. This is the path where the ouitput will be stored as well" 26 | ) 27 | # Parse the arguments 28 | args = parser.parse_args() 29 | # Set the variable for the chosen option 30 | conversion_type = args.convert 31 | full_path = args.in_path 32 | 33 | # Check argument values and convert keymap 34 | if conversion_type not in ['q2c','c2q']: 35 | print("Error: Invalid conversion type selected.") 36 | sys.exit(1) 37 | 38 | path, in_file = os.path.split(full_path) 39 | out_file = 'qwerty.keymap' if conversion_type == 'c2q' else 'colemak_dh.keymap' 40 | out_full_path = os.path.join(path, out_file) 41 | 42 | print("#####################################################################") 43 | print(f"Selected conversion type: {conversion_type}") 44 | print(f"path:........{path}") 45 | print(f"input_file:..{in_file}") 46 | print(f"out_file:....{out_file}") 47 | print("#####################################################################") 48 | 49 | ##################################################################### 50 | # Define conversions 51 | ##################################################################### 52 | 53 | if conversion_type == 'q2c': 54 | initial_keymap = { 55 | # Top row (numbers and symbols are not included in this example) 56 | 'Q': 'Q', 'W': 'W', 'E': 'F', 'R': 'P', 'T': 'B', 'Y': 'J', 'U': 'L', 'I': 'U', 'O': 'Y', 'P': 'APOS', 57 | # Home row 58 | 'A': 'A', 'S': 'R', 'D': 'S', 'F': 'T', 'G': 'G', 'H': 'M', 'J': 'N', 'K': 'E', 'L': 'I', 'SEMICOLON': 'O', 59 | # Bottom row 60 | 'Z': 'Z', 'X': 'X', 'C': 'C', 'V': 'D', 'B': 'V', 'N': 'K', 'M': 'H', 61 | } 62 | else: 63 | initial_keymap = { 64 | # Top row (numbers and symbols are not included in this example) 65 | 'Q': 'Q', 'W': 'W', 'F': 'E', 'P': 'R', 'B': 'T', 'J': 'Y', 'L': 'U', 'U': 'I', 'Y': 'O', 'APOS': 'P', 66 | # Home row 67 | 'A': 'A', 'R': 'S', 'S': 'D', 'T': 'F', 'G': 'G', 'M': 'H', 'N': 'J', 'E': 'K', 'I': 'L', 'O': 'SEMICOLON', 68 | # Bottom row 69 | 'Z': 'Z', 'X': 'X', 'C': 'C', 'D': 'V', 'V': 'B', 'K': 'N', 'H': 'M' 70 | } 71 | 72 | ##################################################################### 73 | # Read and store input keymap 74 | ##################################################################### 75 | 76 | # Read the content of the keymap_contents 77 | with open(full_path, 'r') as keymap_file: 78 | keymap_contents = keymap_file.read() 79 | 80 | ##################################################################### 81 | # Functions 82 | ##################################################################### 83 | 84 | def convert_keymap(keymap_contents): 85 | # Define regex pattern to find the 'Base' keymap section 86 | base_keymap_pattern = re.compile(r'(BASE\s*\{\s*bindings\s*=\s*<\s*)(.*?)(\s*>;)', re.DOTALL) 87 | # Apply regex substitution to convert keymap 88 | new_keymap_contents = base_keymap_pattern.sub(replace_keymap, keymap_contents) 89 | return new_keymap_contents 90 | 91 | # Find and replace the 'BASE' keymap layer 92 | def replace_keymap(match): 93 | before_keymap = match.group(1) 94 | old_keymap = match.group(2) 95 | after_keymap = match.group(3) 96 | 97 | print(f">> Found BASE keymap \n{old_keymap}") 98 | 99 | # Split the old keymap by lines 100 | lines = old_keymap.strip().split('\n') 101 | 102 | # Process each line 103 | new_lines = [] 104 | print(">> Converting letter keys") 105 | for line in lines: 106 | # Split the line by spaces or other delimiters 107 | parts = line.split() 108 | new_parts = [] 109 | 110 | for part in parts: 111 | if not part.startswith('&'): 112 | # Extract key (removing ZMK behavior commands) 113 | key = part.split()[1] if len(part.split()) > 1 else part 114 | # Map the key to the conversion type if applicable 115 | if key.upper() in initial_keymap: 116 | print(key.upper(),end=":") 117 | new_key = initial_keymap[key] 118 | print(new_key) 119 | new_parts.append(part.replace(key, new_key)) 120 | else: 121 | new_parts.append(part) 122 | else: 123 | new_parts.append(part) 124 | # Join new parts for the line and add to new_lines 125 | new_lines.append(' '.join(new_parts)) 126 | 127 | # Join new lines to form the new keymap keymap_contents 128 | new_keymap = '\n'.join(new_lines) 129 | print(f"\n>> Generated {out_file} \n{format_columns(new_keymap)}") 130 | return before_keymap + format_columns(new_keymap) + after_keymap 131 | 132 | def format_columns(text): 133 | zmk_behavior = r'(&\w+)' 134 | 135 | # Split the input text into lines 136 | lines = text.strip().split('\n') 137 | 138 | # Split each line into columns 139 | split_lines = [re.split(zmk_behavior,line) for line in lines] 140 | 141 | # Determine the number of columns 142 | num_columns = max(len(line) for line in split_lines) 143 | 144 | # Calculate the maximum width for each column 145 | column_widths = [0] * num_columns 146 | for line in split_lines: 147 | for i, item in enumerate(line): 148 | column_widths[i] = max(column_widths[i], len(item)) 149 | 150 | # Format each line with the calculated column widths 151 | formatted_lines = [] 152 | for line in split_lines: 153 | formatted_line = ''.join(f"{item:<{column_widths[i] + 1}}" for i, item in enumerate(line)) 154 | formatted_lines.append(formatted_line) 155 | 156 | # Join all formatted lines 157 | formatted_text = '\n'.join(formatted_lines) 158 | return formatted_text 159 | 160 | converted_map = convert_keymap(keymap_contents) 161 | 162 | # Write the new keymap_contents to the output file 163 | with open(out_full_path, 'w') as file: 164 | file.write(converted_map) 165 | 166 | print("#####################################################################") 167 | print(f"Updated keymap written to {out_full_path}") 168 | print("#####################################################################") 169 | if __name__ == "__main__": 170 | main() -------------------------------------------------------------------------------- /keymap-drawer/charybdis.yaml: -------------------------------------------------------------------------------- 1 | layout: {zmk_keyboard: charybdis} 2 | layers: 3 | BASE: 4 | - DEV TOOLS 5 | - Q 6 | - W 7 | - E 8 | - R 9 | - T 10 | - Y 11 | - U 12 | - I 13 | - O 14 | - P 15 | - L_BASE L_PHOTO L_GAME 16 | - {t: TAB, h: LCMD} 17 | - {t: A, h: SYM} 18 | - {t: S, h: LEFT ALT} 19 | - {t: D, h: LCTRL} 20 | - {t: F, h: LEFT SHIFT} 21 | - G 22 | - H 23 | - {t: J, h: RIGHT SHIFT} 24 | - {t: K, h: RCTRL} 25 | - {t: L, h: RIGHT ALT} 26 | - {t: ;, h: RCMD} 27 | - DEL 28 | - SEARCH 29 | - Z 30 | - {t: X, h: MEH} 31 | - {t: C, h: HYPER} 32 | - V 33 | - B 34 | - N 35 | - M 36 | - {t: ',', h: HYPER} 37 | - {t: ., h: MEH} 38 | - ~/ 39 | - CLIP HIST 40 | - SCRL SLOW 41 | - L CLK 42 | - '&td_bs' 43 | - {t: RETURN, h: MOUSE} 44 | - '&td_space' 45 | NUM: 46 | - {t: ▽, type: trans} 47 | - '' 48 | - '' 49 | - '' 50 | - '' 51 | - '' 52 | - '*' 53 | - {t: '7', h: F7} 54 | - {t: '8', h: F8} 55 | - {t: '9', h: F9} 56 | - F10 57 | - '' 58 | - {t: ▽, type: trans} 59 | - '' 60 | - '' 61 | - '' 62 | - '' 63 | - '' 64 | - '-' 65 | - {t: '4', h: F4} 66 | - {t: '5', h: F5} 67 | - {t: '6', h: F6} 68 | - {t: +, h: F11} 69 | - {t: ▽, type: trans} 70 | - '' 71 | - '' 72 | - '' 73 | - '' 74 | - BACK SPACE 75 | - SPACE 76 | - '=' 77 | - {t: '1', h: F1} 78 | - {t: '2', h: F2} 79 | - {t: '3', h: F3} 80 | - {t: /, h: DEV TOOLS} 81 | - '' 82 | - '' 83 | - '' 84 | - {t: ▽, type: trans} 85 | - . 86 | - '0' 87 | NAV: 88 | - '' 89 | - '' 90 | - '' 91 | - '' 92 | - '' 93 | - '' 94 | - '&detach_session' 95 | - '&create_or_attach' 96 | - '&next_pane' 97 | - '&show_sessions' 98 | - '&sync_panes' 99 | - '' 100 | - {t: ▽, type: trans} 101 | - {t: ▽, type: trans} 102 | - {t: ▽, type: trans} 103 | - {t: ▽, type: trans} 104 | - {t: ▽, type: trans} 105 | - '' 106 | - HSPLIT 107 | - LEFT 108 | - DOWN 109 | - UP 110 | - RIGHT 111 | - '' 112 | - '' 113 | - '' 114 | - '' 115 | - '' 116 | - '' 117 | - '' 118 | - VSPLIT 119 | - HOME 120 | - PG DN 121 | - PG UP 122 | - END 123 | - '' 124 | - '' 125 | - '' 126 | - {t: ▽, type: trans} 127 | - {t: ▽, type: trans} 128 | - {t: ▽, type: trans} 129 | SYM: 130 | - '' 131 | - '' 132 | - '' 133 | - '' 134 | - '' 135 | - '' 136 | - '*' 137 | - '&' 138 | - '{' 139 | - $ 140 | - '&td_grave' 141 | - '!' 142 | - '' 143 | - {type: held} 144 | - '' 145 | - '' 146 | - {t: ▽, type: trans} 147 | - '' 148 | - '&td_hyphen' 149 | - ; 150 | - ( 151 | - '[' 152 | - + 153 | - '|' 154 | - '' 155 | - '' 156 | - '' 157 | - '' 158 | - '' 159 | - '' 160 | - '@' 161 | - _ 162 | - < 163 | - '#' 164 | - '%' 165 | - \ 166 | - {t: ▽, type: trans} 167 | - {t: ▽, type: trans} 168 | - {t: ▽, type: trans} 169 | - {t: ▽, type: trans} 170 | - {t: ▽, type: trans} 171 | GAME: 172 | - '1' 173 | - TAB 174 | - Q 175 | - W 176 | - E 177 | - R 178 | - '' 179 | - '' 180 | - '' 181 | - '' 182 | - '' 183 | - {t: ▽, type: trans} 184 | - '2' 185 | - LCTRL 186 | - A 187 | - S 188 | - D 189 | - F 190 | - '' 191 | - '' 192 | - '' 193 | - '' 194 | - '' 195 | - '' 196 | - '3' 197 | - LSHFT 198 | - Z 199 | - X 200 | - C 201 | - V 202 | - '' 203 | - '' 204 | - '' 205 | - '' 206 | - '' 207 | - '' 208 | - '' 209 | - SPACE 210 | - LEFT ALT 211 | - '' 212 | - '' 213 | PHOTOS: 214 | - {t: ▽, type: trans} 215 | - TAG Z 216 | - TAG E 217 | - TAG A 218 | - TAG S 219 | - TAG B 220 | - '' 221 | - '' 222 | - '' 223 | - '' 224 | - '' 225 | - {t: ▽, type: trans} 226 | - FIT 227 | - LEFT 228 | - DOWN 229 | - UP 230 | - RIGHT 231 | - LCTRL 232 | - '' 233 | - '' 234 | - '' 235 | - '' 236 | - '' 237 | - {t: ▽, type: trans} 238 | - SHRED 239 | - SHOW TAGS 240 | - REJECT 241 | - PENDING 242 | - ACCEPT 243 | - '' 244 | - '' 245 | - '' 246 | - '' 247 | - '' 248 | - '' 249 | - '' 250 | - {t: ▽, type: trans} 251 | - {t: ▽, type: trans} 252 | - {t: ▽, type: trans} 253 | - {t: ▽, type: trans} 254 | - {t: ▽, type: trans} 255 | EXTRAS: 256 | - POWER 257 | - SHRUG 258 | - LGTM 259 | - GIT COMMIT 260 | - '' 261 | - BRIGHTNESS INC 262 | - {t: BT, h: '0'} 263 | - {t: BT, h: '1'} 264 | - {t: BT, h: '2'} 265 | - {t: BT, h: '3'} 266 | - OUT TOG 267 | - BT CLR 268 | - SLEEP 269 | - '&sudo' 270 | - '' 271 | - '' 272 | - '' 273 | - BRIGHTNESS DEC 274 | - '' 275 | - PREVIOUS 276 | - PLAY PAUSE 277 | - STOP 278 | - NEXT 279 | - '' 280 | - LOCK 281 | - '' 282 | - '' 283 | - '' 284 | - '' 285 | - '' 286 | - NEW DIR 287 | - MUTE 288 | - VOLUME DOWN 289 | - VOLUME UP 290 | - PRINT SCREEN 291 | - ZMK Studio 292 | - '' 293 | - '' 294 | - '' 295 | - '' 296 | - '' 297 | MOUSE: 298 | - '' 299 | - WHEEL L 300 | - WHEEL D 301 | - MOVE UP 302 | - WHEEL U 303 | - WHEEL R 304 | - '' 305 | - '' 306 | - '' 307 | - '' 308 | - '' 309 | - '' 310 | - TAB 311 | - BACK 312 | - MOVE L 313 | - MOVE D 314 | - MOVE R 315 | - FWD 316 | - '' 317 | - RIGHT SHIFT 318 | - RCTRL 319 | - LEFT ALT 320 | - LCMD 321 | - '' 322 | - '' 323 | - UNDO 324 | - CUT 325 | - COPY 326 | - PASTE 327 | - Mid CLK 328 | - '' 329 | - '' 330 | - '' 331 | - '' 332 | - '' 333 | - '' 334 | - {t: ▽, type: trans} 335 | - {t: ▽, type: trans} 336 | - {t: ▽, type: trans} 337 | - {t: RETURN, type: held} 338 | - {t: ▽, type: trans} 339 | SLOW: 340 | - {t: ▽, type: trans} 341 | - {t: ▽, type: trans} 342 | - {t: ▽, type: trans} 343 | - {t: ▽, type: trans} 344 | - {t: ▽, type: trans} 345 | - {t: ▽, type: trans} 346 | - {t: ▽, type: trans} 347 | - {t: ▽, type: trans} 348 | - {t: ▽, type: trans} 349 | - {t: ▽, type: trans} 350 | - {t: ▽, type: trans} 351 | - {t: ▽, type: trans} 352 | - {t: ▽, type: trans} 353 | - {t: ▽, type: trans} 354 | - {t: ▽, type: trans} 355 | - {t: ▽, type: trans} 356 | - {t: ▽, type: trans} 357 | - {t: ▽, type: trans} 358 | - {t: ▽, type: trans} 359 | - {t: ▽, type: trans} 360 | - {t: ▽, type: trans} 361 | - {t: ▽, type: trans} 362 | - {t: ▽, type: trans} 363 | - {t: ▽, type: trans} 364 | - {t: ▽, type: trans} 365 | - {t: ▽, type: trans} 366 | - {t: ▽, type: trans} 367 | - {t: ▽, type: trans} 368 | - {t: ▽, type: trans} 369 | - {t: ▽, type: trans} 370 | - {t: ▽, type: trans} 371 | - {t: ▽, type: trans} 372 | - {t: ▽, type: trans} 373 | - {t: ▽, type: trans} 374 | - {t: ▽, type: trans} 375 | - {t: ▽, type: trans} 376 | - {t: ▽, type: trans} 377 | - {t: ▽, type: trans} 378 | - {t: ▽, type: trans} 379 | - {t: ▽, type: trans} 380 | - {t: ▽, type: trans} 381 | SCROLL: 382 | - {t: ▽, type: trans} 383 | - {t: ▽, type: trans} 384 | - {t: ▽, type: trans} 385 | - {t: ▽, type: trans} 386 | - {t: ▽, type: trans} 387 | - {t: ▽, type: trans} 388 | - {t: ▽, type: trans} 389 | - {t: ▽, type: trans} 390 | - {t: ▽, type: trans} 391 | - {t: ▽, type: trans} 392 | - {t: ▽, type: trans} 393 | - {t: ▽, type: trans} 394 | - {t: ▽, type: trans} 395 | - {t: ▽, type: trans} 396 | - {t: ▽, type: trans} 397 | - {t: ▽, type: trans} 398 | - {t: ▽, type: trans} 399 | - {t: ▽, type: trans} 400 | - {t: ▽, type: trans} 401 | - {t: ▽, type: trans} 402 | - {t: ▽, type: trans} 403 | - {t: ▽, type: trans} 404 | - {t: ▽, type: trans} 405 | - {t: ▽, type: trans} 406 | - {t: ▽, type: trans} 407 | - {t: ▽, type: trans} 408 | - {t: ▽, type: trans} 409 | - {t: ▽, type: trans} 410 | - {t: ▽, type: trans} 411 | - {t: ▽, type: trans} 412 | - {t: ▽, type: trans} 413 | - {t: ▽, type: trans} 414 | - {t: ▽, type: trans} 415 | - {t: ▽, type: trans} 416 | - {t: ▽, type: trans} 417 | - {t: ▽, type: trans} 418 | - {t: ▽, type: trans} 419 | - {t: ▽, type: trans} 420 | - {t: ▽, type: trans} 421 | - {t: ▽, type: trans} 422 | - {t: ▽, type: trans} 423 | combos: 424 | - p: [17, 18] 425 | k: CAPS WORD 426 | l: [BASE] 427 | - p: [28, 37] 428 | k: Mid CLK 429 | - p: [16, 37] 430 | k: R CLK 431 | - p: [20, 21] 432 | k: ) 433 | l: [SYM] 434 | - p: [21, 22] 435 | k: ']' 436 | l: [SYM] 437 | - p: [8, 9] 438 | k: '}' 439 | l: [SYM] 440 | - p: [32, 33] 441 | k: '>' 442 | l: [SYM] 443 | - p: [38, 39] 444 | k: L_BASE L_EXTRA 445 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![.github/workflows/build.yml](https://github.com/280Zo/charybdis-wireless-mini-zmk-firmware/actions/workflows/build.yml/badge.svg)](https://github.com/280Zo/charybdis-wireless-mini-zmk-firmware/actions/workflows/build.yml) 2 | 3 | ## Intro 4 | 5 | This repository offers pre-configured ZMK firmware designed for Wireless Charybdis keyboards, supporting both the ubiquitous QWERTY layout and the optimized Colemak DH layout. You can choose from two configurations: 6 | 7 | - Bluetooth and USB 8 | - Dongle 9 | 10 | Additionally, this repository automatically generates SVG images of all layers in the keymap, and adds it to the README. It also provides high level instructions and resources on how to customize and build the firmware to meet your specific needs. 11 | 12 | Check out the [Charybdis Mini Wireless build guide](https://github.com/280Zo/charybdis-wireless-mini-3x6-build-guide?tab=readme-ov-file) if you haven't yet built your own Charybdis keyboard. 13 | 14 | ## Usage 15 | 16 | If you'd like to use the pre-built firmware the files can be found in the [Actions Workflows](https://github.com/280Zo/charybdis-wireless-mini-zmk-firmware/actions?query=is%3Acompleted+branch%3Amain). To download them, log into Github, click the link, select the latest run that passed on the main branch, and download the applicable firmware. There are five firmware artifacts to choose from. If you're unsure which one to use, you probably want the firmware-charybdis-qwerty build. 17 | 18 | - **firmware-charybdis-qwerty** - Bluetooth/USB with QWERTY layout 19 | - **firmware-charybdis-qwerty-dongle** - Dongle with QWERTY layout 20 | - **firmware-charybdis-colemak** - Bluetooth/USB with Colemak DH layout 21 | - **firmware-charybdis-colemak-dongle** - Dongle with Colemak DH layout 22 | - **firmware-reset-nanov2** - Reset the firmware completely 23 | 24 | There are a few things to note about how the pre-built firmware is configured: 25 | 26 | - ZMK has terms for each side of a split keyboard. Central is the half that sends keyboard outputs over USB or advertises to other devices over bluetooth. Peripheral is the half that will only send keystrokes to the central once they are paired and connected through bluetooth. The Bluetooth/USB firmware uses the right side as central. 27 | - The dongle firmware will have much better battery life for the central side, but requires an extra MCU and can only be connected through the dongle. 28 | - The Bluetooth/USB firmware can connect through Bluetooth, but the central side will have a shorter battery life because it needs to maintain that connection. 29 | - The central side can also be plugged in to USB and the keyboard can be used when Bluetooth on the host computer isn't available (e.g. BIOS navigation) 30 | - To add support for the PMW3610 low power trackball sensor, badjeff's [zmk-pmw3610-driver](https://github.com/badjeff/zmk-pmw3610-driver), [ZMK Input Behavior Listener](https://github.com/badjeff/zmk-input-behavior-listener?tab=readme-ov-file), and [ZMK Split Peripheral Input Relay](https://github.com/badjeff/zmk-split-peripheral-input-relay) modules are included in the firmware. 31 | 32 | ## Flashing the Firmware 33 | 34 | Follow the steps below to flash the firmware 35 | 36 | - If you are flashing the firmware for the first time, or if you're switching between the dongle and the Bluetooth/USB configuration, flash the reset firmware to all the devices first 37 | - Unzip the firmware.zip 38 | - Plug the right half info the computer through USB 39 | - Double press the reset button 40 | - The keyboard will mount as a removable storage device 41 | - Copy the applicable uf2 file into the NICENANO storage device (e.g. charybdis_qwerty_dongle.uf2 -> dongle) 42 | - It will take a few seconds, then it will unmount and restart itself. 43 | - Repeat these steps for all devices. 44 | - You should now be able to use your keyboard 45 | 46 | > [!NOTE] 47 | > If the keyboard halves aren't connecting as expected, try pressing the reset button on both halves at the same time. If that doesn't work, follow the [ZMK Connection Issues](https://zmk.dev/docs/troubleshooting/connection-issues#acquiring-a-reset-uf2) documentation for more troubleshooting steps. 48 | 49 | ## Keymaps & Layers 50 | 51 | The base layer uses [home row mods](https://precondition.github.io/home-row-mods) to make typing as efficient and comfortable as possible. To reduce hand movement, extra attention has also been given to making sure cursor, scrolling, and mouse button operations are as seamless as possible. 52 | 53 | Review the layer maps below to see how each one functions. Then either modify the keymap to fit your needs, or start using these defaults to become more familiar with them. 54 | 55 | Here are a few tips for a quick start: 56 | 57 | - The bluetooth keys on the EXTRAS layer allow you to select which bluetooth pairing you want, BT-CLR clears the pairing on the selected profile. 58 | 59 | - The left most thumb button has multiple functions 60 | - When held, the function of the trackball is changed from moving the cursor to scrolling. 61 | - When double tapped and held, it will reduce the cursor speed for more precision. 62 | - When single tapped it outputs the escape key. 63 | 64 | ![keymap images](keymap-drawer/charybdis.svg) 65 | 66 | ## Modify Key Mappings 67 | 68 | ### ZMK Studio 69 | 70 | [ZMK Studio](https://zmk.studio/) allows users to update functionality during runtime. It's currently in beta, but the physical layout and updated config files are included in the BT/USB firmware for testing. The dongle firmware does not have this integration at the moment. 71 | 72 | To change the visual layout of the keys, the physical layout must be updated. This is the charybdis-layouts.dtsi file, which handles the actual physical positions of the keys. Though they may appear to be similar, this is different than the matrix transform file (charybdis.json) which handles the electrical matrix to keymap relationship. 73 | 74 | To easily modify the physical layout, or convert a matrix transform file, [caksoylar](https://github.com/caksoylar/zmk-physical-layout-converter) has built the [ZMK physical layouts converter](https://zmk-physical-layout-converter.streamlit.app/). 75 | 76 | For more details on how to use ZMK Studio, refer to the [ZMK documentation](https://zmk.dev/docs/features/studio). 77 | 78 | ### Keymap GUI 79 | 80 | Using a GUI to generate the keymap file before building the firmware is another easy way to modify the key mappings. Head over to nickcoutsos' keymap editor and follow the steps below. 81 | 82 | - Fork/Clone this repo 83 | - Open a new tab to the [keymap editor](https://nickcoutsos.github.io/keymap-editor/) 84 | - Give it permission to see your repo 85 | - Select the branch you'd like to modify 86 | - Update the keys to match what you'd like to use on your keyboard 87 | - Save 88 | - Wait for the pipeline to run 89 | - Download and flash the new firmware 90 | 91 | ### Edit Keymap Directly 92 | 93 | To change a key layout choose a behavior you'd like to assign to a key, then choose a parameter code. This process is more clearly outlined on ZMK's [Keymaps & Behaviors](https://zmk.dev/docs/features/keymaps) page. 94 | 95 | - Behaviors are all documented on the [Behaviors Overview](https://zmk.dev/docs/behaviors) 96 | - Codes are all documented on the [keycodes](https://zmk.dev/docs/codes) page 97 | 98 | Open the keymap file and change keys, or add/remove layers, then merge the changes and re-flash the keyboard with the updated firmware. 99 | 100 | ## Changing the Central and Peripheral Assignments 101 | 102 | Follow the ZMK documentation [Kconfig.deconfig](https://zmk.dev/docs/development/new-shield#kconfigdefconfig) to change which keyboard half is the central and which is the peripheral. This does not apply to the dongle configuration. 103 | 104 | ## Changing the Keyboard Name 105 | 106 | Follow the ZMK [Kconfig.defconfig](https://zmk.dev/docs/development/new-shield#kconfigdefconfig) section to update the keyboard name. Make sure to read about the danger in exceeding the 16 character limit. 107 | 108 | ## Building Your Own Firmware 109 | 110 | ZMK provides a comprehensive guide to follow when creating a [New Keyboard Shield](https://zmk.dev/docs/development/new-shield). I'll touch on some of the points here, but their docs should be what you reference when you're building your own firmware. 111 | 112 | ### File Structure 113 | 114 | When building the ZMK firmware, the files need to be located in the correct place. The formats and locations of the files can be found on ZMK's [Configuration Overview](https://zmk.dev/docs/config). 115 | 116 | ### Mapping GPIO Pins to Keys 117 | 118 | To set up some of the configuration files it requires a knowledge of which keys connect to which pins on the MCU (see the [Shield Overlays](https://zmk.dev/docs/development/new-shield#shield-overlays) section), and how the rows and columns are wired. 119 | 120 | To get this information, look at the PCB kcad files and follow the traces from key pads, to row and column through holes, to MCU through holes. Once you have that information you can update the applicable dtsi/overlay files. 121 | 122 | ## Creating Graphical Key Maps 123 | 124 | This repo uses the excellent work of caksoylar's [Keymap Drawer](https://keymap-drawer.streamlit.app/) to automatically generate a key mapping of each layer when the Github Actions are run. 125 | 126 | ## Credits 127 | 128 | - [eigatech](https://github.com/eigatech) 129 | - [badjeff](https://github.com/badjeff) 130 | - [inorichi](https://github.com/inorichi) 131 | - [manna-harbour](https://github.com/manna-harbour) 132 | - [nickcoutsos](https://github.com/nickcoutsos/keymap-editor) 133 | - [Petejohanson](https://github.com/petejohanson) 134 | - [caksoylar](https://github.com/caksoylar/keymap-drawer) -------------------------------------------------------------------------------- /config/charybdis.keymap: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "macros.dtsi" 9 | #include "behaviors.dtsi" 10 | #include "combos.dtsi" 11 | 12 | / { 13 | /* input config for mouse move mode */ 14 | trackball_listener { 15 | compatible = "zmk,input-behavior-listener"; 16 | device = <&vtrackball>; 17 | layers = <0 2 7>; 18 | evt-type = ; 19 | x-input-code = ; 20 | y-input-code = ; 21 | scale-multiplier = <4>; 22 | scale-divisor = <5>; 23 | // bindings = <&ib_toggle_layer 7>; 24 | }; 25 | 26 | /* input config for snipe mode */ 27 | trackball_snipe_listener { 28 | compatible = "zmk,input-behavior-listener"; 29 | device = <&vtrackball>; 30 | layers = <8>; 31 | evt-type = ; 32 | x-input-code = ; 33 | y-input-code = ; 34 | scale-multiplier = <1>; 35 | scale-divisor = <6>; 36 | }; 37 | 38 | /* input config for mouse scroll mode */ 39 | trackball_scroll_listener { 40 | compatible = "zmk,input-behavior-listener"; 41 | device = <&vtrackball>; 42 | layers = <9>; 43 | evt-type = ; 44 | x-input-code = ; 45 | y-input-code = ; 46 | y-invert; 47 | bindings = <&ib_wheel_scaler 1 14>; 48 | }; 49 | 50 | /* define a resolution down scaler only for INPUT_REL_WHEEL */ 51 | ib_wheel_scaler: ib_wheel_scaler { 52 | compatible = "zmk,input-behavior-scaler"; 53 | #binding-cells = <2>; 54 | evt-type = ; 55 | input-code = ; 56 | }; 57 | 58 | /* adjust cooldown waiting period for mouse key layer after activated */ 59 | ib_toggle_layer: ib_toggle_layer { 60 | compatible = "zmk,input-behavior-tog-layer"; 61 | #binding-cells = <1>; 62 | time-to-live-ms = <750>; 63 | }; 64 | // ╭──────┬──────┬──────┬──────┬──────┬──────╮ ╭──────┬──────┬──────┬──────┬──────┬──────╮ 65 | // 00 01 02 03 04 05 06 07 08 09 10 11 66 | // ├──────┼──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┼──────┤ 67 | // 12 13 14 15 16 17 18 19 20 21 22 23 68 | // ├──────┼──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┼──────┤ 69 | // 24 25 26 27 28 29 30 31 32 33 34 35 70 | // ╰──────┴──────┴──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┴──────┴──────┴──────╯ 71 | // 36 37 38 39 40 72 | // ╰──────┴──────┴──────╯ ╰──────┴──────╯ 73 | // #define BASE 0 74 | // #define NUM 1 75 | // #define NAV 2 76 | // #define SYM 3 77 | // #define GAME 4 78 | // #define PHOTOS 5 79 | // #define EXTRAS 6 80 | // #define MOUSE 7 81 | // #define SLOW 8 82 | // #define SCROLL 9 83 | 84 | keymap { 85 | compatible = "zmk,keymap"; 86 | BASE { 87 | bindings = < 88 | // ╭────────────────┬──────────────┬─────────────────────────────┬──────────────────────────────────┬──────────────────────┬────────╮ ╭────────────────┬─────────────────────────┬──────────────────────────────────────┬────────────────────────────────────┬────────────────────────┬──────────────╮ 89 | &kp F12 &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &td_layers 90 | // ├────────────────┼──────────────┼─────────────────────────────┼──────────────────────────────────┼──────────────────────┼────────┤ ├────────────────┼─────────────────────────┼──────────────────────────────────────┼────────────────────────────────────┼────────────────────────┼──────────────┤ 91 | &hm_left LCMD TAB < 3 A &hm_left LEFT_ALT S &hm_left LCTRL D &hm_left LEFT_SHIFT F &kp G &kp H &hm_right RIGHT_SHIFT J &hm_right RCTRL K &hm_right RIGHT_ALT L &hm_right RCMD SEMICOLON &kp DEL 92 | // ├────────────────┼──────────────┼─────────────────────────────┼──────────────────────────────────┼──────────────────────┼────────┤ ├────────────────┼─────────────────────────┼──────────────────────────────────────┼────────────────────────────────────┼────────────────────────┼──────────────┤ 93 | &kp C_AC_SEARCH &kp Z &hm_left LC(LA(LEFT_SHIFT)) X &hm_left LA(LC(LG(LEFT_SHIFT))) C &kp V &kp B &kp N &kp M &hm_right LA(LC(LG(LEFT_SHIFT))) COMMA &hm_right LC(LA(LEFT_SHIFT)) PERIOD &td_home &clip_hist 94 | // ╰────────────────┴──────────────┴─────────────────────────────┼──────────────────────────────────┼──────────────────────┼────────┤ ├────────────────┼─────────────────────────┼──────────────────────────────────────┴────────────────────────────────────┴────────────────────────┴──────────────╯ 95 | &td_cursor &mkp LCLK &td_bs < 7 RETURN &td_space 96 | // ╰─────────────┴──────────────────┴────────╯ ╰────────────────┴──────────────╯ 97 | >; 98 | }; 99 | 100 | NUM { 101 | bindings = < 102 | &trans &none &none &none &none &none &kp ASTRK &hm F7 N7 &hm F8 N8 &hm F9 N9 &kp F10 &none 103 | &trans &none &none &none &none &none &kp MINUS &hm F4 N4 &hm F5 N5 &hm F6 N6 &hm F11 PLUS &trans 104 | &none &none &none &none &kp BACKSPACE &kp SPACE &td_equals &hm F1 N1 &hm F2 N2 &hm F3 N3 &hm F12 SLASH &none 105 | &none &none &trans &kp DOT &kp N0 106 | >; 107 | }; 108 | 109 | NAV { 110 | bindings = < 111 | &none &none &none &none &none &none &detach_session &create_or_attach &next_pane &show_sessions &sync_panes &none 112 | &trans &trans &trans &trans &trans &none &h_split &kp LEFT_ARROW &kp DOWN &kp UP &kp RIGHT_ARROW &none 113 | &none &none &none &none &none &none &v_split &kp HOME &kp PG_DN &kp PG_UP &kp END &none 114 | &none &none &trans &trans &trans 115 | >; 116 | }; 117 | 118 | SYM { 119 | bindings = < 120 | &none &none &none &none &none &none &kp ASTRK &kp AMPS &kp LEFT_BRACE &kp DLLR &td_grave &kp EXCL 121 | &none &none &none &none &trans &none &td_hyphen &kp SEMICOLON &kp LPAR &kp LBKT &kp PLUS &kp PIPE 122 | &none &none &none &none &none &none &kp AT_SIGN &kp UNDERSCORE &kp LT &kp HASH &kp PRCNT &kp BSLH 123 | &trans &trans &trans &trans &trans 124 | >; 125 | }; 126 | 127 | GAME { 128 | bindings = < 129 | &kp N1 &kp TAB &kp Q &kp W &kp E &kp R &none &none &none &none &none &trans 130 | &kp N2 &kp LCTRL &kp A &kp S &kp D &kp F &none &none &none &none &none &none 131 | &kp N3 &kp LSHFT &kp Z &kp X &kp C &kp V &none &none &none &none &none &none 132 | &none &kp SPACE &kp LEFT_ALT &none &none 133 | >; 134 | }; 135 | 136 | PHOTOS { 137 | bindings = < 138 | &trans &kp LA(LC(LS(Z))) &kp LA(LC(LS(E))) &kp LA(LC(LS(A))) &kp LA(LC(LS(S))) &kp LA(LC(LS(B))) &none &none &none &none &none &trans 139 | &kp LC(LA(E)) &kp LEFT &kp DOWN &kp UP &kp RIGHT &kp LCTRL &none &none &none &none &none &trans 140 | &kp LS(DELETE) &kp LA(LS(A)) &kp LA(KP_N1) &kp LA(KP_N2) &kp LA(KP_N3) &none &none &none &none &none &none &none 141 | &trans &trans &trans &trans &trans 142 | >; 143 | }; 144 | 145 | EXTRAS { 146 | bindings = < 147 | &kp C_POWER &shrug &lgtm &gcm &none &kp C_BRIGHTNESS_INC &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &out OUT_TOG &bt BT_CLR 148 | &kp C_SLEEP &sudo &none &none &none &kp C_BRIGHTNESS_DEC &none &kp C_PREVIOUS &kp C_PLAY_PAUSE &kp C_STOP &kp C_NEXT &none 149 | &kp C_AL_COFFEE &none &none &none &none &none &new_dir &kp K_MUTE &kp C_VOLUME_DOWN &kp C_VOLUME_UP &kp PRINTSCREEN &studio_unlock 150 | &none &none &none &none &none 151 | >; 152 | }; 153 | 154 | MOUSE { 155 | bindings = < 156 | &none &msc MOVE_LEFT &msc MOVE_UP &mmv MOVE_UP &msc MOVE_DOWN &msc MOVE_RIGHT &none &none &none &none &none &none 157 | &kp TAB &mkp MB4 &mmv MOVE_LEFT &mmv MOVE_DOWN &mmv MOVE_RIGHT &mkp MB5 &none &kp RIGHT_SHIFT &kp RCTRL &kp LEFT_ALT &kp LCMD &none 158 | &none &kp C_AC_UNDO &kp C_AC_CUT &kp C_AC_COPY &kp C_AC_PASTE &mkp MCLK &none &none &none &none &none &none 159 | &trans &trans &trans &kp RETURN &trans 160 | >; 161 | }; 162 | 163 | SLOW { 164 | bindings = < 165 | &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans 166 | &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans 167 | &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans 168 | &trans &trans &trans &trans &trans 169 | >; 170 | }; 171 | 172 | SCROLL { 173 | bindings = < 174 | &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans 175 | &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans 176 | &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans 177 | &trans &trans &trans &trans &trans 178 | >; 179 | }; 180 | }; 181 | }; -------------------------------------------------------------------------------- /.github/workflows/user_config_build.yaml: -------------------------------------------------------------------------------- 1 | name: Reusable user config build 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | build_matrix_path: 7 | description: "Path to the build matrix file" 8 | default: "build.yaml" 9 | required: false 10 | type: string 11 | config_path: 12 | description: "Path to the config directory" 13 | default: "config" 14 | required: false 15 | type: string 16 | shield_path: 17 | description: "Path to the shields directory" 18 | default: "boards/shields/charybdis" 19 | required: false 20 | type: string 21 | keymap_path: 22 | description: "Path to the keymap directory" 23 | default: "boards/shields/charybdis/keymaps" 24 | required: false 25 | type: string 26 | fallback_binary: 27 | description: "Fallback binary format, if no *.uf2 file was built" 28 | default: "bin" 29 | required: false 30 | type: string 31 | 32 | jobs: 33 | matrix: 34 | runs-on: ubuntu-latest 35 | name: Fetch Build Keyboards 36 | outputs: 37 | build_matrix: ${{ env.build_matrix }} 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v4 41 | 42 | - name: Install yaml2json 43 | run: python3 -m pip install remarshal 44 | 45 | - name: Fetch Build Matrix 46 | run: | 47 | echo "build_matrix=$(yaml2json '${{ inputs.build_matrix_path }}' | jq -c .)" >> $GITHUB_ENV 48 | yaml2json "${{ inputs.build_matrix_path }}" | jq 49 | 50 | build: 51 | runs-on: ubuntu-latest 52 | container: 53 | image: zmkfirmware/zmk-build-arm:stable 54 | needs: matrix 55 | name: Build 56 | strategy: 57 | fail-fast: false 58 | matrix: ${{ fromJson(needs.matrix.outputs.build_matrix) }} 59 | steps: 60 | - name: Checkout 61 | uses: actions/checkout@v4 62 | 63 | - name: Create build directory 64 | run: | 65 | echo "build_dir=$(mktemp -d)" >> $GITHUB_ENV 66 | 67 | - name: Prepare variables 68 | shell: sh -x {0} 69 | env: 70 | board: ${{ matrix.board }} 71 | shield: ${{ matrix.shield }} 72 | keymap: ${{ matrix.keymap }} 73 | format: ${{ matrix.format }} 74 | artifact_name: ${{ matrix.artifact-name }} 75 | snippet: ${{ matrix.snippet }} 76 | run: | 77 | if [ -e zephyr/module.yml ]; then 78 | export zmk_load_arg=" -DZMK_EXTRA_MODULES='${GITHUB_WORKSPACE}'" 79 | new_tmp_dir="${TMPDIR:-/tmp}/zmk-config" 80 | mkdir -p "${new_tmp_dir}" 81 | echo "base_dir=${new_tmp_dir}" >> $GITHUB_ENV 82 | else 83 | echo "base_dir=${GITHUB_WORKSPACE}" >> $GITHUB_ENV 84 | fi 85 | 86 | if [ -n "${snippet}" ]; then 87 | extra_west_args="-S \"${snippet}\"" 88 | fi 89 | 90 | echo "zephyr_version=${ZEPHYR_VERSION}" >> $GITHUB_ENV 91 | echo "extra_west_args=${extra_west_args}" >> $GITHUB_ENV 92 | echo "extra_cmake_args=${shield:+-DSHIELD=\"$shield\"}${zmk_load_arg}" >> $GITHUB_ENV 93 | echo "display_name=${shield:+$shield - }${board}" >> $GITHUB_ENV 94 | echo "format=${format}" >> $GITHUB_ENV 95 | echo "keymap=${keymap}" >> $GITHUB_ENV 96 | echo "shield=${shield}" >> $GITHUB_ENV 97 | echo "artifact_name=${artifact_name:-${shield:+$shield-}${board}-zmk}" >> $GITHUB_ENV 98 | 99 | - name: Download keymaps 100 | uses: actions/download-artifact@v4 101 | with: 102 | name: keymap_files 103 | path: "${{ github.workspace }}/" 104 | 105 | - name: Copy keymaps to keymap directory 106 | run: | 107 | rm -f $GITHUB_WORKSPACE/config/*.keymap 108 | mkdir -p "$GITHUB_WORKSPACE/${{ inputs.keymap_path }}/" 109 | cd "$GITHUB_WORKSPACE" 110 | mv charybdis.keymap qwerty.keymap 111 | mv *.keymap "$GITHUB_WORKSPACE/${{ inputs.keymap_path }}/" 112 | ls -lR "$GITHUB_WORKSPACE/${{ inputs.keymap_path }}/" 113 | 114 | - name: Copy config files to isolated temporary directory 115 | run: | 116 | if [ "${{ env.base_dir }}" != "${GITHUB_WORKSPACE}" ]; then 117 | apt-get -qq update && apt-get -q install -o Dpkg::Progress-Fancy="0" -o APT::Color="0" -o Dpkg::Use-Pty="0" tree 118 | 119 | # Define base directory for config 120 | base_config_path="${{ env.base_dir }}/${{ inputs.config_path }}" 121 | 122 | # Create config directory 123 | mkdir -p "$base_config_path" 124 | 125 | # Copy config files 126 | cp -R "${{ inputs.config_path }}"/* "$base_config_path/" 127 | echo "config files ready" 128 | 129 | # Copy keymap & physical layouts 130 | if [ "${{ env.shield }}" != "settings_reset" ]; then 131 | # Copy physical layout file to shield directory 132 | mv -v "$base_config_path/charybdis-layouts.dtsi" \ 133 | "${GITHUB_WORKSPACE}/boards/shields/charybdis-${{ env.format }}/" 134 | 135 | # Copy active keymap to the board directory & adjust for bt/dongle format 136 | cp -Rv "$GITHUB_WORKSPACE/${{ inputs.keymap_path }}/${{ env.keymap }}.keymap" \ 137 | "$base_config_path/charybdis.keymap" 138 | echo "format: ${{ env.format }}" 139 | case "${{ env.format }}" in 140 | *bt*) 141 | echo "converting trackball device to bt/usb format" 142 | sed -i 's/device = <&vtrackball>;/device = <\&trackball>;/g' "$base_config_path/charybdis.keymap" 143 | grep "device = <" "$base_config_path/charybdis.keymap" 144 | ;; 145 | esac 146 | fi 147 | fi 148 | 149 | # Remove untargeted shields so they don't get auto picked up by the west build command 150 | find "${GITHUB_WORKSPACE}/boards/shields" \ 151 | -mindepth 1 \ 152 | -maxdepth 1 \ 153 | ! -name "charybdis-${{ env.format }}" \ 154 | -exec rm -rf {} + 155 | echo "Removed extra shields" 156 | 157 | echo "Build files list:" 158 | tree "${{ env.base_dir }}" 159 | tree "${GITHUB_WORKSPACE}/boards/shields" 160 | 161 | - name: Cache west modules 162 | uses: actions/cache@v4 163 | continue-on-error: true 164 | env: 165 | cache_name: cache-zephyr-${{ env.zephyr_version }}-modules 166 | with: 167 | path: | 168 | ${{ env.base_dir }}/modules/ 169 | ${{ env.base_dir }}/tools/ 170 | ${{ env.base_dir }}/zephyr/ 171 | ${{ env.base_dir }}/bootloader/ 172 | ${{ env.base_dir }}/zmk/ 173 | key: ${{ runner.os }}-build-${{ env.cache_name }}-${{ hashFiles('**/west.yml', '**/build.yaml') }} 174 | restore-keys: | 175 | ${{ runner.os }}-build-${{ env.cache_name }}- 176 | ${{ runner.os }}-build- 177 | ${{ runner.os }}- 178 | 179 | - name: West Init 180 | working-directory: ${{ env.base_dir }} 181 | run: west init -l "${{ env.base_dir }}/${{ inputs.config_path }}" 182 | 183 | - name: West Update 184 | working-directory: ${{ env.base_dir }} 185 | run: west update 186 | 187 | - name: West Zephyr export 188 | working-directory: ${{ env.base_dir }} 189 | run: west zephyr-export 190 | 191 | - name: West Build (${{ env.display_name }}) 192 | working-directory: ${{ env.base_dir }} 193 | shell: sh -x {0} 194 | run: west build --pristine -s zmk/app -d "${{ env.build_dir }}" -b "${{ matrix.board }}" ${{ env.extra_west_args }} -- -DZMK_CONFIG=${{ env.base_dir }}/${{ inputs.config_path }} ${{ env.extra_cmake_args }} ${{ matrix.cmake-args }} 195 | 196 | - name: ${{ env.display_name }} Kconfig file 197 | run: | 198 | if [ -f "${{ env.build_dir }}/zephyr/.config" ] 199 | then 200 | grep -v -e "^#" -e "^$" "${{ env.build_dir }}/zephyr/.config" | sort 201 | else 202 | echo "No Kconfig output" 203 | fi 204 | if: ${{ !cancelled() }} 205 | 206 | - name: ${{ env.display_name }} Devicetree file 207 | run: | 208 | if [ -f "${{ env.build_dir }}/zephyr/zephyr.dts" ] 209 | then 210 | cat "${{ env.build_dir }}/zephyr/zephyr.dts" 211 | elif [ -f "${{ env.build_dir }}/zephyr/zephyr.dts.pre" ] 212 | then 213 | cat -s "${{ env.build_dir }}/zephyr/zephyr.dts.pre" 214 | else 215 | echo "No Devicetree output" 216 | fi 217 | if: ${{ !cancelled() }} 218 | 219 | - name: Rename artifacts 220 | shell: sh -x {0} 221 | run: | 222 | mkdir "${{ env.build_dir }}/artifacts" 223 | if [ -f "${{ env.build_dir }}/zephyr/zmk.uf2" ] 224 | then 225 | cp "${{ env.build_dir }}/zephyr/zmk.uf2" "${{ env.build_dir }}/artifacts/${{ env.artifact_name }}.uf2" 226 | elif [ -f "${{ env.build_dir }}/zephyr/zmk.${{ inputs.fallback_binary }}" ] 227 | then 228 | cp "${{ env.build_dir }}/zephyr/zmk.${{ inputs.fallback_binary }}" "${{ env.build_dir }}/artifacts/${{ env.artifact_name }}.${{ inputs.fallback_binary }}" 229 | fi 230 | 231 | - name: Upload (${{ env.display_name }}) 232 | uses: actions/upload-artifact@v4 233 | with: 234 | name: artifact-${{ env.artifact_name }} 235 | path: ${{ env.build_dir }}/artifacts 236 | 237 | cleanup: 238 | runs-on: ubuntu-latest 239 | continue-on-error: true 240 | needs: build 241 | name: Merge & Prune Artifacts (optional) 242 | steps: 243 | - name: Upload Charybdis ZMK Firmware (QWERTY BT) 244 | continue-on-error: true 245 | uses: actions/upload-artifact/merge@v4 246 | with: 247 | pattern: artifact-charybdis_qwerty_{left,right} 248 | name: firmware-charybdis-qwerty 249 | delete-merged: true 250 | 251 | - name: Upload Charybdis ZMK Firmware (QWERTY Dongle) 252 | continue-on-error: true 253 | uses: actions/upload-artifact/merge@v4 254 | with: 255 | pattern: artifact-charybdis_qwerty_dongle* 256 | name: firmware-charybdis-qwerty-dongle 257 | delete-merged: true 258 | 259 | - name: Upload Charybdis ZMK Firmware (Colemak BT) 260 | continue-on-error: true 261 | uses: actions/upload-artifact/merge@v4 262 | with: 263 | pattern: artifact-charybdis_colemak_{left,right} 264 | name: firmware-charybdis-colemak 265 | delete-merged: true 266 | 267 | - name: Upload Charybdis ZMK Firmware (Colemak Dongle) 268 | continue-on-error: true 269 | uses: actions/upload-artifact/merge@v4 270 | with: 271 | pattern: artifact-charybdis_colemak_dongle* 272 | name: firmware-charybdis-colemak-dongle 273 | delete-merged: true 274 | 275 | - name: Upload Firmware Reset (Nano v2) 276 | continue-on-error: true 277 | uses: actions/upload-artifact/merge@v4 278 | with: 279 | pattern: artifact-firmware_reset_nano_v2 280 | name: firmware-reset-nanov2 281 | delete-merged: true 282 | 283 | - name: Delete keymap artifacts 284 | continue-on-error: true 285 | uses: geekyeggo/delete-artifact@v5 286 | with: 287 | name: keymap_files 288 | -------------------------------------------------------------------------------- /.github/workflows/draw_keymaps.yaml: -------------------------------------------------------------------------------- 1 | # Reusable workflow for drawing and committing an automated keymap diagram 2 | name: Draw ZMK keymaps 3 | on: 4 | workflow_call: 5 | inputs: 6 | keymap_patterns: 7 | description: "Path specification for keymaps to be parsed" 8 | default: "config/*.keymap" 9 | required: false 10 | type: string 11 | config_path: 12 | description: "Path to the keymap-drawer configuration file, ignored if non-existent" 13 | default: "keymap-drawer/config.yaml" 14 | required: false 15 | type: string 16 | west_config_path: 17 | description: "Path to the folder containing west.yml, e.g. `config`. If specified, west will be initialized to fetch modules defined in west.yml" 18 | default: "" 19 | required: false 20 | type: string 21 | output_folder: 22 | description: "Output folder for SVG and YAML files" 23 | default: "keymap-drawer" 24 | required: false 25 | type: string 26 | json_path: 27 | description: "Path containing .json physical layout description files, ignored if non-existent" 28 | default: "config" 29 | required: false 30 | type: string 31 | parse_args: 32 | description: "Map of keyboard names to extra `keymap parse` args, e.g. `corne:'--layer-names Def Lwr Rse Fun'`" 33 | default: "charybdis: '-b keymap-drawer/charybdis.yaml'" 34 | required: false 35 | type: string 36 | draw_args: 37 | description: "Map of keyboard names to extra `keymap draw` args, e.g. `corne:'-k corne_rotated -l LAYOUT_split_3x5_3'`" 38 | default: "" 39 | required: false 40 | type: string 41 | commit_message: 42 | description: "Commit message for updated images. Ignored if `amend_commit` is `true`." 43 | default: "keymap-drawer render" 44 | required: false 45 | type: string 46 | amend_commit: 47 | description: "Whether to amend the last commit instead of creating a new one. Make sure you understand the implications of rewriting the branch history if you use this option!" 48 | default: true 49 | required: false 50 | type: boolean 51 | install_branch: 52 | description: "Install keymap-drawer from a git branch, use empty for pypi release (default)" 53 | default: "" 54 | required: false 55 | type: string 56 | install_repo: 57 | description: "Install keymap-drawer from a different git remote, primarily for testing changes using a keymap-drawer fork. Ignored if `install_branch` is unset/empty." 58 | default: "https://github.com/caksoylar/keymap-drawer.git" 59 | required: false 60 | type: string 61 | destination: 62 | description: "Add the output files to a commit, as artifacts or both, values: `commit`, `artifact`, `both`" 63 | default: "commit" 64 | required: false 65 | type: string 66 | artifact_name: 67 | description: "Name of the produced artifact containing SVG and YAML outputs. Ignored if `destination` is `commit`." 68 | default: "keymap_layouts" 69 | required: false 70 | type: string 71 | fail_on_error: 72 | description: "Fail the action if an error occurs during parse/draw" 73 | default: true 74 | required: false 75 | type: boolean 76 | outputs: 77 | drawings: 78 | description: "Archive with keymap in YAML and drawing in SVG formats" 79 | value: ${{ jobs.draw.outputs.drawings }} 80 | 81 | jobs: 82 | draw: 83 | runs-on: ubuntu-latest 84 | outputs: 85 | drawings: ${{ steps.artifact-upload-step.outputs.artifact-id }} 86 | steps: 87 | - name: Checkout 88 | uses: actions/checkout@v4 89 | with: 90 | # So the reference to the parent commit is available when amending 91 | # See: 92 | # - https://github.com/stefanzweifel/git-auto-commit-action#using---amend-and---no-edit-as-commit-options 93 | # - https://github.com/stefanzweifel/git-auto-commit-action/issues/159#issuecomment-845347950 94 | # - https://github.com/actions/checkout 95 | fetch-depth: ${{ (inputs.amend_commit == true && 2) || 1 }} 96 | submodules: recursive 97 | 98 | - name: Install keymap-drawer (pypi) 99 | if: inputs.install_branch == '' 100 | run: python3 -m pip install keymap-drawer 101 | 102 | - name: Install keymap-drawer (git) 103 | if: inputs.install_branch != '' 104 | run: python3 -m pip install "git+${{ inputs.install_repo }}@${{ inputs.install_branch }}" 105 | 106 | - name: Install west 107 | if: inputs.west_config_path != '' 108 | run: python3 -m pip install west 109 | 110 | - name: Cache west modules 111 | if: inputs.west_config_path != '' 112 | uses: actions/cache@v4 113 | continue-on-error: true 114 | env: 115 | cache_name: cache-zephyr-3.5.0-modules 116 | with: 117 | path: | 118 | modules/ 119 | tools/ 120 | zephyr/ 121 | bootloader/ 122 | zmk/ 123 | key: ${{ runner.os }}-build-${{ env.cache_name }}-${{ hashFiles('**/west.yml', '**/build.yaml') }} 124 | restore-keys: | 125 | ${{ runner.os }}-build-${{ env.cache_name }}- 126 | ${{ runner.os }}-build- 127 | ${{ runner.os }}- 128 | 129 | - name: West Init 130 | if: inputs.west_config_path != '' 131 | run: | 132 | ls -Rhl $GITHUB_WORKSPACE 133 | west init -l ${{ inputs.west_config_path }} 134 | 135 | - name: West Update 136 | if: inputs.west_config_path != '' 137 | run: west update 138 | 139 | - name: Draw keymaps 140 | id: draw 141 | env: 142 | KEYMAP_raw_binding_map: > 143 | { 144 | "&bootloader": "BOOT LDR", 145 | "&mkp RCLK": "R CLK", 146 | "&mkp LCLK": "L CLK", 147 | "&mkp MCLK": "Mid CLK", 148 | "&mkp MB4": "BACK", 149 | "&mkp MB5": "FWD", 150 | "&mmv MOVE_UP": "MOVE UP", 151 | "&mmv MOVE_DOWN": "MOVE D", 152 | "&mmv MOVE_LEFT": "MOVE L", 153 | "&mmv MOVE_RIGHT": "MOVE R", 154 | "&msc MOVE_UP": "WHEEL D", 155 | "&msc MOVE_DOWN": "WHEEL U", 156 | "&msc MOVE_LEFT": "WHEEL L", 157 | "&msc MOVE_RIGHT": "WHEEL R", 158 | "&h_split": "HSPLIT", 159 | "&v_split": "VSPLIT", 160 | "&caps_word": "CAPS WORD", 161 | "&td_bore": "L_BASE L_EXTRA", 162 | "&tdmt": "L1/BS LCLICK", 163 | "&tdbt": "L2/ESC RCLICK", 164 | "&td_layers": "L_BASE L_PHOTO L_GAME", 165 | "&td_cursor": "SCRL SLOW", 166 | "&td_home": "~/", 167 | "&td_equals": "=", 168 | "td_hyphen": "-", 169 | "td_grave": "`", 170 | "detach_session": "DETACH", 171 | "create_or_attach": "NEW SESH", 172 | "sync_panes": "SYNC PANES", 173 | "next_pane": "NEXT PANE", 174 | "show_sessions": "SHOW SESHS", 175 | "sync_panes": "-", 176 | "&shrug": "SHRUG", 177 | "&lgtm": "LGTM", 178 | "&gcm": "GIT COMMIT", 179 | "&clip_hist": "CLIP HIST", 180 | "&shit": "SHIT SUDO", 181 | "&new_dir": "NEW DIR", 182 | "&py_iter": "PYTHON LOOP", 183 | "&js_iter": "JS LOOP", 184 | "&studio_unlock": "ZMK Studio" 185 | } 186 | run: | 187 | get_args() { 188 | local keyboard=$2 189 | eval set -- "$1" 190 | for arg; do 191 | local key=${arg%%:*} 192 | local val=${arg#*:} 193 | if [ "$key" = "$keyboard" ]; then 194 | echo "$val" 195 | break 196 | fi 197 | done 198 | } 199 | 200 | declare -a DRAWINGS 201 | error_occurred=0 202 | mkdir -p "${{ inputs.output_folder }}" 203 | 204 | config_path="${{ inputs.config_path }}" 205 | [ -e "$config_path" ] && config_arg=(-c "$config_path") || config_arg=() 206 | echo "INFO: using config args: ${config_arg[@]}" 207 | for keymap_file in ${{ inputs.keymap_patterns }}; do 208 | keyboard=$(basename -s .keymap "$keymap_file") 209 | echo "INFO: drawing for $keyboard" 210 | 211 | parse_args=$(get_args "${{ inputs.parse_args }}" "$keyboard") 212 | echo "INFO: got extra parse args: $parse_args" 213 | draw_args=$(get_args "${{ inputs.draw_args }}" "$keyboard") 214 | echo "INFO: got extra draw args: $draw_args" 215 | 216 | json_path="${{ inputs.json_path }}" 217 | if [ -f "$json_path/${keyboard}.json" ]; then 218 | echo "INFO: found $json_path/${keyboard}.json"; 219 | draw_args+=" -j $json_path/${keyboard}.json" 220 | fi 221 | 222 | keymap "${config_arg[@]}" parse -z "$keymap_file" $parse_args >"${{ inputs.output_folder }}/$keyboard.yaml" \ 223 | && keymap "${config_arg[@]}" draw "${{ inputs.output_folder }}/$keyboard.yaml" $draw_args >"${{ inputs.output_folder }}/$keyboard.svg" \ 224 | || { 225 | echo "ERROR: parsing or drawing failed for $keyboard!" 226 | error_occurred=1 227 | } 228 | DRAWINGS+=(\"${{ inputs.output_folder }}/$keyboard.yaml\" \"${{ inputs.output_folder }}/$keyboard.svg\") 229 | done 230 | IFS=',' 231 | echo "DRAWINGS=[${DRAWINGS[*]}]" >> $GITHUB_OUTPUT 232 | unset IFS 233 | 234 | if [ "${{ inputs.fail_on_error }}" == "true" ] && [ $error_occurred -eq 1 ]; then 235 | exit 1 236 | fi 237 | 238 | - name: Get last commit message 239 | id: last_commit_message 240 | if: inputs.amend_commit == true && (inputs.destination == 'commit' || inputs.destination == 'both') 241 | run: | 242 | echo "msg=$(git log -1 --pretty=%s)" >> $GITHUB_OUTPUT 243 | 244 | - name: Commit updated images 245 | if: ( inputs.destination == 'commit' || inputs.destination == 'both' ) 246 | uses: stefanzweifel/git-auto-commit-action@v5 247 | with: 248 | file_pattern: "${{ inputs.output_folder }}/*.svg ${{ inputs.output_folder }}/*.yaml" 249 | # So the previous commit is amended instead of creating a new one when desired 250 | # See: 251 | # - https://github.com/stefanzweifel/git-auto-commit-action#using---amend-and---no-edit-as-commit-options 252 | # - https://github.com/stefanzweifel/git-auto-commit-action/issues/159#issuecomment-845347950 253 | # - https://github.com/actions/checkout 254 | commit_message: "${{ (inputs.amend_commit == true && steps.last_commit_message.outputs.msg) || inputs.commit_message }}" 255 | commit_options: "${{ (inputs.amend_commit == true && '--amend --no-edit') || '' }}" 256 | push_options: "${{ (inputs.amend_commit == true && '--force-with-lease') || '' }}" 257 | skip_fetch: ${{ inputs.amend_commit == true }} 258 | 259 | - name: Artifact upload 260 | id: artifact-upload-step 261 | if: ( inputs.destination == 'artifact' || inputs.destination == 'both' ) 262 | uses: actions/upload-artifact@v4 263 | with: 264 | name: "${{ inputs.artifact_name }}" 265 | path: | 266 | ${{ join(fromJSON(steps.draw.outputs.DRAWINGS), ' 267 | ') }} 268 | --------------------------------------------------------------------------------